diff --git a/README.md b/README.md index 1eb01054b..542a52e00 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,8 @@ export OVH_IP_REVERSE_TEST="..." export OVH_IP_MOVE_SERVICE_NAME_TEST="..." export OVH_IPLB_SERVICE_TEST="..." export OVH_CLOUD_PROJECT_SERVICE_TEST="..." +export OVH_CLOUD_PROJECT_REGION_TEST="..." +export OVH_CLOUD_PROJECT_LOADBALANCER_TEST="..." export OVH_CLOUD_PROJECT_FAILOVER_IP_TEST="..." export OVH_CLOUD_PROJECT_FAILOVER_IP_ROUTED_TO_1_TEST="..." export OVH_CLOUD_PROJECT_FAILOVER_IP_ROUTED_TO_2_TEST="..." diff --git a/ovh/data_cloud_project_loadbalancer.go b/ovh/data_cloud_project_loadbalancer.go new file mode 100644 index 000000000..ed53a545d --- /dev/null +++ b/ovh/data_cloud_project_loadbalancer.go @@ -0,0 +1,76 @@ +package ovh + +import ( + "context" + "fmt" + "net/url" + "os" + + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" +) + +var _ datasource.DataSourceWithConfigure = (*cloudProjectLoadbalancerDataSource)(nil) + +func NewCloudProjectLoadbalancerDataSource() datasource.DataSource { + return &cloudProjectLoadbalancerDataSource{} +} + +type cloudProjectLoadbalancerDataSource struct { + config *Config +} + +func (d *cloudProjectLoadbalancerDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_cloud_project_loadbalancer" +} + +func (d *cloudProjectLoadbalancerDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + config, ok := req.ProviderData.(*Config) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + d.config = config +} + +func (d *cloudProjectLoadbalancerDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = CloudProjectLoadbalancerDataSourceSchema(ctx) +} + +func (d *cloudProjectLoadbalancerDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data CloudProjectLoadbalancerModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + if data.ServiceName.IsNull() { + data.ServiceName.StringValue = basetypes.NewStringValue(os.Getenv("OVH_CLOUD_PROJECT_SERVICE")) + } + + // Read API call logic + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/loadbalancing/loadbalancer/%s", + url.PathEscape(data.ServiceName.ValueString()), + url.PathEscape(data.RegionName.ValueString()), + url.PathEscape(data.Id.ValueString()), + ) + + if err := d.config.OVHClient.Get(endpoint, &data); err != nil { + resp.Diagnostics.AddError("Failed to get loadbalancer details", fmt.Sprintf("error calling GET %s: %s", endpoint, err)) + return + } + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/ovh/data_cloud_project_loadbalancer_gen.go b/ovh/data_cloud_project_loadbalancer_gen.go new file mode 100644 index 000000000..2fc566985 --- /dev/null +++ b/ovh/data_cloud_project_loadbalancer_gen.go @@ -0,0 +1,600 @@ +// Code generated by terraform-plugin-framework-generator DO NOT EDIT. + +package ovh + +import ( + "context" + "encoding/json" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" + ovhtypes "github.com/ovh/terraform-provider-ovh/ovh/types" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func CloudProjectLoadbalancerDataSourceSchema(ctx context.Context) schema.Schema { + attrs := map[string]schema.Attribute{ + "created_at": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "The UTC date and timestamp when the loadbalancer was created", + MarkdownDescription: "The UTC date and timestamp when the loadbalancer was created", + }, + "flavor_id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "ID of the flavor", + MarkdownDescription: "ID of the flavor", + }, + "floating_ip": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "ID of the floating IP", + MarkdownDescription: "ID of the floating IP", + }, + "ip": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "IP Address of the floating IP", + MarkdownDescription: "IP Address of the floating IP", + }, + }, + CustomType: FloatingIpType{ + ObjectType: types.ObjectType{ + AttrTypes: FloatingIpValue{}.AttributeTypes(ctx), + }, + }, + Computed: true, + Description: "Information about floating IP", + MarkdownDescription: "Information about floating IP", + }, + "id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Required: true, + Description: "Load balancer ID", + MarkdownDescription: "Load balancer ID", + }, + "name": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Name of the loadbalancer", + MarkdownDescription: "Name of the loadbalancer", + }, + "operating_status": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Operating status of the loadbalancer", + MarkdownDescription: "Operating status of the loadbalancer", + }, + "provisioning_status": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Provisioning status of the loadbalancer", + MarkdownDescription: "Provisioning status of the loadbalancer", + }, + "region_name": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Required: true, + Description: "Region of the loadbalancer", + MarkdownDescription: "Region of the loadbalancer", + }, + "service_name": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Required: true, + Description: "Service name", + MarkdownDescription: "Service name", + }, + "updated_at": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "UTC date and timestamp when the loadbalancer was updated", + MarkdownDescription: "UTC date and timestamp when the loadbalancer was updated", + }, + "vip_address": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "IP address of the Virtual IP", + MarkdownDescription: "IP address of the Virtual IP", + }, + "vip_network_id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Openstack ID of the network for the Virtual IP", + MarkdownDescription: "Openstack ID of the network for the Virtual IP", + }, + "vip_subnet_id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "ID of the subnet for the Virtual IP", + MarkdownDescription: "ID of the subnet for the Virtual IP", + }, + } + + return schema.Schema{ + Attributes: attrs, + } +} + +type CloudProjectLoadbalancerModel struct { + CreatedAt ovhtypes.TfStringValue `tfsdk:"created_at" json:"createdAt"` + FlavorId ovhtypes.TfStringValue `tfsdk:"flavor_id" json:"flavorId"` + FloatingIp FloatingIpValue `tfsdk:"floating_ip" json:"floatingIp"` + Id ovhtypes.TfStringValue `tfsdk:"id" json:"id"` + Name ovhtypes.TfStringValue `tfsdk:"name" json:"name"` + OperatingStatus ovhtypes.TfStringValue `tfsdk:"operating_status" json:"operatingStatus"` + ProvisioningStatus ovhtypes.TfStringValue `tfsdk:"provisioning_status" json:"provisioningStatus"` + RegionName ovhtypes.TfStringValue `tfsdk:"region_name" json:"regionName"` + ServiceName ovhtypes.TfStringValue `tfsdk:"service_name" json:"serviceName"` + UpdatedAt ovhtypes.TfStringValue `tfsdk:"updated_at" json:"updatedAt"` + VipAddress ovhtypes.TfStringValue `tfsdk:"vip_address" json:"vipAddress"` + VipNetworkId ovhtypes.TfStringValue `tfsdk:"vip_network_id" json:"vipNetworkId"` + VipSubnetId ovhtypes.TfStringValue `tfsdk:"vip_subnet_id" json:"vipSubnetId"` +} + +func (v *CloudProjectLoadbalancerModel) MergeWith(other *CloudProjectLoadbalancerModel) { + + if (v.CreatedAt.IsUnknown() || v.CreatedAt.IsNull()) && !other.CreatedAt.IsUnknown() { + v.CreatedAt = other.CreatedAt + } + + if (v.FlavorId.IsUnknown() || v.FlavorId.IsNull()) && !other.FlavorId.IsUnknown() { + v.FlavorId = other.FlavorId + } + + if (v.FloatingIp.IsUnknown() || v.FloatingIp.IsNull()) && !other.FloatingIp.IsUnknown() { + v.FloatingIp = other.FloatingIp + } + + if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() { + v.Id = other.Id + } + + if (v.Name.IsUnknown() || v.Name.IsNull()) && !other.Name.IsUnknown() { + v.Name = other.Name + } + + if (v.OperatingStatus.IsUnknown() || v.OperatingStatus.IsNull()) && !other.OperatingStatus.IsUnknown() { + v.OperatingStatus = other.OperatingStatus + } + + if (v.ProvisioningStatus.IsUnknown() || v.ProvisioningStatus.IsNull()) && !other.ProvisioningStatus.IsUnknown() { + v.ProvisioningStatus = other.ProvisioningStatus + } + + if (v.RegionName.IsUnknown() || v.RegionName.IsNull()) && !other.RegionName.IsUnknown() { + v.RegionName = other.RegionName + } + + if (v.ServiceName.IsUnknown() || v.ServiceName.IsNull()) && !other.ServiceName.IsUnknown() { + v.ServiceName = other.ServiceName + } + + if (v.UpdatedAt.IsUnknown() || v.UpdatedAt.IsNull()) && !other.UpdatedAt.IsUnknown() { + v.UpdatedAt = other.UpdatedAt + } + + if (v.VipAddress.IsUnknown() || v.VipAddress.IsNull()) && !other.VipAddress.IsUnknown() { + v.VipAddress = other.VipAddress + } + + if (v.VipNetworkId.IsUnknown() || v.VipNetworkId.IsNull()) && !other.VipNetworkId.IsUnknown() { + v.VipNetworkId = other.VipNetworkId + } + + if (v.VipSubnetId.IsUnknown() || v.VipSubnetId.IsNull()) && !other.VipSubnetId.IsUnknown() { + v.VipSubnetId = other.VipSubnetId + } + +} + +var _ basetypes.ObjectTypable = FloatingIpType{} + +type FloatingIpType struct { + basetypes.ObjectType +} + +func (t FloatingIpType) Equal(o attr.Type) bool { + other, ok := o.(FloatingIpType) + + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +func (t FloatingIpType) String() string { + return "FloatingIpType" +} + +func (t FloatingIpType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { + var diags diag.Diagnostics + + attributes := in.Attributes() + + idAttribute, ok := attributes["id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `id is missing from object`) + + return nil, diags + } + + idVal, ok := idAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`id expected to be ovhtypes.TfStringValue, was: %T`, idAttribute)) + } + + ipAttribute, ok := attributes["ip"] + + if !ok { + diags.AddError( + "Attribute Missing", + `ip is missing from object`) + + return nil, diags + } + + ipVal, ok := ipAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`ip expected to be ovhtypes.TfStringValue, was: %T`, ipAttribute)) + } + + if diags.HasError() { + return nil, diags + } + + return FloatingIpValue{ + Id: idVal, + Ip: ipVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewFloatingIpValueNull() FloatingIpValue { + return FloatingIpValue{ + state: attr.ValueStateNull, + } +} + +func NewFloatingIpValueUnknown() FloatingIpValue { + return FloatingIpValue{ + state: attr.ValueStateUnknown, + } +} + +func NewFloatingIpValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (FloatingIpValue, diag.Diagnostics) { + var diags diag.Diagnostics + + // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 + ctx := context.Background() + + for name, attributeType := range attributeTypes { + attribute, ok := attributes[name] + + if !ok { + diags.AddError( + "Missing FloatingIpValue Attribute Value", + "While creating a FloatingIpValue value, a missing attribute value was detected. "+ + "A FloatingIpValue must contain values for all attributes, even if null or unknown. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("FloatingIpValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()), + ) + + continue + } + + if !attributeType.Equal(attribute.Type(ctx)) { + diags.AddError( + "Invalid FloatingIpValue Attribute Type", + "While creating a FloatingIpValue value, an invalid attribute value was detected. "+ + "A FloatingIpValue must use a matching attribute type for the value. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("FloatingIpValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ + fmt.Sprintf("FloatingIpValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), + ) + } + } + + for name := range attributes { + _, ok := attributeTypes[name] + + if !ok { + diags.AddError( + "Extra FloatingIpValue Attribute Value", + "While creating a FloatingIpValue value, an extra attribute value was detected. "+ + "A FloatingIpValue must not contain values beyond the expected attribute types. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("Extra FloatingIpValue Attribute Name: %s", name), + ) + } + } + + if diags.HasError() { + return NewFloatingIpValueUnknown(), diags + } + + idAttribute, ok := attributes["id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `id is missing from object`) + + return NewFloatingIpValueUnknown(), diags + } + + idVal, ok := idAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`id expected to be ovhtypes.TfStringValue, was: %T`, idAttribute)) + } + + ipAttribute, ok := attributes["ip"] + + if !ok { + diags.AddError( + "Attribute Missing", + `ip is missing from object`) + + return NewFloatingIpValueUnknown(), diags + } + + ipVal, ok := ipAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`ip expected to be ovhtypes.TfStringValue, was: %T`, ipAttribute)) + } + + if diags.HasError() { + return NewFloatingIpValueUnknown(), diags + } + + return FloatingIpValue{ + Id: idVal, + Ip: ipVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewFloatingIpValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) FloatingIpValue { + object, diags := NewFloatingIpValue(attributeTypes, attributes) + + if diags.HasError() { + // This could potentially be added to the diag package. + diagsStrings := make([]string, 0, len(diags)) + + for _, diagnostic := range diags { + diagsStrings = append(diagsStrings, fmt.Sprintf( + "%s | %s | %s", + diagnostic.Severity(), + diagnostic.Summary(), + diagnostic.Detail())) + } + + panic("NewFloatingIpValueMust received error(s): " + strings.Join(diagsStrings, "\n")) + } + + return object +} + +func (t FloatingIpType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + if in.Type() == nil { + return NewFloatingIpValueNull(), nil + } + + if !in.Type().Equal(t.TerraformType(ctx)) { + return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type()) + } + + if !in.IsKnown() { + return NewFloatingIpValueUnknown(), nil + } + + if in.IsNull() { + return NewFloatingIpValueNull(), nil + } + + attributes := map[string]attr.Value{} + + val := map[string]tftypes.Value{} + + err := in.As(&val) + + if err != nil { + return nil, err + } + + for k, v := range val { + a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v) + + if err != nil { + return nil, err + } + + attributes[k] = a + } + + return NewFloatingIpValueMust(FloatingIpValue{}.AttributeTypes(ctx), attributes), nil +} + +func (t FloatingIpType) ValueType(ctx context.Context) attr.Value { + return FloatingIpValue{} +} + +var _ basetypes.ObjectValuable = FloatingIpValue{} + +type FloatingIpValue struct { + Id ovhtypes.TfStringValue `tfsdk:"id" json:"id"` + Ip ovhtypes.TfStringValue `tfsdk:"ip" json:"ip"` + state attr.ValueState +} + +func (v *FloatingIpValue) UnmarshalJSON(data []byte) error { + type JsonFloatingIpValue FloatingIpValue + + var tmp JsonFloatingIpValue + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + v.Id = tmp.Id + v.Ip = tmp.Ip + + v.state = attr.ValueStateKnown + + return nil +} + +func (v *FloatingIpValue) MergeWith(other *FloatingIpValue) { + + if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() { + v.Id = other.Id + } + + if (v.Ip.IsUnknown() || v.Ip.IsNull()) && !other.Ip.IsUnknown() { + v.Ip = other.Ip + } + + if (v.state == attr.ValueStateUnknown || v.state == attr.ValueStateNull) && other.state != attr.ValueStateUnknown { + v.state = other.state + } +} + +func (v FloatingIpValue) Attributes() map[string]attr.Value { + return map[string]attr.Value{ + "id": v.Id, + "ip": v.Ip, + } +} +func (v FloatingIpValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { + attrTypes := make(map[string]tftypes.Type, 2) + + var val tftypes.Value + var err error + + attrTypes["id"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["ip"] = basetypes.StringType{}.TerraformType(ctx) + + objectType := tftypes.Object{AttributeTypes: attrTypes} + + switch v.state { + case attr.ValueStateKnown: + vals := make(map[string]tftypes.Value, 2) + + val, err = v.Id.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["id"] = val + + val, err = v.Ip.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["ip"] = val + + if err := tftypes.ValidateValue(objectType, vals); err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + return tftypes.NewValue(objectType, vals), nil + case attr.ValueStateNull: + return tftypes.NewValue(objectType, nil), nil + case attr.ValueStateUnknown: + return tftypes.NewValue(objectType, tftypes.UnknownValue), nil + default: + panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) + } +} + +func (v FloatingIpValue) IsNull() bool { + return v.state == attr.ValueStateNull +} + +func (v FloatingIpValue) IsUnknown() bool { + return v.state == attr.ValueStateUnknown +} + +func (v FloatingIpValue) String() string { + return "FloatingIpValue" +} + +func (v FloatingIpValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { + var diags diag.Diagnostics + + objVal, diags := types.ObjectValue( + map[string]attr.Type{ + "id": ovhtypes.TfStringType{}, + "ip": ovhtypes.TfStringType{}, + }, + map[string]attr.Value{ + "id": v.Id, + "ip": v.Ip, + }) + + return objVal, diags +} + +func (v FloatingIpValue) Equal(o attr.Value) bool { + other, ok := o.(FloatingIpValue) + + if !ok { + return false + } + + if v.state != other.state { + return false + } + + if v.state != attr.ValueStateKnown { + return true + } + + if !v.Id.Equal(other.Id) { + return false + } + + if !v.Ip.Equal(other.Ip) { + return false + } + + return true +} + +func (v FloatingIpValue) Type(ctx context.Context) attr.Type { + return FloatingIpType{ + basetypes.ObjectType{ + AttrTypes: v.AttributeTypes(ctx), + }, + } +} + +func (v FloatingIpValue) AttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "id": ovhtypes.TfStringType{}, + "ip": ovhtypes.TfStringType{}, + } +} diff --git a/ovh/data_cloud_project_loadbalancer_test.go b/ovh/data_cloud_project_loadbalancer_test.go new file mode 100644 index 000000000..a50f3fa00 --- /dev/null +++ b/ovh/data_cloud_project_loadbalancer_test.go @@ -0,0 +1,31 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccDataSourceCloudProjectBalancer_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckCloudRegionLoadbalancer(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + data "ovh_cloud_project_loadbalancer" "lb" { + service_name = "%s" + region_name = "%s" + id = "%s" + } + `, os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"), os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST"), os.Getenv("OVH_CLOUD_PROJECT_LOADBALANCER_TEST")), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.ovh_cloud_project_loadbalancer.lb", "region_name", os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST")), + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_loadbalancer.lb", "name"), + ), + }, + }, + }) +} diff --git a/ovh/data_cloud_project_loadbalancers.go b/ovh/data_cloud_project_loadbalancers.go new file mode 100644 index 000000000..4abd40ce2 --- /dev/null +++ b/ovh/data_cloud_project_loadbalancers.go @@ -0,0 +1,88 @@ +package ovh + +import ( + "context" + "fmt" + "net/url" + "os" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + ovhtypes "github.com/ovh/terraform-provider-ovh/ovh/types" +) + +var _ datasource.DataSourceWithConfigure = (*cloudProjectLoadbalancersDataSource)(nil) + +func NewCloudProjectLoadbalancersDataSource() datasource.DataSource { + return &cloudProjectLoadbalancersDataSource{} +} + +type cloudProjectLoadbalancersDataSource struct { + config *Config +} + +func (d *cloudProjectLoadbalancersDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_cloud_project_loadbalancers" +} + +func (d *cloudProjectLoadbalancersDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + if req.ProviderData == nil { + return + } + + config, ok := req.ProviderData.(*Config) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *Config, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + d.config = config +} + +func (d *cloudProjectLoadbalancersDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = CloudProjectLoadbalancersDataSourceSchema(ctx) +} + +func (d *cloudProjectLoadbalancersDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data CloudProjectLoadbalancersModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + if data.ServiceName.IsNull() { + data.ServiceName.StringValue = basetypes.NewStringValue(os.Getenv("OVH_CLOUD_PROJECT_SERVICE")) + } + + // Read API call logic + endpoint := fmt.Sprintf("/cloud/project/%s/region/%s/loadbalancing/loadbalancer", + url.PathEscape(data.ServiceName.ValueString()), + url.PathEscape(data.RegionName.ValueString()), + ) + + var arr []CloudProjectLoadbalancersValue + + if err := d.config.OVHClient.Get(endpoint, &arr); err != nil { + resp.Diagnostics.AddError("Failed to list loadbalancers", fmt.Sprintf("error calling GET %s: %s", endpoint, err)) + return + } + + var b []attr.Value + for _, a := range arr { + b = append(b, a) + } + + data.CloudProjectLoadbalancers = ovhtypes.TfListNestedValue[CloudProjectLoadbalancersValue]{ + ListValue: basetypes.NewListValueMust(CloudProjectLoadbalancersValue{}.Type(ctx), b), + } + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/ovh/data_cloud_project_loadbalancers_gen.go b/ovh/data_cloud_project_loadbalancers_gen.go new file mode 100644 index 000000000..d18834e95 --- /dev/null +++ b/ovh/data_cloud_project_loadbalancers_gen.go @@ -0,0 +1,1590 @@ +// Code generated by terraform-plugin-framework-generator DO NOT EDIT. + +package ovh + +import ( + "context" + "encoding/json" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-go/tftypes" + ovhtypes "github.com/ovh/terraform-provider-ovh/ovh/types" + "strings" + + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" +) + +func CloudProjectLoadbalancersDataSourceSchema(ctx context.Context) schema.Schema { + attrs := map[string]schema.Attribute{ + "loadbalancers": schema.SetNestedAttribute{ + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "created_at": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "The UTC date and timestamp when the loadbalancer was created", + MarkdownDescription: "The UTC date and timestamp when the loadbalancer was created", + }, + "flavor_id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "ID of the flavor", + MarkdownDescription: "ID of the flavor", + }, + "floating_ip": schema.SingleNestedAttribute{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "ID of the floating IP", + MarkdownDescription: "ID of the floating IP", + }, + "ip": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "IP Address of the floating IP", + MarkdownDescription: "IP Address of the floating IP", + }, + }, + CustomType: CloudProjectLoadbalancersFloatingIpType{ + ObjectType: types.ObjectType{ + AttrTypes: CloudProjectLoadbalancersFloatingIpValue{}.AttributeTypes(ctx), + }, + }, + Computed: true, + Description: "Information about floating IP", + MarkdownDescription: "Information about floating IP", + }, + "id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "ID of the loadbalancer", + MarkdownDescription: "ID of the loadbalancer", + }, + "name": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Name of the loadbalancer", + MarkdownDescription: "Name of the loadbalancer", + }, + "operating_status": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Operating status of the loadbalancer", + MarkdownDescription: "Operating status of the loadbalancer", + }, + "provisioning_status": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Provisioning status of the loadbalancer", + MarkdownDescription: "Provisioning status of the loadbalancer", + }, + "region": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Region of the loadbalancer", + MarkdownDescription: "Region of the loadbalancer", + }, + "updated_at": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "UTC date and timestamp when the loadbalancer was updated", + MarkdownDescription: "UTC date and timestamp when the loadbalancer was updated", + }, + "vip_address": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "IP address of the Virtual IP", + MarkdownDescription: "IP address of the Virtual IP", + }, + "vip_network_id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "Openstack ID of the network for the Virtual IP", + MarkdownDescription: "Openstack ID of the network for the Virtual IP", + }, + "vip_subnet_id": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Computed: true, + Description: "ID of the subnet for the Virtual IP", + MarkdownDescription: "ID of the subnet for the Virtual IP", + }, + }, + CustomType: CloudProjectLoadbalancersType{ + ObjectType: types.ObjectType{ + AttrTypes: CloudProjectLoadbalancersValue{}.AttributeTypes(ctx), + }, + }, + }, + CustomType: ovhtypes.NewTfListNestedType[CloudProjectLoadbalancersValue](ctx), + Computed: true, + }, + "region_name": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Required: true, + Description: "Region name", + MarkdownDescription: "Region name", + }, + "service_name": schema.StringAttribute{ + CustomType: ovhtypes.TfStringType{}, + Required: true, + Description: "Service name", + MarkdownDescription: "Service name", + }, + } + + return schema.Schema{ + Attributes: attrs, + } +} + +type CloudProjectLoadbalancersModel struct { + CloudProjectLoadbalancers ovhtypes.TfListNestedValue[CloudProjectLoadbalancersValue] `tfsdk:"loadbalancers" json:"cloudProjectLoadbalancers"` + RegionName ovhtypes.TfStringValue `tfsdk:"region_name" json:"regionName"` + ServiceName ovhtypes.TfStringValue `tfsdk:"service_name" json:"serviceName"` +} + +func (v *CloudProjectLoadbalancersModel) MergeWith(other *CloudProjectLoadbalancersModel) { + + if (v.CloudProjectLoadbalancers.IsUnknown() || v.CloudProjectLoadbalancers.IsNull()) && !other.CloudProjectLoadbalancers.IsUnknown() { + v.CloudProjectLoadbalancers = other.CloudProjectLoadbalancers + } + + if (v.RegionName.IsUnknown() || v.RegionName.IsNull()) && !other.RegionName.IsUnknown() { + v.RegionName = other.RegionName + } + + if (v.ServiceName.IsUnknown() || v.ServiceName.IsNull()) && !other.ServiceName.IsUnknown() { + v.ServiceName = other.ServiceName + } + +} + +var _ basetypes.ObjectTypable = CloudProjectLoadbalancersType{} + +type CloudProjectLoadbalancersType struct { + basetypes.ObjectType +} + +func (t CloudProjectLoadbalancersType) Equal(o attr.Type) bool { + other, ok := o.(CloudProjectLoadbalancersType) + + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +func (t CloudProjectLoadbalancersType) String() string { + return "CloudProjectLoadbalancersType" +} + +func (t CloudProjectLoadbalancersType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { + var diags diag.Diagnostics + + attributes := in.Attributes() + + createdAtAttribute, ok := attributes["created_at"] + + if !ok { + diags.AddError( + "Attribute Missing", + `created_at is missing from object`) + + return nil, diags + } + + createdAtVal, ok := createdAtAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`created_at expected to be ovhtypes.TfStringValue, was: %T`, createdAtAttribute)) + } + + flavorIdAttribute, ok := attributes["flavor_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `flavor_id is missing from object`) + + return nil, diags + } + + flavorIdVal, ok := flavorIdAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`flavor_id expected to be ovhtypes.TfStringValue, was: %T`, flavorIdAttribute)) + } + + floatingIpAttribute, ok := attributes["floating_ip"] + + if !ok { + diags.AddError( + "Attribute Missing", + `floating_ip is missing from object`) + + return nil, diags + } + + floatingIpVal, ok := floatingIpAttribute.(CloudProjectLoadbalancersFloatingIpValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`floating_ip expected to be CloudProjectLoadbalancersFloatingIpValue, was: %T`, floatingIpAttribute)) + } + + idAttribute, ok := attributes["id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `id is missing from object`) + + return nil, diags + } + + idVal, ok := idAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`id expected to be ovhtypes.TfStringValue, was: %T`, idAttribute)) + } + + nameAttribute, ok := attributes["name"] + + if !ok { + diags.AddError( + "Attribute Missing", + `name is missing from object`) + + return nil, diags + } + + nameVal, ok := nameAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`name expected to be ovhtypes.TfStringValue, was: %T`, nameAttribute)) + } + + operatingStatusAttribute, ok := attributes["operating_status"] + + if !ok { + diags.AddError( + "Attribute Missing", + `operating_status is missing from object`) + + return nil, diags + } + + operatingStatusVal, ok := operatingStatusAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`operating_status expected to be ovhtypes.TfStringValue, was: %T`, operatingStatusAttribute)) + } + + provisioningStatusAttribute, ok := attributes["provisioning_status"] + + if !ok { + diags.AddError( + "Attribute Missing", + `provisioning_status is missing from object`) + + return nil, diags + } + + provisioningStatusVal, ok := provisioningStatusAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`provisioning_status expected to be ovhtypes.TfStringValue, was: %T`, provisioningStatusAttribute)) + } + + regionAttribute, ok := attributes["region"] + + if !ok { + diags.AddError( + "Attribute Missing", + `region is missing from object`) + + return nil, diags + } + + regionVal, ok := regionAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`region expected to be ovhtypes.TfStringValue, was: %T`, regionAttribute)) + } + + updatedAtAttribute, ok := attributes["updated_at"] + + if !ok { + diags.AddError( + "Attribute Missing", + `updated_at is missing from object`) + + return nil, diags + } + + updatedAtVal, ok := updatedAtAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`updated_at expected to be ovhtypes.TfStringValue, was: %T`, updatedAtAttribute)) + } + + vipAddressAttribute, ok := attributes["vip_address"] + + if !ok { + diags.AddError( + "Attribute Missing", + `vip_address is missing from object`) + + return nil, diags + } + + vipAddressVal, ok := vipAddressAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`vip_address expected to be ovhtypes.TfStringValue, was: %T`, vipAddressAttribute)) + } + + vipNetworkIdAttribute, ok := attributes["vip_network_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `vip_network_id is missing from object`) + + return nil, diags + } + + vipNetworkIdVal, ok := vipNetworkIdAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`vip_network_id expected to be ovhtypes.TfStringValue, was: %T`, vipNetworkIdAttribute)) + } + + vipSubnetIdAttribute, ok := attributes["vip_subnet_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `vip_subnet_id is missing from object`) + + return nil, diags + } + + vipSubnetIdVal, ok := vipSubnetIdAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`vip_subnet_id expected to be ovhtypes.TfStringValue, was: %T`, vipSubnetIdAttribute)) + } + + if diags.HasError() { + return nil, diags + } + + return CloudProjectLoadbalancersValue{ + CreatedAt: createdAtVal, + FlavorId: flavorIdVal, + FloatingIp: floatingIpVal, + Id: idVal, + Name: nameVal, + OperatingStatus: operatingStatusVal, + ProvisioningStatus: provisioningStatusVal, + Region: regionVal, + UpdatedAt: updatedAtVal, + VipAddress: vipAddressVal, + VipNetworkId: vipNetworkIdVal, + VipSubnetId: vipSubnetIdVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewCloudProjectLoadbalancersValueNull() CloudProjectLoadbalancersValue { + return CloudProjectLoadbalancersValue{ + state: attr.ValueStateNull, + } +} + +func NewCloudProjectLoadbalancersValueUnknown() CloudProjectLoadbalancersValue { + return CloudProjectLoadbalancersValue{ + state: attr.ValueStateUnknown, + } +} + +func NewCloudProjectLoadbalancersValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (CloudProjectLoadbalancersValue, diag.Diagnostics) { + var diags diag.Diagnostics + + // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 + ctx := context.Background() + + for name, attributeType := range attributeTypes { + attribute, ok := attributes[name] + + if !ok { + diags.AddError( + "Missing CloudProjectLoadbalancersValue Attribute Value", + "While creating a CloudProjectLoadbalancersValue value, a missing attribute value was detected. "+ + "A CloudProjectLoadbalancersValue must contain values for all attributes, even if null or unknown. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("CloudProjectLoadbalancersValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()), + ) + + continue + } + + if !attributeType.Equal(attribute.Type(ctx)) { + diags.AddError( + "Invalid CloudProjectLoadbalancersValue Attribute Type", + "While creating a CloudProjectLoadbalancersValue value, an invalid attribute value was detected. "+ + "A CloudProjectLoadbalancersValue must use a matching attribute type for the value. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("CloudProjectLoadbalancersValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ + fmt.Sprintf("CloudProjectLoadbalancersValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), + ) + } + } + + for name := range attributes { + _, ok := attributeTypes[name] + + if !ok { + diags.AddError( + "Extra CloudProjectLoadbalancersValue Attribute Value", + "While creating a CloudProjectLoadbalancersValue value, an extra attribute value was detected. "+ + "A CloudProjectLoadbalancersValue must not contain values beyond the expected attribute types. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("Extra CloudProjectLoadbalancersValue Attribute Name: %s", name), + ) + } + } + + if diags.HasError() { + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + createdAtAttribute, ok := attributes["created_at"] + + if !ok { + diags.AddError( + "Attribute Missing", + `created_at is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + createdAtVal, ok := createdAtAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`created_at expected to be ovhtypes.TfStringValue, was: %T`, createdAtAttribute)) + } + + flavorIdAttribute, ok := attributes["flavor_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `flavor_id is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + flavorIdVal, ok := flavorIdAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`flavor_id expected to be ovhtypes.TfStringValue, was: %T`, flavorIdAttribute)) + } + + floatingIpAttribute, ok := attributes["floating_ip"] + + if !ok { + diags.AddError( + "Attribute Missing", + `floating_ip is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + floatingIpVal, ok := floatingIpAttribute.(CloudProjectLoadbalancersFloatingIpValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`floating_ip expected to be CloudProjectLoadbalancersFloatingIpValue, was: %T`, floatingIpAttribute)) + } + + idAttribute, ok := attributes["id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `id is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + idVal, ok := idAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`id expected to be ovhtypes.TfStringValue, was: %T`, idAttribute)) + } + + nameAttribute, ok := attributes["name"] + + if !ok { + diags.AddError( + "Attribute Missing", + `name is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + nameVal, ok := nameAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`name expected to be ovhtypes.TfStringValue, was: %T`, nameAttribute)) + } + + operatingStatusAttribute, ok := attributes["operating_status"] + + if !ok { + diags.AddError( + "Attribute Missing", + `operating_status is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + operatingStatusVal, ok := operatingStatusAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`operating_status expected to be ovhtypes.TfStringValue, was: %T`, operatingStatusAttribute)) + } + + provisioningStatusAttribute, ok := attributes["provisioning_status"] + + if !ok { + diags.AddError( + "Attribute Missing", + `provisioning_status is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + provisioningStatusVal, ok := provisioningStatusAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`provisioning_status expected to be ovhtypes.TfStringValue, was: %T`, provisioningStatusAttribute)) + } + + regionAttribute, ok := attributes["region"] + + if !ok { + diags.AddError( + "Attribute Missing", + `region is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + regionVal, ok := regionAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`region expected to be ovhtypes.TfStringValue, was: %T`, regionAttribute)) + } + + updatedAtAttribute, ok := attributes["updated_at"] + + if !ok { + diags.AddError( + "Attribute Missing", + `updated_at is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + updatedAtVal, ok := updatedAtAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`updated_at expected to be ovhtypes.TfStringValue, was: %T`, updatedAtAttribute)) + } + + vipAddressAttribute, ok := attributes["vip_address"] + + if !ok { + diags.AddError( + "Attribute Missing", + `vip_address is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + vipAddressVal, ok := vipAddressAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`vip_address expected to be ovhtypes.TfStringValue, was: %T`, vipAddressAttribute)) + } + + vipNetworkIdAttribute, ok := attributes["vip_network_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `vip_network_id is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + vipNetworkIdVal, ok := vipNetworkIdAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`vip_network_id expected to be ovhtypes.TfStringValue, was: %T`, vipNetworkIdAttribute)) + } + + vipSubnetIdAttribute, ok := attributes["vip_subnet_id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `vip_subnet_id is missing from object`) + + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + vipSubnetIdVal, ok := vipSubnetIdAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`vip_subnet_id expected to be ovhtypes.TfStringValue, was: %T`, vipSubnetIdAttribute)) + } + + if diags.HasError() { + return NewCloudProjectLoadbalancersValueUnknown(), diags + } + + return CloudProjectLoadbalancersValue{ + CreatedAt: createdAtVal, + FlavorId: flavorIdVal, + FloatingIp: floatingIpVal, + Id: idVal, + Name: nameVal, + OperatingStatus: operatingStatusVal, + ProvisioningStatus: provisioningStatusVal, + Region: regionVal, + UpdatedAt: updatedAtVal, + VipAddress: vipAddressVal, + VipNetworkId: vipNetworkIdVal, + VipSubnetId: vipSubnetIdVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewCloudProjectLoadbalancersValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) CloudProjectLoadbalancersValue { + object, diags := NewCloudProjectLoadbalancersValue(attributeTypes, attributes) + + if diags.HasError() { + // This could potentially be added to the diag package. + diagsStrings := make([]string, 0, len(diags)) + + for _, diagnostic := range diags { + diagsStrings = append(diagsStrings, fmt.Sprintf( + "%s | %s | %s", + diagnostic.Severity(), + diagnostic.Summary(), + diagnostic.Detail())) + } + + panic("NewCloudProjectLoadbalancersValueMust received error(s): " + strings.Join(diagsStrings, "\n")) + } + + return object +} + +func (t CloudProjectLoadbalancersType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + if in.Type() == nil { + return NewCloudProjectLoadbalancersValueNull(), nil + } + + if !in.Type().Equal(t.TerraformType(ctx)) { + return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type()) + } + + if !in.IsKnown() { + return NewCloudProjectLoadbalancersValueUnknown(), nil + } + + if in.IsNull() { + return NewCloudProjectLoadbalancersValueNull(), nil + } + + attributes := map[string]attr.Value{} + + val := map[string]tftypes.Value{} + + err := in.As(&val) + + if err != nil { + return nil, err + } + + for k, v := range val { + a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v) + + if err != nil { + return nil, err + } + + attributes[k] = a + } + + return NewCloudProjectLoadbalancersValueMust(CloudProjectLoadbalancersValue{}.AttributeTypes(ctx), attributes), nil +} + +func (t CloudProjectLoadbalancersType) ValueType(ctx context.Context) attr.Value { + return CloudProjectLoadbalancersValue{} +} + +var _ basetypes.ObjectValuable = CloudProjectLoadbalancersValue{} + +type CloudProjectLoadbalancersValue struct { + CreatedAt ovhtypes.TfStringValue `tfsdk:"created_at" json:"createdAt"` + FlavorId ovhtypes.TfStringValue `tfsdk:"flavor_id" json:"flavorId"` + FloatingIp CloudProjectLoadbalancersFloatingIpValue `tfsdk:"floating_ip" json:"floatingIp"` + Id ovhtypes.TfStringValue `tfsdk:"id" json:"id"` + Name ovhtypes.TfStringValue `tfsdk:"name" json:"name"` + OperatingStatus ovhtypes.TfStringValue `tfsdk:"operating_status" json:"operatingStatus"` + ProvisioningStatus ovhtypes.TfStringValue `tfsdk:"provisioning_status" json:"provisioningStatus"` + Region ovhtypes.TfStringValue `tfsdk:"region" json:"region"` + UpdatedAt ovhtypes.TfStringValue `tfsdk:"updated_at" json:"updatedAt"` + VipAddress ovhtypes.TfStringValue `tfsdk:"vip_address" json:"vipAddress"` + VipNetworkId ovhtypes.TfStringValue `tfsdk:"vip_network_id" json:"vipNetworkId"` + VipSubnetId ovhtypes.TfStringValue `tfsdk:"vip_subnet_id" json:"vipSubnetId"` + state attr.ValueState +} + +func (v *CloudProjectLoadbalancersValue) UnmarshalJSON(data []byte) error { + type JsonCloudProjectLoadbalancersValue CloudProjectLoadbalancersValue + + var tmp JsonCloudProjectLoadbalancersValue + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + v.CreatedAt = tmp.CreatedAt + v.FlavorId = tmp.FlavorId + v.FloatingIp = tmp.FloatingIp + v.Id = tmp.Id + v.Name = tmp.Name + v.OperatingStatus = tmp.OperatingStatus + v.ProvisioningStatus = tmp.ProvisioningStatus + v.Region = tmp.Region + v.UpdatedAt = tmp.UpdatedAt + v.VipAddress = tmp.VipAddress + v.VipNetworkId = tmp.VipNetworkId + v.VipSubnetId = tmp.VipSubnetId + + v.state = attr.ValueStateKnown + + return nil +} + +func (v *CloudProjectLoadbalancersValue) MergeWith(other *CloudProjectLoadbalancersValue) { + + if (v.CreatedAt.IsUnknown() || v.CreatedAt.IsNull()) && !other.CreatedAt.IsUnknown() { + v.CreatedAt = other.CreatedAt + } + + if (v.FlavorId.IsUnknown() || v.FlavorId.IsNull()) && !other.FlavorId.IsUnknown() { + v.FlavorId = other.FlavorId + } + + if (v.FloatingIp.IsUnknown() || v.FloatingIp.IsNull()) && !other.FloatingIp.IsUnknown() { + v.FloatingIp = other.FloatingIp + } + + if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() { + v.Id = other.Id + } + + if (v.Name.IsUnknown() || v.Name.IsNull()) && !other.Name.IsUnknown() { + v.Name = other.Name + } + + if (v.OperatingStatus.IsUnknown() || v.OperatingStatus.IsNull()) && !other.OperatingStatus.IsUnknown() { + v.OperatingStatus = other.OperatingStatus + } + + if (v.ProvisioningStatus.IsUnknown() || v.ProvisioningStatus.IsNull()) && !other.ProvisioningStatus.IsUnknown() { + v.ProvisioningStatus = other.ProvisioningStatus + } + + if (v.Region.IsUnknown() || v.Region.IsNull()) && !other.Region.IsUnknown() { + v.Region = other.Region + } + + if (v.UpdatedAt.IsUnknown() || v.UpdatedAt.IsNull()) && !other.UpdatedAt.IsUnknown() { + v.UpdatedAt = other.UpdatedAt + } + + if (v.VipAddress.IsUnknown() || v.VipAddress.IsNull()) && !other.VipAddress.IsUnknown() { + v.VipAddress = other.VipAddress + } + + if (v.VipNetworkId.IsUnknown() || v.VipNetworkId.IsNull()) && !other.VipNetworkId.IsUnknown() { + v.VipNetworkId = other.VipNetworkId + } + + if (v.VipSubnetId.IsUnknown() || v.VipSubnetId.IsNull()) && !other.VipSubnetId.IsUnknown() { + v.VipSubnetId = other.VipSubnetId + } + + if (v.state == attr.ValueStateUnknown || v.state == attr.ValueStateNull) && other.state != attr.ValueStateUnknown { + v.state = other.state + } +} + +func (v CloudProjectLoadbalancersValue) Attributes() map[string]attr.Value { + return map[string]attr.Value{ + "createdAt": v.CreatedAt, + "flavorId": v.FlavorId, + "floatingIp": v.FloatingIp, + "id": v.Id, + "name": v.Name, + "operatingStatus": v.OperatingStatus, + "provisioningStatus": v.ProvisioningStatus, + "region": v.Region, + "updatedAt": v.UpdatedAt, + "vipAddress": v.VipAddress, + "vipNetworkId": v.VipNetworkId, + "vipSubnetId": v.VipSubnetId, + } +} +func (v CloudProjectLoadbalancersValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { + attrTypes := make(map[string]tftypes.Type, 12) + + var val tftypes.Value + var err error + + attrTypes["created_at"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["flavor_id"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["floating_ip"] = basetypes.ObjectType{ + AttrTypes: CloudProjectLoadbalancersFloatingIpValue{}.AttributeTypes(ctx), + }.TerraformType(ctx) + attrTypes["id"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["name"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["operating_status"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["provisioning_status"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["region"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["updated_at"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["vip_address"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["vip_network_id"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["vip_subnet_id"] = basetypes.StringType{}.TerraformType(ctx) + + objectType := tftypes.Object{AttributeTypes: attrTypes} + + switch v.state { + case attr.ValueStateKnown: + vals := make(map[string]tftypes.Value, 12) + + val, err = v.CreatedAt.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["created_at"] = val + + val, err = v.FlavorId.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["flavor_id"] = val + + val, err = v.FloatingIp.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["floating_ip"] = val + + val, err = v.Id.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["id"] = val + + val, err = v.Name.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["name"] = val + + val, err = v.OperatingStatus.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["operating_status"] = val + + val, err = v.ProvisioningStatus.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["provisioning_status"] = val + + val, err = v.Region.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["region"] = val + + val, err = v.UpdatedAt.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["updated_at"] = val + + val, err = v.VipAddress.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["vip_address"] = val + + val, err = v.VipNetworkId.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["vip_network_id"] = val + + val, err = v.VipSubnetId.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["vip_subnet_id"] = val + + if err := tftypes.ValidateValue(objectType, vals); err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + return tftypes.NewValue(objectType, vals), nil + case attr.ValueStateNull: + return tftypes.NewValue(objectType, nil), nil + case attr.ValueStateUnknown: + return tftypes.NewValue(objectType, tftypes.UnknownValue), nil + default: + panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) + } +} + +func (v CloudProjectLoadbalancersValue) IsNull() bool { + return v.state == attr.ValueStateNull +} + +func (v CloudProjectLoadbalancersValue) IsUnknown() bool { + return v.state == attr.ValueStateUnknown +} + +func (v CloudProjectLoadbalancersValue) String() string { + return "CloudProjectLoadbalancersValue" +} + +func (v CloudProjectLoadbalancersValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { + var diags diag.Diagnostics + + objVal, diags := types.ObjectValue( + map[string]attr.Type{ + "created_at": ovhtypes.TfStringType{}, + "flavor_id": ovhtypes.TfStringType{}, + "floating_ip": CloudProjectLoadbalancersFloatingIpType{ + basetypes.ObjectType{ + AttrTypes: CloudProjectLoadbalancersFloatingIpValue{}.AttributeTypes(ctx), + }, + }, + "id": ovhtypes.TfStringType{}, + "name": ovhtypes.TfStringType{}, + "operating_status": ovhtypes.TfStringType{}, + "provisioning_status": ovhtypes.TfStringType{}, + "region": ovhtypes.TfStringType{}, + "updated_at": ovhtypes.TfStringType{}, + "vip_address": ovhtypes.TfStringType{}, + "vip_network_id": ovhtypes.TfStringType{}, + "vip_subnet_id": ovhtypes.TfStringType{}, + }, + map[string]attr.Value{ + "created_at": v.CreatedAt, + "flavor_id": v.FlavorId, + "floating_ip": v.FloatingIp, + "id": v.Id, + "name": v.Name, + "operating_status": v.OperatingStatus, + "provisioning_status": v.ProvisioningStatus, + "region": v.Region, + "updated_at": v.UpdatedAt, + "vip_address": v.VipAddress, + "vip_network_id": v.VipNetworkId, + "vip_subnet_id": v.VipSubnetId, + }) + + return objVal, diags +} + +func (v CloudProjectLoadbalancersValue) Equal(o attr.Value) bool { + other, ok := o.(CloudProjectLoadbalancersValue) + + if !ok { + return false + } + + if v.state != other.state { + return false + } + + if v.state != attr.ValueStateKnown { + return true + } + + if !v.CreatedAt.Equal(other.CreatedAt) { + return false + } + + if !v.FlavorId.Equal(other.FlavorId) { + return false + } + + if !v.FloatingIp.Equal(other.FloatingIp) { + return false + } + + if !v.Id.Equal(other.Id) { + return false + } + + if !v.Name.Equal(other.Name) { + return false + } + + if !v.OperatingStatus.Equal(other.OperatingStatus) { + return false + } + + if !v.ProvisioningStatus.Equal(other.ProvisioningStatus) { + return false + } + + if !v.Region.Equal(other.Region) { + return false + } + + if !v.UpdatedAt.Equal(other.UpdatedAt) { + return false + } + + if !v.VipAddress.Equal(other.VipAddress) { + return false + } + + if !v.VipNetworkId.Equal(other.VipNetworkId) { + return false + } + + if !v.VipSubnetId.Equal(other.VipSubnetId) { + return false + } + + return true +} + +func (v CloudProjectLoadbalancersValue) Type(ctx context.Context) attr.Type { + return CloudProjectLoadbalancersType{ + basetypes.ObjectType{ + AttrTypes: v.AttributeTypes(ctx), + }, + } +} + +func (v CloudProjectLoadbalancersValue) AttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "created_at": ovhtypes.TfStringType{}, + "flavor_id": ovhtypes.TfStringType{}, + "floating_ip": CloudProjectLoadbalancersFloatingIpValue{}.Type(ctx), + "id": ovhtypes.TfStringType{}, + "name": ovhtypes.TfStringType{}, + "operating_status": ovhtypes.TfStringType{}, + "provisioning_status": ovhtypes.TfStringType{}, + "region": ovhtypes.TfStringType{}, + "updated_at": ovhtypes.TfStringType{}, + "vip_address": ovhtypes.TfStringType{}, + "vip_network_id": ovhtypes.TfStringType{}, + "vip_subnet_id": ovhtypes.TfStringType{}, + } +} + +var _ basetypes.ObjectTypable = CloudProjectLoadbalancersFloatingIpType{} + +type CloudProjectLoadbalancersFloatingIpType struct { + basetypes.ObjectType +} + +func (t CloudProjectLoadbalancersFloatingIpType) Equal(o attr.Type) bool { + other, ok := o.(CloudProjectLoadbalancersFloatingIpType) + + if !ok { + return false + } + + return t.ObjectType.Equal(other.ObjectType) +} + +func (t CloudProjectLoadbalancersFloatingIpType) String() string { + return "CloudProjectLoadbalancersFloatingIpType" +} + +func (t CloudProjectLoadbalancersFloatingIpType) ValueFromObject(ctx context.Context, in basetypes.ObjectValue) (basetypes.ObjectValuable, diag.Diagnostics) { + var diags diag.Diagnostics + + attributes := in.Attributes() + + idAttribute, ok := attributes["id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `id is missing from object`) + + return nil, diags + } + + idVal, ok := idAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`id expected to be ovhtypes.TfStringValue, was: %T`, idAttribute)) + } + + ipAttribute, ok := attributes["ip"] + + if !ok { + diags.AddError( + "Attribute Missing", + `ip is missing from object`) + + return nil, diags + } + + ipVal, ok := ipAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`ip expected to be ovhtypes.TfStringValue, was: %T`, ipAttribute)) + } + + if diags.HasError() { + return nil, diags + } + + return CloudProjectLoadbalancersFloatingIpValue{ + Id: idVal, + Ip: ipVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewCloudProjectLoadbalancersFloatingIpValueNull() CloudProjectLoadbalancersFloatingIpValue { + return CloudProjectLoadbalancersFloatingIpValue{ + state: attr.ValueStateNull, + } +} + +func NewCloudProjectLoadbalancersFloatingIpValueUnknown() CloudProjectLoadbalancersFloatingIpValue { + return CloudProjectLoadbalancersFloatingIpValue{ + state: attr.ValueStateUnknown, + } +} + +func NewCloudProjectLoadbalancersFloatingIpValue(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) (CloudProjectLoadbalancersFloatingIpValue, diag.Diagnostics) { + var diags diag.Diagnostics + + // Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/521 + ctx := context.Background() + + for name, attributeType := range attributeTypes { + attribute, ok := attributes[name] + + if !ok { + diags.AddError( + "Missing CloudProjectLoadbalancersFloatingIpValue Attribute Value", + "While creating a CloudProjectLoadbalancersFloatingIpValue value, a missing attribute value was detected. "+ + "A CloudProjectLoadbalancersFloatingIpValue must contain values for all attributes, even if null or unknown. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("CloudProjectLoadbalancersFloatingIpValue Attribute Name (%s) Expected Type: %s", name, attributeType.String()), + ) + + continue + } + + if !attributeType.Equal(attribute.Type(ctx)) { + diags.AddError( + "Invalid CloudProjectLoadbalancersFloatingIpValue Attribute Type", + "While creating a CloudProjectLoadbalancersFloatingIpValue value, an invalid attribute value was detected. "+ + "A CloudProjectLoadbalancersFloatingIpValue must use a matching attribute type for the value. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("CloudProjectLoadbalancersFloatingIpValue Attribute Name (%s) Expected Type: %s\n", name, attributeType.String())+ + fmt.Sprintf("CloudProjectLoadbalancersFloatingIpValue Attribute Name (%s) Given Type: %s", name, attribute.Type(ctx)), + ) + } + } + + for name := range attributes { + _, ok := attributeTypes[name] + + if !ok { + diags.AddError( + "Extra CloudProjectLoadbalancersFloatingIpValue Attribute Value", + "While creating a CloudProjectLoadbalancersFloatingIpValue value, an extra attribute value was detected. "+ + "A CloudProjectLoadbalancersFloatingIpValue must not contain values beyond the expected attribute types. "+ + "This is always an issue with the provider and should be reported to the provider developers.\n\n"+ + fmt.Sprintf("Extra CloudProjectLoadbalancersFloatingIpValue Attribute Name: %s", name), + ) + } + } + + if diags.HasError() { + return NewCloudProjectLoadbalancersFloatingIpValueUnknown(), diags + } + + idAttribute, ok := attributes["id"] + + if !ok { + diags.AddError( + "Attribute Missing", + `id is missing from object`) + + return NewCloudProjectLoadbalancersFloatingIpValueUnknown(), diags + } + + idVal, ok := idAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`id expected to be ovhtypes.TfStringValue, was: %T`, idAttribute)) + } + + ipAttribute, ok := attributes["ip"] + + if !ok { + diags.AddError( + "Attribute Missing", + `ip is missing from object`) + + return NewCloudProjectLoadbalancersFloatingIpValueUnknown(), diags + } + + ipVal, ok := ipAttribute.(ovhtypes.TfStringValue) + + if !ok { + diags.AddError( + "Attribute Wrong Type", + fmt.Sprintf(`ip expected to be ovhtypes.TfStringValue, was: %T`, ipAttribute)) + } + + if diags.HasError() { + return NewCloudProjectLoadbalancersFloatingIpValueUnknown(), diags + } + + return CloudProjectLoadbalancersFloatingIpValue{ + Id: idVal, + Ip: ipVal, + state: attr.ValueStateKnown, + }, diags +} + +func NewCloudProjectLoadbalancersFloatingIpValueMust(attributeTypes map[string]attr.Type, attributes map[string]attr.Value) CloudProjectLoadbalancersFloatingIpValue { + object, diags := NewCloudProjectLoadbalancersFloatingIpValue(attributeTypes, attributes) + + if diags.HasError() { + // This could potentially be added to the diag package. + diagsStrings := make([]string, 0, len(diags)) + + for _, diagnostic := range diags { + diagsStrings = append(diagsStrings, fmt.Sprintf( + "%s | %s | %s", + diagnostic.Severity(), + diagnostic.Summary(), + diagnostic.Detail())) + } + + panic("NewCloudProjectLoadbalancersFloatingIpValueMust received error(s): " + strings.Join(diagsStrings, "\n")) + } + + return object +} + +func (t CloudProjectLoadbalancersFloatingIpType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { + if in.Type() == nil { + return NewCloudProjectLoadbalancersFloatingIpValueNull(), nil + } + + if !in.Type().Equal(t.TerraformType(ctx)) { + return nil, fmt.Errorf("expected %s, got %s", t.TerraformType(ctx), in.Type()) + } + + if !in.IsKnown() { + return NewCloudProjectLoadbalancersFloatingIpValueUnknown(), nil + } + + if in.IsNull() { + return NewCloudProjectLoadbalancersFloatingIpValueNull(), nil + } + + attributes := map[string]attr.Value{} + + val := map[string]tftypes.Value{} + + err := in.As(&val) + + if err != nil { + return nil, err + } + + for k, v := range val { + a, err := t.AttrTypes[k].ValueFromTerraform(ctx, v) + + if err != nil { + return nil, err + } + + attributes[k] = a + } + + return NewCloudProjectLoadbalancersFloatingIpValueMust(CloudProjectLoadbalancersFloatingIpValue{}.AttributeTypes(ctx), attributes), nil +} + +func (t CloudProjectLoadbalancersFloatingIpType) ValueType(ctx context.Context) attr.Value { + return CloudProjectLoadbalancersFloatingIpValue{} +} + +var _ basetypes.ObjectValuable = CloudProjectLoadbalancersFloatingIpValue{} + +type CloudProjectLoadbalancersFloatingIpValue struct { + Id ovhtypes.TfStringValue `tfsdk:"id" json:"id"` + Ip ovhtypes.TfStringValue `tfsdk:"ip" json:"ip"` + state attr.ValueState +} + +func (v *CloudProjectLoadbalancersFloatingIpValue) UnmarshalJSON(data []byte) error { + type JsonCloudProjectLoadbalancersFloatingIpValue CloudProjectLoadbalancersFloatingIpValue + + var tmp JsonCloudProjectLoadbalancersFloatingIpValue + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + v.Id = tmp.Id + v.Ip = tmp.Ip + + v.state = attr.ValueStateKnown + + return nil +} + +func (v *CloudProjectLoadbalancersFloatingIpValue) MergeWith(other *CloudProjectLoadbalancersFloatingIpValue) { + + if (v.Id.IsUnknown() || v.Id.IsNull()) && !other.Id.IsUnknown() { + v.Id = other.Id + } + + if (v.Ip.IsUnknown() || v.Ip.IsNull()) && !other.Ip.IsUnknown() { + v.Ip = other.Ip + } + + if (v.state == attr.ValueStateUnknown || v.state == attr.ValueStateNull) && other.state != attr.ValueStateUnknown { + v.state = other.state + } +} + +func (v CloudProjectLoadbalancersFloatingIpValue) Attributes() map[string]attr.Value { + return map[string]attr.Value{ + "id": v.Id, + "ip": v.Ip, + } +} +func (v CloudProjectLoadbalancersFloatingIpValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) { + attrTypes := make(map[string]tftypes.Type, 2) + + var val tftypes.Value + var err error + + attrTypes["id"] = basetypes.StringType{}.TerraformType(ctx) + attrTypes["ip"] = basetypes.StringType{}.TerraformType(ctx) + + objectType := tftypes.Object{AttributeTypes: attrTypes} + + switch v.state { + case attr.ValueStateKnown: + vals := make(map[string]tftypes.Value, 2) + + val, err = v.Id.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["id"] = val + + val, err = v.Ip.ToTerraformValue(ctx) + + if err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + vals["ip"] = val + + if err := tftypes.ValidateValue(objectType, vals); err != nil { + return tftypes.NewValue(objectType, tftypes.UnknownValue), err + } + + return tftypes.NewValue(objectType, vals), nil + case attr.ValueStateNull: + return tftypes.NewValue(objectType, nil), nil + case attr.ValueStateUnknown: + return tftypes.NewValue(objectType, tftypes.UnknownValue), nil + default: + panic(fmt.Sprintf("unhandled Object state in ToTerraformValue: %s", v.state)) + } +} + +func (v CloudProjectLoadbalancersFloatingIpValue) IsNull() bool { + return v.state == attr.ValueStateNull +} + +func (v CloudProjectLoadbalancersFloatingIpValue) IsUnknown() bool { + return v.state == attr.ValueStateUnknown +} + +func (v CloudProjectLoadbalancersFloatingIpValue) String() string { + return "CloudProjectLoadbalancersFloatingIpValue" +} + +func (v CloudProjectLoadbalancersFloatingIpValue) ToObjectValue(ctx context.Context) (basetypes.ObjectValue, diag.Diagnostics) { + var diags diag.Diagnostics + + objVal, diags := types.ObjectValue( + map[string]attr.Type{ + "id": ovhtypes.TfStringType{}, + "ip": ovhtypes.TfStringType{}, + }, + map[string]attr.Value{ + "id": v.Id, + "ip": v.Ip, + }) + + return objVal, diags +} + +func (v CloudProjectLoadbalancersFloatingIpValue) Equal(o attr.Value) bool { + other, ok := o.(CloudProjectLoadbalancersFloatingIpValue) + + if !ok { + return false + } + + if v.state != other.state { + return false + } + + if v.state != attr.ValueStateKnown { + return true + } + + if !v.Id.Equal(other.Id) { + return false + } + + if !v.Ip.Equal(other.Ip) { + return false + } + + return true +} + +func (v CloudProjectLoadbalancersFloatingIpValue) Type(ctx context.Context) attr.Type { + return CloudProjectLoadbalancersFloatingIpType{ + basetypes.ObjectType{ + AttrTypes: v.AttributeTypes(ctx), + }, + } +} + +func (v CloudProjectLoadbalancersFloatingIpValue) AttributeTypes(ctx context.Context) map[string]attr.Type { + return map[string]attr.Type{ + "id": ovhtypes.TfStringType{}, + "ip": ovhtypes.TfStringType{}, + } +} diff --git a/ovh/data_cloud_project_loadbalancers_test.go b/ovh/data_cloud_project_loadbalancers_test.go new file mode 100644 index 000000000..22dffc223 --- /dev/null +++ b/ovh/data_cloud_project_loadbalancers_test.go @@ -0,0 +1,30 @@ +package ovh + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccDataSourceCloudProjectBalancers_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckCloudRegion(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + data "ovh_cloud_project_loadbalancers" "lbs" { + service_name = "%s" + region_name = "%s" + } + `, os.Getenv("OVH_CLOUD_PROJECT_SERVICE_TEST"), os.Getenv("OVH_CLOUD_PROJECT_REGION_TEST")), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_loadbalancers.lbs", "loadbalancers.0.name"), + resource.TestCheckResourceAttrSet("data.ovh_cloud_project_loadbalancers.lbs", "loadbalancers.0.vip_address"), + ), + }, + }, + }) +} diff --git a/ovh/provider_new.go b/ovh/provider_new.go index 85fd60b1d..662f92255 100644 --- a/ovh/provider_new.go +++ b/ovh/provider_new.go @@ -173,6 +173,8 @@ func (p *OvhProvider) Configure(ctx context.Context, req provider.ConfigureReque func (p *OvhProvider) DataSources(_ context.Context) []func() datasource.DataSource { return []func() datasource.DataSource{ NewCloudProjectDatabaseIPRestrictionsDataSource, + NewCloudProjectLoadbalancerDataSource, + NewCloudProjectLoadbalancersDataSource, NewCloudProjectDataSource, NewCloudProjectsDataSource, NewDedicatedServerSpecificationsHardwareDataSource, diff --git a/ovh/provider_test.go b/ovh/provider_test.go index e3dab8a0e..9fc6daf4f 100644 --- a/ovh/provider_test.go +++ b/ovh/provider_test.go @@ -273,6 +273,17 @@ func testAccPreCheckCloudDatabaseIpRestriction(t *testing.T) { checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_DATABASE_IP_RESTRICTION_IP_TEST") } +func testAccPreCheckCloudRegion(t *testing.T) { + testAccPreCheckCloud(t) + testAccCheckCloudProjectExists(t) + checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_REGION_TEST") +} + +func testAccPreCheckCloudRegionLoadbalancer(t *testing.T) { + testAccPreCheckCloudRegion(t) + checkEnvOrSkip(t, "OVH_CLOUD_PROJECT_LOADBALANCER_TEST") +} + // Checks that the environment variables needed for the /cloud/project/{projectId}/ip/failover acceptance tests // are set. func testAccPreCheckFailoverIpAttach(t *testing.T) { diff --git a/website/docs/d/cloud_project_loadbalancer.html.markdown b/website/docs/d/cloud_project_loadbalancer.html.markdown new file mode 100644 index 000000000..95cdd68dc --- /dev/null +++ b/website/docs/d/cloud_project_loadbalancer.html.markdown @@ -0,0 +1,51 @@ +--- +subcategory : "Public Cloud Network" +--- + +# ovh_cloud_project_loadbalancer + +Get the details of a public cloud project loadbalancer. + +## Example Usage + +```hcl +data "ovh_cloud_project_loadbalancer" "lb" { + service_name = "XXXXXX" + region_name = "XXX" + id = "XXX" +} +output "lb" { + value = data.ovh_cloud_project_loadbalancer.lb +} +``` + +## Argument Reference + +The following arguments are supported: + +- `service_name` - (Required) The ID of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. + +- `region_name` - (Required) Region of the loadbalancer. + +- `id`: (Required) ID of the loadbalancer + +## Attributes Reference + +The following attributes are exported: + +- `service_name` - ID of the public cloud project +- `region_name` - Region of the loadbalancer +- `id`: ID of the loadbalancer +- `flavor_id`: ID of the flavor +- `name`: Name of the loadbalancer +- `created_at` - Date of creation of the loadbalancer +- `updated_at` - Last update date of the loadbalancer +- `operating_status`: Operating status of the loadbalancer +- `provisioning_status`: Provisioning status of the loadbalancer +- `vip_address`: IP address of the Virtual IP +- `vip_network_id`: Openstack ID of the network for the Virtual IP +- `vip_subnet_id`: ID of the subnet for the Virtual IP +- `floating_ip`: Information about the floating IP + - `id`: ID of the floating IP + - `ip`: Value of the floating IP \ No newline at end of file diff --git a/website/docs/d/cloud_project_loadbalancers.html.markdown b/website/docs/d/cloud_project_loadbalancers.html.markdown new file mode 100644 index 000000000..f425d469d --- /dev/null +++ b/website/docs/d/cloud_project_loadbalancers.html.markdown @@ -0,0 +1,50 @@ +--- +subcategory : "Public Cloud Network" +--- + +# ovh_cloud_project_loadbalancers + +List your public cloud loadbalancers. + +## Example Usage + +```hcl +data "ovh_cloud_project_loadbalancers" "lbs" { + service_name = "XXXXXX" + region_name = "XXX" +} +output "lbs" { + value = data.ovh_cloud_project_loadbalancers.lbs +} +``` + +## Argument Reference + +The following arguments are supported: + +- `service_name` - (Required) The ID of the public cloud project. If omitted, + the `OVH_CLOUD_PROJECT_SERVICE` environment variable is used. + +- `region_name` - (Required) Region of the loadbalancers. + +## Attributes Reference + +The following attributes are exported: + +- `service_name` - ID of the public cloud project +- `region_name` - Region of the loadbalancers +- `loadbalancers` - List of loadbalancer + - `id`: ID of the loadbalancer + - `flavor_id`: ID of the flavor + - `name`: Name of the loadbalancer + - `region` - Region of the loadbalancer + - `created_at` - Date of creation of the loadbalancer + - `updated_at` - Last update date of the loadbalancer + - `operating_status`: Operating status of the loadbalancer + - `provisioning_status`: Provisioning status of the loadbalancer + - `vip_address`: IP address of the Virtual IP + - `vip_network_id`: Openstack ID of the network for the Virtual IP + - `vip_subnet_id`: ID of the subnet for the Virtual IP + - `floating_ip`: Information about the floating IP + - `id`: ID of the floating IP + - `ip`: Value of the floating IP \ No newline at end of file