diff --git a/pkg/tortoise/tortoise.go b/pkg/tortoise/tortoise.go index a49189ee..db42336f 100644 --- a/pkg/tortoise/tortoise.go +++ b/pkg/tortoise/tortoise.go @@ -639,6 +639,11 @@ func UpdateTortoiseAutoscalingPolicyInStatus(tortoise *v1beta3.Tortoise, hpa *v2 return tortoise } +type containerNameAndResource struct { + containerName string + resourceName corev1.ResourceName +} + // UpdateResourceRequest updates pods' resource requests based on the calculated recommendation. // Updated ContainerResourceRequests will be used in the next mutating webhook of Pods. // It updates ContainerResourceRequests in the status of the Tortoise, when ALL the following conditions are met: @@ -649,6 +654,15 @@ func (c *Service) UpdateResourceRequest(ctx context.Context, tortoise *v1beta3.T *v1beta3.Tortoise, error, ) { + offResources := map[containerNameAndResource]bool{} + for _, policy := range tortoise.Status.AutoscalingPolicy { + for rn, p := range policy.Policy { + if p == v1beta3.AutoscalingTypeOff { + offResources[containerNameAndResource{containerName: policy.ContainerName, resourceName: rn}] = true + } + } + } + oldTortoise := tortoise.DeepCopy() newRequests := make([]v1beta3.ContainerResourceRequests, 0, len(tortoise.Status.Recommendations.Vertical.ContainerResourceRecommendation)) @@ -657,6 +671,15 @@ func (c *Service) UpdateResourceRequest(ctx context.Context, tortoise *v1beta3.T // We only records proposed* metrics here (record applied* metrics later) // because we don't want to record applied* metrics when UpdateMode is Off. for resourcename, value := range r.RecommendedResource { + if offResources[containerNameAndResource{containerName: r.ContainerName, resourceName: resourcename}] { + // ignore + oldvalue, ok := utils.GetRequestFromTortoise(tortoise, r.ContainerName, resourcename) + if ok { + recommendation[resourcename] = oldvalue + } + continue + } + if resourcename == corev1.ResourceCPU { metrics.ProposedCPURequest.WithLabelValues(tortoise.Name, tortoise.Namespace, r.ContainerName, tortoise.Spec.TargetRefs.ScaleTargetRef.Name, tortoise.Spec.TargetRefs.ScaleTargetRef.Kind).Set(float64(value.MilliValue())) if value.IsZero() { diff --git a/pkg/tortoise/tortoise_test.go b/pkg/tortoise/tortoise_test.go index 2a514f20..b90e26f1 100644 --- a/pkg/tortoise/tortoise_test.go +++ b/pkg/tortoise/tortoise_test.go @@ -3328,6 +3328,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, // No ContainerResourceRequests is set. Recommendations: v1beta3.Recommendations{ Vertical: v1beta3.VerticalRecommendations{ @@ -3360,6 +3376,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -3410,6 +3442,157 @@ func TestService_UpdateResourceRequest(t *testing.T) { }, }, }, + { + name: "ContainerResourceRequests won't be set if the autoscaling policy is Off", + tortoise: &v1beta3.Tortoise{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tortoise", + Namespace: "default", + }, + Spec: v1beta3.TortoiseSpec{ + UpdateMode: v1beta3.UpdateModeAuto, + }, + Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeHorizontal, + corev1.ResourceCPU: v1beta3.AutoscalingTypeOff, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeOff, + corev1.ResourceCPU: v1beta3.AutoscalingTypeVertical, + }, + }, + }, + Conditions: v1beta3.Conditions{ + TortoiseConditions: []v1beta3.TortoiseCondition{ + { + Type: v1beta3.TortoiseConditionTypeVerticalRecommendationUpdated, + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.NewTime(now), + LastUpdateTime: metav1.NewTime(now), + Message: "The recommendation is provided", + }, + }, + ContainerResourceRequests: []v1beta3.ContainerResourceRequests{ + { + ContainerName: "app", + Resource: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceCPU: resource.MustParse("1"), + }, + }, + { + ContainerName: "sidecar", + Resource: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1Gi"), + corev1.ResourceCPU: resource.MustParse("1"), + }, + }, + }, + }, + Recommendations: v1beta3.Recommendations{ + Vertical: v1beta3.VerticalRecommendations{ + ContainerResourceRecommendation: []v1beta3.RecommendedContainerResources{ + { + ContainerName: "app", + RecommendedResource: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("2Gi"), + corev1.ResourceCPU: resource.MustParse("0"), + }, + }, + { + ContainerName: "sidecar", + RecommendedResource: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("0Gi"), + corev1.ResourceCPU: resource.MustParse("2"), + }, + }, + }, + }, + }, + }, + }, + wantTortoise: &v1beta3.Tortoise{ + ObjectMeta: metav1.ObjectMeta{ + Name: "tortoise", + Namespace: "default", + }, + Spec: v1beta3.TortoiseSpec{ + UpdateMode: v1beta3.UpdateModeAuto, + }, + Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeHorizontal, + corev1.ResourceCPU: v1beta3.AutoscalingTypeOff, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeOff, + corev1.ResourceCPU: v1beta3.AutoscalingTypeVertical, + }, + }, + }, + Conditions: v1beta3.Conditions{ + TortoiseConditions: []v1beta3.TortoiseCondition{ + { + Type: v1beta3.TortoiseConditionTypeVerticalRecommendationUpdated, + Status: corev1.ConditionTrue, + LastTransitionTime: metav1.NewTime(now), + LastUpdateTime: metav1.NewTime(now), + Message: "The recommendation is provided", + }, + }, + ContainerResourceRequests: []v1beta3.ContainerResourceRequests{ + { + ContainerName: "app", + Resource: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("2Gi"), + corev1.ResourceCPU: resource.MustParse("1"), // off is ignored. + }, + }, + { + ContainerName: "sidecar", + Resource: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("1Gi"), // off is ignored. + corev1.ResourceCPU: resource.MustParse("2"), + }, + }, + }, + }, + Recommendations: v1beta3.Recommendations{ + Vertical: v1beta3.VerticalRecommendations{ + ContainerResourceRecommendation: []v1beta3.RecommendedContainerResources{ + { + ContainerName: "app", + RecommendedResource: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("2Gi"), + corev1.ResourceCPU: resource.MustParse("0"), + }, + }, + { + ContainerName: "sidecar", + RecommendedResource: corev1.ResourceList{ + corev1.ResourceMemory: resource.MustParse("0Gi"), + corev1.ResourceCPU: resource.MustParse("2"), + }, + }, + }, + }, + }, + }, + }, + }, { name: "ContainerResourceRequests won't be set if the recommendation is zero", tortoise: &v1beta3.Tortoise{ @@ -3421,6 +3604,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -3479,6 +3678,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -3540,6 +3755,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -3599,6 +3830,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -3661,6 +3908,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -3720,6 +3983,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -3782,6 +4061,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ ContainerResourceRequests: []v1beta3.ContainerResourceRequests{ // updated @@ -3832,6 +4127,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ ContainerResourceRequests: []v1beta3.ContainerResourceRequests{ { @@ -3884,6 +4195,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeOff, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ // empty ContainerResourceRequests: nil, @@ -3919,6 +4246,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeOff, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -3966,6 +4309,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeOff, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -4024,6 +4383,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeOff, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -4086,6 +4461,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Conditions: v1beta3.Conditions{ TortoiseConditions: []v1beta3.TortoiseCondition{ { @@ -4129,6 +4520,22 @@ func TestService_UpdateResourceRequest(t *testing.T) { UpdateMode: v1beta3.UpdateModeAuto, }, Status: v1beta3.TortoiseStatus{ + AutoscalingPolicy: []v1beta3.ContainerAutoscalingPolicy{ + { + ContainerName: "app", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "sidecar", + Policy: map[corev1.ResourceName]v1beta3.AutoscalingType{ + corev1.ResourceMemory: v1beta3.AutoscalingTypeVertical, + corev1.ResourceCPU: v1beta3.AutoscalingTypeHorizontal, + }, + }, + }, Recommendations: v1beta3.Recommendations{ Vertical: v1beta3.VerticalRecommendations{ ContainerResourceRecommendation: []v1beta3.RecommendedContainerResources{