Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PodFailurePolicy for Jobs #2394

Merged
merged 32 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
142e81f
initial schema creation
BBBmau Jan 9, 2024
4e1f7f8
add expander functions
BBBmau Jan 10, 2024
9d17978
add flattener functions
BBBmau Jan 10, 2024
7b1aba5
WIP: podFailurePolicy tests
BBBmau Jan 10, 2024
17fae61
finish pod_failure_policy in jobs
BBBmau Jan 12, 2024
f65ebc3
remove typo
BBBmau Jan 12, 2024
29d796e
add changelog-entry
BBBmau Jan 12, 2024
5ee4810
add cronjob podFailurePolicy test
BBBmau Jan 12, 2024
60f11ae
add requested changes
BBBmau Jan 12, 2024
678f7c4
changelog-entry
BBBmau Jan 12, 2024
d5d8838
fix lints
BBBmau Jan 12, 2024
626317e
add podFailurePolicy to uJobv1_update test
BBBmau Jan 12, 2024
3f44cd6
fix jobv1_update test
BBBmau Jan 13, 2024
3f1e447
lint-fix
BBBmau Jan 13, 2024
db5e8de
Merge branch 'main' into add-pod-failure-policy
BBBmau Jan 13, 2024
7165648
add value check in tests cronjobs/jobs
BBBmau Jan 17, 2024
5daf1f1
add value check
BBBmau Jan 17, 2024
00ac84d
update cronjobs/jobs docs
BBBmau Jan 17, 2024
42b59a0
update in docs to be required
BBBmau Jan 17, 2024
9f3a208
use TypeList for values field in on_exit_codes
BBBmau Jan 24, 2024
268cc8f
Merge branch 'main' into add-pod-failure-policy
BBBmau Jan 24, 2024
9dd8b98
WIP: values is empty in fflatten function
BBBmau Jan 24, 2024
a2566cc
fix flatten/expand of exit codes list
jrhouston Jan 24, 2024
31648db
remove sort function
BBBmau Jan 25, 2024
42cc8e8
update values in tests to be in order
BBBmau Jan 25, 2024
bd6db51
update docs / remove unused functions
BBBmau Jan 25, 2024
dd1ce97
Merge branch 'main' into add-pod-failure-policy
BBBmau Jan 26, 2024
2870330
add pod-failure-policy to patchSpec function
BBBmau Jan 29, 2024
fcfa46e
path typo
BBBmau Jan 29, 2024
d4f7248
forceNew
BBBmau Jan 29, 2024
5da45d8
add TestAccKubernetesCronJobV1_minimalWithPodFailurePolicy
BBBmau Jan 30, 2024
3b3ae95
tests-lint-fix
BBBmau Jan 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/2394.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
`resource/kubernetes_job_v1`: add new attribute `spec.pod_failure_policy` to job spec
```
27 changes: 27 additions & 0 deletions kubernetes/resource_kubernetes_cron_job_v1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ func TestAccKubernetesCronJobV1_basic(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.template.0.metadata.0.annotations.controller.kubernetes.io/pod-deletion-cost", "10000"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.template.0.spec.0.container.0.name", "hello"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.template.0.spec.0.container.0.image", imageName),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.#", "2"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.0.action", "FailJob"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.container_name", "hello"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.#", "3"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.0", "1"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.1", "2"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.2", "42"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.1.action", "Ignore"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.1.on_pod_condition.0.type", "DisruptionTarget"),
resource.TestCheckResourceAttr(resourceName, "spec.0.job_template.0.spec.0.pod_failure_policy.0.rule.1.on_pod_condition.0.status", "False"),
),
},
{
Expand Down Expand Up @@ -255,6 +265,23 @@ func testAccKubernetesCronJobV1Config_basic(name, imageName string) string {
}
spec {
backoff_limit = 2
pod_failure_policy {
rule {
action = "FailJob"
on_exit_codes {
container_name = "hello"
operator = "In"
values = [2, 1, 42]
}
}
rule {
action = "Ignore"
on_pod_condition {
status = "False"
type = "DisruptionTarget"
}
}
}
template {
metadata {
annotations = {
Expand Down
51 changes: 51 additions & 0 deletions kubernetes/resource_kubernetes_job_v1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ func TestAccKubernetesJobV1_basic(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.container.0.name", "hello"),
resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.container.0.image", imageName),
resource.TestCheckResourceAttr(resourceName, "wait_for_completion", "false"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.#", "2"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.action", "FailJob"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.container_name", "hello"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.#", "3"),
BBBmau marked this conversation as resolved.
Show resolved Hide resolved
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.0", "1"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.1", "2"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.2", "42"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.1.action", "Ignore"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.1.on_pod_condition.0.type", "DisruptionTarget"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.1.on_pod_condition.0.status", "False"),
),
},
{
Expand Down Expand Up @@ -131,6 +141,13 @@ func TestAccKubernetesJobV1_update(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "spec.0.template.0.spec.0.container.0.image", imageName),
resource.TestCheckResourceAttr(resourceName, "wait_for_completion", "false"),
resource.TestCheckResourceAttr(resourceName, "spec.0.manual_selector", "false"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.#", "2"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.action", "FailJob"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.container_name", "hello"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.0.on_exit_codes.0.values.#", "3"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.1.action", "Ignore"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.1.on_pod_condition.0.type", "DisruptionTarget"),
resource.TestCheckResourceAttr(resourceName, "spec.0.pod_failure_policy.0.rule.1.on_pod_condition.0.status", "False"),
),
},
{
Expand Down Expand Up @@ -307,6 +324,23 @@ func testAccKubernetesJobV1Config_basic(name, imageName string) string {
backoff_limit = 10
completions = 4
parallelism = 2
pod_failure_policy {
rule {
action = "FailJob"
on_exit_codes {
container_name = "hello"
operator = "In"
values = [2, 1, 42]
}
}
rule {
action = "Ignore"
on_pod_condition {
status = "False"
type = "DisruptionTarget"
}
}
}
template {
metadata {}
spec {
Expand Down Expand Up @@ -334,6 +368,23 @@ func testAccKubernetesJobV1Config_updateMutableFields(name, imageName, activeDea
completions = 4
manual_selector = %s
parallelism = %s
pod_failure_policy {
rule {
action = "FailJob"
on_exit_codes {
container_name = "hello"
operator = "In"
values = [2, 1, 42]
}
}
rule {
action = "Ignore"
on_pod_condition {
status = "False"
type = "DisruptionTarget"
}
}
}
template {
metadata {}
spec {
Expand Down
66 changes: 66 additions & 0 deletions kubernetes/schema_job_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,72 @@ func jobSpecFields(specUpdatable bool) map[string]*schema.Schema {
ValidateFunc: validateNonNegativeInteger,
Description: "Specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/",
},
"pod_failure_policy": {
BBBmau marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeList,
Optional: true,
ForceNew: false,
MaxItems: 1,
Description: "Specifies the maximum desired number of pods the job should run at any given time. The actual number of pods running in steady state will be less than this number when ((.spec.completions - .status.successful) < .spec.parallelism), i.e. when the work left to do is less than max parallelism. More info: https://kubernetes.io/docs/concepts/workloads/controllers/jobs-run-to-completion/",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"rule": {
Type: schema.TypeList,
Description: "A label query over volumes to consider for binding.",
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"action": {
Type: schema.TypeString,
Optional: true,
},
"on_exit_codes": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"container_name": {
Type: schema.TypeString,
Optional: true,
},
"operator": {
Type: schema.TypeString,
Optional: true,
},
"values": {
Type: schema.TypeList,
Required: true,
MinItems: 1,
MaxItems: 255,
Elem: &schema.Schema{Type: schema.TypeInt,
ValidateFunc: validation.IntNotInSlice([]int{0})},
},
},
},
},
"on_pod_condition": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"status": {
Type: schema.TypeString,
Optional: true,
Default: "True",
},
"type": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
},
},
},
},
},
// This field is immutable in Jobs.
"selector": {
Type: schema.TypeList,
Expand Down
157 changes: 157 additions & 0 deletions kubernetes/structure_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ package kubernetes
import (
"strconv"

"slices"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
batchv1 "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/utils/ptr"
)

Expand Down Expand Up @@ -38,6 +41,10 @@ func flattenJobV1Spec(in batchv1.JobSpec, d *schema.ResourceData, meta interface
att["parallelism"] = *in.Parallelism
}

if in.PodFailurePolicy != nil {
att["pod_failure_policy"] = flattenPodFailurePolicy(in.PodFailurePolicy)
}

if in.Selector != nil {
att["selector"] = flattenLabelSelector(in.Selector)
}
Expand Down Expand Up @@ -91,6 +98,10 @@ func expandJobV1Spec(j []interface{}) (batchv1.JobSpec, error) {
obj.Parallelism = ptr.To(int32(v))
}

if v, ok := in["pod_failure_policy"].([]interface{}); ok && len(v) > 0 {
obj.PodFailurePolicy = expandPodFailurePolicy(v)
}

if v, ok := in["selector"].([]interface{}); ok && len(v) > 0 {
obj.Selector = expandLabelSelector(v)
}
Expand All @@ -112,6 +123,152 @@ func expandJobV1Spec(j []interface{}) (batchv1.JobSpec, error) {
return obj, nil
}

func expandPodFailurePolicy(l []interface{}) *batchv1.PodFailurePolicy {
obj := &batchv1.PodFailurePolicy{}
if len(l) == 0 || l[0] == nil {
return obj
}
in := l[0].(map[string]interface{})

if v, ok := in["rule"].([]interface{}); ok && len(v) > 0 {
rules := expandPodFailurePolicyRules(v)
obj.Rules = rules
}
return obj
}

func expandPodFailurePolicyRules(l []interface{}) []batchv1.PodFailurePolicyRule {
obj := make([]batchv1.PodFailurePolicyRule, len(l))
if len(l) == 0 || l[0] == nil {
return obj
}
for i, rule := range l {
objRule := &batchv1.PodFailurePolicyRule{}

r := rule.(map[string]interface{})

if v, ok := r["action"].(string); ok && v != "" {
objRule.Action = batchv1.PodFailurePolicyAction(v)
}

if v, ok := r["on_exit_codes"].([]interface{}); ok && len(v) > 0 {
onExitCodes := expandPodFailurePolicyOnExitCodesRequirement(v)
objRule.OnExitCodes = onExitCodes
}

if v, ok := r["on_pod_condition"].([]interface{}); ok && len(v) > 0 {
podConditions := expandPodFailurePolicyOnPodConditionsPattern(v)
objRule.OnPodConditions = podConditions
}

obj[i] = *objRule
}

return obj
}

func expandPodFailurePolicyOnExitCodesRequirement(l []interface{}) *batchv1.PodFailurePolicyOnExitCodesRequirement {
obj := &batchv1.PodFailurePolicyOnExitCodesRequirement{}
if len(l) == 0 || l[0] == nil {
return obj
}
in := l[0].(map[string]interface{})

if v, ok := in["container_name"].(string); ok && v != "" {
obj.ContainerName = &v
}

if v, ok := in["operator"].(string); ok && v != "" {
obj.Operator = batchv1.PodFailurePolicyOnExitCodesOperator(v)
}

if v, ok := in["values"].(*schema.Set); ok && v != nil {
BBBmau marked this conversation as resolved.
Show resolved Hide resolved
v := schemaSetToInt32Array(v)
slices.Sort(v)
obj.Values = v
}

return obj
}

func expandPodFailurePolicyOnPodConditionsPattern(l []interface{}) []batchv1.PodFailurePolicyOnPodConditionsPattern {
obj := make([]batchv1.PodFailurePolicyOnPodConditionsPattern, len(l))
if len(l) == 0 || l[0] == nil {
return obj
}
for i, condition := range l {
objCondition := &batchv1.PodFailurePolicyOnPodConditionsPattern{}
c := condition.(map[string]interface{})
if v, ok := c["status"].(string); ok && v != "" {
objCondition.Status = v1.ConditionStatus(v)
}

if v, ok := c["type"].(string); ok && v != "" {
objCondition.Type = v1.PodConditionType(v)
}
obj[i] = *objCondition
}
return obj
}

func flattenPodFailurePolicy(in *batchv1.PodFailurePolicy) []interface{} {
att := make(map[string]interface{})
if len(in.Rules) > 0 {
att["rule"] = flattenPodFailurePolicyRules(in.Rules)
}
return []interface{}{att}
}

func flattenPodFailurePolicyRules(in []batchv1.PodFailurePolicyRule) []interface{} {
att := make([]interface{}, len(in))

for i, r := range in {
m := make(map[string]interface{})
m["action"] = r.Action
if r.OnExitCodes != nil {
m["on_exit_codes"] = flattenPodFailurePolicyOnExitCodes(r.OnExitCodes)
}
if r.OnPodConditions != nil {
m["on_pod_condition"] = flattenPodFailurePolicyOnPodConditions(r.OnPodConditions)
}
att[i] = m
}

return att
}

func flattenPodFailurePolicyOnExitCodes(in *batchv1.PodFailurePolicyOnExitCodesRequirement) []interface{} {
att := make(map[string]interface{})
if *in.ContainerName != "" {
att["container_name"] = *in.ContainerName
}
att["operator"] = in.Operator
if len(in.Values) > 0 {
att["values"] = newInt32Set(schema.HashSchema(&schema.Schema{
Type: schema.TypeInt,
}), in.Values)
}

return []interface{}{att}
}

func flattenPodFailurePolicyOnPodConditions(in []batchv1.PodFailurePolicyOnPodConditionsPattern) []interface{} {
att := make([]interface{}, len(in))

for i, r := range in {
m := make(map[string]interface{})
if r.Status != "" {
m["status"] = r.Status
}
if r.Type != "" {
m["type"] = r.Type
}
att[i] = m
}

return att
}

func patchJobV1Spec(pathPrefix, prefix string, d *schema.ResourceData) PatchOperations {
ops := make([]PatchOperation, 0)

Expand Down
Loading
Loading