diff --git a/api/autoscaling/v2/testdata/mutating/has-annotation-but-invalid1/tortoise.yaml b/api/autoscaling/v2/testdata/mutating/has-annotation-but-invalid1/tortoise.yaml index e342e242..b2e68624 100644 --- a/api/autoscaling/v2/testdata/mutating/has-annotation-but-invalid1/tortoise.yaml +++ b/api/autoscaling/v2/testdata/mutating/has-annotation-but-invalid1/tortoise.yaml @@ -25,12 +25,16 @@ status: containerResourcePhases: - containerName: "nginx" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working targets: deployment: sample horizontalPodAutoscaler: sample diff --git a/api/autoscaling/v2/testdata/mutating/has-annotation-but-invalid2/tortoise.yaml b/api/autoscaling/v2/testdata/mutating/has-annotation-but-invalid2/tortoise.yaml index e7cbfd74..e27ad9bc 100644 --- a/api/autoscaling/v2/testdata/mutating/has-annotation-but-invalid2/tortoise.yaml +++ b/api/autoscaling/v2/testdata/mutating/has-annotation-but-invalid2/tortoise.yaml @@ -25,12 +25,16 @@ status: containerResourcePhases: - containerName: "nginx" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working targets: deployment: sample horizontalPodAutoscaler: sample2 diff --git a/api/autoscaling/v2/testdata/mutating/mutate-by-recommendations/tortoise.yaml b/api/autoscaling/v2/testdata/mutating/mutate-by-recommendations/tortoise.yaml index e342e242..b2e68624 100644 --- a/api/autoscaling/v2/testdata/mutating/mutate-by-recommendations/tortoise.yaml +++ b/api/autoscaling/v2/testdata/mutating/mutate-by-recommendations/tortoise.yaml @@ -25,12 +25,16 @@ status: containerResourcePhases: - containerName: "nginx" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working targets: deployment: sample horizontalPodAutoscaler: sample diff --git a/api/v1beta1/tortoise_types.go b/api/v1beta1/tortoise_types.go index c0248c50..bd2fa1b5 100644 --- a/api/v1beta1/tortoise_types.go +++ b/api/v1beta1/tortoise_types.go @@ -154,7 +154,15 @@ type ContainerResourcePhases struct { // ContainerName is the name of target container. ContainerName string `json:"containerName" protobuf:"bytes,1,name=containerName"` // ResourcePhases is the phase of each resource of this container. - ResourcePhases map[v1.ResourceName]ContainerResourcePhase `json:"resourcePhases" protobuf:"bytes,2,name=resourcePhases"` + ResourcePhases map[v1.ResourceName]ResourcePhase `json:"resourcePhases" protobuf:"bytes,2,name=resourcePhases"` +} + +type ResourcePhase struct { + Phase ContainerResourcePhase `json:"phase" protobuf:"bytes,1,name=phase"` + // lastTransitionTime is the last time the condition transitioned from + // one status to another + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" protobuf:"bytes,2,opt,name=lastTransitionTime"` } type ContainerResourcePhase string @@ -176,6 +184,10 @@ const ( // TortoisePhaseWorking means tortoise is making the recommendations, // and applying the recommendation values. TortoisePhaseWorking TortoisePhase = "Working" + // TortoisePhasePartlyWorking means tortoise is making the recommendations, + // and applying the recommendation values. + // But, some of the resources are not scaled due to some reasons. (probably still gathering data) + TortoisePhasePartlyWorking TortoisePhase = "PartlyWorking" // TortoisePhaseEmergency means tortoise is in the emergency mode. TortoisePhaseEmergency TortoisePhase = "Emergency" // TortoisePhaseBackToNormal means tortoise was in the emergency mode, and now it's coming back to the normal operation. diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 0b24ab9f..b1b62c16 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -97,9 +97,9 @@ func (in *ContainerResourcePhases) DeepCopyInto(out *ContainerResourcePhases) { *out = *in if in.ResourcePhases != nil { in, out := &in.ResourcePhases, &out.ResourcePhases - *out = make(map[v1.ResourceName]ContainerResourcePhase, len(*in)) + *out = make(map[v1.ResourceName]ResourcePhase, len(*in)) for key, val := range *in { - (*out)[key] = val + (*out)[key] = *val.DeepCopy() } } } @@ -276,6 +276,22 @@ func (in *ReplicasRecommendation) DeepCopy() *ReplicasRecommendation { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResourcePhase) DeepCopyInto(out *ResourcePhase) { + *out = *in + in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcePhase. +func (in *ResourcePhase) DeepCopy() *ResourcePhase { + if in == nil { + return nil + } + out := new(ResourcePhase) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceQuantity) DeepCopyInto(out *ResourceQuantity) { *out = *in diff --git a/config/crd/bases/autoscaling.mercari.com_tortoises.yaml b/config/crd/bases/autoscaling.mercari.com_tortoises.yaml index 026a75c5..c70398a1 100644 --- a/config/crd/bases/autoscaling.mercari.com_tortoises.yaml +++ b/config/crd/bases/autoscaling.mercari.com_tortoises.yaml @@ -593,7 +593,17 @@ spec: type: string resourcePhases: additionalProperties: - type: string + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another + format: date-time + type: string + phase: + type: string + required: + - phase + type: object description: ResourcePhases is the phase of each resource of this container. type: object diff --git a/controllers/testdata/deletion-no-delete/before/tortoise.yaml b/controllers/testdata/deletion-no-delete/before/tortoise.yaml index 928c71fb..2e264a87 100644 --- a/controllers/testdata/deletion-no-delete/before/tortoise.yaml +++ b/controllers/testdata/deletion-no-delete/before/tortoise.yaml @@ -86,9 +86,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/deletion-policy-all/before/tortoise.yaml b/controllers/testdata/deletion-policy-all/before/tortoise.yaml index c3c6575b..8172727c 100644 --- a/controllers/testdata/deletion-policy-all/before/tortoise.yaml +++ b/controllers/testdata/deletion-policy-all/before/tortoise.yaml @@ -86,9 +86,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/after/tortoise.yaml b/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/after/tortoise.yaml index c72fbc84..e26f3f07 100644 --- a/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/after/tortoise.yaml +++ b/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/after/tortoise.yaml @@ -94,9 +94,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: GatheringData + resourcePhases: + cpu: + phase: Working + memory: + phase: GatheringData diff --git a/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/before/tortoise.yaml b/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/before/tortoise.yaml index 27b042a5..527ffd2c 100644 --- a/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/before/tortoise.yaml +++ b/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/before/tortoise.yaml @@ -85,9 +85,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/mutable-autoscalingpolicy-no-hpa-and-add-horizontal/after/tortoise.yaml b/controllers/testdata/mutable-autoscalingpolicy-no-hpa-and-add-horizontal/after/tortoise.yaml index e3a5d034..e60cb081 100644 --- a/controllers/testdata/mutable-autoscalingpolicy-no-hpa-and-add-horizontal/after/tortoise.yaml +++ b/controllers/testdata/mutable-autoscalingpolicy-no-hpa-and-add-horizontal/after/tortoise.yaml @@ -92,9 +92,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: GatheringData - memory: Working + cpu: + phase: GatheringData + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/mutable-autoscalingpolicy-no-hpa-and-add-horizontal/before/tortoise.yaml b/controllers/testdata/mutable-autoscalingpolicy-no-hpa-and-add-horizontal/before/tortoise.yaml index 3aaeb37e..a5575367 100644 --- a/controllers/testdata/mutable-autoscalingpolicy-no-hpa-and-add-horizontal/before/tortoise.yaml +++ b/controllers/testdata/mutable-autoscalingpolicy-no-hpa-and-add-horizontal/before/tortoise.yaml @@ -90,9 +90,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/mutable-autoscalingpolicy-remove-horizontal/after/tortoise.yaml b/controllers/testdata/mutable-autoscalingpolicy-remove-horizontal/after/tortoise.yaml index e3c7f1ec..805dd83d 100644 --- a/controllers/testdata/mutable-autoscalingpolicy-remove-horizontal/after/tortoise.yaml +++ b/controllers/testdata/mutable-autoscalingpolicy-remove-horizontal/after/tortoise.yaml @@ -91,9 +91,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/mutable-autoscalingpolicy-remove-horizontal/before/tortoise.yaml b/controllers/testdata/mutable-autoscalingpolicy-remove-horizontal/before/tortoise.yaml index eb30161f..64e254dd 100644 --- a/controllers/testdata/mutable-autoscalingpolicy-remove-horizontal/before/tortoise.yaml +++ b/controllers/testdata/mutable-autoscalingpolicy-remove-horizontal/before/tortoise.yaml @@ -85,9 +85,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-off/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-off/after/tortoise.yaml index b0f92f6b..da130bd8 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-off/after/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-off/after/tortoise.yaml @@ -89,9 +89,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-off/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-off/before/tortoise.yaml index 34c6d87e..f041f0e4 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-off/before/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-off/before/tortoise.yaml @@ -81,9 +81,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-vertical/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-vertical/after/tortoise.yaml index b83edaf6..dd84ae2c 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-vertical/after/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-vertical/after/tortoise.yaml @@ -90,9 +90,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-vertical/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-vertical/before/tortoise.yaml index 002cd103..351e064e 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-vertical/before/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-all-vertical/before/tortoise.yaml @@ -82,9 +82,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-emergency/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-emergency/after/tortoise.yaml index b4e79681..b0a9d79c 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-emergency/after/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-emergency/after/tortoise.yaml @@ -94,9 +94,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-emergency/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-emergency/before/tortoise.yaml index ba8c8651..38382456 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-emergency/before/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-emergency/before/tortoise.yaml @@ -86,9 +86,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-one-off/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-one-off/after/tortoise.yaml index 4cefdd45..5e54f6b2 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-one-off/after/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-one-off/after/tortoise.yaml @@ -92,9 +92,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-one-off/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-one-off/before/tortoise.yaml index b3ddfe12..bb8794cd 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-one-off/before/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-one-off/before/tortoise.yaml @@ -85,9 +85,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-working/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-working/after/tortoise.yaml index 3bb189a9..cc880c6e 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-working/after/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-working/after/tortoise.yaml @@ -93,9 +93,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-multiple-containers-pod-working/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-multiple-containers-pod-working/before/tortoise.yaml index 075e5fe3..2fcee779 100644 --- a/controllers/testdata/reconcile-for-the-multiple-containers-pod-working/before/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-multiple-containers-pod-working/before/tortoise.yaml @@ -85,9 +85,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-dryrun/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-dryrun/after/tortoise.yaml index 21efea1b..eddb0e13 100644 --- a/controllers/testdata/reconcile-for-the-single-container-pod-dryrun/after/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-single-container-pod-dryrun/after/tortoise.yaml @@ -68,9 +68,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-dryrun/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-dryrun/before/tortoise.yaml index c7f220cd..92767d5d 100644 --- a/controllers/testdata/reconcile-for-the-single-container-pod-dryrun/before/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-single-container-pod-dryrun/before/tortoise.yaml @@ -64,9 +64,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-emergency/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-emergency/after/tortoise.yaml index ac881a01..622c0a37 100644 --- a/controllers/testdata/reconcile-for-the-single-container-pod-emergency/after/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-single-container-pod-emergency/after/tortoise.yaml @@ -68,9 +68,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-emergency/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-emergency/before/tortoise.yaml index 905704c8..800b9d82 100644 --- a/controllers/testdata/reconcile-for-the-single-container-pod-emergency/before/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-single-container-pod-emergency/before/tortoise.yaml @@ -64,9 +64,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-working/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-working/after/tortoise.yaml index d7be922b..78dfbc9b 100644 --- a/controllers/testdata/reconcile-for-the-single-container-pod-working/after/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-single-container-pod-working/after/tortoise.yaml @@ -67,9 +67,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-working/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-working/before/tortoise.yaml index 61c454c2..6dd64540 100644 --- a/controllers/testdata/reconcile-for-the-single-container-pod-working/before/tortoise.yaml +++ b/controllers/testdata/reconcile-for-the-single-container-pod-working/before/tortoise.yaml @@ -63,9 +63,13 @@ status: containerResourcePhases: - containerName: "app" resourcePhases: - cpu: Working - memory: Working + cpu: + phase: Working + memory: + phase: Working - containerName: "istio-proxy" - resourcePhases: - cpu: Working - memory: Working + resourcePhases: + cpu: + phase: Working + memory: + phase: Working diff --git a/controllers/tortoise_controller.go b/controllers/tortoise_controller.go index f82a9cb2..e881c37c 100644 --- a/controllers/tortoise_controller.go +++ b/controllers/tortoise_controller.go @@ -128,7 +128,7 @@ func (r *TortoiseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ return ctrl.Result{}, err } - tortoise = r.TortoiseService.UpdateTortoisePhase(tortoise, dm) + tortoise = r.TortoiseService.UpdateTortoisePhase(tortoise, dm, now) if tortoise.Status.TortoisePhase == autoscalingv1beta1.TortoisePhaseInitializing { logger.V(4).Info("initializing tortoise", "tortoise", req.NamespacedName) // need to initialize HPA and VPA. @@ -139,7 +139,7 @@ func (r *TortoiseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ return ctrl.Result{RequeueAfter: r.Interval}, nil } - tortoise, err = r.HpaService.UpdateHPASpecFromTortoiseAutoscalingPolicy(ctx, tortoise, dm) + tortoise, err = r.HpaService.UpdateHPASpecFromTortoiseAutoscalingPolicy(ctx, tortoise, dm, now) if err != nil { logger.Error(err, "update HPA spec from Tortoise autoscaling policy", "tortoise", req.NamespacedName) return ctrl.Result{}, err @@ -162,7 +162,7 @@ func (r *TortoiseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ } // VPA is ready, we mark all Vertical scaling resources as Running. - tortoise = vpa.MakeAllVerticalContainerResourcePhaseWorking(tortoise) + tortoise = vpa.MakeAllVerticalContainerResourcePhaseWorking(tortoise, now) logger.V(4).Info("VPA created by tortoise is ready, proceeding to generate the recommendation", "tortoise", req.NamespacedName) hpa, err := r.HpaService.GetHPAOnTortoise(ctx, tortoise) @@ -241,7 +241,7 @@ func (r *TortoiseReconciler) deleteVPAAndHPA(ctx context.Context, tortoise *auto func (r *TortoiseReconciler) initializeVPAAndHPA(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment, now time.Time) error { // need to initialize HPA and VPA. - tortoise, err := r.HpaService.InitializeHPA(ctx, tortoise, dm) + tortoise, err := r.HpaService.InitializeHPA(ctx, tortoise, dm, now) if err != nil { return err } diff --git a/manifests/crd/apiextensions.k8s.io_v1_customresourcedefinition_tortoises.autoscaling.mercari.com.yaml b/manifests/crd/apiextensions.k8s.io_v1_customresourcedefinition_tortoises.autoscaling.mercari.com.yaml index 4d8ad511..48d2efbb 100644 --- a/manifests/crd/apiextensions.k8s.io_v1_customresourcedefinition_tortoises.autoscaling.mercari.com.yaml +++ b/manifests/crd/apiextensions.k8s.io_v1_customresourcedefinition_tortoises.autoscaling.mercari.com.yaml @@ -483,7 +483,16 @@ spec: type: string resourcePhases: additionalProperties: - type: string + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another + format: date-time + type: string + phase: + type: string + required: + - phase + type: object description: ResourcePhases is the phase of each resource of this container. type: object required: diff --git a/manifests/default/apiextensions.k8s.io_v1_customresourcedefinition_tortoises.autoscaling.mercari.com.yaml b/manifests/default/apiextensions.k8s.io_v1_customresourcedefinition_tortoises.autoscaling.mercari.com.yaml index 0a4a74d0..cf1bac45 100644 --- a/manifests/default/apiextensions.k8s.io_v1_customresourcedefinition_tortoises.autoscaling.mercari.com.yaml +++ b/manifests/default/apiextensions.k8s.io_v1_customresourcedefinition_tortoises.autoscaling.mercari.com.yaml @@ -483,7 +483,16 @@ spec: type: string resourcePhases: additionalProperties: - type: string + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another + format: date-time + type: string + phase: + type: string + required: + - phase + type: object description: ResourcePhases is the phase of each resource of this container. type: object required: diff --git a/pkg/hpa/service.go b/pkg/hpa/service.go index 299026c7..4395aedb 100644 --- a/pkg/hpa/service.go +++ b/pkg/hpa/service.go @@ -43,7 +43,7 @@ func New(c client.Client, recorder record.EventRecorder, replicaReductionFactor } } -func (c *Service) InitializeHPA(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment) (*autoscalingv1beta1.Tortoise, error) { +func (c *Service) InitializeHPA(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment, now time.Time) (*autoscalingv1beta1.Tortoise, error) { logger := log.FromContext(ctx) // if all policy is off or Vertical, we don't need HPA. if !hasHorizontal(tortoise) { @@ -67,7 +67,7 @@ func (c *Service) InitializeHPA(ctx context.Context, tortoise *autoscalingv1beta logger.V(4).Info("no existing HPA specified, creating HPA") // create default HPA. - _, tortoise, err := c.CreateHPA(ctx, tortoise, dm) + _, tortoise, err := c.CreateHPA(ctx, tortoise, dm, now) if err != nil { return tortoise, fmt.Errorf("create hpa: %w", err) } @@ -140,7 +140,7 @@ type resourceNameAndContainerName struct { // addHPAMetricsFromTortoiseAutoscalingPolicy adds metrics to the HPA based on the autoscaling policy in the tortoise. // Note that it doesn't update the HPA in kube-apiserver, you have to do that after this function. -func (c *Service) addHPAMetricsFromTortoiseAutoscalingPolicy(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, currenthpa *v2.HorizontalPodAutoscaler) (*v2.HorizontalPodAutoscaler, *autoscalingv1beta1.Tortoise, bool) { +func (c *Service) addHPAMetricsFromTortoiseAutoscalingPolicy(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, currenthpa *v2.HorizontalPodAutoscaler, now time.Time) (*v2.HorizontalPodAutoscaler, *autoscalingv1beta1.Tortoise, bool) { hpaEdited := false policies := sets.New[string]() @@ -189,7 +189,11 @@ func (c *Service) addHPAMetricsFromTortoiseAutoscalingPolicy(ctx context.Context found := false for i, p := range tortoise.Status.ContainerResourcePhases { if p.ContainerName == d.containerName { - tortoise.Status.ContainerResourcePhases[i].ResourcePhases[d.rn] = autoscalingv1beta1.ContainerResourcePhaseGatheringData + tortoise.Status.ContainerResourcePhases[i].ResourcePhases[d.rn] = autoscalingv1beta1.ResourcePhase{ + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now), + } + found = true break } @@ -197,8 +201,11 @@ func (c *Service) addHPAMetricsFromTortoiseAutoscalingPolicy(ctx context.Context if !found { tortoise.Status.ContainerResourcePhases = append(tortoise.Status.ContainerResourcePhases, autoscalingv1beta1.ContainerResourcePhases{ ContainerName: d.containerName, - ResourcePhases: map[corev1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - d.rn: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + ResourcePhases: map[corev1.ResourceName]autoscalingv1beta1.ResourcePhase{ + d.rn: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now), + }, }, }) } @@ -221,7 +228,7 @@ func (c *Service) addHPAMetricsFromTortoiseAutoscalingPolicy(ctx context.Context return currenthpa, tortoise, hpaEdited } -func (c *Service) CreateHPA(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment) (*v2.HorizontalPodAutoscaler, *autoscalingv1beta1.Tortoise, error) { +func (c *Service) CreateHPA(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment, now time.Time) (*v2.HorizontalPodAutoscaler, *autoscalingv1beta1.Tortoise, error) { if !hasHorizontal(tortoise) { // no need to create HPA return nil, tortoise, nil @@ -271,7 +278,7 @@ func (c *Service) CreateHPA(ctx context.Context, tortoise *autoscalingv1beta1.To }, } - hpa, tortoise, _ = c.addHPAMetricsFromTortoiseAutoscalingPolicy(ctx, tortoise, hpa) + hpa, tortoise, _ = c.addHPAMetricsFromTortoiseAutoscalingPolicy(ctx, tortoise, hpa, now) tortoise.Status.Targets.HorizontalPodAutoscaler = hpa.Name @@ -339,7 +346,7 @@ func (c *Service) ChangeHPAFromTortoiseRecommendation(tortoise *autoscalingv1bet return hpa, tortoise, nil } -func (c *Service) UpdateHPASpecFromTortoiseAutoscalingPolicy(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment) (*autoscalingv1beta1.Tortoise, error) { +func (c *Service) UpdateHPASpecFromTortoiseAutoscalingPolicy(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment, now time.Time) (*autoscalingv1beta1.Tortoise, error) { if !hasHorizontal(tortoise) { err := c.DeleteHPACreatedByTortoise(ctx, tortoise) if err != nil && !apierrors.IsNotFound(err) { @@ -359,7 +366,7 @@ func (c *Service) UpdateHPASpecFromTortoiseAutoscalingPolicy(ctx context.Context // - the user didn't specify Horizontal in any autoscalingPolicy previously, // but just updated tortoise to have Horizontal in some. // - In that case, we need to create an initial HPA. - tortoise, err = c.InitializeHPA(ctx, tortoise, dm) + tortoise, err = c.InitializeHPA(ctx, tortoise, dm, now) if err != nil { return tortoise, fmt.Errorf("initialize hpa: %w", err) } @@ -377,7 +384,7 @@ func (c *Service) UpdateHPASpecFromTortoiseAutoscalingPolicy(ctx context.Context var newhpa *v2.HorizontalPodAutoscaler var isHpaEdited bool - newhpa, tortoise, isHpaEdited = c.addHPAMetricsFromTortoiseAutoscalingPolicy(ctx, tortoise, hpa) + newhpa, tortoise, isHpaEdited = c.addHPAMetricsFromTortoiseAutoscalingPolicy(ctx, tortoise, hpa, now) if !isHpaEdited { // User didn't change anything. return tortoise, nil diff --git a/pkg/hpa/service_test.go b/pkg/hpa/service_test.go index 4ed975f3..5e40bd60 100644 --- a/pkg/hpa/service_test.go +++ b/pkg/hpa/service_test.go @@ -1062,7 +1062,7 @@ func TestService_InitializeHPA(t *testing.T) { if tt.initialHPA != nil { c = New(fake.NewClientBuilder().WithRuntimeObjects(tt.initialHPA).Build(), record.NewFakeRecorder(10), 0.95, 90) } - _, err := c.InitializeHPA(context.Background(), tt.args.tortoise, tt.args.dm) + _, err := c.InitializeHPA(context.Background(), tt.args.tortoise, tt.args.dm, time.Now()) if (err != nil) != tt.wantErr { t.Errorf("Service.InitializeHPA() error = %v, wantErr %v", err, tt.wantErr) } @@ -1072,7 +1072,7 @@ func TestService_InitializeHPA(t *testing.T) { t.Errorf("get hpa error = %v", err) } - if d := cmp.Diff(tt.afterHPA, hpa, cmpopts.IgnoreFields(v2.HorizontalPodAutoscaler{}, "TypeMeta"), cmpopts.IgnoreFields(metav1.ObjectMeta{}, "ResourceVersion")); d != "" { + if d := cmp.Diff(tt.afterHPA, hpa, cmpopts.IgnoreFields(v2.HorizontalPodAutoscaler{}, "TypeMeta"), cmpopts.IgnoreFields(metav1.ObjectMeta{}, "ResourceVersion"), cmpopts.IgnoreTypes(metav1.Time{})); d != "" { t.Errorf("Service.InitializeHPA() hpa diff = %v", d) } }) @@ -1129,16 +1129,24 @@ func TestService_UpdateHPASpecFromTortoiseAutoscalingPolicy(t *testing.T) { ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, { ContainerName: "istio-proxy", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, }, @@ -1245,16 +1253,24 @@ func TestService_UpdateHPASpecFromTortoiseAutoscalingPolicy(t *testing.T) { ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, { ContainerName: "istio-proxy", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseGatheringData, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, }, @@ -1354,16 +1370,24 @@ func TestService_UpdateHPASpecFromTortoiseAutoscalingPolicy(t *testing.T) { ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, { ContainerName: "istio-proxy", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, }, @@ -1481,16 +1505,24 @@ func TestService_UpdateHPASpecFromTortoiseAutoscalingPolicy(t *testing.T) { ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, { ContainerName: "istio-proxy", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, }, @@ -1580,16 +1612,24 @@ func TestService_UpdateHPASpecFromTortoiseAutoscalingPolicy(t *testing.T) { ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, { ContainerName: "istio-proxy", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, }, @@ -1709,16 +1749,24 @@ func TestService_UpdateHPASpecFromTortoiseAutoscalingPolicy(t *testing.T) { ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, { ContainerName: "istio-proxy", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseWorking, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, }, @@ -1736,13 +1784,13 @@ func TestService_UpdateHPASpecFromTortoiseAutoscalingPolicy(t *testing.T) { if tt.initialHPA != nil { c = New(fake.NewClientBuilder().WithRuntimeObjects(tt.initialHPA).Build(), record.NewFakeRecorder(10), 0.95, 90) } - tortoise, err := c.UpdateHPASpecFromTortoiseAutoscalingPolicy(context.Background(), tt.args.tortoise, tt.args.dm) + tortoise, err := c.UpdateHPASpecFromTortoiseAutoscalingPolicy(context.Background(), tt.args.tortoise, tt.args.dm, time.Now()) if (err != nil) != tt.wantErr { t.Errorf("Service.UpdateHPASpecFromTortoiseAutoscalingPolicy() error = %v, wantErr %v", err, tt.wantErr) return } - if d := cmp.Diff(tt.wantTortoise, tortoise); d != "" { + if d := cmp.Diff(tt.wantTortoise, tortoise, cmpopts.IgnoreTypes(metav1.Time{})); d != "" { t.Errorf("Service.UpdateHPASpecFromTortoiseAutoscalingPolicy() tortoise diff = %v", d) } diff --git a/pkg/tortoise/tortoise.go b/pkg/tortoise/tortoise.go index 1c1e239c..2ff0458d 100644 --- a/pkg/tortoise/tortoise.go +++ b/pkg/tortoise/tortoise.go @@ -75,10 +75,10 @@ func (s *Service) ShouldReconcileTortoiseNow(tortoise *v1beta1.Tortoise, now tim return false, lastTime.Add(s.tortoiseUpdateInterval).Sub(now) } -func (s *Service) UpdateTortoisePhase(tortoise *v1beta1.Tortoise, dm *appv1.Deployment) *v1beta1.Tortoise { +func (s *Service) UpdateTortoisePhase(tortoise *v1beta1.Tortoise, dm *appv1.Deployment, now time.Time) *v1beta1.Tortoise { switch tortoise.Status.TortoisePhase { case "": - tortoise = s.initializeTortoise(tortoise, dm) + tortoise = s.initializeTortoise(tortoise, dm, now) r := "1 week" if s.minMaxReplicasRoutine == "daily" { r = "1 day" @@ -89,7 +89,12 @@ func (s *Service) UpdateTortoisePhase(tortoise *v1beta1.Tortoise, dm *appv1.Depl // change it to GatheringData anyway. Later the controller may change it back to initialize if VPA isn't ready. tortoise.Status.TortoisePhase = v1beta1.TortoisePhaseGatheringData case v1beta1.TortoisePhaseGatheringData: - tortoise = s.checkIfTortoiseFinishedGatheringData(tortoise) + tortoise = s.changeTortoisePhaseWorkingIfTortoiseFinishedGatheringData(tortoise, now) + if tortoise.Status.TortoisePhase == v1beta1.TortoisePhaseWorking { + s.recorder.Event(tortoise, corev1.EventTypeNormal, "Working", "Tortoise finishes gathering data and it starts to work on autoscaling") + } + case v1beta1.TortoisePhasePartlyWorking: + tortoise = s.changeTortoisePhaseWorkingIfTortoiseFinishedGatheringData(tortoise, now) if tortoise.Status.TortoisePhase == v1beta1.TortoisePhaseWorking { s.recorder.Event(tortoise, corev1.EventTypeNormal, "Working", "Tortoise finishes gathering data and it starts to work on autoscaling") } @@ -111,7 +116,7 @@ func (s *Service) UpdateTortoisePhase(tortoise *v1beta1.Tortoise, dm *appv1.Depl return tortoise } -func (s *Service) checkIfTortoiseFinishedGatheringData(tortoise *v1beta1.Tortoise) *v1beta1.Tortoise { +func (s *Service) changeTortoisePhaseWorkingIfTortoiseFinishedGatheringData(tortoise *v1beta1.Tortoise, now time.Time) *v1beta1.Tortoise { for _, r := range tortoise.Status.Recommendations.Horizontal.MinReplicas { if r.Value == 0 { return tortoise @@ -123,7 +128,42 @@ func (s *Service) checkIfTortoiseFinishedGatheringData(tortoise *v1beta1.Tortois } } - tortoise.Status.TortoisePhase = v1beta1.TortoisePhaseWorking + // At least, MaxReplicas/MinReplicas recommendation are ready. + + someAreGathering := false + someAreWorking := false + for i, c := range tortoise.Status.ContainerResourcePhases { + for rn, p := range c.ResourcePhases { + if p.Phase == v1beta1.ContainerResourcePhaseOff { + // ignore + continue + } + if p.Phase == v1beta1.ContainerResourcePhaseWorking { + someAreWorking = true + continue + } + // If the last transition time is within 1 week, we consider it's still gathering data. + if p.LastTransitionTime.Add(7 * 24 * time.Hour).After(now) { + someAreGathering = true + } else { + // It's finish gathering data. + tortoise.Status.ContainerResourcePhases[i].ResourcePhases[rn] = v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseWorking, + LastTransitionTime: metav1.NewTime(now), + } + someAreWorking = true + } + } + } + + if someAreGathering && someAreWorking { + // Some are working, but some are still gathering data. + tortoise.Status.TortoisePhase = v1beta1.TortoisePhasePartlyWorking + } else if !someAreGathering && someAreWorking { + // All are working. + tortoise.Status.TortoisePhase = v1beta1.TortoisePhaseWorking + } + return tortoise } @@ -167,7 +207,7 @@ func (s *Service) initializeMinMaxReplicas(tortoise *v1beta1.Tortoise) *v1beta1. return tortoise } -func (s *Service) initializeTortoise(tortoise *v1beta1.Tortoise, dm *appv1.Deployment) *v1beta1.Tortoise { +func (s *Service) initializeTortoise(tortoise *v1beta1.Tortoise, dm *appv1.Deployment, now time.Time) *v1beta1.Tortoise { tortoise = s.initializeMinMaxReplicas(tortoise) tortoise.Status.TortoisePhase = v1beta1.TortoisePhaseInitializing @@ -190,15 +230,21 @@ func (s *Service) initializeTortoise(tortoise *v1beta1.Tortoise, dm *appv1.Deplo for _, p := range tortoise.Spec.ResourcePolicy { phase := v1beta1.ContainerResourcePhases{ ContainerName: p.ContainerName, - ResourcePhases: map[corev1.ResourceName]v1beta1.ContainerResourcePhase{}, + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{}, } for rn, policy := range p.AutoscalingPolicy { if policy == v1beta1.AutoscalingTypeOff { - phase.ResourcePhases[rn] = v1beta1.ContainerResourcePhaseOff + phase.ResourcePhases[rn] = v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseOff, + LastTransitionTime: metav1.NewTime(now), + } continue } - phase.ResourcePhases[rn] = v1beta1.ContainerResourcePhaseGatheringData + phase.ResourcePhases[rn] = v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now), + } } tortoise.Status.ContainerResourcePhases = append(tortoise.Status.ContainerResourcePhases, phase) } diff --git a/pkg/tortoise/tortoise_test.go b/pkg/tortoise/tortoise_test.go index 778df205..7bd49a84 100644 --- a/pkg/tortoise/tortoise_test.go +++ b/pkg/tortoise/tortoise_test.go @@ -318,16 +318,24 @@ func TestService_InitializeTortoise(t *testing.T) { ContainerResourcePhases: []v1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[corev1.ResourceName]v1beta1.ContainerResourcePhase{ - corev1.ResourceMemory: v1beta1.ContainerResourcePhaseGatheringData, - corev1.ResourceCPU: v1beta1.ContainerResourcePhaseGatheringData, + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceMemory: v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + }, + corev1.ResourceCPU: v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + }, }, }, { ContainerName: "istio", - ResourcePhases: map[corev1.ResourceName]v1beta1.ContainerResourcePhase{ - corev1.ResourceMemory: v1beta1.ContainerResourcePhaseOff, - corev1.ResourceCPU: v1beta1.ContainerResourcePhaseGatheringData, + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceMemory: v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseOff, + }, + corev1.ResourceCPU: v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + }, }, }, }, @@ -664,9 +672,13 @@ func TestService_InitializeTortoise(t *testing.T) { ContainerResourcePhases: []v1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[corev1.ResourceName]v1beta1.ContainerResourcePhase{ - corev1.ResourceCPU: v1beta1.ContainerResourcePhaseGatheringData, - corev1.ResourceMemory: v1beta1.ContainerResourcePhaseGatheringData, + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + }, + corev1.ResourceMemory: v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + }, }, }, }, @@ -719,8 +731,8 @@ func TestService_InitializeTortoise(t *testing.T) { minMaxReplicasRoutine: tt.fields.minMaxReplicasRoutine, timeZone: tt.fields.timeZone, } - got := s.initializeTortoise(tt.tortoise, tt.deployment) - if d := cmp.Diff(got, tt.want); d != "" { + got := s.initializeTortoise(tt.tortoise, tt.deployment, time.Now()) + if d := cmp.Diff(got, tt.want, cmpopts.IgnoreTypes(metav1.Time{})); d != "" { t.Errorf("initializeTortoise() diff = %v", d) } }) @@ -1008,3 +1020,439 @@ func TestService_RecordReconciliationFailure(t *testing.T) { }) } } + +func TestService_changeTortoisePhaseWorkingIfTortoiseFinishedGatheringData(t *testing.T) { + timeZone := "Asia/Tokyo" + now := time.Now() + tests := []struct { + name string + tortoise *v1beta1.Tortoise + want *v1beta1.Tortoise + }{ + { + name: "minReplicas/maxReplicas recommendation is not yet gathered", + tortoise: &v1beta1.Tortoise{ + Status: v1beta1.TortoiseStatus{ + TortoisePhase: v1beta1.TortoisePhaseGatheringData, + Recommendations: v1beta1.Recommendations{ + Horizontal: v1beta1.HorizontalRecommendations{ + MinReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + // empty + }, + }, + MaxReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + // empty + }, + }, + }, + }, + }, + }, + want: &v1beta1.Tortoise{ + Status: v1beta1.TortoiseStatus{ + TortoisePhase: v1beta1.TortoisePhaseGatheringData, + Recommendations: v1beta1.Recommendations{ + Horizontal: v1beta1.HorizontalRecommendations{ + MinReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + // empty + }, + }, + MaxReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + // empty + }, + }, + }, + }, + }, + }, + }, + { + name: "some container resource need to gather more data", + tortoise: &v1beta1.Tortoise{ + Status: v1beta1.TortoiseStatus{ + TortoisePhase: v1beta1.TortoisePhaseGatheringData, + Recommendations: v1beta1.Recommendations{ + Horizontal: v1beta1.HorizontalRecommendations{ + MinReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + Value: 3, + }, + }, + MaxReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + Value: 3, + }, + }, + }, + }, + ContainerResourcePhases: []v1beta1.ContainerResourcePhases{ + { + ContainerName: "app", + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: { + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now.Add(-24 * 1 * time.Hour)), + }, + corev1.ResourceMemory: { + Phase: v1beta1.ContainerResourcePhaseGatheringData, + // finish gathering + LastTransitionTime: metav1.NewTime(now.Add(-24 * 8 * time.Hour)), + }, + }, + }, + { + ContainerName: "istio-proxy", + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: { + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now.Add(-1 * time.Minute)), + }, + corev1.ResourceMemory: { + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now.Add(-1 * time.Minute)), + }, + }, + }, + }, + }, + }, + want: &v1beta1.Tortoise{ + Status: v1beta1.TortoiseStatus{ + TortoisePhase: v1beta1.TortoisePhasePartlyWorking, + Recommendations: v1beta1.Recommendations{ + Horizontal: v1beta1.HorizontalRecommendations{ + MinReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + Value: 3, + }, + }, + MaxReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + Value: 3, + }, + }, + }, + }, + ContainerResourcePhases: []v1beta1.ContainerResourcePhases{ + { + ContainerName: "app", + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: { + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now.Add(-24 * 1 * time.Hour)), + }, + corev1.ResourceMemory: { + Phase: v1beta1.ContainerResourcePhaseWorking, + LastTransitionTime: metav1.NewTime(now), + }, + }, + }, + { + ContainerName: "istio-proxy", + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: { + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now.Add(-1 * time.Minute)), + }, + corev1.ResourceMemory: { + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.NewTime(now.Add(-1 * time.Minute)), + }, + }, + }, + }, + }, + }, + }, + { + name: "all container resource gathered the data", + tortoise: &v1beta1.Tortoise{ + Status: v1beta1.TortoiseStatus{ + TortoisePhase: v1beta1.TortoisePhaseGatheringData, + Recommendations: v1beta1.Recommendations{ + Horizontal: v1beta1.HorizontalRecommendations{ + MinReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + Value: 3, + }, + }, + MaxReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + Value: 3, + }, + }, + }, + }, + ContainerResourcePhases: []v1beta1.ContainerResourcePhases{ + { + ContainerName: "app", + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: { + Phase: v1beta1.ContainerResourcePhaseWorking, + }, + corev1.ResourceMemory: { + Phase: v1beta1.ContainerResourcePhaseGatheringData, + // finish gathering + LastTransitionTime: metav1.NewTime(now.Add(-24 * 8 * time.Hour)), + }, + }, + }, + { + ContainerName: "istio-proxy", + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: { + Phase: v1beta1.ContainerResourcePhaseWorking, + LastTransitionTime: metav1.NewTime(now.Add(-24 * 2 * time.Hour)), + }, + corev1.ResourceMemory: { + // off is ignored + Phase: v1beta1.ContainerResourcePhaseOff, + }, + }, + }, + }, + }, + }, + want: &v1beta1.Tortoise{ + Status: v1beta1.TortoiseStatus{ + TortoisePhase: v1beta1.TortoisePhaseWorking, + Recommendations: v1beta1.Recommendations{ + Horizontal: v1beta1.HorizontalRecommendations{ + MinReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + Value: 3, + }, + }, + MaxReplicas: []v1beta1.ReplicasRecommendation{ + { + From: 0, + To: 8, + TimeZone: timeZone, + Value: 3, + }, + { + From: 8, + To: 16, + TimeZone: timeZone, + Value: 3, + }, + { + From: 16, + To: 24, + TimeZone: timeZone, + Value: 3, + }, + }, + }, + }, + ContainerResourcePhases: []v1beta1.ContainerResourcePhases{ + { + ContainerName: "app", + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: { + Phase: v1beta1.ContainerResourcePhaseWorking, + }, + corev1.ResourceMemory: { + Phase: v1beta1.ContainerResourcePhaseWorking, + }, + }, + }, + { + ContainerName: "istio-proxy", + ResourcePhases: map[corev1.ResourceName]v1beta1.ResourcePhase{ + corev1.ResourceCPU: { + Phase: v1beta1.ContainerResourcePhaseWorking, + LastTransitionTime: metav1.NewTime(now.Add(-24 * 2 * time.Hour)), + }, + corev1.ResourceMemory: { + // off is ignored + Phase: v1beta1.ContainerResourcePhaseOff, + }, + }, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Service{} + got := s.changeTortoisePhaseWorkingIfTortoiseFinishedGatheringData(tt.tortoise, now) + + if d := cmp.Diff(got, tt.want, cmpopts.IgnoreTypes(metav1.Time{})); d != "" { + t.Errorf("initializeTortoise() diff = %v", d) + } + }) + } +} diff --git a/pkg/vpa/service.go b/pkg/vpa/service.go index 6d3322f6..e5a96571 100644 --- a/pkg/vpa/service.go +++ b/pkg/vpa/service.go @@ -3,6 +3,7 @@ package vpa import ( "context" "fmt" + "time" autoscaling "k8s.io/api/autoscaling/v1" corev1 "k8s.io/api/core/v1" @@ -274,7 +275,7 @@ func (c *Service) GetTortoiseMonitorVPA(ctx context.Context, tortoise *autoscali return vpa, false, nil } -func MakeAllVerticalContainerResourcePhaseWorking(tortoise *autoscalingv1beta1.Tortoise) *autoscalingv1beta1.Tortoise { +func MakeAllVerticalContainerResourcePhaseWorking(tortoise *autoscalingv1beta1.Tortoise, now time.Time) *autoscalingv1beta1.Tortoise { verticalResourceAndContainer := sets.New[resourceNameAndContainerName]() for _, p := range tortoise.Spec.ResourcePolicy { for rn, ap := range p.AutoscalingPolicy { @@ -288,7 +289,10 @@ func MakeAllVerticalContainerResourcePhaseWorking(tortoise *autoscalingv1beta1.T for _, d := range verticalResourceAndContainer.UnsortedList() { for i, p := range tortoise.Status.ContainerResourcePhases { if p.ContainerName == d.containerName { - tortoise.Status.ContainerResourcePhases[i].ResourcePhases[d.rn] = autoscalingv1beta1.ContainerResourcePhaseWorking + tortoise.Status.ContainerResourcePhases[i].ResourcePhases[d.rn] = autoscalingv1beta1.ResourcePhase{ + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + LastTransitionTime: metav1.NewTime(now), + } found = true break } @@ -296,8 +300,11 @@ func MakeAllVerticalContainerResourcePhaseWorking(tortoise *autoscalingv1beta1.T if !found { tortoise.Status.ContainerResourcePhases = append(tortoise.Status.ContainerResourcePhases, autoscalingv1beta1.ContainerResourcePhases{ ContainerName: d.containerName, - ResourcePhases: map[corev1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - d.rn: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[corev1.ResourceName]autoscalingv1beta1.ResourcePhase{ + d.rn: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + LastTransitionTime: metav1.NewTime(now), + }, }, }) } diff --git a/pkg/vpa/service_test.go b/pkg/vpa/service_test.go index af8810ea..f9385b70 100644 --- a/pkg/vpa/service_test.go +++ b/pkg/vpa/service_test.go @@ -2,8 +2,10 @@ package vpa import ( "testing" + "time" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -50,16 +52,25 @@ func TestMakeAllVerticalContainerResourcePhaseRunning(t *testing.T) { ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseGatheringData, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + }, }, }, { ContainerName: "istio-proxy", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseGatheringData, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.Time{}, + }, }, }, }, @@ -93,16 +104,24 @@ func TestMakeAllVerticalContainerResourcePhaseRunning(t *testing.T) { ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ { ContainerName: "app", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseGatheringData, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, { ContainerName: "istio-proxy", - ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ContainerResourcePhase{ - v1.ResourceCPU: autoscalingv1beta1.ContainerResourcePhaseGatheringData, - v1.ResourceMemory: autoscalingv1beta1.ContainerResourcePhaseWorking, + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + }, + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, }, }, }, @@ -112,10 +131,10 @@ func TestMakeAllVerticalContainerResourcePhaseRunning(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := MakeAllVerticalContainerResourcePhaseWorking(tt.args.tortoise) + got := MakeAllVerticalContainerResourcePhaseWorking(tt.args.tortoise, time.Now()) // use diff to compare - if diff := cmp.Diff(got, tt.want); diff != "" { + if diff := cmp.Diff(got, tt.want, cmpopts.IgnoreTypes(metav1.Time{})); diff != "" { t.Fatalf("MakeAllVerticalContainerResourcePhaseRunning() mismatch (-want +got):\n%s", diff) } })