From ffe48f3eb26d730017e522b4db6c3206bac4bbe1 Mon Sep 17 00:00:00 2001 From: Mahesh Rayas Date: Wed, 10 Apr 2024 21:49:54 +1000 Subject: [PATCH 1/5] feat: Ignore downscale/upscale if ignore annotation is set --- src/downscaler/resource/common.rs | 147 +++++++++++++++++------------- 1 file changed, 86 insertions(+), 61 deletions(-) diff --git a/src/downscaler/resource/common.rs b/src/downscaler/resource/common.rs index 4ca18ff..4538942 100644 --- a/src/downscaler/resource/common.rs +++ b/src/downscaler/resource/common.rs @@ -23,80 +23,105 @@ pub struct ScalingMachinery { } impl ScalingMachinery { + fn should_downscale(&self) -> bool { + if let Some(annotations) = self.annotations.as_ref() { + if let Some(is_downscaled) = annotations.get("kubesaver.com/is_downscaled") { + if is_downscaled == "false" { + return true; + } + } + } + false + } + + fn should_upscale(&self) -> Option { + if let Some(annotations) = self.annotations.as_ref() { + if let Some(is_downscaled) = annotations.get("kubesaver.com/is_downscaled") { + if is_downscaled == "true" { + if let Some(original_count) = annotations.get("kubesaver.com/original_count") { + if let Ok(scale_up) = original_count.parse::() { + return Some(scale_up); + } + } + } + } + } + None + } + + async fn action_for_downscale(&self, c: Client) -> Result, Error> { + info!("downscaling {} : {}", &self.resource_type, &self.name); + let patch_result = self + .patching( + c.clone(), + &self.original_replicas, + self.tobe_replicas, + "true", + self.scale_state.clone(), + ) + .await?; + Ok(Some(patch_result)) + } + + async fn action_for_upscale( + &self, + c: Client, + scale_up: i32, + ) -> Result, Error> { + info!("upscaling {} : {}", &self.resource_type, &self.name); + let patch_result = self + .patching( + c.clone(), + &scale_up.to_string(), + Some(scale_up), + "false", + self.scale_state.clone(), + ) + .await?; + Ok(Some(patch_result)) + } + + fn should_downscale_first_time(&self) -> bool { + self.annotations.is_none() + || self + .annotations + .as_ref() + .map_or(true, |a| a.get("kubesaver.com/is_downscaled").is_none()) + } + pub async fn scaling_machinery( &self, c: Client, is_uptime: bool, ) -> Result, Error> { + // check if the resource has an annotation kubesaver.com/ignore:"true" + if let Some(ignore_annotations) = self + .annotations + .as_ref() + .and_then(|a| a.get("kubesaver.com/ignore")) + { + if ignore_annotations.eq("true") { + return Ok(None); + } + } if !is_uptime { - // check if the resource has annotations - if self.annotations.is_none() - || self - .annotations - .to_owned() - .unwrap() - .get("kubesaver.com/is_downscaled") - .is_none() - { - // first time action - info!("downscaling {} : {}", &self.resource_type, &self.name,); - return Ok(Some( - self.patching( + if self.should_downscale_first_time() { + info!("downscaling {} : {}", &self.resource_type, &self.name); + let patch_result = self + .patching( c.clone(), &self.original_replicas, self.tobe_replicas, "true", self.scale_state.clone(), ) - .await?, - )); - } else if let Some(x) = self - .annotations - .as_ref() - .unwrap() - .get("kubesaver.com/is_downscaled") - { - // if the resources are already upscaled by the kube-saver and now its the time to be downscaled - if x == "false" { - info!("downscaling {} : {}", &self.resource_type, &self.name); - return Ok(Some( - self.patching( - c.clone(), - &self.original_replicas, - self.tobe_replicas, - "true", - self.scale_state.clone(), - ) - .await?, - )); - } - } - } else { - // its a uptime - // should be up and running - // check if annotation is true - let y = self.annotations.as_ref().unwrap(); - if let Some(x) = y.get("kubesaver.com/is_downscaled") { - let scale_up: i32 = y - .get("kubesaver.com/original_count") - .unwrap() - .parse() - .unwrap(); - if x == "true" { - info!("upscaling {} : {} ", &self.resource_type, &self.name); - // this is needed becoz the next day I want to downscale after the end time - return Ok(Some( - self.patching( - c.clone(), - &scale_up.to_string(), // after scaleup, keep the kubesaver.com/original_count as the real non-zero count. - Some(scale_up), - "false", - self.scale_state.clone(), - ) - .await?, - )); - } + .await?; + return Ok(Some(patch_result)); + } else if self.should_downscale() { + return self.action_for_downscale(c.clone()).await; } + } else if let Some(scale_up) = self.should_upscale() { + return self.action_for_upscale(c, scale_up).await; } Ok(None) } From 37fe6d2530bac2e617c01e0d5795915e2071c1a2 Mon Sep 17 00:00:00 2001 From: Mahesh Rayas Date: Wed, 10 Apr 2024 21:58:58 +1000 Subject: [PATCH 2/5] test if its working, tests should fail --- tests/data/test-deploy1.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/data/test-deploy1.yaml b/tests/data/test-deploy1.yaml index 0cd6984..3750c84 100644 --- a/tests/data/test-deploy1.yaml +++ b/tests/data/test-deploy1.yaml @@ -8,6 +8,8 @@ kind: Deployment metadata: namespace: kuber1 name: test-kuber1-deploy1 + annotations: + kubesaver.com/ignore: true labels: app: go-app-kuber1 spec: From 791b8311134ad68bd9c56c4847141588235c0f05 Mon Sep 17 00:00:00 2001 From: Mahesh Rayas Date: Wed, 10 Apr 2024 22:01:07 +1000 Subject: [PATCH 3/5] test if its working, tests should fail --- tests/data/test-deploy1.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/test-deploy1.yaml b/tests/data/test-deploy1.yaml index 3750c84..02020e5 100644 --- a/tests/data/test-deploy1.yaml +++ b/tests/data/test-deploy1.yaml @@ -9,7 +9,7 @@ metadata: namespace: kuber1 name: test-kuber1-deploy1 annotations: - kubesaver.com/ignore: true + kubesaver.com/ignore: "true" labels: app: go-app-kuber1 spec: From 444e381feddab6804e19ecd9a9aa8c88d47e2604 Mon Sep 17 00:00:00 2001 From: Mahesh Rayas Date: Wed, 10 Apr 2024 22:07:54 +1000 Subject: [PATCH 4/5] add new test case --- tests/data/test-deploy1.yaml | 2 -- tests/data/test-ignore-downscale-14.yaml | 30 ++++++++++++++++++++++++ tests/downscaler/processor.rs | 17 ++++++++++++++ tests/rules/rules14.yaml | 7 ++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 tests/data/test-ignore-downscale-14.yaml create mode 100644 tests/rules/rules14.yaml diff --git a/tests/data/test-deploy1.yaml b/tests/data/test-deploy1.yaml index 02020e5..0cd6984 100644 --- a/tests/data/test-deploy1.yaml +++ b/tests/data/test-deploy1.yaml @@ -8,8 +8,6 @@ kind: Deployment metadata: namespace: kuber1 name: test-kuber1-deploy1 - annotations: - kubesaver.com/ignore: "true" labels: app: go-app-kuber1 spec: diff --git a/tests/data/test-ignore-downscale-14.yaml b/tests/data/test-ignore-downscale-14.yaml new file mode 100644 index 0000000..7b6f8c8 --- /dev/null +++ b/tests/data/test-ignore-downscale-14.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: kuber14 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + namespace: kuber14 + name: test-kuber14-deploy1 + annotations: + kubesaver.com/ignore: "true" + labels: + app: go-app-kuber14 +spec: + replicas: 2 + selector: + matchLabels: + app: go-app + template: + metadata: + labels: + app: go-app + spec: + containers: + - name: go-app + image: maheshrayas/goapp:1.0 + ports: + - containerPort: 8090 +--- \ No newline at end of file diff --git a/tests/downscaler/processor.rs b/tests/downscaler/processor.rs index 490797e..dd8652f 100644 --- a/tests/downscaler/processor.rs +++ b/tests/downscaler/processor.rs @@ -348,3 +348,20 @@ async fn test4_hpa_scale_all_resources_replicas_1() { //Deployment must be scaled back to original replicas assert_eq!(d.spec.unwrap().replicas, Some(3)); } + +#[tokio::test] +async fn test5_check_if_ignored() { + let f = File::open("tests/rules/rules14.yaml").unwrap(); + let r: Rules = serde_yaml::from_reader(f).unwrap(); + let client = Client::try_default() + .await + .expect("Failed to read kubeconfig"); + r.process_rules(client.clone(), None, None, SCALED_STATE.clone()) + .await + .ok(); + // kube-saver must ignore + let api: Api = Api::namespaced(client.clone(), "kuber14"); + let d = api.get("test-kuber14-deploy1").await.unwrap(); + assert_eq!(d.spec.unwrap().replicas, Some(0)); + +} diff --git a/tests/rules/rules14.yaml b/tests/rules/rules14.yaml new file mode 100644 index 0000000..3a3b749 --- /dev/null +++ b/tests/rules/rules14.yaml @@ -0,0 +1,7 @@ +rules: + - id: rules-downscale-kuber14 + uptime: Mon-Sun 22:59-23:00 Australia/Sydney + jmespath: "metadata.name == 'kuber14'" + resource: + - Namespace + replicas: 0 \ No newline at end of file From d9ef0b347e7cd9db5008268374cb1238acb4b1db Mon Sep 17 00:00:00 2001 From: Mahesh Rayas Date: Wed, 10 Apr 2024 22:15:15 +1000 Subject: [PATCH 5/5] feat: add the correct replicas in test --- tests/downscaler/processor.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/downscaler/processor.rs b/tests/downscaler/processor.rs index dd8652f..fded327 100644 --- a/tests/downscaler/processor.rs +++ b/tests/downscaler/processor.rs @@ -362,6 +362,5 @@ async fn test5_check_if_ignored() { // kube-saver must ignore let api: Api = Api::namespaced(client.clone(), "kuber14"); let d = api.get("test-kuber14-deploy1").await.unwrap(); - assert_eq!(d.spec.unwrap().replicas, Some(0)); - + assert_eq!(d.spec.unwrap().replicas, Some(2)); }