From a9f91826931cf44e133e2fe239578e5c9c317b19 Mon Sep 17 00:00:00 2001 From: Aleksandr Rybolovlev Date: Thu, 19 Dec 2024 15:02:37 +0100 Subject: [PATCH] Handle scenario when imagePullSecret name is an empty string --- kubernetes/resource_kubernetes_pod_v1_test.go | 112 ++++++++++++++++++ ...urce_kubernetes_service_account_v1_test.go | 54 +++++++++ kubernetes/structures.go | 6 + 3 files changed, 172 insertions(+) diff --git a/kubernetes/resource_kubernetes_pod_v1_test.go b/kubernetes/resource_kubernetes_pod_v1_test.go index d1970deb96..1d2fcad6fb 100644 --- a/kubernetes/resource_kubernetes_pod_v1_test.go +++ b/kubernetes/resource_kubernetes_pod_v1_test.go @@ -1640,6 +1640,61 @@ func TestAccKubernetesPodV1_os(t *testing.T) { }) } +func TestAccKubernetesPodV1_imagePullSecret(t *testing.T) { + name := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "kubernetes_pod_v1.test" + imageName := busyboxImage + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesPodV1Destroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesPodV1ConfigImagePullSecret(name, imageName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.generation"), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.uid"), + resource.TestCheckResourceAttr(resourceName, "spec.0.image_pull_secrets.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.image_pull_secrets.0.name", "secret"), + ), + }, + { + Config: testAccKubernetesPodV1ConfigImagePullSecretEmpty(name, imageName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.generation"), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.uid"), + resource.TestCheckResourceAttr(resourceName, "spec.0.image_pull_secrets.#", "1"), + resource.TestCheckResourceAttr(resourceName, "spec.0.image_pull_secrets.0.name", ""), + ), + }, + { + Config: testAccKubernetesPodV1ConfigImagePullSecretMulty(name, imageName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.generation"), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.uid"), + resource.TestCheckResourceAttr(resourceName, "spec.0.image_pull_secrets.#", "2"), + resource.TestCheckResourceAttr(resourceName, "spec.0.image_pull_secrets.0.name", ""), + resource.TestCheckResourceAttr(resourceName, "spec.0.image_pull_secrets.1.name", "secret"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version"}, + }, + { + Config: testAccKubernetesPodV1ConfigMinimal(name, imageName), + PlanOnly: true, + }, + }, + }) +} + func testAccCheckCSIDriverExists(csiDriverName string) error { conn, err := testAccProvider.Meta().(KubeClientsets).MainClientset() if err != nil { @@ -3551,3 +3606,60 @@ func testAccKubernetesPodV1ConfigOS(name, imageName string) string { } `, name, imageName) } + +func testAccKubernetesPodV1ConfigImagePullSecret(name, imageName string) string { + return fmt.Sprintf(`resource "kubernetes_pod_v1" "test" { + metadata { + name = "%s" + } + spec { + image_pull_secrets { + name = "secret" + } + container { + image = "%s" + name = "containername" + } + } +} +`, name, imageName) +} + +func testAccKubernetesPodV1ConfigImagePullSecretEmpty(name, imageName string) string { + return fmt.Sprintf(`resource "kubernetes_pod_v1" "test" { + metadata { + name = "%s" + } + spec { + image_pull_secrets { + name = "" + } + container { + image = "%s" + name = "containername" + } + } +} +`, name, imageName) +} + +func testAccKubernetesPodV1ConfigImagePullSecretMulty(name, imageName string) string { + return fmt.Sprintf(`resource "kubernetes_pod_v1" "test" { + metadata { + name = "%s" + } + spec { + image_pull_secrets { + name = "" + } + image_pull_secrets { + name = "secret" + } + container { + image = "%s" + name = "containername" + } + } +} +`, name, imageName) +} diff --git a/kubernetes/resource_kubernetes_service_account_v1_test.go b/kubernetes/resource_kubernetes_service_account_v1_test.go index f1571e40bd..fb93a33ded 100644 --- a/kubernetes/resource_kubernetes_service_account_v1_test.go +++ b/kubernetes/resource_kubernetes_service_account_v1_test.go @@ -271,6 +271,44 @@ func TestAccKubernetesServiceAccount_generatedName(t *testing.T) { }) } +func TestAccKubernetesServiceAccount_imagePullSecret(t *testing.T) { + var conf corev1.ServiceAccount + name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + resourceName := "kubernetes_service_account_v1.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "kubernetes_service_account.test", + IDRefreshIgnore: []string{"metadata.0.resource_version"}, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesServiceAccountV1Destroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesServiceAccountV1ConfigImagePullSecret(name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesServiceAccountV1Exists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "metadata.0.name", name), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.generation"), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.resource_version"), + resource.TestCheckResourceAttrSet(resourceName, "metadata.0.uid"), + resource.TestCheckResourceAttr(resourceName, "image_pull_secret.#", "2"), + resource.TestCheckResourceAttr(resourceName, "automount_service_account_token", "true"), + testAccCheckServiceAccountV1ImagePullSecrets(&conf, []*regexp.Regexp{ + regexp.MustCompile("^$"), + regexp.MustCompile("^secret$"), + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "automount_service_account_token"}, + }, + }, + }) +} + func testAccCheckServiceAccountV1ImagePullSecrets(m *corev1.ServiceAccount, expected []*regexp.Regexp) resource.TestCheckFunc { return func(s *terraform.State) error { if len(expected) == 0 && len(m.ImagePullSecrets) == 0 { @@ -591,3 +629,19 @@ resource "kubernetes_secret_v1" "four" { } `, name, name, name, name, name) } + +func testAccKubernetesServiceAccountV1ConfigImagePullSecret(name string) string { + return fmt.Sprintf(`resource "kubernetes_service_account_v1" "test" { + metadata { + name = "%s" + } + image_pull_secret { + name = "" + } + + image_pull_secret { + name = "secret" + } +} +`, name) +} diff --git a/kubernetes/structures.go b/kubernetes/structures.go index d0696740a1..2e3574fc1b 100644 --- a/kubernetes/structures.go +++ b/kubernetes/structures.go @@ -607,6 +607,12 @@ func expandLocalObjectReferenceArray(in []interface{}) []api.LocalObjectReferenc } att = make([]api.LocalObjectReference, len(in)) for i, c := range in { + // If an item is an empty string, we treat it as nil. + // Kubernetes accepts an empty string as a name but issues a warning: `invalid empty name ""`. + // Therefore, we should handle this case appropriately. + if c == nil { + continue + } p := c.(map[string]interface{}) if name, ok := p["name"]; ok { att[i].Name = name.(string)