From 7302f722ffd21a193cea53e56695150b06e1cc08 Mon Sep 17 00:00:00 2001 From: karolgorc Date: Fri, 25 Oct 2024 19:34:38 +0200 Subject: [PATCH] Add plan-time validation of `name` on `google_compute_instance` (#11886) Co-authored-by: Cameron Thornton --- .../compute/resource_compute_instance.go.tmpl | 2 ++ .../terraform/verify/validation.go | 35 +++++++++++-------- .../terraform/verify/validation_test.go | 8 ++++- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl index f6f055e48e4b..3b1ff46a2250 100644 --- a/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl +++ b/mmv1/third_party/terraform/services/compute/resource_compute_instance.go.tmpl @@ -20,6 +20,7 @@ import ( "github.com/hashicorp/terraform-provider-google/google/tpgresource" transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport" + "github.com/hashicorp/terraform-provider-google/google/verify" {{ if eq $.TargetVersionName `ga` }} "google.golang.org/api/compute/v1" @@ -396,6 +397,7 @@ func ResourceComputeInstance() *schema.Resource { 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.`, }, diff --git a/mmv1/third_party/terraform/verify/validation.go b/mmv1/third_party/terraform/verify/validation.go index 2e9da45a5cf2..b3a0152e7436 100644 --- a/mmv1/third_party/terraform/verify/validation.go +++ b/mmv1/third_party/terraform/verify/validation.go @@ -22,7 +22,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 @@ -41,7 +41,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{ @@ -54,7 +54,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: @@ -194,19 +194,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/mmv1/third_party/terraform/verify/validation_test.go b/mmv1/third_party/terraform/verify/validation_test.go index b03fac489df7..19555861fb64 100644 --- a/mmv1/third_party/terraform/verify/validation_test.go +++ b/mmv1/third_party/terraform/verify/validation_test.go @@ -170,12 +170,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 {