diff --git a/.changelog/849.txt b/.changelog/849.txt new file mode 100644 index 00000000..e5320650 --- /dev/null +++ b/.changelog/849.txt @@ -0,0 +1,7 @@ +```release-note:bug +`datasource/cloudavenue_edgegateway_app_port_profile` - Fixed the issue where the `cloudavenue_edgegateway_app_port_profile` datasource was not returning the correct value App Port Profile ID. +``` + +```release-note:breaking-change +`datasource/cloudavenue_edgegateway_app_port_profile` - Now the datasource require one of the following attributes to be set: `edge_gateway_id` or `edge_gateway_name`. +``` \ No newline at end of file diff --git a/docs/data-sources/edgegateway_app_port_profile.md b/docs/data-sources/edgegateway_app_port_profile.md index 8a9c3cfb..f2401593 100644 --- a/docs/data-sources/edgegateway_app_port_profile.md +++ b/docs/data-sources/edgegateway_app_port_profile.md @@ -25,6 +25,8 @@ data "cloudavenue_edgegateway_app_port_profile" "example" { ### Optional +- `edge_gateway_id` (String) ID of the Edge Gateway. Ensure that one and only one attribute from this collection is set : `edge_gateway_id`, `edge_gateway_name`. +- `edge_gateway_name` (String) Name of the Edge Gateway. Ensure that one and only one attribute from this collection is set : `edge_gateway_id`, `edge_gateway_name`. - `id` (String) The ID of the App Port profile. Ensure that one and only one attribute from this collection is set : `name`, `id`. - `name` (String) Application Port Profile name. Ensure that one and only one attribute from this collection is set : `name`, `id`. diff --git a/internal/provider/edgegw/app_port_profile_datasource.go b/internal/provider/edgegw/app_port_profile_datasource.go index bdda49ae..896201f4 100644 --- a/internal/provider/edgegw/app_port_profile_datasource.go +++ b/internal/provider/edgegw/app_port_profile_datasource.go @@ -5,13 +5,13 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/datasource" - supertypes "github.com/FrangipaneTeam/terraform-plugin-framework-supertypes" - "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/client" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/metrics" + "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/common/edgegw" "github.com/orange-cloudavenue/terraform-provider-cloudavenue/internal/provider/common/org" ) @@ -27,11 +27,28 @@ func NewAppPortProfileDataSource() datasource.DataSource { type appPortProfileDataSource struct { client *client.CloudAvenue org org.Org + edgegw edgegw.EdgeGateway } // Init Initializes the data source. -func (d *appPortProfileDataSource) Init(ctx context.Context, dm *AppPortProfileModelADatasource) (diags diag.Diagnostics) { +func (d *appPortProfileDataSource) Init(ctx context.Context, dm *AppPortProfileModel) (diags diag.Diagnostics) { + var err error + d.org, diags = org.Init(d.client) + if diags.HasError() { + return + } + + // Retrieve VDC from edge gateway + d.edgegw, err = d.org.GetEdgeGateway(edgegw.BaseEdgeGW{ + ID: types.StringValue(dm.EdgeGatewayID.Get()), + Name: types.StringValue(dm.EdgeGatewayName.Get()), + }) + if err != nil { + diags.AddError("Error retrieving Edge Gateway", err.Error()) + return + } + return } @@ -63,8 +80,7 @@ func (d *appPortProfileDataSource) Configure(ctx context.Context, req datasource func (d *appPortProfileDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { defer metrics.New("data.cloudavenue_edgegateway_app_port_profile", d.client.GetOrgName(), metrics.Read)() - config := &AppPortProfileModelADatasource{} - + config := &AppPortProfileModel{} // Read Terraform configuration data into the model resp.Diagnostics.Append(req.Config.Get(ctx, config)...) if resp.Diagnostics.HasError() { @@ -85,17 +101,11 @@ func (d *appPortProfileDataSource) Read(ctx context.Context, req datasource.Read s := &appPortProfileResource{ client: d.client, org: d.org, + edgegw: d.edgegw, } // Read data from the API - data, found, diags := s.read(ctx, &AppPortProfileModel{ - ID: config.ID, - Name: config.Name, - Description: config.Description, - EdgeGatewayID: supertypes.NewStringNull(), - EdgeGatewayName: supertypes.NewStringNull(), - AppPorts: config.AppPorts, - }) + configRefreshed, found, diags := s.read(ctx, config) if !found { if config.ID.IsKnown() { resp.Diagnostics.AddError("Not found", fmt.Sprintf("App Port Profile ID %q not found", config.ID)) @@ -109,12 +119,6 @@ func (d *appPortProfileDataSource) Read(ctx context.Context, req datasource.Read return } - // Write data into the model - config.ID = data.ID - config.Name = data.Name - config.Description = data.Description - config.AppPorts = data.AppPorts - // Save data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &config)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &configRefreshed)...) } diff --git a/internal/provider/edgegw/app_port_profile_resource.go b/internal/provider/edgegw/app_port_profile_resource.go index 098e64f3..86408a6c 100644 --- a/internal/provider/edgegw/app_port_profile_resource.go +++ b/internal/provider/edgegw/app_port_profile_resource.go @@ -3,6 +3,7 @@ package edgegw import ( "context" "fmt" + "net/url" "strings" "github.com/vmware/go-vcloud-director/v2/govcd" @@ -31,11 +32,28 @@ func NewAppPortProfileResource() resource.Resource { type appPortProfileResource struct { client *client.CloudAvenue org org.Org + edgegw edgegw.EdgeGateway } // Init Initializes the resource. func (r *appPortProfileResource) Init(ctx context.Context, rm *AppPortProfileModel) (diags diag.Diagnostics) { + var err error + r.org, diags = org.Init(r.client) + if diags.HasError() { + return + } + + // Retrieve VDC from edge gateway + r.edgegw, err = r.org.GetEdgeGateway(edgegw.BaseEdgeGW{ + ID: types.StringValue(rm.EdgeGatewayID.Get()), + Name: types.StringValue(rm.EdgeGatewayName.Get()), + }) + if err != nil { + diags.AddError("Error retrieving Edge Gateway", err.Error()) + return + } + return } @@ -88,17 +106,7 @@ func (r *appPortProfileResource) Create(ctx context.Context, req resource.Create Implement the resource creation logic here. */ - // Retrieve VDC from edge gateway - edgegw, err := r.org.GetEdgeGateway(edgegw.BaseEdgeGW{ - ID: types.StringValue(plan.EdgeGatewayID.Get()), - Name: types.StringValue(plan.EdgeGatewayName.Get()), - }) - if err != nil { - resp.Diagnostics.AddError("Error retrieving Edge Gateway", err.Error()) - return - } - - vdcOrVDCGroup, err := edgegw.GetParent() + vdcOrVDCGroup, err := r.edgegw.GetParent() if err != nil { resp.Diagnostics.AddError("Error retrieving Edge Gateway parent", err.Error()) return @@ -124,8 +132,8 @@ func (r *appPortProfileResource) Create(ctx context.Context, req resource.Create } plan.ID.Set(appPortProfile.NsxtAppPortProfile.ID) - plan.EdgeGatewayID.Set(edgegw.GetID()) - plan.EdgeGatewayName.Set(edgegw.GetName()) + plan.EdgeGatewayID.Set(r.edgegw.GetID()) + plan.EdgeGatewayName.Set(r.edgegw.GetName()) state, found, d := r.read(ctx, plan) if !found { resp.State.RemoveResource(ctx) @@ -274,12 +282,6 @@ func (r *appPortProfileResource) ImportState(ctx context.Context, req resource.I var d diag.Diagnostics - r.org, d = org.Init(r.client) - if d.HasError() { - resp.Diagnostics.Append(d...) - return - } - // split req.ID into edge gateway ID and app port profile ID/name split := strings.Split(req.ID, ".") if len(split) != 2 { @@ -307,19 +309,11 @@ func (r *appPortProfileResource) ImportState(ctx context.Context, req resource.I x.Name.Set(appPortProfileIDOrName) } - // Retrieve VDC from edge gateway - edgegw, err := r.org.GetEdgeGateway(edgegw.BaseEdgeGW{ - ID: types.StringValue(x.EdgeGatewayID.Get()), - Name: types.StringValue(x.EdgeGatewayName.Get()), - }) - if err != nil { - resp.Diagnostics.AddError("Error retrieving Edge Gateway", err.Error()) + resp.Diagnostics.Append(r.Init(ctx, x)...) + if resp.Diagnostics.HasError() { return } - x.EdgeGatewayID.Set(edgegw.GetID()) - x.EdgeGatewayName.Set(edgegw.GetName()) - stateRefreshed, found, d := r.read(ctx, x) if !found { resp.State.RemoveResource(ctx) @@ -347,51 +341,31 @@ func (r *appPortProfileResource) read(ctx context.Context, planOrState *AppPortP if planOrState.ID.IsKnown() { appPortProfile, err = r.org.GetNsxtAppPortProfileById(stateRefreshed.ID.Get()) } else { - appPortProfilesTenant, erR := r.org.GetAllNsxtAppPortProfiles(nil, govcdtypes.ApplicationPortProfileScopeTenant) - if erR != nil { - diags.AddError("Error reading App Port Profiles", erR.Error()) - return - } - - for _, singleAppPortProfile := range appPortProfilesTenant { - if singleAppPortProfile.NsxtAppPortProfile.Name == stateRefreshed.Name.Get() { - appPortProfile = singleAppPortProfile - break - } - } - - if appPortProfile == nil { - appPortProfilesProvider, erR := r.org.GetAllNsxtAppPortProfiles(nil, govcdtypes.ApplicationPortProfileScopeProvider) - if erR != nil { - diags.AddError("Error reading App Port Profiles", erR.Error()) - return - } + scopes := []string{govcdtypes.ApplicationPortProfileScopeTenant, govcdtypes.ApplicationPortProfileScopeProvider, govcdtypes.ApplicationPortProfileScopeSystem} - for _, singleAppPortProfile := range appPortProfilesProvider { - if singleAppPortProfile.NsxtAppPortProfile.Name == stateRefreshed.Name.Get() { - appPortProfile = singleAppPortProfile - break - } - } + vdcOrVDCGroup, err := r.edgegw.GetParent() + if err != nil { + diags.AddError("Error retrieving Edge Gateway parent", err.Error()) + return } - if appPortProfile == nil { - appPortProfilesSystem, erR := r.org.GetAllNsxtAppPortProfiles(nil, govcdtypes.ApplicationPortProfileScopeSystem) - if erR != nil { - diags.AddError("Error reading App Port Profiles", erR.Error()) - return - } - - for _, singleAppPortProfile := range appPortProfilesSystem { - if singleAppPortProfile.NsxtAppPortProfile.Name == stateRefreshed.Name.Get() { - appPortProfile = singleAppPortProfile - break + for _, scope := range scopes { + queryParams := url.Values{} + queryParams.Add("filter", fmt.Sprintf("name==%s;scope==%s;_context==%s", stateRefreshed.Name.Get(), scope, vdcOrVDCGroup.GetID())) + appPortProfiles, _ := r.org.GetAllNsxtAppPortProfiles(queryParams, "") + // Error is ignored because we want to continue searching in other scopes if not found + if len(appPortProfiles) > 0 { + if len(appPortProfiles) > 1 { + diags.AddError("Error reading App Port Profiles", fmt.Sprintf("expected exactly one Application Port Profile with name '%s'. Got %d", stateRefreshed.Name.Get(), len(appPortProfiles))) + return } + appPortProfile = appPortProfiles[0] + break } } if appPortProfile == nil { - err = govcd.ErrorEntityNotFound + return nil, false, nil } } @@ -427,6 +401,8 @@ func (r *appPortProfileResource) read(ctx context.Context, planOrState *AppPortP stateRefreshed.Name.Set(appPortProfile.NsxtAppPortProfile.Name) stateRefreshed.Description.Set(appPortProfile.NsxtAppPortProfile.Description) stateRefreshed.AppPorts.Set(ctx, appPorts) + stateRefreshed.EdgeGatewayID.Set(r.edgegw.GetID()) + stateRefreshed.EdgeGatewayName.Set(r.edgegw.GetName()) return stateRefreshed, true, nil } diff --git a/internal/provider/edgegw/app_port_profile_schema.go b/internal/provider/edgegw/app_port_profile_schema.go index eedd2e18..f6326d05 100644 --- a/internal/provider/edgegw/app_port_profile_schema.go +++ b/internal/provider/edgegw/app_port_profile_schema.go @@ -78,26 +78,30 @@ func appPortProfilesSchema(_ context.Context) superschema.Schema { }, }, "edge_gateway_id": superschema.SuperStringAttribute{ - Resource: &schemaR.StringAttribute{ + Common: &schemaR.StringAttribute{ MarkdownDescription: "ID of the Edge Gateway.", Optional: true, Computed: true, Validators: []validator.String{ stringvalidator.ExactlyOneOf(path.MatchRoot("edge_gateway_id"), path.MatchRoot("edge_gateway_name")), }, + }, + Resource: &schemaR.StringAttribute{ PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIfConfigured(), }, }, }, "edge_gateway_name": superschema.SuperStringAttribute{ - Resource: &schemaR.StringAttribute{ + Common: &schemaR.StringAttribute{ MarkdownDescription: "Name of the Edge Gateway.", Optional: true, Computed: true, Validators: []validator.String{ stringvalidator.ExactlyOneOf(path.MatchRoot("edge_gateway_id"), path.MatchRoot("edge_gateway_name")), }, + }, + Resource: &schemaR.StringAttribute{ PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplaceIfConfigured(), }, diff --git a/internal/provider/edgegw/app_port_profile_type.go b/internal/provider/edgegw/app_port_profile_type.go index c3c74cf9..dfdc9a32 100644 --- a/internal/provider/edgegw/app_port_profile_type.go +++ b/internal/provider/edgegw/app_port_profile_type.go @@ -21,12 +21,12 @@ type AppPortProfileModel struct { AppPorts supertypes.ListNestedObjectValueOf[AppPortProfileModelAppPort] `tfsdk:"app_ports"` } -type AppPortProfileModelADatasource struct { - ID supertypes.StringValue `tfsdk:"id"` - Name supertypes.StringValue `tfsdk:"name"` - Description supertypes.StringValue `tfsdk:"description"` - AppPorts supertypes.ListNestedObjectValueOf[AppPortProfileModelAppPort] `tfsdk:"app_ports"` -} +// type AppPortProfileModelADatasource struct { +// ID supertypes.StringValue `tfsdk:"id"` +// Name supertypes.StringValue `tfsdk:"name"` +// Description supertypes.StringValue `tfsdk:"description"` +// AppPorts supertypes.ListNestedObjectValueOf[AppPortProfileModelAppPort] `tfsdk:"app_ports"` +// } type AppPortProfileModelAppPort struct { Protocol supertypes.StringValue `tfsdk:"protocol"` diff --git a/internal/testsacc/edgegw_app_port_profile_datasource_test.go b/internal/testsacc/edgegw_app_port_profile_datasource_test.go index 8915a88d..2046b877 100644 --- a/internal/testsacc/edgegw_app_port_profile_datasource_test.go +++ b/internal/testsacc/edgegw_app_port_profile_datasource_test.go @@ -44,11 +44,13 @@ func (r *EdgeGatewayAppPortProfileDatasource) Tests(ctx context.Context) map[tes Create: testsacc.TFConfig{ TFConfig: ` data "cloudavenue_edgegateway_app_port_profile" "example" { + edge_gateway_name = cloudavenue_edgegateway_app_port_profile.example.edge_gateway_id name = cloudavenue_edgegateway_app_port_profile.example.name }`, // Here use resource config test to test the data source Checks: GetResourceConfig()[EdgeGatewayAppPortProfileResourceName]().GetDefaultChecks(), }, + Destroy: true, } }, "example_by_id": func(_ context.Context, _ string) testsacc.Test { @@ -61,19 +63,26 @@ func (r *EdgeGatewayAppPortProfileDatasource) Tests(ctx context.Context) map[tes Create: testsacc.TFConfig{ TFConfig: ` data "cloudavenue_edgegateway_app_port_profile" "example_by_id" { + edge_gateway_id = cloudavenue_edgegateway_app_port_profile.example.edge_gateway_id id = cloudavenue_edgegateway_app_port_profile.example.id }`, // Here use resource config test to test the data source Checks: GetResourceConfig()[EdgeGatewayAppPortProfileResourceName]().GetDefaultChecks(), }, + Destroy: true, } }, "example_provider_scope": func(_ context.Context, resourceName string) testsacc.Test { return testsacc.Test{ // ! Create testing + CommonDependencies: func() (resp testsacc.DependenciesConfigResponse) { + resp.Append(GetResourceConfig()[EdgeGatewayResourceName]().GetDefaultConfig) + return + }, Create: testsacc.TFConfig{ TFConfig: ` data "cloudavenue_edgegateway_app_port_profile" "example_provider_scope" { + edge_gateway_id = cloudavenue_edgegateway.example.id name = "BKP_TCP_bpcd" }`, Checks: []resource.TestCheckFunc{ @@ -83,14 +92,20 @@ func (r *EdgeGatewayAppPortProfileDatasource) Tests(ctx context.Context) map[tes resource.TestCheckResourceAttr(resourceName, "app_ports.0.ports.0", "13782"), }, }, + Destroy: true, } }, "example_system_scope": func(_ context.Context, resourceName string) testsacc.Test { return testsacc.Test{ // ! Create testing + CommonDependencies: func() (resp testsacc.DependenciesConfigResponse) { + resp.Append(GetResourceConfig()[EdgeGatewayResourceName]().GetDefaultConfig) + return + }, Create: testsacc.TFConfig{ TFConfig: ` data "cloudavenue_edgegateway_app_port_profile" "example_system_scope" { + edge_gateway_id = cloudavenue_edgegateway.example.id name = "HTTP" }`, Checks: []resource.TestCheckFunc{ @@ -101,6 +116,37 @@ func (r *EdgeGatewayAppPortProfileDatasource) Tests(ctx context.Context) map[tes resource.TestCheckResourceAttr(resourceName, "app_ports.0.ports.0", "80"), }, }, + Destroy: true, + } + }, + "example_with_vdc_group": func(_ context.Context, resourceName string) testsacc.Test { + return testsacc.Test{ + CommonDependencies: func() (resp testsacc.DependenciesConfigResponse) { + resp.Append(GetResourceConfig()[EdgeGatewayResourceName]().GetSpecificConfig("example_with_vdc_group")) + return + }, + Create: testsacc.TFConfig{ + TFConfig: ` + data "cloudavenue_edgegateway_app_port_profile" "example_with_vdc_group" { + edge_gateway_id = cloudavenue_edgegateway.example_with_vdc_group.id + name = "Heartbeat" + }`, + Checks: []resource.TestCheckFunc{ + resource.TestCheckResourceAttrWith(resourceName, "id", uuid.TestIsType(uuid.AppPortProfile)), + resource.TestCheckResourceAttr(resourceName, "name", "Heartbeat"), + resource.TestCheckResourceAttr(resourceName, "description", "Heartbeat"), + resource.TestCheckResourceAttr(resourceName, "app_ports.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "app_ports.*", map[string]string{ + "protocol": "TCP", + "ports.0": "57348", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "app_ports.*", map[string]string{ + "protocol": "TCP", + "ports.0": "52267", + }), + }, + }, + Destroy: true, } }, }