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

Add path to invoke update without determining changed CRN data, add validator for name of secrets #4859

Expand Up @@ -45,6 +45,9 @@ func ResourceIBMContainerIngressSecretOpaque() *schema.Resource {
Required: true,
Description: "Secret name",
ForceNew: true,
ValidateFunc: validate.InvokeValidator(
"ibm_container_ingress_secret_opaque",
"secret_name"),
},
"secret_namespace": {
Type: schema.TypeString,
Expand Down Expand Up @@ -73,6 +76,17 @@ func ResourceIBMContainerIngressSecretOpaque() *schema.Resource {
Computed: true,
Description: "Status of the secret",
},
"update_secret": {
Type: schema.TypeBool,
Copy link
Collaborator

Choose a reason for hiding this comment

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

we will have issue if you want to update_secret multiple times .
If user add this field for first time and mark update_secret = true it goes a head and update secret but next time when he wants to update again it will not show any diff to apply since update_secret = true already exists in state file.

Generally for this kind of scenario we define "update_secret" as int when user want to change it he will just increment the value

similar to https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/container_vpc_cluster#retry_patch_version

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Optional: true,
Default: false,
Description: "Updates secret from secrets manager if true",
},
"last_updated_timestamp": {
Type: schema.TypeString,
Computed: true,
Description: "Timestamp secret was last updated",
},
"fields": {
Type: schema.TypeSet,
Required: true,
Expand Down Expand Up @@ -122,6 +136,16 @@ func ResourceIBMContainerIngressSecretOpaqueValidator() *validate.ResourceValida
CloudDataType: "cluster",
CloudDataRange: []string{"resolved_to:id"}})

validateSchema = append(validateSchema, validate.ValidateSchema{
Identifier: "secret_name",
ValidateFunctionIdentifier: validate.ValidateRegexpLen,
Type: validate.TypeString,
Required: true,
Regexp: `^([a-z0-9]([-a-z0-9]*[a-z0-9])?(.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$`,
MinValueLength: 1,
MaxValueLength: 63,
})

iBMContainerIngressInstanceValidator := validate.ResourceValidator{ResourceName: "ibm_container_ingress_secret_opaque", Schema: validateSchema}
return &iBMContainerIngressInstanceValidator
}
Expand Down Expand Up @@ -205,6 +229,7 @@ func resourceIBMContainerIngressSecretOpaqueRead(d *schema.ResourceData, meta in
d.Set("persistence", ingressSecretConfig.Persistence)
d.Set("user_managed", ingressSecretConfig.UserManaged)
d.Set("status", ingressSecretConfig.Status)
d.Set("last_updated_timestamp", ingressSecretConfig.LastUpdatedTimestamp)

if len(ingressSecretConfig.Fields) > 0 {
d.Set("fields", flex.FlattenOpaqueSecret(ingressSecretConfig.Fields))
Expand Down Expand Up @@ -261,6 +286,7 @@ func resourceIBMContainerIngressSecretOpaqueUpdate(d *schema.ResourceData, meta
Namespace: secretNamespace,
}

ingressAPI := ingressClient.Ingresses()
if d.HasChange("fields") {
oldList, newList := d.GetChange("fields")

Expand All @@ -276,7 +302,6 @@ func resourceIBMContainerIngressSecretOpaqueUpdate(d *schema.ResourceData, meta
remove := os.Difference(ns).List()
add := ns.Difference(os).List()

ingressAPI := ingressClient.Ingresses()
if len(remove) > 0 {
actualSecret, err := ingressAPI.GetIngressSecret(cluster, secretName, secretNamespace)
if err != nil {
Expand Down Expand Up @@ -342,8 +367,13 @@ func resourceIBMContainerIngressSecretOpaqueUpdate(d *schema.ResourceData, meta
return err
}
}
} else if d.HasChange("update_secret") {
// user wants to force an upstream secret update from secrets manager onto kube cluster w/out changing crn
_, err = ingressAPI.UpdateIngressSecret(params)
if err != nil {
return err
}
}

return resourceIBMContainerIngressSecretOpaqueRead(d, meta)
}

Expand Down
Expand Up @@ -5,6 +5,7 @@ package kubernetes_test

import (
"fmt"
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -70,7 +71,95 @@ func TestAccIBMContainerIngressSecretOpaque_Basic(t *testing.T) {
ResourceName: "ibm_container_ingress_secret_opaque.secret",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"region", "issuer_name"},
ImportStateVerifyIgnore: []string{"region", "issuer_name", "update_secret"},
},
},
})
}

