Hello,
I am building a resource in a provider in which I need to have optional attribute blocks, but need default values when they are not supplied.
Example HCL:
resource "adguard_config" "test" {
filtering = {
// enabled = true|false, defaults to true
update_interval = 1 // defaults to 24
}
// stats = {
// enabled = true|false, defaults to false
// interval = int, defaults to 24
// ignored = set, defaults to empty set
// }
}
Models:
type configResourceModel struct {
ID types.String `tfsdk:"id"`
LastUpdated types.String `tfsdk:"last_updated"`
Filtering types.Object `tfsdk:"filtering"`
Stats types.Object `tfsdk:"stats"`
}
type filteringModel struct {
Enabled types.Bool `tfsdk:"enabled"`
UpdateInterval types.Int64 `tfsdk:"update_interval"`
}
type statsConfigModel struct {
Enabled types.Bool `tfsdk:"enabled"`
Interval types.Int64 `tfsdk:"interval"`
Ignored types.Set `tfsdk:"ignored"`
}
Schema:
func (r *configResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "Internal identifier for this config",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"last_updated": schema.StringAttribute{
Description: "Timestamp of the last Terraform update of the config",
Computed: true,
},
"filtering": schema.SingleNestedAttribute{
Computed: true,
Optional: true,
Default: objectdefault.StaticValue(types.ObjectValueMust(
filteringModel{}.attrTypes(), filteringModel{}.defaultObject()),
),
Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{
Description: "Whether DNS filtering is enabled. Defaults to `true`",
Computed: true,
Optional: true,
Default: booldefault.StaticBool(true),
},
"update_interval": schema.Int64Attribute{
Description: "Update interval for all list-based filters, in hours. Defaults to `24`",
Computed: true,
Optional: true,
Default: int64default.StaticInt64(24),
Validators: []validator.Int64{
int64validator.OneOf([]int64{1, 12, 24, 72, 168}...),
},
},
},
},
"stats": schema.SingleNestedAttribute{
Computed: true,
Optional: true,
Default: objectdefault.StaticValue(types.ObjectValueMust(
queryLogConfigModel{}.attrTypes(), queryLogConfigModel{}.defaultObject()),
),
Attributes: map[string]schema.Attribute{
"enabled": schema.BoolAttribute{
Description: "Whether server statistics are enabled. Defaults to `true`",
Computed: true,
Optional: true,
Default: booldefault.StaticBool(true),
},
"interval": schema.Int64Attribute{
Description: "Time period for server statistics rotation, in hours. Defaults to `24` (1 day)",
Computed: true,
Optional: true,
Default: int64default.StaticInt64(24),
},
"ignored": schema.SetAttribute{
Description: "List of host names which should not be counted in the server statistics",
ElementType: types.StringType,
Computed: true,
Optional: true,
Validators: []validator.Set{
setvalidator.AlsoRequires(path.Expressions{
path.MatchRelative().AtParent().AtName("enabled"),
}...),
setvalidator.SizeAtLeast(1),
setvalidator.ValueStringsAre(
stringvalidator.RegexMatches(
regexp.MustCompile(`^[a-z0-9.-_]+$`),
"must be a valid domain name",
),
),
},
Default: setdefault.StaticValue(
types.SetNull(types.StringType),
),
},
},
},
},
}
}
As you can see, I am trying to use Default
on the top-level object, for which I have some helper functions to gather the types and default values, for when the entire block is not supplied:
func (o filteringModel) attrTypes() map[string]attr.Type {
return map[string]attr.Type{
"enabled": types.BoolType,
"update_interval": types.Int64Type,
}
}
func (o filteringModel) defaultObject() map[string]attr.Value {
return map[string]attr.Value{
"enabled": types.BoolValue(true),
"update_interval": types.Int64Value(24),
}
}
func (o statsConfigModel) attrTypes() map[string]attr.Type {
return map[string]attr.Type{
"enabled": types.BoolType,
"interval": types.Int64Type,
"ignored": types.SetType{ElemType: types.StringType},
}
}
func (o statsConfigModel) defaultObject() map[string]attr.Value {
return map[string]attr.Value{
"enabled": types.BoolValue(true),
"interval": types.Int64Value(1 * 24),
"ignored": basetypes.NewSetNull(types.StringType),
}
}
The problem I am seeing is when I supply the example HCL, the Stats
attribute in the plan has a null value, and I was expecting it to have the default values, per the Default
directive.
What am I missing here?
Thank you