diff --git a/.changelog/567.txt b/.changelog/567.txt new file mode 100644 index 00000000..04d74147 --- /dev/null +++ b/.changelog/567.txt @@ -0,0 +1,8 @@ +```release-note:note +`resource/cloudavenue_edgegateway` - The `lb_enabled` attribute is now deprecated and will be removed in the version [`v0.16.0`](https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/milestone/8) of the provider. See the [GitHub issue](https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/issues/567) for more information. +``` + +```release-note:note +`datasource/cloudavenue_edgegateway` - The `lb_enabled` attribute is now deprecated and will be removed in the version [`v0.16.0`](https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/milestone/8) of the provider. +``` + diff --git a/.changelog/568.txt b/.changelog/568.txt new file mode 100644 index 00000000..25fcf263 --- /dev/null +++ b/.changelog/568.txt @@ -0,0 +1,12 @@ +```release-note:dependency +deps: bumps github.com/orange-cloudavenue/cloudavenue-sdk-go from 0.0.4 to 0.1.0 +``` + +```release-note:enhancement +`resource/cloudavenue_edgegateway` - Add new `bandwidth` attribute to manage bandwidth of the edge gateway (in Mbps). +``` + +```release-note:enhancement +`datasource/cloudavenue_edgegateway` - Add new `bandwidth` attribute to retrieve bandwidth of the edge gateway (in Mbps). +``` + diff --git a/docs/data-sources/edgegateway.md b/docs/data-sources/edgegateway.md index 09b67901..62e4ea36 100644 --- a/docs/data-sources/edgegateway.md +++ b/docs/data-sources/edgegateway.md @@ -30,10 +30,13 @@ output "gateway" { ### Read-Only +- `bandwidth` (Number) The bandwidth in Mbps of the Edge Gateway. - `description` (String) The description of the Edge Gateway. - `id` (String) The ID of the Edge Gateway. -- `lb_enabled` (Boolean) Load Balancing state on the Edge Gateway. +- `lb_enabled` (Boolean, Deprecated) Load Balancing state on the Edge Gateway. + + ~> **Attribute deprecated** Remove the `lb_enabled` attribute configuration, it will be removed in the version [`v0.16.0`](https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/milestone/8) of the provider. See the [GitHub issue](https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/issues/567) for more information. - `owner_name` (String) The name of the Edge Gateway owner. -- `owner_type` (String) The type of the Edge Gateway owner. Must be vdc or vdc-group. +- `owner_type` (String) The type of the Edge Gateway owner. Value must be one of : `vdc`, `vdc-group`. - `tier0_vrf_name` (String) The name of the Tier-0 VRF to which the Edge Gateway is attached. diff --git a/docs/resources/edgegateway.md b/docs/resources/edgegateway.md index 6c0ad651..7d37678e 100644 --- a/docs/resources/edgegateway.md +++ b/docs/resources/edgegateway.md @@ -14,11 +14,10 @@ The Edge Gateway resource allows you to create and delete Edge Gateways in Cloud ```terraform data "cloudavenue_tier0_vrfs" "example_with_vdc" {} -resource "cloudavenue_edgegateway" "example_with_vdc" { - owner_name = "MyVDC" +resource "cloudavenue_edgegateway" "example" { tier0_vrf_name = data.cloudavenue_tier0_vrfs.example_with_vdc.names.0 + owner_name = "MyVDC" owner_type = "vdc" - lb_enabled = false } ``` @@ -28,12 +27,15 @@ resource "cloudavenue_edgegateway" "example_with_vdc" { ### Required - `owner_name` (String) (ForceNew) The name of the Edge Gateway owner. -- `owner_type` (String) (ForceNew) The type of the Edge Gateway owner. Must be vdc or vdc-group. +- `owner_type` (String) (ForceNew) The type of the Edge Gateway owner. Value must be one of : `vdc`, `vdc-group`. - `tier0_vrf_name` (String) (ForceNew) The name of the Tier-0 VRF to which the Edge Gateway is attached. ### Optional -- `lb_enabled` (Boolean) Load Balancing state on the Edge Gateway. Value defaults to `true`. +- `bandwidth` (Number) The bandwidth in Mbps of the Edge Gateway. If no value is not specified, the bandwidth is automatically calculated based on the remaining bandwidth of the Tier-0 VRF. +- `lb_enabled` (Boolean, Deprecated) Load Balancing state on the Edge Gateway. + + ~> **Attribute deprecated** Remove the `lb_enabled` attribute configuration, it will be removed in the version [`v0.16.0`](https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/milestone/8) of the provider. See the [GitHub issue](https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/issues/567) for more information. - `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts)) ### Read-Only diff --git a/examples/resources/cloudavenue_edgegateway/resource.tf b/examples/resources/cloudavenue_edgegateway/resource.tf index a75278b0..98c2113a 100644 --- a/examples/resources/cloudavenue_edgegateway/resource.tf +++ b/examples/resources/cloudavenue_edgegateway/resource.tf @@ -1,8 +1,7 @@ data "cloudavenue_tier0_vrfs" "example_with_vdc" {} -resource "cloudavenue_edgegateway" "example_with_vdc" { - owner_name = "MyVDC" +resource "cloudavenue_edgegateway" "example" { tier0_vrf_name = data.cloudavenue_tier0_vrfs.example_with_vdc.names.0 + owner_name = "MyVDC" owner_type = "vdc" - lb_enabled = false -} \ No newline at end of file +} diff --git a/go.mod b/go.mod index e0bcc022..41b469d3 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 github.com/iancoleman/strcase v0.3.0 - github.com/orange-cloudavenue/cloudavenue-sdk-go v0.0.4-0.20231010202218-e3886106bffa + github.com/orange-cloudavenue/cloudavenue-sdk-go v0.2.0 github.com/orange-cloudavenue/infrapi-sdk-go v0.1.4-0.20231005074857-89878ea119fb github.com/rs/zerolog v1.31.0 github.com/thanhpk/randstr v1.0.6 @@ -48,7 +48,7 @@ require ( github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-chi/render v1.0.3 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect - github.com/go-resty/resty/v2 v2.9.1 // indirect + github.com/go-resty/resty/v2 v2.10.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index bb55191e..226c8f02 100644 --- a/go.sum +++ b/go.sum @@ -78,8 +78,8 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-resty/resty/v2 v2.9.1 h1:PIgGx4VrHvag0juCJ4dDv3MiFRlDmP0vicBucwf+gLM= -github.com/go-resty/resty/v2 v2.9.1/go.mod h1:4/GYJVjh9nhkhGR6AUNW3XhpDYNUr+Uvy9gV/VGZIy4= +github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo= +github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= @@ -216,8 +216,8 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/orange-cloudavenue/cloudavenue-sdk-go v0.0.4-0.20231010202218-e3886106bffa h1:98Mkhxg3OOqP66+S55oMVZnSichMsmiRH2kXc9NJAWU= -github.com/orange-cloudavenue/cloudavenue-sdk-go v0.0.4-0.20231010202218-e3886106bffa/go.mod h1:DWBIS3DJtS5ZiZzblCwYNo123RaxO+UrGyfqsVQbFb0= +github.com/orange-cloudavenue/cloudavenue-sdk-go v0.2.0 h1:pNCOlxzCX7Q2/8zNApgWP8VeZFwNXABNVELWElqsxQE= +github.com/orange-cloudavenue/cloudavenue-sdk-go v0.2.0/go.mod h1:8HtVSQVDVoW1pk/pFUTTOOQ2TL1SErMxV7fmke2+acg= github.com/orange-cloudavenue/infrapi-sdk-go v0.1.4-0.20231005074857-89878ea119fb h1:1/Wc21Tp9RnDOUTjKBm9x3wi+UgUkDc2bv0fHJc5f2o= github.com/orange-cloudavenue/infrapi-sdk-go v0.1.4-0.20231005074857-89878ea119fb/go.mod h1:pGa9mB6s+weCi5QtNe5nicp7yL0C/e+i+3wHRh4cjBE= github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c= @@ -287,7 +287,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= 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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= @@ -307,7 +306,6 @@ 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.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= @@ -349,7 +347,7 @@ 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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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= @@ -363,8 +361,8 @@ golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M= -golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/internal/client/client.go b/internal/client/client.go index e8be7fc2..c7b2cfe2 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -44,9 +44,9 @@ type CloudAvenue struct { urlVmware *url.URL VCDVersion string - // API Backup of NetBackup - BackupClient *clientca.Client - BackupOpts *clientca.ClientOpts + // SDK CLOUDAVENUE + CAVSDK *clientca.Client + CAVSDKOpts *clientca.ClientOpts } // New creates a new CloudAvenue client. @@ -78,12 +78,10 @@ func (c *CloudAvenue) New() (*CloudAvenue, error) { return nil, fmt.Errorf("%w : %w", ErrConfigureVmware, err) } - // API Backup of NetBackup - if c.BackupOpts.Netbackup.Username != "" && c.BackupOpts.Netbackup.Password != "" { - c.BackupClient, err = clientca.New(*c.BackupOpts) - if err != nil { - return nil, fmt.Errorf("%w : %w", ErrConfigureNetBackup, err) - } + // New SDK CloudAvenue + c.CAVSDK, err = clientca.New(*c.CAVSDKOpts) + if err != nil { + return nil, fmt.Errorf("%w : %w", ErrConfigureNetBackup, err) } return c, nil diff --git a/internal/provider/backup/backup_datasource.go b/internal/provider/backup/backup_datasource.go index f1522e04..42956a1c 100644 --- a/internal/provider/backup/backup_datasource.go +++ b/internal/provider/backup/backup_datasource.go @@ -65,7 +65,7 @@ func (d *backupDataSource) Read(ctx context.Context, req datasource.ReadRequest, } // Refresh data NetBackup from the API - job, err := d.client.BackupClient.V1.Netbackup.Inventory.Refresh() + job, err := d.client.CAVSDK.V1.Netbackup.Inventory.Refresh() if err != nil { resp.Diagnostics.AddError("Error refreshing NetBackup inventory", err.Error()) return diff --git a/internal/provider/backup/backup_resource.go b/internal/provider/backup/backup_resource.go index fb0d49cf..661a7c3d 100644 --- a/internal/provider/backup/backup_resource.go +++ b/internal/provider/backup/backup_resource.go @@ -84,7 +84,7 @@ func (r *backupResource) Create(ctx context.Context, req resource.CreateRequest, } // Refresh data NetBackup from the API - job, err := r.client.BackupClient.V1.Netbackup.Inventory.Refresh() + job, err := r.client.CAVSDK.V1.Netbackup.Inventory.Refresh() if err != nil { resp.Diagnostics.AddError("Error refreshing NetBackup inventory", err.Error()) return @@ -297,7 +297,7 @@ func (r *backupResource) ImportState(ctx context.Context, req resource.ImportSta } // Refresh data NetBackup from the API - job, err := r.client.BackupClient.V1.Netbackup.Inventory.Refresh() + job, err := r.client.CAVSDK.V1.Netbackup.Inventory.Refresh() if err != nil { resp.Diagnostics.AddError("Error refreshing NetBackup inventory", err.Error()) return @@ -459,11 +459,11 @@ func (r *backupResource) getTarget(data *backupModel) (typeTarget target, d diag var err error switch data.Type.Get() { case vdc: - typeTarget, err = r.client.BackupClient.V1.Netbackup.VCloud.GetVdcByNameOrIdentifier(data.getTargetIDOrName()) + typeTarget, err = r.client.CAVSDK.V1.Netbackup.VCloud.GetVdcByNameOrIdentifier(data.getTargetIDOrName()) case vapp: - typeTarget, err = r.client.BackupClient.V1.Netbackup.VCloud.GetVAppByNameOrIdentifier(data.getTargetIDOrName()) + typeTarget, err = r.client.CAVSDK.V1.Netbackup.VCloud.GetVAppByNameOrIdentifier(data.getTargetIDOrName()) case vm: - typeTarget, err = r.client.BackupClient.V1.Netbackup.Machines.GetMachineByNameOrIdentifier(data.getTargetIDOrName()) + typeTarget, err = r.client.CAVSDK.V1.Netbackup.Machines.GetMachineByNameOrIdentifier(data.getTargetIDOrName()) } if err != nil { d.AddError(fmt.Sprintf("Error getting vCloud Director %s", data.Type.Get()), err.Error()) diff --git a/internal/provider/edgegw/edgegateway_datasource.go b/internal/provider/edgegw/edgegateway_datasource.go index 178238b3..da6819b8 100644 --- a/internal/provider/edgegw/edgegateway_datasource.go +++ b/internal/provider/edgegw/edgegateway_datasource.go @@ -3,16 +3,11 @@ package edgegw import ( "context" - "errors" "fmt" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework/datasource" - apiclient "github.com/orange-cloudavenue/infrapi-sdk-go" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/client" - "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/helpers" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/metrics" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/pkg/uuid" ) @@ -60,67 +55,37 @@ func (d *edgeGatewayDataSource) Configure(ctx context.Context, req datasource.Co } func (d *edgeGatewayDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { - var ( - data edgeGatewayDataSourceModel - gateway apiclient.EdgeGateway - ) defer metrics.New("data.cloudavenue_edgegateway", d.client.GetOrgName(), metrics.Read)() - // Read Terraform configuration data into the model - resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + config := &edgeGatewayDatasourceModel{} + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, config)...) if resp.Diagnostics.HasError() { return } - gateways, httpR, err := d.client.APIClient.EdgeGatewaysApi.GetEdges(d.client.Auth) - - if httpR != nil { - defer func() { - err = errors.Join(err, httpR.Body.Close()) - }() - } + data := config.Copy() - if apiErr := helpers.CheckAPIError(err, httpR); apiErr != nil { - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) + // Read data from the API + edgegw, err := d.client.CAVSDK.V1.EdgeGateway.GetByName(config.Name.Get()) + if err != nil { + resp.Diagnostics.AddError("Error retrieving edge gateway", err.Error()) return } - found := false - for _, gateway = range gateways { - if data.Name.Equal(types.StringValue(gateway.EdgeName)) { - found = true - break - } - } + data.ID.Set(uuid.Normalize(uuid.Gateway, edgegw.GetID()).String()) + data.Tier0VrfID.Set(edgegw.GetTier0VrfID()) + data.OwnerName.Set(edgegw.GetOwnerName()) + data.OwnerType.Set(string(edgegw.GetOwnerType())) + data.Description.Set(edgegw.GetDescription()) + data.Bandwidth.SetInt(int(edgegw.GetBandwidth())) - if !found { - data.ID = types.StringValue("") - } else { - // Get LoadBalancing state. - gatewaysLoadBalancing, httpR, err := d.client.APIClient.EdgeGatewaysApi.GetEdgeLoadBalancing(d.client.Auth, gateway.EdgeId) - if apiErr := helpers.CheckAPIError(err, httpR); apiErr != nil { - defer httpR.Body.Close() - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) - if resp.Diagnostics.HasError() { - return - } - } - - data = edgeGatewayDataSourceModel{ - Tier0VrfID: types.StringValue(gateway.Tier0VrfId), - Name: types.StringValue(gateway.EdgeName), - ID: types.StringValue(uuid.Normalize(uuid.Gateway, gateway.EdgeId).String()), - OwnerType: types.StringValue(gateway.OwnerType), - OwnerName: types.StringValue(gateway.OwnerName), - Description: types.StringValue(gateway.Description), - EnableLoadBalancing: types.BoolValue((gatewaysLoadBalancing.Enabled)), - } + // EnableLoadBalancing is now deprecated, but we still need to set it to false if it is unknown + if !data.EnableLoadBalancing.IsKnown() { + data.EnableLoadBalancing.Set(false) } - // Save data into Terraform state + // Set refreshed state resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) - if resp.Diagnostics.HasError() { - return - } } diff --git a/internal/provider/edgegw/edgegateway_resource.go b/internal/provider/edgegw/edgegateway_resource.go index 283174f0..80eeff0c 100644 --- a/internal/provider/edgegw/edgegateway_resource.go +++ b/internal/provider/edgegw/edgegateway_resource.go @@ -5,23 +5,19 @@ import ( "context" "errors" "fmt" - "net/http" "time" "golang.org/x/exp/slices" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - - apiclient "github.com/orange-cloudavenue/infrapi-sdk-go" + commoncloudavenue "github.com/orange-cloudavenue/cloudavenue-sdk-go/pkg/common/cloudavenue" + v1 "github.com/orange-cloudavenue/cloudavenue-sdk-go/v1" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/client" - "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/helpers" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/metrics" - "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/common" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/common/cloudavenue" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/pkg/uuid" ) @@ -32,9 +28,10 @@ const ( // Ensure the implementation satisfies the expected interfaces. var ( - _ resource.Resource = &edgeGatewaysResource{} - _ resource.ResourceWithConfigure = &edgeGatewaysResource{} - _ resource.ResourceWithImportState = &edgeGatewaysResource{} + _ resource.Resource = &edgeGatewayResource{} + _ resource.ResourceWithConfigure = &edgeGatewayResource{} + _ resource.ResourceWithImportState = &edgeGatewayResource{} + _ resource.ResourceWithModifyPlan = &edgeGatewayResource{} // ConfigEdgeGateway is the default configuration for edge gateway. ConfigEdgeGateway setDefaultEdgeGateway = func() EdgeGatewayConfig { @@ -46,7 +43,7 @@ var ( // NewEdgeGatewayResource returns a new resource implementing the edge_gateway data source. func NewEdgeGatewayResource() resource.Resource { - return &edgeGatewaysResource{} + return &edgeGatewayResource{} } type setDefaultEdgeGateway func() EdgeGatewayConfig @@ -56,27 +53,144 @@ type EdgeGatewayConfig struct { CheckJobDelay time.Duration } -// edgeGatewaysResource is the resource implementation. -type edgeGatewaysResource struct { +// edgeGatewayResource is the resource implementation. +type edgeGatewayResource struct { client *client.CloudAvenue EdgeGatewayConfig } +// ModifyPlan modifies the plan to add the default values. +func (r *edgeGatewayResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { + var ( + plan = &edgeGatewayResourceModel{} + state = &edgeGatewayResourceModel{} + ) + + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + loadRemainingBandwidth := func() (int, error) { + edgegws, err := r.client.CAVSDK.V1.EdgeGateway.List() + if err != nil { + return 0, err + } + + return edgegws.GetBandwidthCapacityRemaining(plan.Tier0VrfID.Get()) + } + + allowedValuesFunc := func() { + allowedValues, err := r.client.CAVSDK.V1.EdgeGateway.GetAllowedBandwidthValues(plan.Tier0VrfID.Get()) + if err != nil { + resp.Diagnostics.AddError("Error on calculating allowed Bandwidth values", err.Error()) + return + } + + if !slices.Contains(allowedValues, plan.Bandwidth.GetInt()) { + resp.Diagnostics.AddError("Invalid Bandwidth value", fmt.Sprintf("Bandwidth value must be one of %v", allowedValues)) + return + } + } + + // determine la valeur autorisé la plus proche de la valeur demandé + calculBestValue := func(value int, allowedValues []int) int { + var bestValue int + for _, v := range allowedValues { + if v <= value && v > bestValue { + bestValue = v + } + } + return bestValue + } + + // If the plan is nil, then this is a delete operation. + if plan == nil { + return + } + + switch { + // Create case with value is known + case plan.Bandwidth.IsKnown() && (state == nil || !state.Bandwidth.IsKnown()): + allowedValuesFunc() + remaining, err := loadRemainingBandwidth() + if err != nil { + if errors.Is(err, fmt.Errorf("no bandwidth capacity remaining")) { + resp.Diagnostics.AddError("Error on calculating remaining bandwidth", "Not enough bandwidth available") + return + } + resp.Diagnostics.AddError("Error on calculating remaining bandwidth", err.Error()) + return + } + + if plan.Bandwidth.GetInt() > remaining { + resp.Diagnostics.AddAttributeError(path.Root("bandwidth"), "Overcommitting bandwidth", fmt.Sprintf("Not enough bandwidth available, requested: %dMbps, available: %dMbps", plan.Bandwidth.GetInt(), remaining)) + } + goto END + + // Create case with value is unknown + case !plan.Bandwidth.IsKnown(): + remaining, err := loadRemainingBandwidth() + if err != nil { + if errors.Is(err, fmt.Errorf("no bandwidth capacity remaining")) { + resp.Diagnostics.AddError("Error on calculating remaining bandwidth", "Not enough bandwidth available") + return + } + resp.Diagnostics.AddError("Error on calculating remaining bandwidth", err.Error()) + return + } + + allowedValues, err := r.client.CAVSDK.V1.EdgeGateway.GetAllowedBandwidthValues(plan.Tier0VrfID.Get()) + if err != nil { + resp.Diagnostics.AddError("Error on calculating allowed Bandwidth values", err.Error()) + return + } + + remaining = calculBestValue(remaining, allowedValues) + + resp.Diagnostics.AddAttributeWarning(path.Root("bandwidth"), "Bandwidth value is unknown, will be set to remaining bandwidth", fmt.Sprintf("Bandwidth defined to %dMbps", remaining)) + plan.Bandwidth.SetInt(remaining) + goto END + + // Update case + case !plan.Bandwidth.Equal(state.Bandwidth): + allowedValues, err := r.client.CAVSDK.V1.EdgeGateway.GetAllowedBandwidthValues(plan.Tier0VrfID.Get()) + if err != nil { + resp.Diagnostics.AddError("Error on calculating allowed Bandwidth values", err.Error()) + return + } + + // Ignore error because recalculating remaining bandwidth with bandwidth released by the update + remaining, _ := loadRemainingBandwidth() + remainingOnUpdate := calculBestValue(remaining+state.Bandwidth.GetInt(), allowedValues) + + if plan.Bandwidth.IsUnknown() && remainingOnUpdate > 0 { + plan.Bandwidth.SetInt(remainingOnUpdate) + } else if plan.Bandwidth.GetInt() > remainingOnUpdate { + resp.Diagnostics.AddAttributeError(path.Root("bandwidth"), "Overcommitting bandwidth", fmt.Sprintf("Not enough bandwidth available, requested: %dMbps, available: %dMbps", plan.Bandwidth.GetInt(), remainingOnUpdate)) + return + } + + allowedValuesFunc() + goto END + } + +END: + resp.Diagnostics.Append(resp.Plan.Set(ctx, plan)...) +} + // Metadata returns the resource type name. -func (r *edgeGatewaysResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { +func (r *edgeGatewayResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_" + categoryName } // Schema defines the schema for the resource. -func (r *edgeGatewaysResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *edgeGatewayResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = edgegwSchema().GetResource(ctx) } -func (r *edgeGatewaysResource) Configure( - ctx context.Context, - req resource.ConfigureRequest, - resp *resource.ConfigureResponse, -) { +func (r *edgeGatewayResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { // Prevent panic if the provider has not been configured. if req.ProviderData == nil { return @@ -101,192 +215,121 @@ func (r *edgeGatewaysResource) Configure( } // Create creates the resource and sets the initial Terraform state. -func (r *edgeGatewaysResource) Create( - ctx context.Context, - req resource.CreateRequest, - resp *resource.CreateResponse, -) { - // Retrieve values from plan - var plan *edgeGatewaysResourceModel +func (r *edgeGatewayResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Create)() - diags := req.Plan.Get(ctx, &plan) - resp.Diagnostics.Append(diags...) + plan := &edgeGatewayResourceModel{} + + // Retrieve values from plan + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Create)() - // Create() is passed a default timeout to use if no value // has been supplied in the Terraform configuration. - createTimeout, errTO := plan.Timeouts.Create(ctx, 8*time.Minute) - if errTO != nil { - resp.Diagnostics.AddError( - "Error creating timeout", - "Could not create timeout, unexpected error", - ) + createTimeout, d := plan.Timeouts.Create(ctx, 8*time.Minute) + if d.HasError() { + resp.Diagnostics.Append(d...) return } - ctxTO, cancel := context.WithTimeout(ctx, createTimeout) + var cancel context.CancelFunc + + ctx, cancel = context.WithTimeout(ctx, createTimeout) defer cancel() - auth, errCtx := helpers.GetAuthContextWithTO(r.client.Auth, ctxTO) - if errCtx != nil { - resp.Diagnostics.AddError( - "Error creating context", - "Could not create context, context value token is not a string ?", - ) + // List all edge gateways for determining the ID of the new edge gateway + edgegws, err := r.client.CAVSDK.V1.EdgeGateway.List() + if err != nil { + resp.Diagnostics.AddError("Error listing edge gateways", err.Error()) return } - // Create new edge gateway - body := apiclient.EdgeGatewayCreate{ - Tier0VrfId: plan.Tier0VrfID.ValueString(), - EnableLoadBalancing: plan.EnableLoadBalancing.ValueBool(), - } - cloudavenue.Lock(ctx) defer cloudavenue.Unlock(ctx) - var err error - var job apiclient.Jobcreated - var httpR *http.Response + var job *commoncloudavenue.JobStatus switch plan.OwnerType.ValueString() { case "vdc": - // Check if vDC exist - if _, _, errGetOrg := r.client.GetOrgAndVDC(r.client.GetOrgName(), plan.OwnerName.ValueString()); errGetOrg != nil { - resp.Diagnostics.AddError("Error retrieving VDC", errGetOrg.Error()) - return - } - - // Create Edge Gateway - job, httpR, err = r.client.APIClient.EdgeGatewaysApi.CreateVdcEdge( - auth, - body, - plan.OwnerName.ValueString(), - ) - - if httpR != nil { - defer func() { - err = errors.Join(err, httpR.Body.Close()) - }() - } - - if apiErr := helpers.CheckAPIError(err, httpR); apiErr != nil { - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) - return - } + job, err = r.client.CAVSDK.V1.EdgeGateway.New(plan.OwnerName.Get(), plan.Tier0VrfID.Get()) case "vdc-group": - // Check if vDC Group exist - adminOrg, errGetAdminOrg := r.client.Vmware.GetAdminOrgByNameOrId(r.client.GetOrgName()) - if errGetAdminOrg != nil { - resp.Diagnostics.AddError("Error retrieving Org", errGetAdminOrg.Error()) - return - } - if _, errGetVDCGroup := adminOrg.GetVdcGroupByName(plan.OwnerName.ValueString()); errGetVDCGroup != nil { - resp.Diagnostics.AddError("Error retrieving vDC Group", errGetVDCGroup.Error()) - return - } - - // Create Edge Gateway - job, httpR, errGetAdminOrg = r.client.APIClient.EdgeGatewaysApi.CreateVdcGroupEdge( - auth, - body, - plan.OwnerName.ValueString(), - ) - - if httpR != nil { - defer func() { - errGetAdminOrg = errors.Join(errGetAdminOrg, httpR.Body.Close()) - }() - } - - if apiErr := helpers.CheckAPIError(errGetAdminOrg, httpR); apiErr != nil { - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) - return - } + job, err = r.client.CAVSDK.V1.EdgeGateway.NewFromVDCGroup(plan.OwnerName.Get(), plan.Tier0VrfID.Get()) } - - // Wait for job to complete - errRetry := retry.RetryContext(ctxTO, createTimeout, func() *retry.RetryError { - jobStatus, errGetJob := helpers.GetJobStatus(auth, r.client, job.JobId) - if errGetJob != nil { - return retry.NonRetryableError(errGetJob) - } - if !slices.Contains(helpers.JobStateDone(), jobStatus.String()) { - return retry.RetryableError(fmt.Errorf("expected job done but was %s", jobStatus)) - } - - return nil - }) - - if errRetry != nil { - resp.Diagnostics.AddError("Error waiting job to complete", errRetry.Error()) + if err != nil { + resp.Diagnostics.AddError("Error creating edge gateway", err.Error()) return } - // Job done, retrieve edge gateway - // get all edge gateways and find the one that matches the tier0_vrf_id and owner_name - gateways, httpRc, errEdgesGet := r.client.APIClient.EdgeGatewaysApi.GetEdges(auth) - if httpRc != nil { - defer func() { - err = errors.Join(err, httpRc.Body.Close()) - }() + if err := job.Wait(1, int(createTimeout.Seconds())); err != nil { + resp.Diagnostics.AddError("Error waiting for edge gateway creation", err.Error()) + return } - if apiErr := helpers.CheckAPIError(errEdgesGet, httpRc); apiErr != nil { - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) + + // Find the new edge gateway + edgegwsRefreshed, err := r.client.CAVSDK.V1.EdgeGateway.List() + if err != nil { + resp.Diagnostics.AddError("Error listing edge gateways", err.Error()) return } - var newEdgeGW apiclient.EdgeGateway - for _, gw := range gateways { - if gw.Tier0VrfId == plan.Tier0VrfID.ValueString() && gw.OwnerName == plan.OwnerName.ValueString() { - newEdgeGW = gw + var edgegwNew v1.EdgeGw + + // Find the new edge gateway in the list of all edge gateways and set the ID. New edge gateway is in the list refreshed but not in the old list. + for _, edgegw := range *edgegwsRefreshed { + var found bool + for _, edgegwOld := range *edgegws { + if edgegw.GetID() == edgegwOld.GetID() { + found = true + } + } + if !found { + plan.ID.Set(uuid.Normalize(uuid.Gateway, edgegw.GetID()).String()) + plan.Name.Set(edgegw.GetName()) + edgegwNew = edgegw break } } - if newEdgeGW == (apiclient.EdgeGateway{}) { - resp.Diagnostics.AddError("Error retrieving edge gateway", "edge gateway not found after the create action") + if edgegwNew == (v1.EdgeGw{}) { + resp.Diagnostics.AddError("Error retrieving new edge gateway", "New edge gateway not found") return } - plan = &edgeGatewaysResourceModel{ - ID: types.StringValue(uuid.Normalize(uuid.Gateway, newEdgeGW.EdgeId).String()), - Name: types.StringValue(newEdgeGW.EdgeName), - Description: types.StringValue(newEdgeGW.Description), - Tier0VrfID: plan.Tier0VrfID, - OwnerName: plan.OwnerName, - OwnerType: plan.OwnerType, - Timeouts: plan.Timeouts, - EnableLoadBalancing: plan.EnableLoadBalancing, + if int(edgegwNew.GetBandwidth()) != plan.Bandwidth.GetInt() { + job, err = edgegwNew.UpdateBandwidth(plan.Bandwidth.GetInt()) + if err != nil { + resp.Diagnostics.AddError("Error setting Bandwidth", err.Error()) + return + } + + if err := job.Wait(1, int(createTimeout.Seconds())); err != nil { + resp.Diagnostics.AddError("Error waiting for Bandwidth update", err.Error()) + return + } } - // Set state to fully populated data - diags = resp.State.Set(ctx, plan) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { + // Use generic read function to refresh the state + stateRefreshed, _, d := r.read(ctx, plan) + if d.HasError() { + resp.Diagnostics.Append(d...) return } + + // Set state to fully populated data + resp.Diagnostics.Append(resp.State.Set(ctx, stateRefreshed)...) } // Read refreshes the Terraform state with the latest data. -func (r *edgeGatewaysResource) Read( - ctx context.Context, - req resource.ReadRequest, - resp *resource.ReadResponse, -) { - // Get current state - var state *edgeGatewaysResourceModel - diags := req.State.Get(ctx, &state) - resp.Diagnostics.Append(diags...) +func (r *edgeGatewayResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Read)() + + state := &edgeGatewayResourceModel{} + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Read)() - // Read timeout readTimeout, errTO := state.Timeouts.Read(ctx, 8*time.Minute) if errTO != nil { @@ -297,109 +340,31 @@ func (r *edgeGatewaysResource) Read( return } - ctxTO, cancel := context.WithTimeout(ctx, readTimeout) + var cancel context.CancelFunc + + ctx, cancel = context.WithTimeout(ctx, readTimeout) defer cancel() - auth, errCtx := helpers.GetAuthContextWithTO(r.client.Auth, ctxTO) - if errCtx != nil { - resp.Diagnostics.AddError( - "Error creating context", - "Could not create context, context value token is not a string ?", - ) + // Refresh the state + stateRefreshed, found, d := r.read(ctx, state) + if !found { + resp.State.RemoveResource(ctx) return } - - var gateway apiclient.EdgeGateway - // Get edge gateway - if !state.ID.IsNull() { - var ( - httpR *http.Response - err error - ) - gateway, httpR, err = r.client.APIClient.EdgeGatewaysApi.GetEdgeById( - auth, - common.ExtractUUID(state.ID.ValueString()), - ) - - if httpR != nil { - defer func() { - err = errors.Join(err, httpR.Body.Close()) - }() - } - - if apiErr := helpers.CheckAPIError(err, httpR); apiErr != nil { - if !apiErr.IsNotFound() { - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) - return - } - - resp.State.RemoveResource(ctxTO) - return - } - } else { - gateways, httpR, err := r.client.APIClient.EdgeGatewaysApi.GetEdges(auth) - - if httpR != nil { - defer func() { - err = errors.Join(err, httpR.Body.Close()) - }() - } - - if apiErr := helpers.CheckAPIError(err, httpR); apiErr != nil { - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) - return - } - - found := false - for _, gateway = range gateways { - if state.Name.Equal(types.StringValue(gateway.EdgeName)) { - found = true - break - } - } - - if !found { - resp.State.RemoveResource(ctxTO) - return - } - } - - // Get LoadBalancing state. - gatewaysLoadBalancing, httpR, err := r.client.APIClient.EdgeGatewaysApi.GetEdgeLoadBalancing(auth, gateway.EdgeId) - if apiErr := helpers.CheckAPIError(err, httpR); apiErr != nil { - defer httpR.Body.Close() - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) - if resp.Diagnostics.HasError() { - return - } - } - - state = &edgeGatewaysResourceModel{ - ID: types.StringValue(uuid.Normalize(uuid.Gateway, gateway.EdgeId).String()), - Tier0VrfID: types.StringValue(gateway.Tier0VrfId), - Name: types.StringValue(gateway.EdgeName), - OwnerType: types.StringValue(gateway.OwnerType), - OwnerName: types.StringValue(gateway.OwnerName), - Description: types.StringValue(gateway.Description), - EnableLoadBalancing: types.BoolValue(gatewaysLoadBalancing.Enabled), - Timeouts: state.Timeouts, + if d.HasError() { + resp.Diagnostics.Append(d...) + return } // Set refreshed state - diags = resp.State.Set(ctxTO, &state) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } + resp.Diagnostics.Append(resp.State.Set(ctx, stateRefreshed)...) } // Update updates the resource and sets the updated Terraform state on success. -func (r *edgeGatewaysResource) Update( - ctx context.Context, - req resource.UpdateRequest, - resp *resource.UpdateResponse, -) { - var plan *edgeGatewaysResourceModel +func (r *edgeGatewayResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Update)() + + plan := &edgeGatewayResourceModel{} // Read Terraform plan data into the model resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) @@ -407,7 +372,8 @@ func (r *edgeGatewaysResource) Update( return } - defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Update)() + cloudavenue.Lock(ctx) + defer cloudavenue.Unlock(ctx) // Update() is passed a default timeout to use if no value // has been supplied in the Terraform configuration. @@ -420,80 +386,55 @@ func (r *edgeGatewaysResource) Update( return } - ctxTO, cancel := context.WithTimeout(ctx, updateTimeout) + var cancel context.CancelFunc + + ctx, cancel = context.WithTimeout(ctx, updateTimeout) defer cancel() - auth, errCtx := helpers.GetAuthContextWithTO(r.client.Auth, ctxTO) - if errCtx != nil { - resp.Diagnostics.AddError( - "Error creating context", - "Could not create context, context value token is not a string ?", - ) + edgegw, err := r.client.CAVSDK.V1.EdgeGateway.GetByID(plan.ID.Get()) + if err != nil { + resp.Diagnostics.AddError("Error retrieving edge gateway", err.Error()) return } - // Convert from Terraform data model into API data model - body := apiclient.EdgeGatewayLoadBalancing{ - Enabled: plan.EnableLoadBalancing.ValueBool(), + job, err := edgegw.UpdateBandwidth(plan.Bandwidth.GetInt()) + if err != nil { + resp.Diagnostics.AddError("Error setting Bandwidth", err.Error()) + return } - var err error - var job apiclient.Jobcreated - var httpR *http.Response - - // Call API to update the resource and test for errors. - job, httpR, err = r.client.APIClient.EdgeGatewaysApi.UpdateEdgeLoadBalancing(auth, body, common.ExtractUUID(plan.ID.ValueString())) - if apiErr := helpers.CheckAPIError(err, httpR); apiErr != nil { - defer httpR.Body.Close() - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) - if resp.Diagnostics.HasError() { - return - } + if err := job.Wait(1, int(updateTimeout.Seconds())); err != nil { + resp.Diagnostics.AddError("Error waiting for Bandwidth update", err.Error()) + return } - // Wait for job to complete - errRetry := retry.RetryContext(ctxTO, updateTimeout, func() *retry.RetryError { - jobStatus, errGetJob := helpers.GetJobStatus(auth, r.client, job.JobId) - if errGetJob != nil { - retry.NonRetryableError(err) - } - if !slices.Contains(helpers.JobStateDone(), jobStatus.String()) { - return retry.RetryableError(fmt.Errorf("expected job done but was %s", jobStatus)) - } - - return nil - }) - - if errRetry != nil { - resp.Diagnostics.AddError("Error waiting job to complete", errRetry.Error()) + // Use generic read function to refresh the state + stateRefreshed, _, d := r.read(ctx, plan) + if d.HasError() { + resp.Diagnostics.Append(d...) return } - // Save updated data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + // Set state to fully populated data + resp.Diagnostics.Append(resp.State.Set(ctx, stateRefreshed)...) } // Delete deletes the resource and removes the Terraform state on success. -func (r *edgeGatewaysResource) Delete( - ctx context.Context, - req resource.DeleteRequest, - resp *resource.DeleteResponse, -) { +func (r *edgeGatewayResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Delete)() + + state := &edgeGatewayResourceModel{} + // Get current state - var state edgeGatewaysResourceModel - diags := req.State.Get(ctx, &state) - resp.Diagnostics.Append(diags...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Delete)() - cloudavenue.Lock(ctx) defer cloudavenue.Unlock(ctx) - // Delete timeout - deleteTimeout, errTO := state.Timeouts.Delete(ctx, 8*time.Minute) + deleteTimeout, errTO := state.Timeouts.Update(ctx, 8*time.Minute) if errTO != nil { resp.Diagnostics.AddError( "Error creating timeout", @@ -502,59 +443,82 @@ func (r *edgeGatewaysResource) Delete( return } - ctxTO, cancel := context.WithTimeout(ctx, deleteTimeout) - defer cancel() - - auth, errCtx := helpers.GetAuthContextWithTO(r.client.Auth, ctxTO) - if errCtx != nil { - resp.Diagnostics.AddError( - "Error creating context", - "Could not create context, context value token is not a string ?", - ) + edgegw, err := r.client.CAVSDK.V1.EdgeGateway.GetByID(state.ID.Get()) + if err != nil { + if commoncloudavenue.IsNotFound(err) { + resp.State.RemoveResource(ctx) + return + } + resp.Diagnostics.AddError("Error retrieving edge gateway", err.Error()) return } - // Delete the edge gateway - job, httpR, err := r.client.APIClient.EdgeGatewaysApi.DeleteEdge( - auth, - common.ExtractUUID(state.ID.ValueString()), - ) - if httpR != nil { - defer func() { - err = errors.Join(err, httpR.Body.Close()) - }() + job, err := edgegw.Delete() + if err != nil { + resp.Diagnostics.AddError("Error deleting edge gateway", err.Error()) + return } - if apiErr := helpers.CheckAPIError(err, httpR); apiErr != nil { - resp.Diagnostics.Append(apiErr.GetTerraformDiagnostic()) + if err := job.Wait(1, int(deleteTimeout.Seconds())); err != nil { + resp.Diagnostics.AddError("Error waiting for edge gateway deletion", err.Error()) return } +} + +func (r *edgeGatewayResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Import)() + + // Retrieve import Name and save to name attribute + resource.ImportStatePassthroughID(ctx, path.Root("name"), req, resp) +} + +// * Custom funcs. +func (r *edgeGatewayResource) read(_ context.Context, planOrState *edgeGatewayResourceModel) (stateRefreshed *edgeGatewayResourceModel, found bool, diags diag.Diagnostics) { + stateRefreshed = planOrState.Copy() - // Wait for job to complete - errRetry := retry.RetryContext(ctxTO, deleteTimeout, func() *retry.RetryError { - jobStatus, errGetJob := helpers.GetJobStatus(auth, r.client, job.JobId) - if errGetJob != nil { - retry.NonRetryableError(err) + var ( + edgegw *v1.EdgeGw + err error + ) + + switch { + case planOrState.ID.IsKnown(): + edgegw, err = r.client.CAVSDK.V1.EdgeGateway.GetByID(planOrState.ID.Get()) + if err != nil { + if commoncloudavenue.IsNotFound(err) { + return nil, false, nil + } + diags.AddError("Error retrieving edge gateway", err.Error()) + return nil, true, diags } - if !slices.Contains(helpers.JobStateDone(), jobStatus.String()) { - return retry.RetryableError(fmt.Errorf("expected job done but was %s", jobStatus)) + case planOrState.Name.IsKnown(): + edgegw, err = r.client.CAVSDK.V1.EdgeGateway.GetByName(planOrState.Name.Get()) + if err != nil { + if commoncloudavenue.IsNotFound(err) { + return nil, false, nil + } + diags.AddError("Error retrieving edge gateway", err.Error()) + return nil, true, diags } + default: + diags.AddError("Error retrieving edge gateway", "Either name or ID must be set") + return nil, true, diags + } - return nil - }) - - if errRetry != nil { - resp.Diagnostics.AddError("Error waiting job to complete", errRetry.Error()) + if !planOrState.ID.IsKnown() { + stateRefreshed.ID.Set(uuid.Normalize(uuid.Gateway, edgegw.GetID()).String()) } -} -func (r *edgeGatewaysResource) ImportState( - ctx context.Context, - req resource.ImportStateRequest, - resp *resource.ImportStateResponse, -) { - defer metrics.New("cloudavenue_edgegateway", r.client.GetOrgName(), metrics.Import)() + stateRefreshed.Tier0VrfID.Set(edgegw.GetTier0VrfID()) + stateRefreshed.OwnerName.Set(edgegw.GetOwnerName()) + stateRefreshed.OwnerType.Set(string(edgegw.GetOwnerType())) + stateRefreshed.Description.Set(edgegw.GetDescription()) + stateRefreshed.Bandwidth.SetInt(int(edgegw.GetBandwidth())) - // Retrieve import Name and save to name attribute - resource.ImportStatePassthroughID(ctx, path.Root("name"), req, resp) + // EnableLoadBalancing is now deprecated, but we still need to set it to false if it is unknown + if !stateRefreshed.EnableLoadBalancing.IsKnown() { + stateRefreshed.EnableLoadBalancing.Set(false) + } + + return stateRefreshed, true, nil } diff --git a/internal/provider/edgegw/edgegateway_schema.go b/internal/provider/edgegw/edgegateway_schema.go index 1fb942ce..e5bfd87d 100644 --- a/internal/provider/edgegw/edgegateway_schema.go +++ b/internal/provider/edgegw/edgegateway_schema.go @@ -1,14 +1,11 @@ package edgegw import ( - "regexp" - "github.com/hashicorp/terraform-plugin-framework/schema/validator" schemaD "github.com/hashicorp/terraform-plugin-framework/datasource/schema" schemaR "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" @@ -42,7 +39,7 @@ func edgegwSchema() superschema.Schema { Update: true, }, }, - "id": &superschema.StringAttribute{ + "id": &superschema.SuperStringAttribute{ Common: &schemaR.StringAttribute{ MarkdownDescription: "The ID of the Edge Gateway.", Computed: true, @@ -53,7 +50,7 @@ func edgegwSchema() superschema.Schema { }, }, }, - "name": &superschema.StringAttribute{ + "name": &superschema.SuperStringAttribute{ Common: &schemaR.StringAttribute{ MarkdownDescription: "The name of the Edge Gateway.", }, @@ -67,7 +64,7 @@ func edgegwSchema() superschema.Schema { Required: true, }, }, - "tier0_vrf_name": &superschema.StringAttribute{ + "tier0_vrf_name": &superschema.SuperStringAttribute{ Common: &schemaR.StringAttribute{ MarkdownDescription: "The name of the Tier-0 VRF to which the Edge Gateway is attached.", }, @@ -81,14 +78,11 @@ func edgegwSchema() superschema.Schema { Computed: true, }, }, - "owner_type": &superschema.StringAttribute{ + "owner_type": &superschema.SuperStringAttribute{ Common: &schemaR.StringAttribute{ MarkdownDescription: "The type of the Edge Gateway owner.", Validators: []validator.String{ - stringvalidator.RegexMatches( - regexp.MustCompile(`^(vdc|vdc-group)$`), - "must be vdc or vdc-group", - ), + stringvalidator.OneOf("vdc", "vdc-group"), }, }, Resource: &schemaR.StringAttribute{ @@ -101,7 +95,7 @@ func edgegwSchema() superschema.Schema { Computed: true, }, }, - "owner_name": &superschema.StringAttribute{ + "owner_name": &superschema.SuperStringAttribute{ Common: &schemaR.StringAttribute{ MarkdownDescription: "The name of the Edge Gateway owner.", }, @@ -115,7 +109,7 @@ func edgegwSchema() superschema.Schema { Computed: true, }, }, - "description": &superschema.StringAttribute{ + "description": &superschema.SuperStringAttribute{ Common: &schemaR.StringAttribute{ MarkdownDescription: "The description of the Edge Gateway.", Computed: true, @@ -126,14 +120,32 @@ func edgegwSchema() superschema.Schema { }, }, }, - "lb_enabled": &superschema.BoolAttribute{ + "bandwidth": &superschema.SuperInt64Attribute{ + Common: &schemaR.Int64Attribute{ + MarkdownDescription: "The bandwidth in Mbps of the Edge Gateway.", + Computed: true, + }, + Resource: &schemaR.Int64Attribute{ + Optional: true, + MarkdownDescription: "If no value is not specified, the bandwidth is automatically calculated based on the remaining bandwidth of the Tier-0 VRF.", + }, + }, + "lb_enabled": &superschema.SuperBoolAttribute{ + Deprecated: &superschema.Deprecated{ + DeprecationMessage: "Remove the lb_enabled attribute configuration and the attribute will be removed in the version 0.16.0 of the provider. This field have does not work and will be replaced soon by a new resource.", + ComputeMarkdownDeprecationMessage: true, + Removed: true, + FromAttributeName: "lb_enabled", + TargetRelease: "v0.16.0", + LinkToMilestone: "https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/milestone/8", + LinkToIssue: "https://github.com/orange-cloudavenue/terraform-provider-cloudavenue/issues/567", + }, Common: &schemaR.BoolAttribute{ MarkdownDescription: "Load Balancing state on the Edge Gateway.", Computed: true, }, Resource: &schemaR.BoolAttribute{ Optional: true, - Default: booldefault.StaticBool(true), }, }, }, diff --git a/internal/provider/edgegw/edgegateway_types.go b/internal/provider/edgegw/edgegateway_types.go index 581807b4..5674a00a 100644 --- a/internal/provider/edgegw/edgegateway_types.go +++ b/internal/provider/edgegw/edgegateway_types.go @@ -1,28 +1,46 @@ package edgegw import ( - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + + supertypes "github.com/FrangipaneTeam/terraform-plugin-framework-supertypes" + + "github.com/orange-cloudavenue/terraform-provider-cloudavenue/pkg/utils" ) -type edgeGatewaysResourceModel struct { - Timeouts timeouts.Value `tfsdk:"timeouts"` - ID types.String `tfsdk:"id"` - Tier0VrfID types.String `tfsdk:"tier0_vrf_name"` - Name types.String `tfsdk:"name"` - OwnerType types.String `tfsdk:"owner_type"` - OwnerName types.String `tfsdk:"owner_name"` - Description types.String `tfsdk:"description"` - EnableLoadBalancing types.Bool `tfsdk:"lb_enabled"` +type edgeGatewayResourceModel struct { + Timeouts timeouts.Value `tfsdk:"timeouts"` + ID supertypes.StringValue `tfsdk:"id"` + Tier0VrfID supertypes.StringValue `tfsdk:"tier0_vrf_name"` + Name supertypes.StringValue `tfsdk:"name"` + OwnerType supertypes.StringValue `tfsdk:"owner_type"` + OwnerName supertypes.StringValue `tfsdk:"owner_name"` + Description supertypes.StringValue `tfsdk:"description"` + EnableLoadBalancing supertypes.BoolValue `tfsdk:"lb_enabled"` + Bandwidth supertypes.Int64Value `tfsdk:"bandwidth"` +} + +type edgeGatewayDatasourceModel struct { + ID supertypes.StringValue `tfsdk:"id"` + Tier0VrfID supertypes.StringValue `tfsdk:"tier0_vrf_name"` + Name supertypes.StringValue `tfsdk:"name"` + OwnerType supertypes.StringValue `tfsdk:"owner_type"` + OwnerName supertypes.StringValue `tfsdk:"owner_name"` + Description supertypes.StringValue `tfsdk:"description"` + EnableLoadBalancing supertypes.BoolValue `tfsdk:"lb_enabled"` + Bandwidth supertypes.Int64Value `tfsdk:"bandwidth"` +} + +// Copy returns a copy of the edgeGatewayResourceModel. +func (rm *edgeGatewayResourceModel) Copy() *edgeGatewayResourceModel { + x := &edgeGatewayResourceModel{} + utils.ModelCopy(rm, x) + return x } -type edgeGatewayDataSourceModel struct { - ID types.String `tfsdk:"id"` - Tier0VrfID types.String `tfsdk:"tier0_vrf_name"` - Name types.String `tfsdk:"name"` - OwnerType types.String `tfsdk:"owner_type"` - OwnerName types.String `tfsdk:"owner_name"` - Description types.String `tfsdk:"description"` - EnableLoadBalancing types.Bool `tfsdk:"lb_enabled"` +// Copy returns a copy of the edgeGatewayDatasourceModel. +func (dm *edgeGatewayDatasourceModel) Copy() *edgeGatewayDatasourceModel { + x := &edgeGatewayDatasourceModel{} + utils.ModelCopy(dm, x) + return x } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 6bad2979..676f2ac9 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -57,13 +57,9 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config return } - netbackup := clientnetbackup.Opts{ - Endpoint: findValue(config.NetBackupURL, "NETBACKUP_URL"), - Username: findValue(config.NetBackupUser, "NETBACKUP_USER"), - Password: findValue(config.NetBackupPassword, "NETBACKUP_PASSWORD"), - } - cloudAvenue := client.CloudAvenue{ + CloudAvenueVersion: p.version, + // Legacy SDK Cloudavenue URL: func() string { url := findValue(config.URL, "CLOUDAVENUE_URL") if url == "" { @@ -71,14 +67,35 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config } return url }(), - User: findValue(config.User, "CLOUDAVENUE_USER"), - Password: findValue(config.Password, "CLOUDAVENUE_PASSWORD"), - Org: findValue(config.Org, "CLOUDAVENUE_ORG"), - VDC: findValue(config.VDC, "CLOUDAVENUE_VDC"), - TerraformVersion: req.TerraformVersion, - CloudAvenueVersion: p.version, - VCDVersion: VCDVersion, - BackupOpts: &casdk.ClientOpts{Netbackup: netbackup, CloudAvenue: clientcloudavenue.Opts{}}, + User: findValue(config.User, "CLOUDAVENUE_USER"), + Password: findValue(config.Password, "CLOUDAVENUE_PASSWORD"), + Org: findValue(config.Org, "CLOUDAVENUE_ORG"), + VDC: findValue(config.VDC, "CLOUDAVENUE_VDC"), + TerraformVersion: req.TerraformVersion, + VCDVersion: VCDVersion, + + // This is a new SDK Cloudavenue + CAVSDKOpts: &casdk.ClientOpts{ + Netbackup: clientnetbackup.Opts{ + Endpoint: findValue(config.NetBackupURL, "NETBACKUP_URL"), + Username: findValue(config.NetBackupUser, "NETBACKUP_USER"), + Password: findValue(config.NetBackupPassword, "NETBACKUP_PASSWORD"), + }, + CloudAvenue: clientcloudavenue.Opts{ + Endpoint: func() string { + url := findValue(config.URL, "CLOUDAVENUE_URL") + if url == "" { + url = "https://console1.cloudavenue.orange-business.com" + } + return url + }(), + Username: findValue(config.User, "CLOUDAVENUE_USER"), + Password: findValue(config.Password, "CLOUDAVENUE_PASSWORD"), + Org: findValue(config.Org, "CLOUDAVENUE_ORG"), + VDC: findValue(config.VDC, "CLOUDAVENUE_VDC"), + VCDVersion: VCDVersion, + }, + }, } // If any of the expected configurations are missing, return @@ -111,7 +128,7 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config ) } - if cloudAvenue.BackupOpts.Netbackup.Username == "" && cloudAvenue.BackupOpts.Netbackup.Password != "" { + if cloudAvenue.CAVSDKOpts.Netbackup.Username == "" && cloudAvenue.CAVSDKOpts.Netbackup.Password != "" { resp.Diagnostics.AddAttributeError( path.Root("netbackup_user"), "Missing NetBackup API User", @@ -120,7 +137,7 @@ func (p *cloudavenueProvider) Configure(ctx context.Context, req provider.Config "If either is already set, ensure the value is not empty.", ) } - if cloudAvenue.BackupOpts.Netbackup.Password == "" && cloudAvenue.BackupOpts.Netbackup.Username != "" { + if cloudAvenue.CAVSDKOpts.Netbackup.Password == "" && cloudAvenue.CAVSDKOpts.Netbackup.Username != "" { resp.Diagnostics.AddAttributeError( path.Root("netbackup_password"), "Missing NetBackup API Password", diff --git a/internal/testsacc/acctest_datasources_test.go b/internal/testsacc/acctest_datasources_test.go index 3b7360e1..904febd6 100644 --- a/internal/testsacc/acctest_datasources_test.go +++ b/internal/testsacc/acctest_datasources_test.go @@ -12,5 +12,8 @@ func GetDataSourceConfig() map[testsacc.ResourceName]func() resourceConfig { // * Backup BackupDataSourceName: NewResourceConfig(NewBackupDataSourceTest()), + + // * EdgeGateway + EdgeGatewayDataSourceName: NewResourceConfig(NewEdgeGatewayDataSourceTest()), } } diff --git a/internal/testsacc/edgegw_edgegateway_datasource_test.go b/internal/testsacc/edgegw_edgegateway_datasource_test.go index 306d65e8..0768fa82 100644 --- a/internal/testsacc/edgegw_edgegateway_datasource_test.go +++ b/internal/testsacc/edgegw_edgegateway_datasource_test.go @@ -2,40 +2,58 @@ package testsacc import ( - "regexp" + "context" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/orange-cloudavenue/terraform-provider-cloudavenue/pkg/uuid" + "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/helpers/testsacc" ) -const testAccEdgeGatewayDataSourceConfig = ` -data "cloudavenue_edgegateway" "test" { - name = "tn01e02ocb0006205spt101" +var _ testsacc.TestACC = &EdgeGatewayDataSource{} + +const ( + EdgeGatewayDataSourceName = testsacc.ResourceName("data.cloudavenue_edgegateway") +) + +type EdgeGatewayDataSource struct{} + +func NewEdgeGatewayDataSourceTest() testsacc.TestACC { + return &EdgeGatewayDataSource{} +} + +// GetResourceName returns the name of the resource. +func (r *EdgeGatewayDataSource) GetResourceName() string { + return EdgeGatewayDataSourceName.String() +} + +func (r *EdgeGatewayDataSource) DependenciesConfig() (configs testsacc.TFData) { + configs.Append(GetResourceConfig()[EdgeGatewayResourceName]().GetDefaultConfig()) + return +} + +func (r *EdgeGatewayDataSource) Tests(ctx context.Context) map[testsacc.TestName]func(ctx context.Context, resourceName string) testsacc.Test { + return map[testsacc.TestName]func(ctx context.Context, resourceName string) testsacc.Test{ + // * Test One (backup vdc example) + "example": func(_ context.Context, _ string) testsacc.Test { + return testsacc.Test{ + // ! Create testing + Create: testsacc.TFConfig{ + TFConfig: ` + data "cloudavenue_edgegateway" "example" { + name = cloudavenue_edgegateway.example.name + }`, + Checks: GetResourceConfig()[EdgeGatewayResourceName]().GetDefaultChecks(), + }, + } + }, + } } -` func TestAccEdgeGatewayDataSource(t *testing.T) { - dataSourceName := "data.cloudavenue_edgegateway.test" resource.Test(t, resource.TestCase{ PreCheck: func() { TestAccPreCheck(t) }, ProtoV6ProviderFactories: TestAccProtoV6ProviderFactories, - Steps: []resource.TestStep{ - // Read testing - { - Config: testAccEdgeGatewayDataSourceConfig, - Check: resource.ComposeAggregateTestCheckFunc( - // Verify placeholder id attribute - resource.TestMatchResourceAttr(dataSourceName, "id", regexp.MustCompile(uuid.Gateway.String()+`[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`)), - resource.TestCheckResourceAttrSet(dataSourceName, "name"), - resource.TestCheckResourceAttrSet(dataSourceName, "tier0_vrf_name"), - resource.TestCheckResourceAttrSet(dataSourceName, "owner_type"), - resource.TestCheckResourceAttrSet(dataSourceName, "owner_name"), - resource.TestCheckResourceAttrSet(dataSourceName, "description"), - resource.TestCheckResourceAttrSet(dataSourceName, "lb_enabled"), - ), - }, - }, + Steps: testsacc.GenerateTests(&EdgeGatewayDataSource{}), }) } diff --git a/internal/testsacc/edgegw_edgegateway_resource_test.go b/internal/testsacc/edgegw_edgegateway_resource_test.go index a958754e..908eed3c 100644 --- a/internal/testsacc/edgegw_edgegateway_resource_test.go +++ b/internal/testsacc/edgegw_edgegateway_resource_test.go @@ -65,12 +65,12 @@ func (r *EdgeGatewayResource) Tests(ctx context.Context) map[testsacc.TestName]f "example": func(_ context.Context, resourceName string) testsacc.Test { return testsacc.Test{ CommonChecks: []resource.TestCheckFunc{ - resource.TestCheckResourceAttrWith(resourceName, "id", uuid.TestIsType(uuid.Gateway)), resource.TestCheckResourceAttrSet(resourceName, "owner_name"), resource.TestCheckResourceAttr(resourceName, "owner_type", "vdc"), // Read-Only attributes - resource.TestMatchResourceAttr(resourceName, "name", regexp.MustCompile(`tn01e02ocb0006205spt[0-9]{3}`)), + resource.TestCheckResourceAttrWith(resourceName, "id", uuid.TestIsType(uuid.Gateway)), + resource.TestCheckResourceAttrSet(resourceName, "name"), resource.TestCheckResourceAttrSet(resourceName, "description"), }, // ! Create testing @@ -80,34 +80,64 @@ func (r *EdgeGatewayResource) Tests(ctx context.Context) map[testsacc.TestName]f owner_name = cloudavenue_vdc.example.name tier0_vrf_name = data.cloudavenue_tier0_vrf.example.name owner_type = "vdc" - lb_enabled = false }`), Checks: []resource.TestCheckFunc{ - resource.TestCheckResourceAttr(resourceName, "lb_enabled", "false"), + resource.TestCheckResourceAttrSet(resourceName, "bandwidth"), + resource.TestCheckResourceAttr(resourceName, "lb_enabled", "false"), // Deprecated attribute }, }, // ! Updates testing Updates: []testsacc.TFConfig{ - // Update lb_enabled - // { - // TFConfig: testsacc.GenerateFromTemplate(resourceName, ` - // resource "cloudavenue_edgegateway" "example" { - // owner_name = cloudavenue_vdc.example.name - // tier0_vrf_name = data.cloudavenue_tier0_vrf.example.name - // owner_type = "vdc" - // lb_enabled = true - // }`), - // Checks: []resource.TestCheckFunc{ - // resource.TestCheckResourceAttr(resourceName, "lb_enabled", "true"), - // }, - // }, + // Test one of range value allowed in bandwidth attribute + { + TFConfig: testsacc.GenerateFromTemplate(resourceName, ` + resource "cloudavenue_edgegateway" "example" { + owner_name = cloudavenue_vdc.example.name + tier0_vrf_name = data.cloudavenue_tier0_vrf.example.name + owner_type = "vdc" + bandwidth = 20 + }`), + TFAdvanced: testsacc.TFAdvanced{ + PlanOnly: true, + ExpectNonEmptyPlan: true, + ExpectError: regexp.MustCompile(`Invalid Bandwidth value`), + }, + }, + // Test overcommit bandwidth + { + TFConfig: testsacc.GenerateFromTemplate(resourceName, ` + resource "cloudavenue_edgegateway" "example" { + owner_name = cloudavenue_vdc.example.name + tier0_vrf_name = data.cloudavenue_tier0_vrf.example.name + owner_type = "vdc" + bandwidth = 300 + }`), + TFAdvanced: testsacc.TFAdvanced{ + PlanOnly: true, + ExpectNonEmptyPlan: true, + ExpectError: regexp.MustCompile(`Overcommitting bandwidth`), + }, + }, + // Update bandwidth + { + TFConfig: testsacc.GenerateFromTemplate(resourceName, ` + resource "cloudavenue_edgegateway" "example" { + owner_name = cloudavenue_vdc.example.name + tier0_vrf_name = data.cloudavenue_tier0_vrf.example.name + owner_type = "vdc" + bandwidth = 25 + }`), + Checks: []resource.TestCheckFunc{ + resource.TestCheckResourceAttr(resourceName, "bandwidth", "25"), + resource.TestCheckResourceAttr(resourceName, "lb_enabled", "false"), + }, + }, }, // ! Imports testing Imports: []testsacc.TFImport{ { ImportStateIDBuilder: []string{"name"}, ImportState: true, - ImportStateVerify: true, }, }, }