diff --git a/kubernetes/data_source_kubernetes_service_account.go b/kubernetes/data_source_kubernetes_service_account.go index 45f92ac1c7..beff26017b 100644 --- a/kubernetes/data_source_kubernetes_service_account.go +++ b/kubernetes/data_source_kubernetes_service_account.go @@ -48,8 +48,9 @@ func dataSourceKubernetesServiceAccount() *schema.Resource { Computed: true, }, "default_secret_name": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, + Deprecated: "Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, `default_secret_name` will be empty", }, }, } diff --git a/kubernetes/data_source_kubernetes_service_account_test.go b/kubernetes/data_source_kubernetes_service_account_test.go index ce6f4f05a6..02743c08ff 100644 --- a/kubernetes/data_source_kubernetes_service_account_test.go +++ b/kubernetes/data_source_kubernetes_service_account_test.go @@ -25,7 +25,6 @@ func TestAccKubernetesDataSourceServiceAccount_basic(t *testing.T) { resource.TestCheckResourceAttr("kubernetes_service_account.test", "secret.0.name", name+"-secret"), resource.TestCheckResourceAttr("kubernetes_service_account.test", "image_pull_secret.0.name", name+"-image-pull-secret"), resource.TestCheckResourceAttr("kubernetes_service_account.test", "automount_service_account_token", "true"), - resource.TestCheckResourceAttrSet("kubernetes_service_account.test", "default_secret_name"), ), }, { @@ -38,7 +37,6 @@ func TestAccKubernetesDataSourceServiceAccount_basic(t *testing.T) { resource.TestCheckResourceAttr("data.kubernetes_service_account.test", "secret.0.name", name+"-secret"), resource.TestCheckResourceAttr("data.kubernetes_service_account.test", "image_pull_secret.0.name", name+"-image-pull-secret"), resource.TestCheckResourceAttr("data.kubernetes_service_account.test", "automount_service_account_token", "true"), - resource.TestCheckResourceAttrSet("data.kubernetes_service_account.test", "default_secret_name"), ), }, }, @@ -49,18 +47,21 @@ func TestAccKubernetesDataSourceServiceAccount_default_secret(t *testing.T) { name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { + testAccPreCheck(t) + skipIfClusterVersionGreaterThanOrEqual(t, "1.24.0") + }, ProviderFactories: testAccProviderFactories, Steps: []resource.TestStep{ { - Config: testAccKubernetesServiceAccountConfig_default_secret(name), + Config: testAccKubernetesDataSourceServiceAccountConfig_default_secret(name), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("kubernetes_service_account.test", "metadata.0.name", name), resource.TestCheckResourceAttr("kubernetes_service_account.test", "secret.#", "1"), ), }, { - Config: testAccKubernetesServiceAccountConfig_default_secret(name) + + Config: testAccKubernetesDataSourceServiceAccountConfig_default_secret(name) + testAccKubernetesDataSourceServiceAccountConfig_default_secret_read(name), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("data.kubernetes_service_account.test", "metadata.0.name", name), @@ -117,7 +118,7 @@ func testAccKubernetesDataSourceServiceAccountConfig_read() string { `) } -func testAccKubernetesServiceAccountConfig_default_secret(name string) string { +func testAccKubernetesDataSourceServiceAccountConfig_default_secret(name string) string { return fmt.Sprintf(` variable "token_name" { default = "%s-token-test0" diff --git a/kubernetes/provider.go b/kubernetes/provider.go index 1ab078094e..912262c4de 100644 --- a/kubernetes/provider.go +++ b/kubernetes/provider.go @@ -11,6 +11,7 @@ import ( "strconv" "github.com/hashicorp/go-cty/cty" + gversion "github.com/hashicorp/go-version" "github.com/mitchellh/go-homedir" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -633,3 +634,26 @@ func useAdmissionregistrationV1beta1(conn *kubernetes.Clientset) (bool, error) { useadmissionregistrationv1beta1 = ptrToBool(true) return true, nil } + +func getServerVersion(connection *kubernetes.Clientset) (*gversion.Version, error) { + sv, err := connection.ServerVersion() + if err != nil { + return nil, err + } + + return gversion.NewVersion(sv.String()) +} + +func serverVersionGreaterThanOrEqual(connection *kubernetes.Clientset, version string) (bool, error) { + sv, err := getServerVersion(connection) + if err != nil { + return false, err + } + // server version that we need to compare with + cv, err := gversion.NewVersion(version) + if err != nil { + return false, err + } + + return sv.GreaterThanOrEqual(cv), nil +} diff --git a/kubernetes/resource_kubernetes_default_service_account.go b/kubernetes/resource_kubernetes_default_service_account.go index 5f6f98acb0..ce80196fb5 100644 --- a/kubernetes/resource_kubernetes_default_service_account.go +++ b/kubernetes/resource_kubernetes_default_service_account.go @@ -55,7 +55,6 @@ func resourceKubernetesDefaultServiceAccountCreate(ctx context.Context, d *schem log.Printf("[INFO] Default service account exists: %s", metadata.Namespace) return nil }) - if err != nil { return diag.FromErr(err) } diff --git a/kubernetes/resource_kubernetes_service.go b/kubernetes/resource_kubernetes_service.go index f870ba34c3..e5ba21db4e 100644 --- a/kubernetes/resource_kubernetes_service.go +++ b/kubernetes/resource_kubernetes_service.go @@ -442,7 +442,7 @@ func resourceKubernetesServiceUpdate(ctx context.Context, d *schema.ResourceData ops := patchMetadata("metadata.0.", "/metadata/", d) if d.HasChange("spec") { - serverVersion, err := conn.ServerVersion() + serverVersion, err := getServerVersion(conn) if err != nil { return diag.FromErr(err) } diff --git a/kubernetes/resource_kubernetes_service_account.go b/kubernetes/resource_kubernetes_service_account.go index 8463061578..19198ffd1d 100644 --- a/kubernetes/resource_kubernetes_service_account.go +++ b/kubernetes/resource_kubernetes_service_account.go @@ -69,8 +69,9 @@ func resourceKubernetesServiceAccount() *schema.Resource { Default: true, }, "default_secret_name": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, + Deprecated: "Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, `default_secret_name` will be empty", }, }, } @@ -106,12 +107,21 @@ func resourceKubernetesServiceAccountCreate(ctx context.Context, d *schema.Resou if err != nil { return diag.FromErr(err) } + return resourceKubernetesServiceAccountRead(ctx, d, meta) } func getServiceAccountDefaultSecret(ctx context.Context, name string, config api.ServiceAccount, timeout time.Duration, conn *kubernetes.Clientset) (*api.Secret, error) { + sv, err := serverVersionGreaterThanOrEqual(conn, "1.24.0") + if err != nil { + return &api.Secret{}, err + } + if sv { + return &api.Secret{}, nil + } + var svcAccTokens []api.Secret - err := resource.RetryContext(ctx, timeout, func() *resource.RetryError { + err = resource.RetryContext(ctx, timeout, func() *resource.RetryError { resp, err := conn.CoreV1().ServiceAccounts(config.Namespace).Get(ctx, name, metav1.GetOptions{}) if err != nil { return resource.NonRetryableError(err) @@ -167,6 +177,20 @@ func findDefaultServiceAccount(ctx context.Context, sa *api.ServiceAccount, conn */ ds := make([]string, 0) + sv, err := serverVersionGreaterThanOrEqual(conn, "1.24.0") + if err != nil { + return "", diag.FromErr(err) + } + if sv { + return "", diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: `"default_secret_name" is no longer applicable for Kubernetes v1.24.0 and above`, + Detail: `Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, "default_secret_name" will be empty`, + }, + } + } + for _, saSecret := range sa.Secrets { if !strings.HasPrefix(saSecret.Name, fmt.Sprintf("%s-token-", sa.Name)) { log.Printf("[DEBUG] Skipping %s as it doesn't have the right name", saSecret.Name) @@ -292,6 +316,20 @@ func resourceKubernetesServiceAccountRead(ctx context.Context, d *schema.Resourc return diag.FromErr(err) } + sv, err := serverVersionGreaterThanOrEqual(conn, "1.24.0") + if err != nil { + return diag.FromErr(err) + } + if sv { + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: `"default_secret_name" is no longer applicable for Kubernetes v1.24.0 and above`, + Detail: `Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, "default_secret_name" will be empty`, + }, + } + } + return nil } @@ -415,6 +453,7 @@ func resourceKubernetesServiceAccountImportState(ctx context.Context, d *schema. if err != nil { return nil, fmt.Errorf("Unable to set default_secret_name: %s", err) } + d.SetId(buildId(sa.ObjectMeta)) return []*schema.ResourceData{d}, nil diff --git a/kubernetes/resource_kubernetes_service_account_test.go b/kubernetes/resource_kubernetes_service_account_test.go index fb6105c207..8ae4990839 100644 --- a/kubernetes/resource_kubernetes_service_account_test.go +++ b/kubernetes/resource_kubernetes_service_account_test.go @@ -63,6 +63,38 @@ func TestAccKubernetesServiceAccount_basic(t *testing.T) { }) } +func TestAccKubernetesServiceAccount_default_secret(t *testing.T) { + var conf api.ServiceAccount + name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + resourceName := "kubernetes_service_account_v1.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + skipIfClusterVersionGreaterThanOrEqual(t, "1.24.0") + }, + IDRefreshName: resourceName, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckKubernetesServiceAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccKubernetesServiceAccountConfig_default_secret(name), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckKubernetesServiceAccountExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "metadata.0.name", name), + resource.TestCheckResourceAttrSet(resourceName, "default_secret_name"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"metadata.0.resource_version", "automount_service_account_token"}, + }, + }, + }) +} + func TestAccKubernetesServiceAccount_automount(t *testing.T) { var conf api.ServiceAccount name := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) @@ -257,6 +289,9 @@ func matchLocalObjectReferenceName(lor []api.LocalObjectReference, expected []*r func testAccCheckServiceAccountSecrets(m *api.ServiceAccount, expected []*regexp.Regexp) resource.TestCheckFunc { return func(s *terraform.State) error { + if clusterVersionGreaterThanOrEqual("1.24.0") { + return nil + } if len(expected) == 0 && len(m.Secrets) == 0 { return nil } @@ -397,6 +432,14 @@ resource "kubernetes_secret" "four" { `, name, name, name, name, name) } +func testAccKubernetesServiceAccountConfig_default_secret(name string) string { + return fmt.Sprintf(`resource "kubernetes_service_account_v1" "test" { + metadata { + name = "%s" + } +}`, name) +} + func testAccKubernetesServiceAccountConfig_modified(name string) string { return fmt.Sprintf(`resource "kubernetes_service_account" "test" { metadata { diff --git a/kubernetes/structure_service_spec.go b/kubernetes/structure_service_spec.go index a35449ce31..8dbcd1c3e3 100644 --- a/kubernetes/structure_service_spec.go +++ b/kubernetes/structure_service_spec.go @@ -5,7 +5,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/version" ) // Flatteners @@ -284,7 +283,7 @@ func expandServiceSpec(l []interface{}) v1.ServiceSpec { // Patch Ops -func patchServiceSpec(keyPrefix, pathPrefix string, d *schema.ResourceData, v *version.Info) (PatchOperations, error) { +func patchServiceSpec(keyPrefix, pathPrefix string, d *schema.ResourceData, kv *gversion.Version) (PatchOperations, error) { ops := make([]PatchOperation, 0, 0) if d.HasChange(keyPrefix + "allocate_load_balancer_node_ports") { @@ -360,12 +359,8 @@ func patchServiceSpec(keyPrefix, pathPrefix string, d *schema.ResourceData, v *v }) } if d.HasChange(keyPrefix + "external_ips") { - k8sVersion, err := gversion.NewVersion(v.String()) - if err != nil { - return nil, err - } - v1_8_0, _ := gversion.NewVersion("1.8.0") - if k8sVersion.LessThan(v1_8_0) { + version, _ := gversion.NewVersion("1.8.0") + if kv.LessThan(version) { // If we haven't done this the deprecated field would have priority ops = append(ops, &ReplaceOperation{ Path: pathPrefix + "deprecatedPublicIPs", diff --git a/website/docs/d/service_account.html.markdown b/website/docs/d/service_account.html.markdown index 431f371323..2637e64355 100644 --- a/website/docs/d/service_account.html.markdown +++ b/website/docs/d/service_account.html.markdown @@ -53,7 +53,7 @@ The following arguments are supported: * `image_pull_secret` - A list of image pull secrets associated with the service account. * `secret` - A list of secrets associated with the service account. -* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. +* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, `default_secret_name` will be empty. ### `image_pull_secret` diff --git a/website/docs/d/service_account_v1.html.markdown b/website/docs/d/service_account_v1.html.markdown index 8227ad84cf..629dda01e2 100644 --- a/website/docs/d/service_account_v1.html.markdown +++ b/website/docs/d/service_account_v1.html.markdown @@ -53,7 +53,7 @@ The following arguments are supported: * `image_pull_secret` - A list of image pull secrets associated with the service account. * `secret` - A list of secrets associated with the service account. -* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. +* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, `default_secret_name` will be empty. ### `image_pull_secret` diff --git a/website/docs/r/default_service_account.html.markdown b/website/docs/r/default_service_account.html.markdown index dfb2fd75db..c489a6eb04 100644 --- a/website/docs/r/default_service_account.html.markdown +++ b/website/docs/r/default_service_account.html.markdown @@ -79,7 +79,7 @@ The following arguments are supported: In addition to the arguments listed above, the following computed attributes are exported: -* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. +* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, `default_secret_name` will be empty. ## Destroying diff --git a/website/docs/r/default_service_account_v1.html.markdown b/website/docs/r/default_service_account_v1.html.markdown index aad5723263..94548e4457 100644 --- a/website/docs/r/default_service_account_v1.html.markdown +++ b/website/docs/r/default_service_account_v1.html.markdown @@ -79,7 +79,7 @@ The following arguments are supported: In addition to the arguments listed above, the following computed attributes are exported: -* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. +* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, `default_secret_name` will be empty. ## Destroying diff --git a/website/docs/r/service_account.html.markdown b/website/docs/r/service_account.html.markdown index 8b4f3b2dc6..6fa686f3d1 100644 --- a/website/docs/r/service_account.html.markdown +++ b/website/docs/r/service_account.html.markdown @@ -81,7 +81,7 @@ The following arguments are supported: In addition to the arguments listed above, the following computed attributes are exported: -* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. +* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, `default_secret_name` will be empty. ## Import diff --git a/website/docs/r/service_account_v1.html.markdown b/website/docs/r/service_account_v1.html.markdown index 757ee49fe9..59e398b15f 100644 --- a/website/docs/r/service_account_v1.html.markdown +++ b/website/docs/r/service_account_v1.html.markdown @@ -81,7 +81,7 @@ The following arguments are supported: In addition to the arguments listed above, the following computed attributes are exported: -* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. +* `default_secret_name` - Name of the default secret, containing service account token, created & managed by the service. By default, the provider will try to find the secret containing the service account token that Kubernetes automatically created for the service account. Where there are multiple tokens and the provider cannot determine which was created by Kubernetes, this attribute will be empty. When only one token is associated with the service account, the provider will return this single token secret. Starting from version 1.24.0 Kubernetes does not automatically generate a token for service accounts, in this case, `default_secret_name` will be empty. ## Import