From 43f6d85c9db361ce5d0a0df933869cece694e132 Mon Sep 17 00:00:00 2001 From: Tim Hogarty Date: Tue, 6 Aug 2024 15:40:04 -0700 Subject: [PATCH] Update schema to handle computed values for non-set config items marked as optional or computed --- .../framework/types/set_nested_objectof.go | 216 ----------------- internal/framework/types/setof.go | 136 ----------- .../fabric/precision_time/datasources.go | 2 +- .../precision_time/datasources_schema.go | 124 ++-------- .../resources/fabric/precision_time/models.go | 228 ++++++++++-------- .../fabric/precision_time/resource.go | 216 +++++++++-------- .../fabric/precision_time/resource_schema.go | 165 ++++--------- 7 files changed, 315 insertions(+), 772 deletions(-) delete mode 100644 internal/framework/types/set_nested_objectof.go delete mode 100644 internal/framework/types/setof.go diff --git a/internal/framework/types/set_nested_objectof.go b/internal/framework/types/set_nested_objectof.go deleted file mode 100644 index 28bd82dfb..000000000 --- a/internal/framework/types/set_nested_objectof.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package types - -import ( - "context" - "fmt" - equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" - - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -var ( - _ basetypes.SetTypable = (*setNestedObjectTypeOf[struct{}])(nil) - _ NestedObjectCollectionType = (*setNestedObjectTypeOf[struct{}])(nil) - _ basetypes.SetValuable = (*SetNestedObjectValueOf[struct{}])(nil) - _ NestedObjectCollectionValue = (*SetNestedObjectValueOf[struct{}])(nil) -) - -// setNestedObjectTypeOf is the attribute type of a SetNestedObjectValueOf. -type setNestedObjectTypeOf[T any] struct { - basetypes.SetType -} - -func NewSetNestedObjectTypeOf[T any](ctx context.Context) setNestedObjectTypeOf[T] { - return setNestedObjectTypeOf[T]{basetypes.SetType{ElemType: NewObjectTypeOf[T](ctx)}} -} - -func (t setNestedObjectTypeOf[T]) Equal(o attr.Type) bool { - other, ok := o.(setNestedObjectTypeOf[T]) - - if !ok { - return false - } - - return t.SetType.Equal(other.SetType) -} - -func (t setNestedObjectTypeOf[T]) String() string { - var zero T - return fmt.Sprintf("SetNestedObjectTypeOf[%T]", zero) -} - -func (t setNestedObjectTypeOf[T]) ValueFromSet(ctx context.Context, in basetypes.SetValue) (basetypes.SetValuable, diag.Diagnostics) { - var diags diag.Diagnostics - - if in.IsNull() { - return NewSetNestedObjectValueOfNull[T](ctx), diags - } - if in.IsUnknown() { - return NewSetNestedObjectValueOfUnknown[T](ctx), diags - } - - typ := NewObjectTypeOf[T](ctx) - - v, d := basetypes.NewSetValue(typ, in.Elements()) - diags.Append(d...) - if diags.HasError() { - return NewSetNestedObjectValueOfUnknown[T](ctx), diags - } - - return SetNestedObjectValueOf[T]{SetValue: v}, diags -} - -func (t setNestedObjectTypeOf[T]) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { - attrValue, err := t.SetType.ValueFromTerraform(ctx, in) - - if err != nil { - return nil, err - } - - setValue, ok := attrValue.(basetypes.SetValue) - - if !ok { - return nil, fmt.Errorf("unexpected value type of %T", attrValue) - } - - setValuable, diags := t.ValueFromSet(ctx, setValue) - - if diags.HasError() { - return nil, fmt.Errorf("unexpected error converting SetValue to SetValuable: %v", diags) - } - - return setValuable, nil -} - -func (t setNestedObjectTypeOf[T]) ValueType(ctx context.Context) attr.Value { - return SetNestedObjectValueOf[T]{} -} - -func (t setNestedObjectTypeOf[T]) NewObjectPtr(ctx context.Context) (any, diag.Diagnostics) { - return objectTypeNewObjectPtr[T](ctx) -} - -func (t setNestedObjectTypeOf[T]) NewObjectSlice(ctx context.Context, len, cap int) (any, diag.Diagnostics) { - return nestedObjectTypeNewObjectSlice[T](ctx, len, cap) -} - -func (t setNestedObjectTypeOf[T]) NullValue(ctx context.Context) (attr.Value, diag.Diagnostics) { - var diags diag.Diagnostics - - return NewSetNestedObjectValueOfNull[T](ctx), diags -} - -func (t setNestedObjectTypeOf[T]) ValueFromObjectPtr(ctx context.Context, ptr any) (attr.Value, diag.Diagnostics) { - var diags diag.Diagnostics - - if v, ok := ptr.(*T); ok { - v, d := NewSetNestedObjectValueOfPtr(ctx, v) - diags.Append(d...) - return v, d - } - - diags.Append(diag.NewErrorDiagnostic("Invalid pointer value", fmt.Sprintf("incorrect type: want %T, got %T", (*T)(nil), ptr))) - return nil, diags -} - -func (t setNestedObjectTypeOf[T]) ValueFromObjectSlice(ctx context.Context, slice any) (attr.Value, diag.Diagnostics) { - var diags diag.Diagnostics - - if v, ok := slice.([]*T); ok { - v, d := NewSetNestedObjectValueOfSlice(ctx, v) - diags.Append(d...) - return v, d - } - - diags.Append(diag.NewErrorDiagnostic("Invalid slice value", fmt.Sprintf("incorrect type: want %T, got %T", (*[]T)(nil), slice))) - return nil, diags -} - -// SetNestedObjectValueOf represents a Terraform Plugin Framework Set value whose elements are of type `ObjectTypeOf[T]`. -type SetNestedObjectValueOf[T any] struct { - basetypes.SetValue -} - -func (v SetNestedObjectValueOf[T]) Equal(o attr.Value) bool { - other, ok := o.(SetNestedObjectValueOf[T]) - - if !ok { - return false - } - - return v.SetValue.Equal(other.SetValue) -} - -func (v SetNestedObjectValueOf[T]) Type(ctx context.Context) attr.Type { - return NewSetNestedObjectTypeOf[T](ctx) -} - -func (v SetNestedObjectValueOf[T]) ToObjectPtr(ctx context.Context) (any, diag.Diagnostics) { - return v.ToPtr(ctx) -} - -func (v SetNestedObjectValueOf[T]) ToObjectSlice(ctx context.Context) (any, diag.Diagnostics) { - return v.ToSlice(ctx) -} - -// ToPtr returns a pointer to the single element of a SetNestedObject. -func (v SetNestedObjectValueOf[T]) ToPtr(ctx context.Context) (*T, diag.Diagnostics) { - return nestedObjectValueObjectPtr[T](ctx, v.SetValue) -} - -// ToSlice returns a slice of pointers to the elements of a SetNestedObject. -func (v SetNestedObjectValueOf[T]) ToSlice(ctx context.Context) ([]*T, diag.Diagnostics) { - return nestedObjectValueObjectSlice[T](ctx, v.SetValue) -} - -func NewSetNestedObjectValueOfNull[T any](ctx context.Context) SetNestedObjectValueOf[T] { - return SetNestedObjectValueOf[T]{SetValue: basetypes.NewSetNull(NewObjectTypeOf[T](ctx))} -} - -func NewSetNestedObjectValueOfUnknown[T any](ctx context.Context) SetNestedObjectValueOf[T] { - return SetNestedObjectValueOf[T]{SetValue: basetypes.NewSetUnknown(NewObjectTypeOf[T](ctx))} -} - -func NewSetNestedObjectValueOfPtr[T any](ctx context.Context, t *T) (SetNestedObjectValueOf[T], diag.Diagnostics) { - return NewSetNestedObjectValueOfSlice(ctx, []*T{t}) -} - -func NewSetNestedObjectValueOfPtrMust[T any](ctx context.Context, t *T) SetNestedObjectValueOf[T] { - return equinix_errors.MustWithDiagnostics(NewSetNestedObjectValueOfPtr(ctx, t)) -} - -func NewSetNestedObjectValueOfSlice[T any](ctx context.Context, ts []*T) (SetNestedObjectValueOf[T], diag.Diagnostics) { - return newSetNestedObjectValueOf[T](ctx, ts) -} - -func NewSetNestedObjectValueOfSliceMust[T any](ctx context.Context, ts []*T) SetNestedObjectValueOf[T] { - return equinix_errors.MustWithDiagnostics(NewSetNestedObjectValueOfSlice(ctx, ts)) -} - -func NewSetNestedObjectValueOfValueSlice[T any](ctx context.Context, ts []T) (SetNestedObjectValueOf[T], diag.Diagnostics) { - return newSetNestedObjectValueOf[T](ctx, ts) -} - -func NewSetNestedObjectValueOfValueSliceMust[T any](ctx context.Context, ts []T) SetNestedObjectValueOf[T] { - return equinix_errors.MustWithDiagnostics(NewSetNestedObjectValueOfValueSlice(ctx, ts)) -} - -func newSetNestedObjectValueOf[T any](ctx context.Context, elements any) (SetNestedObjectValueOf[T], diag.Diagnostics) { - var diags diag.Diagnostics - - typ := NewObjectTypeOf[T](ctx) - - v, d := basetypes.NewSetValueFrom(ctx, typ, elements) - diags.Append(d...) - if diags.HasError() { - return NewSetNestedObjectValueOfUnknown[T](ctx), diags - } - - return SetNestedObjectValueOf[T]{SetValue: v}, diags -} diff --git a/internal/framework/types/setof.go b/internal/framework/types/setof.go deleted file mode 100644 index 439cc73d0..000000000 --- a/internal/framework/types/setof.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package types - -import ( - "context" - "fmt" - equinix_errors "github.com/equinix/terraform-provider-equinix/internal/errors" - - "github.com/hashicorp/terraform-plugin-framework/attr" - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/types/basetypes" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -var ( - _ basetypes.SetTypable = (*setTypeOf[basetypes.StringValue])(nil) - _ basetypes.SetValuable = (*SetValueOf[basetypes.StringValue])(nil) -) - -// setTypeOf is the attribute type of a SetValueOf. -type setTypeOf[T attr.Value] struct { - basetypes.SetType -} - -var ( - SetOfStringType = setTypeOf[basetypes.StringValue]{basetypes.SetType{ElemType: basetypes.StringType{}}} -) - -func NewSetTypeOf[T attr.Value](ctx context.Context) setTypeOf[T] { - return setTypeOf[T]{basetypes.SetType{ElemType: newAttrTypeOf[T](ctx)}} -} - -func (t setTypeOf[T]) Equal(o attr.Type) bool { - other, ok := o.(setTypeOf[T]) - - if !ok { - return false - } - - return t.SetType.Equal(other.SetType) -} - -func (t setTypeOf[T]) String() string { - var zero T - return fmt.Sprintf("SetTypeOf[%T]", zero) -} - -func (t setTypeOf[T]) ValueFromSet(ctx context.Context, in basetypes.SetValue) (basetypes.SetValuable, diag.Diagnostics) { - var diags diag.Diagnostics - - if in.IsNull() { - return NewSetValueOfNull[T](ctx), diags - } - if in.IsUnknown() { - return NewSetValueOfUnknown[T](ctx), diags - } - - v, d := basetypes.NewSetValue(newAttrTypeOf[T](ctx), in.Elements()) - diags.Append(d...) - if diags.HasError() { - return NewSetValueOfUnknown[T](ctx), diags - } - - return SetValueOf[T]{SetValue: v}, diags -} - -func (t setTypeOf[T]) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { - attrValue, err := t.SetType.ValueFromTerraform(ctx, in) - - if err != nil { - return nil, err - } - - setValue, ok := attrValue.(basetypes.SetValue) - - if !ok { - return nil, fmt.Errorf("unexpected value type of %T", attrValue) - } - - setValuable, diags := t.ValueFromSet(ctx, setValue) - - if diags.HasError() { - return nil, fmt.Errorf("unexpected error converting SetValue to SetValuable: %v", diags) - } - - return setValuable, nil -} - -func (t setTypeOf[T]) ValueType(ctx context.Context) attr.Value { - return SetValueOf[T]{} -} - -// SetValueOf represents a Terraform Plugin Framework Set value whose elements are of type `T`. -type SetValueOf[T attr.Value] struct { - basetypes.SetValue -} - -func (v SetValueOf[T]) Equal(o attr.Value) bool { - other, ok := o.(SetValueOf[T]) - - if !ok { - return false - } - - return v.SetValue.Equal(other.SetValue) -} - -func (v SetValueOf[T]) Type(ctx context.Context) attr.Type { - return NewSetTypeOf[T](ctx) -} - -func NewSetValueOfNull[T attr.Value](ctx context.Context) SetValueOf[T] { - return SetValueOf[T]{SetValue: basetypes.NewSetNull(newAttrTypeOf[T](ctx))} -} - -func NewSetValueOfUnknown[T attr.Value](ctx context.Context) SetValueOf[T] { - return SetValueOf[T]{SetValue: basetypes.NewSetUnknown(newAttrTypeOf[T](ctx))} -} - -func NewSetValueOf[T attr.Value](ctx context.Context, elements []attr.Value) (SetValueOf[T], diag.Diagnostics) { - var diags diag.Diagnostics - - v, d := basetypes.NewSetValue(newAttrTypeOf[T](ctx), elements) - diags.Append(d...) - if diags.HasError() { - return NewSetValueOfUnknown[T](ctx), diags - } - - return SetValueOf[T]{SetValue: v}, diags -} - -func NewSetValueOfMust[T attr.Value](ctx context.Context, elements []attr.Value) SetValueOf[T] { - return equinix_errors.MustWithDiagnostics(NewSetValueOf[T](ctx, elements)) -} diff --git a/internal/resources/fabric/precision_time/datasources.go b/internal/resources/fabric/precision_time/datasources.go index 4e7181065..a2e5c4e76 100644 --- a/internal/resources/fabric/precision_time/datasources.go +++ b/internal/resources/fabric/precision_time/datasources.go @@ -39,7 +39,7 @@ func (r *DataSource) Read( client := r.Meta.NewFabricClientForFramework(ctx, req.ProviderMeta) // Retrieve values from plan - var data DataSourceModel + var data PrecisionTimeModel resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return diff --git a/internal/resources/fabric/precision_time/datasources_schema.go b/internal/resources/fabric/precision_time/datasources_schema.go index 57bd940f3..1da17e1a7 100644 --- a/internal/resources/fabric/precision_time/datasources_schema.go +++ b/internal/resources/fabric/precision_time/datasources_schema.go @@ -6,6 +6,7 @@ import ( "github.com/equinix/equinix-sdk-go/services/fabricv4" "github.com/equinix/terraform-provider-equinix/internal/framework" fwtypes "github.com/equinix/terraform-provider-equinix/internal/framework/types" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -48,6 +49,28 @@ func dataSourceSchema(ctx context.Context) schema.Schema { Description: fmt.Sprintf("Indicator of the state of this Precision Time Service. One of: [%v]", fabricv4.AllowedPrecisionTimeServiceCreateResponseStateEnumValues), Computed: true, }, + "project_id": schema.StringAttribute{ + Description: "Equinix Fabric Project ID", + Computed: true, + }, + "advance_configuration": schema.ListAttribute{ + Description: "An object that has advanced configuration options.", + CustomType: fwtypes.NewListNestedObjectTypeOf[AdvanceConfigurationModel](ctx), + ElementType: fwtypes.NewObjectTypeOf[AdvanceConfigurationModel](ctx), + Computed: true, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "account": schema.ListAttribute{ + Description: "Equinix User Account associated with Precision Time Service", + CustomType: fwtypes.NewListNestedObjectTypeOf[AccountModel](ctx), + ElementType: fwtypes.NewObjectTypeOf[AccountModel](ctx), + Computed: true, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, }, Blocks: map[string]schema.Block{ "package": schema.SingleNestedBlock{ @@ -122,85 +145,6 @@ func dataSourceSchema(ctx context.Context) schema.Schema { }, }, }, - "advance_configuration": schema.SingleNestedBlock{ - Description: "An object that has advanced configuration options.", - CustomType: fwtypes.NewObjectTypeOf[AdvanceConfigurationModel](ctx), - Attributes: map[string]schema.Attribute{ - "ntp": schema.ListAttribute{ - Description: "Advance Configuration for NTP; a list of MD5 objects", - Computed: true, - CustomType: fwtypes.NewListNestedObjectTypeOf[MD5Model](ctx), - ElementType: fwtypes.NewObjectTypeOf[MD5Model](ctx), - }, - }, - Blocks: map[string]schema.Block{ - "ptp": schema.SingleNestedBlock{ - Description: "An object that has advanced PTP configuration.", - CustomType: fwtypes.NewObjectTypeOf[PTPModel](ctx), - Attributes: map[string]schema.Attribute{ - "time_scale": schema.StringAttribute{ - Description: "Time scale value. ARB denotes Arbitrary, and PTP denotes Precision Time Protocol.", - Computed: true, - Validators: []validator.String{ - stringvalidator.OneOf( - string(fabricv4.PTPADVANCECONFIGURATIONTIMESCALE_ARB), - string(fabricv4.PTPADVANCECONFIGURATIONTIMESCALE_PTP), - ), - }, - }, - "domain": schema.Int64Attribute{ - Description: "Represents the domain number associated with the PTP profile. This is used to differentiate multiple PTP networks within a single physical network.", - Computed: true, - }, - "priority_1": schema.Int64Attribute{ - Description: "Specifies the priority level 1 for the clock. The value helps in determining the best clock in the PTP network. Lower values are considered higher priority.", - Computed: true, - }, - "priority_2": schema.Int64Attribute{ - Description: "Specifies the priority level 2 for the clock. It acts as a tie-breaker if multiple clocks have the same priority 1 value. Lower values are considered higher priority.", - Computed: true, - }, - "log_announce_interval": schema.Int64Attribute{ - Description: "Represents the log2 interval between consecutive PTP announce messages. For example, a value of 0 implies an interval of 2^0 = 1 second.", - Computed: true, - }, - "log_sync_interval": schema.Int64Attribute{ - Description: "Represents the log2 interval between consecutive PTP synchronization messages. A value of 0 implies an interval of 2^0 = 1 second.", - Computed: true, - }, - "log_delay_req_interval": schema.Int64Attribute{ - Description: "Represents the log2 interval between consecutive PTP delay request messages. A value of 0 implies an interval of 2^0 = 1 second.", - Computed: true, - }, - "transport_mode": schema.StringAttribute{ - Description: "Mode of transport for the Time Precision Service.", - Computed: true, - Validators: []validator.String{ - stringvalidator.OneOf( - string(fabricv4.PTPADVANCECONFIGURATIONTRANSPORTMODE_MULTICAST), - string(fabricv4.PTPADVANCECONFIGURATIONTRANSPORTMODE_UNICAST), - string(fabricv4.PTPADVANCECONFIGURATIONTRANSPORTMODE_HYBRID), - ), - }, - }, - "grant_time": schema.Int64Attribute{ - Description: "Unicast Grant Time in seconds. For Multicast and Hybrid transport modes, grant time defaults to 300 seconds. For Unicast mode, grant time can be between 30 to 7200.", - Computed: true, - }, - }, - }, - }, - }, - "project": schema.SingleNestedBlock{ - Description: "An object that contains the Equinix Fabric project_id used for linking the Time Precision Service to a specific Equinix Fabric Project", - CustomType: fwtypes.NewObjectTypeOf[ProjectModel](ctx), - Attributes: map[string]schema.Attribute{ - "project_id": schema.StringAttribute{ - Description: "Equinix Fabric Project ID", - Computed: true, - }, - }, - }, "connections": schema.ListNestedBlock{ Description: "An array of objects with unique identifiers of connections.", CustomType: fwtypes.NewListNestedObjectTypeOf[ConnectionModel](ctx), @@ -222,28 +166,6 @@ func dataSourceSchema(ctx context.Context) schema.Schema { }, }, }, - "account": schema.SingleNestedBlock{ - Description: "Equinix User Account associated with Precision Time Service", - CustomType: fwtypes.NewObjectTypeOf[AccountModel](ctx), - Attributes: map[string]schema.Attribute{ - "account_number": schema.Int64Attribute{ - Description: "Equinix User account number", - Computed: true, - }, - "is_reseller_account": schema.BoolAttribute{ - Description: "Equinix User Boolean flag indicating if it is a reseller account", - Computed: true, - }, - "org_id": schema.StringAttribute{ - Description: "Equinix User organization id", - Computed: true, - }, - "global_org_id": schema.StringAttribute{ - Description: "Equinix User global organization id", - Computed: true, - }, - }, - }, }, } } diff --git a/internal/resources/fabric/precision_time/models.go b/internal/resources/fabric/precision_time/models.go index 9da38a679..28f48e93f 100644 --- a/internal/resources/fabric/precision_time/models.go +++ b/internal/resources/fabric/precision_time/models.go @@ -4,41 +4,28 @@ import ( "context" "github.com/equinix/equinix-sdk-go/services/fabricv4" fwtypes "github.com/equinix/terraform-provider-equinix/internal/framework/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "reflect" ) -type ResourceModel struct { - ID types.String `tfsdk:"id"` - Type types.String `tfsdk:"type"` - Href types.String `tfsdk:"href"` - Uuid types.String `tfsdk:"uuid"` - Name types.String `tfsdk:"name"` - Description types.String `tfsdk:"description"` - State types.String `tfsdk:"state"` - Package fwtypes.ObjectValueOf[PackageModel] `tfsdk:"package"` - Connections fwtypes.ListNestedObjectValueOf[ConnectionModel] `tfsdk:"connections"` - Ipv4 fwtypes.ObjectValueOf[Ipv4Model] `tfsdk:"ipv4"` - Account fwtypes.ObjectValueOf[AccountModel] `tfsdk:"account"` - AdvanceConfiguration fwtypes.ObjectValueOf[AdvanceConfigurationModel] `tfsdk:"advance_configuration"` - Project fwtypes.ObjectValueOf[ProjectModel] `tfsdk:"project"` -} - -type DataSourceModel struct { - ID types.String `tfsdk:"id"` - Type types.String `tfsdk:"type"` - Href types.String `tfsdk:"href"` - Uuid types.String `tfsdk:"uuid"` - Name types.String `tfsdk:"name"` - Description types.String `tfsdk:"description"` - State types.String `tfsdk:"state"` - Package fwtypes.ObjectValueOf[PackageModel] `tfsdk:"package"` - Connections fwtypes.ListNestedObjectValueOf[ConnectionModel] `tfsdk:"connections"` - Ipv4 fwtypes.ObjectValueOf[Ipv4Model] `tfsdk:"ipv4"` - Account fwtypes.ObjectValueOf[AccountModel] `tfsdk:"account"` - AdvanceConfiguration fwtypes.ObjectValueOf[AdvanceConfigurationModel] `tfsdk:"advance_configuration"` - Project fwtypes.ObjectValueOf[ProjectModel] `tfsdk:"project"` +type PrecisionTimeModel struct { + ID types.String `tfsdk:"id"` + Type types.String `tfsdk:"type"` + Href types.String `tfsdk:"href"` + Uuid types.String `tfsdk:"uuid"` + Name types.String `tfsdk:"name"` + Description types.String `tfsdk:"description"` + State types.String `tfsdk:"state"` + Package fwtypes.ObjectValueOf[PackageModel] `tfsdk:"package"` + Connections fwtypes.ListNestedObjectValueOf[ConnectionModel] `tfsdk:"connections"` + Ipv4 fwtypes.ObjectValueOf[Ipv4Model] `tfsdk:"ipv4"` + Account fwtypes.ListNestedObjectValueOf[AccountModel] `tfsdk:"account"` + AdvanceConfiguration fwtypes.ListNestedObjectValueOf[AdvanceConfigurationModel] `tfsdk:"advance_configuration"` + ProjectId types.String `tfsdk:"project_id"` + Timeouts timeouts.Value `tfsdk:"timeouts"` } type PackageModel struct { @@ -77,7 +64,7 @@ type AccountModel struct { type AdvanceConfigurationModel struct { Ntp fwtypes.ListNestedObjectValueOf[MD5Model] `tfsdk:"ntp"` - Ptp fwtypes.ObjectValueOf[PTPModel] `tfsdk:"ptp"` + Ptp fwtypes.ListNestedObjectValueOf[PTPModel] `tfsdk:"ptp"` } type MD5Model struct { @@ -98,30 +85,12 @@ type PTPModel struct { GrantTime types.Int64 `tfsdk:"grant_time"` } -type ProjectModel struct { - ProjectId types.String `tfsdk:"project_id"` -} - -func (m *ResourceModel) parse(ctx context.Context, ept *fabricv4.PrecisionTimeServiceCreateResponse) diag.Diagnostics { +func (m *PrecisionTimeModel) parse(ctx context.Context, ept *fabricv4.PrecisionTimeServiceCreateResponse) diag.Diagnostics { var diags diag.Diagnostics diags = parsePrecisionTime(ctx, ept, &m.ID, &m.Type, &m.Href, &m.Uuid, &m.Name, &m.Description, - &m.State, &m.Package, &m.Ipv4, &m.Project, - &m.Account, - &m.AdvanceConfiguration, - &m.Connections, - ) - - return diags -} - -func (m *DataSourceModel) parse(ctx context.Context, ept *fabricv4.PrecisionTimeServiceCreateResponse) diag.Diagnostics { - var diags diag.Diagnostics - - diags = parsePrecisionTime(ctx, ept, - &m.ID, &m.Type, &m.Href, &m.Uuid, &m.Name, &m.Description, - &m.State, &m.Package, &m.Ipv4, &m.Project, + &m.State, &m.ProjectId, &m.Package, &m.Ipv4, &m.Account, &m.AdvanceConfiguration, &m.Connections, @@ -133,64 +102,76 @@ func (m *DataSourceModel) parse(ctx context.Context, ept *fabricv4.PrecisionTime func parsePrecisionTime( ctx context.Context, ept *fabricv4.PrecisionTimeServiceCreateResponse, - id, type_, href, uuid, name, description, state *basetypes.StringValue, + id, type_, href, uuid, name, description, state, projectId *basetypes.StringValue, package_ *fwtypes.ObjectValueOf[PackageModel], ipv4 *fwtypes.ObjectValueOf[Ipv4Model], - project *fwtypes.ObjectValueOf[ProjectModel], - account *fwtypes.ObjectValueOf[AccountModel], - advanceConfiguration *fwtypes.ObjectValueOf[AdvanceConfigurationModel], + account *fwtypes.ListNestedObjectValueOf[AccountModel], + advanceConfiguration *fwtypes.ListNestedObjectValueOf[AdvanceConfigurationModel], connections *fwtypes.ListNestedObjectValueOf[ConnectionModel], ) diag.Diagnostics { var diags diag.Diagnostics *id = types.StringValue(ept.GetUuid()) *type_ = types.StringValue(string(ept.GetType())) - *href = types.StringValue(ept.GetHref()) *uuid = types.StringValue(ept.GetUuid()) - *name = types.StringValue(ept.GetName()) - *description = types.StringValue(ept.GetDescription()) *state = types.StringValue(string(ept.GetState())) + *href = types.StringValue(ept.GetHref()) + + if ept.GetName() != "" { + *name = types.StringValue(ept.GetName()) + } + if ept.GetDescription() != "" { + *description = types.StringValue(ept.GetDescription()) + } + + eptProject := ept.GetProject() + if eptProject.GetProjectId() != "" { + *projectId = types.StringValue(eptProject.GetProjectId()) + } eptPackage := ept.GetPackage() parsedEptPackage, diags := parsePackage(ctx, &eptPackage) if diags.HasError() { return diags } - *package_ = parsedEptPackage + if !reflect.DeepEqual(parsedEptPackage, fwtypes.NewObjectValueOfNull[PackageModel](ctx)) { + *package_ = parsedEptPackage + } parsedEptConnections, diags := parseConnections(ctx, ept.GetConnections()) if diags.HasError() { return diags } - *connections = parsedEptConnections + if !reflect.DeepEqual(parsedEptConnections, fwtypes.NewListNestedObjectValueOfValueSlice[ConnectionModel](ctx, []ConnectionModel{})) { + *connections = parsedEptConnections + } eptIpv4 := ept.GetIpv4() parsedEptIpv4, diags := parseIpv4(ctx, &eptIpv4) if diags.HasError() { return diags } - *ipv4 = parsedEptIpv4 + if !reflect.DeepEqual(parsedEptIpv4, fwtypes.NewObjectValueOfNull[Ipv4Model](ctx)) { + *ipv4 = parsedEptIpv4 + } eptAccount := ept.GetAccount() parsedEptAccount, diags := parseAccount(ctx, &eptAccount) if diags.HasError() { return diags } - *account = parsedEptAccount + if !reflect.DeepEqual(parsedEptAccount, fwtypes.NewObjectValueOfNull[AccountModel](ctx)) { + *account = parsedEptAccount + } eptAdvanceConfiguration := ept.GetAdvanceConfiguration() parsedEptAdvanceConfiguration, diags := parseAdvanceConfiguration(ctx, &eptAdvanceConfiguration) if diags.HasError() { return diags } - *advanceConfiguration = parsedEptAdvanceConfiguration - - eptProject := ept.GetProject() - parsedEptProject, diags := parseProject(ctx, &eptProject) - if diags.HasError() { - return diags + if !reflect.DeepEqual(parsedEptAdvanceConfiguration, fwtypes.NewListNestedObjectValueOfNull[AdvanceConfigurationModel](ctx)) { + *advanceConfiguration = parsedEptAdvanceConfiguration } - *project = parsedEptProject return diags } @@ -199,7 +180,22 @@ func parsePackage(ctx context.Context, package_ *fabricv4.PrecisionTimePackageRe packageModel := &PackageModel{} packageModel.Code = types.StringValue(string(package_.GetCode())) - packageModel.Href = types.StringValue(package_.GetHref()) + if package_.GetHref() != "" { + packageModel.Href = types.StringValue(package_.GetHref()) + } + if string(package_.GetType()) != "" { + packageModel.Type = types.StringValue(string(package_.GetType())) + } + if package_.GetAccuracyUnit() != "" { + packageModel.AccuracyUnit = types.StringValue(package_.GetAccuracyUnit()) + } + packageModel.AccuracySla = types.Int64Value(int64(package_.GetAccuracySla())) + packageModel.AccuracyAvgMin = types.Int64Value(int64(package_.GetAccuracyAvgMin())) + packageModel.AccuracyAvgMax = types.Int64Value(int64(package_.GetAccuracyAvgMax())) + packageModel.Bandwidth = types.Int64Value(int64(package_.GetBandwidth())) + packageModel.ClientsPerSecondMax = types.Int64Value(int64(package_.GetClientsPerSecondMax())) + packageModel.RedundancySupported = types.BoolValue(package_.GetRedundancySupported()) + packageModel.MultiSubnetSupported = types.BoolValue(package_.GetMultiSubnetSupported()) return fwtypes.NewObjectValueOf[PackageModel](ctx, packageModel), nil } @@ -208,11 +204,16 @@ func parseConnections(ctx context.Context, connections []fabricv4.FabricConnecti connectionModels := make([]ConnectionModel, len(connections)) for index, connection := range connections { - connectionModels[index] = ConnectionModel{ + connectionModel := ConnectionModel{ Uuid: types.StringValue(connection.GetUuid()), - Href: types.StringValue(connection.GetHref()), - Type: types.StringValue(connection.GetType()), } + if connection.GetHref() != "" { + connectionModel.Href = types.StringValue(connection.GetHref()) + } + if connection.GetType() != "" { + connectionModel.Type = types.StringValue(connection.GetType()) + } + connectionModels[index] = connectionModel } return fwtypes.NewListNestedObjectValueOfValueSlice(ctx, connectionModels), nil @@ -229,8 +230,8 @@ func parseIpv4(ctx context.Context, ipv4 *fabricv4.Ipv4) (fwtypes.ObjectValueOf[ return fwtypes.NewObjectValueOf[Ipv4Model](ctx, ipv4Model), nil } -func parseAccount(ctx context.Context, account *fabricv4.Account) (fwtypes.ObjectValueOf[AccountModel], diag.Diagnostics) { - accountModel := &AccountModel{} +func parseAccount(ctx context.Context, account *fabricv4.Account) (fwtypes.ListNestedObjectValueOf[AccountModel], diag.Diagnostics) { + accountModel := AccountModel{} if account.GetAccountNumber() != 0 { accountModel.AccountNumber = types.Int64Value(int64(account.GetAccountNumber())) @@ -245,27 +246,39 @@ func parseAccount(ctx context.Context, account *fabricv4.Account) (fwtypes.Objec accountModel.GlobalOrgId = types.StringValue(account.GetGlobalOrgId()) } - return fwtypes.NewObjectValueOf[AccountModel](ctx, accountModel), nil + return fwtypes.NewListNestedObjectValueOfValueSlice[AccountModel](ctx, []AccountModel{accountModel}), nil } -func parseAdvanceConfiguration(ctx context.Context, advConfig *fabricv4.AdvanceConfiguration) (fwtypes.ObjectValueOf[AdvanceConfigurationModel], diag.Diagnostics) { +func parseAdvanceConfiguration(ctx context.Context, advConfig *fabricv4.AdvanceConfiguration) (fwtypes.ListNestedObjectValueOf[AdvanceConfigurationModel], diag.Diagnostics) { var diags diag.Diagnostics - advConfigModel := &AdvanceConfigurationModel{} + advConfigModel := AdvanceConfigurationModel{} md5s, diags := parseNtp(ctx, advConfig.GetNtp()) if diags.HasError() { - return fwtypes.NewObjectValueOfNull[AdvanceConfigurationModel](ctx), diags + return fwtypes.NewListNestedObjectValueOfUnknown[AdvanceConfigurationModel](ctx), diags + } + if len(md5s.Elements()) > 0 { + advConfigModel.Ntp = md5s + } else { + advConfigModel.Ntp = fwtypes.NewListNestedObjectValueOfNull[MD5Model](ctx) } - advConfigModel.Ntp = md5s ptp := advConfig.GetPtp() parsedPtp, diags := parsePtp(ctx, &ptp) if diags.HasError() { - return fwtypes.NewObjectValueOfNull[AdvanceConfigurationModel](ctx), diags + return fwtypes.NewListNestedObjectValueOfUnknown[AdvanceConfigurationModel](ctx), diags + } + if len(parsedPtp.Elements()) > 0 { + advConfigModel.Ptp = parsedPtp + } else { + advConfigModel.Ptp = fwtypes.NewListNestedObjectValueOfNull[PTPModel](ctx) + } + + if reflect.DeepEqual(advConfigModel, AdvanceConfigurationModel{}) { + return fwtypes.NewListNestedObjectValueOfNull[AdvanceConfigurationModel](ctx), nil } - advConfigModel.Ptp = parsedPtp - return fwtypes.NewObjectValueOf[AdvanceConfigurationModel](ctx, advConfigModel), nil + return fwtypes.NewListNestedObjectValueOfValueSlice[AdvanceConfigurationModel](ctx, []AdvanceConfigurationModel{advConfigModel}), nil } func parseNtp(ctx context.Context, ntp []fabricv4.Md5) (fwtypes.ListNestedObjectValueOf[MD5Model], diag.Diagnostics) { @@ -282,27 +295,38 @@ func parseNtp(ctx context.Context, ntp []fabricv4.Md5) (fwtypes.ListNestedObject return fwtypes.NewListNestedObjectValueOfValueSlice(ctx, ntpModel), nil } -func parsePtp(ctx context.Context, ptp *fabricv4.PtpAdvanceConfiguration) (fwtypes.ObjectValueOf[PTPModel], diag.Diagnostics) { - ptpModel := &PTPModel{} - - ptpModel.TimeScale = types.StringValue(string(ptp.GetTimeScale())) - ptpModel.Domain = types.Int64Value(int64(ptp.GetDomain())) - ptpModel.Priority1 = types.Int64Value(int64(ptp.GetPriority1())) - ptpModel.Priority2 = types.Int64Value(int64(ptp.GetPriority2())) - ptpModel.LogAnnounceInterval = types.Int64Value(int64(ptp.GetLogAnnounceInterval())) - ptpModel.LogSyncInterval = types.Int64Value(int64(ptp.GetLogSyncInterval())) - ptpModel.LogDelayReqInterval = types.Int64Value(int64(ptp.GetLogDelayReqInterval())) - ptpModel.TransportMode = types.StringValue(string(ptp.GetTransportMode())) - ptpModel.GrantTime = types.Int64Value(int64(ptp.GetGrantTime())) +func parsePtp(ctx context.Context, ptp *fabricv4.PtpAdvanceConfiguration) (fwtypes.ListNestedObjectValueOf[PTPModel], diag.Diagnostics) { + ptpModel := PTPModel{} - return fwtypes.NewObjectValueOf[PTPModel](ctx, ptpModel), nil - -} + if timeScale, ok := ptp.GetTimeScaleOk(); ok && timeScale != nil { + ptpModel.TimeScale = types.StringValue(string(ptp.GetTimeScale())) + } + if domain, ok := ptp.GetDomainOk(); ok && domain != nil { + ptpModel.Domain = types.Int64Value(int64(ptp.GetDomain())) + } + if priority1, ok := ptp.GetPriority1Ok(); ok && priority1 != nil { + ptpModel.Priority1 = types.Int64Value(int64(ptp.GetPriority1())) + } + if priority2, ok := ptp.GetPriority2Ok(); ok && priority2 != nil { + ptpModel.Priority2 = types.Int64Value(int64(ptp.GetPriority2())) + } + if logAnnounceInterval, ok := ptp.GetLogAnnounceIntervalOk(); ok && logAnnounceInterval != nil { + ptpModel.LogAnnounceInterval = types.Int64Value(int64(ptp.GetLogAnnounceInterval())) + } + if logSyncInterval, ok := ptp.GetLogSyncIntervalOk(); ok && logSyncInterval != nil { + ptpModel.LogSyncInterval = types.Int64Value(int64(ptp.GetLogSyncInterval())) + } + if logDelayReqInterval, ok := ptp.GetLogDelayReqIntervalOk(); ok && logDelayReqInterval != nil { + ptpModel.LogDelayReqInterval = types.Int64Value(int64(ptp.GetLogDelayReqInterval())) + } + if transportMode, ok := ptp.GetTransportModeOk(); ok && transportMode != nil { + ptpModel.TransportMode = types.StringValue(string(ptp.GetTransportMode())) -func parseProject(ctx context.Context, project *fabricv4.Project) (fwtypes.ObjectValueOf[ProjectModel], diag.Diagnostics) { - projectModel := &ProjectModel{} + } + if grantTime, ok := ptp.GetGrantTimeOk(); ok && grantTime != nil { + ptpModel.GrantTime = types.Int64Value(int64(ptp.GetGrantTime())) + } - projectModel.ProjectId = types.StringValue(project.GetProjectId()) + return fwtypes.NewListNestedObjectValueOfValueSlice[PTPModel](ctx, []PTPModel{ptpModel}), nil - return fwtypes.NewObjectValueOf[ProjectModel](ctx, projectModel), nil } diff --git a/internal/resources/fabric/precision_time/resource.go b/internal/resources/fabric/precision_time/resource.go index 0ac323bd9..74be8a89a 100644 --- a/internal/resources/fabric/precision_time/resource.go +++ b/internal/resources/fabric/precision_time/resource.go @@ -46,7 +46,7 @@ func (r *Resource) Create( resp *resource.CreateResponse, ) { - var plan ResourceModel + var plan PrecisionTimeModel diags := req.Plan.Get(ctx, &plan) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -72,14 +72,21 @@ func (r *Resource) Create( return } - ept, diags = getEpt(ctx, client, &resp.State, ept.GetUuid()) - if diags != nil { + createTimeout, diags := plan.Timeouts.Create(ctx, 10*time.Minute) + if diags.HasError() { resp.Diagnostics.Append(diags...) return } + createWaiter := getCreateUpdateWaiter(ctx, client, ept.GetUuid(), createTimeout) + eptChecked, err := createWaiter.WaitForStateContext(ctx) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed to create Precision Time Service %s", ept.GetUuid()), err.Error()) + return + } // Parse API response into the Terraform state - resp.Diagnostics.Append(plan.parse(ctx, ept)...) + resp.Diagnostics.Append(plan.parse(ctx, eptChecked.(*fabricv4.PrecisionTimeServiceCreateResponse))...) if resp.Diagnostics.HasError() { return } @@ -93,7 +100,7 @@ func (r *Resource) Read( req resource.ReadRequest, resp *resource.ReadResponse, ) { - var state ResourceModel + var state PrecisionTimeModel diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -106,10 +113,10 @@ func (r *Resource) Read( // Extract the ID of the resource from the state id := state.ID.ValueString() - // Use API client to get the current state of the resource - ept, diags := getEpt(ctx, client, &resp.State, id) - if diags != nil { - resp.Diagnostics.Append(diags...) + ept, _, err := client.PrecisionTimeApi.GetTimeServicesById(ctx, id).Execute() + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed retrieving Precision Time Service %s", id), err.Error()) return } @@ -131,7 +138,7 @@ func (r *Resource) Update( client := r.Meta.NewFabricClientForFramework(ctx, req.ProviderMeta) // Retrieve values from plan - var state, plan ResourceModel + var state, plan PrecisionTimeModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { @@ -194,15 +201,22 @@ func (r *Resource) Update( } } - // Use API client to get the current state of the resource - conn, diags := getEpt(ctx, client, &resp.State, id) - if diags != nil { + updateTimeout, diags := plan.Timeouts.Create(ctx, 10*time.Minute) + if diags.HasError() { resp.Diagnostics.Append(diags...) return } + updateWaiter := getCreateUpdateWaiter(ctx, client, id, updateTimeout) + ept, err := updateWaiter.WaitForStateContext(ctx) + if err != nil { + resp.Diagnostics.AddError( + fmt.Sprintf("Failed to update Precision Time Service %s", id), err.Error()) + return + } + // Set state to fully populated data - resp.Diagnostics.Append(plan.parse(ctx, conn)...) + resp.Diagnostics.Append(plan.parse(ctx, ept.(*fabricv4.PrecisionTimeServiceCreateResponse))...) if resp.Diagnostics.HasError() { return } @@ -220,7 +234,7 @@ func (r *Resource) Delete( client := r.Meta.NewFabricClientForFramework(ctx, req.ProviderMeta) // Retrieve the current state - var state ResourceModel + var state PrecisionTimeModel diags := req.State.Get(ctx, &state) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -239,7 +253,11 @@ func (r *Resource) Delete( } } - deleteTimeout := 10 * 60 * time.Second + deleteTimeout, diags := state.Timeouts.Create(ctx, 10*time.Minute) + if diags.HasError() { + resp.Diagnostics.Append(diags...) + return + } deleteWaiter := getDeleteWaiter(ctx, client, id, deleteTimeout) _, err = deleteWaiter.WaitForStateContext(ctx) @@ -249,7 +267,7 @@ func (r *Resource) Delete( } } -func buildCreateRequest(ctx context.Context, plan ResourceModel) (fabricv4.PrecisionTimeServiceRequest, diag.Diagnostics) { +func buildCreateRequest(ctx context.Context, plan PrecisionTimeModel) (fabricv4.PrecisionTimeServiceRequest, diag.Diagnostics) { var diags diag.Diagnostics request := fabricv4.PrecisionTimeServiceRequest{ Type: fabricv4.PrecisionTimeServiceRequestType(plan.Type.ValueString()), @@ -313,95 +331,86 @@ func buildCreateRequest(ctx context.Context, plan ResourceModel) (fabricv4.Preci ipv4.SetDefaultGateway(ipv4Model.DefaultGateway.ValueString()) request.SetIpv4(ipv4) - advConfigModel := AdvanceConfigurationModel{} - diags = plan.AdvanceConfiguration.As(ctx, &advConfigModel, basetypes.ObjectAsOptions{ - UnhandledNullAsEmpty: true, - UnhandledUnknownAsEmpty: true, - }) + advConfigList := make([]AdvanceConfigurationModel, 0) + diags = plan.AdvanceConfiguration.ElementsAs(ctx, &advConfigList, true) if diags.HasError() { return fabricv4.PrecisionTimeServiceRequest{}, diags } - ptpModel := PTPModel{} - diags = advConfigModel.Ptp.As(ctx, &ptpModel, basetypes.ObjectAsOptions{ - UnhandledNullAsEmpty: true, - UnhandledUnknownAsEmpty: true, - }) - if diags.HasError() { - return fabricv4.PrecisionTimeServiceRequest{}, diags - } + if len(advConfigList) != 0 { + advConfigModel := advConfigList[0] + ptpList := make([]PTPModel, 0) + diags = advConfigModel.Ptp.ElementsAs(ctx, &ptpList, true) + if diags.HasError() { + return fabricv4.PrecisionTimeServiceRequest{}, diags + } - ptp := fabricv4.PtpAdvanceConfiguration{} - if timeScale := ptpModel.TimeScale.ValueString(); timeScale != "" { - ptp.SetTimeScale(fabricv4.PtpAdvanceConfigurationTimeScale(timeScale)) - } - if domain := ptpModel.Domain.ValueInt64(); domain != 0 { - ptp.SetPriority1(int32(domain)) - } - if priority1 := ptpModel.Priority1.ValueInt64(); priority1 != 0 { - ptp.SetPriority1(int32(priority1)) - } - if priority2 := ptpModel.Priority2.ValueInt64(); priority2 != 0 { - ptp.SetPriority2(int32(priority2)) - } - if logAnnounceInterval := ptpModel.LogAnnounceInterval.ValueInt64(); logAnnounceInterval != 0 { - ptp.SetLogAnnounceInterval(int32(logAnnounceInterval)) - } - if logSyncInterval := ptpModel.LogSyncInterval.ValueInt64(); logSyncInterval != 0 { - ptp.SetLogSyncInterval(int32(logSyncInterval)) - } - if logDelayReqInterval := ptpModel.LogDelayReqInterval.ValueInt64(); logDelayReqInterval != 0 { - ptp.SetLogDelayReqInterval(int32(logDelayReqInterval)) - } - if transportMode := ptpModel.TransportMode.ValueString(); transportMode != "" { - ptp.SetTransportMode(fabricv4.PtpAdvanceConfigurationTransportMode(transportMode)) - } - if grantTime := ptpModel.GrantTime.ValueInt64(); grantTime != 0 { - ptp.SetGrantTime(int32(grantTime)) - } + ptp := fabricv4.PtpAdvanceConfiguration{} + if len(ptpList) != 0 { + ptpModel := ptpList[0] + if timeScale := ptpModel.TimeScale.ValueString(); timeScale != "" { + ptp.SetTimeScale(fabricv4.PtpAdvanceConfigurationTimeScale(timeScale)) + } + if domain := ptpModel.Domain.ValueInt64(); domain != 0 { + ptp.SetPriority1(int32(domain)) + } + if priority1 := ptpModel.Priority1.ValueInt64(); priority1 != 0 { + ptp.SetPriority1(int32(priority1)) + } + if priority2 := ptpModel.Priority2.ValueInt64(); priority2 != 0 { + ptp.SetPriority2(int32(priority2)) + } + if logAnnounceInterval := ptpModel.LogAnnounceInterval.ValueInt64(); logAnnounceInterval != 0 { + ptp.SetLogAnnounceInterval(int32(logAnnounceInterval)) + } + if logSyncInterval := ptpModel.LogSyncInterval.ValueInt64(); logSyncInterval != 0 { + ptp.SetLogSyncInterval(int32(logSyncInterval)) + } + if logDelayReqInterval := ptpModel.LogDelayReqInterval.ValueInt64(); logDelayReqInterval != 0 { + ptp.SetLogDelayReqInterval(int32(logDelayReqInterval)) + } + if transportMode := ptpModel.TransportMode.ValueString(); transportMode != "" { + ptp.SetTransportMode(fabricv4.PtpAdvanceConfigurationTransportMode(transportMode)) + } + if grantTime := ptpModel.GrantTime.ValueInt64(); grantTime != 0 { + ptp.SetGrantTime(int32(grantTime)) + } + } - ntpModels, diags := advConfigModel.Ntp.ToSlice(ctx) - if diags.HasError() { - return fabricv4.PrecisionTimeServiceRequest{}, diags - } + ntpModels, diags := advConfigModel.Ntp.ToSlice(ctx) + if diags.HasError() { + return fabricv4.PrecisionTimeServiceRequest{}, diags + } - ntps := make([]fabricv4.Md5, len(ntpModels)) - for index, md5 := range ntpModels { - ntps[index] = fabricv4.Md5{} - if type_ := md5.Type.ValueString(); type_ != "" { - ntps[index].SetType(fabricv4.Md5Type(type_)) + ntps := make([]fabricv4.Md5, len(ntpModels)) + for index, md5 := range ntpModels { + ntps[index] = fabricv4.Md5{} + if type_ := md5.Type.ValueString(); type_ != "" { + ntps[index].SetType(fabricv4.Md5Type(type_)) + } + if id := md5.Id.ValueString(); id != "" { + ntps[index].SetId(id) + } + if password := md5.Password.ValueString(); password != "" { + ntps[index].SetPassword(password) + } } - if id := md5.Id.ValueString(); id != "" { - ntps[index].SetId(id) + + advConfig := fabricv4.AdvanceConfiguration{} + if len(ntps) > 0 { + advConfig.SetNtp(ntps) } - if password := md5.Password.ValueString(); password != "" { - ntps[index].SetPassword(password) + if !reflect.DeepEqual(ptp, fabricv4.PtpAdvanceConfiguration{}) { + advConfig.SetPtp(ptp) + } + if !reflect.DeepEqual(advConfig, fabricv4.AdvanceConfiguration{}) { + request.SetAdvanceConfiguration(advConfig) } } - advConfig := fabricv4.AdvanceConfiguration{} - if len(ntps) > 0 { - advConfig.SetNtp(ntps) - } - if !reflect.DeepEqual(ptp, fabricv4.PtpAdvanceConfiguration{}) { - advConfig.SetPtp(ptp) - } - if !reflect.DeepEqual(advConfig, fabricv4.AdvanceConfiguration{}) { - request.SetAdvanceConfiguration(advConfig) - } - - projectModel := ProjectModel{} - diags = plan.Project.As(ctx, &projectModel, basetypes.ObjectAsOptions{ - UnhandledNullAsEmpty: true, - UnhandledUnknownAsEmpty: true, - }) - if diags.HasError() { - return fabricv4.PrecisionTimeServiceRequest{}, diags - } - - project := fabricv4.Project{} - if projectId := projectModel.ProjectId.ValueString(); projectId != "" { - project.SetProjectId(projectId) + if plan.ProjectId.ValueString() != "" { + project := fabricv4.Project{} + project.SetProjectId(plan.ProjectId.ValueString()) request.SetProject(project) } @@ -434,6 +443,25 @@ func getEpt(ctx context.Context, client *fabricv4.APIClient, state *tfsdk.State, return ept, diags } +func getCreateUpdateWaiter(ctx context.Context, client *fabricv4.APIClient, id string, timeout time.Duration) *retry.StateChangeConf { + return &retry.StateChangeConf{ + Target: []string{ + string(fabricv4.PRECISIONTIMESERVICECREATERESPONSESTATE_PROVISIONED), + string(fabricv4.PRECISIONTIMESERVICECREATERESPONSESTATE_PENDING_CONFIGURATION), + }, + Refresh: func() (interface{}, string, error) { + ept, _, err := client.PrecisionTimeApi.GetTimeServicesById(ctx, id).Execute() + if err != nil { + return 0, "", err + } + return ept, string(ept.GetState()), nil + }, + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } +} + func getDeleteWaiter(ctx context.Context, client *fabricv4.APIClient, id string, timeout time.Duration) *retry.StateChangeConf { // deletedMarker is a terraform-provider-only value that is used by the waiter // to indicate that the Precision Time Service appears to be deleted successfully based on diff --git a/internal/resources/fabric/precision_time/resource_schema.go b/internal/resources/fabric/precision_time/resource_schema.go index d616799ec..e0f44edf4 100644 --- a/internal/resources/fabric/precision_time/resource_schema.go +++ b/internal/resources/fabric/precision_time/resource_schema.go @@ -6,10 +6,13 @@ import ( "github.com/equinix/equinix-sdk-go/services/fabricv4" "github.com/equinix/terraform-provider-equinix/internal/framework" fwtypes "github.com/equinix/terraform-provider-equinix/internal/framework/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" ) @@ -45,13 +48,52 @@ func resourceSchema(ctx context.Context) schema.Schema { "description": schema.StringAttribute{ Description: "Optional description of time service", Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "state": schema.StringAttribute{ Description: fmt.Sprintf("Indicator of the state of this Precision Time Service. One of: [%v]", fabricv4.AllowedPrecisionTimeServiceCreateResponseStateEnumValues), Computed: true, }, + "project_id": schema.StringAttribute{ + Description: "Equinix Fabric Project ID", + Optional: true, + Computed: true, + }, + "advance_configuration": schema.ListAttribute{ + Description: "An object that has advanced configuration options.", + CustomType: fwtypes.NewListNestedObjectTypeOf[AdvanceConfigurationModel](ctx), + ElementType: fwtypes.NewObjectTypeOf[AdvanceConfigurationModel](ctx), + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, + "account": schema.ListAttribute{ + Description: "Equinix User Account associated with Precision Time Service", + CustomType: fwtypes.NewListNestedObjectTypeOf[AccountModel](ctx), + ElementType: fwtypes.NewObjectTypeOf[AccountModel](ctx), + Computed: true, + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + }, + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + }, }, Blocks: map[string]schema.Block{ + "timeouts": timeouts.Block(ctx, timeouts.Opts{ + Create: true, + Read: true, + Update: true, + Delete: true, + }), "package": schema.SingleNestedBlock{ Description: "Precision Time Service Package Details", CustomType: fwtypes.NewObjectTypeOf[PackageModel](ctx), @@ -133,102 +175,6 @@ func resourceSchema(ctx context.Context) schema.Schema { }, }, }, - "advance_configuration": schema.SingleNestedBlock{ - Description: "An object that has advanced configuration options.", - CustomType: fwtypes.NewObjectTypeOf[AdvanceConfigurationModel](ctx), - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.UseStateForUnknown(), - }, - Attributes: map[string]schema.Attribute{ - "ntp": schema.ListAttribute{ - Description: "Advance Configuration for NTP; a list of MD5 objects", - Optional: true, - Computed: true, - CustomType: fwtypes.NewListNestedObjectTypeOf[MD5Model](ctx), - ElementType: fwtypes.NewObjectTypeOf[MD5Model](ctx), - }, - }, - Blocks: map[string]schema.Block{ - "ptp": schema.SingleNestedBlock{ - Description: "An object that has advanced PTP configuration.", - CustomType: fwtypes.NewObjectTypeOf[PTPModel](ctx), - Attributes: map[string]schema.Attribute{ - "time_scale": schema.StringAttribute{ - Description: "Time scale value. ARB denotes Arbitrary, and PTP denotes Precision Time Protocol.", - Optional: true, - Computed: true, - Validators: []validator.String{ - stringvalidator.OneOf( - string(fabricv4.PTPADVANCECONFIGURATIONTIMESCALE_ARB), - string(fabricv4.PTPADVANCECONFIGURATIONTIMESCALE_PTP), - ), - }, - }, - "domain": schema.Int64Attribute{ - Description: "Represents the domain number associated with the PTP profile. This is used to differentiate multiple PTP networks within a single physical network.", - Optional: true, - Computed: true, - }, - "priority_1": schema.Int64Attribute{ - Description: "Specifies the priority level 1 for the clock. The value helps in determining the best clock in the PTP network. Lower values are considered higher priority.", - Optional: true, - Computed: true, - }, - "priority_2": schema.Int64Attribute{ - Description: "Specifies the priority level 2 for the clock. It acts as a tie-breaker if multiple clocks have the same priority 1 value. Lower values are considered higher priority.", - Optional: true, - Computed: true, - }, - "log_announce_interval": schema.Int64Attribute{ - Description: "Represents the log2 interval between consecutive PTP announce messages. For example, a value of 0 implies an interval of 2^0 = 1 second.", - Optional: true, - Computed: true, - }, - "log_sync_interval": schema.Int64Attribute{ - Description: "Represents the log2 interval between consecutive PTP synchronization messages. A value of 0 implies an interval of 2^0 = 1 second.", - Optional: true, - Computed: true, - }, - "log_delay_req_interval": schema.Int64Attribute{ - Description: "Represents the log2 interval between consecutive PTP delay request messages. A value of 0 implies an interval of 2^0 = 1 second.", - Optional: true, - Computed: true, - }, - "transport_mode": schema.StringAttribute{ - Description: "Mode of transport for the Time Precision Service.", - Optional: true, - Computed: true, - Validators: []validator.String{ - stringvalidator.OneOf( - string(fabricv4.PTPADVANCECONFIGURATIONTRANSPORTMODE_MULTICAST), - string(fabricv4.PTPADVANCECONFIGURATIONTRANSPORTMODE_UNICAST), - string(fabricv4.PTPADVANCECONFIGURATIONTRANSPORTMODE_HYBRID), - ), - }, - }, - "grant_time": schema.Int64Attribute{ - Description: "Unicast Grant Time in seconds. For Multicast and Hybrid transport modes, grant time defaults to 300 seconds. For Unicast mode, grant time can be between 30 to 7200.", - Optional: true, - Computed: true, - }, - }, - }, - }, - }, - "project": schema.SingleNestedBlock{ - Description: "An object that contains the Equinix Fabric project_id used for linking the Time Precision Service to a specific Equinix Fabric Project", - CustomType: fwtypes.NewObjectTypeOf[ProjectModel](ctx), - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.UseStateForUnknown(), - }, - Attributes: map[string]schema.Attribute{ - "project_id": schema.StringAttribute{ - Description: "Equinix Fabric Project ID", - Optional: true, - Computed: true, - }, - }, - }, "connections": schema.ListNestedBlock{ Description: "An array of objects with unique identifiers of connections.", CustomType: fwtypes.NewListNestedObjectTypeOf[ConnectionModel](ctx), @@ -250,31 +196,6 @@ func resourceSchema(ctx context.Context) schema.Schema { }, }, }, - "account": schema.SingleNestedBlock{ - Description: "Equinix User Account associated with Precision Time Service", - CustomType: fwtypes.NewObjectTypeOf[AccountModel](ctx), - PlanModifiers: []planmodifier.Object{ - objectplanmodifier.UseStateForUnknown(), - }, - Attributes: map[string]schema.Attribute{ - "account_number": schema.Int64Attribute{ - Description: "Equinix User account number", - Computed: true, - }, - "is_reseller_account": schema.BoolAttribute{ - Description: "Equinix User Boolean flag indicating if it is a reseller account", - Computed: true, - }, - "org_id": schema.StringAttribute{ - Description: "Equinix User organization id", - Computed: true, - }, - "global_org_id": schema.StringAttribute{ - Description: "Equinix User global organization id", - Computed: true, - }, - }, - }, }, } }