From 245261eb64455a61433763be0b9f97f70f443595 Mon Sep 17 00:00:00 2001 From: Modular Magician Date: Fri, 25 Oct 2024 17:38:34 +0000 Subject: [PATCH] Add plan-time validation of `name` on `google_compute_instance` (#11886) Co-authored-by: Cameron Thornton [upstream:4f8bd9304149a31b696139ce9c5086f3b42e00b9] Signed-off-by: Modular Magician --- .changelog/11886.txt | 3 ++ .../compute/resource_compute_instance.go | 10 +++--- google-beta/verify/validation.go | 35 +++++++++++-------- google-beta/verify/validation_test.go | 8 ++++- 4 files changed, 37 insertions(+), 19 deletions(-) create mode 100644 .changelog/11886.txt diff --git a/.changelog/11886.txt b/.changelog/11886.txt new file mode 100644 index 0000000000..7d318ad4e9 --- /dev/null +++ b/.changelog/11886.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +compute: added plan-time validation to `name` on `google_compute_instance` +``` \ No newline at end of file diff --git a/google-beta/services/compute/resource_compute_instance.go b/google-beta/services/compute/resource_compute_instance.go index 0009e5e8a9..f7e50c5d0b 100644 --- a/google-beta/services/compute/resource_compute_instance.go +++ b/google-beta/services/compute/resource_compute_instance.go @@ -22,6 +22,7 @@ import ( "github.com/hashicorp/terraform-provider-google-beta/google-beta/tpgresource" transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport" + "github.com/hashicorp/terraform-provider-google-beta/google-beta/verify" compute "google.golang.org/api/compute/v0.beta" ) @@ -389,10 +390,11 @@ func ResourceComputeInstance() *schema.Resource { }, "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: `The name of the instance. One of name or self_link must be provided.`, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: verify.ValidateRFC1035Name(1, 63), + Description: `The name of the instance. One of name or self_link must be provided.`, }, "network_interface": { diff --git a/google-beta/verify/validation.go b/google-beta/verify/validation.go index 710a1a106e..b046ff60e6 100644 --- a/google-beta/verify/validation.go +++ b/google-beta/verify/validation.go @@ -24,7 +24,7 @@ const ( SubnetworkLinkRegex = "projects/(" + ProjectRegex + ")/regions/(" + RegionRegex + ")/subnetworks/(" + SubnetworkRegex + ")$" - RFC1035NameTemplate = "[a-z](?:[-a-z0-9]{%d,%d}[a-z0-9])" + RFC1035NameTemplate = "[a-z]([-a-z0-9]%v[a-z0-9])?" CloudIoTIdRegex = "^[a-zA-Z][-a-zA-Z0-9._+~%]{2,254}$" // Format of default Compute service accounts created by Google @@ -43,7 +43,7 @@ var ( // The first and last characters have different restrictions, than // the middle characters. The middle characters length must be between // 4 and 28 since the first and last character are excluded. - ServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, 4, 28) + ServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, "{4,28}") ServiceAccountLinkRegexPrefix = "projects/" + ProjectRegexWildCard + "/serviceAccounts/" PossibleServiceAccountNames = []string{ @@ -56,7 +56,7 @@ var ( ServiceAccountKeyNameRegex = ServiceAccountLinkRegexPrefix + "(.+)/keys/(.+)" // Format of service accounts created through the API - CreatedServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, 4, 28) + "@" + ProjectNameInDNSFormRegex + "\\.iam\\.gserviceaccount\\.com$" + CreatedServiceAccountNameRegex = fmt.Sprintf(RFC1035NameTemplate, "{4,28}") + "@" + ProjectNameInDNSFormRegex + "\\.iam\\.gserviceaccount\\.com$" // Format of service-created service account // examples are: @@ -196,19 +196,26 @@ func ValidateRFC3339Time(v interface{}, k string) (warnings []string, errors []e } func ValidateRFC1035Name(min, max int) schema.SchemaValidateFunc { - if min < 2 || max < min { - return func(i interface{}, k string) (s []string, errors []error) { - if min < 2 { - errors = append(errors, fmt.Errorf("min must be at least 2. Got: %d", min)) - } - if max < min { - errors = append(errors, fmt.Errorf("max must greater than min. Got [%d, %d]", min, max)) - } - return + return func(i interface{}, k string) (s []string, errors []error) { + value := i.(string) + re := fmt.Sprintf("^"+RFC1035NameTemplate+"$", "*") + if min < 1 { + errors = append(errors, fmt.Errorf("min must be at least 1. Got: %d", min)) + } + if max < min { + errors = append(errors, fmt.Errorf("max must greater than min. Got [%d, %d]", min, max)) + } + + if len(value) < min || len(value) > max { + errors = append(errors, fmt.Errorf("%q (%q) must be between %d and %d characters long", k, value, min, max)) + } + + if !regexp.MustCompile(re).MatchString(value) { + errors = append(errors, fmt.Errorf("%q (%q) must match regex %q", k, value, re)) } - } - return ValidateRegexp(fmt.Sprintf("^"+RFC1035NameTemplate+"$", min-2, max-2)) + return + } } func ValidateIpCidrRange(v interface{}, k string) (warnings []string, errors []error) { diff --git a/google-beta/verify/validation_test.go b/google-beta/verify/validation_test.go index b6e9a27b8d..fc2a6e2973 100644 --- a/google-beta/verify/validation_test.go +++ b/google-beta/verify/validation_test.go @@ -172,12 +172,18 @@ func TestValidateRFC1035Name(t *testing.T) { {TestName: "valid lower bound", Min: 12, Max: 30, Value: "a-valid-name"}, {TestName: "valid upper bound", Min: 6, Max: 12, Value: "a-valid-name"}, {TestName: "valid with numbers", Min: 6, Max: 30, Value: "valid000-name"}, + {TestName: "valid shortest", Min: 1, Max: 63, Value: "a"}, + {TestName: "valid longest", Min: 1, Max: 63, Value: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, {TestName: "must start with a letter", Min: 6, Max: 10, Value: "0invalid", ExpectError: true}, {TestName: "cannot end with a dash", Min: 6, Max: 10, Value: "invalid-", ExpectError: true}, {TestName: "too short", Min: 6, Max: 10, Value: "short", ExpectError: true}, {TestName: "too long", Min: 6, Max: 10, Value: "toolooooong", ExpectError: true}, - {TestName: "min too small", Min: 1, Max: 10, Value: "", ExpectError: true}, + {TestName: "min too small", Min: 0, Max: 10, Value: "", ExpectError: true}, {TestName: "min < max", Min: 6, Max: 5, Value: "", ExpectError: true}, + {TestName: "min < max", Min: 6, Max: 5, Value: "", ExpectError: true}, + {TestName: "invalid smallest possible w/ higher limit", Min: 2, Max: 63, Value: "a", ExpectError: true}, + {TestName: "invalid smallest possible hyphen", Min: 1, Max: 1, Value: "-", ExpectError: true}, + {TestName: "invalid smallest possible ends with hyphen", Min: 2, Max: 63, Value: "a-", ExpectError: true}, } for _, c := range cases {