From 9ba642f2451047f8b0d278c3d7c1a450edc2aa21 Mon Sep 17 00:00:00 2001 From: adinhodovic Date: Wed, 18 Oct 2023 12:42:00 +0200 Subject: [PATCH] feat: Support spec.persistentVolumeClaimRetentionPolicy in kubernetes_stateful_set Signed-off-by: adinhodovic --- ...esource_kubernetes_stateful_set_v1_test.go | 112 ++++++++++++++++++ kubernetes/schema_stateful_set_spec.go | 30 +++++ kubernetes/structures_stateful_set.go | 65 ++++++++++ 3 files changed, 207 insertions(+) diff --git a/kubernetes/resource_kubernetes_stateful_set_v1_test.go b/kubernetes/resource_kubernetes_stateful_set_v1_test.go index 519c4efe78..5516482b75 100644 --- a/kubernetes/resource_kubernetes_stateful_set_v1_test.go +++ b/kubernetes/resource_kubernetes_stateful_set_v1_test.go @@ -78,6 +78,8 @@ func TestAccKubernetesStatefulSetV1_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "spec.0.replicas", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.revision_history_limit", "11"), resource.TestCheckResourceAttr(resourceName, "spec.0.service_name", "ss-test-service"), + resource.TestCheckResourceAttr(resourceName, "spec.0.persistent_volume_claim_retention_policy.0.when_deleted", "Delete"), + resource.TestCheckResourceAttr(resourceName, "spec.0.persistent_volume_claim_retention_policy.0.when_scaled", "Delete"), resource.TestCheckResourceAttr(resourceName, "spec.0.selector.0.match_labels.%", "1"), resource.TestCheckResourceAttr(resourceName, "spec.0.selector.0.match_labels.app", "ss-test"), resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.metadata.#", "1"), @@ -273,6 +275,15 @@ func TestAccKubernetesStatefulSetV1_Update(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.dns_policy", "Default"), ), }, + { + Config: testAccKubernetesStatefulSetV1ConfigUpdatePersistentVolumeClaimRetentionPolicy(name, imageName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesStatefulSetV1Exists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "metadata.0.name", name), + resource.TestCheckResourceAttr(resourceName, "spec.0.persistent_volume_claim_retention_policy.0.when_deleted", "Retain"), + resource.TestCheckResourceAttr(resourceName, "spec.0.persistent_volume_claim_retention_policy.0.when_scaled", "Retain"), + ), + }, }, }) } @@ -482,6 +493,11 @@ func testAccKubernetesStatefulSetV1ConfigBasic(name, imageName string) string { service_name = "ss-test-service" + persistent_volume_claim_retention_policy { + when_deleted = "Delete" + when_scaled = "Delete" + } + template { metadata { labels = { @@ -1130,3 +1146,99 @@ func testAccKubernetesStatefulSetV1ConfigWaitForRollout(name, imageName, waitFor } `, name, imageName, waitForRollout) } + +func testAccKubernetesStatefulSetV1ConfigUpdatePersistentVolumeClaimRetentionPolicy(name, imageName string) string { + return fmt.Sprintf(`resource "kubernetes_stateful_set_v1" "test" { + metadata { + annotations = { + TestAnnotationOne = "one" + TestAnnotationTwo = "two" + } + + labels = { + TestLabelOne = "one" + TestLabelTwo = "two" + TestLabelThree = "three" + } + + name = "%s" + } + + spec { + pod_management_policy = "OrderedReady" + replicas = 1 + revision_history_limit = 11 + + selector { + match_labels = { + app = "ss-test" + } + } + + service_name = "ss-test-service" + + persistent_volume_claim_retention_policy { + when_deleted = "Retain" + when_scaled = "Retain" + } + + template { + metadata { + labels = { + app = "ss-test" + } + } + + spec { + container { + name = "ss-test" + image = %q + args = ["test-webserver"] + + port { + name = "web" + container_port = 80 + } + + readiness_probe { + initial_delay_seconds = 5 + http_get { + path = "/" + port = 80 + } + } + + volume_mount { + name = "ss-test" + mount_path = "/work-dir" + } + } + } + } + + update_strategy { + type = "RollingUpdate" + + rolling_update { + partition = 1 + } + } + + volume_claim_template { + metadata { + name = "ss-test" + } + + spec { + access_modes = ["ReadWriteOnce"] + resources { + requests = { + storage = "1Gi" + } + } + } + } + } +} +`, name, imageName) +} diff --git a/kubernetes/schema_stateful_set_spec.go b/kubernetes/schema_stateful_set_spec.go index ba6856a6be..306b9d1689 100644 --- a/kubernetes/schema_stateful_set_spec.go +++ b/kubernetes/schema_stateful_set_spec.go @@ -104,6 +104,36 @@ func statefulSetSpecFields() map[string]*schema.Schema { Schema: persistentVolumeClaimFields(), }, }, + "persistent_volume_claim_retention_policy": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Description: "The field controls if and how PVCs are deleted during the lifecycle of a StatefulSet.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "when_deleted": { + Type: schema.TypeString, + Description: "This field controls what happens when a Statefulset is deleted. Default is Retain", + Optional: true, + Default: "Retain", + ValidateFunc: validation.StringInSlice([]string{ + "Retain", + "Delete", + }, false), + }, + "when_scaled": { + Type: schema.TypeString, + Description: "This field controls what happens when a Statefulset is scaled. Default is Retain", + Optional: true, + Default: "Retain", + ValidateFunc: validation.StringInSlice([]string{ + "Retain", + "Delete", + }, false), + }, + }, + }, + }, } return s } diff --git a/kubernetes/structures_stateful_set.go b/kubernetes/structures_stateful_set.go index 7461e47840..743a4cb1a0 100644 --- a/kubernetes/structures_stateful_set.go +++ b/kubernetes/structures_stateful_set.go @@ -55,6 +55,14 @@ func expandStatefulSetSpec(s []interface{}) (*v1.StatefulSetSpec, error) { obj.UpdateStrategy = *us } + if v, ok := in["persistent_volume_claim_retention_policy"].([]interface{}); ok { + ret, err := expandStatefulSetSpecPersistentVolumeClaimRetentionPolicy(v) + if err != nil { + return obj, err + } + obj.PersistentVolumeClaimRetentionPolicy = ret + } + template, err := expandPodTemplate(in["template"].([]interface{})) if err != nil { return obj, err @@ -111,6 +119,31 @@ func expandStatefulSetSpecUpdateStrategy(s []interface{}) (*v1.StatefulSetUpdate return ust, nil } +func expandStatefulSetSpecPersistentVolumeClaimRetentionPolicy(s []interface{}) (*v1.StatefulSetPersistentVolumeClaimRetentionPolicy, error) { + retPolicySpec := &v1.StatefulSetPersistentVolumeClaimRetentionPolicy{} + if len(s) == 0 { + return retPolicySpec, nil + } + retPolicy, ok := s[0].(map[string]interface{}) + if !ok { + return retPolicySpec, errors.New("failed to expand 'spec.persistent_volume_claim_retention_policy'") + } + retWd, ok := retPolicy["when_deleted"].(string) + if !ok { + return retPolicySpec, errors.New("failed to expand 'spec.persistent_volume_claim_retention_policy.when_deleted'") + } + retPolicySpec.WhenDeleted = v1.PersistentVolumeClaimRetentionPolicyType(retWd) + + retWs, ok := retPolicy["when_scaled"].(string) + if !ok { + return retPolicySpec, errors.New("failed to expand 'spec.persistent_volume_claim_retention_policy.when_scaled'") + } + retPolicySpec.WhenScaled = v1.PersistentVolumeClaimRetentionPolicyType(retWs) + + log.Printf("[DEBUG] Expanded StatefulSet.Spec.PersistentVolumeClaimRetentionPolicy: %#v", retPolicySpec) + return retPolicySpec, nil +} + func flattenStatefulSetSpec(spec v1.StatefulSetSpec, d *schema.ResourceData, meta interface{}) ([]interface{}, error) { att := make(map[string]interface{}) @@ -143,6 +176,10 @@ func flattenStatefulSetSpec(spec v1.StatefulSetSpec, d *schema.ResourceData, met att["update_strategy"] = flattenStatefulSetSpecUpdateStrategy(spec.UpdateStrategy) } + if spec.PersistentVolumeClaimRetentionPolicy != nil { + att["persistent_volume_claim_retention_policy"] = flattenStatefulSetSpecPersistentVolumeClaimRetentionPolicy(*spec.PersistentVolumeClaimRetentionPolicy) + } + return []interface{}{att}, nil } @@ -189,6 +226,18 @@ func flattenStatefulSetSpecUpdateStrategy(s v1.StatefulSetUpdateStrategy) []inte return []interface{}{att} } +func flattenStatefulSetSpecPersistentVolumeClaimRetentionPolicy(s v1.StatefulSetPersistentVolumeClaimRetentionPolicy) []interface{} { + ret := make(map[string]interface{}) + + if s.WhenDeleted != "" { + ret["when_deleted"] = s.WhenDeleted + } + if s.WhenScaled != "" { + ret["when_scaled"] = s.WhenScaled + } + return []interface{}{ret} +} + // Patchers func patchStatefulSetSpec(d *schema.ResourceData) (PatchOperations, error) { @@ -228,6 +277,22 @@ func patchStatefulSetSpec(d *schema.ResourceData) (PatchOperations, error) { } ops = append(ops, u...) } + + if d.HasChange("spec.0.persistent_volume_claim_retention_policy") { + log.Printf("[TRACE] StatefulSet.Spec.PersistentVolumeClaimRetentionPolicy has changes") + if wd, ok := d.Get("spec.0.persistent_volume_claim_retention_policy.0.when_deleted").(string); ok && wd != "" { + ops = append(ops, &ReplaceOperation{ + Path: "/spec/persistentVolumeClaimRetentionPolicy/whenDeleted", + Value: wd, + }) + } + if ws, ok := d.Get("spec.0.persistent_volume_claim_retention_policy.0.when_scaled").(string); ok && ws != "" { + ops = append(ops, &ReplaceOperation{ + Path: "/spec/persistentVolumeClaimRetentionPolicy/whenScaled", + Value: ws, + }) + } + } return ops, nil }