-
Notifications
You must be signed in to change notification settings - Fork 61
feat(vpn): Onboarding VPN gateway #1453
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
Open
s-inter
wants to merge
36
commits into
main
Choose a base branch
from
si/onboard-VPN-gateway
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
38faf3b
feat(vpn): add vpn to provider and core
s-inter c7bc13a
feat(vpn): add utils
s-inter 84a4998
feat(vpn): add datasource for VPN gateway
s-inter 7dbf049
feat(vpn): add resource for VPN gateway
s-inter ce6c205
feat(vpn): add unit tests for VPN gateway
s-inter 907878a
feat(vpn): add test data and acceptance tests for VPN gateway
s-inter 161ec48
fix(vpn): resolve linter issues - linter bypass per contribution guid…
s-inter d7c6581
fix(vpn): update gateway tests to use new() function
s-inter f9bce3e
feat(vpn): add example resource for VPN gateway
s-inter 046bce8
fix(vpn): update VPN gateway data source schema and add acceptance tests
s-inter b3b1b93
feat(vpn): upgrade to v1 api version
s-inter 1f5dc72
Merge branch 'main' into si/onboard-VPN-gateway
s-inter 9dddb67
feat(vpn): update vpn service dependency to v0.8.0
s-inter a1d144a
feat(docs): add VPN Gateway documentation
s-inter 3f3255a
Merge branch 'main' into si/onboard-VPN-gateway
s-inter 89f8aa4
fix(vpn): apply review suggestions
s-inter 717b089
fix(vpn): remove duplicate util function
s-inter 8be876d
fix(vpn): update VPN service version to v0.9.0 and refactor client co…
s-inter a4d44e5
fix(vpn): rename to util.go (singular)
s-inter 889dd7c
fix(vpn): remove redundant datasource test file (map function already…
s-inter de6c7a2
fix(vpn): refactor gateway unit tests
s-inter b1b2d6d
fix(vpn): fix acceptance test
s-inter 47d8450
fix(vpn): add additional acc testing
s-inter 8dc4893
Merge branch 'main' into si/onboard-VPN-gateway
s-inter ba5027a
fix(vpn): update LocalAsn initialization to use new() for pointer
s-inter ab4bafb
fix(vpn): use standard field naming for 'Id'
s-inter 07d97b6
fix(vpn): preserve empty routes and labels in state mapping and updat…
s-inter c827000
fix(vpn): apply review suggestions
s-inter 2327a5f
docs(vpn): add example for data source and remove region from resourc…
s-inter 9e04072
Merge branch 'main' into si/onboard-VPN-gateway
s-inter 0347849
refactor(vpn): use label helper in gateway resource
s-inter 558a82a
Merge branch 'main' into si/onboard-VPN-gateway
s-inter 0f8ff77
fix(vpn): fix tests
s-inter 4538c49
refactor(vpn): remove unnecessary availability zones check in payload…
s-inter a12d990
feat(vpn): add validation for BGP routing type configuration
s-inter ea20b42
refactor(vpn): update descriptions for schema
s-inter File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| package gateway | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
| "fmt" | ||
| "net/http" | ||
|
|
||
| "github.com/hashicorp/terraform-plugin-framework/datasource" | ||
| "github.com/hashicorp/terraform-plugin-framework/datasource/schema" | ||
| "github.com/hashicorp/terraform-plugin-framework/schema/validator" | ||
| "github.com/hashicorp/terraform-plugin-framework/types" | ||
| "github.com/hashicorp/terraform-plugin-log/tflog" | ||
|
|
||
| "github.com/stackitcloud/stackit-sdk-go/core/oapierror" | ||
| vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1beta1api" | ||
|
|
||
| "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" | ||
| "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/vpn/utils" | ||
|
|
||
| "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" | ||
| "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" | ||
| ) | ||
|
|
||
| var ( | ||
| _ datasource.DataSource = (*vpnGatewayDataSource)(nil) | ||
| _ datasource.DataSourceWithConfigure = (*vpnGatewayDataSource)(nil) | ||
|
s-inter marked this conversation as resolved.
Outdated
|
||
| ) | ||
|
|
||
| type vpnGatewayDataSource struct { | ||
| client *vpn.APIClient | ||
| providerData core.ProviderData | ||
| } | ||
|
|
||
| func NewVPNGatewayDataSource() datasource.DataSource { | ||
| return &vpnGatewayDataSource{} | ||
| } | ||
|
|
||
| // Configure implements [datasource.DataSourceWithConfigure]. | ||
| func (d *vpnGatewayDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { | ||
| providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) | ||
|
s-inter marked this conversation as resolved.
Outdated
|
||
| if !ok { | ||
| return | ||
| } | ||
|
|
||
| apiClient := utils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) | ||
|
s-inter marked this conversation as resolved.
Outdated
|
||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } | ||
| d.client = apiClient | ||
| d.providerData = providerData | ||
| tflog.Info(ctx, "VPN client configured") | ||
| } | ||
|
|
||
| // Metadata implements [datasource.DataSource]. | ||
| func (d *vpnGatewayDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { | ||
| resp.TypeName = req.ProviderTypeName + "_vpn_gateway" | ||
| } | ||
|
|
||
| // Schema implements [datasource.DataSource]. | ||
| func (d *vpnGatewayDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { | ||
| resp.Schema = schema.Schema{ | ||
| Description: fmt.Sprintf("VPN Gateway data source schema. %s", core.DatasourceRegionFallbackDocstring), | ||
| Attributes: map[string]schema.Attribute{ | ||
| "id": schema.StringAttribute{ | ||
| Description: schemaDescriptions["id"], | ||
| Computed: true, | ||
| }, | ||
| "project_id": schema.StringAttribute{ | ||
| Description: schemaDescriptions["project_id"], | ||
| Required: true, | ||
| Validators: []validator.String{ | ||
| validate.UUID(), | ||
| validate.NoSeparator(), | ||
| }, | ||
| }, | ||
| "region": schema.StringAttribute{ | ||
| Description: schemaDescriptions["region"], | ||
| Required: true, | ||
|
s-inter marked this conversation as resolved.
Outdated
|
||
| }, | ||
| "gateway_id": schema.StringAttribute{ | ||
| Description: schemaDescriptions["gateway_id"], | ||
| Required: true, | ||
| Validators: []validator.String{ | ||
| validate.UUID(), | ||
| validate.NoSeparator(), | ||
| }, | ||
| }, | ||
| "display_name": schema.StringAttribute{ | ||
| Description: schemaDescriptions["display_name"], | ||
| Computed: true, | ||
| }, | ||
| "plan_id": schema.StringAttribute{ | ||
| Description: schemaDescriptions["plan_id"], | ||
| Computed: true, | ||
| }, | ||
| "routing_type": schema.StringAttribute{ | ||
| Description: schemaDescriptions["routing_type"], | ||
| Computed: true, | ||
| }, | ||
| "availability_zones": schema.SingleNestedAttribute{ | ||
| Description: schemaDescriptions["availability_zones"], | ||
| Computed: true, | ||
| Attributes: map[string]schema.Attribute{ | ||
| "tunnel1": schema.StringAttribute{ | ||
| Description: "Availability zone for tunnel 1.", | ||
| Computed: true, | ||
| }, | ||
| "tunnel2": schema.StringAttribute{ | ||
| Description: "Availability zone for tunnel 2.", | ||
| Computed: true, | ||
| }, | ||
| }, | ||
| }, | ||
| "bgp": schema.SingleNestedAttribute{ | ||
| Description: schemaDescriptions["bgp"], | ||
| Computed: true, | ||
| Attributes: map[string]schema.Attribute{ | ||
| "local_asn": schema.Int64Attribute{ | ||
| Description: "Local ASN for BGP (private ASN range, 64512-4294967294).", | ||
| Computed: true, | ||
| }, | ||
| "override_advertised_routes": schema.ListAttribute{ | ||
| Description: "List of IPv4 CIDRs to advertise via BGP.", | ||
| Computed: true, | ||
| ElementType: types.StringType, | ||
| }, | ||
| }, | ||
| }, | ||
| "labels": schema.MapAttribute{ | ||
| Description: schemaDescriptions["labels"], | ||
| Computed: true, | ||
| ElementType: types.StringType, | ||
| }, | ||
| "state": schema.StringAttribute{ | ||
| Description: schemaDescriptions["state"], | ||
| Computed: true, | ||
| }, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| // Read implements [datasource.DataSource]. | ||
| func (d *vpnGatewayDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform | ||
| var model Model | ||
| diags := req.Config.Get(ctx, &model) | ||
| resp.Diagnostics.Append(diags...) | ||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } | ||
|
|
||
| ctx = core.InitProviderContext(ctx) | ||
|
|
||
| projectId := model.ProjectID.ValueString() | ||
| region := d.providerData.GetRegionWithOverride(model.Region) | ||
| gatewayId := model.GatewayID.ValueString() | ||
|
|
||
| ctx = tflog.SetField(ctx, "project_id", projectId) | ||
| ctx = tflog.SetField(ctx, "region", region) | ||
| ctx = tflog.SetField(ctx, "gateway_id", gatewayId) | ||
|
|
||
| gatewayResponse, err := d.client.DefaultAPI.GetVPNGateway(ctx, projectId, vpn.Region(region), gatewayId).Execute() | ||
| if err != nil { | ||
| var oapiErr *oapierror.GenericOpenAPIError | ||
| ok := errors.As(err, &oapiErr) | ||
| if ok && oapiErr.StatusCode == http.StatusNotFound { | ||
| resp.State.RemoveResource(ctx) | ||
| return | ||
| } | ||
| core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading VPN gateway", fmt.Sprintf("Calling API: %v", err)) | ||
| return | ||
| } | ||
| ctx = core.LogResponse(ctx) | ||
|
|
||
| err = mapFields(ctx, gatewayResponse, &model, region) | ||
| if err != nil { | ||
| core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading VPN gateway", fmt.Sprintf("Processing response: %v", err)) | ||
| return | ||
| } | ||
|
|
||
| // Set state | ||
| diags = resp.State.Set(ctx, model) | ||
| resp.Diagnostics.Append(diags...) | ||
| if resp.Diagnostics.HasError() { | ||
| return | ||
| } | ||
| tflog.Info(ctx, "VPN gateway read", map[string]any{ | ||
| "gateway_id": gatewayId, | ||
| }) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| package gateway | ||
|
|
||
| import ( | ||
| "context" | ||
| "testing" | ||
|
|
||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/hashicorp/terraform-plugin-framework/types" | ||
| "github.com/stackitcloud/stackit-sdk-go/core/utils" | ||
| vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1beta1api" | ||
| ) | ||
|
|
||
| func TestDataSourceMapFields(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| input *vpn.GatewayResponse | ||
| expected Model | ||
| isValid bool | ||
| }{ | ||
| { | ||
| "basic_gateway", | ||
| &vpn.GatewayResponse{ | ||
| Id: utils.Ptr("gateway-id"), | ||
| DisplayName: "test-gateway", | ||
| PlanId: "p500", | ||
| RoutingType: vpn.ROUTINGTYPE_ROUTE_BASED, | ||
| AvailabilityZones: vpn.GatewayAvailabilityZones{ | ||
| Tunnel1: "eu01-1", | ||
| Tunnel2: "eu01-2", | ||
| }, | ||
| State: utils.Ptr(vpn.GATEWAYSTATUS_READY), | ||
| }, | ||
| Model{ | ||
| GatewayID: types.StringValue("gateway-id"), | ||
| DisplayName: types.StringValue("test-gateway"), | ||
| PlanID: types.StringValue("p500"), | ||
| RoutingType: types.StringValue("ROUTE_BASED"), | ||
| AvailabilityZones: &AvailabilityZonesModel{ | ||
| Tunnel1: types.StringValue("eu01-1"), | ||
| Tunnel2: types.StringValue("eu01-2"), | ||
| }, | ||
| State: types.StringValue("READY"), | ||
| }, | ||
| true, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| var model Model | ||
| model.ProjectID = types.StringValue("test-project") | ||
| model.Region = types.StringValue("eu01") | ||
|
|
||
| err := mapFields(context.Background(), tt.input, &model, "eu01") | ||
|
|
||
| if !tt.isValid && err == nil { | ||
| t.Fatalf("expected error, got none") | ||
| } | ||
| if tt.isValid && err != nil { | ||
| t.Fatalf("expected no error, got %v", err) | ||
| } | ||
| if !tt.isValid { | ||
| return | ||
| } | ||
|
|
||
| if diff := cmp.Diff(model.GatewayID, tt.expected.GatewayID); diff != "" { | ||
|
s-inter marked this conversation as resolved.
Outdated
|
||
| t.Fatalf("GatewayID mismatch (-got +want):\n%s", diff) | ||
| } | ||
| if diff := cmp.Diff(model.DisplayName, tt.expected.DisplayName); diff != "" { | ||
| t.Fatalf("DisplayName mismatch (-got +want):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.