Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add region_from_zone provider function #10073

Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions mmv1/third_party/terraform/functions/region_from_zone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
SarahFrench marked this conversation as resolved.
Show resolved Hide resolved
package functions

import (
"context"

"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.Diagnostics.Append(req.Arguments.GetArgument(ctx, 0, &arg0)...)

if resp.Diagnostics.HasError() {
return
}

if arg0 == "" {
resp.Diagnostics.AddArgumentError(
0,
noMatchesErrorSummary,
"The input string is empty.",
BBBmau marked this conversation as resolved.
Show resolved Hide resolved
)
return
}

if arg0[len(arg0)-2] != '-' {
resp.Diagnostics.AddArgumentError(
0,
noMatchesErrorSummary,
"The input string is invalid.",
BBBmau marked this conversation as resolved.
Show resolved Hide resolved
)
return
}

resp.Diagnostics.Append(resp.Result.Set(ctx, arg0[:len(arg0)-2])...)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package functions

import (
"context"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"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()),
Diagnostics: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(
0,
noMatchesErrorSummary,
"The input string is 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()),
Diagnostics: diag.Diagnostics{
diag.NewArgumentErrorDiagnostic(
0,
noMatchesErrorSummary,
"The input string is invalid.",
),
},
},
},
}

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.Diagnostics, tc.expected.Diagnostics); diff != "" {
t.Errorf("unexpected diff between expected and received diagnostics: %s", diff)
}
})
}
}
74 changes: 74 additions & 0 deletions mmv1/third_party/terraform/functions/region_from_zone_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
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_zone(t *testing.T) {
t.Parallel()

SarahFrench marked this conversation as resolved.
Show resolved Hide resolved
projectZone := envvar.GetTestZoneFromEnv()
projectZoneRegex := regexp.MustCompile(fmt.Sprintf("^%s$", projectZone[:len(projectZone)-2]))

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), projectZoneRegex),
),
},
},
})
}

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_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.location)
}
`, context)
}
Original file line number Diff line number Diff line change
Expand Up @@ -304,5 +304,6 @@ func (p *FrameworkProvider) Resources(_ context.Context) []func() resource.Resou
func (p *FrameworkProvider) Functions(_ context.Context) []func() function.Function {
return []func() function.Function{
functions.NewProjectFromIdFunction,
functions.NewRegionFromZoneFunction,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
page_title: region_from_zone Function - terraform-provider-google
description: |-
Returns the region within a provided zone.
---

# Function: region_from_zone

Returns the region within a provided resource's zone.
BBBmau marked this conversation as resolved.
Show resolved Hide resolved

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"
}
}
}

# Value is "us-central1"
output "function_output" {
value = provider::google::region_from_zone("us-central1-b")
}
```

### Use with the `google-beta` provider

```terraform
terraform {
required_providers {
google-beta = {
source = "hashicorp/google-beta"
}
}
}

# Value is "us-central1"
output "function_output" {
value = provider::google-beta::region_from_zone("us-central1-b")
}
```

## Signature

```text
region_from_zone(zone string) string
```

## Arguments

1. `zone` (String) A string of a resource's zone
Loading