diff --git a/kubernetes/resource_kubernetes_deployment.go b/kubernetes/resource_kubernetes_deployment.go index 5243a009fb..c33a898cce 100644 --- a/kubernetes/resource_kubernetes_deployment.go +++ b/kubernetes/resource_kubernetes_deployment.go @@ -416,7 +416,7 @@ func waitForDeploymentReplicasFunc(ctx context.Context, conn *kubernetes.Clients return resource.NonRetryableError(err) } - var specReplicas int32 = 1 // default, acording to API docs + var specReplicas int32 = 1 // default, according to API docs if dply.Spec.Replicas != nil { specReplicas = *dply.Spec.Replicas } @@ -436,6 +436,10 @@ func waitForDeploymentReplicasFunc(ctx context.Context, conn *kubernetes.Clients return resource.RetryableError(fmt.Errorf("Waiting for rollout to finish: %d old replicas are pending termination...", dply.Status.Replicas-dply.Status.UpdatedReplicas)) } + if dply.Status.Replicas > dply.Status.ReadyReplicas { + return resource.RetryableError(fmt.Errorf("Waiting for rollout to finish: %d replicas wanted; %d replicas Ready", dply.Status.Replicas, dply.Status.ReadyReplicas)) + } + if dply.Status.AvailableReplicas < dply.Status.UpdatedReplicas { return resource.RetryableError(fmt.Errorf("Waiting for rollout to finish: %d of %d updated replicas are available...", dply.Status.AvailableReplicas, dply.Status.UpdatedReplicas)) } diff --git a/kubernetes/resource_kubernetes_deployment_test.go b/kubernetes/resource_kubernetes_deployment_test.go index b8d0fda2d2..75b3b474d1 100644 --- a/kubernetes/resource_kubernetes_deployment_test.go +++ b/kubernetes/resource_kubernetes_deployment_test.go @@ -32,7 +32,6 @@ func TestAccKubernetesDeployment_basic(t *testing.T) { Config: testAccKubernetesDeploymentConfig_basic(name), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckKubernetesDeploymentExists("kubernetes_deployment.test", &conf), - testAccCheckKubernetesDeploymentRolledOut("kubernetes_deployment.test"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.%", "2"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.TestAnnotationOne", "one"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.TestAnnotationTwo", "two"), @@ -436,24 +435,15 @@ func TestAccKubernetesDeployment_with_container_security_context_run_as_group(t testAccCheckKubernetesDeploymentExists("kubernetes_deployment.test", &conf), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.#", "2"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.security_context.#", "1"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.security_context.0.capabilities.#", "0"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.security_context.0.privileged", "true"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.security_context.0.se_linux_options.#", "1"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.security_context.0.se_linux_options.0.level", "s0:c123,c456"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.#", "1"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.allow_privilege_escalation", "true"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.capabilities.#", "1"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.capabilities.0.add.#", "1"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.capabilities.0.add.0", "NET_BIND_SERVICE"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.capabilities.0.drop.#", "1"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.capabilities.0.drop.0", "all"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.privileged", "true"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.read_only_root_filesystem", "true"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.privileged", "false"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.run_as_group", "200"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.run_as_non_root", "true"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.run_as_non_root", "false"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.run_as_user", "201"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.se_linux_options.#", "1"), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.1.security_context.0.se_linux_options.0.level", "s0:c123,c789"), ), }, }, @@ -915,6 +905,7 @@ func TestAccKubernetesDeployment_regression(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, IDRefreshName: "kubernetes_deployment.test", + IDRefreshIgnore: []string{"metadata.0.resource_version"}, ExternalProviders: testAccExternalProviders, CheckDestroy: testAccCheckKubernetesDeploymentDestroy, Steps: []resource.TestStep{ @@ -922,7 +913,6 @@ func TestAccKubernetesDeployment_regression(t *testing.T) { Config: requiredProviders() + testAccKubernetesDeploymentConfig_regression("kubernetes-released", name), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckKubernetesDeploymentExists("kubernetes_deployment.test", &conf1), - testAccCheckKubernetesDeploymentRolledOut("kubernetes_deployment.test"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.%", "2"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.TestAnnotationOne", "one"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.TestAnnotationTwo", "two"), @@ -938,7 +928,7 @@ func TestAccKubernetesDeployment_regression(t *testing.T) { resource.TestCheckResourceAttrSet("kubernetes_deployment.test", "metadata.0.self_link"), resource.TestCheckResourceAttrSet("kubernetes_deployment.test", "metadata.0.uid"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.image", defaultNginxImage), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.name", "tf-acc-test"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.name", "containername"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.strategy.0.type", "RollingUpdate"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.strategy.0.rolling_update.0.max_surge", "25%"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.strategy.0.rolling_update.0.max_unavailable", "25%"), @@ -949,7 +939,6 @@ func TestAccKubernetesDeployment_regression(t *testing.T) { Config: requiredProviders() + testAccKubernetesDeploymentConfig_regression("kubernetes-local", name), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckKubernetesDeploymentExists("kubernetes_deployment.test", &conf2), - testAccCheckKubernetesDeploymentRolledOut("kubernetes_deployment.test"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.%", "2"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.TestAnnotationOne", "one"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "metadata.0.annotations.TestAnnotationTwo", "two"), @@ -965,7 +954,7 @@ func TestAccKubernetesDeployment_regression(t *testing.T) { resource.TestCheckResourceAttrSet("kubernetes_deployment.test", "metadata.0.self_link"), resource.TestCheckResourceAttrSet("kubernetes_deployment.test", "metadata.0.uid"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.image", defaultNginxImage), - resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.name", "tf-acc-test"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.name", "containername"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.strategy.0.type", "RollingUpdate"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.strategy.0.rolling_update.0.max_surge", "25%"), resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.strategy.0.rolling_update.0.max_unavailable", "25%"), @@ -977,6 +966,57 @@ func TestAccKubernetesDeployment_regression(t *testing.T) { }) } +func TestAccKubernetesDeployment_with_resource_field_selector(t *testing.T) { + var conf appsv1.Deployment + rcName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + imageName := "nginx:1.7.9" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesDeploymentDestroy, + Steps: []resource.TestStep{ + { + ExpectError: regexp.MustCompile("quantities must match the regular expression"), + Config: testAccKubernetesDeploymentConfigWithResourceFieldSelector(rcName, imageName, "limits.cpu", ""), + }, + { + ExpectError: regexp.MustCompile("only divisor's values 1m and 1 are supported with the cpu resource"), + Config: testAccKubernetesDeploymentConfigWithResourceFieldSelector(rcName, imageName, "limits.cpu", "2"), + }, + { + Config: testAccKubernetesDeploymentConfigWithResourceFieldSelector(rcName, imageName, "limits.cpu", "1m"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesDeploymentExists("kubernetes_deployment.test", &conf), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.image", imageName), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.resources.0.limits.0.memory", "512Mi"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.#", "1"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.0.name", "K8S_LIMITS_CPU"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.0.value_from.0.resource_field_ref.0.container_name", "containername"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.0.value_from.0.resource_field_ref.0.divisor", "1m"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.0.value_from.0.resource_field_ref.0.resource", "limits.cpu"), + ), + }, + { + Config: testAccKubernetesDeploymentConfigWithResourceFieldSelector(rcName, imageName, "limits.memory", "1Mi"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesDeploymentExists("kubernetes_deployment.test", &conf), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.0.value_from.0.resource_field_ref.0.divisor", "1Mi"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.0.value_from.0.resource_field_ref.0.resource", "limits.memory"), + ), + }, + { + Config: testAccKubernetesDeploymentConfigWithResourceFieldSelector(rcName, imageName, "requests.memory", "1Ki"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesDeploymentExists("kubernetes_deployment.test", &conf), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.0.value_from.0.resource_field_ref.0.divisor", "1Ki"), + resource.TestCheckResourceAttr("kubernetes_deployment.test", "spec.0.template.0.spec.0.container.0.env.0.value_from.0.resource_field_ref.0.resource", "requests.memory"), + ), + }, + }, + }) +} + func testAccCheckKubernetesDeploymentForceNew(old, new *appsv1.Deployment, wantNew bool) resource.TestCheckFunc { return func(s *terraform.State) error { if wantNew { @@ -1095,21 +1135,6 @@ func testAccCheckKubernetesDeploymentRollingOut(n string) resource.TestCheckFunc } } -func testAccCheckKubernetesDeploymentRolledOut(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - d, err := getDeploymentFromResourceName(s, n) - if err != nil { - return err - } - - if d.Status.Replicas != d.Status.ReadyReplicas { - return fmt.Errorf("deployment is still rolling out") - } - - return nil - } -} - func testAccKubernetesDeploymentConfig_basic(name string) string { return fmt.Sprintf(`resource "kubernetes_deployment" "test" { metadata { @@ -1841,27 +1866,12 @@ func testAccKubernetesDeploymentConfigWithContainerSecurityContextRunAsGroup(dep } container { - image = "gcr.io/google_containers/liveness" - name = "containername2" - args = ["/server"] - + name = "container2" + image = "busybox" + command = ["sh", "-c", "echo The app is running! && sleep 3600"] security_context { - allow_privilege_escalation = true - - capabilities { - drop = ["all"] - add = ["NET_BIND_SERVICE"] - } - - privileged = true - read_only_root_filesystem = true - run_as_group = 200 - run_as_non_root = true - run_as_user = 201 - - se_linux_options { - level = "s0:c123,c789" - } + run_as_group = 200 + run_as_user = 201 } } } @@ -2388,7 +2398,7 @@ func testAccKubernetesDeploymentConfig_regression(provider, name string) string spec { container { image = %q - name = "tf-acc-test" + name = "containername" port { container_port = 80 @@ -2408,6 +2418,24 @@ func testAccKubernetesDeploymentConfig_regression(provider, name string) string cpu = "50m" } } + env { + name = "LIMITS_CPU" + value_from { + resource_field_ref { + container_name = "containername" + resource = "requests.cpu" + } + } + } + env { + name = "LIMITS_MEM" + value_from { + resource_field_ref { + container_name = "containername" + resource = "requests.memory" + } + } + } } } } @@ -2474,3 +2502,50 @@ resource "kubernetes_deployment" "test" { } `, secretName, label, deploymentName, imageName) } + +func testAccKubernetesDeploymentConfigWithResourceFieldSelector(rcName, imageName, resourceName, divisor string) string { + return fmt.Sprintf(`resource "kubernetes_deployment" "test" { + metadata { + name = "%s" + labels = { + Test = "TfAcceptanceTest" + } + } + spec { + selector { + match_labels = { + Test = "TfAcceptanceTest" + } + } + template { + metadata { + labels = { + Test = "TfAcceptanceTest" + } + } + spec { + container { + image = "%s" + name = "containername" + resources { + limits { + memory = "512Mi" + } + } + env { + name = "K8S_LIMITS_CPU" + value_from { + resource_field_ref { + container_name = "containername" + resource = "%s" + divisor = "%s" + } + } + } + } + } + } + } +} +`, rcName, imageName, resourceName, divisor) +} diff --git a/kubernetes/schema_container.go b/kubernetes/schema_container.go index 67bfae42b8..940fe2353f 100644 --- a/kubernetes/schema_container.go +++ b/kubernetes/schema_container.go @@ -274,6 +274,13 @@ func containerFields(isUpdatable, isInitContainer bool) map[string]*schema.Schem Type: schema.TypeString, Optional: true, }, + "divisor": { + Type: schema.TypeString, + Optional: true, + Default: "1", + ValidateFunc: validateResourceQuantity, + DiffSuppressFunc: suppressEquivalentResourceQuantity, + }, "resource": { Type: schema.TypeString, Required: true, diff --git a/kubernetes/schema_pod_spec.go b/kubernetes/schema_pod_spec.go index ec474c423b..7fe50c67ef 100644 --- a/kubernetes/schema_pod_spec.go +++ b/kubernetes/schema_pod_spec.go @@ -547,9 +547,12 @@ func volumeSchema(isUpdatable bool) *schema.Resource { Type: schema.TypeString, Required: true, }, - "quantity": { - Type: schema.TypeString, - Optional: true, + "divisor": { + Type: schema.TypeString, + Optional: true, + Default: "1", + ValidateFunc: validateResourceQuantity, + DiffSuppressFunc: suppressEquivalentResourceQuantity, }, "resource": { Type: schema.TypeString, diff --git a/kubernetes/structures_container.go b/kubernetes/structures_container.go index 8c2746c7f6..5562ef0366 100644 --- a/kubernetes/structures_container.go +++ b/kubernetes/structures_container.go @@ -1,7 +1,8 @@ package kubernetes import ( - v1 "k8s.io/api/core/v1" + "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/intstr" ) @@ -205,6 +206,9 @@ func flattenResourceFieldSelector(in *v1.ResourceFieldSelector) []interface{} { if in.Resource != "" { att["resource"] = in.Resource } + if in.Divisor.String() != "" { + att["divisor"] = in.Divisor.String() + } return []interface{}{att} } @@ -861,6 +865,13 @@ func expandResourceFieldRef(r []interface{}) (*v1.ResourceFieldSelector, error) if v, ok := in["resource"].(string); ok { obj.Resource = v } + if v, ok := in["divisor"].(string); ok { + q, err := resource.ParseQuantity(v) + if err != nil { + return obj, err + } + obj.Divisor = q + } return obj, nil }