Files
terraform-provider-unifi/internal/provider/settings/resource_setting_ntp.go
Mateusz Filipowicz 8b5ed14d8d feat: add NTP setting resource support with unifi_setting_ntp resource (#36)
* feat: add NTP setting resource support with `unifi_setting_ntp` resource

* linting

* fix missing method

* add missing validators
2025-03-02 01:10:41 +01:00

179 lines
6.5 KiB
Go

package settings
import (
"context"
"github.com/filipowm/go-unifi/unifi"
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
"github.com/filipowm/terraform-provider-unifi/internal/provider/validators"
"github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
)
// ntpModel represents the data model for NTP (Network Time Protocol) settings.
// It defines how NTP servers are configured for a UniFi site.
type ntpModel struct {
base.Model
NtpServer1 types.String `tfsdk:"ntp_server_1"`
NtpServer2 types.String `tfsdk:"ntp_server_2"`
NtpServer3 types.String `tfsdk:"ntp_server_3"`
NtpServer4 types.String `tfsdk:"ntp_server_4"`
Mode types.String `tfsdk:"mode"`
}
func (d *ntpModel) AsUnifiModel() (interface{}, diag.Diagnostics) {
diags := diag.Diagnostics{}
model := &unifi.SettingNtp{
ID: d.ID.ValueString(),
SettingPreference: d.Mode.ValueString(),
}
if d.Mode.ValueString() == "auto" {
model.NtpServer1 = ""
model.NtpServer2 = ""
model.NtpServer3 = ""
model.NtpServer4 = ""
} else {
if !base.IsEmptyString(d.NtpServer1) {
model.NtpServer1 = d.NtpServer1.ValueString()
}
if !base.IsEmptyString(d.NtpServer2) {
model.NtpServer2 = d.NtpServer2.ValueString()
}
if !base.IsEmptyString(d.NtpServer3) {
model.NtpServer3 = d.NtpServer3.ValueString()
}
if !base.IsEmptyString(d.NtpServer4) {
model.NtpServer4 = d.NtpServer4.ValueString()
}
}
return model, diags
}
func (d *ntpModel) Merge(other interface{}) diag.Diagnostics {
diags := diag.Diagnostics{}
model, ok := other.(*unifi.SettingNtp)
if !ok {
diags.AddError("Cannot merge", "Cannot merge type that is not *unifi.SettingNtp")
return diags
}
d.ID = types.StringValue(model.ID)
d.Mode = types.StringValue(model.SettingPreference)
if model.NtpServer1 != "" {
d.NtpServer1 = types.StringValue(model.NtpServer1)
}
if model.NtpServer2 != "" {
d.NtpServer2 = types.StringValue(model.NtpServer2)
}
if model.NtpServer3 != "" {
d.NtpServer3 = types.StringValue(model.NtpServer3)
}
if model.NtpServer4 != "" {
d.NtpServer4 = types.StringValue(model.NtpServer4)
}
return diags
}
var (
_ base.ResourceModel = &ntpModel{}
_ resource.Resource = &ntpResource{}
_ resource.ResourceWithConfigure = &ntpResource{}
_ resource.ResourceWithImportState = &ntpResource{}
_ resource.ResourceWithConfigValidators = &ntpResource{}
)
type ntpResource struct {
*BaseSettingResource[*ntpModel]
}
func (r *ntpResource) ConfigValidators(_ context.Context) []resource.ConfigValidator {
return []resource.ConfigValidator{
validators.RequiredNoneIf(path.MatchRoot("mode"), types.StringValue("auto"), path.MatchRoot("ntp_server_1"), path.MatchRoot("ntp_server_2"), path.MatchRoot("ntp_server_3"), path.MatchRoot("ntp_server_4")),
validators.ResourceIf(path.MatchRoot("mode"),
types.StringValue("manual"),
resourcevalidator.AtLeastOneOf(path.MatchRoot("ntp_server_1"), path.MatchRoot("ntp_server_2"), path.MatchRoot("ntp_server_3"), path.MatchRoot("ntp_server_4")),
),
}
}
func (r *ntpResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
serverValidators := func() []validator.String {
return []validator.String{
stringvalidator.Any(validators.Hostname(), validators.IPv4()),
}
}
resp.Schema = schema.Schema{
MarkdownDescription: "The `unifi_setting_ntp` resource allows you to configure Network Time Protocol (NTP) server settings for your UniFi network.\n\n" +
"NTP servers provide time synchronization for your network devices. This resource supports both automatic and manual NTP configuration modes.",
Attributes: map[string]schema.Attribute{
"id": base.ID(),
"site": base.SiteAttribute(),
"ntp_server_1": schema.StringAttribute{
MarkdownDescription: "Primary NTP server hostname or IP address. Must be a valid hostname (e.g., `pool.ntp.org`) or IPv4 address. " +
"Only applicable when `mode` is set to `manual`.",
Optional: true,
Computed: true,
Validators: serverValidators(),
},
"ntp_server_2": schema.StringAttribute{
MarkdownDescription: "Secondary NTP server hostname or IP address. Must be a valid hostname (e.g., `time.google.com`) or IPv4 address. " +
"Only applicable when `mode` is set to `manual`.",
Optional: true,
Computed: true,
Validators: serverValidators(),
},
"ntp_server_3": schema.StringAttribute{
MarkdownDescription: "Tertiary NTP server hostname or IP address. Must be a valid hostname or IPv4 address. " +
"Only applicable when `mode` is set to `manual`.",
Optional: true,
Computed: true,
Validators: serverValidators(),
},
"ntp_server_4": schema.StringAttribute{
MarkdownDescription: "Quaternary NTP server hostname or IP address. Must be a valid hostname or IPv4 address. " +
"Only applicable when `mode` is set to `manual`.",
Optional: true,
Computed: true,
Validators: serverValidators(),
},
"mode": schema.StringAttribute{
MarkdownDescription: "NTP server configuration mode. Valid values are:\n" +
"* `auto` - Use NTP servers configured on the controller\n" +
"* `manual` - Use custom NTP servers specified in this resource\n\n" +
"When set to `auto`, all NTP server fields will be cleared. " +
"When set to `manual`, at least one NTP server must be specified.",
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.OneOf("auto", "manual"),
},
},
},
}
}
// NewNtpResource creates a new instance of the NTP resource.
func NewNtpResource() resource.Resource {
r := &ntpResource{}
r.BaseSettingResource = NewBaseSettingResource(
"unifi_setting_ntp",
func() *ntpModel { return &ntpModel{} },
func(ctx context.Context, client *base.Client, site string) (interface{}, error) {
return client.GetSettingNtp(ctx, site)
},
func(ctx context.Context, client *base.Client, site string, body interface{}) (interface{}, error) {
return client.UpdateSettingNtp(ctx, site, body.(*unifi.SettingNtp))
},
)
return r
}