From 75da873edb216ee3ace2dd88ce0be95a96aa7980 Mon Sep 17 00:00:00 2001 From: Alex Pilon Date: Tue, 27 Feb 2024 14:54:37 -0500 Subject: [PATCH] feat: Add missing topologySpreadConstraints (#2429) * Add minDomains topologySpreadConstraint * Add node affinity and taints policy * Add match_label_keys to topologySpreadConstraint --- .changelog/2429.txt | 3 + kubernetes/resource_kubernetes_pod_v1_test.go | 69 +++++++++++++++++-- kubernetes/schema_pod_spec.go | 30 ++++++++ kubernetes/structures_pod.go | 24 +++++++ website/docs/r/pod.html.markdown | 4 ++ website/docs/r/pod_v1.html.markdown | 4 ++ 6 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 .changelog/2429.txt diff --git a/.changelog/2429.txt b/.changelog/2429.txt new file mode 100644 index 0000000000..f554f5b52a --- /dev/null +++ b/.changelog/2429.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +`resource/kubernetes_pod_v1`: add missing `topology_spread_constraints`: `node_affinity_policy`, `node_taints_policy`, `match_label_keys`, `min_domains` +``` diff --git a/kubernetes/resource_kubernetes_pod_v1_test.go b/kubernetes/resource_kubernetes_pod_v1_test.go index a4daab041b..3a71b90236 100644 --- a/kubernetes/resource_kubernetes_pod_v1_test.go +++ b/kubernetes/resource_kubernetes_pod_v1_test.go @@ -1440,7 +1440,6 @@ func TestAccKubernetesPodV1_topologySpreadConstraint(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) - skipIfClusterVersionGreaterThanOrEqual(t, "1.17.0") }, ProviderFactories: testAccProviderFactories, CheckDestroy: testAccCheckKubernetesPodV1Destroy, @@ -1450,7 +1449,10 @@ func TestAccKubernetesPodV1_topologySpreadConstraint(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( testAccCheckKubernetesPodV1Exists(resourceName, &conf1), resource.TestCheckResourceAttr(resourceName, "spec.0.topology_spread_constraint.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "spec.0.topology_spread_constraint.0.match_label_keys.*", "pod-template-hash"), resource.TestCheckResourceAttr(resourceName, "spec.0.topology_spread_constraint.0.max_skew", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.topology_spread_constraint.0.node_affinity_policy", "Ignore"), + resource.TestCheckResourceAttr(resourceName, "spec.0.topology_spread_constraint.0.node_taints_policy", "Honor"), resource.TestCheckResourceAttr(resourceName, "spec.0.topology_spread_constraint.0.topology_key", "topology.kubernetes.io/zone"), resource.TestCheckResourceAttr(resourceName, "spec.0.topology_spread_constraint.0.when_unsatisfiable", "ScheduleAnyway"), ), @@ -1465,6 +1467,38 @@ func TestAccKubernetesPodV1_topologySpreadConstraint(t *testing.T) { }) } +func TestAccKubernetesPodV1_topologySpreadConstraintMinDomains(t *testing.T) { + var conf1 api.Pod + + podName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "kubernetes_pod_v1.test" + imageName := busyboxImage + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesPodV1Destroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesPodV1TopologySpreadConstraintConfigMinDomains(podName, imageName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesPodV1Exists(resourceName, &conf1), + resource.TestCheckResourceAttr(resourceName, "spec.0.topology_spread_constraint.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.topology_spread_constraint.0.min_domains", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version"}, + }, + }, + }) +} + func TestAccKubernetesPodV1_runtimeClassName(t *testing.T) { var conf1 api.Pod @@ -3219,9 +3253,12 @@ func testAccKubernetesPodV1TopologySpreadConstraintConfig(podName, imageName str name = "containername" } topology_spread_constraint { - max_skew = 1 - topology_key = "topology.kubernetes.io/zone" - when_unsatisfiable = "ScheduleAnyway" + match_label_keys = ["pod-template-hash"] + max_skew = 1 + node_affinity_policy = "Ignore" + node_taints_policy = "Honor" + topology_key = "topology.kubernetes.io/zone" + when_unsatisfiable = "ScheduleAnyway" label_selector { match_labels = { "app.kubernetes.io/instance" = "terraform-example" @@ -3233,6 +3270,30 @@ func testAccKubernetesPodV1TopologySpreadConstraintConfig(podName, imageName str `, podName, imageName) } +func testAccKubernetesPodV1TopologySpreadConstraintConfigMinDomains(podName, imageName string) string { + return fmt.Sprintf(`resource "kubernetes_pod_v1" "test" { + metadata { + name = "%s" + } + spec { + container { + image = "%s" + name = "containername" + } + topology_spread_constraint { + min_domains = 1 + topology_key = "kubernetes.io/hostname" + label_selector { + match_labels = { + "test" = "test" + } + } + } + } +} +`, podName, imageName) +} + func testAccKubernetesPodV1ConfigRuntimeClassName(name, imageName, runtimeHandler string) string { return fmt.Sprintf(`resource "kubernetes_runtime_class_v1" "test" { metadata { diff --git a/kubernetes/schema_pod_spec.go b/kubernetes/schema_pod_spec.go index 2feade4525..c760b58cca 100644 --- a/kubernetes/schema_pod_spec.go +++ b/kubernetes/schema_pod_spec.go @@ -497,6 +497,15 @@ func podSpecFields(isUpdatable, isComputed bool) map[string]*schema.Schema { Description: "describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "match_label_keys": { + Type: schema.TypeSet, + Description: "is a set of pod label keys to select the pods over which spreading will be calculated.", + Optional: true, + ForceNew: !isUpdatable, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, "max_skew": { Type: schema.TypeInt, Description: "describes the degree to which pods may be unevenly distributed.", @@ -504,6 +513,27 @@ func podSpecFields(isUpdatable, isComputed bool) map[string]*schema.Schema { Default: 1, ValidateFunc: validation.IntAtLeast(1), }, + "min_domains": { + Type: schema.TypeInt, + Description: "indicates a minimum number of eligible domains.", + Optional: true, + ForceNew: !isUpdatable, + ValidateFunc: validation.IntAtLeast(1), + }, + "node_affinity_policy": { + Type: schema.TypeString, + Description: "indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew.", + Optional: true, + ForceNew: !isUpdatable, + ValidateFunc: validation.StringInSlice([]string{string(corev1.NodeInclusionPolicyHonor), string(corev1.NodeInclusionPolicyIgnore)}, false), + }, + "node_taints_policy": { + Type: schema.TypeString, + Description: "indicates how we will treat node taints when calculating pod topology spread skew.", + Optional: true, + ForceNew: !isUpdatable, + ValidateFunc: validation.StringInSlice([]string{string(corev1.NodeInclusionPolicyHonor), string(corev1.NodeInclusionPolicyIgnore)}, false), + }, "topology_key": { Type: schema.TypeString, Description: "the key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology.", diff --git a/kubernetes/structures_pod.go b/kubernetes/structures_pod.go index 5d572453b1..eeb5b5d3c3 100644 --- a/kubernetes/structures_pod.go +++ b/kubernetes/structures_pod.go @@ -331,9 +331,21 @@ func flattenTopologySpreadConstraints(tsc []v1.TopologySpreadConstraint) []inter if v.TopologyKey != "" { obj["topology_key"] = v.TopologyKey } + if len(v.MatchLabelKeys) != 0 { + obj["match_label_keys"] = newStringSet(schema.HashString, v.MatchLabelKeys) + } if v.MaxSkew != 0 { obj["max_skew"] = v.MaxSkew } + if v.MinDomains != nil && *v.MinDomains != 0 { + obj["min_domains"] = int(*v.MinDomains) + } + if v.NodeAffinityPolicy != nil && *v.NodeAffinityPolicy != "" { + obj["node_affinity_policy"] = string(*v.NodeAffinityPolicy) + } + if v.NodeTaintsPolicy != nil && *v.NodeAffinityPolicy != "" { + obj["node_taints_policy"] = string(*v.NodeTaintsPolicy) + } if v.WhenUnsatisfiable != "" { obj["when_unsatisfiable"] = string(v.WhenUnsatisfiable) } @@ -1493,9 +1505,21 @@ func expandTopologySpreadConstraints(tsc []interface{}) []v1.TopologySpreadConst ts[i].WhenUnsatisfiable = v1.UnsatisfiableConstraintAction(value) } + if value, ok := m["match_label_keys"].(*schema.Set); ok && value != nil { + ts[i].MatchLabelKeys = sliceOfString(value.List()) + } if value, ok := m["max_skew"].(int); ok { ts[i].MaxSkew = int32(value) } + if value, ok := m["min_domains"].(int); ok && value != 0 { + ts[i].MinDomains = ptr.To(int32(value)) + } + if value, ok := m["node_affinity_policy"].(string); ok && value != "" { + ts[i].NodeAffinityPolicy = ptr.To(v1.NodeInclusionPolicy(value)) + } + if value, ok := m["node_taints_policy"].(string); ok && value != "" { + ts[i].NodeTaintsPolicy = ptr.To(v1.NodeInclusionPolicy(value)) + } } return ts diff --git a/website/docs/r/pod.html.markdown b/website/docs/r/pod.html.markdown index 75a23a8c84..e43d706adf 100644 --- a/website/docs/r/pod.html.markdown +++ b/website/docs/r/pod.html.markdown @@ -863,7 +863,11 @@ The `items` block supports the following: #### Arguments +* `match_label_keys` - (Optional) Is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both `match_label_keys` and `label_selector`. `match_label_keys` cannot be set when `label_selector` isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against `label_selector`. * `max_skew` - (Optional) Describes the degree to which pods may be unevenly distributed. Default value is `1`. +* `min_domains` - (Optional) Indicates a minimum number of eligible domains. Must be number greater than `0`. When set, `when_unsatisfiable` must be set to `DoNotSchedule`. +* `node_affinity_policy` - (Optional) Indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Valid values are `Honor` and `Ignore`. When unset, behavior defaults to `Honor`. +* `node_taints_policy` - (Optional) Indicates how we will treat node taints when calculating pod topology spread skew. Valid values are `Honor` and `Ignore`. When unset, behavior defaults to `Ignore`. * `topology_key` - (Optional) The key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. * `when_unsatisfiable` - (Optional) Indicates how to deal with a pod if it doesn't satisfy the spread constraint. Valid values are `DoNotSchedule` and `ScheduleAnyway`. Default value is `DoNotSchedule`. * `label_selector` - (Optional) A label query over a set of resources, in this case pods. diff --git a/website/docs/r/pod_v1.html.markdown b/website/docs/r/pod_v1.html.markdown index b43826b81d..c45044e4c4 100644 --- a/website/docs/r/pod_v1.html.markdown +++ b/website/docs/r/pod_v1.html.markdown @@ -852,7 +852,11 @@ The `items` block supports the following: #### Arguments +* `match_label_keys` - (Optional) Is a set of pod label keys to select the pods over which spreading will be calculated. The keys are used to lookup values from the incoming pod labels, those key-value labels are ANDed with labelSelector to select the group of existing pods over which spreading will be calculated for the incoming pod. The same key is forbidden to exist in both `match_label_keys` and `label_selector`. `match_label_keys` cannot be set when `label_selector` isn't set. Keys that don't exist in the incoming pod labels will be ignored. A null or empty list means only match against `label_selector`. * `max_skew` - (Optional) Describes the degree to which pods may be unevenly distributed. Default value is `1`. +* `min_domains` - (Optional) Indicates a minimum number of eligible domains. Must be number greater than `0`. When set, `when_unsatisfiable` must be set to `DoNotSchedule`. +* `node_affinity_policy` - (Optional) Indicates how we will treat Pod's nodeAffinity/nodeSelector when calculating pod topology spread skew. Valid values are `Honor` and `Ignore`. When unset, behavior defaults to `Honor`. +* `node_taints_policy` - (Optional) Indicates how we will treat node taints when calculating pod topology spread skew. Valid values are `Honor` and `Ignore`. When unset, behavior defaults to `Ignore`. * `topology_key` - (Optional) The key of node labels. Nodes that have a label with this key and identical values are considered to be in the same topology. * `when_unsatisfiable` - (Optional) Indicates how to deal with a pod if it doesn't satisfy the spread constraint. Valid values are `DoNotSchedule` and `ScheduleAnyway`. Default value is `DoNotSchedule`. * `label_selector` - (Optional) A label query over a set of resources, in this case pods.