func TestAccIBMContainerIngressSecretOpaque_InvalidName(t *testing.T) {
secretName := fmt.Sprintf(")-tf-container-ingress-secret-name-opaque-%d", acctest.RandIntRange(10, 100))

resource.Test(t, resource.TestCase{
PreCheck: func() { acc.TestAccPreCheck(t) },
Providers: acc.TestAccProviders,
CheckDestroy: testAccCheckIBMContainerIngressSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckIBMContainerIngressSecretOpaqueBasic(secretName),
ExpectError: regexp.MustCompile(".*should match regexp"),
},
},
})
}

func TestAccIBMContainerIngressSecretOpaque_ForceUpdate(t *testing.T) {
secretName := fmt.Sprintf("tf-container-ingress-secret-name-opaque-update-%d", acctest.RandIntRange(10, 100))

var originalTS string

resource.Test(t, resource.TestCase{
PreCheck: func() { acc.TestAccPreCheck(t) },
Providers: acc.TestAccProviders,
CheckDestroy: testAccCheckIBMContainerIngressSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckIBMContainerIngressSecretOpaqueForceUpdateCreate(secretName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "cluster", acc.ClusterName),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "secret_name", secretName),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "secret_namespace", "ibm-cert-store"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "persistence", "true"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "type", "Opaque"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "fields.#", "1"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "user_managed", "true"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "status", "created"),
resource.TestCheckResourceAttrWith("ibm_container_ingress_secret_opaque.secret", "last_updated_timestamp", func(value string) error {
originalTS = value
return nil
}),
),
},
{
Config: testAccCheckIBMContainerIngressSecretOpaqueForceUpdate(secretName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "cluster", acc.ClusterName),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "secret_name", secretName),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "secret_namespace", "ibm-cert-store"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "persistence", "true"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "type", "Opaque"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "fields.#", "1"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "user_managed", "true"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_opaque.secret", "status", "created"),
resource.TestCheckResourceAttrWith("ibm_container_ingress_secret_opaque.secret", "last_updated_timestamp", func(value string) error {
if originalTS == value {
return fmt.Errorf("error timestamp not changed, indicates update didnt go through. original: %s, actual: %s", originalTS, value)
}
return nil
}),
),
},
{
ResourceName: "ibm_container_ingress_secret_opaque.secret",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"region", "issuer_name", "update_secret"},
},
},
})
Expand Down Expand Up @@ -134,3 +223,30 @@ resource "ibm_container_ingress_secret_opaque" "secret" {
}
}`, secretName, "ibm-cert-store", acc.ClusterName, true, acc.SecretCRN)
}

func testAccCheckIBMContainerIngressSecretOpaqueForceUpdateCreate(secretName string) string {
return fmt.Sprintf(`
resource "ibm_container_ingress_secret_opaque" "secret" {
secret_name = "%s"
secret_namespace = "%s"
cluster = "%s"
persistence = "%t"
fields {
crn = "%s"
}
}`, secretName, "ibm-cert-store", acc.ClusterName, true, acc.SecretCRN)
}

func testAccCheckIBMContainerIngressSecretOpaqueForceUpdate(secretName string) string {
return fmt.Sprintf(`
resource "ibm_container_ingress_secret_opaque" "secret" {
secret_name = "%s"
secret_namespace = "%s"
cluster = "%s"
persistence = "%t"
update_secret = "%t"
fields {
crn = "%s"
}
}`, secretName, "ibm-cert-store", acc.ClusterName, true, true, acc.SecretCRN)
}
Expand Up @@ -44,6 +44,10 @@ func ResourceIBMContainerIngressSecretTLS() *schema.Resource {
Required: true,
Description: "Secret name",
ForceNew: true,
ValidateFunc: validate.InvokeValidator(
"ibm_container_ingress_secret_tls",
"secret_name",
),
},
"secret_namespace": {
Type: schema.TypeString,
Expand Down Expand Up @@ -91,6 +95,12 @@ func ResourceIBMContainerIngressSecretTLS() *schema.Resource {
Computed: true,
Description: "Timestamp secret was last updated",
},
"update_secret": {
Type: schema.TypeBool,
Optional: true,
Default: false,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Default can't be false for int type

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed - field now has exact same structure as others that fulfill same purpose

Description: "Updates secret from secrets manager if true",
},
},
}
}
Expand All @@ -106,6 +116,15 @@ func ResourceIBMContainerIngressSecretTLSValidator() *validate.ResourceValidator
CloudDataType: "cluster",
CloudDataRange: []string{"resolved_to:id"}})

validateSchema = append(validateSchema, validate.ValidateSchema{
Identifier: "secret_name",
ValidateFunctionIdentifier: validate.ValidateRegexpLen,
Type: validate.TypeString,
Required: true,
Regexp: `^([a-z0-9]([-a-z0-9]*[a-z0-9])?(.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$`,
MinValueLength: 1,
MaxValueLength: 63,
})
iBMContainerIngressInstanceValidator := validate.ResourceValidator{ResourceName: "ibm_container_ingress_secret_tls", Schema: validateSchema}
return &iBMContainerIngressInstanceValidator
}
Expand Down Expand Up @@ -228,14 +247,20 @@ func resourceIBMContainerIngressSecretTLSUpdate(d *schema.ResourceData, meta int
Namespace: secretNamespace,
}

ingressAPI := ingressClient.Ingresses()
if d.HasChange("cert_crn") {
params.CRN = d.Get("cert_crn").(string)

ingressAPI := ingressClient.Ingresses()
_, err := ingressAPI.UpdateIngressSecret(params)
if err != nil {
return err
}
} else if d.HasChange("update_secret") {
// user wants to force an upstream secret update from secrets manager onto kube cluster w/out changing crn
_, err = ingressAPI.UpdateIngressSecret(params)
if err != nil {
return err
}
}

return resourceIBMContainerIngressSecretTLSRead(d, meta)
Expand Down
Expand Up @@ -5,6 +5,7 @@ package kubernetes_test

import (
"fmt"
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -64,7 +65,89 @@ func TestAccIBMContainerIngressSecretTLS_Basic(t *testing.T) {
ResourceName: "ibm_container_ingress_secret_tls.secret",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"region", "issuer_name"},
ImportStateVerifyIgnore: []string{"region", "issuer_name", "update_secret"},
},
},
})
}

func TestAccIBMContainerIngressSecretTLS_InvalidName(t *testing.T) {
secretName := fmt.Sprintf("-)tf-container-ingress-secret-name-%d", acctest.RandIntRange(10, 100))

resource.Test(t, resource.TestCase{
Providers: acc.TestAccProviders,
CheckDestroy: testAccCheckIBMContainerIngressSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckIBMContainerIngressSecretTLSBasic(secretName),
ExpectError: regexp.MustCompile(".*should match regexp"),
},
},
})
}

// test ability to flip update_secret field to get upstream secret update from secrets manager instance via ingress API
func TestAccIBMContainerIngressSecretTLS_BasicForceUpdate(t *testing.T) {
secretName := fmt.Sprintf("tf-container-ingress-secret-name-%d", acctest.RandIntRange(10, 100))

var originalTS string
resource.Test(t, resource.TestCase{
PreCheck: func() { acc.TestAccPreCheck(t) },
Providers: acc.TestAccProviders,
CheckDestroy: testAccCheckIBMContainerIngressSecretDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckIBMContainerIngressSecretTLSBasic(secretName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "cluster", acc.ClusterName),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "secret_name", secretName),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "secret_namespace", "ibm-cert-store"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "cert_crn", acc.CertCRN),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "persistence", "true"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "type", "TLS"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "user_managed", "true"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "status", "created"),

resource.TestCheckResourceAttrWith("ibm_container_ingress_secret_tls.secret", "last_updated_timestamp", func(value string) error {
originalTS = value
return nil
}),
),
},
{
Config: testAccCheckIBMContainerIngressSecretTLSForceUpdate(secretName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "secret_name", secretName),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "secret_namespace", "ibm-cert-store"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "cert_crn", acc.CertCRN),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "user_managed", "true"),
resource.TestCheckResourceAttr(
"ibm_container_ingress_secret_tls.secret", "status", "created"),
resource.TestCheckResourceAttrWith("ibm_container_ingress_secret_tls.secret", "last_updated_timestamp", func(value string) error {
if originalTS == value {
return fmt.Errorf("error timestamp not changed, indicates update didnt go through. original: %s, actual: %s", originalTS, value)
}
return nil
}),
),
},
{
ResourceName: "ibm_container_ingress_secret_tls.secret",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"region", "issuer_name", "update_secret"},
},
},
})
Expand Down Expand Up @@ -121,3 +204,15 @@ resource "ibm_container_ingress_secret_tls" "secret" {
persistence = "%t"
}`, acc.ClusterName, secretName, "ibm-cert-store", acc.UpdatedCertCRN, true)
}

func testAccCheckIBMContainerIngressSecretTLSForceUpdate(secretName string) string {
return fmt.Sprintf(`
resource "ibm_container_ingress_secret_tls" "secret" {
cluster = "%s"
secret_name = "%s"
secret_namespace = "%s"
cert_crn = "%s"
persistence = "%t"
update_secret = "%t"
}`, acc.ClusterName, secretName, "ibm-cert-store", acc.CertCRN, true, true)
}