Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main' into feature/convert-res…
Browse files Browse the repository at this point in the history
…ource-object
  • Loading branch information
vmanilo committed Oct 29, 2023
2 parents 7db6408 + 6001536 commit 6de2221
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 29 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:
branches:
- main
- feature/convert-resource-object
paths-ignore:
- 'README.md'

Expand All @@ -15,6 +16,7 @@ on:
- 'README.md'
branches:
- main
- feature/convert-resource-object

# Ensures only 1 action runs per PR and previous is canceled on new trigger
concurrency:
Expand Down Expand Up @@ -118,7 +120,7 @@ jobs:
name: Matrix Acceptance Tests
needs: build
runs-on: ubuntu-latest
if: "!github.event.pull_request.head.repo.fork"
# if: "!github.event.pull_request.head.repo.fork"
timeout-minutes: 15
strategy:
fail-fast: false
Expand Down Expand Up @@ -169,7 +171,7 @@ jobs:

cleanup:
name: Cleanup
if: "!github.event.pull_request.head.repo.fork"
# if: "!github.event.pull_request.head.repo.fork"
needs: tests-acceptance
runs-on: ubuntu-latest
timeout-minutes: 15
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.4
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/terraform-plugin-docs v0.16.0
github.com/hashicorp/terraform-plugin-framework v1.4.1
github.com/hashicorp/terraform-plugin-framework v1.4.2
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
github.com/hashicorp/terraform-plugin-go v0.19.0
github.com/hashicorp/terraform-plugin-testing v1.5.1
Expand Down Expand Up @@ -92,7 +92,7 @@ require (
golang.org/x/tools v0.14.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
google.golang.org/grpc v1.57.0 // indirect
google.golang.org/grpc v1.57.1 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ github.com/hashicorp/terraform-json v0.17.1 h1:eMfvh/uWggKmY7Pmb3T85u86E2EQg6EQH
github.com/hashicorp/terraform-json v0.17.1/go.mod h1:Huy6zt6euxaY9knPAFKjUITn8QxUFIe9VuSzb4zn/0o=
github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFccGyBZn52KtMNsS12dI=
github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA=
github.com/hashicorp/terraform-plugin-framework v1.4.1 h1:ZC29MoB3Nbov6axHdgPbMz7799pT5H8kIrM8YAsaVrs=
github.com/hashicorp/terraform-plugin-framework v1.4.1/go.mod h1:XC0hPcQbBvlbxwmjxuV/8sn8SbZRg4XwGMs22f+kqV0=
github.com/hashicorp/terraform-plugin-framework v1.4.2 h1:P7a7VP1GZbjc4rv921Xy5OckzhoiO3ig6SGxwelD2sI=
github.com/hashicorp/terraform-plugin-framework v1.4.2/go.mod h1:GWl3InPFZi2wVQmdVnINPKys09s9mLmTZr95/ngLnbY=
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc=
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg=
github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU=
Expand Down Expand Up @@ -399,8 +399,8 @@ google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg=
google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
Expand Down
34 changes: 34 additions & 0 deletions twingate/internal/provider/resource/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,37 @@ func stringPtr(s string) *string {
func boolPtr(b bool) *bool {
return &b
}

func TestIsWildcardAddress(t *testing.T) {
cases := []struct {
address string
expected bool
}{
{
address: "hello.com",
expected: false,
},
{
address: "*.hello.com",
expected: true,
},
{
address: "redis-?-blah.internal",
expected: true,
},
{
address: "redis-*-blah.internal",
expected: true,
},
{
address: "10.0.0.0/16",
expected: true,
},
}

for n, c := range cases {
t.Run(fmt.Sprintf("case_%d", n), func(t *testing.T) {
assert.Equal(t, c.expected, isWildcardAddress(c.address))
})
}
}
57 changes: 36 additions & 21 deletions twingate/internal/provider/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"reflect"
"regexp"
"strings"

"github.com/Twingate/terraform-provider-twingate/twingate/internal/attr"
Expand Down Expand Up @@ -33,10 +34,11 @@ import (
)

var (
ErrPortsWithPolicyAllowAll = errors.New(model.PolicyAllowAll + " policy does not allow specifying ports.")
ErrPortsWithPolicyDenyAll = errors.New(model.PolicyDenyAll + " policy does not allow specifying ports.")
ErrPolicyRestrictedWithoutPorts = errors.New(model.PolicyRestricted + " policy requires specifying ports.")
ErrInvalidAttributeCombination = errors.New("invalid attribute combination")
ErrPortsWithPolicyAllowAll = errors.New(model.PolicyAllowAll + " policy does not allow specifying ports.")
ErrPortsWithPolicyDenyAll = errors.New(model.PolicyDenyAll + " policy does not allow specifying ports.")
ErrPolicyRestrictedWithoutPorts = errors.New(model.PolicyRestricted + " policy requires specifying ports.")
ErrInvalidAttributeCombination = errors.New("invalid attribute combination")
ErrWildcardAddressWithEnabledShortcut = errors.New("Resources with a CIDR range or wildcard can't have the browser shortcut enabled.")
)

// Ensure the implementation satisfies the desired interfaces.
Expand Down Expand Up @@ -150,10 +152,9 @@ func (r *twingateResource) Schema(_ context.Context, _ resource.SchemaRequest, r
PlanModifiers: []planmodifier.Bool{boolplanmodifier.UseStateForUnknown()},
},
attr.IsBrowserShortcutEnabled: schema.BoolAttribute{
Optional: true,
Computed: true,
Description: `Controls whether an "Open in Browser" shortcut will be shown for this Resource in the Twingate Client.`,
PlanModifiers: []planmodifier.Bool{boolplanmodifier.UseStateForUnknown()},
Optional: true,
Computed: true,
Description: `Controls whether an "Open in Browser" shortcut will be shown for this Resource in the Twingate Client.`,
},
attr.ID: schema.StringAttribute{
Computed: true,
Expand Down Expand Up @@ -395,18 +396,10 @@ func convertResource(plan *resourceModel) (*model.Resource, error) {
return nil, ErrInvalidAttributeCombination
}

var isVisible, isBrowserShortcutEnabled *bool
if !plan.IsVisible.IsUnknown() {
isVisible = plan.IsVisible.ValueBoolPointer()
}
isBrowserShortcutEnabled := getOptionalBool(plan.IsBrowserShortcutEnabled)

if !plan.IsBrowserShortcutEnabled.IsUnknown() {
isBrowserShortcutEnabled = plan.IsBrowserShortcutEnabled.ValueBoolPointer()
}

var alias *string
if !plan.Alias.IsUnknown() && !plan.Alias.IsNull() {
alias = plan.Alias.ValueStringPointer()
if isBrowserShortcutEnabled != nil && *isBrowserShortcutEnabled && isWildcardAddress(plan.Address.ValueString()) {
return nil, ErrWildcardAddressWithEnabledShortcut
}

return &model.Resource{
Expand All @@ -417,12 +410,28 @@ func convertResource(plan *resourceModel) (*model.Resource, error) {
Groups: groupIDs,
ServiceAccounts: serviceAccountIDs,
IsAuthoritative: convertAuthoritativeFlag(plan.IsAuthoritative),
Alias: alias,
IsVisible: isVisible,
Alias: getOptionalString(plan.Alias),
IsVisible: getOptionalBool(plan.IsVisible),
IsBrowserShortcutEnabled: isBrowserShortcutEnabled,
}, nil
}

func getOptionalBool(val types.Bool) *bool {
if !val.IsUnknown() {
return val.ValueBoolPointer()
}

return nil
}

func getOptionalString(val types.String) *string {
if !val.IsUnknown() && !val.IsNull() {
return val.ValueStringPointer()
}

return nil
}

func convertIDs(list types.Set) []string {
return utils.Map(list.Elements(), func(item tfattr.Value) string {
return item.(types.String).ValueString()
Expand Down Expand Up @@ -1075,3 +1084,9 @@ func (m caseInsensitiveDiffModifier) PlanModifyString(ctx context.Context, req p
resp.PlanValue = req.StateValue
}
}

var cidrRgxp = regexp.MustCompile(`(\d{1,3}\.){3}\d{1,3}(/\d+)?`)

func isWildcardAddress(address string) bool {
return strings.ContainsAny(address, "*?") || cidrRgxp.MatchString(address)
}
121 changes: 121 additions & 0 deletions twingate/internal/test/acctests/resource/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2454,3 +2454,124 @@ func TestAccTwingateResourceTestCaseInsensitiveAlias(t *testing.T) {
},
})
}

func TestAccTwingateResourceWithBrowserOption(t *testing.T) {
const terraformResourceName = "test40"
theResource := acctests.TerraformResource(terraformResourceName)
remoteNetworkName := test.RandomName()
resourceName := test.RandomResourceName()
wildcardAddress := "*.acc-test.com"

sdk.Test(t, sdk.TestCase{
ProtoV6ProviderFactories: acctests.ProviderFactories,
PreCheck: func() { acctests.PreCheck(t) },
CheckDestroy: acctests.CheckTwingateResourceDestroy,
Steps: []sdk.TestStep{
{
Config: createResourceWithoutBrowserOption(terraformResourceName, remoteNetworkName, resourceName, wildcardAddress),
Check: acctests.ComposeTestCheckFunc(
acctests.CheckTwingateResourceExists(theResource),
),
},
{
Config: createResourceWithBrowserOption(terraformResourceName, remoteNetworkName, resourceName, wildcardAddress, false),
Check: acctests.ComposeTestCheckFunc(
acctests.CheckTwingateResourceExists(theResource),
),
},
{
Config: createResourceWithBrowserOption(terraformResourceName, remoteNetworkName, resourceName, wildcardAddress, true),
ExpectError: regexp.MustCompile("Resources with a CIDR range or wildcard"),
},
},
})
}

func TestAccTwingateResourceWithBrowserOptionFailOnUpdate(t *testing.T) {
const terraformResourceName = "test41"
theResource := acctests.TerraformResource(terraformResourceName)
remoteNetworkName := test.RandomName()
resourceName := test.RandomResourceName()
wildcardAddress := "*.acc-test.com"
simpleAddress := "acc-test.com"

sdk.Test(t, sdk.TestCase{
ProtoV6ProviderFactories: acctests.ProviderFactories,
PreCheck: func() { acctests.PreCheck(t) },
CheckDestroy: acctests.CheckTwingateResourceDestroy,
Steps: []sdk.TestStep{
{
Config: createResourceWithoutBrowserOption(terraformResourceName, remoteNetworkName, resourceName, simpleAddress),
Check: acctests.ComposeTestCheckFunc(
acctests.CheckTwingateResourceExists(theResource),
),
},
{
Config: createResourceWithBrowserOption(terraformResourceName, remoteNetworkName, resourceName, simpleAddress, true),
Check: acctests.ComposeTestCheckFunc(
acctests.CheckTwingateResourceExists(theResource),
),
},
{
Config: createResourceWithBrowserOption(terraformResourceName, remoteNetworkName, resourceName, wildcardAddress, true),
ExpectError: regexp.MustCompile("Resources with a CIDR range or wildcard"),
},
},
})
}

func TestAccTwingateResourceWithBrowserOptionRecovered(t *testing.T) {
const terraformResourceName = "test42"
theResource := acctests.TerraformResource(terraformResourceName)
remoteNetworkName := test.RandomName()
resourceName := test.RandomResourceName()
wildcardAddress := "*.acc-test.com"
simpleAddress := "acc-test.com"

sdk.Test(t, sdk.TestCase{
ProtoV6ProviderFactories: acctests.ProviderFactories,
PreCheck: func() { acctests.PreCheck(t) },
CheckDestroy: acctests.CheckTwingateResourceDestroy,
Steps: []sdk.TestStep{
{
Config: createResourceWithBrowserOption(terraformResourceName, remoteNetworkName, resourceName, simpleAddress, true),
Check: acctests.ComposeTestCheckFunc(
acctests.CheckTwingateResourceExists(theResource),
),
},
{
Config: createResourceWithoutBrowserOption(terraformResourceName, remoteNetworkName, resourceName, wildcardAddress),
Check: acctests.ComposeTestCheckFunc(
acctests.CheckTwingateResourceExists(theResource),
),
},
},
})
}

func createResourceWithoutBrowserOption(name, networkName, resourceName, address string) string {
return fmt.Sprintf(`
resource "twingate_remote_network" "%[1]s" {
name = "%[2]s"
}
resource "twingate_resource" "%[1]s" {
name = "%[3]s"
address = "%[4]s"
remote_network_id = twingate_remote_network.%[1]s.id
}
`, name, networkName, resourceName, address)
}

func createResourceWithBrowserOption(name, networkName, resourceName, address string, browserOption bool) string {
return fmt.Sprintf(`
resource "twingate_remote_network" "%[1]s" {
name = "%[2]s"
}
resource "twingate_resource" "%[1]s" {
name = "%[3]s"
address = "%[4]s"
remote_network_id = twingate_remote_network.%[1]s.id
is_browser_shortcut_enabled = %[5]v
}
`, name, networkName, resourceName, address, browserOption)
}

0 comments on commit 6de2221

Please sign in to comment.