diff --git a/mmv1/provider/terraform/common~compile.yaml b/mmv1/provider/terraform/common~compile.yaml index 2ca0e491bd18..266818c89957 100644 --- a/mmv1/provider/terraform/common~compile.yaml +++ b/mmv1/provider/terraform/common~compile.yaml @@ -102,6 +102,12 @@ -%> '<%= dir -%>/<%= fname.delete_suffix(".erb") -%>': 'third_party/terraform/framework_models/<%= fname -%>' <% end -%> +<% + Dir["third_party/terraform/functions/*.go.erb"].each do |file_path| + fname = file_path.split('/')[-1] +-%> +'<%= dir -%>/functions/<%= fname.delete_suffix(".erb") -%>': 'third_party/terraform/functions/<%= fname -%>' +<% end -%> <% Dir["third_party/terraform/scripts/**/*.erb"].each do |file_path| fname = file_path.delete_prefix("third_party/terraform/") diff --git a/mmv1/provider/terraform/common~copy.yaml b/mmv1/provider/terraform/common~copy.yaml index 1409c612a3f6..1565f5b67fea 100644 --- a/mmv1/provider/terraform/common~copy.yaml +++ b/mmv1/provider/terraform/common~copy.yaml @@ -122,6 +122,13 @@ '<%= dir -%>/envvar/<%= fname -%>': 'third_party/terraform/envvar/<%= fname -%>' <% end -%> +<% + Dir["third_party/terraform/functions/*.go"].each do |file_path| + fname = file_path.split('/')[-1] +-%> +'<%= dir -%>/functions/<%= fname -%>': 'third_party/terraform/functions/<%= fname -%>' +<% end -%> + <% Dir["third_party/terraform/scripts/**/*.*"].each do |file_path| next if file_path.end_with?('.erb') diff --git a/mmv1/third_party/terraform/functions/element_from_id.go b/mmv1/third_party/terraform/functions/element_from_id.go new file mode 100644 index 000000000000..3cd73983c92e --- /dev/null +++ b/mmv1/third_party/terraform/functions/element_from_id.go @@ -0,0 +1,35 @@ +package functions + +import ( + "context" + "fmt" + "log" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework/function" +) + +// ValidateElementFromIdArguments is reusable validation logic used in provider-defined functions that use the GetElementFromId function +func ValidateElementFromIdArguments(ctx context.Context, input string, regex *regexp.Regexp, pattern string, functionName string) *function.FuncError { + submatches := regex.FindAllStringSubmatchIndex(input, -1) + + // Zero matches means unusable input; error returned + if len(submatches) == 0 { + return function.NewArgumentFuncError(0, fmt.Sprintf("The input string \"%s\" doesn't contain the expected pattern \"%s\".", input, pattern)) + } + + // >1 matches means input usable but not ideal; debug log + if len(submatches) > 1 { + log.Printf("[DEBUG] Provider-defined function %s was called with input string: %s. This contains more than one match for the pattern %s. Terraform will use the first found match.", functionName, input, pattern) + } + + return nil +} + +// GetElementFromId is reusable logic that is used in multiple provider-defined functions for pulling elements out of self links and ids of resources and data sources +func GetElementFromId(input string, regex *regexp.Regexp, template string) string { + submatches := regex.FindAllStringSubmatchIndex(input, -1) + submatch := submatches[0] // Take the only / left-most submatch + dst := []byte{} + return string(regex.ExpandString(dst, template, input, submatch)) +} diff --git a/mmv1/third_party/terraform/functions/element_from_id_internal_test.go b/mmv1/third_party/terraform/functions/element_from_id_internal_test.go new file mode 100644 index 000000000000..4921bc7c8f4e --- /dev/null +++ b/mmv1/third_party/terraform/functions/element_from_id_internal_test.go @@ -0,0 +1,84 @@ +package functions_test + +import ( + "context" + "regexp" + "testing" + + tpg_functions "github.com/hashicorp/terraform-provider-google/google/functions" +) + +func TestFunctionInternals_ValidateElementFromIdArguments(t *testing.T) { + + // Values here are matched to test case values below + regex := regexp.MustCompile("two/(?P[^/]+)/") + pattern := "two/{two}/" + + cases := map[string]struct { + Input string + ExpectedElement string + ExpectError bool + }{ + "it sets an error if no match is found": { + Input: "one/element-1/three/element-3", + ExpectError: true, + }, + "it doesn't set an error if more than one match is found": { + Input: "two/element-2/two/element-2/two/element-2", + ExpectedElement: "element-2", + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + + // Arrange + ctx := context.Background() + + // Act + err := tpg_functions.ValidateElementFromIdArguments(ctx, tc.Input, regex, pattern, "function-name-here") // last arg value is inconsequential for this test + + // Assert + if err != nil && !tc.ExpectError { + t.Fatalf("Unexpected error(s) were set in response diags: %s", err.Text) + } + if err == nil && tc.ExpectError { + t.Fatal("Expected error(s) to be set in response diags, but there were none.") + } + }) + } +} + +func TestFunctionInternals_GetElementFromId(t *testing.T) { + + // Values here are matched to test case values below + regex := regexp.MustCompile("two/(?P[^/]+)/") + template := "$Element" + + cases := map[string]struct { + Input string + ExpectedElement string + }{ + "it can pull out a value from a string using a regex with a submatch": { + Input: "one/element-1/two/element-2/three/element-3", + ExpectedElement: "element-2", + }, + "it will pull out the first value from a string with more than one submatch": { + Input: "one/element-1/two/element-2/two/not-this-one/three/element-3", + ExpectedElement: "element-2", + }, + } + + for tn, tc := range cases { + t.Run(tn, func(t *testing.T) { + + // Act + result := tpg_functions.GetElementFromId(tc.Input, regex, template) + + // Assert + if result != tc.ExpectedElement { + t.Fatalf("Expected function logic to retrieve %s from input %s, got %s", tc.ExpectedElement, tc.Input, result) + } + }) + } +} diff --git a/mmv1/third_party/terraform/functions/location_from_id.go b/mmv1/third_party/terraform/functions/location_from_id.go new file mode 100644 index 000000000000..3aaf4ce554b8 --- /dev/null +++ b/mmv1/third_party/terraform/functions/location_from_id.go @@ -0,0 +1,62 @@ +package functions + +import ( + "context" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework/function" +) + +var _ function.Function = LocationFromIdFunction{} + +func NewLocationFromIdFunction() function.Function { + return &LocationFromIdFunction{ + name: "location_from_id", + } +} + +type LocationFromIdFunction struct { + name string // Makes function name available in Run logic for logging purposes +} + +func (f LocationFromIdFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { + resp.Name = f.name +} + +func (f LocationFromIdFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { + resp.Definition = function.Definition{ + Summary: "Returns the location name within a provided resource id, self link, or OP style resource name.", + Description: "Takes a single string argument, which should be a resource id, self link, or OP style resource name. This function will either return the location name from the input string or raise an error due to no location being present in the string. The function uses the presence of \"locations/{{location}}/\" in the input string to identify the location name, e.g. when the function is passed the id \"projects/my-project/locations/us-central1/services/my-service\" as an argument it will return \"us-central1\".", + Parameters: []function.Parameter{ + function.StringParameter{ + Name: "id", + Description: "A string of a resource's id, a resource's self link, or an OP style resource name. For example, \"projects/my-project/locations/us-central1/services/my-service\" and \"https://run.googleapis.com/v2/projects/my-project/locations/us-central1/services/my-service\" are valid values containing locations", + }, + }, + Return: function.StringReturn{}, + } +} + +func (f LocationFromIdFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { + // Load arguments from function call + var arg0 string + resp.Error = function.ConcatFuncErrors(req.Arguments.GetArgument(ctx, 0, &arg0)) + if resp.Error != nil { + return + } + + // Prepare how we'll identify location name from input string + regex := regexp.MustCompile("locations/(?P[^/]+)/") // Should match the pattern below + template := "$LocationName" // Should match the submatch identifier in the regex + pattern := "locations/{location}/" // Human-readable pseudo-regex pattern used in errors and warnings + + // Validate input + resp.Error = function.ConcatFuncErrors(ValidateElementFromIdArguments(ctx, arg0, regex, pattern, f.name)) + if resp.Error != nil { + return + } + + // Get and return element from input string + location := GetElementFromId(arg0, regex, template) + resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, location)) +} diff --git a/mmv1/third_party/terraform/functions/location_from_id_internal_test.go b/mmv1/third_party/terraform/functions/location_from_id_internal_test.go new file mode 100644 index 000000000000..2ee280a7668e --- /dev/null +++ b/mmv1/third_party/terraform/functions/location_from_id_internal_test.go @@ -0,0 +1,99 @@ +package functions + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/function" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +func TestFunctionRun_location_from_id(t *testing.T) { + t.Parallel() + + location := "us-central1" + + // Happy path inputs + validId := fmt.Sprintf("projects/my-project/locations/%s/services/my-service", location) + validSelfLink := fmt.Sprintf("https://run.googleapis.com/v2/%s", validId) + validOpStyleResourceName := fmt.Sprintf("//run.googleapis.com/v2/%s", validId) + + // Unhappy path inputs + repetitiveInput := fmt.Sprintf("https://run.googleapis.com/v2/projects/my-project/locations/%s/locations/not-this-one/services/my-service", location) // Multiple /locations/{{location}}/ + invalidInput := "zones/us-central1-c/instances/my-instance" + + testCases := map[string]struct { + request function.RunRequest + expected function.RunResponse + }{ + "it returns the expected output value when given a valid resource id input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validId)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(location)), + }, + }, + "it returns the expected output value when given a valid resource self_link input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validSelfLink)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(location)), + }, + }, + "it returns the expected output value when given a valid OP style resource name input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validOpStyleResourceName)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(location)), + }, + }, + "it returns the first submatch (with no error) when given repetitive input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(repetitiveInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(location)), + }, + }, + "it returns an error when given input with no submatches": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(invalidInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringNull()), + Error: function.NewArgumentFuncError(0, fmt.Sprintf("The input string \"%s\" doesn't contain the expected pattern \"locations/{location}/\".", invalidInput)), + }, + }, + } + + for name, testCase := range testCases { + tn, tc := name, testCase + + t.Run(tn, func(t *testing.T) { + t.Parallel() + + // Arrange + got := function.RunResponse{ + Result: function.NewResultData(basetypes.StringValue{}), + } + + // Act + NewLocationFromIdFunction().Run(context.Background(), tc.request, &got) + + // Assert + if diff := cmp.Diff(got.Result, tc.expected.Result); diff != "" { + t.Errorf("unexpected diff between expected and received result: %s", diff) + } + if diff := cmp.Diff(got.Error, tc.expected.Error); diff != "" { + t.Errorf("unexpected diff between expected and received errors: %s", diff) + } + }) + } +} diff --git a/mmv1/third_party/terraform/functions/location_from_id_test.go b/mmv1/third_party/terraform/functions/location_from_id_test.go new file mode 100644 index 000000000000..26b73d2bc26b --- /dev/null +++ b/mmv1/third_party/terraform/functions/location_from_id_test.go @@ -0,0 +1,75 @@ +package functions_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccProviderFunction_location_from_id(t *testing.T) { + t.Parallel() + // Skipping due to requiring TF 1.8.0 in VCR systems : https://github.com/hashicorp/terraform-provider-google/issues/17451 + acctest.SkipIfVcr(t) + + location := "us-central1" + locationRegex := regexp.MustCompile(fmt.Sprintf("^%s$", location)) + + context := map[string]interface{}{ + "function_name": "location_from_id", + "output_name": "location", + "resource_name": fmt.Sprintf("tf-test-location-id-func-%s", acctest.RandString(t, 10)), + "resource_location": location, + } + + acctest.VcrTest(t, resource.TestCase{ + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Can get the location from a resource's id in one step + // Uses google_cloud_run_service resource's id attribute with format projects/{project}/locations/{location}/services/{service}. + Config: testProviderFunction_get_location_from_resource_id(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), locationRegex), + ), + }, + }, + }) +} + +func testProviderFunction_get_location_from_resource_id(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_cloud_run_service" "default" { + name = "%{resource_name}" + location = "%{resource_location}" + + template { + spec { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + } + } + } + + traffic { + percent = 100 + latest_revision = true + } +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_cloud_run_service.default.id) +} +`, context) +} diff --git a/mmv1/third_party/terraform/functions/name_from_id.go b/mmv1/third_party/terraform/functions/name_from_id.go new file mode 100644 index 000000000000..860342517afc --- /dev/null +++ b/mmv1/third_party/terraform/functions/name_from_id.go @@ -0,0 +1,62 @@ +package functions + +import ( + "context" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework/function" +) + +var _ function.Function = NameFromIdFunction{} + +func NewNameFromIdFunction() function.Function { + return &NameFromIdFunction{ + name: "name_from_id", + } +} + +type NameFromIdFunction struct { + name string +} + +func (f NameFromIdFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { + resp.Name = f.name +} + +func (f NameFromIdFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { + resp.Definition = function.Definition{ + Summary: "Returns the short-form name of a resource within a provided resource's id, resource URI, self link, or full resource name.", + Description: "Takes a single string argument, which should be a resource's id, resource URI, self link, or full resource name. This function will return the short-form name of a resource from the input string, or raise an error due to a problem with the input string. The function returns the final element in the input string as the resource's name, e.g. when the function is passed the id \"projects/my-project/zones/us-central1-c/instances/my-instance\" as an argument it will return \"my-instance\".", + Parameters: []function.Parameter{ + function.StringParameter{ + Name: "id", + Description: "A string of a resource's id, resource URI, self link, or full resource name. For example, \"projects/my-project/zones/us-central1-c/instances/my-instance\", \"https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-c/instances/my-instance\" and \"//gkehub.googleapis.com/projects/my-project/locations/us-central1/memberships/my-membership\" are valid values", + }, + }, + Return: function.StringReturn{}, + } +} + +func (f NameFromIdFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { + // Load arguments from function call + var arg0 string + resp.Error = function.ConcatFuncErrors(req.Arguments.GetArgument(ctx, 0, &arg0)) + if resp.Error != nil { + return + } + + // Prepare how we'll identify resource name from input string + regex := regexp.MustCompile("/(?P[^/]+)$") // Should match the pattern below + template := "$ResourceName" // Should match the submatch identifier in the regex + pattern := "resourceType/{name}$" // Human-readable pseudo-regex pattern used in errors and warnings + + // Validate input + resp.Error = function.ConcatFuncErrors(ValidateElementFromIdArguments(ctx, arg0, regex, pattern, f.name)) + if resp.Error != nil { + return + } + + // Get and return element from input string + name := GetElementFromId(arg0, regex, template) + resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, name)) +} diff --git a/mmv1/third_party/terraform/functions/name_from_id_internal_test.go b/mmv1/third_party/terraform/functions/name_from_id_internal_test.go new file mode 100644 index 000000000000..b51849728f86 --- /dev/null +++ b/mmv1/third_party/terraform/functions/name_from_id_internal_test.go @@ -0,0 +1,90 @@ +package functions + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/function" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +func TestFunctionRun_name_from_id(t *testing.T) { + t.Parallel() + + name := "foobar" + + // Happy path inputs + validId := fmt.Sprintf("projects/my-project/zones/us-central1-c/instances/%s", name) + validSelfLink := fmt.Sprintf("https://www.googleapis.com/compute/v1/%s", validId) + validOpStyleResourceName := fmt.Sprintf("//gkehub.googleapis.com/projects/my-project/locations/us-central1/memberships/%s", name) + + // Unhappy path inputs + invalidInput := "this isn't a URI or id" + + testCases := map[string]struct { + request function.RunRequest + expected function.RunResponse + }{ + "it returns the expected output value when given a valid resource id input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validId)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(name)), + }, + }, + "it returns the expected output value when given a valid resource self_link input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validSelfLink)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(name)), + }, + }, + "it returns the expected output value when given a valid OP style resource name input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validOpStyleResourceName)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(name)), + }, + }, + "it returns an error when given input with no submatches": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(invalidInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringNull()), + Error: function.NewArgumentFuncError(0, fmt.Sprintf("The input string \"%s\" doesn't contain the expected pattern \"resourceType/{name}$\".", invalidInput)), + }, + }, + } + + for name, testCase := range testCases { + tn, tc := name, testCase + + t.Run(tn, func(t *testing.T) { + t.Parallel() + + // Arrange + got := function.RunResponse{ + Result: function.NewResultData(basetypes.StringValue{}), + } + + // Act + NewNameFromIdFunction().Run(context.Background(), tc.request, &got) + + // Assert + if diff := cmp.Diff(got.Result, tc.expected.Result); diff != "" { + t.Errorf("unexpected diff between expected and received result: %s", diff) + } + if diff := cmp.Diff(got.Error, tc.expected.Error); diff != "" { + t.Errorf("unexpected diff between expected and received errors: %s", diff) + } + }) + } +} diff --git a/mmv1/third_party/terraform/functions/name_from_id_test.go b/mmv1/third_party/terraform/functions/name_from_id_test.go new file mode 100644 index 000000000000..8eaf139918c3 --- /dev/null +++ b/mmv1/third_party/terraform/functions/name_from_id_test.go @@ -0,0 +1,88 @@ +package functions_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccProviderFunction_name_from_id(t *testing.T) { + t.Parallel() + // Skipping due to requiring TF 1.8.0 in VCR systems : https://github.com/hashicorp/terraform-provider-google/issues/17451 + acctest.SkipIfVcr(t) + + context := map[string]interface{}{ + "function_name": "name_from_id", + "output_name": "name", + "resource_name": fmt.Sprintf("tf-test-name-id-func-%s", acctest.RandString(t, 10)), + } + + nameRegex := regexp.MustCompile(fmt.Sprintf("^%s$", context["resource_name"])) + + acctest.VcrTest(t, resource.TestCase{ + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Can get the name from a resource's id in one step + // Uses google_pubsub_topic resource's id attribute with format projects/{{project}}/topics/{{name}} + Config: testProviderFunction_get_name_from_resource_id(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), nameRegex), + ), + }, + { + // Can get the name from a resource's self_link in one step + // Uses google_compute_disk resource's self_link attribute + Config: testProviderFunction_get_name_from_resource_self_link(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), nameRegex), + ), + }, + }, + }) +} + +func testProviderFunction_get_name_from_resource_id(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_pubsub_topic" "default" { + name = "%{resource_name}" +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_pubsub_topic.default.id) +} +`, context) +} + +func testProviderFunction_get_name_from_resource_self_link(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_disk" "default" { + name = "%{resource_name}" +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_compute_disk.default.self_link) +} +`, context) +} diff --git a/mmv1/third_party/terraform/functions/project_from_id.go b/mmv1/third_party/terraform/functions/project_from_id.go new file mode 100644 index 000000000000..70b27a74123f --- /dev/null +++ b/mmv1/third_party/terraform/functions/project_from_id.go @@ -0,0 +1,62 @@ +package functions + +import ( + "context" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework/function" +) + +var _ function.Function = ProjectFromIdFunction{} + +func NewProjectFromIdFunction() function.Function { + return &ProjectFromIdFunction{ + name: "project_from_id", + } +} + +type ProjectFromIdFunction struct { + name string // Makes function name available in Run logic for logging purposes +} + +func (f ProjectFromIdFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { + resp.Name = f.name +} + +func (f ProjectFromIdFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { + resp.Definition = function.Definition{ + Summary: "Returns the project within a provided resource's id, resource URI, self link, or full resource name.", + Description: "Takes a single string argument, which should be a resource's id, resource URI, self link, or full resource name. This function will either return the project name from the input string or raise an error due to no project being present in the string. The function uses the presence of \"projects/{{project}}/\" in the input string to identify the project name, e.g. when the function is passed the id \"projects/my-project/zones/us-central1-c/instances/my-instance\" as an argument it will return \"my-project\".", + Parameters: []function.Parameter{ + function.StringParameter{ + Name: "id", + Description: "A string of a resource's id, resource URI, self link, or full resource name. For example, \"projects/my-project/zones/us-central1-c/instances/my-instance\", \"https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-c/instances/my-instance\" and \"//gkehub.googleapis.com/projects/my-project/locations/us-central1/memberships/my-membership\" are valid values", + }, + }, + Return: function.StringReturn{}, + } +} + +func (f ProjectFromIdFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { + // Load arguments from function call + var arg0 string + resp.Error = function.ConcatFuncErrors(req.Arguments.GetArgument(ctx, 0, &arg0)) + if resp.Error != nil { + return + } + + // Prepare how we'll identify project id from input string + regex := regexp.MustCompile("projects/(?P[^/]+)/") // Should match the pattern below + template := "$ProjectId" // Should match the submatch identifier in the regex + pattern := "projects/{project}/" // Human-readable pseudo-regex pattern used in errors and warnings + + // Validate input + resp.Error = function.ConcatFuncErrors(ValidateElementFromIdArguments(ctx, arg0, regex, pattern, f.name)) + if resp.Error != nil { + return + } + + // Get and return element from input string + projectId := GetElementFromId(arg0, regex, template) + resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, projectId)) +} diff --git a/mmv1/third_party/terraform/functions/project_from_id_internal_test.go b/mmv1/third_party/terraform/functions/project_from_id_internal_test.go new file mode 100644 index 000000000000..dd6155e4d0c6 --- /dev/null +++ b/mmv1/third_party/terraform/functions/project_from_id_internal_test.go @@ -0,0 +1,101 @@ +package functions + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/function" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +func TestFunctionRun_project_from_id(t *testing.T) { + t.Parallel() + + projectId := "my-project" + + // Happy path inputs + validId := fmt.Sprintf("projects/%s/zones/us-central1-c/instances/my-instance", projectId) + validSelfLink := fmt.Sprintf("https://www.googleapis.com/compute/v1/%s", validId) + validOpStyleResourceName := fmt.Sprintf("//gkehub.googleapis.com/projects/%s/locations/us-central1/memberships/my-membership", projectId) + + // Unhappy path inputs + repetitiveInput := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%s/projects/not-this-1/projects/not-this-2/instances/my-instance", projectId) // Multiple /projects/{{project}}/ + invalidInput := "zones/us-central1-c/instances/my-instance" + + testCases := map[string]struct { + request function.RunRequest + expected function.RunResponse + }{ + "it returns the expected output value when given a valid resource id input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validId)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(projectId)), + }, + }, + "it returns the expected output value when given a valid resource self_link input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validSelfLink)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(projectId)), + }, + }, + "it returns the expected output value when given a valid OP style resource name input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validOpStyleResourceName)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(projectId)), + }, + }, + "it returns the first submatch (with no error) when given repetitive input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(repetitiveInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(projectId)), + }, + }, + "it returns an error when given input with no submatches": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(invalidInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringNull()), + Error: function.NewArgumentFuncError( + 0, + fmt.Sprintf("The input string \"%s\" doesn't contain the expected pattern \"projects/{project}/\".", invalidInput)), + }, + }, + } + + for name, testCase := range testCases { + tn, tc := name, testCase + + t.Run(tn, func(t *testing.T) { + t.Parallel() + + // Arrange + got := function.RunResponse{ + Result: function.NewResultData(basetypes.StringValue{}), + } + + // Act + NewProjectFromIdFunction().Run(context.Background(), tc.request, &got) + + // Assert + if diff := cmp.Diff(got.Result, tc.expected.Result); diff != "" { + t.Errorf("unexpected diff between expected and received result: %s", diff) + } + if diff := cmp.Diff(got.Error, tc.expected.Error); diff != "" { + t.Errorf("unexpected diff between expected and received errors: %s", diff) + } + }) + } +} diff --git a/mmv1/third_party/terraform/functions/project_from_id_test.go b/mmv1/third_party/terraform/functions/project_from_id_test.go new file mode 100644 index 000000000000..34f0f624af59 --- /dev/null +++ b/mmv1/third_party/terraform/functions/project_from_id_test.go @@ -0,0 +1,91 @@ +package functions_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccProviderFunction_project_from_id(t *testing.T) { + t.Parallel() + // Skipping due to requiring TF 1.8.0 in VCR systems : https://github.com/hashicorp/terraform-provider-google/issues/17451 + acctest.SkipIfVcr(t) + + projectId := envvar.GetTestProjectFromEnv() + projectIdRegex := regexp.MustCompile(fmt.Sprintf("^%s$", projectId)) + + context := map[string]interface{}{ + "function_name": "project_from_id", + "output_name": "project_id", + "resource_name": fmt.Sprintf("tf_test_project_id_func_%s", acctest.RandString(t, 10)), + } + + acctest.VcrTest(t, resource.TestCase{ + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Can get the project from a resource's id in one step + // Uses google_pubsub_topic resource's id attribute with format projects/{{project}}/topics/{{name}} + Config: testProviderFunction_get_project_from_resource_id(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), projectIdRegex), + ), + }, + { + // Can get the project from a resource's self_link in one step + // Uses google_bigquery_dataset resource's self_link attribute + Config: testProviderFunction_get_project_from_resource_self_link(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), projectIdRegex), + ), + }, + }, + }) +} + +func testProviderFunction_get_project_from_resource_id(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_pubsub_topic" "default" { + name = "%{resource_name}" +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_pubsub_topic.default.id) +} +`, context) +} + +func testProviderFunction_get_project_from_resource_self_link(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_bigquery_dataset" "default" { + dataset_id = "%{resource_name}" + description = "This dataset is made in an acceptance test" +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_bigquery_dataset.default.self_link) +} +`, context) +} diff --git a/mmv1/third_party/terraform/functions/region_from_id.go b/mmv1/third_party/terraform/functions/region_from_id.go new file mode 100644 index 000000000000..11235c3592ea --- /dev/null +++ b/mmv1/third_party/terraform/functions/region_from_id.go @@ -0,0 +1,62 @@ +package functions + +import ( + "context" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework/function" +) + +var _ function.Function = RegionFromIdFunction{} + +func NewRegionFromIdFunction() function.Function { + return &RegionFromIdFunction{ + name: "region_from_id", + } +} + +type RegionFromIdFunction struct { + name string // Makes function name available in Run logic for logging purposes +} + +func (f RegionFromIdFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { + resp.Name = f.name +} + +func (f RegionFromIdFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { + resp.Definition = function.Definition{ + Summary: "Returns the region name within a provided resource id, self link, or OP style resource name.", + Description: "Takes a single string argument, which should be a resource id, self link, or OP style resource name. This function will either return the region name from the input string or raise an error due to no region being present in the string. The function uses the presence of \"regions/{{region}}/\" in the input string to identify the region name, e.g. when the function is passed the id \"projects/my-project/regions/us-central1/subnetworks/my-subnetwork\" as an argument it will return \"us-central1\".", + Parameters: []function.Parameter{ + function.StringParameter{ + Name: "id", + Description: "A string of a resource's id, a resource's self link, or an OP style resource name. For example, \"projects/my-project/regions/us-central1/subnetworks/my-subnetwork\" and \"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1/subnetworks/my-subnetwork\" are valid values containing regions", + }, + }, + Return: function.StringReturn{}, + } +} + +func (f RegionFromIdFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { + // Load arguments from function call + var arg0 string + resp.Error = function.ConcatFuncErrors(req.Arguments.GetArgument(ctx, 0, &arg0)) + if resp.Error != nil { + return + } + + // Prepare how we'll identify region name from input string + regex := regexp.MustCompile("regions/(?P[^/]+)/") // Should match the pattern below + template := "$RegionName" // Should match the submatch identifier in the regex + pattern := "regions/{region}/" // Human-readable pseudo-regex pattern used in errors and warnings + + // Validate input + resp.Error = function.ConcatFuncErrors(ValidateElementFromIdArguments(ctx, arg0, regex, pattern, f.name)) + if resp.Error != nil { + return + } + + // Get and return element from input string + region := GetElementFromId(arg0, regex, template) + resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, region)) +} diff --git a/mmv1/third_party/terraform/functions/region_from_id_internal_test.go b/mmv1/third_party/terraform/functions/region_from_id_internal_test.go new file mode 100644 index 000000000000..655740b7c05a --- /dev/null +++ b/mmv1/third_party/terraform/functions/region_from_id_internal_test.go @@ -0,0 +1,102 @@ +package functions + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/function" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +func TestFunctionRun_region_from_id(t *testing.T) { + t.Parallel() + + regionName := "us-central1" + + // Happy path inputs + validId := fmt.Sprintf("projects/my-project/regions/%s/subnetworks/my-subnetwork", regionName) + validSelfLink := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/my-project/regions/%s/subnetworks/my-subnetwork", regionName) + validOpStyleResourceName := fmt.Sprintf("//compute.googleapis.com/projects/my-project/regions/%s/addresses/my-address", regionName) + + // Unhappy path inputs + repetitiveInput := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/my-project/regions/%s/regions/not-this-one/subnetworks/my-subnetwork", regionName) + invalidInput := "projects/my-project/zones/us-central1-c/instances/my-instance" + + testCases := map[string]struct { + request function.RunRequest + expected function.RunResponse + }{ + "it returns the expected output value when given a valid resource id input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validId)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(regionName)), + }, + }, + "it returns the expected output value when given a valid resource self_link input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validSelfLink)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(regionName)), + }, + }, + "it returns the expected output value when given a valid OP style resource name input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validOpStyleResourceName)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(regionName)), + }, + }, + "it returns the first submatch (with no error) when given repetitive input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(repetitiveInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(regionName)), + }, + }, + "it returns an error when given input with no submatches": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(invalidInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringNull()), + Error: function.NewArgumentFuncError( + 0, + fmt.Sprintf("The input string \"%s\" doesn't contain the expected pattern \"regions/{region}/\".", invalidInput), + ), + }, + }, + } + + for name, testCase := range testCases { + tn, tc := name, testCase + + t.Run(tn, func(t *testing.T) { + t.Parallel() + + // Arrange + got := function.RunResponse{ + Result: function.NewResultData(basetypes.StringValue{}), + } + + // Act + NewRegionFromIdFunction().Run(context.Background(), tc.request, &got) + + // Assert + if diff := cmp.Diff(got.Result, tc.expected.Result); diff != "" { + t.Errorf("unexpected diff between expected and received result: %s", diff) + } + if diff := cmp.Diff(got.Error, tc.expected.Error); diff != "" { + t.Errorf("unexpected diff between expected and received errors: %s", diff) + } + }) + } +} diff --git a/mmv1/third_party/terraform/functions/region_from_id_test.go b/mmv1/third_party/terraform/functions/region_from_id_test.go new file mode 100644 index 000000000000..79ef135d9a27 --- /dev/null +++ b/mmv1/third_party/terraform/functions/region_from_id_test.go @@ -0,0 +1,92 @@ +package functions_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccProviderFunction_region_from_id(t *testing.T) { + t.Parallel() + // Skipping due to requiring TF 1.8.0 in VCR systems : https://github.com/hashicorp/terraform-provider-google/issues/17451 + acctest.SkipIfVcr(t) + + region := envvar.GetTestRegionFromEnv() + regionRegex := regexp.MustCompile(fmt.Sprintf("^%s$", region)) + + context := map[string]interface{}{ + "function_name": "region_from_id", + "output_name": "region", + "resource_name": fmt.Sprintf("tf-test-region-id-func-%s", acctest.RandString(t, 10)), + } + + acctest.VcrTest(t, resource.TestCase{ + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Can get the region from a resource's id in one step + // Uses google_compute_node_template resource's id attribute with format projects/{{project}}/regions/{{region}}/nodeTemplates/{{name}} + Config: testProviderFunction_get_region_from_resource_id(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), regionRegex), + ), + }, + { + // Can get the region from a resource's self_link in one step + // Uses google_compute_node_template resource's self_link attribute + Config: testProviderFunction_get_region_from_resource_self_link(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), regionRegex), + ), + }, + }, + }) +} + +func testProviderFunction_get_region_from_resource_id(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_node_template" "default" { + name = "%{resource_name}" + node_type = "n1-node-96-624" +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_compute_node_template.default.id) +} +`, context) +} + +func testProviderFunction_get_region_from_resource_self_link(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_node_template" "default" { + name = "%{resource_name}" + node_type = "n1-node-96-624" +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_compute_node_template.default.self_link) +} +`, context) +} diff --git a/mmv1/third_party/terraform/functions/region_from_zone.go b/mmv1/third_party/terraform/functions/region_from_zone.go new file mode 100644 index 000000000000..d1ba3104e88c --- /dev/null +++ b/mmv1/third_party/terraform/functions/region_from_zone.go @@ -0,0 +1,57 @@ +package functions + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/function" +) + +var _ function.Function = RegionFromZoneFunction{} + +func NewRegionFromZoneFunction() function.Function { + return &RegionFromZoneFunction{} +} + +type RegionFromZoneFunction struct{} + +func (f RegionFromZoneFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { + resp.Name = "region_from_zone" +} + +func (f RegionFromZoneFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { + resp.Definition = function.Definition{ + Summary: "Returns the region within a provided resource's zone", + Description: "Takes a single string argument, which should be a resource's zone.", + Parameters: []function.Parameter{ + function.StringParameter{ + Name: "zone", + Description: "A string of a resource's zone.", + }, + }, + Return: function.StringReturn{}, + } +} + +func (f RegionFromZoneFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { + // Load arguments from function call + var arg0 string + resp.Error = function.ConcatFuncErrors(req.Arguments.GetArgument(ctx, 0, &arg0)) + if resp.Error != nil { + return + } + + if arg0 == "" { + err := function.NewArgumentFuncError(0, "The input string cannot be empty.") + resp.Error = function.ConcatFuncErrors(err) + return + } + + if arg0[len(arg0)-2] != '-' { + err := function.NewArgumentFuncError(0, fmt.Sprintf("The input string \"%s\" is not a valid zone name.", arg0)) + resp.Error = function.ConcatFuncErrors(err) + return + } + + resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, arg0[:len(arg0)-2])) +} diff --git a/mmv1/third_party/terraform/functions/region_from_zone_internal_test.go b/mmv1/third_party/terraform/functions/region_from_zone_internal_test.go new file mode 100644 index 000000000000..ceeac2b1c9a3 --- /dev/null +++ b/mmv1/third_party/terraform/functions/region_from_zone_internal_test.go @@ -0,0 +1,74 @@ +package functions + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/function" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +func TestFunctionRun_region_from_zone(t *testing.T) { + t.Parallel() + + region := "us-central1" + + testCases := map[string]struct { + request function.RunRequest + expected function.RunResponse + }{ + "it returns the expected output value when given a valid zone input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue("us-central1-b")}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(region)), + }, + }, + "it returns an error when given input is empty": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue("")}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringNull()), + Error: function.NewArgumentFuncError(0, "The input string cannot be empty."), + }, + }, + "it returns an error when given input is not a zone": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue("foobar")}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringNull()), + Error: function.NewArgumentFuncError(0, "The input string \"foobar\" is not a valid zone name."), + }, + }, + } + + for name, testCase := range testCases { + tn, tc := name, testCase + + t.Run(tn, func(t *testing.T) { + t.Parallel() + + // Arrange + got := function.RunResponse{ + Result: function.NewResultData(basetypes.StringValue{}), + } + + // Act + NewRegionFromZoneFunction().Run(context.Background(), tc.request, &got) + + // Assert + if diff := cmp.Diff(got.Result, tc.expected.Result); diff != "" { + t.Errorf("unexpected diff between expected and received result: %s", diff) + } + if diff := cmp.Diff(got.Error, tc.expected.Error); diff != "" { + t.Errorf("unexpected diff between expected and received errors: %s", diff) + } + }) + } +} diff --git a/mmv1/third_party/terraform/functions/region_from_zone_test.go b/mmv1/third_party/terraform/functions/region_from_zone_test.go new file mode 100644 index 000000000000..68c001ada13a --- /dev/null +++ b/mmv1/third_party/terraform/functions/region_from_zone_test.go @@ -0,0 +1,66 @@ +package functions_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" +) + +func TestAccProviderFunction_region_from_zone(t *testing.T) { + t.Parallel() + // Skipping due to requiring TF 1.8.0 in VCR systems : https://github.com/hashicorp/terraform-provider-google/issues/17451 + acctest.SkipIfVcr(t) + projectZone := "us-central1-a" + projectRegion := "us-central1" + projectRegionRegex := regexp.MustCompile(fmt.Sprintf("^%s$", projectRegion)) + + context := map[string]interface{}{ + "function_name": "region_from_zone", + "output_name": "zone", + "resource_name": fmt.Sprintf("tf-test-region-from-zone-func-%s", acctest.RandString(t, 10)), + "resource_location": projectZone, + } + + acctest.VcrTest(t, resource.TestCase{ + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + Config: testProviderFunction_get_region_from_zone(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), projectRegionRegex), + ), + }, + }, + }) +} + +func testProviderFunction_get_region_from_zone(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_disk" "default" { + name = "%{resource_name}" + type = "pd-ssd" + zone = "%{resource_location}" + image = "debian-11-bullseye-v20220719" + labels = { + environment = "dev" + } + physical_block_size_bytes = 4096 + } + +output "%{output_name}" { + value = provider::google::%{function_name}(google_compute_disk.default.zone) +} +`, context) +} diff --git a/mmv1/third_party/terraform/functions/zone_from_id.go b/mmv1/third_party/terraform/functions/zone_from_id.go new file mode 100644 index 000000000000..c1f3ae95eeaa --- /dev/null +++ b/mmv1/third_party/terraform/functions/zone_from_id.go @@ -0,0 +1,62 @@ +package functions + +import ( + "context" + "regexp" + + "github.com/hashicorp/terraform-plugin-framework/function" +) + +var _ function.Function = ZoneFromIdFunction{} + +func NewZoneFromIdFunction() function.Function { + return &ZoneFromIdFunction{ + name: "zone_from_id", + } +} + +type ZoneFromIdFunction struct { + name string // Makes function name available in Run logic for logging purposes +} + +func (f ZoneFromIdFunction) Metadata(ctx context.Context, req function.MetadataRequest, resp *function.MetadataResponse) { + resp.Name = f.name +} + +func (f ZoneFromIdFunction) Definition(ctx context.Context, req function.DefinitionRequest, resp *function.DefinitionResponse) { + resp.Definition = function.Definition{ + Summary: "Returns the zone name within the resource id or self link provided as an argument.", + Description: "Takes a single string argument, which should be an id or self link of a resource. This function will either return the zone name from the input string or raise an error due to no zone being present in the string. The function uses the presence of \"zones/{{zone}}/\" in the input string to identify the zone name, e.g. when the function is passed the id \"projects/my-project/zones/us-central1-c/instances/my-instance\" as an argument it will return \"us-central1-c\".", + Parameters: []function.Parameter{ + function.StringParameter{ + Name: "id", + Description: "An id of a resouce, or a self link. For example, both \"projects/my-project/zones/us-central1-c/instances/my-instance\" and \"https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-c/instances/my-instance\" are valid inputs", + }, + }, + Return: function.StringReturn{}, + } +} + +func (f ZoneFromIdFunction) Run(ctx context.Context, req function.RunRequest, resp *function.RunResponse) { + // Load arguments from function call + var arg0 string + resp.Error = function.ConcatFuncErrors(req.Arguments.GetArgument(ctx, 0, &arg0)) + if resp.Error != nil { + return + } + + // Prepare how we'll identify zone name from input string + regex := regexp.MustCompile("zones/(?P[^/]+)/") // Should match the pattern below + template := "$ZoneName" // Should match the submatch identifier in the regex + pattern := "zones/{zone}/" // Human-readable pseudo-regex pattern used in errors and warnings + + // Validate input + resp.Error = function.ConcatFuncErrors(ValidateElementFromIdArguments(ctx, arg0, regex, pattern, f.name)) + if resp.Error != nil { + return + } + + // Get and return element from input string + zone := GetElementFromId(arg0, regex, template) + resp.Error = function.ConcatFuncErrors(resp.Result.Set(ctx, zone)) +} diff --git a/mmv1/third_party/terraform/functions/zone_from_id_internal_test.go b/mmv1/third_party/terraform/functions/zone_from_id_internal_test.go new file mode 100644 index 000000000000..f473f43c24f0 --- /dev/null +++ b/mmv1/third_party/terraform/functions/zone_from_id_internal_test.go @@ -0,0 +1,102 @@ +package functions + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/function" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +func TestFunctionRun_zone_from_id(t *testing.T) { + t.Parallel() + + zone := "us-central1-a" + + // Happy path inputs + validId := fmt.Sprintf("projects/my-project/zones/%s/networkEndpointGroups/my-neg", zone) + validSelfLink := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/my-project/zones/%s/networkEndpointGroups/my-neg", zone) + validOpStyleResourceName := fmt.Sprintf("//compute.googleapis.com/projects/my-project/zones/%s/instances/my-instance", zone) + + // Unhappy path inputs + repetitiveInput := fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/my-project/zones/%s/zones/not-this-one/networkEndpointGroups/my-neg", zone) + invalidInput := "projects/my-project/regions/us-central1/subnetworks/my-subnetwork" + + testCases := map[string]struct { + request function.RunRequest + expected function.RunResponse + }{ + "it returns the expected output value when given a valid resource id input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validId)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(zone)), + }, + }, + "it returns the expected output value when given a valid resource self_link input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validSelfLink)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(zone)), + }, + }, + "it returns the expected output value when given a valid OP style resource name input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(validOpStyleResourceName)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(zone)), + }, + }, + "it returns the first submatch (with no error) when given repetitive input": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(repetitiveInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringValue(zone)), + }, + }, + "it returns an error when given input with no submatches": { + request: function.RunRequest{ + Arguments: function.NewArgumentsData([]attr.Value{types.StringValue(invalidInput)}), + }, + expected: function.RunResponse{ + Result: function.NewResultData(types.StringNull()), + Error: function.NewArgumentFuncError( + 0, + fmt.Sprintf("The input string \"%s\" doesn't contain the expected pattern \"zones/{zone}/\".", invalidInput), + ), + }, + }, + } + + for name, testCase := range testCases { + tn, tc := name, testCase + + t.Run(tn, func(t *testing.T) { + t.Parallel() + + // Arrange + got := function.RunResponse{ + Result: function.NewResultData(basetypes.StringValue{}), + } + + // Act + NewZoneFromIdFunction().Run(context.Background(), tc.request, &got) + + // Assert + if diff := cmp.Diff(got.Result, tc.expected.Result); diff != "" { + t.Errorf("unexpected diff between expected and received result: %s", diff) + } + if diff := cmp.Diff(got.Error, tc.expected.Error); diff != "" { + t.Errorf("unexpected diff between expected and received errors: %s", diff) + } + }) + } +} diff --git a/mmv1/third_party/terraform/functions/zone_from_id_test.go b/mmv1/third_party/terraform/functions/zone_from_id_test.go new file mode 100644 index 000000000000..7058825a05bc --- /dev/null +++ b/mmv1/third_party/terraform/functions/zone_from_id_test.go @@ -0,0 +1,90 @@ +package functions_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-google/google/acctest" + "github.com/hashicorp/terraform-provider-google/google/envvar" +) + +func TestAccProviderFunction_zone_from_id(t *testing.T) { + t.Parallel() + // Skipping due to requiring TF 1.8.0 in VCR systems : https://github.com/hashicorp/terraform-provider-google/issues/17451 + acctest.SkipIfVcr(t) + + zone := envvar.GetTestZoneFromEnv() + zoneRegex := regexp.MustCompile(fmt.Sprintf("^%s$", zone)) + + context := map[string]interface{}{ + "function_name": "zone_from_id", + "output_name": "zone", + "resource_name": fmt.Sprintf("tf-test-zone-id-func-%s", acctest.RandString(t, 10)), + } + + acctest.VcrTest(t, resource.TestCase{ + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t), + Steps: []resource.TestStep{ + { + // Can get the zone from a resource's id in one step + // Uses google_compute_disk resource's id attribute with format projects/{{project}}/zones/{{zone}}/disks/{{name}} + Config: testProviderFunction_get_zone_from_resource_id(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), zoneRegex), + ), + }, + { + // Can get the zone from a resource's self_link in one step + // Uses google_compute_disk resource's self_link attribute + Config: testProviderFunction_get_zone_from_resource_self_link(context), + Check: resource.ComposeTestCheckFunc( + resource.TestMatchOutput(context["output_name"].(string), zoneRegex), + ), + }, + }, + }) +} + +func testProviderFunction_get_zone_from_resource_id(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_disk" "default" { + name = "%{resource_name}" +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_compute_disk.default.id) +} +`, context) +} + +func testProviderFunction_get_zone_from_resource_self_link(context map[string]interface{}) string { + return acctest.Nprintf(` +# terraform block required for provider function to be found +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_disk" "default" { + name = "%{resource_name}" +} + +output "%{output_name}" { + value = provider::google::%{function_name}(google_compute_disk.default.self_link) +} +`, context) +} diff --git a/mmv1/third_party/terraform/fwprovider/framework_provider.go.erb b/mmv1/third_party/terraform/fwprovider/framework_provider.go.erb index 0d1aa04c979e..0aec3eaaf331 100644 --- a/mmv1/third_party/terraform/fwprovider/framework_provider.go.erb +++ b/mmv1/third_party/terraform/fwprovider/framework_provider.go.erb @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/provider" "github.com/hashicorp/terraform-plugin-framework/provider/metaschema" @@ -14,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-provider-google/google/functions" "github.com/hashicorp/terraform-provider-google/google/fwmodels" "github.com/hashicorp/terraform-provider-google/google/fwtransport" "github.com/hashicorp/terraform-provider-google/google/services/resourcemanager" @@ -28,6 +30,7 @@ import ( // Ensure the implementation satisfies the expected interfaces var ( _ provider.ProviderWithMetaSchema = &FrameworkProvider{} + _ provider.ProviderWithFunctions = &FrameworkProvider{} ) // New is a helper function to simplify provider server and testing implementation. @@ -294,5 +297,17 @@ func (p *FrameworkProvider) DataSources(_ context.Context) []func() datasource.D // Resources defines the resources implemented in the provider. func (p *FrameworkProvider) Resources(_ context.Context) []func() resource.Resource { - return nil + return nil } + +// Functions defines the provider functions implemented in the provider. +func (p *FrameworkProvider) Functions(_ context.Context) []func() function.Function { + return []func() function.Function{ + functions.NewLocationFromIdFunction, + functions.NewNameFromIdFunction, + functions.NewProjectFromIdFunction, + functions.NewRegionFromIdFunction, + functions.NewRegionFromZoneFunction, + functions.NewZoneFromIdFunction, + } +} \ No newline at end of file diff --git a/mmv1/third_party/terraform/go.mod.erb b/mmv1/third_party/terraform/go.mod.erb index f6c84e1c5648..3f898b29e2ad 100644 --- a/mmv1/third_party/terraform/go.mod.erb +++ b/mmv1/third_party/terraform/go.mod.erb @@ -10,18 +10,19 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/dnaeon/go-vcr v1.0.1 github.com/gammazero/workerpool v0.0.0-20181230203049-86a96b5d5d92 + github.com/google/go-cmp v0.6.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 github.com/hashicorp/errwrap v1.0.0 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.6.0 - github.com/hashicorp/terraform-plugin-framework v1.5.0 + github.com/hashicorp/terraform-plugin-framework v1.7.0 github.com/hashicorp/terraform-plugin-framework-validators v0.9.0 - github.com/hashicorp/terraform-plugin-go v0.20.0 + github.com/hashicorp/terraform-plugin-go v0.22.1 github.com/hashicorp/terraform-plugin-log v0.9.0 - github.com/hashicorp/terraform-plugin-mux v0.13.0 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 + github.com/hashicorp/terraform-plugin-mux v0.15.0 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/hashstructure v1.1.0 github.com/sirupsen/logrus v1.8.1 @@ -29,8 +30,8 @@ require ( golang.org/x/oauth2 v0.17.0 google.golang.org/api v0.167.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 - google.golang.org/grpc v1.61.1 - google.golang.org/protobuf v1.32.0 + google.golang.org/grpc v1.62.1 + google.golang.org/protobuf v1.33.0 ) require ( @@ -40,26 +41,25 @@ require ( cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/longrunning v0.5.5 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect + github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe // indirect - github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 // indirect - github.com/envoyproxy/go-control-plane v0.11.1 // indirect - github.com/envoyproxy/protoc-gen-validate v1.0.2 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa // indirect + github.com/envoyproxy/go-control-plane v0.12.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect + github.com/fatih/color v1.16.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gammazero/deque v0.0.0-20180920172122-f6adf94963e4 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang/glog v1.1.2 // indirect + github.com/golang/glog v1.2.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cpy v0.0.0-20211218193943-a9c933c06932 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/uuid v1.6.0 // indirect @@ -69,27 +69,28 @@ require ( github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/hc-install v0.6.2 // indirect + github.com/hashicorp/hc-install v0.6.3 // indirect github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.19.0 // indirect - github.com/hashicorp/terraform-json v0.18.0 // indirect + github.com/hashicorp/terraform-exec v0.20.0 // indirect + github.com/hashicorp/terraform-json v0.21.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/oklog/run v1.0.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/zclconf/go-cty v1.14.1 // indirect + github.com/zclconf/go-cty v1.14.2 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect @@ -97,7 +98,7 @@ require ( go.opentelemetry.io/otel/metric v1.23.0 // indirect go.opentelemetry.io/otel/trace v1.23.0 // indirect golang.org/x/crypto v0.19.0 // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/mod v0.15.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/mmv1/third_party/terraform/go.sum b/mmv1/third_party/terraform/go.sum index a34b9931b5c6..1672ae714e55 100644 --- a/mmv1/third_party/terraform/go.sum +++ b/mmv1/third_party/terraform/go.sum @@ -20,8 +20,8 @@ github.com/GoogleCloudPlatform/declarative-resource-client-library v1.64.0 h1:QA github.com/GoogleCloudPlatform/declarative-resource-client-library v1.64.0/go.mod h1:pL2Qt5HT+x6xrTd806oMiM3awW6kNIXB/iiuClz6m6k= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= -github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0= +github.com/ProtonMail/go-crypto v1.1.0-alpha.0/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= @@ -31,7 +31,6 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -40,14 +39,14 @@ github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= -github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/creachadair/staticfile v0.1.2/go.mod h1:a3qySzCIXEprDGxk6tSxSI+dBBdLzqeBOMhZ+o2d3pM= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= @@ -61,13 +60,14 @@ github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FM github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= -github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/go-control-plane v0.12.0 h1:4X+VP1GHd1Mhj6IB5mMeGbLCleqxjletLK6K0rbxyZI= +github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gammazero/deque v0.0.0-20180920172122-f6adf94963e4 h1:R+19WKQClnfMXS60cP5BmMe1wjZ4u0evY2p2Ar0ZTXo= @@ -78,8 +78,8 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66D github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= -github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk= -github.com/go-git/go-git/v5 v5.10.1/go.mod h1:uEuHjxkHap8kAl//V5F/nNWwqIYtP/402ddd05mp0wg= +github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4= +github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -93,8 +93,8 @@ github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= +github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -158,28 +158,28 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.6.2 h1:V1k+Vraqz4olgZ9UzKiAcbman9i9scg9GgSt/U3mw/M= -github.com/hashicorp/hc-install v0.6.2/go.mod h1:2JBpd+NCFKiHiu/yYCGaPyPHhZLxXTpz8oreHa/a3Ps= +github.com/hashicorp/hc-install v0.6.3 h1:yE/r1yJvWbtrJ0STwScgEnCanb0U9v7zp0Gbkmcoxqs= +github.com/hashicorp/hc-install v0.6.3/go.mod h1:KamGdbodYzlufbWh4r9NRo8y6GLHWZP2GBtdnms1Ln0= github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.19.0 h1:FpqZ6n50Tk95mItTSS9BjeOVUb4eg81SpgVtZNNtFSM= -github.com/hashicorp/terraform-exec v0.19.0/go.mod h1:tbxUpe3JKruE9Cuf65mycSIT8KiNPZ0FkuTE3H4urQg= -github.com/hashicorp/terraform-json v0.18.0 h1:pCjgJEqqDESv4y0Tzdqfxr/edOIGkjs8keY42xfNBwU= -github.com/hashicorp/terraform-json v0.18.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= -github.com/hashicorp/terraform-plugin-framework v1.5.0 h1:8kcvqJs/x6QyOFSdeAyEgsenVOUeC/IyKpi2ul4fjTg= -github.com/hashicorp/terraform-plugin-framework v1.5.0/go.mod h1:6waavirukIlFpVpthbGd2PUNYaFedB0RwW3MDzJ/rtc= +github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= +github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= +github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= +github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= +github.com/hashicorp/terraform-plugin-framework v1.7.0 h1:wOULbVmfONnJo9iq7/q+iBOBJul5vRovaYJIu2cY/Pw= +github.com/hashicorp/terraform-plugin-framework v1.7.0/go.mod h1:jY9Id+3KbZ17OMpulgnWLSfwxNVYSoYBQFTgsx044CI= github.com/hashicorp/terraform-plugin-framework-validators v0.9.0 h1:LYz4bXh3t7bTEydXOmPDPupRRnA480B/9+jV8yZvxBA= github.com/hashicorp/terraform-plugin-framework-validators v0.9.0/go.mod h1:+BVERsnfdlhYR2YkXMBtPnmn9UsL19U3qUtSZ+Y/5MY= -github.com/hashicorp/terraform-plugin-go v0.20.0 h1:oqvoUlL+2EUbKNsJbIt3zqqZ7wi6lzn4ufkn/UA51xQ= -github.com/hashicorp/terraform-plugin-go v0.20.0/go.mod h1:Rr8LBdMlY53a3Z/HpP+ZU3/xCDqtKNCkeI9qOyT10QE= +github.com/hashicorp/terraform-plugin-go v0.22.1 h1:iTS7WHNVrn7uhe3cojtvWWn83cm2Z6ryIUDTRO0EV7w= +github.com/hashicorp/terraform-plugin-go v0.22.1/go.mod h1:qrjnqRghvQ6KnDbB12XeZ4FluclYwptntoWCr9QaXTI= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-mux v0.13.0 h1:79U401/3nd8CWwDGtTHc8F3miSCAS9XGtVarxSTDgwA= -github.com/hashicorp/terraform-plugin-mux v0.13.0/go.mod h1:Ndv0FtwDG2ogzH59y64f2NYimFJ6I0smRgFUKfm6dyQ= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0 h1:Bl3e2ei2j/Z3Hc2HIS15Gal2KMKyLAZ2om1HCEvK6es= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.31.0/go.mod h1:i2C41tszDjiWfziPQDL5R/f3Zp0gahXe5No/MIO9rCE= +github.com/hashicorp/terraform-plugin-mux v0.15.0 h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME= +github.com/hashicorp/terraform-plugin-mux v0.15.0/go.mod h1:9ezplb1Dyq394zQ+ldB0nvy/qbNAz3mMoHHseMTMaKo= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 h1:qHprzXy/As0rxedphECBEQAh3R4yp6pKksKHcqZx5G8= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0/go.mod h1:H+8tjs9TjV2w57QFVSMBQacf8k/E1XwLXGCARgViC6A= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -196,19 +196,23 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -232,6 +236,8 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -263,8 +269,8 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zclconf/go-cty v1.14.1 h1:t9fyA35fwjjUMcmL5hLER+e/rEPqrbCK1/OSE4SI9KA= -github.com/zclconf/go-cty v1.14.1/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.14.2 h1:kTG7lqmBou0Zkx35r6HJHUQTvaRPr5bIAf3AoHS0izI= +github.com/zclconf/go-cty v1.14.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.48.0 h1:P+/g8GpuJGYbOp2tAdKrIPUX9JO02q8Q0YNlHolpibA= @@ -286,8 +292,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -297,9 +301,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -311,9 +314,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -325,7 +325,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -343,17 +342,12 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -361,9 +355,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -377,7 +368,6 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -406,8 +396,8 @@ google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= -google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -419,11 +409,12 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/mmv1/third_party/terraform/website/docs/functions/location_from_id.html.markdown b/mmv1/third_party/terraform/website/docs/functions/location_from_id.html.markdown new file mode 100644 index 000000000000..40eeffa32df4 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/functions/location_from_id.html.markdown @@ -0,0 +1,99 @@ +--- +page_title: location_from_id Function - terraform-provider-google +description: |- + Returns the location within a provided resource id, self link, or OP style resource name. +--- + +# Function: location_from_id + +Returns the location within a provided resource's id, resource URI, self link, or full resource name. + +For more information about using provider-defined functions with Terraform [see the official documentation](https://developer.hashicorp.com/terraform/plugin/framework/functions/concepts). + +## Example Usage + +### Use with the `google` provider + +```terraform +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_cloud_run_service" "default" { + name = "my-service" + location = "us-central1" + + template { + spec { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + } + } + } + + traffic { + percent = 100 + latest_revision = true + } +} + +# Value is "us-central1" +output "location_from_id" { + value = provider::google::location_from_id(google_cloud_run_service.default.id) +} +``` + +### Use with the `google-beta` provider + +```terraform +terraform { + required_providers { + google-beta = { + source = "hashicorp/google-beta" + } + } +} + +resource "google_cloud_run_service" "default" { + # provider argument omitted - provisioning by google or google-beta doesn't impact this example + name = "my-service" + location = "us-central1" + + template { + spec { + containers { + image = "us-docker.pkg.dev/cloudrun/container/hello" + } + } + } + + traffic { + percent = 100 + latest_revision = true + } +} + + +# Value is "us-central1" +output "location_from_id" { + value = provider::google-beta::location_from_id(google_cloud_run_service.default.id) +} +``` + +## Signature + +```text +location_from_id(id string) string +``` + +## Arguments + +1. `id` (String) A string of a resource's id, resource URI, self link, or full resource name. For example, these are all valid values: + +* `"projects/my-project/locations/us-central1/services/my-service"` +* `"https://run.googleapis.com/v2/projects/my-project/locations/us-central1/services/my-service"` +* `"//run.googleapis.com/v2/projects/my-project/locations/us-central1/services/my-service"` diff --git a/mmv1/third_party/terraform/website/docs/functions/name_from_id.html.markdown b/mmv1/third_party/terraform/website/docs/functions/name_from_id.html.markdown new file mode 100644 index 000000000000..d45b589ed8fa --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/functions/name_from_id.html.markdown @@ -0,0 +1,70 @@ +--- +page_title: name_from_id Function - terraform-provider-google +description: |- + Returns the project within a provided resource id, self link, or OP style resource name. +--- + +# Function: name_from_id + +Returns the short-form name within a provided resource's id, resource URI, self link, or full resource name. + +For more information about using provider-defined functions with Terraform [see the official documentation](https://developer.hashicorp.com/terraform/plugin/framework/functions/concepts). + +## Example Usage + +### Use with the `google` provider + +```terraform +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_pubsub_topic" "default" { + name = "my-topic" +} + +# Value is "my-topic" +output "function_output" { + value = provider::google::name_from_id(google_pubsub_topic.default.id) +} +``` + +### Use with the `google-beta` provider + +```terraform +terraform { + required_providers { + google-beta = { + source = "hashicorp/google-beta" + } + } +} + +resource "google_pubsub_topic" "default" { + # provider argument omitted - provisioning by google or google-beta doesn't impact this example + name = "my-topic" +} + +# Value is "my-topic" +output "function_output" { + value = provider::google-beta::name_from_id(google_pubsub_topic.default.id) +} +``` + +## Signature + +```text +name_from_id(id string) string +``` + +## Arguments + +1. `id` (String) A string of a resource's id, resource URI, self link, or full resource name. For example, these are all valid values: + +* `"projects/my-project/zones/us-central1-c/instances/my-instance"` +* `"https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-c/instances/my-instance"` +* `"//gkehub.googleapis.com/projects/my-project/locations/us-central1/memberships/my-membership"` diff --git a/mmv1/third_party/terraform/website/docs/functions/project_from_id.html.markdown b/mmv1/third_party/terraform/website/docs/functions/project_from_id.html.markdown new file mode 100644 index 000000000000..33a8b05faacd --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/functions/project_from_id.html.markdown @@ -0,0 +1,72 @@ +--- +page_title: project_from_id Function - terraform-provider-google +description: |- + Returns the project within a provided resource id, self link, or OP style resource name. +--- + +# Function: project_from_id + +Returns the project within a provided resource's id, resource URI, self link, or full resource name. + +For more information about using provider-defined functions with Terraform [see the official documentation](https://developer.hashicorp.com/terraform/plugin/framework/functions/concepts). + +## Example Usage + +### Use with the `google` provider + +```terraform +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_pubsub_topic" "default" { + project = "my-project" + name = "my-topic" +} + +# Value is "my-project" +output "project_from_id" { + value = provider::google::project_from_id(google_pubsub_topic.default.id) +} +``` + +### Use with the `google-beta` provider + +```terraform +terraform { + required_providers { + google-beta = { + source = "hashicorp/google-beta" + } + } +} + +resource "google_pubsub_topic" "default" { + # provider argument omitted - provisioning by google or google-beta doesn't impact this example + project = "my-project" + name = "my-topic" +} + +# Value is "my-project" +output "project_from_id" { + value = provider::google-beta::project_from_id(google_pubsub_topic.default.id) +} +``` + +## Signature + +```text +project_from_id(id string) string +``` + +## Arguments + +1. `id` (String) A string of a resource's id, resource URI, self link, or full resource name. For example, these are all valid values: + +* `"projects/my-project/zones/us-central1-c/instances/my-instance"` +* `"https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-c/instances/my-instance"` +* `"//gkehub.googleapis.com/projects/my-project/locations/us-central1/memberships/my-membership"` diff --git a/mmv1/third_party/terraform/website/docs/functions/region_from_id.html.markdown b/mmv1/third_party/terraform/website/docs/functions/region_from_id.html.markdown new file mode 100644 index 000000000000..18e31ff60fc7 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/functions/region_from_id.html.markdown @@ -0,0 +1,82 @@ +--- +page_title: region_from_id Function - terraform-provider-google +description: |- + Returns the region within a provided resource id, self link, or OP style resource name. +--- + +# Function: region_from_id + +Returns the region within a provided resource's id, resource URI, self link, or full resource name. + +For more information about using provider-defined functions with Terraform [see the official documentation](https://developer.hashicorp.com/terraform/plugin/framework/functions/concepts). + +## Example Usage + +### Use with the `google` provider + +```terraform +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_node_template" "default" { + name = "my-node-template" + region = "us-central1" +} + +# Value is "us-central1" +output "region_from_id" { + value = provider::google::region_from_id(google_compute_node_template.default.id) +} + +# Value is "us-central1" +output "region_from_self_link" { + value = provider::google::region_from_id(google_compute_node_template.default.self_link) +} +``` + +### Use with the `google-beta` provider + +```terraform +terraform { + required_providers { + google-beta = { + source = "hashicorp/google-beta" + } + } +} + +resource "google_compute_node_template" "default" { + # provider argument omitted - provisioning by google or google-beta doesn't impact this example + name = "my-node-template" + region = "us-central1" +} + +# Value is "us-central1" +output "region_from_id" { + value = provider::google-beta::region_from_id(google_compute_node_template.default.id) +} + +# Value is "us-central1" +output "region_from_self_link" { + value = provider::google-beta::region_from_id(google_compute_node_template.default.self_link) +} +``` + +## Signature + +```text +region_from_id(id string) string +``` + +## Arguments + +1. `id` (String) A string of a resource's id, resource URI, self link, or full resource name. For example, these are all valid values: + +* `"projects/my-project/regions/us-central1/subnetworks/my-subnetwork"` +* `"https://www.googleapis.com/compute/v1/projects/my-project/regions/us-central1/subnetworks/my-subnetwork"` +* `"//compute.googleapis.com/projects/my-project/regions/us-central1/subnetworks/my-subnetwork"` \ No newline at end of file diff --git a/mmv1/third_party/terraform/website/docs/functions/region_from_zone.html.markdown b/mmv1/third_party/terraform/website/docs/functions/region_from_zone.html.markdown new file mode 100644 index 000000000000..9fd4ab6fa422 --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/functions/region_from_zone.html.markdown @@ -0,0 +1,138 @@ +--- +page_title: region_from_zone Function - terraform-provider-google +description: |- + Returns the region within a provided zone. +--- + +# Function: region_from_zone + +Returns a region name derived from a provided zone. + +For more information about using provider-defined functions with Terraform [see the official documentation](https://developer.hashicorp.com/terraform/plugin/framework/functions/concepts). + +## Example Usage + +### Use with the `google` provider + +```terraform +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_instance" "default" { + name = "my-instance" + machine_type = "n2-standard-2" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "debian-cloud/debian-11" + labels = { + my_label = "value" + } + } + } + + network_interface { + network = "default" + subnetwork = google_compute_subnetwork.default.id + access_config { + // Ephemeral public IP + } + } + + metadata_startup_script = "echo hi > /test.txt" +} + +data "google_compute_network" "default" { + name = "default" +} + +resource "google_compute_subnetwork" "default" { + name = "my-subnet" + region = "us-central1" + network = data.google_compute_network.default.id + ip_cidr_range = "192.168.10.0/24" +} + +// The region_from_zone function is used to assert that the VM and subnet are in the same region +check "vm_subnet_compatibility_check" { + assert { + condition = google_compute_subnetwork.default.region == provider::google::region_from_zone(google_compute_instance.default.zone) + error_message = "Subnet ${google_compute_subnetwork.default.id} and VM ${google_compute_instance.default.id} are not in the same region" + } +} +``` + +### Use with the `google-beta` provider + +```terraform +terraform { + required_providers { + google-beta = { + source = "hashicorp/google-beta" + } + } +} + +resource "google_compute_instance" "default" { + provider = google-beta + name = "my-instance" + machine_type = "n2-standard-2" + zone = "us-central1-a" + + boot_disk { + initialize_params { + image = "debian-cloud/debian-11" + labels = { + my_label = "value" + } + } + } + + network_interface { + network = "default" + subnetwork = google_compute_subnetwork.default.id + access_config { + // Ephemeral public IP + } + } + + metadata_startup_script = "echo hi > /test.txt" +} + +data "google_compute_network" "default" { + provider = google-beta + name = "default" +} + +resource "google_compute_subnetwork" "default" { + provider = google-beta + name = "my-subnet" + region = "us-central1" + network = data.google_compute_network.default.id + ip_cidr_range = "192.168.10.0/24" +} + +// The region_from_zone function is used to assert that the VM and subnet are in the same region +check "vm_subnet_compatibility_check" { + assert { + condition = google_compute_subnetwork.default.region == provider::google-beta::region_from_zone(google_compute_instance.default.zone) + error_message = "Subnet ${google_compute_subnetwork.default.id} and VM ${google_compute_instance.default.id} are not in the same region" + } +} +``` + +## Signature + +```text +region_from_zone(zone string) string +``` + +## Arguments + +1. `zone` (String) A string of a resource's zone diff --git a/mmv1/third_party/terraform/website/docs/functions/zone_from_id.html.markdown b/mmv1/third_party/terraform/website/docs/functions/zone_from_id.html.markdown new file mode 100644 index 000000000000..6d2d6bfb273f --- /dev/null +++ b/mmv1/third_party/terraform/website/docs/functions/zone_from_id.html.markdown @@ -0,0 +1,82 @@ +--- +page_title: zone_from_id Function - terraform-provider-google +description: |- + Returns the project within a provided resource id, self link, or OP style resource name. +--- + +# Function: zone_from_id + +Returns the zone within a provided resource's id, resource URI, self link, or full resource name. + +For more information about using provider-defined functions with Terraform [see the official documentation](https://developer.hashicorp.com/terraform/plugin/framework/functions/concepts). + +## Example Usage + +### Use with the `google` provider + +```terraform +terraform { + required_providers { + google = { + source = "hashicorp/google" + } + } +} + +resource "google_compute_disk" "default" { + name = "my-disk" + zone = "us-central1-c" +} + +# Value is "us-central1-c" +output "zone_from_id" { + value = provider::google::zone_from_id(google_compute_disk.default.id) +} + +# Value is "us-central1-c" +output "zone_from_self_link" { + value = provider::google::zone_from_id(google_compute_disk.default.self_link) +} +``` + +### Use with the `google-beta` provider + +```terraform +terraform { + required_providers { + google-beta = { + source = "hashicorp/google-beta" + } + } +} + +resource "google_compute_disk" "default" { + # provider argument omitted - provisioning by google or google-beta doesn't impact this example + name = "my-disk" + zone = "us-central1-c" +} + +# Value is "us-central1-c" +output "zone_from_id" { + value = provider::google-beta::zone_from_id(google_compute_disk.default.id) +} + +# Value is "us-central1-c" +output "zone_from_self_link" { + value = provider::google-beta::zone_from_id(google_compute_disk.default.self_link) +} +``` + +## Signature + +```text +zone_from_id(id string) string +``` + +## Arguments + +1. `id` (String) A string of a resource's id, resource URI, self link, or full resource name. For example, these are all valid values: + +* `"projects/my-project/zones/us-central1-c/instances/my-instance"` +* `"https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-c/instances/my-instance"` +* `"//gkehub.googleapis.com/projects/my-project/locations/us-central1/memberships/my-membership"` \ No newline at end of file