From 4a18d914846f49490f6dae7748a41fd5d3e80d8b Mon Sep 17 00:00:00 2001 From: "Poddo, Michael (mp940m)" <8801231+poddm@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:54:48 -0700 Subject: [PATCH 1/8] sdk framework rewrite --- cloudstack/provider_v6.go | 6 +- .../service_offering_constrained_resource.go | 265 +++++++++++++++ ...vice_offering_constrained_resource_test.go | 313 ++++++++++++++++++ cloudstack/service_offering_fixed_resource.go | 242 ++++++++++++++ .../service_offering_fixed_resource_test.go | 239 +++++++++++++ cloudstack/service_offering_models.go | 70 ++++ cloudstack/service_offering_schema.go | 196 +++++++++++ ...service_offering_unconstrained_resource.go | 202 +++++++++++ ...ce_offering_unconstrained_resource_test.go | 207 ++++++++++++ cloudstack/service_offering_util.go | 267 +++++++++++++++ go.mod | 33 +- go.sum | 60 ++-- 12 files changed, 2053 insertions(+), 47 deletions(-) create mode 100644 cloudstack/service_offering_constrained_resource.go create mode 100644 cloudstack/service_offering_constrained_resource_test.go create mode 100644 cloudstack/service_offering_fixed_resource.go create mode 100644 cloudstack/service_offering_fixed_resource_test.go create mode 100644 cloudstack/service_offering_models.go create mode 100644 cloudstack/service_offering_schema.go create mode 100644 cloudstack/service_offering_unconstrained_resource.go create mode 100644 cloudstack/service_offering_unconstrained_resource_test.go create mode 100644 cloudstack/service_offering_util.go diff --git a/cloudstack/provider_v6.go b/cloudstack/provider_v6.go index 339e4b37..e2d73e80 100644 --- a/cloudstack/provider_v6.go +++ b/cloudstack/provider_v6.go @@ -147,7 +147,11 @@ func (p *CloudstackProvider) ConfigValidators(ctx context.Context) []provider.Co } func (p *CloudstackProvider) Resources(ctx context.Context) []func() resource.Resource { - return []func() resource.Resource{} + return []func() resource.Resource{ + NewserviceOfferingUnconstrainedResource, + NewserviceOfferingConstrainedResource, + NewserviceOfferingFixedResource, + } } func (p *CloudstackProvider) DataSources(ctx context.Context) []func() datasource.DataSource { diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go new file mode 100644 index 00000000..52ac2ee7 --- /dev/null +++ b/cloudstack/service_offering_constrained_resource.go @@ -0,0 +1,265 @@ +package cloudstack + +import ( + "context" + "fmt" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var ( + _ resource.Resource = &serviceOfferingConstrainedResource{} + _ resource.ResourceWithConfigure = &serviceOfferingConstrainedResource{} +) + +func NewserviceOfferingConstrainedResource() resource.Resource { + return &serviceOfferingConstrainedResource{} +} + +type serviceOfferingConstrainedResource struct { + client *cloudstack.CloudStackClient +} + +func (r *serviceOfferingConstrainedResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{ + "cpu_speed": schema.Int32Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "max_cpu_number": schema.Int32Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "max_memory": schema.Int32Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "min_cpu_number": schema.Int32Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "min_memory": schema.Int32Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + }), + } +} + +func (r *serviceOfferingConstrainedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // + var plan serviceOfferingConstrainedResourceModel + var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var planDiskOffering ServiceOfferingDiskOffering + var planDiskQosStorage ServiceOfferingDiskQosStorage + + // + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskOffering.As(ctx, &planDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx, &planDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // common params + params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) + plan.commonCreateParams(params) + planDiskQosHypervisor.commonCreateParams(params) + planDiskOffering.commonCreateParams(params) + planDiskQosStorage.commonCreateParams(params) + + // resource specific params + if !plan.CpuSpeed.IsNull() { + params.SetCpuspeed(int(plan.CpuSpeed.ValueInt32())) + } + if !plan.MaxCpuNumber.IsNull() { + params.SetMaxcpunumber(int(plan.MaxCpuNumber.ValueInt32())) + } + if !plan.MaxMemory.IsNull() { + params.SetMaxmemory(int(plan.MaxMemory.ValueInt32())) + } + if !plan.MinCpuNumber.IsNull() { + params.SetMincpunumber(int(plan.MinCpuNumber.ValueInt32())) + } + if !plan.MinMemory.IsNull() { + params.SetMinmemory(int(plan.MinMemory.ValueInt32())) + } + + // create offering + cs, err := r.client.ServiceOffering.CreateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error creating service offering", + "Could not create constrained offering, unexpected error: "+err.Error(), + ) + return + } + + // + plan.Id = types.StringValue(cs.Id) + + // + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *serviceOfferingConstrainedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // + var state serviceOfferingConstrainedResourceModel + var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var stateDiskOffering ServiceOfferingDiskOffering + var stateDiskQosStorage ServiceOfferingDiskQosStorage + + // + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if !state.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskOffering.As(ctx, &stateDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosStorage.As(ctx, &stateDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // + cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering, unexpected error: "+err.Error(), + ) + return + } + + // resource specific + if cs.Cpuspeed > 0 { + state.CpuSpeed = types.Int32Value(int32(cs.Cpuspeed)) + } + // These fields arent returned from list + // max_cpu_number + // max_memory + // min_cpu_number + // min_memory + + // + state.commonRead(ctx, cs) + stateDiskQosHypervisor.commonRead(ctx, cs) + stateDiskOffering.commonRead(ctx, cs) + stateDiskQosStorage.commonRead(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + + // + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *serviceOfferingConstrainedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // + var state serviceOfferingConstrainedResourceModel + + // + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // + params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) + state.commonUpdateParams(ctx, params) + + // + cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error updating service offering", + "Could not update constrained service offering, unexpected error: "+err.Error(), + ) + return + } + + // + state.commonUpdate(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +// Delete deletes the resource and removes the Terraform state on success. +func (r *serviceOfferingConstrainedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // + var state serviceOfferingConstrainedResourceModel + + // + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete the service offering + _, err := r.client.ServiceOffering.DeleteServiceOffering(r.client.ServiceOffering.NewDeleteServiceOfferingParams(state.Id.ValueString())) + if err != nil { + resp.Diagnostics.AddError( + "Error deleting service offering", + "Could not delete constrained offering, unexpected error: "+err.Error(), + ) + return + } +} + +// Configure adds the provider configured client to the resource. +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. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*cloudstack.CloudStackClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *cloudstack.CloudStackClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + r.client = client +} + +// Metadata returns the resource type name. +func (r *serviceOfferingConstrainedResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_service_offering_constrained" +} diff --git a/cloudstack/service_offering_constrained_resource_test.go b/cloudstack/service_offering_constrained_resource_test.go new file mode 100644 index 00000000..247c1560 --- /dev/null +++ b/cloudstack/service_offering_constrained_resource_test.go @@ -0,0 +1,313 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccServiceOfferingConstrained(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccMuxProvider, + Steps: []resource.TestStep{ + { + Config: testAccServiceOfferingCustomConstrained1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained1", "name", "constrained1"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained1ZoneAll, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained1", "name", "constrained1"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained2, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained2", "name", "constrained2"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained2_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained2", "name", "constrained2update"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained_disk, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.constrained1", "name", "constrained1"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained_disk_hypervisor, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.disk_hypervisor", "name", "disk_hypervisor"), + ), + }, + { + Config: testAccServiceOfferingCustomConstrained_disk_storage, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_constrained.disk_storage", "name", "disk_storage"), + ), + }, + }, + }) +} + +const testAccServiceOfferingCustomConstrained1 = ` +resource "cloudstack_service_offering_constrained" "constrained1" { + display_text = "constrained1" + name = "constrained1" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + storage_tags = "foo" + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + //Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + zone_ids = [] + +} +` + +const testAccServiceOfferingCustomConstrained1ZoneAll = ` +resource "cloudstack_service_offering_constrained" "constrained1" { + display_text = "constrained11" + name = "constrained1" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + storage_tags = "foo1" + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + zone_ids = [] +} +` + +const testAccServiceOfferingCustomConstrained2 = ` +resource "cloudstack_service_offering_constrained" "constrained2" { + display_text = "constrained2" + name = "constrained2" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` +const testAccServiceOfferingCustomConstrained2_update = ` +resource "cloudstack_service_offering_constrained" "constrained2" { + display_text = "constrained2update" + name = "constrained2update" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingCustomConstrained_disk = ` +resource "cloudstack_service_offering_constrained" "constrained1" { + display_text = "constrained1" + name = "constrained1" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } +} +` + +const testAccServiceOfferingCustomConstrained_disk_hypervisor = ` +resource "cloudstack_service_offering_constrained" "disk_hypervisor" { + display_text = "disk_hypervisor" + name = "disk_hypervisor" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } + disk_hypervisor = { + bytes_read_rate = 1024 + bytes_read_rate_max = 1024 + bytes_read_rate_max_length = 1024 + bytes_write_rate = 1024 + bytes_write_rate_max = 1024 + bytes_write_rate_max_length = 1024 + } +} +` + +const testAccServiceOfferingCustomConstrained_disk_storage = ` +resource "cloudstack_service_offering_constrained" "disk_storage" { + display_text = "disk_storage" + name = "disk_storage" + + // compute + cpu_speed = 2500 + max_cpu_number = 10 + min_cpu_number = 2 + + // memory + max_memory = 4096 + min_memory = 1024 + + // other + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + // Feature flags + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } + disk_hypervisor = { + bytes_read_rate = 1024 + bytes_read_rate_max = 1024 + bytes_read_rate_max_length = 1024 + bytes_write_rate = 1024 + bytes_write_rate_max = 1024 + bytes_write_rate_max_length = 1024 + } +} +` diff --git a/cloudstack/service_offering_fixed_resource.go b/cloudstack/service_offering_fixed_resource.go new file mode 100644 index 00000000..c512c329 --- /dev/null +++ b/cloudstack/service_offering_fixed_resource.go @@ -0,0 +1,242 @@ +package cloudstack + +import ( + "context" + "fmt" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var ( + _ resource.Resource = &serviceOfferingFixedResource{} + _ resource.ResourceWithConfigure = &serviceOfferingFixedResource{} +) + +func NewserviceOfferingFixedResource() resource.Resource { + return &serviceOfferingFixedResource{} +} + +type serviceOfferingFixedResource struct { + client *cloudstack.CloudStackClient +} + +func (r *serviceOfferingFixedResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{ + "cpu_number": schema.Int32Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "cpu_speed": schema.Int32Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "memory": schema.Int32Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + }), + } +} + +func (r *serviceOfferingFixedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // + var plan serviceOfferingFixedResourceModel + var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var planDiskOffering ServiceOfferingDiskOffering + var planDiskQosStorage ServiceOfferingDiskQosStorage + + // + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskOffering.As(ctx, &planDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx, &planDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // cloudstack params + params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) + plan.commonCreateParams(params) + + // resource specific params + if !plan.CpuNumber.IsNull() { + params.SetCpunumber(int(plan.CpuNumber.ValueInt32())) + } + if !plan.CpuSpeed.IsNull() { + params.SetCpuspeed(int(plan.CpuSpeed.ValueInt32())) + } + if !plan.Memory.IsNull() { + params.SetMemory(int(plan.Memory.ValueInt32())) + } + + // create offering + cs, err := r.client.ServiceOffering.CreateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error creating service offering", + "Could not create fixed offering, unexpected error: "+err.Error(), + ) + return + } + + // + plan.Id = types.StringValue(cs.Id) + + // + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *serviceOfferingFixedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // + var state serviceOfferingFixedResourceModel + var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var stateDiskOffering ServiceOfferingDiskOffering + var stateDiskQosStorage ServiceOfferingDiskQosStorage + + // + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if !state.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskOffering.As(ctx, &stateDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosStorage.As(ctx, &stateDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // + cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read fixed service offering, unexpected error: "+err.Error(), + ) + return + } + + // resource specific + if cs.Cpunumber > 0 { + state.CpuNumber = types.Int32Value(int32(cs.Cpunumber)) + } + if cs.Cpuspeed > 0 { + state.CpuSpeed = types.Int32Value(int32(cs.Cpuspeed)) + } + if cs.Memory > 0 { + state.Memory = types.Int32Value(int32(cs.Memory)) + } + + // + state.commonRead(ctx, cs) + stateDiskQosHypervisor.commonRead(ctx, cs) + stateDiskOffering.commonRead(ctx, cs) + stateDiskQosStorage.commonRead(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + + // + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + +} + +// Update updates the resource and sets the updated Terraform state on success. +func (r *serviceOfferingFixedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // + var state serviceOfferingFixedResourceModel + // + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // + params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) + state.commonUpdateParams(ctx, params) + + // + cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error updating fixed service offering", + "Could not update fixed service offering, unexpected error: "+err.Error(), + ) + return + } + + // + state.commonUpdate(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) +} + +// Delete deletes the resource and removes the Terraform state on success. +func (r *serviceOfferingFixedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // + var state serviceOfferingFixedResourceModel + + // + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete the service offering + _, err := r.client.ServiceOffering.DeleteServiceOffering(r.client.ServiceOffering.NewDeleteServiceOfferingParams(state.Id.ValueString())) + if err != nil { + resp.Diagnostics.AddError( + "Error deleting service offering", + "Could not delete fixed offering, unexpected error: "+err.Error(), + ) + return + } +} + +// Configure adds the provider configured client to the resource. +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. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*cloudstack.CloudStackClient) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *cloudstack.CloudStackClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + r.client = client +} + +// Metadata returns the resource type name. +func (r *serviceOfferingFixedResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_service_offering_fixed" +} diff --git a/cloudstack/service_offering_fixed_resource_test.go b/cloudstack/service_offering_fixed_resource_test.go new file mode 100644 index 00000000..56d23a5e --- /dev/null +++ b/cloudstack/service_offering_fixed_resource_test.go @@ -0,0 +1,239 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package cloudstack + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccServiceOfferingFixed(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccMuxProvider, + Steps: []resource.TestStep{ + { + Config: testAccServiceOfferingFixed1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.fixed1", "name", "fixed1"), + ), + }, + { + Config: testAccServiceOfferingFixed2, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.fixed2", "name", "fixed2"), + ), + }, + { + Config: testAccServiceOfferingFixed2_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.fixed2", "name", "fixed2update"), + ), + }, + { + Config: testAccServiceOfferingFixed_disk, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.disk", "name", "disk"), + ), + }, + { + Config: testAccServiceOfferingFixed_disk_hypervisor, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.disk_hypervisor", "name", "disk_hypervisor"), + ), + }, + { + Config: testAccServiceOfferingFixed_disk_storage, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_fixed.disk_storage", "name", "disk_storage"), + ), + }, + }, + }) +} + +const testAccServiceOfferingFixed1 = ` +resource "cloudstack_service_offering_fixed" "fixed1" { + display_text = "fixed1" + name = "fixed1" + + // compute + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + // other + host_tags = "test0101, test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + +} +` + +const testAccServiceOfferingFixed2 = ` +resource "cloudstack_service_offering_fixed" "fixed2" { + display_text = "fixed2" + name = "fixed2" + + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingFixed2_update = ` +resource "cloudstack_service_offering_fixed" "fixed2" { + display_text = "fixed2update" + name = "fixed2update" + + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingFixed_disk = ` +resource "cloudstack_service_offering_fixed" "disk" { + display_text = "disk" + name = "disk" + + // compute + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + // other + host_tags = "test0101, test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } +} +` + +const testAccServiceOfferingFixed_disk_hypervisor = ` +resource "cloudstack_service_offering_fixed" "disk_hypervisor" { + display_text = "disk_hypervisor" + name = "disk_hypervisor" + + // compute + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + // other + host_tags = "test0101, test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } + disk_hypervisor = { + bytes_read_rate = 1024 + bytes_read_rate_max = 1024 + bytes_read_rate_max_length = 1024 + bytes_write_rate = 1024 + bytes_write_rate_max = 1024 + bytes_write_rate_max_length = 1024 + } +} +` + +const testAccServiceOfferingFixed_disk_storage = ` +resource "cloudstack_service_offering_fixed" "disk_storage" { + display_text = "disk_storage" + name = "disk_storage" + + // compute + cpu_number = 2 + cpu_speed = 2500 + memory = 2048 + + // other + host_tags = "test0101, test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false + + disk_offering = { + storage_type = "local" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } + disk_storage = { + min_iops = 100 + max_iops = 100 + } +} +` diff --git a/cloudstack/service_offering_models.go b/cloudstack/service_offering_models.go new file mode 100644 index 00000000..84f08d39 --- /dev/null +++ b/cloudstack/service_offering_models.go @@ -0,0 +1,70 @@ +package cloudstack + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type serviceOfferingConstrainedResourceModel struct { + CpuSpeed types.Int32 `tfsdk:"cpu_speed"` + MaxCpuNumber types.Int32 `tfsdk:"max_cpu_number"` + MaxMemory types.Int32 `tfsdk:"max_memory"` + MinCpuNumber types.Int32 `tfsdk:"min_cpu_number"` + MinMemory types.Int32 `tfsdk:"min_memory"` + serviceOfferingCommonResourceModel +} + +// customized types.String `tfsdk:"Iscustomized"` + +type serviceOfferingUnconstrainedResourceModel struct { + serviceOfferingCommonResourceModel +} + +type serviceOfferingFixedResourceModel struct { + CpuNumber types.Int32 `tfsdk:"cpu_number"` + CpuSpeed types.Int32 `tfsdk:"cpu_speed"` + Memory types.Int32 `tfsdk:"memory"` + serviceOfferingCommonResourceModel +} + +type serviceOfferingCommonResourceModel struct { + DeploymentPlanner types.String `tfsdk:"deployment_planner"` + DiskOfferingId types.String `tfsdk:"disk_offering_id"` + DisplayText types.String `tfsdk:"display_text"` + DomainIds types.Set `tfsdk:"domain_ids"` + DynamicScalingEnabled types.Bool `tfsdk:"dynamic_scaling_enabled"` + HostTags types.String `tfsdk:"host_tags"` + Id types.String `tfsdk:"id"` + IsVolatile types.Bool `tfsdk:"is_volatile"` + LimitCpuUse types.Bool `tfsdk:"limit_cpu_use"` + Name types.String `tfsdk:"name"` + NetworkRate types.Int32 `tfsdk:"network_rate"` + OfferHa types.Bool `tfsdk:"offer_ha"` + StorageTags types.String `tfsdk:"storage_tags"` + ZoneIds types.Set `tfsdk:"zone_ids"` + // + ServiceOfferingDiskQosHypervisor types.Object `tfsdk:"disk_hypervisor"` + ServiceOfferingDiskOffering types.Object `tfsdk:"disk_offering"` + ServiceOfferingDiskQosStorage types.Object `tfsdk:"disk_storage"` +} + +type ServiceOfferingDiskQosHypervisor struct { + DiskBytesReadRate types.Int64 `tfsdk:"bytes_read_rate"` + DiskBytesReadRateMax types.Int64 `tfsdk:"bytes_read_rate_max"` + DiskBytesReadRateMaxLength types.Int64 `tfsdk:"bytes_read_rate_max_length"` + DiskBytesWriteRate types.Int64 `tfsdk:"bytes_write_rate"` + DiskBytesWriteRateMax types.Int64 `tfsdk:"bytes_write_rate_max"` + DiskBytesWriteRateMaxLength types.Int64 `tfsdk:"bytes_write_rate_max_length"` +} + +type ServiceOfferingDiskOffering struct { + CacheMode types.String `tfsdk:"cache_mode"` + DiskOfferingStrictness types.Bool `tfsdk:"disk_offering_strictness"` + ProvisionType types.String `tfsdk:"provisioning_type"` + RootDiskSize types.Int64 `tfsdk:"root_disk_size"` + StorageType types.String `tfsdk:"storage_type"` +} + +type ServiceOfferingDiskQosStorage struct { + CustomizedIops types.Bool `tfsdk:"customized_iops"` + HypervisorSnapshotReserve types.Int32 `tfsdk:"hypervisor_snapshot_reserve"` + MaxIops types.Int64 `tfsdk:"max_iops"` + MinIops types.Int64 `tfsdk:"min_iops"` +} diff --git a/cloudstack/service_offering_schema.go b/cloudstack/service_offering_schema.go new file mode 100644 index 00000000..3aeb82be --- /dev/null +++ b/cloudstack/service_offering_schema.go @@ -0,0 +1,196 @@ +package cloudstack + +import ( + "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/boolplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int32planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string]schema.Attribute { + common := map[string]schema.Attribute{ + "deployment_planner": schema.StringAttribute{ + Optional: true, + }, + "disk_offering_id": schema.StringAttribute{ + Optional: true, + }, + "display_text": schema.StringAttribute{ + Required: true, + }, + "domain_ids": schema.SetAttribute{ + Optional: true, + ElementType: types.StringType, + }, + "dynamic_scaling_enabled": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "host_tags": schema.StringAttribute{ + Optional: true, + }, + "id": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "is_volatile": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "limit_cpu_use": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "name": schema.StringAttribute{ + Required: true, + }, + "network_rate": schema.Int32Attribute{ + Optional: true, + }, + "offer_ha": schema.BoolAttribute{ + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "storage_tags": schema.StringAttribute{ + Optional: true, + }, + "zone_ids": schema.SetAttribute{ + Optional: true, + ElementType: types.StringType, + }, + "disk_hypervisor": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "bytes_read_rate": schema.Int64Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_read_rate_max": schema.Int64Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_read_rate_max_length": schema.Int64Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate": schema.Int64Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate_max": schema.Int64Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate_max_length": schema.Int64Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + }, + }, + "disk_offering": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "cache_mode": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "disk_offering_strictness": schema.BoolAttribute{ + Required: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + }, + "provisioning_type": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "root_disk_size": schema.Int64Attribute{ + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "storage_type": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + }, + }, + "disk_storage": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "customized_iops": schema.BoolAttribute{ + Optional: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + }, + "hypervisor_snapshot_reserve": schema.Int32Attribute{ + Optional: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "max_iops": schema.Int64Attribute{ + Optional: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "min_iops": schema.Int64Attribute{ + Optional: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + }, + }, + } + + for key, value := range s1 { + common[key] = value + } + + return common + +} diff --git a/cloudstack/service_offering_unconstrained_resource.go b/cloudstack/service_offering_unconstrained_resource.go new file mode 100644 index 00000000..0087b0f6 --- /dev/null +++ b/cloudstack/service_offering_unconstrained_resource.go @@ -0,0 +1,202 @@ +package cloudstack + +import ( + "context" + "fmt" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var ( + _ resource.Resource = &serviceOfferingUnconstrainedResource{} + _ resource.ResourceWithConfigure = &serviceOfferingUnconstrainedResource{} +) + +func NewserviceOfferingUnconstrainedResource() resource.Resource { + return &serviceOfferingUnconstrainedResource{} +} + +type serviceOfferingUnconstrainedResource struct { + client *cloudstack.CloudStackClient +} + +// Schema defines the schema for the resource. +func (r *serviceOfferingUnconstrainedResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{}), + } +} + +func (r *serviceOfferingUnconstrainedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + // + var plan serviceOfferingUnconstrainedResourceModel + var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var planDiskOffering ServiceOfferingDiskOffering + var planDiskQosStorage ServiceOfferingDiskQosStorage + + // + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskOffering.As(ctx, &planDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !plan.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(plan.ServiceOfferingDiskQosStorage.As(ctx, &planDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // cloudstack params + params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) + plan.commonCreateParams(params) + planDiskQosHypervisor.commonCreateParams(params) + planDiskOffering.commonCreateParams(params) + planDiskQosStorage.commonCreateParams(params) + + // create offering + cs, err := r.client.ServiceOffering.CreateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error creating service offering", + "Could not create unconstrained offering, unexpected error: "+err.Error(), + ) + return + } + + // + plan.Id = types.StringValue(cs.Id) + + // + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) + +} + +func (r *serviceOfferingUnconstrainedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + // + var state serviceOfferingUnconstrainedResourceModel + var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor + var stateDiskOffering ServiceOfferingDiskOffering + var stateDiskQosStorage ServiceOfferingDiskQosStorage + + // + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if !state.ServiceOfferingDiskQosHypervisor.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskOffering.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskOffering.As(ctx, &stateDiskOffering, basetypes.ObjectAsOptions{})...) + } + if !state.ServiceOfferingDiskQosStorage.IsNull() { + resp.Diagnostics.Append(state.ServiceOfferingDiskQosStorage.As(ctx, &stateDiskQosStorage, basetypes.ObjectAsOptions{})...) + } + if resp.Diagnostics.HasError() { + return + } + + // + cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Error creating service offering", + "Could not read unconstrained service offering, unexpected error: "+err.Error(), + ) + return + } + + // + state.commonRead(ctx, cs) + stateDiskQosHypervisor.commonRead(ctx, cs) + stateDiskOffering.commonRead(ctx, cs) + stateDiskQosStorage.commonRead(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + + // + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + +} + +func (r *serviceOfferingUnconstrainedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + // + var state serviceOfferingUnconstrainedResourceModel + + // + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + // + params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) + state.commonUpdateParams(ctx, params) + + // + cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) + if err != nil { + resp.Diagnostics.AddError( + "Error updating service offering", + "Could not update unconstrained service offering, unexpected error: "+err.Error(), + ) + return + } + + // + state.commonUpdate(ctx, cs) + if resp.Diagnostics.HasError() { + return + } + resp.Diagnostics.Append(resp.State.Set(ctx, state)...) + +} + +func (r *serviceOfferingUnconstrainedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + // + var state serviceOfferingUnconstrainedResourceModel + + // + diags := req.State.Get(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + // Delete the service offering + _, err := r.client.ServiceOffering.DeleteServiceOffering(r.client.ServiceOffering.NewDeleteServiceOfferingParams(state.Id.ValueString())) + if err != nil { + resp.Diagnostics.AddError( + "Error deleting service offering", + "Could not delete unconstrained offering, unexpected error: "+err.Error(), + ) + return + } +} + +func (r *serviceOfferingUnconstrainedResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*cloudstack.CloudStackClient) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *cloudstack.CloudStackClient, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + r.client = client +} + +func (r *serviceOfferingUnconstrainedResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_service_offering_unconstrained" +} diff --git a/cloudstack/service_offering_unconstrained_resource_test.go b/cloudstack/service_offering_unconstrained_resource_test.go new file mode 100644 index 00000000..82bafdda --- /dev/null +++ b/cloudstack/service_offering_unconstrained_resource_test.go @@ -0,0 +1,207 @@ +// // +// // Licensed to the Apache Software Foundation (ASF) under one +// // or more contributor license agreements. See the NOTICE file +// // distributed with this work for additional information +// // regarding copyright ownership. The ASF licenses this file +// // to you under the Apache License, Version 2.0 (the +// // "License"); you may not use this file except in compliance +// // with the License. You may obtain a copy of the License at +// // +// // http://www.apache.org/licenses/LICENSE-2.0 +// // +// // Unless required by applicable law or agreed to in writing, +// // software distributed under the License is distributed on an +// // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// // KIND, either express or implied. See the License for the +// // specific language governing permissions and limitations +// // under the License. +// // + +package cloudstack + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccServiceOfferingUnconstrained(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccMuxProvider, + Steps: []resource.TestStep{ + { + Config: testAccServiceOfferingUnconstrained1, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.unconstrained1", "name", "unconstrained1"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained2, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.unconstrained2", "name", "unconstrained2"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained2_update, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.unconstrained2", "name", "unconstrained2update"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained_disk, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.disk", "name", "disk"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained_disk_hypervisor, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.disk_hypervisor", "name", "disk_hypervisor"), + ), + }, + { + Config: testAccServiceOfferingUnconstrained_disk_storage, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("cloudstack_service_offering_unconstrained.disk_storage", "name", "disk_storage"), + ), + }, + }, + }) +} + +const testAccServiceOfferingUnconstrained1 = ` +resource "cloudstack_service_offering_unconstrained" "unconstrained1" { + display_text = "unconstrained1" + name = "unconstrained1" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = false + is_volatile = false + limit_cpu_use = false + offer_ha = false +} +` + +const testAccServiceOfferingUnconstrained2 = ` +resource "cloudstack_service_offering_unconstrained" "unconstrained2" { + display_text = "unconstrained2" + name = "unconstrained2" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingUnconstrained2_update = ` +resource "cloudstack_service_offering_unconstrained" "unconstrained2" { + display_text = "unconstrained2update" + name = "unconstrained2update" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true +} +` + +const testAccServiceOfferingUnconstrained_disk = ` +resource "cloudstack_service_offering_unconstrained" "disk" { + display_text = "disk" + name = "disk" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true + + disk_offering = { + storage_type = "shared" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } +} +` + +const testAccServiceOfferingUnconstrained_disk_hypervisor = ` +resource "cloudstack_service_offering_unconstrained" "disk_hypervisor" { + display_text = "disk_hypervisor" + name = "disk_hypervisor" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true + + disk_offering = { + storage_type = "shared" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } + disk_hypervisor = { + bytes_read_rate = 1024 + bytes_read_rate_max = 1024 + bytes_read_rate_max_length = 1024 + bytes_write_rate = 1024 + bytes_write_rate_max = 1024 + bytes_write_rate_max_length = 1024 + } + +} +` + +const testAccServiceOfferingUnconstrained_disk_storage = ` +resource "cloudstack_service_offering_unconstrained" "disk_storage" { + display_text = "disk_storage" + name = "disk_storage" + + host_tags = "test0101,test0202" + network_rate = 1024 + deployment_planner = "UserDispersingPlanner" + + dynamic_scaling_enabled = true + is_volatile = true + limit_cpu_use = true + offer_ha = true + + disk_offering = { + storage_type = "shared" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + tags = "FOO" + disk_offering_strictness = false + } + disk_storage = { + min_iops = 100 + max_iops = 100 + } +} +` diff --git a/cloudstack/service_offering_util.go b/cloudstack/service_offering_util.go new file mode 100644 index 00000000..9dd51e78 --- /dev/null +++ b/cloudstack/service_offering_util.go @@ -0,0 +1,267 @@ +package cloudstack + +import ( + "context" + "strings" + + "github.com/apache/cloudstack-go/v2/cloudstack" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +// ------------------------------------------------------------------------------------------------------------------------------ +// Common update methods +// - +func (state *serviceOfferingCommonResourceModel) commonUpdate(ctx context.Context, cs *cloudstack.UpdateServiceOfferingResponse) { + if cs.Displaytext != "" { + state.DisplayText = types.StringValue(cs.Displaytext) + } + if cs.Domainid != "" { + state.DomainIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Domainid, ",")) + } + if cs.Hosttags != "" { + state.HostTags = types.StringValue(cs.Hosttags) + } + if cs.Name != "" { + state.Name = types.StringValue(cs.Name) + } + if cs.Storagetags != "" { + state.StorageTags = types.StringValue(cs.Storagetags) + } + if cs.Zoneid != "" { + state.ZoneIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) + } +} + +func (plan *serviceOfferingCommonResourceModel) commonUpdateParams(ctx context.Context, p *cloudstack.UpdateServiceOfferingParams) *cloudstack.UpdateServiceOfferingParams { + if !plan.DisplayText.IsNull() { + p.SetDisplaytext(plan.DisplayText.ValueString()) + } + if !plan.DomainIds.IsNull() { + p.SetDomainid(plan.DomainIds.String()) + } + if !plan.HostTags.IsNull() { + p.SetHosttags(plan.HostTags.ValueString()) + } + if !plan.Name.IsNull() { + p.SetName(plan.Name.ValueString()) + } + if !plan.StorageTags.IsNull() { + p.SetStoragetags(plan.StorageTags.ValueString()) + } + if !plan.ZoneIds.IsNull() && len(plan.ZoneIds.Elements()) > 0 { + p.SetZoneid(plan.ZoneIds.String()) + } + + return p + +} + +// ------------------------------------------------------------------------------------------------------------------------------ +// common Read methods +// - +func (state *serviceOfferingCommonResourceModel) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { + state.Id = types.StringValue(cs.Id) + if cs.Deploymentplanner != "" { + state.DeploymentPlanner = types.StringValue(cs.Deploymentplanner) + } + if cs.Diskofferingid != "" { + state.DiskOfferingId = types.StringValue(cs.Diskofferingid) + } + if cs.Displaytext != "" { + state.DisplayText = types.StringValue(cs.Displaytext) + } + if cs.Domainid != "" { + state.DomainIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Domainid, ",")) + } + if cs.Hosttags != "" { + state.HostTags = types.StringValue(cs.Hosttags) + } + if cs.Name != "" { + state.Name = types.StringValue(cs.Name) + } + if cs.Networkrate > 0 { + state.NetworkRate = types.Int32Value(int32(cs.Networkrate)) + } + if cs.Storagetags != "" { + state.StorageTags = types.StringValue(cs.Storagetags) + } + if cs.Zoneid != "" { + state.ZoneIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) + } + + state.DynamicScalingEnabled = types.BoolValue(cs.Dynamicscalingenabled) + state.IsVolatile = types.BoolValue(cs.Isvolatile) + state.LimitCpuUse = types.BoolValue(cs.Limitcpuuse) + state.OfferHa = types.BoolValue(cs.Offerha) + +} + +func (state *ServiceOfferingDiskQosHypervisor) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { + if cs.DiskBytesReadRate > 0 { + state.DiskBytesReadRate = types.Int64Value(cs.DiskBytesReadRate) + } + if cs.DiskBytesReadRateMax > 0 { + state.DiskBytesReadRateMax = types.Int64Value(cs.DiskBytesReadRateMax) + } + if cs.DiskBytesReadRateMaxLength > 0 { + state.DiskBytesReadRateMaxLength = types.Int64Value(cs.DiskBytesReadRateMaxLength) + } + if cs.DiskBytesWriteRate > 0 { + state.DiskBytesWriteRate = types.Int64Value(cs.DiskBytesWriteRate) + } + if cs.DiskBytesWriteRateMax > 0 { + state.DiskBytesWriteRateMax = types.Int64Value(cs.DiskBytesWriteRateMax) + } + if cs.DiskBytesWriteRateMaxLength > 0 { + state.DiskBytesWriteRateMaxLength = types.Int64Value(cs.DiskBytesWriteRateMaxLength) + } + +} + +func (state *ServiceOfferingDiskOffering) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { + + if cs.CacheMode != "" { + state.CacheMode = types.StringValue(cs.CacheMode) + } + if cs.Diskofferingstrictness { + state.DiskOfferingStrictness = types.BoolValue(cs.Diskofferingstrictness) + } + if cs.Provisioningtype != "" { + state.ProvisionType = types.StringValue(cs.Provisioningtype) + } + if cs.Rootdisksize > 0 { + state.RootDiskSize = types.Int64Value(cs.Rootdisksize) + } + if cs.Storagetype != "" { + state.StorageType = types.StringValue(cs.Storagetype) + } +} + +func (state *ServiceOfferingDiskQosStorage) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { + if cs.Iscustomizediops { + state.CustomizedIops = types.BoolValue(cs.Iscustomizediops) + } + if cs.Hypervisorsnapshotreserve > 0 { + state.HypervisorSnapshotReserve = types.Int32Value(int32(cs.Hypervisorsnapshotreserve)) + } + if cs.Maxiops > 0 { + state.MaxIops = types.Int64Value(cs.Maxiops) + } + if cs.Miniops > 0 { + state.MinIops = types.Int64Value(cs.Miniops) + } + +} + +// ------------------------------------------------------------------------------------------------------------------------------ +// common Create methods +// - +func (plan *serviceOfferingCommonResourceModel) commonCreateParams(p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { + if !plan.DeploymentPlanner.IsNull() && !plan.DeploymentPlanner.IsUnknown() { + p.SetDeploymentplanner(plan.DeploymentPlanner.ValueString()) + } else { + plan.DeploymentPlanner = types.StringNull() + } + if !plan.DiskOfferingId.IsNull() { + p.SetDiskofferingid(plan.DiskOfferingId.ValueString()) + } + if !plan.DomainIds.IsNull() { + domainids := make([]string, len(plan.DomainIds.Elements())) + for i, v := range plan.DomainIds.Elements() { + domainids[i] = v.String() + } + p.SetDomainid(domainids) + } + if !plan.DynamicScalingEnabled.IsNull() { + p.SetDynamicscalingenabled(plan.DynamicScalingEnabled.ValueBool()) + } + if !plan.HostTags.IsNull() { + p.SetHosttags(plan.HostTags.ValueString()) + } + if !plan.IsVolatile.IsNull() { + p.SetIsvolatile(plan.IsVolatile.ValueBool()) + } + if !plan.LimitCpuUse.IsNull() { + p.SetLimitcpuuse(plan.LimitCpuUse.ValueBool()) + } + if !plan.NetworkRate.IsNull() { + p.SetNetworkrate(int(plan.NetworkRate.ValueInt32())) + } + if !plan.OfferHa.IsNull() { + p.SetOfferha(plan.OfferHa.ValueBool()) + } + if !plan.StorageTags.IsNull() { + p.SetTags(plan.StorageTags.ValueString()) + } + if !plan.ZoneIds.IsNull() { + zids := make([]string, len(plan.ZoneIds.Elements())) + for i, v := range plan.ZoneIds.Elements() { + zids[i] = v.String() + } + p.SetZoneid(zids) + } + + return p + +} +func (plan *ServiceOfferingDiskQosHypervisor) commonCreateParams(p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { + if !plan.DiskBytesReadRate.IsNull() { + p.SetBytesreadrate(plan.DiskBytesReadRate.ValueInt64()) + } + if !plan.DiskBytesReadRateMax.IsNull() { + p.SetBytesreadratemax(plan.DiskBytesReadRateMax.ValueInt64()) + } + if !plan.DiskBytesReadRateMaxLength.IsNull() { + p.SetBytesreadratemaxlength(plan.DiskBytesReadRateMaxLength.ValueInt64()) + } + if !plan.DiskBytesWriteRate.IsNull() { + p.SetByteswriterate(plan.DiskBytesWriteRate.ValueInt64()) + } + if !plan.DiskBytesWriteRateMax.IsNull() { + p.SetByteswriteratemax(plan.DiskBytesWriteRateMax.ValueInt64()) + } + if !plan.DiskBytesWriteRateMaxLength.IsNull() { + p.SetByteswriteratemaxlength(plan.DiskBytesWriteRateMaxLength.ValueInt64()) + } + + return p +} + +func (plan *ServiceOfferingDiskOffering) commonCreateParams(p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { + + if !plan.CacheMode.IsNull() { + p.SetCachemode(plan.CacheMode.ValueString()) + } + if !plan.DiskOfferingStrictness.IsNull() { + p.SetDiskofferingstrictness(plan.DiskOfferingStrictness.ValueBool()) + } + if !plan.ProvisionType.IsNull() { + p.SetProvisioningtype(plan.ProvisionType.ValueString()) + } + if !plan.RootDiskSize.IsNull() { + p.SetRootdisksize(plan.RootDiskSize.ValueInt64()) + } + if !plan.StorageType.IsNull() { + p.SetStoragetype(plan.StorageType.ValueString()) + } + + return p + +} + +func (plan *ServiceOfferingDiskQosStorage) commonCreateParams(p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { + if !plan.CustomizedIops.IsNull() { + p.SetCustomizediops(plan.CustomizedIops.ValueBool()) + } + if !plan.HypervisorSnapshotReserve.IsNull() { + p.SetHypervisorsnapshotreserve(int(plan.HypervisorSnapshotReserve.ValueInt32())) + } + if !plan.MaxIops.IsNull() { + p.SetMaxiops(int64(plan.MaxIops.ValueInt64())) + } + if !plan.MinIops.IsNull() { + p.SetMiniops((plan.MinIops.ValueInt64())) + } + + return p +} diff --git a/go.mod b/go.mod index 8952c350..7b36c049 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,10 @@ require ( github.com/apache/cloudstack-go/v2 v2.16.1 github.com/go-ini/ini v1.67.0 github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/terraform-plugin-framework v1.7.0 + github.com/hashicorp/terraform-plugin-framework v1.12.0 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 - github.com/hashicorp/terraform-plugin-go v0.22.1 - github.com/hashicorp/terraform-plugin-mux v0.15.0 + github.com/hashicorp/terraform-plugin-go v0.24.0 + github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 github.com/hashicorp/terraform-plugin-testing v1.7.0 ) @@ -19,14 +19,14 @@ require ( github.com/cloudflare/circl v1.3.7 // indirect github.com/fatih/color v1.16.0 // indirect github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 // indirect github.com/hashicorp/go-hclog v1.6.2 // indirect - github.com/hashicorp/go-plugin v1.6.0 // indirect + github.com/hashicorp/go-plugin v1.6.1 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.6.3 // indirect @@ -53,19 +53,20 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.14.3 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/mod v0.15.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.13.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect - google.golang.org/grpc v1.62.1 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect + google.golang.org/grpc v1.66.2 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) -go 1.21 +go 1.22.0 -toolchain go1.21.6 +toolchain go1.22.4 diff --git a/go.sum b/go.sum index 51980c36..af0f5e12 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -62,8 +62,8 @@ github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04n github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= -github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= +github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= +github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -79,16 +79,16 @@ github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8J github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= -github.com/hashicorp/terraform-plugin-framework v1.7.0 h1:wOULbVmfONnJo9iq7/q+iBOBJul5vRovaYJIu2cY/Pw= -github.com/hashicorp/terraform-plugin-framework v1.7.0/go.mod h1:jY9Id+3KbZ17OMpulgnWLSfwxNVYSoYBQFTgsx044CI= +github.com/hashicorp/terraform-plugin-framework v1.12.0 h1:7HKaueHPaikX5/7cbC1r9d1m12iYHY+FlNZEGxQ42CQ= +github.com/hashicorp/terraform-plugin-framework v1.12.0/go.mod h1:N/IOQ2uYjW60Jp39Cp3mw7I/OpC/GfZ0385R0YibmkE= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc= github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg= -github.com/hashicorp/terraform-plugin-go v0.22.1 h1:iTS7WHNVrn7uhe3cojtvWWn83cm2Z6ryIUDTRO0EV7w= -github.com/hashicorp/terraform-plugin-go v0.22.1/go.mod h1:qrjnqRghvQ6KnDbB12XeZ4FluclYwptntoWCr9QaXTI= +github.com/hashicorp/terraform-plugin-go v0.24.0 h1:2WpHhginCdVhFIrWHxDEg6RBn3YaWzR2o6qUeIEat2U= +github.com/hashicorp/terraform-plugin-go v0.24.0/go.mod h1:tUQ53lAsOyYSckFGEefGC5C8BAaO0ENqzFd3bQeuYQg= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-mux v0.15.0 h1:+/+lDx0WUsIOpkAmdwBIoFU8UP9o2eZASoOnLsWbKME= -github.com/hashicorp/terraform-plugin-mux v0.15.0/go.mod h1:9ezplb1Dyq394zQ+ldB0nvy/qbNAz3mMoHHseMTMaKo= +github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQsWn/ZECEiW7p2023I= +github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 h1:qHprzXy/As0rxedphECBEQAh3R4yp6pKksKHcqZx5G8= github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0/go.mod h1:H+8tjs9TjV2w57QFVSMBQacf8k/E1XwLXGCARgViC6A= github.com/hashicorp/terraform-plugin-testing v1.7.0 h1:I6aeCyZ30z4NiI3tzyDoO6fS7YxP5xSL1ceOon3gTe8= @@ -168,25 +168,25 @@ github.com/zclconf/go-cty v1.14.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgr golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -202,8 +202,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -211,14 +211,14 @@ 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= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 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.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -226,14 +226,14 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= +google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 7e343ea43ae0ad93fd5a4ee31516a46ed2a3c374 Mon Sep 17 00:00:00 2001 From: "Poddo, Michael (mp940m)" <8801231+poddm@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:53:06 -0700 Subject: [PATCH 2/8] adding description to schema fields --- .../service_offering_constrained_resource.go | 15 ++- cloudstack/service_offering_fixed_resource.go | 9 +- cloudstack/service_offering_schema.go | 91 ++++++++++++------- 3 files changed, 76 insertions(+), 39 deletions(-) diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go index 52ac2ee7..990f1eee 100644 --- a/cloudstack/service_offering_constrained_resource.go +++ b/cloudstack/service_offering_constrained_resource.go @@ -30,31 +30,36 @@ func (r *serviceOfferingConstrainedResource) Schema(_ context.Context, _ resourc resp.Schema = schema.Schema{ Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{ "cpu_speed": schema.Int32Attribute{ - Required: true, + Description: "Speed of CPU in Mhz. This does not apply to kvm.", + Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, }, "max_cpu_number": schema.Int32Attribute{ - Required: true, + Description: "The maximum number of CPUs to be set with Custom Computer Offering", + Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, }, "max_memory": schema.Int32Attribute{ - Required: true, + Description: "The maximum memory size of the custom service offering in MB", + Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, }, "min_cpu_number": schema.Int32Attribute{ - Required: true, + Description: "The minimum number of CPUs to be set with Custom Computer Offering", + Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, }, "min_memory": schema.Int32Attribute{ - Required: true, + Description: "The minimum memory size of the custom service offering in MB", + Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, diff --git a/cloudstack/service_offering_fixed_resource.go b/cloudstack/service_offering_fixed_resource.go index c512c329..b7f70a6f 100644 --- a/cloudstack/service_offering_fixed_resource.go +++ b/cloudstack/service_offering_fixed_resource.go @@ -30,19 +30,22 @@ func (r *serviceOfferingFixedResource) Schema(_ context.Context, _ resource.Sche resp.Schema = schema.Schema{ Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{ "cpu_number": schema.Int32Attribute{ - Required: true, + Description: "Number of CPU cores", + Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, }, "cpu_speed": schema.Int32Attribute{ - Required: true, + Description: "the CPU speed of the service offering in MHz. This does not apply to KVM.", + Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, }, "memory": schema.Int32Attribute{ - Required: true, + Description: "the total memory of the service offering in MB", + Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, diff --git a/cloudstack/service_offering_schema.go b/cloudstack/service_offering_schema.go index 3aeb82be..e11c5d1d 100644 --- a/cloudstack/service_offering_schema.go +++ b/cloudstack/service_offering_schema.go @@ -14,69 +14,83 @@ import ( func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string]schema.Attribute { common := map[string]schema.Attribute{ "deployment_planner": schema.StringAttribute{ - Optional: true, + Description: "The deployment planner for the service offering", + Optional: true, }, "disk_offering_id": schema.StringAttribute{ - Optional: true, + Description: "The ID of the disk offering", + Optional: true, }, "display_text": schema.StringAttribute{ - Required: true, + Description: "The display text of the service offering", + Required: true, }, "domain_ids": schema.SetAttribute{ + Description: "the ID of the containing domain(s), null for public offerings", Optional: true, ElementType: types.StringType, }, "dynamic_scaling_enabled": schema.BoolAttribute{ - Optional: true, - Computed: true, + Description: "Enable dynamic scaling of the service offering", + Optional: true, + Computed: true, PlanModifiers: []planmodifier.Bool{ boolplanmodifier.RequiresReplace(), }, Default: booldefault.StaticBool(false), }, "host_tags": schema.StringAttribute{ - Optional: true, + Description: "The host tag for this service offering", + Optional: true, }, "id": schema.StringAttribute{ - Computed: true, + Description: "uuid of service offering", + Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, }, "is_volatile": schema.BoolAttribute{ - Optional: true, - Computed: true, + Description: "Service offering is volatile", + Optional: true, + Computed: true, PlanModifiers: []planmodifier.Bool{ boolplanmodifier.RequiresReplace(), }, Default: booldefault.StaticBool(false), }, "limit_cpu_use": schema.BoolAttribute{ - Optional: true, - Computed: true, + Description: "Restrict the CPU usage to committed service offering", + Optional: true, + Computed: true, PlanModifiers: []planmodifier.Bool{ boolplanmodifier.RequiresReplace(), }, Default: booldefault.StaticBool(false), }, "name": schema.StringAttribute{ - Required: true, + Description: "The name of the service offering", + Required: true, }, "network_rate": schema.Int32Attribute{ - Optional: true, + Description: "Data transfer rate in megabits per second", + Optional: true, }, "offer_ha": schema.BoolAttribute{ - Optional: true, - Computed: true, + Description: "The HA for the service offering", + Optional: true, + Computed: true, PlanModifiers: []planmodifier.Bool{ boolplanmodifier.RequiresReplace(), }, Default: booldefault.StaticBool(false), }, "storage_tags": schema.StringAttribute{ - Optional: true, + Description: "the tags for the service offering", + Optional: true, }, "zone_ids": schema.SetAttribute{ + Description: "The ID of the zone(s)", Optional: true, ElementType: types.StringType, }, @@ -84,37 +98,43 @@ func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string Optional: true, Attributes: map[string]schema.Attribute{ "bytes_read_rate": schema.Int64Attribute{ - Required: true, + Description: "io requests read rate of the disk offering", + Required: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, }, "bytes_read_rate_max": schema.Int64Attribute{ - Required: true, + Description: "burst requests read rate of the disk offering", + Required: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, }, "bytes_read_rate_max_length": schema.Int64Attribute{ - Required: true, + Description: "length (in seconds) of the burst", + Required: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, }, "bytes_write_rate": schema.Int64Attribute{ - Required: true, + Description: "io requests write rate of the disk offering", + Required: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, }, "bytes_write_rate_max": schema.Int64Attribute{ - Required: true, + Description: "burst io requests write rate of the disk offering", + Required: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, }, "bytes_write_rate_max_length": schema.Int64Attribute{ - Required: true, + Description: "length (in seconds) of the burst", + Required: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, @@ -125,31 +145,36 @@ func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string Optional: true, Attributes: map[string]schema.Attribute{ "cache_mode": schema.StringAttribute{ - Required: true, + Description: "the cache mode to use for this disk offering. none, writeback or writethrough", + Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, }, "disk_offering_strictness": schema.BoolAttribute{ - Required: true, + Description: "True/False to indicate the strictness of the disk offering association with the compute offering. When set to true, override of disk offering is not allowed when VM is deployed and change disk offering is not allowed for the ROOT disk after the VM is deployed", + Required: true, PlanModifiers: []planmodifier.Bool{ boolplanmodifier.RequiresReplace(), }, }, "provisioning_type": schema.StringAttribute{ - Required: true, + Description: "provisioning type used to create volumes. Valid values are thin, sparse, fat.", + Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, }, "root_disk_size": schema.Int64Attribute{ - Required: true, + Description: "the Root disk size in GB.", + Required: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, }, "storage_type": schema.StringAttribute{ - Required: true, + Description: "the storage type of the service offering. Values are local and shared.", + Required: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), }, @@ -160,25 +185,29 @@ func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string Optional: true, Attributes: map[string]schema.Attribute{ "customized_iops": schema.BoolAttribute{ - Optional: true, + Description: "true if disk offering uses custom iops, false otherwise", + Optional: true, PlanModifiers: []planmodifier.Bool{ boolplanmodifier.RequiresReplace(), }, }, "hypervisor_snapshot_reserve": schema.Int32Attribute{ - Optional: true, + Description: "Hypervisor snapshot reserve space as a percent of a volume (for managed storage using Xen or VMware)", + Optional: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), }, }, "max_iops": schema.Int64Attribute{ - Optional: true, + Description: "max iops of the compute offering", + Optional: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, }, "min_iops": schema.Int64Attribute{ - Optional: true, + Description: "min iops of the compute offering", + Optional: true, PlanModifiers: []planmodifier.Int64{ int64planmodifier.RequiresReplace(), }, From f4bbefd5e132d0e47f45a48b7417106537e9faf9 Mon Sep 17 00:00:00 2001 From: "Poddo, Michael (mp940m)" <8801231+poddm@users.noreply.github.com> Date: Thu, 24 Oct 2024 17:44:03 -0700 Subject: [PATCH 3/8] fixing domainids value error --- .../service_offering_constrained_resource.go | 8 +++---- cloudstack/service_offering_fixed_resource.go | 2 +- ...service_offering_unconstrained_resource.go | 8 +++---- cloudstack/service_offering_util.go | 21 ++++++++----------- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go index 990f1eee..7a8ac146 100644 --- a/cloudstack/service_offering_constrained_resource.go +++ b/cloudstack/service_offering_constrained_resource.go @@ -92,10 +92,10 @@ func (r *serviceOfferingConstrainedResource) Create(ctx context.Context, req res // common params params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) - plan.commonCreateParams(params) - planDiskQosHypervisor.commonCreateParams(params) - planDiskOffering.commonCreateParams(params) - planDiskQosStorage.commonCreateParams(params) + plan.commonCreateParams(ctx, params) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, params) // resource specific params if !plan.CpuSpeed.IsNull() { diff --git a/cloudstack/service_offering_fixed_resource.go b/cloudstack/service_offering_fixed_resource.go index b7f70a6f..f413855a 100644 --- a/cloudstack/service_offering_fixed_resource.go +++ b/cloudstack/service_offering_fixed_resource.go @@ -78,7 +78,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(params) + plan.commonCreateParams(ctx, params) // resource specific params if !plan.CpuNumber.IsNull() { diff --git a/cloudstack/service_offering_unconstrained_resource.go b/cloudstack/service_offering_unconstrained_resource.go index 0087b0f6..b2244dbd 100644 --- a/cloudstack/service_offering_unconstrained_resource.go +++ b/cloudstack/service_offering_unconstrained_resource.go @@ -55,10 +55,10 @@ func (r *serviceOfferingUnconstrainedResource) Create(ctx context.Context, req r // cloudstack params params := r.client.ServiceOffering.NewCreateServiceOfferingParams(plan.DisplayText.ValueString(), plan.Name.ValueString()) - plan.commonCreateParams(params) - planDiskQosHypervisor.commonCreateParams(params) - planDiskOffering.commonCreateParams(params) - planDiskQosStorage.commonCreateParams(params) + plan.commonCreateParams(ctx, params) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, params) // create offering cs, err := r.client.ServiceOffering.CreateServiceOffering(params) diff --git a/cloudstack/service_offering_util.go b/cloudstack/service_offering_util.go index 9dd51e78..bb978810 100644 --- a/cloudstack/service_offering_util.go +++ b/cloudstack/service_offering_util.go @@ -61,6 +61,7 @@ func (plan *serviceOfferingCommonResourceModel) commonUpdateParams(ctx context.C // - func (state *serviceOfferingCommonResourceModel) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { state.Id = types.StringValue(cs.Id) + if cs.Deploymentplanner != "" { state.DeploymentPlanner = types.StringValue(cs.Deploymentplanner) } @@ -156,7 +157,7 @@ func (state *ServiceOfferingDiskQosStorage) commonRead(ctx context.Context, cs * // ------------------------------------------------------------------------------------------------------------------------------ // common Create methods // - -func (plan *serviceOfferingCommonResourceModel) commonCreateParams(p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { +func (plan *serviceOfferingCommonResourceModel) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { if !plan.DeploymentPlanner.IsNull() && !plan.DeploymentPlanner.IsUnknown() { p.SetDeploymentplanner(plan.DeploymentPlanner.ValueString()) } else { @@ -167,9 +168,7 @@ func (plan *serviceOfferingCommonResourceModel) commonCreateParams(p *cloudstack } if !plan.DomainIds.IsNull() { domainids := make([]string, len(plan.DomainIds.Elements())) - for i, v := range plan.DomainIds.Elements() { - domainids[i] = v.String() - } + plan.DomainIds.ElementsAs(ctx, &domainids, false) p.SetDomainid(domainids) } if !plan.DynamicScalingEnabled.IsNull() { @@ -194,17 +193,15 @@ func (plan *serviceOfferingCommonResourceModel) commonCreateParams(p *cloudstack p.SetTags(plan.StorageTags.ValueString()) } if !plan.ZoneIds.IsNull() { - zids := make([]string, len(plan.ZoneIds.Elements())) - for i, v := range plan.ZoneIds.Elements() { - zids[i] = v.String() - } - p.SetZoneid(zids) + zoneIds := make([]string, len(plan.ZoneIds.Elements())) + plan.ZoneIds.ElementsAs(ctx, &zoneIds, false) + p.SetZoneid(zoneIds) } return p } -func (plan *ServiceOfferingDiskQosHypervisor) commonCreateParams(p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { +func (plan *ServiceOfferingDiskQosHypervisor) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { if !plan.DiskBytesReadRate.IsNull() { p.SetBytesreadrate(plan.DiskBytesReadRate.ValueInt64()) } @@ -227,7 +224,7 @@ func (plan *ServiceOfferingDiskQosHypervisor) commonCreateParams(p *cloudstack.C return p } -func (plan *ServiceOfferingDiskOffering) commonCreateParams(p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { +func (plan *ServiceOfferingDiskOffering) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { if !plan.CacheMode.IsNull() { p.SetCachemode(plan.CacheMode.ValueString()) @@ -249,7 +246,7 @@ func (plan *ServiceOfferingDiskOffering) commonCreateParams(p *cloudstack.Create } -func (plan *ServiceOfferingDiskQosStorage) commonCreateParams(p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { +func (plan *ServiceOfferingDiskQosStorage) commonCreateParams(ctx context.Context, p *cloudstack.CreateServiceOfferingParams) *cloudstack.CreateServiceOfferingParams { if !plan.CustomizedIops.IsNull() { p.SetCustomizediops(plan.CustomizedIops.ValueBool()) } From 43f2e65ff8e0a3a6f2fb8fd5cc85192584b54466 Mon Sep 17 00:00:00 2001 From: "Poddo, Michael (mp940m)" <8801231+poddm@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:39:57 -0700 Subject: [PATCH 4/8] Adding better cpu_speed description for vmware, xen and kvm --- cloudstack/service_offering_constrained_resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go index 7a8ac146..64a60c9a 100644 --- a/cloudstack/service_offering_constrained_resource.go +++ b/cloudstack/service_offering_constrained_resource.go @@ -30,7 +30,7 @@ func (r *serviceOfferingConstrainedResource) Schema(_ context.Context, _ resourc resp.Schema = schema.Schema{ Attributes: serviceOfferingMergeCommonSchema(map[string]schema.Attribute{ "cpu_speed": schema.Int32Attribute{ - Description: "Speed of CPU in Mhz. This does not apply to kvm.", + Description: "VMware and Xen based hypervisors this is the CPU speed of the service offering in MHz. For the KVM hypervisor the values of the parameters cpuSpeed and cpuNumber will be used to calculate the `shares` value. This value is used by the KVM hypervisor to calculate how much time the VM will have access to the host's CPU. The `shares` value does not have a unit, and its purpose is being a weight value for the host to compare between its guest VMs. For more information, see https://libvirt.org/formatdomain.html#cpu-tuning.", Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), From 9099f8ea6853d3602408c4483e23afb5eff901ed Mon Sep 17 00:00:00 2001 From: "Poddo, Michael (mp940m)" <8801231+poddm@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:46:40 -0700 Subject: [PATCH 5/8] cleaned up comments --- .../service_offering_constrained_resource.go | 22 ++----------- cloudstack/service_offering_fixed_resource.go | 20 ++---------- cloudstack/service_offering_models.go | 31 +++++++++---------- ...service_offering_unconstrained_resource.go | 17 ---------- 4 files changed, 19 insertions(+), 71 deletions(-) diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go index 64a60c9a..294a9ba4 100644 --- a/cloudstack/service_offering_constrained_resource.go +++ b/cloudstack/service_offering_constrained_resource.go @@ -37,7 +37,7 @@ func (r *serviceOfferingConstrainedResource) Schema(_ context.Context, _ resourc }, }, "max_cpu_number": schema.Int32Attribute{ - Description: "The maximum number of CPUs to be set with Custom Computer Offering", + Description: "The maximum number of CPUs to be set with Custom Compute Offering", Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), @@ -51,7 +51,7 @@ func (r *serviceOfferingConstrainedResource) Schema(_ context.Context, _ resourc }, }, "min_cpu_number": schema.Int32Attribute{ - Description: "The minimum number of CPUs to be set with Custom Computer Offering", + Description: "The minimum number of CPUs to be set with Custom Compute Offering", Required: true, PlanModifiers: []planmodifier.Int32{ int32planmodifier.RequiresReplace(), @@ -69,13 +69,11 @@ func (r *serviceOfferingConstrainedResource) Schema(_ context.Context, _ resourc } func (r *serviceOfferingConstrainedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - // var plan serviceOfferingConstrainedResourceModel var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor var planDiskOffering ServiceOfferingDiskOffering var planDiskQosStorage ServiceOfferingDiskQosStorage - // resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) @@ -132,13 +130,11 @@ func (r *serviceOfferingConstrainedResource) Create(ctx context.Context, req res } func (r *serviceOfferingConstrainedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - // var state serviceOfferingConstrainedResourceModel var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor var stateDiskOffering ServiceOfferingDiskOffering var stateDiskQosStorage ServiceOfferingDiskQosStorage - // resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if !state.ServiceOfferingDiskQosHypervisor.IsNull() { resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) @@ -153,7 +149,6 @@ func (r *serviceOfferingConstrainedResource) Read(ctx context.Context, req resou return } - // cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) if err != nil { resp.Diagnostics.AddError( @@ -167,13 +162,12 @@ func (r *serviceOfferingConstrainedResource) Read(ctx context.Context, req resou if cs.Cpuspeed > 0 { state.CpuSpeed = types.Int32Value(int32(cs.Cpuspeed)) } - // These fields arent returned from list + // These fields arent returned from cs client // max_cpu_number // max_memory // min_cpu_number // min_memory - // state.commonRead(ctx, cs) stateDiskQosHypervisor.commonRead(ctx, cs) stateDiskOffering.commonRead(ctx, cs) @@ -182,27 +176,22 @@ func (r *serviceOfferingConstrainedResource) Read(ctx context.Context, req resou return } - // resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } // Update updates the resource and sets the updated Terraform state on success. func (r *serviceOfferingConstrainedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // var state serviceOfferingConstrainedResourceModel - // resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - // params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) state.commonUpdateParams(ctx, params) - // cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) if err != nil { resp.Diagnostics.AddError( @@ -212,7 +201,6 @@ func (r *serviceOfferingConstrainedResource) Update(ctx context.Context, req res return } - // state.commonUpdate(ctx, cs) if resp.Diagnostics.HasError() { return @@ -220,12 +208,9 @@ func (r *serviceOfferingConstrainedResource) Update(ctx context.Context, req res resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } -// Delete deletes the resource and removes the Terraform state on success. func (r *serviceOfferingConstrainedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // var state serviceOfferingConstrainedResourceModel - // diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -243,7 +228,6 @@ func (r *serviceOfferingConstrainedResource) Delete(ctx context.Context, req res } } -// Configure adds the provider configured client to the resource. 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_fixed_resource.go b/cloudstack/service_offering_fixed_resource.go index f413855a..05c61f31 100644 --- a/cloudstack/service_offering_fixed_resource.go +++ b/cloudstack/service_offering_fixed_resource.go @@ -55,13 +55,11 @@ func (r *serviceOfferingFixedResource) Schema(_ context.Context, _ resource.Sche } func (r *serviceOfferingFixedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - // var plan serviceOfferingFixedResourceModel var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor var planDiskOffering ServiceOfferingDiskOffering var planDiskQosStorage ServiceOfferingDiskQosStorage - // resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) @@ -101,21 +99,17 @@ func (r *serviceOfferingFixedResource) Create(ctx context.Context, req resource. return } - // plan.Id = types.StringValue(cs.Id) - - // resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } func (r *serviceOfferingFixedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - // + var state serviceOfferingFixedResourceModel var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor var stateDiskOffering ServiceOfferingDiskOffering var stateDiskQosStorage ServiceOfferingDiskQosStorage - // resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if !state.ServiceOfferingDiskQosHypervisor.IsNull() { resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) @@ -130,7 +124,6 @@ func (r *serviceOfferingFixedResource) Read(ctx context.Context, req resource.Re return } - // cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) if err != nil { resp.Diagnostics.AddError( @@ -151,7 +144,6 @@ func (r *serviceOfferingFixedResource) Read(ctx context.Context, req resource.Re state.Memory = types.Int32Value(int32(cs.Memory)) } - // state.commonRead(ctx, cs) stateDiskQosHypervisor.commonRead(ctx, cs) stateDiskOffering.commonRead(ctx, cs) @@ -160,26 +152,22 @@ func (r *serviceOfferingFixedResource) Read(ctx context.Context, req resource.Re return } - // resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } // Update updates the resource and sets the updated Terraform state on success. func (r *serviceOfferingFixedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // var state serviceOfferingFixedResourceModel - // + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - // params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) state.commonUpdateParams(ctx, params) - // cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) if err != nil { resp.Diagnostics.AddError( @@ -189,7 +177,6 @@ func (r *serviceOfferingFixedResource) Update(ctx context.Context, req resource. return } - // state.commonUpdate(ctx, cs) if resp.Diagnostics.HasError() { return @@ -199,10 +186,8 @@ func (r *serviceOfferingFixedResource) Update(ctx context.Context, req resource. // Delete deletes the resource and removes the Terraform state on success. func (r *serviceOfferingFixedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // var state serviceOfferingFixedResourceModel - // diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -220,7 +205,6 @@ func (r *serviceOfferingFixedResource) Delete(ctx context.Context, req resource. } } -// Configure adds the provider configured client to the resource. 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_models.go b/cloudstack/service_offering_models.go index 84f08d39..7884a7a9 100644 --- a/cloudstack/service_offering_models.go +++ b/cloudstack/service_offering_models.go @@ -11,8 +11,6 @@ type serviceOfferingConstrainedResourceModel struct { serviceOfferingCommonResourceModel } -// customized types.String `tfsdk:"Iscustomized"` - type serviceOfferingUnconstrainedResourceModel struct { serviceOfferingCommonResourceModel } @@ -25,21 +23,20 @@ type serviceOfferingFixedResourceModel struct { } type serviceOfferingCommonResourceModel struct { - DeploymentPlanner types.String `tfsdk:"deployment_planner"` - DiskOfferingId types.String `tfsdk:"disk_offering_id"` - DisplayText types.String `tfsdk:"display_text"` - DomainIds types.Set `tfsdk:"domain_ids"` - DynamicScalingEnabled types.Bool `tfsdk:"dynamic_scaling_enabled"` - HostTags types.String `tfsdk:"host_tags"` - Id types.String `tfsdk:"id"` - IsVolatile types.Bool `tfsdk:"is_volatile"` - LimitCpuUse types.Bool `tfsdk:"limit_cpu_use"` - Name types.String `tfsdk:"name"` - NetworkRate types.Int32 `tfsdk:"network_rate"` - OfferHa types.Bool `tfsdk:"offer_ha"` - StorageTags types.String `tfsdk:"storage_tags"` - ZoneIds types.Set `tfsdk:"zone_ids"` - // + DeploymentPlanner types.String `tfsdk:"deployment_planner"` + DiskOfferingId types.String `tfsdk:"disk_offering_id"` + DisplayText types.String `tfsdk:"display_text"` + DomainIds types.Set `tfsdk:"domain_ids"` + DynamicScalingEnabled types.Bool `tfsdk:"dynamic_scaling_enabled"` + HostTags types.String `tfsdk:"host_tags"` + Id types.String `tfsdk:"id"` + IsVolatile types.Bool `tfsdk:"is_volatile"` + LimitCpuUse types.Bool `tfsdk:"limit_cpu_use"` + Name types.String `tfsdk:"name"` + NetworkRate types.Int32 `tfsdk:"network_rate"` + OfferHa types.Bool `tfsdk:"offer_ha"` + StorageTags types.String `tfsdk:"storage_tags"` + ZoneIds types.Set `tfsdk:"zone_ids"` ServiceOfferingDiskQosHypervisor types.Object `tfsdk:"disk_hypervisor"` ServiceOfferingDiskOffering types.Object `tfsdk:"disk_offering"` ServiceOfferingDiskQosStorage types.Object `tfsdk:"disk_storage"` diff --git a/cloudstack/service_offering_unconstrained_resource.go b/cloudstack/service_offering_unconstrained_resource.go index b2244dbd..9664d4c7 100644 --- a/cloudstack/service_offering_unconstrained_resource.go +++ b/cloudstack/service_offering_unconstrained_resource.go @@ -32,13 +32,11 @@ func (r *serviceOfferingUnconstrainedResource) Schema(_ context.Context, _ resou } func (r *serviceOfferingUnconstrainedResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - // var plan serviceOfferingUnconstrainedResourceModel var planDiskQosHypervisor ServiceOfferingDiskQosHypervisor var planDiskOffering ServiceOfferingDiskOffering var planDiskQosStorage ServiceOfferingDiskQosStorage - // resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if !plan.ServiceOfferingDiskQosHypervisor.IsNull() { resp.Diagnostics.Append(plan.ServiceOfferingDiskQosHypervisor.As(ctx, &planDiskQosHypervisor, basetypes.ObjectAsOptions{})...) @@ -70,22 +68,17 @@ func (r *serviceOfferingUnconstrainedResource) Create(ctx context.Context, req r return } - // plan.Id = types.StringValue(cs.Id) - - // resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } func (r *serviceOfferingUnconstrainedResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { - // var state serviceOfferingUnconstrainedResourceModel var stateDiskQosHypervisor ServiceOfferingDiskQosHypervisor var stateDiskOffering ServiceOfferingDiskOffering var stateDiskQosStorage ServiceOfferingDiskQosStorage - // resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if !state.ServiceOfferingDiskQosHypervisor.IsNull() { resp.Diagnostics.Append(state.ServiceOfferingDiskQosHypervisor.As(ctx, &stateDiskQosHypervisor, basetypes.ObjectAsOptions{})...) @@ -100,7 +93,6 @@ func (r *serviceOfferingUnconstrainedResource) Read(ctx context.Context, req res return } - // cs, _, err := r.client.ServiceOffering.GetServiceOfferingByID(state.Id.ValueString()) if err != nil { resp.Diagnostics.AddError( @@ -110,7 +102,6 @@ func (r *serviceOfferingUnconstrainedResource) Read(ctx context.Context, req res return } - // state.commonRead(ctx, cs) stateDiskQosHypervisor.commonRead(ctx, cs) stateDiskOffering.commonRead(ctx, cs) @@ -119,26 +110,21 @@ func (r *serviceOfferingUnconstrainedResource) Read(ctx context.Context, req res return } - // resp.Diagnostics.Append(resp.State.Set(ctx, state)...) } func (r *serviceOfferingUnconstrainedResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - // var state serviceOfferingUnconstrainedResourceModel - // resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } - // params := r.client.ServiceOffering.NewUpdateServiceOfferingParams(state.Id.ValueString()) state.commonUpdateParams(ctx, params) - // cs, err := r.client.ServiceOffering.UpdateServiceOffering(params) if err != nil { resp.Diagnostics.AddError( @@ -148,7 +134,6 @@ func (r *serviceOfferingUnconstrainedResource) Update(ctx context.Context, req r return } - // state.commonUpdate(ctx, cs) if resp.Diagnostics.HasError() { return @@ -158,10 +143,8 @@ func (r *serviceOfferingUnconstrainedResource) Update(ctx context.Context, req r } func (r *serviceOfferingUnconstrainedResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - // var state serviceOfferingUnconstrainedResourceModel - // diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { From 0ef7e497aeb4405ab0da6c817f689f4bdb55bfa1 Mon Sep 17 00:00:00 2001 From: "Poddo, Michael (mp940m)" <8801231+poddm@users.noreply.github.com> Date: Tue, 29 Oct 2024 12:13:05 -0700 Subject: [PATCH 6/8] Adding service offering details --- .../service_offering_constrained_resource.go | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go index 294a9ba4..517806ca 100644 --- a/cloudstack/service_offering_constrained_resource.go +++ b/cloudstack/service_offering_constrained_resource.go @@ -3,6 +3,7 @@ package cloudstack import ( "context" "fmt" + "strconv" "github.com/apache/cloudstack-go/v2/cloudstack" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -162,11 +163,50 @@ func (r *serviceOfferingConstrainedResource) Read(ctx context.Context, req resou if cs.Cpuspeed > 0 { state.CpuSpeed = types.Int32Value(int32(cs.Cpuspeed)) } - // These fields arent returned from cs client - // max_cpu_number - // max_memory - // min_cpu_number - // min_memory + if v, found := cs.Serviceofferingdetails["maxcpunumber"]; found { + i, err := strconv.Atoi(v) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering max cpu, unexpected error: "+err.Error(), + ) + return + } + state.MaxCpuNumber = types.Int32Value(int32(i)) + } + if v, found := cs.Serviceofferingdetails["mincpunumber"]; found { + i, err := strconv.Atoi(v) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering min cpu number, unexpected error: "+err.Error(), + ) + return + } + state.MinCpuNumber = types.Int32Value(int32(i)) + } + if v, found := cs.Serviceofferingdetails["maxmemory"]; found { + i, err := strconv.Atoi(v) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering max memory, unexpected error: "+err.Error(), + ) + return + } + state.MaxMemory = types.Int32Value(int32(i)) + } + if v, found := cs.Serviceofferingdetails["minmemory"]; found { + i, err := strconv.Atoi(v) + if err != nil { + resp.Diagnostics.AddError( + "Error reading service offering", + "Could not read constrained service offering min memory, unexpected error: "+err.Error(), + ) + return + } + state.MinMemory = types.Int32Value(int32(i)) + } state.commonRead(ctx, cs) stateDiskQosHypervisor.commonRead(ctx, cs) From 692cfcada0cdf551289c32e8be5f86492a0d654c Mon Sep 17 00:00:00 2001 From: "Poddo, Michael (mp940m)" <8801231+poddm@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:12:58 -0700 Subject: [PATCH 7/8] Adding license header to files. Fixed an issue with setting zone ids to null, moved storage_tags field to nested block disk_offering --- .../service_offering_constrained_resource.go | 19 +++ ...vice_offering_constrained_resource_test.go | 20 ++-- cloudstack/service_offering_fixed_resource.go | 19 +++ .../service_offering_fixed_resource_test.go | 6 +- cloudstack/service_offering_models.go | 21 +++- cloudstack/service_offering_schema.go | 111 +++++++++++------- ...service_offering_unconstrained_resource.go | 19 +++ ...ce_offering_unconstrained_resource_test.go | 6 +- cloudstack/service_offering_util.go | 37 ++++-- 9 files changed, 188 insertions(+), 70 deletions(-) diff --git a/cloudstack/service_offering_constrained_resource.go b/cloudstack/service_offering_constrained_resource.go index 517806ca..92c80779 100644 --- a/cloudstack/service_offering_constrained_resource.go +++ b/cloudstack/service_offering_constrained_resource.go @@ -1,3 +1,22 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + package cloudstack import ( diff --git a/cloudstack/service_offering_constrained_resource_test.go b/cloudstack/service_offering_constrained_resource_test.go index 247c1560..5368db7b 100644 --- a/cloudstack/service_offering_constrained_resource_test.go +++ b/cloudstack/service_offering_constrained_resource_test.go @@ -77,6 +77,13 @@ func TestAccServiceOfferingConstrained(t *testing.T) { } const testAccServiceOfferingCustomConstrained1 = ` +resource "cloudstack_zone" "test" { + name = "acctest" + dns1 = "8.8.8.8" + internal_dns1 = "8.8.4.4" + network_type = "Advanced" +} + resource "cloudstack_service_offering_constrained" "constrained1" { display_text = "constrained1" name = "constrained1" @@ -91,17 +98,16 @@ resource "cloudstack_service_offering_constrained" "constrained1" { min_memory = 1024 // other - storage_tags = "foo" host_tags = "test0101,test0202" network_rate = 1024 deployment_planner = "UserDispersingPlanner" - //Feature flags + // Feature flags dynamic_scaling_enabled = false is_volatile = false limit_cpu_use = false offer_ha = false - zone_ids = [] + zone_ids = [cloudstack_zone.test.id] } ` @@ -121,7 +127,6 @@ resource "cloudstack_service_offering_constrained" "constrained1" { min_memory = 1024 // other - storage_tags = "foo1" host_tags = "test0101,test0202" network_rate = 1024 deployment_planner = "UserDispersingPlanner" @@ -215,10 +220,11 @@ resource "cloudstack_service_offering_constrained" "constrained1" { disk_offering = { storage_type = "local" + sdfjklsdf = "sdfjks" provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "FOO" disk_offering_strictness = false } } @@ -254,7 +260,7 @@ resource "cloudstack_service_offering_constrained" "disk_hypervisor" { provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "FOO" disk_offering_strictness = false } disk_hypervisor = { @@ -298,7 +304,7 @@ resource "cloudstack_service_offering_constrained" "disk_storage" { provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "FOO" disk_offering_strictness = false } disk_hypervisor = { diff --git a/cloudstack/service_offering_fixed_resource.go b/cloudstack/service_offering_fixed_resource.go index 05c61f31..e88877dc 100644 --- a/cloudstack/service_offering_fixed_resource.go +++ b/cloudstack/service_offering_fixed_resource.go @@ -1,3 +1,22 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + package cloudstack import ( diff --git a/cloudstack/service_offering_fixed_resource_test.go b/cloudstack/service_offering_fixed_resource_test.go index 56d23a5e..438740e0 100644 --- a/cloudstack/service_offering_fixed_resource_test.go +++ b/cloudstack/service_offering_fixed_resource_test.go @@ -158,7 +158,7 @@ resource "cloudstack_service_offering_fixed" "disk" { provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "test0101,test0202" disk_offering_strictness = false } } @@ -189,7 +189,7 @@ resource "cloudstack_service_offering_fixed" "disk_hypervisor" { provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "test0101,test0202" disk_offering_strictness = false } disk_hypervisor = { @@ -228,7 +228,7 @@ resource "cloudstack_service_offering_fixed" "disk_storage" { provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "test0101,test0202" disk_offering_strictness = false } disk_storage = { diff --git a/cloudstack/service_offering_models.go b/cloudstack/service_offering_models.go index 7884a7a9..a93ffa48 100644 --- a/cloudstack/service_offering_models.go +++ b/cloudstack/service_offering_models.go @@ -1,3 +1,22 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + package cloudstack import "github.com/hashicorp/terraform-plugin-framework/types" @@ -35,7 +54,6 @@ type serviceOfferingCommonResourceModel struct { Name types.String `tfsdk:"name"` NetworkRate types.Int32 `tfsdk:"network_rate"` OfferHa types.Bool `tfsdk:"offer_ha"` - StorageTags types.String `tfsdk:"storage_tags"` ZoneIds types.Set `tfsdk:"zone_ids"` ServiceOfferingDiskQosHypervisor types.Object `tfsdk:"disk_hypervisor"` ServiceOfferingDiskOffering types.Object `tfsdk:"disk_offering"` @@ -57,6 +75,7 @@ type ServiceOfferingDiskOffering struct { ProvisionType types.String `tfsdk:"provisioning_type"` RootDiskSize types.Int64 `tfsdk:"root_disk_size"` StorageType types.String `tfsdk:"storage_type"` + StorageTags types.String `tfsdk:"storage_tags"` } type ServiceOfferingDiskQosStorage struct { diff --git a/cloudstack/service_offering_schema.go b/cloudstack/service_offering_schema.go index e11c5d1d..37e4fb4f 100644 --- a/cloudstack/service_offering_schema.go +++ b/cloudstack/service_offering_schema.go @@ -1,3 +1,26 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +// Nested schema attributes arent validated +// disk_offering, disk_hypervisor, disk_storage +// ref: https://github.com/hashicorp/terraform-plugin-framework/issues/805 + package cloudstack import ( @@ -85,15 +108,55 @@ func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string }, Default: booldefault.StaticBool(false), }, - "storage_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, ElementType: types.StringType, }, + "disk_offering": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "cache_mode": schema.StringAttribute{ + Description: "the cache mode to use for this disk offering. none, writeback or writethrough", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "disk_offering_strictness": schema.BoolAttribute{ + Description: "True/False to indicate the strictness of the disk offering association with the compute offering. When set to true, override of disk offering is not allowed when VM is deployed and change disk offering is not allowed for the ROOT disk after the VM is deployed", + Required: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + }, + "provisioning_type": schema.StringAttribute{ + Description: "provisioning type used to create volumes. Valid values are thin, sparse, fat.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "root_disk_size": schema.Int64Attribute{ + Description: "the Root disk size in GB.", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "storage_type": schema.StringAttribute{ + Description: "the storage type of the service offering. Values are local and shared.", + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "storage_tags": schema.StringAttribute{ + Description: "the tags for the service offering", + Optional: true, + }, + }, + }, "disk_hypervisor": schema.SingleNestedAttribute{ Optional: true, Attributes: map[string]schema.Attribute{ @@ -141,46 +204,6 @@ func serviceOfferingMergeCommonSchema(s1 map[string]schema.Attribute) map[string }, }, }, - "disk_offering": schema.SingleNestedAttribute{ - Optional: true, - Attributes: map[string]schema.Attribute{ - "cache_mode": schema.StringAttribute{ - Description: "the cache mode to use for this disk offering. none, writeback or writethrough", - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - "disk_offering_strictness": schema.BoolAttribute{ - Description: "True/False to indicate the strictness of the disk offering association with the compute offering. When set to true, override of disk offering is not allowed when VM is deployed and change disk offering is not allowed for the ROOT disk after the VM is deployed", - Required: true, - PlanModifiers: []planmodifier.Bool{ - boolplanmodifier.RequiresReplace(), - }, - }, - "provisioning_type": schema.StringAttribute{ - Description: "provisioning type used to create volumes. Valid values are thin, sparse, fat.", - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - "root_disk_size": schema.Int64Attribute{ - Description: "the Root disk size in GB.", - Required: true, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.RequiresReplace(), - }, - }, - "storage_type": schema.StringAttribute{ - Description: "the storage type of the service offering. Values are local and shared.", - Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.RequiresReplace(), - }, - }, - }, - }, "disk_storage": schema.SingleNestedAttribute{ Optional: true, Attributes: map[string]schema.Attribute{ diff --git a/cloudstack/service_offering_unconstrained_resource.go b/cloudstack/service_offering_unconstrained_resource.go index 9664d4c7..98b937cd 100644 --- a/cloudstack/service_offering_unconstrained_resource.go +++ b/cloudstack/service_offering_unconstrained_resource.go @@ -1,3 +1,22 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + package cloudstack import ( diff --git a/cloudstack/service_offering_unconstrained_resource_test.go b/cloudstack/service_offering_unconstrained_resource_test.go index 82bafdda..5aba779f 100644 --- a/cloudstack/service_offering_unconstrained_resource_test.go +++ b/cloudstack/service_offering_unconstrained_resource_test.go @@ -137,7 +137,7 @@ resource "cloudstack_service_offering_unconstrained" "disk" { provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "test0101,test0202" disk_offering_strictness = false } } @@ -162,7 +162,7 @@ resource "cloudstack_service_offering_unconstrained" "disk_hypervisor" { provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "test0101,test0202" disk_offering_strictness = false } disk_hypervisor = { @@ -196,7 +196,7 @@ resource "cloudstack_service_offering_unconstrained" "disk_storage" { provisioning_type = "thin" cache_mode = "none" root_disk_size = "5" - tags = "FOO" + storage_tags = "test0101,test0202" disk_offering_strictness = false } disk_storage = { diff --git a/cloudstack/service_offering_util.go b/cloudstack/service_offering_util.go index bb978810..ae52aa1d 100644 --- a/cloudstack/service_offering_util.go +++ b/cloudstack/service_offering_util.go @@ -1,3 +1,20 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + package cloudstack import ( @@ -24,9 +41,6 @@ func (state *serviceOfferingCommonResourceModel) commonUpdate(ctx context.Contex if cs.Name != "" { state.Name = types.StringValue(cs.Name) } - if cs.Storagetags != "" { - state.StorageTags = types.StringValue(cs.Storagetags) - } if cs.Zoneid != "" { state.ZoneIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) } @@ -45,11 +59,10 @@ func (plan *serviceOfferingCommonResourceModel) commonUpdateParams(ctx context.C if !plan.Name.IsNull() { p.SetName(plan.Name.ValueString()) } - if !plan.StorageTags.IsNull() { - p.SetStoragetags(plan.StorageTags.ValueString()) - } if !plan.ZoneIds.IsNull() && len(plan.ZoneIds.Elements()) > 0 { p.SetZoneid(plan.ZoneIds.String()) + } else { + p.SetZoneid("all") } return p @@ -83,9 +96,6 @@ func (state *serviceOfferingCommonResourceModel) commonRead(ctx context.Context, if cs.Networkrate > 0 { state.NetworkRate = types.Int32Value(int32(cs.Networkrate)) } - if cs.Storagetags != "" { - state.StorageTags = types.StringValue(cs.Storagetags) - } if cs.Zoneid != "" { state.ZoneIds, _ = types.SetValueFrom(ctx, types.StringType, strings.Split(cs.Zoneid, ",")) } @@ -136,6 +146,9 @@ func (state *ServiceOfferingDiskOffering) commonRead(ctx context.Context, cs *cl if cs.Storagetype != "" { state.StorageType = types.StringValue(cs.Storagetype) } + if cs.Storagetags != "" { + state.StorageTags = types.StringValue(cs.Storagetags) + } } func (state *ServiceOfferingDiskQosStorage) commonRead(ctx context.Context, cs *cloudstack.ServiceOffering) { @@ -189,9 +202,6 @@ func (plan *serviceOfferingCommonResourceModel) commonCreateParams(ctx context.C if !plan.OfferHa.IsNull() { p.SetOfferha(plan.OfferHa.ValueBool()) } - if !plan.StorageTags.IsNull() { - p.SetTags(plan.StorageTags.ValueString()) - } if !plan.ZoneIds.IsNull() { zoneIds := make([]string, len(plan.ZoneIds.Elements())) plan.ZoneIds.ElementsAs(ctx, &zoneIds, false) @@ -241,6 +251,9 @@ func (plan *ServiceOfferingDiskOffering) commonCreateParams(ctx context.Context, if !plan.StorageType.IsNull() { p.SetStoragetype(plan.StorageType.ValueString()) } + if !plan.StorageTags.IsNull() { + p.SetTags(plan.StorageTags.ValueString()) + } return p From fa07258302bbc184c6cfcfbfc6c2dd21e41ee2fb Mon Sep 17 00:00:00 2001 From: "Poddo, Michael (mp940m)" <8801231+poddm@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:08:46 -0800 Subject: [PATCH 8/8] add missing set blocks --- cloudstack/service_offering_fixed_resource.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cloudstack/service_offering_fixed_resource.go b/cloudstack/service_offering_fixed_resource.go index e88877dc..6b500fd2 100644 --- a/cloudstack/service_offering_fixed_resource.go +++ b/cloudstack/service_offering_fixed_resource.go @@ -96,6 +96,9 @@ 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) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, params) // resource specific params if !plan.CpuNumber.IsNull() {