Skip to content

Commit 694ae5a

Browse files
jrhoustondak1n1
andauthored
Add wait_for_rollout to kubernetes_stateful_set (#914)
Co-authored-by: Stef Forrester <[email protected]>
1 parent 33b550c commit 694ae5a

File tree

520 files changed

+80488
-2101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

520 files changed

+80488
-2101
lines changed

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,11 @@ require (
2525
github.com/terraform-providers/terraform-provider-random v1.3.2-0.20190925210718-83518d96ae4f // indirect
2626
github.com/ulikunitz/xz v0.5.6 // indirect
2727
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
28-
k8s.io/api v0.0.0-20191025225708-5524a3672fbb
29-
k8s.io/apimachinery v0.0.0-20191025225532-af6325b3a843
28+
k8s.io/api v0.16.12
29+
k8s.io/apimachinery v0.16.12
3030
k8s.io/client-go v10.0.0+incompatible
3131
k8s.io/kube-aggregator v0.0.0-20191025230902-aa872b06629d
32+
k8s.io/kubectl v0.16.12
3233
)
3334

3435
// Override invalid go-autorest pseudo-version. This can be removed once

go.sum

Lines changed: 63 additions & 2 deletions
Large diffs are not rendered by default.

kubernetes/resource_kubernetes_stateful_set.go

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import (
66

77
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
88
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
9-
api "k8s.io/api/apps/v1"
9+
appsv1 "k8s.io/api/apps/v1"
1010
"k8s.io/apimachinery/pkg/api/errors"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12-
pkgApi "k8s.io/apimachinery/pkg/types"
12+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
13+
"k8s.io/apimachinery/pkg/runtime"
14+
"k8s.io/apimachinery/pkg/types"
15+
kubernetes "k8s.io/client-go/kubernetes"
16+
polymorphichelpers "k8s.io/kubectl/pkg/polymorphichelpers"
1317
)
1418

1519
func resourceKubernetesStatefulSet() *schema.Resource {
@@ -35,6 +39,12 @@ func resourceKubernetesStatefulSet() *schema.Resource {
3539
Schema: statefulSetSpecFields(false),
3640
},
3741
},
42+
"wait_for_rollout": {
43+
Type: schema.TypeBool,
44+
Description: "Wait for the rollout of the stateful set to complete. Defaults to false.",
45+
Default: false,
46+
Optional: true,
47+
},
3848
},
3949
}
4050
}
@@ -49,7 +59,7 @@ func resourceKubernetesStatefulSetCreate(d *schema.ResourceData, meta interface{
4959
if err != nil {
5060
return err
5161
}
52-
statefulSet := api.StatefulSet{
62+
statefulSet := appsv1.StatefulSet{
5363
ObjectMeta: metadata,
5464
Spec: *spec,
5565
}
@@ -67,6 +77,14 @@ func resourceKubernetesStatefulSetCreate(d *schema.ResourceData, meta interface{
6777

6878
log.Printf("[INFO] StatefulSet %s created", id)
6979

80+
if d.Get("wait_for_rollout").(bool) {
81+
log.Printf("[INFO] Waiting for StatefulSet %s to rollout", id)
82+
namespace := out.ObjectMeta.Namespace
83+
name := out.ObjectMeta.Name
84+
return resource.Retry(d.Timeout(schema.TimeoutCreate),
85+
retryUntilStatefulSetRolloutComplete(conn, namespace, name))
86+
}
87+
7088
return resourceKubernetesStatefulSetRead(d, meta)
7189
}
7290

@@ -156,12 +174,18 @@ func resourceKubernetesStatefulSetUpdate(d *schema.ResourceData, meta interface{
156174
return fmt.Errorf("Failed to marshal update operations for StatefulSet: %s", err)
157175
}
158176
log.Printf("[INFO] Updating StatefulSet %q: %v", name, string(data))
159-
out, err := conn.AppsV1().StatefulSets(namespace).Patch(name, pkgApi.JSONPatchType, data)
177+
out, err := conn.AppsV1().StatefulSets(namespace).Patch(name, types.JSONPatchType, data)
160178
if err != nil {
161179
return fmt.Errorf("Failed to update StatefulSet: %s", err)
162180
}
163181
log.Printf("[INFO] Submitted updated StatefulSet: %#v", out)
164182

183+
if d.Get("wait_for_rollout").(bool) {
184+
log.Printf("[INFO] Waiting for StatefulSet %s to rollout", d.Id())
185+
return resource.Retry(d.Timeout(schema.TimeoutCreate),
186+
retryUntilStatefulSetRolloutComplete(conn, namespace, name))
187+
}
188+
165189
return resourceKubernetesStatefulSetRead(d, meta)
166190
}
167191

@@ -203,3 +227,46 @@ func resourceKubernetesStatefulSetDelete(d *schema.ResourceData, meta interface{
203227

204228
return nil
205229
}
230+
231+
// retryUntilStatefulSetRolloutComplete checks if a given job finished its execution and is either in 'Complete' or 'Failed' state.
232+
func retryUntilStatefulSetRolloutComplete(conn *kubernetes.Clientset, ns, name string) resource.RetryFunc {
233+
return func() *resource.RetryError {
234+
res, err := conn.AppsV1().StatefulSets(ns).Get(name, metav1.GetOptions{})
235+
if err != nil {
236+
return resource.NonRetryableError(err)
237+
}
238+
239+
// NOTE: This is what kubectl uses to determine if a rollout is done.
240+
// We are using this here because the logic for determining if a StatefulSet
241+
// is done is gnarly and we don't want to duplicate it in the provider.
242+
gvk := appsv1.SchemeGroupVersion.WithKind("StatefulSet")
243+
gk := gvk.GroupKind()
244+
statusViewer, err := polymorphichelpers.StatusViewerFor(gk)
245+
if err != nil {
246+
return resource.NonRetryableError(err)
247+
}
248+
249+
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(res)
250+
if err != nil {
251+
return resource.NonRetryableError(err)
252+
}
253+
254+
// NOTE: For some reason, the Kind and apiVersion get lost when converting to unstructured.
255+
obj["apiVersion"] = gvk.GroupVersion().String()
256+
obj["kind"] = gvk.Kind
257+
u := unstructured.Unstructured{Object: obj}
258+
259+
// NOTE: the revision parameter of the Status function below is not actually used.
260+
// for StatefulSet so it is set to 0 here
261+
_, done, err := statusViewer.Status(&u, 0)
262+
if err != nil {
263+
return resource.NonRetryableError(err)
264+
}
265+
266+
if done {
267+
return nil
268+
}
269+
270+
return resource.RetryableError(fmt.Errorf("StatefulSet %s/%s is not finished rolling out", ns, name))
271+
}
272+
}

kubernetes/resource_kubernetes_stateful_set_test.go

Lines changed: 138 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
99
"github.com/hashicorp/terraform-plugin-sdk/terraform"
1010
api "k8s.io/api/apps/v1"
11+
appsv1 "k8s.io/api/apps/v1"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1213
)
1314

@@ -26,6 +27,7 @@ func TestAccKubernetesStatefulSet_basic(t *testing.T) {
2627
Config: testAccKubernetesStatefulSetConfigBasic(name),
2728
Check: resource.ComposeAggregateTestCheckFunc(
2829
testAccCheckKubernetesStatefulSetExists(statefulSetTestResourceName, &conf),
30+
testAccCheckKubernetesStatefulSetRollingOut(statefulSetTestResourceName),
2931
testAccKubernetesStatefulSetChecksBasic(name),
3032
),
3133
},
@@ -310,6 +312,26 @@ func TestAccKubernetesStatefulSet_update_pod_template(t *testing.T) {
310312
})
311313
}
312314

315+
func TestAccKubernetesStatefulSet_waitForRollout(t *testing.T) {
316+
var conf api.StatefulSet
317+
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
318+
resource.Test(t, resource.TestCase{
319+
PreCheck: func() { testAccPreCheck(t) },
320+
IDRefreshName: statefulSetTestResourceName,
321+
Providers: testAccProviders,
322+
CheckDestroy: testAccCheckKubernetesStatefulSetDestroy,
323+
Steps: []resource.TestStep{
324+
{
325+
Config: testAccKubernetesStatefulSetConfigWaitForRollout(name),
326+
Check: resource.ComposeAggregateTestCheckFunc(
327+
testAccCheckKubernetesStatefulSetExists(statefulSetTestResourceName, &conf),
328+
testAccCheckKubernetesStatefulSetRolledOut(statefulSetTestResourceName),
329+
),
330+
},
331+
},
332+
})
333+
}
334+
313335
func testAccCheckKubernetesStatefulSetDestroy(s *terraform.State) error {
314336
conn, err := testAccProvider.Meta().(KubeClientsets).MainClientset()
315337
if err != nil {
@@ -337,34 +359,74 @@ func testAccCheckKubernetesStatefulSetDestroy(s *terraform.State) error {
337359
return nil
338360
}
339361

340-
func testAccCheckKubernetesStatefulSetExists(n string, obj *api.StatefulSet) resource.TestCheckFunc {
341-
return func(s *terraform.State) error {
342-
rs, ok := s.RootModule().Resources[n]
343-
if !ok {
344-
return fmt.Errorf("Not found: %s", n)
345-
}
362+
func getStatefulSetFromResourceName(s *terraform.State, n string) (*appsv1.StatefulSet, error) {
363+
rs, ok := s.RootModule().Resources[n]
364+
if !ok {
365+
return nil, fmt.Errorf("Not found: %s", n)
366+
}
367+
368+
conn, err := testAccProvider.Meta().(KubeClientsets).MainClientset()
369+
if err != nil {
370+
return nil, err
371+
}
346372

347-
conn, err := testAccProvider.Meta().(KubeClientsets).MainClientset()
373+
namespace, name, err := idParts(rs.Primary.ID)
374+
if err != nil {
375+
return nil, err
376+
}
377+
378+
out, err := conn.AppsV1().StatefulSets(namespace).Get(name, metav1.GetOptions{})
379+
if err != nil {
380+
return nil, err
381+
}
382+
383+
return out, nil
384+
}
385+
386+
func testAccCheckKubernetesStatefulSetExists(n string, obj *appsv1.StatefulSet) resource.TestCheckFunc {
387+
return func(s *terraform.State) error {
388+
d, err := getStatefulSetFromResourceName(s, n)
348389
if err != nil {
349390
return err
350391
}
392+
*obj = *d
393+
return nil
394+
}
395+
}
351396

352-
namespace, name, err := idParts(rs.Primary.ID)
397+
func testAccCheckKubernetesStatefulSetRollingOut(n string) resource.TestCheckFunc {
398+
return func(s *terraform.State) error {
399+
d, err := getStatefulSetFromResourceName(s, n)
353400
if err != nil {
354401
return err
355402
}
356403

357-
out, err := conn.AppsV1().StatefulSets(namespace).Get(name, metav1.GetOptions{})
404+
if d.Status.Replicas == *d.Spec.Replicas {
405+
return fmt.Errorf("StatefulSet has already rolled out")
406+
}
407+
408+
return nil
409+
}
410+
}
411+
412+
func testAccCheckKubernetesStatefulSetRolledOut(n string) resource.TestCheckFunc {
413+
return func(s *terraform.State) error {
414+
d, err := getStatefulSetFromResourceName(s, n)
358415
if err != nil {
359416
return err
360417
}
361-
*obj = *out
418+
419+
if d.Status.Replicas != *d.Spec.Replicas {
420+
return fmt.Errorf("StatefulSet is still rolling out")
421+
}
422+
362423
return nil
363424
}
364425
}
365426

366427
func testAccKubernetesStatefulSetChecksBasic(name string) resource.TestCheckFunc {
367428
return resource.ComposeAggregateTestCheckFunc(
429+
resource.TestCheckResourceAttr(statefulSetTestResourceName, "wait_for_rollout", "false"),
368430
resource.TestCheckResourceAttrSet(statefulSetTestResourceName, "metadata.0.generation"),
369431
resource.TestCheckResourceAttrSet(statefulSetTestResourceName, "metadata.0.resource_version"),
370432
resource.TestCheckResourceAttrSet(statefulSetTestResourceName, "metadata.0.self_link"),
@@ -1009,3 +1071,69 @@ resource "kubernetes_stateful_set" "test" {
10091071
}
10101072
`, name)
10111073
}
1074+
1075+
func testAccKubernetesStatefulSetConfigWaitForRollout(name string) string {
1076+
return fmt.Sprintf(`
1077+
resource "kubernetes_service" "test" {
1078+
metadata {
1079+
name = "ss-test"
1080+
}
1081+
spec {
1082+
port {
1083+
port = 80
1084+
}
1085+
}
1086+
}
1087+
1088+
resource "kubernetes_stateful_set" "test" {
1089+
metadata {
1090+
name = "%s"
1091+
}
1092+
1093+
spec {
1094+
replicas = 2
1095+
1096+
selector {
1097+
match_labels = {
1098+
app = "ss-test"
1099+
}
1100+
}
1101+
1102+
update_strategy {
1103+
type = "RollingUpdate"
1104+
}
1105+
1106+
service_name = kubernetes_service.test.metadata.0.name
1107+
1108+
template {
1109+
metadata {
1110+
labels = {
1111+
app = "ss-test"
1112+
}
1113+
}
1114+
1115+
spec {
1116+
container {
1117+
name = "ss-test"
1118+
image = "nginx:1.19"
1119+
1120+
port {
1121+
container_port = 80
1122+
}
1123+
1124+
readiness_probe {
1125+
initial_delay_seconds = 5
1126+
http_get {
1127+
path = "/"
1128+
port = 80
1129+
}
1130+
}
1131+
}
1132+
}
1133+
}
1134+
}
1135+
1136+
wait_for_rollout = true
1137+
}
1138+
`, name)
1139+
}

vendor/github.com/PuerkitoBio/purell/.gitignore

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/PuerkitoBio/purell/.travis.yml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/PuerkitoBio/purell/LICENSE

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)