Skip to content

Commit

Permalink
[TK-1373] Update resource and data of 'kubernetes_(default_)service_a…
Browse files Browse the repository at this point in the history
…ccount' to handle deprecated 'default_secret_name' in Kubernetes 1.24.0+ (#1792)

* Update resource and data of 'kubernetes_(default_)service_account' to handle deprecated 'default_secret_name' in Kubernetes 1.24.0+
  • Loading branch information
arybolovlev authored and BBBmau committed Aug 30, 2022
1 parent 6b3618a commit 8eea93e
Show file tree
Hide file tree
Showing 14 changed files with 129 additions and 27 deletions.
5 changes: 3 additions & 2 deletions kubernetes/data_source_kubernetes_service_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
}
Expand Down
13 changes: 7 additions & 6 deletions kubernetes/data_source_kubernetes_service_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
),
},
{
Expand All @@ -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"),
),
},
},
Expand All @@ -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),
Expand Down Expand Up @@ -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"
Expand Down
24 changes: 24 additions & 0 deletions kubernetes/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
1 change: 0 additions & 1 deletion kubernetes/resource_kubernetes_default_service_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion kubernetes/resource_kubernetes_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
45 changes: 42 additions & 3 deletions kubernetes/resource_kubernetes_service_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand Down
43 changes: 43 additions & 0 deletions kubernetes/resource_kubernetes_service_account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down
11 changes: 3 additions & 8 deletions kubernetes/structure_service_spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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") {
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion website/docs/d/service_account.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
2 changes: 1 addition & 1 deletion website/docs/d/service_account_v1.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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`

Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/default_service_account.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/default_service_account_v1.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/service_account.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/service_account_v1.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 8eea93e

Please sign in to comment.