Skip to content

Commit 604441a

Browse files
Add wait_for_load_balancer field to kubernetes_ingress (#830)
* Add wait_for_load_balancer field to kubernetes_ingress Co-authored-by: Alex Somesan <[email protected]>
1 parent 96c5ff5 commit 604441a

File tree

3 files changed

+124
-5
lines changed

3 files changed

+124
-5
lines changed

kubernetes/resource_kubernetes_ingress.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import (
44
"fmt"
55
"log"
66

7+
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
78
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
89
"k8s.io/api/extensions/v1beta1"
910
"k8s.io/apimachinery/pkg/api/errors"
10-
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1112
)
1213

1314
func resourceKubernetesIngress() *schema.Resource {
@@ -109,6 +110,12 @@ func resourceKubernetesIngress() *schema.Resource {
109110
},
110111
},
111112
},
113+
"wait_for_load_balancer": {
114+
Type: schema.TypeBool,
115+
Optional: true,
116+
Default: false,
117+
Description: "Terraform will wait for the load balancer to have at least 1 endpoint before considering the resource created.",
118+
},
112119
},
113120
}
114121
}
@@ -132,7 +139,29 @@ func resourceKubernetesIngressCreate(d *schema.ResourceData, meta interface{}) e
132139
log.Printf("[INFO] Submitted new ingress: %#v", out)
133140
d.SetId(buildId(out.ObjectMeta))
134141

135-
return resourceKubernetesIngressRead(d, meta)
142+
if !d.Get("wait_for_load_balancer").(bool) {
143+
return resourceKubernetesIngressRead(d, meta)
144+
}
145+
146+
log.Printf("[INFO] Waiting for load balancer to become ready: %#v", out)
147+
return resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
148+
res, err := conn.ExtensionsV1beta1().Ingresses(metadata.Namespace).Get(metadata.Name, metav1.GetOptions{})
149+
if err != nil {
150+
// NOTE it is possible in some HA apiserver setups that are eventually consistent
151+
// that we could get a 404 when doing a Get immediately after a Create
152+
if errors.IsNotFound(err) {
153+
return resource.RetryableError(err)
154+
}
155+
return resource.NonRetryableError(err)
156+
}
157+
158+
if len(res.Status.LoadBalancer.Ingress) > 0 {
159+
return resource.NonRetryableError(resourceKubernetesIngressRead(d, meta))
160+
}
161+
162+
log.Printf("[INFO] Load Balancer not ready yet...")
163+
return resource.RetryableError(fmt.Errorf("Load Balancer is not ready yet"))
164+
})
136165
}
137166

138167
func resourceKubernetesIngressRead(d *schema.ResourceData, meta interface{}) error {
@@ -147,7 +176,7 @@ func resourceKubernetesIngressRead(d *schema.ResourceData, meta interface{}) err
147176
}
148177

149178
log.Printf("[INFO] Reading ingress %s", name)
150-
ing, err := conn.ExtensionsV1beta1().Ingresses(namespace).Get(name, meta_v1.GetOptions{})
179+
ing, err := conn.ExtensionsV1beta1().Ingresses(namespace).Get(name, metav1.GetOptions{})
151180
if err != nil {
152181
log.Printf("[DEBUG] Received error: %#v", err)
153182
return fmt.Errorf("Failed to read Ingress '%s' because: %s", buildId(ing.ObjectMeta), err)
@@ -217,7 +246,7 @@ func resourceKubernetesIngressDelete(d *schema.ResourceData, meta interface{}) e
217246
}
218247

219248
log.Printf("[INFO] Deleting ingress: %#v", name)
220-
err = conn.ExtensionsV1beta1().Ingresses(namespace).Delete(name, &meta_v1.DeleteOptions{})
249+
err = conn.ExtensionsV1beta1().Ingresses(namespace).Delete(name, &metav1.DeleteOptions{})
221250
if err != nil {
222251
return fmt.Errorf("Failed to delete Ingress %s because: %s", d.Id(), err)
223252
}
@@ -240,7 +269,7 @@ func resourceKubernetesIngressExists(d *schema.ResourceData, meta interface{}) (
240269
}
241270

