diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go index 92c80779..b84c1b5a 100644 --- a/cloudstack/service_offering_constrained_resource.go +++ b/cloudstack/service_offering_constrained_resource.go @@ -25,6 +25,7 @@ import ( "strconv" "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" @@ -111,6 +112,7 @@ func (r *serviceOfferingConstrainedResource) Create(ctx context.Context, req res // common params params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) plan.commonCreateParams(ctx, params) + plan.applyTags(params) planDiskQosHypervisor.commonCreateParams(ctx, params) planDiskOffering.commonCreateParams(ctx, params) planDiskQosStorage.commonCreateParams(ctx, params) @@ -287,6 +289,10 @@ func (r *serviceOfferingConstrainedResource) Delete(ctx context.Context, req res } } +func (r *serviceOfferingConstrainedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + func (r *serviceOfferingConstrainedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { // Add a nil check when handling ProviderData because Terraform // sets that data after it calls the ConfigureProvider RPC. diff --git a/cloudstack/service_offering_constrained_resource_test.go b/cloudstack/service_offering_constrained_resource_test.go index 5368db7b..190fe834 100644 --- a/cloudstack/service_offering_constrained_resource_test.go +++ b/cloudstack/service_offering_constrained_resource_test.go @@ -36,6 +36,11 @@ func TestAccServiceOfferingConstrained(t *testing.T) { resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained1", "name", "constrained1"), ), }, + { + ResourceName: "cloudstack_service_offering_constrained.constrained1", + ImportState: true, + ImportStateVerify: true, + }, { Config: testAccServiceOfferingCustomConstrained1ZoneAll, Check: resource.ComposeTestCheckFunc( diff --git a/cloudstack/service_offering_fixed_resource.go b/cloudstack/service_offering_fixed_resource.go index 6b500fd2..c8b001ae 100644 --- a/cloudstack/service_offering_fixed_resource.go +++ b/cloudstack/service_offering_fixed_resource.go @@ -24,6 +24,7 @@ import ( "fmt" "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" @@ -96,6 +97,7 @@ func (r *serviceOfferingFixedResource) Create(ctx context.Context, req resource. // cloudstack params params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) plan.commonCreateParams(ctx, params) + plan.applyTags(params) planDiskQosHypervisor.commonCreateParams(ctx, params) planDiskOffering.commonCreateParams(ctx, params) planDiskQosStorage.commonCreateParams(ctx, params) @@ -227,6 +229,10 @@ func (r *serviceOfferingFixedResource) Delete(ctx context.Context, req resource. } } +func (r *serviceOfferingFixedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + func (r *serviceOfferingFixedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { // Add a nil check when handling ProviderData because Terraform // sets that data after it calls the ConfigureProvider RPC. diff --git a/cloudstack/service_offering_fixed_resource_test.go b/cloudstack/service_offering_fixed_resource_test.go index 438740e0..f5d4a30e 100644 --- a/cloudstack/service_offering_fixed_resource_test.go +++ b/cloudstack/service_offering_fixed_resource_test.go @@ -36,6 +36,11 @@ func TestAccServiceOfferingFixed(t *testing.T) { resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.fixed1", "name", "fixed1"), ), }, + { + ResourceName: "cloudstack_service_offering_fixed.fixed1", + ImportState: true, + ImportStateVerify: true, + }, { Config: testAccServiceOfferingFixed2, Check: resource.ComposeTestCheckFunc( diff --git a/cloudstack/service_offering_models.go b/cloudstack/service_offering_models.go index a93ffa48..d142b104 100644 --- a/cloudstack/service_offering_models.go +++ b/cloudstack/service_offering_models.go @@ -54,6 +54,7 @@ type serviceOfferingCommonResourceModel struct { Name types.String `tfsdk:"name"` NetworkRate types.Int32 `tfsdk:"network_rate"` OfferHa types.Bool `tfsdk:"offer_ha"` + Tags types.String `tfsdk:"tags"` ZoneIds types.Set `tfsdk:"zone_ids"` ServiceOfferingDiskQosHypervisor types.Object `tfsdk:"disk_hypervisor"` ServiceOfferingDiskOffering types.Object `tfsdk:"disk_offering"` diff --git a/cloudstack/service_offering_schema.go b/cloudstack/service_offering_schema.go index 9586d513..dc9fdfe0 100644 --- a/cloudstack/service_offering_schema.go +++ b/cloudstack/service_offering_schema.go @@ -108,6 +108,10 @@ func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string }, Default: booldefault.StaticBool(false), }, + "tags": schema.StringAttribute{ + Description: "The tags for the service offering", + Optional: true, + }, "zone_ids": schema.SetAttribute{ Description: "The ID of the zone(s)", Optional: true, diff --git a/cloudstack/service_offering_unconstrained_resource.go b/cloudstack/service_offering_unconstrained_resource.go index 98b937cd..bbb89abd 100644 --- a/cloudstack/service_offering_unconstrained_resource.go +++ b/cloudstack/service_offering_unconstrained_resource.go @@ -24,6 +24,7 @@ import ( "fmt" "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/types" @@ -73,6 +74,7 @@ func (r *serviceOfferingUnconstrainedResource) Create(ctx context.Context, req r // cloudstack params params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) plan.commonCreateParams(ctx, params) + plan.applyTags(params) planDiskQosHypervisor.commonCreateParams(ctx, params) planDiskOffering.commonCreateParams(ctx, params) planDiskQosStorage.commonCreateParams(ctx, params) @@ -181,6 +183,10 @@ func (r *serviceOfferingUnconstrainedResource) Delete(ctx context.Context, req r } } +func (r *serviceOfferingUnconstrainedResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp) +} + func (r *serviceOfferingUnconstrainedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { if req.ProviderData == nil { return diff --git a/cloudstack/service_offering_unconstrained_resource_test.go b/cloudstack/service_offering_unconstrained_resource_test.go index 5aba779f..79d83cf2 100644 --- a/cloudstack/service_offering_unconstrained_resource_test.go +++ b/cloudstack/service_offering_unconstrained_resource_test.go @@ -36,6 +36,11 @@ func TestAccServiceOfferingUnconstrained(t *testing.T) { resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.unconstrained1", "name", "unconstrained1"), ), }, + { + ResourceName: "cloudstack_service_offering_unconstrained.unconstrained1", + ImportState: true, + ImportStateVerify: true, + }, { Config: testAccServiceOfferingUnconstrained2, Check: resource.ComposeTestCheckFunc( diff --git a/cloudstack/service_offering_util.go b/cloudstack/service_offering_util.go index 666e0fce..66991b61 100644 --- a/cloudstack/service_offering_util.go +++ b/cloudstack/service_offering_util.go @@ -41,9 +41,11 @@ func (state *serviceOfferingCommonResourceModel) commonUpdate(ctx context.Contex if cs.Name != "" { state.Name = types.StringValue(cs.Name) } - if cs.Zoneid != "" { - state.ZoneIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) + if cs.Zoneid != "" && cs.Zoneid != "all" { + z, _ := types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) + state.ZoneIds = z } + // else: preserve prior state value (null or []) — both mean "all zones" } func (plan *serviceOfferingCommonResourceModel) commonUpdateParams(ctx context.Context, p *cloudstack.UpdateServiceOfferingParams) *cloudstack.UpdateServiceOfferingParams { @@ -51,7 +53,9 @@ func (plan *serviceOfferingCommonResourceModel) commonUpdateParams(ctx context.C p.SetDisplaytext(plan.DisplayText.ValueString()) } if !plan.DomainIds.IsNull() { - p.SetDomainid(plan.DomainIds.String()) + domainIDs := make([]string, len(plan.DomainIds.Elements())) + plan.DomainIds.ElementsAs(ctx, &domainIDs, false) + p.SetDomainid(strings.Join(domainIDs, ",")) } if !plan.HostTags.IsNull() { p.SetHosttags(plan.HostTags.ValueString()) @@ -59,8 +63,11 @@ func (plan *serviceOfferingCommonResourceModel) commonUpdateParams(ctx context.C if !plan.Name.IsNull() { p.SetName(plan.Name.ValueString()) } - if !plan.ZoneIds.IsNull() && len(plan.ZoneIds.Elements()) > 0 { - p.SetZoneid(plan.ZoneIds.String()) + zoneIDs := plan.ZoneIds + if !zoneIDs.IsNull() && len(zoneIDs.Elements()) > 0 { + zoneIDSlice := make([]string, len(zoneIDs.Elements())) + zoneIDs.ElementsAs(ctx, &zoneIDSlice, false) + p.SetZoneid(strings.Join(zoneIDSlice, ",")) } else { p.SetZoneid("all") } @@ -99,9 +106,11 @@ func (state *serviceOfferingCommonResourceModel) commonRead(ctx context.Context, if cs.Networkrate > 0 { state.NetworkRate = types.Int32Value(int32(cs.Networkrate)) } - if cs.Zoneid != "" { - state.ZoneIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) + if cs.Zoneid != "" && cs.Zoneid != "all" { + z, _ := types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) + state.ZoneIds = z } + // else: preserve prior state value (null or []) — both mean "all zones" state.DynamicScalingEnabled = types.BoolValue(cs.Dynamicscalingenabled) state.IsVolatile = types.BoolValue(cs.Isvolatile) @@ -205,9 +214,10 @@ func (plan *serviceOfferingCommonResourceModel) commonCreateParams(ctx context.C if !plan.OfferHa.IsNull() { p.SetOfferha(plan.OfferHa.ValueBool()) } - if !plan.ZoneIds.IsNull() { - zoneIds := make([]string, len(plan.ZoneIds.Elements())) - plan.ZoneIds.ElementsAs(ctx, &zoneIds, false) + zoneIDs := plan.ZoneIds + if !zoneIDs.IsNull() { + zoneIds := make([]string, len(zoneIDs.Elements())) + zoneIDs.ElementsAs(ctx, &zoneIds, false) p.SetZoneid(zoneIds) } @@ -262,6 +272,12 @@ func (plan *ServiceOfferingDiskOffering) commonCreateParams(ctx context.Context, } +func (plan *serviceOfferingCommonResourceModel) applyTags(p *cloudstack.CreateServiceOfferingParams) { + if !plan.Tags.IsNull() { + p.SetTags(plan.Tags.ValueString()) + } +} + func (plan *ServiceOfferingDiskQosStorage) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { if !plan.CustomizedIops.IsNull() { p.SetCustomizediops(plan.CustomizedIops.ValueBool())