Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TK-1373] Update resource and data of 'kubernetes_(default_)service_account' to handle deprecated 'default_secret_name' in Kubernetes 1.24.0+ #1792

Merged
merged 5 commits into from
Aug 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We make use of this over here too:

serverVersion, err := conn.ServerVersion()

Might be worth trying to unify the two.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, made a small improvement. 👍🏻

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
}
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