diff --git a/api/v1beta1/tortoise_types.go b/api/v1beta1/tortoise_types.go index bd2fa1b5..fde40524 100644 --- a/api/v1beta1/tortoise_types.go +++ b/api/v1beta1/tortoise_types.go @@ -101,7 +101,7 @@ type UpdateMode string const ( UpdateModeOff UpdateMode = "Off" UpdateModeEmergency UpdateMode = "Emergency" - AutoUpdateMode UpdateMode = "Auto" + UpdateModeAuto UpdateMode = "Auto" ) // +kubebuilder:validation:Enum=Off;Horizontal;Vertical @@ -184,7 +184,7 @@ const ( // TortoisePhaseWorking means tortoise is making the recommendations, // and applying the recommendation values. TortoisePhaseWorking TortoisePhase = "Working" - // TortoisePhasePartlyWorking means tortoise is making the recommendations, + // TortoisePhasePartlyWorking means tortoise has maxReplicas and minReplicas recommendations ready, // and applying the recommendation values. // But, some of the resources are not scaled due to some reasons. (probably still gathering data) TortoisePhasePartlyWorking TortoisePhase = "PartlyWorking" diff --git a/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/after/hpa.yaml b/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/after/hpa.yaml index ed8c2e55..22200c2b 100644 --- a/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/after/hpa.yaml +++ b/controllers/testdata/mutable-autoscalingpolicy-add-another-horizontal/after/hpa.yaml @@ -39,7 +39,7 @@ spec: container: istio-proxy name: memory target: - averageUtilization: 75 + averageUtilization: 50 type: Utilization type: ContainerResource minReplicas: 5 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/hpa.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/hpa.yaml new file mode 100644 index 00000000..96c8dedd --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/hpa.yaml @@ -0,0 +1,38 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-hpa-mercari + namespace: default +spec: + behavior: + scaleDown: + policies: + - periodSeconds: 90 + type: Percent + value: 2 + selectPolicy: Max + scaleUp: + policies: + - periodSeconds: 60 + type: Percent + value: 100 + selectPolicy: Max + stabilizationWindowSeconds: 0 + maxReplicas: 20 + metrics: + - containerResource: + container: app + name: cpu + target: + averageUtilization: 75 + type: Utilization + type: ContainerResource + minReplicas: 5 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + currentMetrics: null + desiredReplicas: 0 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/tortoise.yaml new file mode 100644 index 00000000..db0a79c5 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/tortoise.yaml @@ -0,0 +1,73 @@ +metadata: + name: mercari + namespace: default +spec: + resourcePolicy: + - autoscalingPolicy: + cpu: Horizontal + memory: Vertical + containerName: app + targetRefs: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + conditions: + containerRecommendationFromVPA: + - containerName: app + maxRecommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + recommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + tortoiseConditions: null + recommendations: + horizontal: + maxReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 20 + minReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 5 + targetUtilizations: + - containerName: app + targetUtilization: + cpu: 75 + vertical: + containerResourceRecommendation: + - RecommendedResource: + cpu: "4" + memory: 3Gi + containerName: app + targets: + deployment: "" + horizontalPodAutoscaler: tortoise-hpa-mercari + verticalPodAutoscalers: + - name: tortoise-updater-mercari + role: Updater + - name: tortoise-monitor-mercari + role: Monitor + tortoisePhase: Working + containerResourcePhases: + - containerName: "app" + resourcePhases: + cpu: + phase: Working + memory: + phase: Working \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/vpa-Monitor.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/vpa-Monitor.yaml new file mode 100644 index 00000000..b618edf9 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/vpa-Monitor.yaml @@ -0,0 +1,33 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-monitor-mercari + namespace: default +spec: + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: "Off" +status: + conditions: + - lastTransitionTime: null + status: "True" + type: RecommendationProvided + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "3" + memory: 3Gi + target: + cpu: "3" + memory: 3Gi + upperBound: + cpu: "5" + memory: 5Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/vpa-Updater.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/vpa-Updater.yaml new file mode 100644 index 00000000..68d3f56a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/after/vpa-Updater.yaml @@ -0,0 +1,34 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-updater-mercari + namespace: default +spec: + recommenders: + - name: tortoise-controller + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: Auto +status: + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "4" + memory: 3Gi + target: + cpu: "4" + memory: 3Gi + uncappedTarget: + cpu: "4" + memory: 3Gi + upperBound: + cpu: "4" + memory: 3Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/deployment.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/deployment.yaml new file mode 100644 index 00000000..34693daa --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/deployment.yaml @@ -0,0 +1,23 @@ +metadata: + name: mercari-app + namespace: default +spec: + selector: + matchLabels: + app: mercari + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: mercari + spec: + containers: + - image: awesome-mercari-app-image + name: app + resources: + requests: + cpu: "4" + memory: 4Gi +status: + replicas: 10 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/hpa.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/hpa.yaml new file mode 100644 index 00000000..2948735a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/hpa.yaml @@ -0,0 +1,38 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-hpa-mercari + namespace: default +spec: + behavior: + scaleDown: + policies: + - periodSeconds: 90 + type: Percent + value: 2 + selectPolicy: Max + scaleUp: + policies: + - periodSeconds: 60 + type: Percent + value: 100 + selectPolicy: Max + stabilizationWindowSeconds: 0 + maxReplicas: 100 + metrics: + - containerResource: + container: app + name: cpu + target: + averageUtilization: 50 + type: Utilization + type: ContainerResource + minReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + currentMetrics: null + desiredReplicas: 0 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/tortoise.yaml new file mode 100644 index 00000000..67c6bf68 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/tortoise.yaml @@ -0,0 +1,69 @@ +metadata: + name: mercari + namespace: default +spec: + resourcePolicy: + - autoscalingPolicy: + cpu: Horizontal + memory: Vertical + containerName: app + targetRefs: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + conditions: + containerRecommendationFromVPA: + - containerName: app + maxRecommendation: + cpu: + quantity: "0" + updatedAt: null + memory: + quantity: "0" + updatedAt: null + recommendation: + cpu: + quantity: "0" + updatedAt: null + memory: + quantity: "0" + updatedAt: null + tortoiseConditions: null + recommendations: + horizontal: + maxReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 15 + minReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 3 + targetUtilizations: + - containerName: app + targetUtilization: + cpu: 50 + vertical: + containerResourceRecommendation: null + targets: + deployment: "" + horizontalPodAutoscaler: tortoise-hpa-mercari + verticalPodAutoscalers: + - name: tortoise-updater-mercari + role: Updater + - name: tortoise-monitor-mercari + role: Monitor + tortoisePhase: GatheringData + containerResourcePhases: + - containerName: "app" + resourcePhases: + cpu: + phase: GatheringData + memory: + phase: GatheringData \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/vpa-Monitor.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/vpa-Monitor.yaml new file mode 100644 index 00000000..b618edf9 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/vpa-Monitor.yaml @@ -0,0 +1,33 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-monitor-mercari + namespace: default +spec: + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: "Off" +status: + conditions: + - lastTransitionTime: null + status: "True" + type: RecommendationProvided + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "3" + memory: 3Gi + target: + cpu: "3" + memory: 3Gi + upperBound: + cpu: "5" + memory: 5Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/vpa-Updater.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/vpa-Updater.yaml new file mode 100644 index 00000000..20b213cc --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data-finished/before/vpa-Updater.yaml @@ -0,0 +1,19 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-updater-mercari + namespace: default +spec: + recommenders: + - name: tortoise-controller + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: Auto +status: {} diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/hpa.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/hpa.yaml new file mode 100644 index 00000000..2948735a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/hpa.yaml @@ -0,0 +1,38 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-hpa-mercari + namespace: default +spec: + behavior: + scaleDown: + policies: + - periodSeconds: 90 + type: Percent + value: 2 + selectPolicy: Max + scaleUp: + policies: + - periodSeconds: 60 + type: Percent + value: 100 + selectPolicy: Max + stabilizationWindowSeconds: 0 + maxReplicas: 100 + metrics: + - containerResource: + container: app + name: cpu + target: + averageUtilization: 50 + type: Utilization + type: ContainerResource + minReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + currentMetrics: null + desiredReplicas: 0 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/tortoise.yaml new file mode 100644 index 00000000..f28da578 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/tortoise.yaml @@ -0,0 +1,73 @@ +metadata: + name: mercari + namespace: default +spec: + resourcePolicy: + - autoscalingPolicy: + cpu: Horizontal + memory: Vertical + containerName: app + targetRefs: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + conditions: + containerRecommendationFromVPA: + - containerName: app + maxRecommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + recommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + tortoiseConditions: null + recommendations: + horizontal: + maxReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 20 + minReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 5 + targetUtilizations: + - containerName: app + targetUtilization: + cpu: 75 + vertical: + containerResourceRecommendation: + - RecommendedResource: + cpu: "4" + memory: 3Gi + containerName: app + targets: + deployment: "" + horizontalPodAutoscaler: tortoise-hpa-mercari + verticalPodAutoscalers: + - name: tortoise-updater-mercari + role: Updater + - name: tortoise-monitor-mercari + role: Monitor + tortoisePhase: GatheringData # It's gathering data because VPA is ready but the max/min replicas recommendation is not ready yet. + containerResourcePhases: + - containerName: "app" + resourcePhases: + cpu: + phase: GatheringData + memory: + phase: Working # VPA gets ready. \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/vpa-Monitor.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/vpa-Monitor.yaml new file mode 100644 index 00000000..b618edf9 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/vpa-Monitor.yaml @@ -0,0 +1,33 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-monitor-mercari + namespace: default +spec: + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: "Off" +status: + conditions: + - lastTransitionTime: null + status: "True" + type: RecommendationProvided + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "3" + memory: 3Gi + target: + cpu: "3" + memory: 3Gi + upperBound: + cpu: "5" + memory: 5Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/vpa-Updater.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/vpa-Updater.yaml new file mode 100644 index 00000000..4bdaa9b8 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/after/vpa-Updater.yaml @@ -0,0 +1,18 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-updater-mercari + namespace: default +spec: + recommenders: + - name: tortoise-controller + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: Auto \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/deployment.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/deployment.yaml new file mode 100644 index 00000000..34693daa --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/deployment.yaml @@ -0,0 +1,23 @@ +metadata: + name: mercari-app + namespace: default +spec: + selector: + matchLabels: + app: mercari + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: mercari + spec: + containers: + - image: awesome-mercari-app-image + name: app + resources: + requests: + cpu: "4" + memory: 4Gi +status: + replicas: 10 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/hpa.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/hpa.yaml new file mode 100644 index 00000000..2948735a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/hpa.yaml @@ -0,0 +1,38 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-hpa-mercari + namespace: default +spec: + behavior: + scaleDown: + policies: + - periodSeconds: 90 + type: Percent + value: 2 + selectPolicy: Max + scaleUp: + policies: + - periodSeconds: 60 + type: Percent + value: 100 + selectPolicy: Max + stabilizationWindowSeconds: 0 + maxReplicas: 100 + metrics: + - containerResource: + container: app + name: cpu + target: + averageUtilization: 50 + type: Utilization + type: ContainerResource + minReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + currentMetrics: null + desiredReplicas: 0 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/tortoise.yaml new file mode 100644 index 00000000..08e16e30 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/tortoise.yaml @@ -0,0 +1,73 @@ +metadata: + name: mercari + namespace: default +spec: + resourcePolicy: + - autoscalingPolicy: + cpu: Horizontal + memory: Vertical + containerName: app + targetRefs: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + conditions: + containerRecommendationFromVPA: + - containerName: app + maxRecommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + recommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + tortoiseConditions: null + recommendations: + horizontal: + maxReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 20 + minReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 5 + targetUtilizations: + - containerName: app + targetUtilization: + cpu: 75 + vertical: + containerResourceRecommendation: + - RecommendedResource: + cpu: "4" + memory: 3Gi + containerName: app + targets: + deployment: "" + horizontalPodAutoscaler: tortoise-hpa-mercari + verticalPodAutoscalers: + - name: tortoise-updater-mercari + role: Updater + - name: tortoise-monitor-mercari + role: Monitor + tortoisePhase: GatheringData + containerResourcePhases: + - containerName: "app" + resourcePhases: + cpu: + phase: GatheringData + memory: + phase: GatheringData \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/vpa-Monitor.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/vpa-Monitor.yaml new file mode 100644 index 00000000..b618edf9 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/vpa-Monitor.yaml @@ -0,0 +1,33 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-monitor-mercari + namespace: default +spec: + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: "Off" +status: + conditions: + - lastTransitionTime: null + status: "True" + type: RecommendationProvided + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "3" + memory: 3Gi + target: + cpu: "3" + memory: 3Gi + upperBound: + cpu: "5" + memory: 5Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/vpa-Updater.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/vpa-Updater.yaml new file mode 100644 index 00000000..20b213cc --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-gathering-data/before/vpa-Updater.yaml @@ -0,0 +1,19 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-updater-mercari + namespace: default +spec: + recommenders: + - name: tortoise-controller + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: Auto +status: {} diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/hpa.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/hpa.yaml new file mode 100644 index 00000000..b76a3d33 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/hpa.yaml @@ -0,0 +1,38 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-hpa-mercari + namespace: default +spec: + behavior: + scaleDown: + policies: + - periodSeconds: 90 + type: Percent + value: 2 + selectPolicy: Max + scaleUp: + policies: + - periodSeconds: 60 + type: Percent + value: 100 + selectPolicy: Max + stabilizationWindowSeconds: 0 + maxReplicas: 20 + metrics: + - containerResource: + container: app + name: cpu + target: + averageUtilization: 50 + type: Utilization + type: ContainerResource + minReplicas: 5 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + currentMetrics: null + desiredReplicas: 0 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/tortoise.yaml new file mode 100644 index 00000000..08e16e30 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/tortoise.yaml @@ -0,0 +1,73 @@ +metadata: + name: mercari + namespace: default +spec: + resourcePolicy: + - autoscalingPolicy: + cpu: Horizontal + memory: Vertical + containerName: app + targetRefs: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + conditions: + containerRecommendationFromVPA: + - containerName: app + maxRecommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + recommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + tortoiseConditions: null + recommendations: + horizontal: + maxReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 20 + minReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 5 + targetUtilizations: + - containerName: app + targetUtilization: + cpu: 75 + vertical: + containerResourceRecommendation: + - RecommendedResource: + cpu: "4" + memory: 3Gi + containerName: app + targets: + deployment: "" + horizontalPodAutoscaler: tortoise-hpa-mercari + verticalPodAutoscalers: + - name: tortoise-updater-mercari + role: Updater + - name: tortoise-monitor-mercari + role: Monitor + tortoisePhase: GatheringData + containerResourcePhases: + - containerName: "app" + resourcePhases: + cpu: + phase: GatheringData + memory: + phase: GatheringData \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/vpa-Monitor.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/vpa-Monitor.yaml new file mode 100644 index 00000000..b618edf9 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/vpa-Monitor.yaml @@ -0,0 +1,33 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-monitor-mercari + namespace: default +spec: + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: "Off" +status: + conditions: + - lastTransitionTime: null + status: "True" + type: RecommendationProvided + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "3" + memory: 3Gi + target: + cpu: "3" + memory: 3Gi + upperBound: + cpu: "5" + memory: 5Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/vpa-Updater.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/vpa-Updater.yaml new file mode 100644 index 00000000..4bdaa9b8 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/after/vpa-Updater.yaml @@ -0,0 +1,18 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-updater-mercari + namespace: default +spec: + recommenders: + - name: tortoise-controller + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: Auto \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/deployment.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/deployment.yaml new file mode 100644 index 00000000..34693daa --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/deployment.yaml @@ -0,0 +1,23 @@ +metadata: + name: mercari-app + namespace: default +spec: + selector: + matchLabels: + app: mercari + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: mercari + spec: + containers: + - image: awesome-mercari-app-image + name: app + resources: + requests: + cpu: "4" + memory: 4Gi +status: + replicas: 10 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/hpa.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/hpa.yaml new file mode 100644 index 00000000..2948735a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/hpa.yaml @@ -0,0 +1,38 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-hpa-mercari + namespace: default +spec: + behavior: + scaleDown: + policies: + - periodSeconds: 90 + type: Percent + value: 2 + selectPolicy: Max + scaleUp: + policies: + - periodSeconds: 60 + type: Percent + value: 100 + selectPolicy: Max + stabilizationWindowSeconds: 0 + maxReplicas: 100 + metrics: + - containerResource: + container: app + name: cpu + target: + averageUtilization: 50 + type: Utilization + type: ContainerResource + minReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + currentMetrics: null + desiredReplicas: 0 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/tortoise.yaml new file mode 100644 index 00000000..08e16e30 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/tortoise.yaml @@ -0,0 +1,73 @@ +metadata: + name: mercari + namespace: default +spec: + resourcePolicy: + - autoscalingPolicy: + cpu: Horizontal + memory: Vertical + containerName: app + targetRefs: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + conditions: + containerRecommendationFromVPA: + - containerName: app + maxRecommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + recommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + tortoiseConditions: null + recommendations: + horizontal: + maxReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 20 + minReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 5 + targetUtilizations: + - containerName: app + targetUtilization: + cpu: 75 + vertical: + containerResourceRecommendation: + - RecommendedResource: + cpu: "4" + memory: 3Gi + containerName: app + targets: + deployment: "" + horizontalPodAutoscaler: tortoise-hpa-mercari + verticalPodAutoscalers: + - name: tortoise-updater-mercari + role: Updater + - name: tortoise-monitor-mercari + role: Monitor + tortoisePhase: GatheringData + containerResourcePhases: + - containerName: "app" + resourcePhases: + cpu: + phase: GatheringData + memory: + phase: GatheringData \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/vpa-Monitor.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/vpa-Monitor.yaml new file mode 100644 index 00000000..b618edf9 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/vpa-Monitor.yaml @@ -0,0 +1,33 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-monitor-mercari + namespace: default +spec: + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: "Off" +status: + conditions: + - lastTransitionTime: null + status: "True" + type: RecommendationProvided + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "3" + memory: 3Gi + target: + cpu: "3" + memory: 3Gi + upperBound: + cpu: "5" + memory: 5Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/vpa-Updater.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/vpa-Updater.yaml new file mode 100644 index 00000000..20b213cc --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-initializing/before/vpa-Updater.yaml @@ -0,0 +1,19 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-updater-mercari + namespace: default +spec: + recommenders: + - name: tortoise-controller + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: Auto +status: {} diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/hpa.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/hpa.yaml new file mode 100644 index 00000000..b76a3d33 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/hpa.yaml @@ -0,0 +1,38 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-hpa-mercari + namespace: default +spec: + behavior: + scaleDown: + policies: + - periodSeconds: 90 + type: Percent + value: 2 + selectPolicy: Max + scaleUp: + policies: + - periodSeconds: 60 + type: Percent + value: 100 + selectPolicy: Max + stabilizationWindowSeconds: 0 + maxReplicas: 20 + metrics: + - containerResource: + container: app + name: cpu + target: + averageUtilization: 50 + type: Utilization + type: ContainerResource + minReplicas: 5 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + currentMetrics: null + desiredReplicas: 0 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/tortoise.yaml new file mode 100644 index 00000000..627b803a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/tortoise.yaml @@ -0,0 +1,73 @@ +metadata: + name: mercari + namespace: default +spec: + resourcePolicy: + - autoscalingPolicy: + cpu: Horizontal + memory: Vertical + containerName: app + targetRefs: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + conditions: + containerRecommendationFromVPA: + - containerName: app + maxRecommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + recommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + tortoiseConditions: null + recommendations: + horizontal: + maxReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 20 + minReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 5 + targetUtilizations: + - containerName: app + targetUtilization: + cpu: 75 + vertical: + containerResourceRecommendation: + - RecommendedResource: + cpu: "4" + memory: 3Gi + containerName: app + targets: + deployment: "" + horizontalPodAutoscaler: tortoise-hpa-mercari + verticalPodAutoscalers: + - name: tortoise-updater-mercari + role: Updater + - name: tortoise-monitor-mercari + role: Monitor + tortoisePhase: PartlyWorking + containerResourcePhases: + - containerName: "app" + resourcePhases: + cpu: + phase: GatheringData + memory: + phase: Working \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/vpa-Monitor.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/vpa-Monitor.yaml new file mode 100644 index 00000000..b618edf9 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/vpa-Monitor.yaml @@ -0,0 +1,33 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-monitor-mercari + namespace: default +spec: + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: "Off" +status: + conditions: + - lastTransitionTime: null + status: "True" + type: RecommendationProvided + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "3" + memory: 3Gi + target: + cpu: "3" + memory: 3Gi + upperBound: + cpu: "5" + memory: 5Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/vpa-Updater.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/vpa-Updater.yaml new file mode 100644 index 00000000..68d3f56a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/after/vpa-Updater.yaml @@ -0,0 +1,34 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-updater-mercari + namespace: default +spec: + recommenders: + - name: tortoise-controller + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: Auto +status: + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "4" + memory: 3Gi + target: + cpu: "4" + memory: 3Gi + uncappedTarget: + cpu: "4" + memory: 3Gi + upperBound: + cpu: "4" + memory: 3Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/deployment.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/deployment.yaml new file mode 100644 index 00000000..34693daa --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/deployment.yaml @@ -0,0 +1,23 @@ +metadata: + name: mercari-app + namespace: default +spec: + selector: + matchLabels: + app: mercari + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + app: mercari + spec: + containers: + - image: awesome-mercari-app-image + name: app + resources: + requests: + cpu: "4" + memory: 4Gi +status: + replicas: 10 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/hpa.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/hpa.yaml new file mode 100644 index 00000000..2948735a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/hpa.yaml @@ -0,0 +1,38 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-hpa-mercari + namespace: default +spec: + behavior: + scaleDown: + policies: + - periodSeconds: 90 + type: Percent + value: 2 + selectPolicy: Max + scaleUp: + policies: + - periodSeconds: 60 + type: Percent + value: 100 + selectPolicy: Max + stabilizationWindowSeconds: 0 + maxReplicas: 100 + metrics: + - containerResource: + container: app + name: cpu + target: + averageUtilization: 50 + type: Utilization + type: ContainerResource + minReplicas: 1 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + currentMetrics: null + desiredReplicas: 0 diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/tortoise.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/tortoise.yaml new file mode 100644 index 00000000..627b803a --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/tortoise.yaml @@ -0,0 +1,73 @@ +metadata: + name: mercari + namespace: default +spec: + resourcePolicy: + - autoscalingPolicy: + cpu: Horizontal + memory: Vertical + containerName: app + targetRefs: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app +status: + conditions: + containerRecommendationFromVPA: + - containerName: app + maxRecommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + recommendation: + cpu: + quantity: "3" + updatedAt: null + memory: + quantity: 3Gi + updatedAt: null + tortoiseConditions: null + recommendations: + horizontal: + maxReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 20 + minReplicas: + - from: 0 + timezone: Local + to: 24 + updatedAt: "2023-10-06T01:01:24Z" + value: 5 + targetUtilizations: + - containerName: app + targetUtilization: + cpu: 75 + vertical: + containerResourceRecommendation: + - RecommendedResource: + cpu: "4" + memory: 3Gi + containerName: app + targets: + deployment: "" + horizontalPodAutoscaler: tortoise-hpa-mercari + verticalPodAutoscalers: + - name: tortoise-updater-mercari + role: Updater + - name: tortoise-monitor-mercari + role: Monitor + tortoisePhase: PartlyWorking + containerResourcePhases: + - containerName: "app" + resourcePhases: + cpu: + phase: GatheringData + memory: + phase: Working \ No newline at end of file diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/vpa-Monitor.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/vpa-Monitor.yaml new file mode 100644 index 00000000..b618edf9 --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/vpa-Monitor.yaml @@ -0,0 +1,33 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-monitor-mercari + namespace: default +spec: + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: "Off" +status: + conditions: + - lastTransitionTime: null + status: "True" + type: RecommendationProvided + recommendation: + containerRecommendations: + - containerName: app + lowerBound: + cpu: "3" + memory: 3Gi + target: + cpu: "3" + memory: 3Gi + upperBound: + cpu: "5" + memory: 5Gi diff --git a/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/vpa-Updater.yaml b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/vpa-Updater.yaml new file mode 100644 index 00000000..20b213cc --- /dev/null +++ b/controllers/testdata/reconcile-for-the-single-container-pod-partly-working/before/vpa-Updater.yaml @@ -0,0 +1,19 @@ +metadata: + annotations: + tortoise.autoscaling.mercari.com/managed-by-tortoise: "true" + tortoises.autoscaling.mercari.com/tortoise-name: mercari + name: tortoise-updater-mercari + namespace: default +spec: + recommenders: + - name: tortoise-controller + resourcePolicy: + containerPolicies: + - containerName: app + targetRef: + apiVersion: apps/v1 + kind: Deployment + name: mercari-app + updatePolicy: + updateMode: Auto +status: {} 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 78dfbc9b..db0a79c5 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 @@ -70,10 +70,4 @@ status: cpu: phase: Working memory: - phase: Working - - containerName: "istio-proxy" - resourcePhases: - cpu: - phase: Working - memory: - phase: Working + phase: Working \ No newline at end of file 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 6dd64540..8f2bf843 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 @@ -66,10 +66,4 @@ status: cpu: phase: Working memory: - phase: Working - - containerName: "istio-proxy" - resourcePhases: - cpu: - phase: Working - memory: - phase: Working + phase: Working \ No newline at end of file diff --git a/controllers/tortoise_controller_test.go b/controllers/tortoise_controller_test.go index 59b4ca5d..3dd1473c 100644 --- a/controllers/tortoise_controller_test.go +++ b/controllers/tortoise_controller_test.go @@ -11,6 +11,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" v1 "k8s.io/api/apps/v1" v2 "k8s.io/api/autoscaling/v2" + corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" autoscalingv1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" @@ -203,11 +204,10 @@ var _ = Describe("Test TortoiseController", func() { } } - runTest := func(path string) { - initializeResourcesFromFiles(ctx, k8sClient, filepath.Join(path, "before")) - stopFunc = startController(ctx) - tc := testCase{want: newResource(filepath.Join(path, "after"))} + checkWithWantedResources := func(path string) { + // wait for the reconciliation. time.Sleep(1 * time.Second) + tc := testCase{want: newResource(filepath.Join(path, "after"))} Eventually(func(g Gomega) { gotTortoise := &v1beta1.Tortoise{} err := k8sClient.Get(ctx, client.ObjectKey{Namespace: "default", Name: "mercari"}, gotTortoise) @@ -236,11 +236,16 @@ var _ = Describe("Test TortoiseController", func() { v1beta1.VerticalPodAutoscalerRoleMonitor: gotMonitorVPA, }}) g.Expect(err).ShouldNot(HaveOccurred()) - - cleanUp() }).Should(Succeed()) } + runTest := func(path string) { + initializeResourcesFromFiles(ctx, k8sClient, filepath.Join(path, "before")) + stopFunc = startController(ctx) + checkWithWantedResources(path) + cleanUp() + } + AfterEach(func() { suiteConfig, _ := GinkgoConfiguration() if CurrentSpecReport().Failed() && suiteConfig.FailFast { @@ -265,6 +270,55 @@ var _ = Describe("Test TortoiseController", func() { It("TortoisePhaseWorking", func() { runTest(filepath.Join("testdata", "reconcile-for-the-single-container-pod-working")) }) + It("TortoisePhaseWorking (PartlyWorking)", func() { + path := filepath.Join("testdata", "reconcile-for-the-single-container-pod-partly-working") + r := initializeResourcesFromFiles(ctx, k8sClient, filepath.Join(path, "before")) + // We need to dynamically modify the status of the Tortoise object from the file because we need to set time. + r.tortoise.Status.ContainerResourcePhases[0].ResourcePhases[corev1.ResourceCPU] = v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.Now(), + } + + v := &v1beta1.Tortoise{} + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: r.tortoise.Namespace, Name: r.tortoise.Name}, v) + Expect(err).NotTo(HaveOccurred()) + + v.Status = r.tortoise.Status + err = k8sClient.Status().Update(ctx, v) + Expect(err).NotTo(HaveOccurred()) + + stopFunc = startController(ctx) + checkWithWantedResources(path) + cleanUp() + }) + It("TortoisePhaseWorking (GatheringData)", func() { + path := filepath.Join("testdata", "reconcile-for-the-single-container-pod-gathering-data") + r := initializeResourcesFromFiles(ctx, k8sClient, filepath.Join(path, "before")) + // We need to dynamically modify the status of the Tortoise object from the file because we need to set time. + r.tortoise.Status.ContainerResourcePhases[0].ResourcePhases[corev1.ResourceCPU] = v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.Now(), + } + r.tortoise.Status.ContainerResourcePhases[0].ResourcePhases[corev1.ResourceMemory] = v1beta1.ResourcePhase{ + Phase: v1beta1.ContainerResourcePhaseGatheringData, + LastTransitionTime: metav1.Now(), + } + + v := &v1beta1.Tortoise{} + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: r.tortoise.Namespace, Name: r.tortoise.Name}, v) + Expect(err).NotTo(HaveOccurred()) + + v.Status = r.tortoise.Status + err = k8sClient.Status().Update(ctx, v) + Expect(err).NotTo(HaveOccurred()) + + stopFunc = startController(ctx) + checkWithWantedResources(path) + cleanUp() + }) + It("TortoisePhaseWorking (GatheringData is just finished)", func() { + runTest(filepath.Join("testdata", "reconcile-for-the-single-container-pod-gathering-data-finished")) + }) It("TortoisePhaseWorking (dryrun)", func() { runTest(filepath.Join("testdata", "reconcile-for-the-single-container-pod-dryrun")) }) diff --git a/pkg/hpa/service.go b/pkg/hpa/service.go index 4395aedb..ab218602 100644 --- a/pkg/hpa/service.go +++ b/pkg/hpa/service.go @@ -46,7 +46,7 @@ func New(c client.Client, recorder record.EventRecorder, replicaReductionFactor 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) { + if !HasHorizontal(tortoise) { logger.V(4).Info("no horizontal policy, no need to create HPA") return tortoise, nil } @@ -229,7 +229,7 @@ func (c *Service) addHPAMetricsFromTortoiseAutoscalingPolicy(ctx context.Context } func (c *Service) CreateHPA(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment, now time.Time) (*v2.HorizontalPodAutoscaler, *autoscalingv1beta1.Tortoise, error) { - if !hasHorizontal(tortoise) { + if !HasHorizontal(tortoise) { // no need to create HPA return nil, tortoise, nil } @@ -287,7 +287,7 @@ func (c *Service) CreateHPA(ctx context.Context, tortoise *autoscalingv1beta1.To } func (c *Service) GetHPAOnTortoise(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise) (*v2.HorizontalPodAutoscaler, error) { - if !hasHorizontal(tortoise) { + if !HasHorizontal(tortoise) { // there should be no HPA return nil, nil } @@ -299,8 +299,28 @@ func (c *Service) GetHPAOnTortoise(ctx context.Context, tortoise *autoscalingv1b } func (c *Service) ChangeHPAFromTortoiseRecommendation(tortoise *autoscalingv1beta1.Tortoise, hpa *v2.HorizontalPodAutoscaler, now time.Time, recordMetrics bool) (*v2.HorizontalPodAutoscaler, *autoscalingv1beta1.Tortoise, error) { + readyHorizontalResourceAndContainer := sets.New[resourceNameAndContainerName]() + for _, p := range tortoise.Spec.ResourcePolicy { + for rn, ap := range p.AutoscalingPolicy { + if ap == autoscalingv1beta1.AutoscalingTypeHorizontal { + readyHorizontalResourceAndContainer.Insert(resourceNameAndContainerName{rn, p.ContainerName}) + } + } + } + for _, p := range tortoise.Status.ContainerResourcePhases { + for rn, phase := range p.ResourcePhases { + if phase.Phase != autoscalingv1beta1.ContainerResourcePhaseWorking { + readyHorizontalResourceAndContainer.Delete(resourceNameAndContainerName{rn, p.ContainerName}) + } + } + } + for _, t := range tortoise.Status.Recommendations.Horizontal.TargetUtilizations { for resourcename, targetutil := range t.TargetUtilization { + if !readyHorizontalResourceAndContainer.Has(resourceNameAndContainerName{resourcename, t.ContainerName}) { + // this recommendation is not ready. We don't want to apply it. + continue + } metrics.ProposedHPATargetUtilization.WithLabelValues(tortoise.Name, tortoise.Namespace, t.ContainerName, resourcename.String(), hpa.Name).Set(float64(targetutil)) if err := updateHPATargetValue(hpa, t.ContainerName, resourcename, targetutil, len(tortoise.Spec.ResourcePolicy) == 1); err != nil { return nil, tortoise, fmt.Errorf("update HPA from the recommendation from tortoise") @@ -347,7 +367,7 @@ func (c *Service) ChangeHPAFromTortoiseRecommendation(tortoise *autoscalingv1bet } func (c *Service) UpdateHPASpecFromTortoiseAutoscalingPolicy(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, dm *v1.Deployment, now time.Time) (*autoscalingv1beta1.Tortoise, error) { - if !hasHorizontal(tortoise) { + if !HasHorizontal(tortoise) { err := c.DeleteHPACreatedByTortoise(ctx, tortoise) if err != nil && !apierrors.IsNotFound(err) { return tortoise, fmt.Errorf("delete hpa created by tortoise: %w", err) @@ -410,7 +430,7 @@ func (c *Service) UpdateHPASpecFromTortoiseAutoscalingPolicy(ctx context.Context return tortoise, nil } -func hasHorizontal(tortoise *autoscalingv1beta1.Tortoise) bool { +func HasHorizontal(tortoise *autoscalingv1beta1.Tortoise) bool { for _, r := range tortoise.Spec.ResourcePolicy { for _, p := range r.AutoscalingPolicy { if p == autoscalingv1beta1.AutoscalingTypeHorizontal { @@ -423,7 +443,7 @@ func hasHorizontal(tortoise *autoscalingv1beta1.Tortoise) bool { func (c *Service) UpdateHPAFromTortoiseRecommendation(ctx context.Context, tortoise *autoscalingv1beta1.Tortoise, now time.Time) (*v2.HorizontalPodAutoscaler, *autoscalingv1beta1.Tortoise, error) { // if all policy is off or Vertical, we don't update HPA. - if !hasHorizontal(tortoise) { + if !HasHorizontal(tortoise) { return nil, tortoise, nil } diff --git a/pkg/hpa/service_test.go b/pkg/hpa/service_test.go index 5e40bd60..789a1341 100644 --- a/pkg/hpa/service_test.go +++ b/pkg/hpa/service_test.go @@ -60,6 +60,24 @@ func TestClient_UpdateHPAFromTortoiseRecommendation(t *testing.T) { }, }, Status: autoscalingv1beta1.TortoiseStatus{ + ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ + { + ContainerName: "app", + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + }, + }, + { + ContainerName: "istio-proxy", + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + }, + }, + }, Targets: autoscalingv1beta1.TargetsStatus{ HorizontalPodAutoscaler: "hpa", }, @@ -198,6 +216,24 @@ func TestClient_UpdateHPAFromTortoiseRecommendation(t *testing.T) { UpdateMode: autoscalingv1beta1.UpdateModeOff, }, Status: autoscalingv1beta1.TortoiseStatus{ + ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ + { + ContainerName: "app", + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + }, + }, + { + ContainerName: "istio-proxy", + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + }, + }, + }, Targets: autoscalingv1beta1.TargetsStatus{ HorizontalPodAutoscaler: "hpa", }, @@ -313,6 +349,162 @@ func TestClient_UpdateHPAFromTortoiseRecommendation(t *testing.T) { }, wantErr: false, }, + { + name: "no update preformed when ContainerResourcePhases isn't working", + args: args{ + ctx: context.Background(), + tortoise: &autoscalingv1beta1.Tortoise{ + Spec: autoscalingv1beta1.TortoiseSpec{ + ResourcePolicy: []autoscalingv1beta1.ContainerResourcePolicy{ + { + ContainerName: "app", + AutoscalingPolicy: map[v1.ResourceName]v1beta1.AutoscalingType{ + v1.ResourceMemory: v1beta1.AutoscalingTypeHorizontal, + }, + }, + { + ContainerName: "istio-proxy", + AutoscalingPolicy: map[v1.ResourceName]v1beta1.AutoscalingType{ + v1.ResourceCPU: v1beta1.AutoscalingTypeHorizontal, + }, + }, + }, + UpdateMode: autoscalingv1beta1.UpdateModeAuto, + }, + Status: autoscalingv1beta1.TortoiseStatus{ + ContainerResourcePhases: []autoscalingv1beta1.ContainerResourcePhases{ + { + ContainerName: "app", + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceMemory: { + Phase: autoscalingv1beta1.ContainerResourcePhaseWorking, + }, + }, + }, + { + ContainerName: "istio-proxy", + ResourcePhases: map[v1.ResourceName]autoscalingv1beta1.ResourcePhase{ + v1.ResourceCPU: { + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, + }, + }, + }, + }, + Targets: autoscalingv1beta1.TargetsStatus{ + HorizontalPodAutoscaler: "hpa", + }, + Recommendations: autoscalingv1beta1.Recommendations{ + Horizontal: autoscalingv1beta1.HorizontalRecommendations{ + TargetUtilizations: []autoscalingv1beta1.HPATargetUtilizationRecommendationPerContainer{ + { + ContainerName: "app", + TargetUtilization: map[v1.ResourceName]int32{ + v1.ResourceMemory: 90, + }, + }, + { + ContainerName: "istio-proxy", + TargetUtilization: map[v1.ResourceName]int32{ + v1.ResourceCPU: 80, + }, + }, + }, + MaxReplicas: []autoscalingv1beta1.ReplicasRecommendation{ + { + From: 0, + To: 2, + Value: 6, + UpdatedAt: now, + WeekDay: pointer.String(now.Weekday().String()), + }, + }, + MinReplicas: []autoscalingv1beta1.ReplicasRecommendation{ + { + From: 0, + To: 2, + Value: 3, + UpdatedAt: now, + WeekDay: pointer.String(now.Weekday().String()), + }, + }, + }, + }, + }, + }, + now: now.Time, + }, + initialHPA: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "hpa", + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + MinReplicas: ptrInt32(1), + MaxReplicas: 2, + Metrics: []v2.MetricSpec{ + { + Type: v2.ObjectMetricSourceType, + // should be ignored + }, + { + Type: v2.ContainerResourceMetricSourceType, + ContainerResource: &v2.ContainerResourceMetricSource{ + Name: v1.ResourceMemory, + Target: v2.MetricTarget{ + AverageUtilization: pointer.Int32(60), + }, + Container: "app", + }, + }, + { + Type: v2.ContainerResourceMetricSourceType, + ContainerResource: &v2.ContainerResourceMetricSource{ + Name: v1.ResourceCPU, + Target: v2.MetricTarget{ + AverageUtilization: pointer.Int32(50), + }, + Container: "istio-proxy", + }, + }, + }, + }, + }, + want: &v2.HorizontalPodAutoscaler{ + ObjectMeta: metav1.ObjectMeta{ + Name: "hpa", + }, + Spec: v2.HorizontalPodAutoscalerSpec{ + MinReplicas: ptrInt32(3), + MaxReplicas: 6, + Metrics: []v2.MetricSpec{ + { + Type: v2.ObjectMetricSourceType, + // should be ignored + }, + { + Type: v2.ContainerResourceMetricSourceType, + ContainerResource: &v2.ContainerResourceMetricSource{ + Name: v1.ResourceMemory, + Target: v2.MetricTarget{ + AverageUtilization: pointer.Int32(90), // updated + }, + Container: "app", + }, + }, + { + Type: v2.ContainerResourceMetricSourceType, + ContainerResource: &v2.ContainerResourceMetricSource{ + Name: v1.ResourceCPU, + Target: v2.MetricTarget{ + AverageUtilization: pointer.Int32(50), // not updated + }, + Container: "istio-proxy", + }, + }, + }, + }, + }, + wantErr: false, + }, { name: "emergency mode", args: args{ diff --git a/pkg/tortoise/tortoise.go b/pkg/tortoise/tortoise.go index 2ff0458d..1aa45bed 100644 --- a/pkg/tortoise/tortoise.go +++ b/pkg/tortoise/tortoise.go @@ -93,6 +93,9 @@ func (s *Service) UpdateTortoisePhase(tortoise *v1beta1.Tortoise, dm *appv1.Depl if tortoise.Status.TortoisePhase == v1beta1.TortoisePhaseWorking { s.recorder.Event(tortoise, corev1.EventTypeNormal, "Working", "Tortoise finishes gathering data and it starts to work on autoscaling") } + if tortoise.Status.TortoisePhase == v1beta1.TortoisePhasePartlyWorking { + s.recorder.Event(tortoise, corev1.EventTypeNormal, "PartlyWorking", "Tortoise finishes gathering data in some metrics and it starts to work on autoscaling for those metrics. But some metrics are still gathering data") + } case v1beta1.TortoisePhasePartlyWorking: tortoise = s.changeTortoisePhaseWorkingIfTortoiseFinishedGatheringData(tortoise, now) if tortoise.Status.TortoisePhase == v1beta1.TortoisePhaseWorking { @@ -128,7 +131,8 @@ func (s *Service) changeTortoisePhaseWorkingIfTortoiseFinishedGatheringData(tort } } - // At least, MaxReplicas/MinReplicas recommendation are ready. + // Until MaxReplicas/MinReplicas recommendation are ready, + // we never set TortoisePhase to Working or PartlyWorking. someAreGathering := false someAreWorking := false diff --git a/pkg/tortoise/tortoise_test.go b/pkg/tortoise/tortoise_test.go index 7bd49a84..1cdb16ac 100644 --- a/pkg/tortoise/tortoise_test.go +++ b/pkg/tortoise/tortoise_test.go @@ -759,7 +759,7 @@ func TestService_ShouldReconcileTortoiseNow(t *testing.T) { Namespace: "default", }, Spec: v1beta1.TortoiseSpec{ - UpdateMode: v1beta1.AutoUpdateMode, + UpdateMode: v1beta1.UpdateModeAuto, }, }, want: true, @@ -775,7 +775,7 @@ func TestService_ShouldReconcileTortoiseNow(t *testing.T) { Namespace: "default", }, Spec: v1beta1.TortoiseSpec{ - UpdateMode: v1beta1.AutoUpdateMode, + UpdateMode: v1beta1.UpdateModeAuto, }, }, want: false, diff --git a/pkg/vpa/service_test.go b/pkg/vpa/service_test.go index f9385b70..dc0c9273 100644 --- a/pkg/vpa/service_test.go +++ b/pkg/vpa/service_test.go @@ -23,7 +23,7 @@ func TestMakeAllVerticalContainerResourcePhaseRunning(t *testing.T) { want *autoscalingv1beta1.Tortoise }{ { - name: "test1", + name: "modified correctly", args: args{ tortoise: &autoscalingv1beta1.Tortoise{ ObjectMeta: metav1.ObjectMeta{ @@ -68,8 +68,7 @@ func TestMakeAllVerticalContainerResourcePhaseRunning(t *testing.T) { Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, }, v1.ResourceMemory: { - Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, - LastTransitionTime: metav1.Time{}, + Phase: autoscalingv1beta1.ContainerResourcePhaseGatheringData, }, }, },