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..92c80779 --- /dev/null +++ b/cloudstack/service_offering_constrained_resource.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 ( + "context" + "fmt" + "strconv" + + "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{ + 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(), + }, + }, + "max_cpu_number": schema.Int32Attribute{ + Description: "The maximum number of CPUs to be set with Custom Compute Offering", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "max_memory": schema.Int32Attribute{ + Description: "The maximum memory size of the custom service offering in MB", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "min_cpu_number": schema.Int32Attribute{ + Description: "The minimum number of CPUs to be set with Custom Compute Offering", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "min_memory": schema.Int32Attribute{ + Description: "The minimum memory size of the custom service offering in MB", + 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(ctx, params) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, 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)) + } + 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) + 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)...) +} + +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 + } +} + +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..5368db7b --- /dev/null +++ b/cloudstack/service_offering_constrained_resource_test.go @@ -0,0 +1,319 @@ +// +// 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_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" + + // 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 + zone_ids = [cloudstack_zone.test.id] + +} +` + +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 + 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" + sdfjklsdf = "sdfjks" + provisioning_type = "thin" + cache_mode = "none" + root_disk_size = "5" + storage_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" + storage_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" + storage_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..6b500fd2 --- /dev/null +++ b/cloudstack/service_offering_fixed_resource.go @@ -0,0 +1,251 @@ +// +// 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 ( + "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{ + Description: "Number of CPU cores", + Required: true, + PlanModifiers: []planmodifier.Int32{ + int32planmodifier.RequiresReplace(), + }, + }, + "cpu_speed": schema.Int32Attribute{ + 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{ + Description: "the total memory of the service offering in MB", + 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(ctx, params) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, 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 + } +} + +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..438740e0 --- /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" + storage_tags = "test0101,test0202" + 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" + storage_tags = "test0101,test0202" + 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" + storage_tags = "test0101,test0202" + 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..a93ffa48 --- /dev/null +++ b/cloudstack/service_offering_models.go @@ -0,0 +1,86 @@ +// +// 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" + +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 +} + +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"` + 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"` + StorageTags types.String `tfsdk:"storage_tags"` +} + +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..37e4fb4f --- /dev/null +++ b/cloudstack/service_offering_schema.go @@ -0,0 +1,248 @@ +// +// 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 ( + "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{ + Description: "The deployment planner for the service offering", + Optional: true, + }, + "disk_offering_id": schema.StringAttribute{ + Description: "The ID of the disk offering", + Optional: true, + }, + "display_text": schema.StringAttribute{ + 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{ + 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{ + Description: "The host tag for this service offering", + Optional: true, + }, + "id": schema.StringAttribute{ + Description: "uuid of service offering", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "is_volatile": schema.BoolAttribute{ + Description: "Service offering is volatile", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "limit_cpu_use": schema.BoolAttribute{ + 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{ + Description: "The name of the service offering", + Required: true, + }, + "network_rate": schema.Int32Attribute{ + Description: "Data transfer rate in megabits per second", + Optional: true, + }, + "offer_ha": schema.BoolAttribute{ + Description: "The HA for the service offering", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + Default: booldefault.StaticBool(false), + }, + "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{ + "bytes_read_rate": schema.Int64Attribute{ + Description: "io requests read rate of the disk offering", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_read_rate_max": schema.Int64Attribute{ + Description: "burst requests read rate of the disk offering", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_read_rate_max_length": schema.Int64Attribute{ + Description: "length (in seconds) of the burst", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate": schema.Int64Attribute{ + Description: "io requests write rate of the disk offering", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate_max": schema.Int64Attribute{ + Description: "burst io requests write rate of the disk offering", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "bytes_write_rate_max_length": schema.Int64Attribute{ + Description: "length (in seconds) of the burst", + Required: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + }, + }, + "disk_storage": schema.SingleNestedAttribute{ + Optional: true, + Attributes: map[string]schema.Attribute{ + "customized_iops": schema.BoolAttribute{ + Description: "true if disk offering uses custom iops, false otherwise", + Optional: true, + PlanModifiers: []planmodifier.Bool{ + boolplanmodifier.RequiresReplace(), + }, + }, + "hypervisor_snapshot_reserve": schema.Int32Attribute{ + 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{ + Description: "max iops of the compute offering", + Optional: true, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.RequiresReplace(), + }, + }, + "min_iops": schema.Int64Attribute{ + Description: "min iops of the compute offering", + 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..98b937cd --- /dev/null +++ b/cloudstack/service_offering_unconstrained_resource.go @@ -0,0 +1,204 @@ +// +// 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 ( + "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(ctx, params) + planDiskQosHypervisor.commonCreateParams(ctx, params) + planDiskOffering.commonCreateParams(ctx, params) + planDiskQosStorage.commonCreateParams(ctx, 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..5aba779f --- /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" + storage_tags = "test0101,test0202" + 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" + storage_tags = "test0101,test0202" + 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" + storage_tags = "test0101,test0202" + 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..ae52aa1d --- /dev/null +++ b/cloudstack/service_offering_util.go @@ -0,0 +1,277 @@ +// 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 ( + "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.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.ZoneIds.IsNull() && len(plan.ZoneIds.Elements()) > 0 { + p.SetZoneid(plan.ZoneIds.String()) + } else { + p.SetZoneid("all") + } + + 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.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) + } + if cs.Storagetags != "" { + state.StorageTags = types.StringValue(cs.Storagetags) + } +} + +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(ctx context.Context, 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())) + plan.DomainIds.ElementsAs(ctx, &domainids, false) + 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.ZoneIds.IsNull() { + zoneIds := make([]string, len(plan.ZoneIds.Elements())) + plan.ZoneIds.ElementsAs(ctx, &zoneIds, false) + p.SetZoneid(zoneIds) + } + + return p + +} +func (plan *ServiceOfferingDiskQosHypervisor) commonCreateParams(ctx context.Context, 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(ctx context.Context, 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()) + } + if !plan.StorageTags.IsNull() { + p.SetTags(plan.StorageTags.ValueString()) + } + + return p + +} + +func (plan *ServiceOfferingDiskQosStorage) commonCreateParams(ctx context.Context, 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=