Skip to content

Commit 8ba8201

Browse files
committed
feat: Support wait for StatefulSet
1 parent 2333640 commit 8ba8201

File tree

14 files changed

+257
-9
lines changed

14 files changed

+257
-9
lines changed

docs/resources/resource.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ resource "kustomization_resource" "p0" {
6868
6969
# then loop through resources in ids_prio[1]
7070
# and set an explicit depends_on on kustomization_resource.p0
71-
# wait 2 minutes for any deployment or daemonset to become ready
71+
# wait 2 minutes for any deployment, statefulset or daemonset to become ready
7272
resource "kustomization_resource" "p1" {
7373
for_each = data.kustomization_build.test.ids_prio[1]
7474
@@ -105,5 +105,5 @@ resource "kustomization_resource" "p2" {
105105
## Argument Reference
106106

107107
- `manifest` - (Required) JSON encoded Kubernetes resource manifest.
108-
- `wait` - Whether to wait for pods to become ready (default false). Currently only has an effect for Deployments and DaemonSets.
108+
- `wait` - Whether to wait for pods to become ready (default false). Currently only has an effect for Deployments, StatefulSets and DaemonSets.
109109
- 'timeouts' - (Optional) Overwrite `create`, `update` or `delete` timeout defaults. Defaults are 5 minutes for `create` and `update` and 10 minutes for `delete`.

kustomize/manifest.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import (
2323
)
2424

2525
var waitRefreshFunctions = map[string]waitRefreshFunction{
26-
"apps/Deployment": waitDeploymentRefresh,
27-
"apps/Daemonset": waitDaemonsetRefresh,
26+
"apps/Deployment": waitDeploymentRefresh,
27+
"apps/Daemonset": waitDaemonsetRefresh,
28+
"apps/StatefulSet": waitStatefulSetRefresh,
2829
}
2930

3031
type kManifestId struct {
@@ -429,6 +430,38 @@ func waitDeploymentRefresh(km *kManifest) (interface{}, string, error) {
429430
return nil, "in progress", nil
430431
}
431432

433+
func statefulSetReady(u *k8sunstructured.Unstructured) (bool, error) {
434+
var statefulSet k8sappsv1.StatefulSet
435+
if err := k8sruntime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), &statefulSet); err != nil {
436+
return false, err
437+
}
438+
if statefulSet.Generation == statefulSet.Status.ObservedGeneration &&
439+
statefulSet.Status.AvailableReplicas == *statefulSet.Spec.Replicas &&
440+
statefulSet.Status.AvailableReplicas == statefulSet.Status.Replicas {
441+
return true, nil
442+
} else {
443+
return false, nil
444+
}
445+
}
446+
447+
func waitStatefulSetRefresh(km *kManifest) (interface{}, string, error) {
448+
resp, err := km.apiGet(k8smetav1.GetOptions{})
449+
if err != nil {
450+
if k8serrors.IsNotFound(err) {
451+
return nil, "missing", nil
452+
}
453+
return nil, "error", err
454+
}
455+
ready, err := statefulSetReady(resp)
456+
if err != nil {
457+
return nil, "error", err
458+
}
459+
if ready {
460+
return resp, "done", nil
461+
}
462+
return nil, "in progress", nil
463+
}
464+
432465
func (km *kManifest) waitCreatedOrUpdated(t time.Duration) error {
433466
gvk := km.gvk()
434467
if refresh, ok := waitRefreshFunctions[fmt.Sprintf("%s/%s", gvk.Group, gvk.Kind)]; ok {

kustomize/resource_kustomization_test.go

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -483,24 +483,26 @@ func TestAccResourceKustomization_wait(t *testing.T) {
483483
Steps: []resource.TestStep{
484484
//
485485
//
486-
// Applying initial config with a svc and deployment in a namespace with wait
486+
// Applying initial config with a deployment and statefulset in a namespace with wait
487487
{
488488
Config: testAccResourceKustomizationConfig_wait("test_kustomizations/wait/initial"),
489489
Check: resource.ComposeAggregateTestCheckFunc(
490490
assertDurationIsShorterThan(now, 5*time.Minute),
491491
testAccCheckManifestNestedString("kustomization_resource.dep1", "test", "spec", "selector", "matchLabels", "app"),
492492
testAccCheckDeploymentReady("kustomization_resource.dep1", "test-wait", "test"),
493+
testAccCheckStatefulSetReady("kustomization_resource.state1", "test-wait", "test-statefulset"),
493494
),
494495
},
495496
//
496497
//
497-
// Applying modified config updating the deployment annotation with wait
498+
// Applying modified config updating the deployment and statefulset annotation with wait
498499
{
499500
Config: testAccResourceKustomizationConfig_wait("test_kustomizations/wait/modified"),
500501
Check: resource.ComposeAggregateTestCheckFunc(
501502
assertDurationIsShorterThan(now, 1*time.Minute),
502503
testAccCheckManifestNestedString("kustomization_resource.dep1", "this will cause a redeploy", "spec", "template", "metadata", "annotations", "new"),
503504
testAccCheckDeploymentReady("kustomization_resource.dep1", "test-wait", "test"),
505+
testAccCheckStatefulSetReady("kustomization_resource.state1", "test-wait", "test-statefulset"),
504506
),
505507
},
506508
},
@@ -520,6 +522,14 @@ resource "kustomization_resource" "dep1" {
520522
update = "1m"
521523
}
522524
}
525+
resource "kustomization_resource" "state1" {
526+
manifest = data.kustomization_build.test.manifests["apps/StatefulSet/test-wait/test-statefulset"]
527+
wait = true
528+
timeouts {
529+
create = "1m"
530+
update = "1m"
531+
}
532+
}
523533
`
524534
}
525535

@@ -548,6 +558,7 @@ func TestAccResourceKustomization_add_wait(t *testing.T) {
548558
assertDurationIsShorterThan(now, 1*time.Minute),
549559
testAccCheckManifestNestedString("kustomization_resource.dep1", "test", "spec", "selector", "matchLabels", "app"),
550560
testAccCheckDeploymentReady("kustomization_resource.dep1", "test-wait-change", "test"),
561+
testAccCheckStatefulSetReady("kustomization_resource.state1", "test-wait-change", "test-statefulset"),
551562
),
552563
},
553564
},
@@ -562,6 +573,9 @@ resource "kustomization_resource" "ns" {
562573
resource "kustomization_resource" "dep1" {
563574
manifest = data.kustomization_build.test.manifests["apps/Deployment/test-wait-change/test"]
564575
}
576+
resource "kustomization_resource" "state1" {
577+
manifest = data.kustomization_build.test.manifests["apps/StatefulSet/test-wait-change/test-statefulset"]
578+
}
565579
`
566580
}
567581

@@ -578,6 +592,14 @@ resource "kustomization_resource" "dep1" {
578592
update = "1m"
579593
}
580594
}
595+
resource "kustomization_resource" "state1" {
596+
manifest = data.kustomization_build.test.manifests["apps/StatefulSet/test-wait-change/test-statefulset"]
597+
wait = true
598+
timeouts {
599+
create = "1m"
600+
update = "1m"
601+
}
602+
}
581603
`
582604
}
583605

@@ -618,32 +640,71 @@ resource "kustomization_resource" "dep1" {
618640
`
619641
}
620642

621-
func TestAccResourceKustomization_nowait(t *testing.T) {
643+
func TestAccResourceKustomization_wait_failure_statefulset(t *testing.T) {
644+
now := time.Now()
622645

623646
resource.Test(t, resource.TestCase{
624647
//PreCheck: func() { testAccPreCheck(t) },
625648
Providers: testAccProviders,
626649
Steps: []resource.TestStep{
627650
//
628651
//
629-
// Applying initial config with a svc and deployment in a namespace without wait
652+
// Applying initial config with a failing statefulset in a namespace with wait
653+
{
654+
Config: testAccResourceKustomizationConfig_wait_failure_statefulset("test_kustomizations/wait-fail-statefulset/initial"),
655+
Check: resource.ComposeAggregateTestCheckFunc(
656+
testAccCheckStatefulSetNotReady("kustomization_resource.state1", "test-wait-fail-statefulset", "test-statefulset"),
657+
assertDurationIsLongerThan(now, 1*time.Minute),
658+
),
659+
ExpectError: regexp.MustCompile("timed out creating/updating StatefulSet test-wait-fail-statefulset/test-statefulset:"),
660+
},
661+
},
662+
})
663+
}
664+
665+
func testAccResourceKustomizationConfig_wait_failure_statefulset(path string) string {
666+
return testAccDataSourceKustomizationConfig_basic(path) + `
667+
resource "kustomization_resource" "ns" {
668+
manifest = data.kustomization_build.test.manifests["_/Namespace/_/test-wait-fail-statefulset"]
669+
}
670+
resource "kustomization_resource" "state1" {
671+
manifest = data.kustomization_build.test.manifests["apps/StatefulSet/test-wait-fail-statefulset/test-statefulset"]
672+
wait = true
673+
timeouts {
674+
create = "1m"
675+
}
676+
}
677+
`
678+
}
679+
680+
func TestAccResourceKustomization_nowait(t *testing.T) {
681+
resource.Test(t, resource.TestCase{
682+
//PreCheck: func() { testAccPreCheck(t) },
683+
Providers: testAccProviders,
684+
Steps: []resource.TestStep{
685+
//
686+
//
687+
// Applying initial config with a svc and deployment and statefulset in a namespace without wait
630688
// so shouldn't exist immediately after creation
631689
{
632690
Config: testAccResourceKustomizationConfig_nowait("test_kustomizations/nowait/initial"),
633691
Check: resource.ComposeAggregateTestCheckFunc(
634692
testAccCheckManifestNestedString("kustomization_resource.dep1", "test", "spec", "selector", "matchLabels", "app"),
635693
testAccCheckDeploymentNotReady("kustomization_resource.dep1", "test-nowait", "test"),
694+
testAccCheckStatefulSetNotReady("kustomization_resource.state1", "test-nowait", "test-statefulset"),
636695
),
637696
},
638697
//
639698
//
640-
// Applying modified config updating the deployment annotation without wait,
699+
// Applying modified config updating the deployment and statefulset annotation without wait,
641700
// so we don't immediately expect the annotation to be present
642701
{
643702
Config: testAccResourceKustomizationConfig_nowait("test_kustomizations/nowait/modified"),
644703
Check: resource.ComposeAggregateTestCheckFunc(
645704
testAccCheckManifestNestedString("kustomization_resource.dep1", "this will cause a redeploy", "spec", "template", "metadata", "annotations", "new"),
646705
testAccCheckDeploymentNotReady("kustomization_resource.dep1", "test-nowait", "test"),
706+
testAccCheckManifestNestedString("kustomization_resource.state1", "this will cause statefulset to redeploy", "spec", "template", "metadata", "annotations", "new"),
707+
testAccCheckStatefulSetNotReady("kustomization_resource.state1", "test-nowait", "test-statefulset"),
647708
),
648709
},
649710
},
@@ -659,6 +720,9 @@ resource "kustomization_resource" "ns" {
659720
resource "kustomization_resource" "dep1" {
660721
manifest = data.kustomization_build.test.manifests["apps/Deployment/test-nowait/test"]
661722
}
723+
resource "kustomization_resource" "state1" {
724+
manifest = data.kustomization_build.test.manifests["apps/StatefulSet/test-nowait/test-statefulset"]
725+
}
662726
`
663727
}
664728

@@ -1122,6 +1186,50 @@ func testAccCheckDeploymentNotReady(n string, namespace string, name string) res
11221186
}
11231187
}
11241188

1189+
func testAccCheckStatefulSetReady(n string, namespace string, name string) resource.TestCheckFunc {
1190+
return func(s *terraform.State) error {
1191+
u, err := getResourceFromTestState(s, n)
1192+
if err != nil {
1193+
return err
1194+
}
1195+
1196+
resp, err := getResourceFromK8sAPI(u)
1197+
if err != nil {
1198+
return err
1199+
}
1200+
ready, err := statefulSetReady(resp)
1201+
if err != nil {
1202+
return err
1203+
}
1204+
if !ready {
1205+
return fmt.Errorf("statefulset %s in %s not ready", name, namespace)
1206+
}
1207+
return nil
1208+
}
1209+
}
1210+
1211+
func testAccCheckStatefulSetNotReady(n string, namespace string, name string) resource.TestCheckFunc {
1212+
return func(s *terraform.State) error {
1213+
u, err := getResourceFromTestState(s, n)
1214+
if err != nil {
1215+
return err
1216+
}
1217+
1218+
resp, err := getResourceFromK8sAPI(u)
1219+
if err != nil {
1220+
return err
1221+
}
1222+
ready, err := statefulSetReady(resp)
1223+
if err != nil {
1224+
return err
1225+
}
1226+
if ready {
1227+
return fmt.Errorf("statefulset %s in %s unexpectedly ready", name, namespace)
1228+
}
1229+
return nil
1230+
}
1231+
}
1232+
11251233
func getResourceFromTestState(s *terraform.State, n string) (ur *k8sunstructured.Unstructured, err error) {
11261234
rs, ok := s.RootModule().Resources[n]
11271235
if !ok {

kustomize/test_kustomizations/nowait/initial/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ namespace: test-nowait
55

66
resources:
77
- namespace.yaml
8+
- statefulset.yaml
89
- ../../_example_app
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: apps/v1
2+
kind: StatefulSet
3+
metadata:
4+
labels:
5+
app: test-statefulset
6+
name: test-statefulset
7+
spec:
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: test-statefulset
12+
template:
13+
metadata:
14+
labels:
15+
app: test-statefulset
16+
spec:
17+
containers:
18+
- image: nginx
19+
name: nginx

kustomize/test_kustomizations/nowait/modified/kustomization.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,11 @@ patches:
1313
path: /spec/template/metadata/annotations
1414
value:
1515
new: this will cause a redeploy
16+
- target:
17+
kind: StatefulSet
18+
name: test-statefulset
19+
patch: |
20+
- op: add
21+
path: /spec/template/metadata/annotations
22+
value:
23+
new: this will cause statefulset to redeploy

kustomize/test_kustomizations/wait-change/initial/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ namespace: test-wait-change
55

66
resources:
77
- namespace.yaml
8+
- statefulset.yaml
89
- ../../_example_app
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
apiVersion: apps/v1
2+
kind: StatefulSet
3+
metadata:
4+
labels:
5+
app: test-statefulset
6+
name: test-statefulset
7+
spec:
8+
replicas: 1
9+
selector:
10+
matchLabels:
11+
app: test-statefulset
12+
template:
13+
metadata:
14+
labels:
15+
app: test-statefulset
16+
spec:
17+
containers:
18+
- image: nginx
19+
name: nginx
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
4+
namespace: test-wait-fail-statefulset
5+
6+
resources:
7+
- namespace.yaml
8+
- statefulset.yaml
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: test-wait-fail-statefulset

0 commit comments

Comments
 (0)