Skip to content

Commit

Permalink
Update resource and data of 'kubernetes_(default_)service_account' to…
Browse files Browse the repository at this point in the history
… handle deprecated 'default_secret_name' in Kubernetes 1.24.0+
  • Loading branch information
Aleksandr Rybolovlev committed Jul 26, 2022
1 parent 94371f3 commit d732153
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 24 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 and deprecated in future",
},
},
}
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
47 changes: 43 additions & 4 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 and deprecated in future",
},
},
}
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) {
b, err := serverVersionGreaterThanOrEqual(conn, "1.24.0")
if err != nil {
return &api.Secret{}, err
}
if b {
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)

b, err := serverVersionGreaterThanOrEqual(conn, "1.24.0")
if err != nil {
return "", diag.FromErr(err)
}
if b {
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 @@ -237,6 +261,9 @@ func diffObjectReferences(origOrs []api.ObjectReference, ors []api.ObjectReferen
}

func resourceKubernetesServiceAccountRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {

var diagMessages []diag.Diagnostic

exists, err := resourceKubernetesServiceAccountExists(ctx, d, meta)
if err != nil {
return diag.FromErr(err)
Expand Down Expand Up @@ -284,6 +311,17 @@ func resourceKubernetesServiceAccountRead(ctx context.Context, d *schema.Resourc
}

defaultSecretName := d.Get("default_secret_name").(string)
b, err := serverVersionGreaterThanOrEqual(conn, "1.24.0")
if err != nil {
return diag.FromErr(err)
}
if b {
diagMessages = append(diagMessages, 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",
})
}
log.Printf("[DEBUG] Default secret name is %q", defaultSecretName)
secrets := flattenServiceAccountSecrets(svcAcc.Secrets, defaultSecretName)
log.Printf("[DEBUG] Flattened secrets: %#v", secrets)
Expand All @@ -292,7 +330,7 @@ func resourceKubernetesServiceAccountRead(ctx context.Context, d *schema.Resourc
return diag.FromErr(err)
}

return nil
return diagMessages
}

func resourceKubernetesServiceAccountUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
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
53 changes: 51 additions & 2 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 @@ -187,7 +219,7 @@ func TestAccKubernetesServiceAccount_update(t *testing.T) {
resource.TestCheckResourceAttr("kubernetes_service_account.test", "image_pull_secret.#", "0"),
resource.TestCheckResourceAttr("kubernetes_service_account.test", "automount_service_account_token", "true"),
testAccCheckServiceAccountImagePullSecrets(&conf, []*regexp.Regexp{}),
testAccCheckServiceAccountSecrets(&conf, []*regexp.Regexp{
testAccCheckServiceAccountDefaultSecrets(&conf, []*regexp.Regexp{
regexp.MustCompile("^" + name + "-token-[a-z0-9]+$"),
}),
),
Expand Down Expand Up @@ -219,7 +251,7 @@ func TestAccKubernetesServiceAccount_generatedName(t *testing.T) {
resource.TestCheckResourceAttrSet("kubernetes_service_account.test", "metadata.0.uid"),
resource.TestCheckResourceAttr("kubernetes_service_account.test", "automount_service_account_token", "true"),
testAccCheckServiceAccountImagePullSecrets(&conf, []*regexp.Regexp{}),
testAccCheckServiceAccountSecrets(&conf, []*regexp.Regexp{
testAccCheckServiceAccountDefaultSecrets(&conf, []*regexp.Regexp{
regexp.MustCompile("^" + prefix + "[a-z0-9]+-token-[a-z0-9]+$"),
}),
),
Expand Down Expand Up @@ -255,6 +287,15 @@ func matchLocalObjectReferenceName(lor []api.LocalObjectReference, expected []*r
return false
}

func testAccCheckServiceAccountDefaultSecrets(m *api.ServiceAccount, expected []*regexp.Regexp) resource.TestCheckFunc {
if clusterVersionGreaterThanOrEqual("1.24.0") {
return func(s *terraform.State) error {
return nil
}
}
return testAccCheckServiceAccountSecrets(m, expected)
}

func testAccCheckServiceAccountSecrets(m *api.ServiceAccount, expected []*regexp.Regexp) resource.TestCheckFunc {
return func(s *terraform.State) error {
if len(expected) == 0 && len(m.Secrets) == 0 {
Expand Down Expand Up @@ -397,6 +438,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

0 comments on commit d732153

Please sign in to comment.