242271
log.Printf("[INFO] Checking ingress %s", name)
243-
_, err = conn.ExtensionsV1beta1().Ingresses(namespace).Get(name, meta_v1.GetOptions{})
272+
_, err = conn.ExtensionsV1beta1().Ingresses(namespace).Get(name, metav1.GetOptions{})
244273
if err != nil {
245274
if statusErr, ok := err.(*errors.StatusError); ok && statusErr.ErrStatus.Code == 404 {
246275
return false, nil

kubernetes/resource_kubernetes_ingress_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ func TestAccKubernetesIngress_TLS(t *testing.T) {
106106
},
107107
})
108108
}
109+
109110
func TestAccKubernetesIngress_InternalKey(t *testing.T) {
110111
var conf api.Ingress
111112
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
@@ -147,6 +148,27 @@ func TestAccKubernetesIngress_InternalKey(t *testing.T) {
147148
})
148149
}
149150

151+
func TestAccKubernetesIngress_WaitForLoadBalancerGoogleCloud(t *testing.T) {
152+
var conf api.Ingress
153+
name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum))
154+
155+
resource.Test(t, resource.TestCase{
156+
PreCheck: func() { testAccPreCheck(t); skipIfNoGoogleCloudSettingsFound(t) },
157+
IDRefreshName: "kubernetes_ingress.test",
158+
Providers: testAccProviders,
159+
CheckDestroy: testAccCheckKubernetesIngressDestroy,
160+
Steps: []resource.TestStep{
161+
{
162+
Config: testAccKubernetesIngressConfig_waitForLoadBalancer(name),
163+
Check: resource.ComposeAggregateTestCheckFunc(
164+
testAccCheckKubernetesIngressExists("kubernetes_ingress.test", &conf),
165+
resource.TestCheckResourceAttrSet("kubernetes_ingress.test", "load_balancer_ingress.0.ip"),
166+
),
167+
},
168+
},
169+
})
170+
}
171+
150172
func testAccCheckKubernetesIngressDestroy(s *terraform.State) error {
151173
conn, err := testAccProvider.Meta().(KubeClientsets).MainClientset()
152174
if err != nil {
@@ -334,3 +356,70 @@ resource "kubernetes_ingress" "test" {
334356
}
335357
}`, name)
336358
}
359+
360+
func testAccKubernetesIngressConfig_waitForLoadBalancer(name string) string {
361+
return fmt.Sprintf(`
362+
resource "kubernetes_service" "test" {
363+
metadata {
364+
name = %q
365+
}
366+
spec {
367+
type = "NodePort"
368+
selector = {
369+
app = %q
370+
}
371+
port {
372+
port = 8000
373+
target_port = 80
374+
protocol = "TCP"
375+
}
376+
}
377+
}
378+
379+
resource "kubernetes_deployment" "test" {
380+
metadata {
381+
name = %q
382+
}
383+
spec {
384+
selector {
385+
match_labels = {
386+
app = %q
387+
}
388+
}
389+
template {
390+
metadata {
391+
labels = {
392+
app = %q
393+
}
394+
}
395+
spec {
396+
container {
397+
name = "test"
398+
image = "gcr.io/google-samples/hello-app:2.0"
399+
env {
400+
name = "PORT"
401+
value = "80"
402+
}
403+
}
404+
}
405+
}
406+
}
407+
}
408+
409+
resource "kubernetes_ingress" "test" {
410+
depends_on = [
411+
kubernetes_service.test,
412+
kubernetes_deployment.test
413+
]
414+
metadata {
415+
name = %q
416+
}
417+
spec {
418+
backend {
419+
service_name = %q
420+
service_port = 8000
421+
}
422+
}
423+
wait_for_load_balancer = true
424+
}`, name, name, name, name, name, name, name)
425+
}

website/docs/r/ingress.html.markdown

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ The following arguments are supported:
100100

101101
* `metadata` - (Required) Standard ingress's metadata. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#metadata
102102
* `spec` - (Required) Spec defines the behavior of a ingress. https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
103+
* `wait_for_load_balancer` - (Optional) Terraform will wait for the load balancer to have at least 1 endpoint before considering the resource created.
103104

104105
## Nested Blocks
105106

0 commit comments

Comments
 (0)