diff --git a/apis/apps/v1alpha1/componentresourceconstraint_types.go b/apis/apps/v1alpha1/componentresourceconstraint_types.go index d3c40af2961..b3d83c15244 100644 --- a/apis/apps/v1alpha1/componentresourceconstraint_types.go +++ b/apis/apps/v1alpha1/componentresourceconstraint_types.go @@ -200,37 +200,62 @@ func (m *ResourceConstraint) ValidateStorage(storage *resource.Quantity) bool { return true } -// ValidateResourceRequirements validates if the resources meets the constraint -func (m *ResourceConstraint) ValidateResourceRequirements(r *corev1.ResourceRequirements) bool { - if !m.ValidateCPU(r.Requests.Cpu()) { +// ValidateResources validates if the resources meets the constraint +func (m *ResourceConstraint) ValidateResources(r corev1.ResourceList) bool { + if !m.ValidateCPU(r.Cpu()) { return false } - if !m.ValidateMemory(r.Requests.Cpu(), r.Requests.Memory()) { + if !m.ValidateMemory(r.Cpu(), r.Memory()) { return false } - if !m.ValidateStorage(r.Requests.Storage()) { + if !m.ValidateStorage(r.Storage()) { return false } return true } +func (m *ResourceConstraint) CompleteResources(r corev1.ResourceList) corev1.ResourceList { + if r.Cpu().IsZero() || !r.Memory().IsZero() { + return corev1.ResourceList{corev1.ResourceCPU: *r.Cpu(), corev1.ResourceMemory: *r.Memory()} + } + + var memory *inf.Dec + if m.Memory.SizePerCPU != nil { + memory = inf.NewDec(1, 0).Mul(r.Cpu().AsDec(), m.Memory.SizePerCPU.AsDec()) + } else { + memory = inf.NewDec(1, 0).Mul(r.Cpu().AsDec(), m.Memory.MinPerCPU.AsDec()) + } + return corev1.ResourceList{ + corev1.ResourceCPU: *r.Cpu(), + corev1.ResourceMemory: resource.MustParse(memory.String()), + } +} + // GetMinimalResources gets the minimal resources meets the constraint func (m *ResourceConstraint) GetMinimalResources() corev1.ResourceList { var ( - minCPU resource.Quantity - minMemory resource.Quantity + minCPU = resource.Quantity{} + minMemory = resource.Quantity{} ) + if len(m.CPU.Slots) == 0 && (m.CPU.Min == nil || m.CPU.Min.IsZero()) { + return corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("1"), + corev1.ResourceMemory: resource.MustParse("1Gi"), + } + } + if len(m.CPU.Slots) > 0 { minCPU = m.CPU.Slots[0] } - if m.CPU.Min != nil && minCPU.Cmp(*m.CPU.Min) > 0 { + if minCPU.IsZero() || (m.CPU.Min != nil && minCPU.Cmp(*m.CPU.Min) > 0) { minCPU = *m.CPU.Min } + var memory *inf.Dec if m.Memory.MinPerCPU != nil { memory = inf.NewDec(1, 0).Mul(minCPU.AsDec(), m.Memory.MinPerCPU.AsDec()) @@ -242,13 +267,13 @@ func (m *ResourceConstraint) GetMinimalResources() corev1.ResourceList { } // FindMatchingConstraints find all constraints that resource satisfies. -func (c *ComponentResourceConstraint) FindMatchingConstraints(r *corev1.ResourceRequirements) []ResourceConstraint { +func (c *ComponentResourceConstraint) FindMatchingConstraints(r corev1.ResourceList) []ResourceConstraint { if c == nil { return nil } var constraints []ResourceConstraint for _, constraint := range c.Spec.Constraints { - if constraint.ValidateResourceRequirements(r) { + if constraint.ValidateResources(r) { constraints = append(constraints, constraint) } } @@ -261,10 +286,6 @@ func (c *ComponentResourceConstraint) MatchClass(class *ComponentClassInstance) corev1.ResourceCPU: class.CPU, corev1.ResourceMemory: class.Memory, } - resource := &corev1.ResourceRequirements{ - Limits: request, - Requests: request, - } - constraints := c.FindMatchingConstraints(resource) + constraints := c.FindMatchingConstraints(request) return len(constraints) > 0 } diff --git a/apis/apps/v1alpha1/componentresourceconstraint_types_test.go b/apis/apps/v1alpha1/componentresourceconstraint_types_test.go index fd39063f69f..7e2da20420d 100644 --- a/apis/apps/v1alpha1/componentresourceconstraint_types_test.go +++ b/apis/apps/v1alpha1/componentresourceconstraint_types_test.go @@ -124,13 +124,11 @@ func TestResourceConstraints(t *testing.T) { cpu = resource.MustParse(item.cpu) memory = resource.MustParse(item.memory) ) - requirements := &corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{ - corev1.ResourceCPU: cpu, - corev1.ResourceMemory: memory, - }, + requests := corev1.ResourceList{ + corev1.ResourceCPU: cpu, + corev1.ResourceMemory: memory, } - assert.Equal(t, item.expect, len(cf.FindMatchingConstraints(requirements)) > 0) + assert.Equal(t, item.expect, len(cf.FindMatchingConstraints(requests)) > 0) class := &ComponentClassInstance{ ComponentClass: ComponentClass{ diff --git a/internal/class/class_utils.go b/internal/class/class_utils.go index 95ab80f0517..dc26f69f048 100644 --- a/internal/class/class_utils.go +++ b/internal/class/class_utils.go @@ -30,7 +30,6 @@ import ( "github.com/ghodss/yaml" "golang.org/x/exp/slices" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" @@ -106,16 +105,14 @@ func (r *Manager) ValidateResources(comp *v1alpha1.ClusterComponentSpec) error { var constraints []v1alpha1.ResourceConstraint // all volumes should match the constraints for _, volume := range comp.VolumeClaimTemplates { - resources := corev1.ResourceRequirements{ - Requests: map[corev1.ResourceName]resource.Quantity{}, - } + resources := corev1.ResourceList{} for k, v := range comp.Resources.Requests { - resources.Requests[k] = v + resources[k] = v } for k, v := range volume.Spec.Resources.Requests { - resources.Requests[k] = v + resources[k] = v } - result := constraint.FindMatchingConstraints(&resources) + result := constraint.FindMatchingConstraints(resources) if len(result) == 0 { break } @@ -131,7 +128,7 @@ func (r *Manager) ValidateResources(comp *v1alpha1.ClusterComponentSpec) error { func (r *Manager) GetResources(comp *v1alpha1.ClusterComponentSpec) (corev1.ResourceList, error) { result := corev1.ResourceList{} - if comp.ClassDefRef != nil { + if comp.ClassDefRef != nil && comp.ClassDefRef.Class != "" { cls, err := r.ChooseClass(comp) if err != nil { return result, err @@ -143,20 +140,34 @@ func (r *Manager) GetResources(comp *v1alpha1.ClusterComponentSpec) (corev1.Reso return nil, nil } - var constraints []v1alpha1.ResourceConstraint + var resourcesList []corev1.ResourceList for _, constraint := range r.constraints { for _, volume := range comp.VolumeClaimTemplates { - resources := corev1.ResourceRequirements{} - comp.Resources.DeepCopyInto(&resources) - volume.Spec.Resources.DeepCopyInto(&resources) - constraints = append(constraints, constraint.FindMatchingConstraints(&resources)...) + resources := corev1.ResourceList{} + for k, v := range comp.Resources.Requests { + resources[k] = v + } + for k, v := range volume.Spec.Resources.Requests { + resources[k] = v + } + rules := constraint.FindMatchingConstraints(resources) + if len(rules) == 0 { + break + } + for _, rule := range rules { + if resources.Cpu().IsZero() && resources.Memory().IsZero() { + resourcesList = append(resourcesList, rule.GetMinimalResources()) + } else { + resourcesList = append(resourcesList, rule.CompleteResources(resources)) + } + } } } - if len(constraints) == 0 { + if len(resourcesList) == 0 { return nil, ErrInvalidResource } - sort.Sort(ByConstraintList(constraints)) - return constraints[0].GetMinimalResources(), nil + sort.Sort(ByResourceList(resourcesList)) + return resourcesList[0], nil } // ChooseClass chooses the classes to be used for a given component with constraints diff --git a/internal/class/types.go b/internal/class/types.go index 9268adcaaed..f926e08f55a 100644 --- a/internal/class/types.go +++ b/internal/class/types.go @@ -22,9 +22,39 @@ package class import ( "sort" + corev1 "k8s.io/api/core/v1" + appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" ) +var _ sort.Interface = ByResourceList{} + +type ByResourceList []corev1.ResourceList + +func (b ByResourceList) Len() int { + return len(b) +} + +func (b ByResourceList) Less(i, j int) bool { + switch b[i].Cpu().Cmp(*b[j].Cpu()) { + case 1: + return true + case -1: + return false + } + switch b[i].Memory().Cmp(*b[j].Memory()) { + case 1: + return true + case -1: + return false + } + return false +} + +func (b ByResourceList) Swap(i, j int) { + b[i], b[j] = b[j], b[i] +} + var _ sort.Interface = ByConstraintList{} type ByConstraintList []appsv1alpha1.ResourceConstraint @@ -40,15 +70,15 @@ func (m ByConstraintList) Less(i, j int) bool { ) switch resource1.Cpu().Cmp(*resource2.Cpu()) { case 1: - return true - case -1: return false + case -1: + return true } switch resource1.Memory().Cmp(*resource2.Memory()) { case 1: - return true - case -1: return false + case -1: + return true } return false } diff --git a/internal/class/types_test.go b/internal/class/types_test.go index b0f96048fbc..e9b96560670 100644 --- a/internal/class/types_test.go +++ b/internal/class/types_test.go @@ -92,7 +92,6 @@ func TestResourceConstraint_ConstraintList(t *testing.T) { for _, constraint := range cf.Spec.Constraints { constraints = append(constraints, constraint) } - resource.MustParse("200Mi") sort.Sort(ByConstraintList(constraints)) resources := constraints[0].GetMinimalResources() assert.Equal(t, resources.Cpu().Cmp(resource.MustParse("0.1")) == 0, true) diff --git a/internal/controller/component/component.go b/internal/controller/component/component.go index 944f69afdc9..df44dddd780 100644 --- a/internal/controller/component/component.go +++ b/internal/controller/component/component.go @@ -148,7 +148,7 @@ func buildComponent(reqCtx intctrlutil.RequestCtx, if clusterCompSpec.Resources.Requests != nil || clusterCompSpec.Resources.Limits != nil { component.PodSpec.Containers[0].Resources = clusterCompSpec.Resources } - if err = fillResources(&cluster, component, clusterCompSpec, clsMgr); err != nil { + if err = updateResources(&cluster, component, clusterCompSpec, clsMgr); err != nil { reqCtx.Log.Error(err, "update class resources failed") return nil, err } @@ -363,7 +363,7 @@ func getClassManager(ctx context.Context, cli types2.ReadonlyClient, cluster *ap return class.NewManager(classDefinitionList, constraintList) } -func fillResources(cluster *appsv1alpha1.Cluster, component *SynthesizedComponent, clusterCompSpec appsv1alpha1.ClusterComponentSpec, clsMgr *class.Manager) error { +func updateResources(cluster *appsv1alpha1.Cluster, component *SynthesizedComponent, clusterCompSpec appsv1alpha1.ClusterComponentSpec, clsMgr *class.Manager) error { if ignoreResourceConstraint(cluster) { return nil }