feat: add automatic speedtest setting resource support with unifi_setting_auto_speedtest resource (#32)
* feat: add automatic speedtest setting resource support with `unifi_setting_auto_speedtest` resource * restore lowered test paralellism * refactoring and fixes * fix speedtest after refactor * run speedtest test on versions [7.2,7.4.156), cause later it was removed from USG which is used in tests
This commit is contained in:
committed by
GitHub
parent
a36940b019
commit
273d0daddd
2
Makefile
2
Makefile
@@ -13,4 +13,4 @@ build:
|
||||
.PHONY: testacc
|
||||
testacc:
|
||||
go build ./...
|
||||
TF_LOG_PROVIDER=debug TF_ACC=1 go test $(TEST) -v -count $(TEST_COUNT) -timeout $(TEST_TIMEOUT) $(TESTARGS)
|
||||
TF_LOG_PROVIDER=debug TF_ACC=1 go test $(TEST) -test.parallel 2 -v -count $(TEST_COUNT) -timeout $(TEST_TIMEOUT) $(TESTARGS)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package acctest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
pt "github.com/filipowm/terraform-provider-unifi/internal/provider/testing"
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAccSettingAutoSpeedtest(t *testing.T) {
|
||||
t.Skip("Auto Speedtest is not supported on test controller")
|
||||
AcceptanceTest(t, AcceptanceTestCase{
|
||||
MinVersion: version.Must(version.NewVersion("7.2")),
|
||||
VersionConstraint: "< 7.5",
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSettingAutoSpeedtestConfig(true, "0 0 * * *"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("unifi_setting_auto_speedtest.test", "enabled", "true"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_auto_speedtest.test", "cron", "0 0 * * *"),
|
||||
),
|
||||
},
|
||||
pt.ImportStep("unifi_setting_auto_speedtest.test"),
|
||||
{
|
||||
Config: testAccSettingAutoSpeedtestConfig(false, "0 0 * * *"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("unifi_setting_auto_speedtest.test", "enabled", "false"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_auto_speedtest.test", "cron", "0 0 * * *"),
|
||||
),
|
||||
},
|
||||
pt.ImportStep("unifi_setting_auto_speedtest.test"),
|
||||
{
|
||||
Config: testAccSettingAutoSpeedtestConfig(true, "5 0 * * *"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr("unifi_setting_auto_speedtest.test", "enabled", "true"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_auto_speedtest.test", "cron", "5 0 * * *"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TODO remove when controller changed from USG, which has removed support of speedtest since 7.4. Other controllers still have it.
|
||||
func TestAccSettingAutoSpeedtest_unsupported(t *testing.T) {
|
||||
AcceptanceTest(t, AcceptanceTestCase{
|
||||
VersionConstraint: ">= 7.5",
|
||||
Steps: []resource.TestStep{
|
||||
{
|
||||
Config: testAccSettingAutoSpeedtestConfig(true, "0 0 * * *"),
|
||||
ExpectError: regexp.MustCompile("Auto Speedtest is not supported on this controller"),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccSettingAutoSpeedtestConfig(enabled bool, cron string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "unifi_setting_auto_speedtest" "test" {
|
||||
enabled = %t
|
||||
cron = %q
|
||||
}
|
||||
`, enabled, cron)
|
||||
}
|
||||
@@ -19,6 +19,8 @@ func TestAccSettingCountry(t *testing.T) {
|
||||
{
|
||||
Config: testAccSettingCountryConfig("US"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("unifi_setting_country.test", "id"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_country.test", "site", "default"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_country.test", "code", "US"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_country.test", "code_numeric", "840"),
|
||||
),
|
||||
@@ -28,6 +30,8 @@ func TestAccSettingCountry(t *testing.T) {
|
||||
{
|
||||
Config: testAccSettingCountryConfig("PL"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttrSet("unifi_setting_country.test", "id"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_country.test", "site", "default"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_country.test", "code", "PL"),
|
||||
resource.TestCheckResourceAttr("unifi_setting_country.test", "code_numeric", "616"),
|
||||
),
|
||||
@@ -37,8 +41,10 @@ func TestAccSettingCountry(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
var invalidCountryCodeErrorRegex = regexp.MustCompile("ISO 3166-1 alpha-2")
|
||||
var stringLengthExactly2Regex = regexp.MustCompile("string length must be exactly 2")
|
||||
var (
|
||||
invalidCountryCodeErrorRegex = regexp.MustCompile("ISO 3166-1 alpha-2")
|
||||
stringLengthExactly2Regex = regexp.MustCompile("string length must be exactly 2")
|
||||
)
|
||||
|
||||
func TestAccSettingCountry_invalidCode(t *testing.T) {
|
||||
AcceptanceTest(t, AcceptanceTestCase{
|
||||
|
||||
@@ -2,34 +2,59 @@ package base
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
type BaseData interface {
|
||||
type Resource interface {
|
||||
SetClient(client *Client)
|
||||
}
|
||||
|
||||
type Site struct {
|
||||
// ResourceModel defines the interface that all setting models must implement
|
||||
type ResourceModel interface {
|
||||
GetSite() string
|
||||
GetRawSite() types.String
|
||||
SetSite(string)
|
||||
GetID() string
|
||||
GetRawID() types.String
|
||||
SetID(string)
|
||||
AsUnifiModel() (interface{}, diag.Diagnostics)
|
||||
Merge(interface{}) diag.Diagnostics
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
ID types.String `tfsdk:"id"`
|
||||
Site types.String `tfsdk:"site"`
|
||||
}
|
||||
|
||||
func NewSite(str string) Site {
|
||||
return Site{
|
||||
Site: types.StringValue(str),
|
||||
}
|
||||
func (b *Model) GetID() string {
|
||||
return b.ID.ValueString()
|
||||
}
|
||||
|
||||
func (s *Site) SetSite(site string) {
|
||||
s.Site = types.StringValue(site)
|
||||
func (b *Model) GetRawID() types.String {
|
||||
return b.ID
|
||||
}
|
||||
|
||||
func (s *Site) AsString() string {
|
||||
return s.Site.ValueString()
|
||||
func (b *Model) SetID(id string) {
|
||||
b.ID = types.StringValue(id)
|
||||
}
|
||||
|
||||
func ConfigureDatasource(base BaseData, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
func (b *Model) GetSite() string {
|
||||
return b.Site.ValueString()
|
||||
}
|
||||
|
||||
func (b *Model) GetRawSite() types.String {
|
||||
return b.Site
|
||||
}
|
||||
|
||||
func (b *Model) SetSite(site string) {
|
||||
b.Site = types.StringValue(site)
|
||||
}
|
||||
|
||||
func ConfigureDatasource(base Resource, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
@@ -46,7 +71,7 @@ func ConfigureDatasource(base BaseData, req datasource.ConfigureRequest, resp *d
|
||||
base.SetClient(cfg)
|
||||
}
|
||||
|
||||
func ConfigureResource(base BaseData, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
func ConfigureResource(base Resource, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
if req.ProviderData == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -80,11 +80,11 @@ type Client struct {
|
||||
Version *version.Version
|
||||
}
|
||||
|
||||
func (c *Client) ResolveSite(site *Site) string {
|
||||
if site == nil || IsEmptyString(site.Site) {
|
||||
func (c *Client) ResolveSite(res ResourceModel) string {
|
||||
if res == nil || IsEmptyString(res.GetRawSite()) {
|
||||
return c.Site
|
||||
}
|
||||
return site.AsString()
|
||||
return res.GetSite()
|
||||
}
|
||||
|
||||
func CreateHttpTransport(insecure bool) http.RoundTripper {
|
||||
|
||||
12
internal/provider/base/errors.go
Normal file
12
internal/provider/base/errors.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func ErrorInvalidModelMergeTarget(expectedType, actualType interface{}) diag.Diagnostic {
|
||||
e := reflect.TypeOf(&expectedType).Elem().String()
|
||||
a := reflect.TypeOf(&actualType).Elem().String()
|
||||
return diag.NewErrorDiagnostic("Invalid model merge target", "Expected target type to be the same a receiver: "+e+". Was : "+a)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
var (
|
||||
_ datasource.DataSource = &dnsRecordDatasource{}
|
||||
_ datasource.DataSourceWithConfigure = &dnsRecordDatasource{}
|
||||
_ base.BaseData = &dnsRecordDatasource{}
|
||||
_ base.Resource = &dnsRecordDatasource{}
|
||||
_ datasource.DataSourceWithConfigValidators = &dnsRecordDatasource{}
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
var (
|
||||
_ datasource.DataSource = &dnsRecordsDatasource{}
|
||||
_ datasource.DataSourceWithConfigure = &dnsRecordsDatasource{}
|
||||
_ base.BaseData = &dnsRecordsDatasource{}
|
||||
_ base.Resource = &dnsRecordsDatasource{}
|
||||
)
|
||||
|
||||
type dnsRecordsDatasource struct {
|
||||
|
||||
@@ -19,7 +19,7 @@ var (
|
||||
_ resource.Resource = &dnsRecordResource{}
|
||||
_ resource.ResourceWithConfigure = &dnsRecordResource{}
|
||||
_ resource.ResourceWithImportState = &dnsRecordResource{}
|
||||
_ base.BaseData = &dnsRecordResource{}
|
||||
_ base.Resource = &dnsRecordResource{}
|
||||
)
|
||||
|
||||
type dnsRecordResource struct {
|
||||
|
||||
@@ -168,6 +168,7 @@ func (p *unifiProvider) Configure(ctx context.Context, req provider.ConfigureReq
|
||||
func (p *unifiProvider) Resources(_ context.Context) []func() resource.Resource {
|
||||
return []func() resource.Resource{
|
||||
dns.NewDnsRecordResource,
|
||||
settings.NewAutoSpeedtestResource,
|
||||
settings.NewCountryResource,
|
||||
}
|
||||
}
|
||||
|
||||
189
internal/provider/settings/base_setting_resource.go
Normal file
189
internal/provider/settings/base_setting_resource.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/filipowm/go-unifi/unifi"
|
||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
)
|
||||
|
||||
// BaseSettingResource provides common functionality for all setting resources
|
||||
type BaseSettingResource[T base.ResourceModel] struct {
|
||||
client *base.Client
|
||||
typeName string
|
||||
modelFactory func() T
|
||||
getter func(context.Context, *base.Client, string) (interface{}, error)
|
||||
updater func(context.Context, *base.Client, string, interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
// NewBaseSettingResource creates a new base setting resource
|
||||
func NewBaseSettingResource[T base.ResourceModel](
|
||||
typeName string,
|
||||
modelFactory func() T,
|
||||
getter func(context.Context, *base.Client, string) (interface{}, error),
|
||||
updater func(context.Context, *base.Client, string, interface{}) (interface{}, error),
|
||||
) *BaseSettingResource[T] {
|
||||
return &BaseSettingResource[T]{
|
||||
typeName: typeName,
|
||||
modelFactory: modelFactory,
|
||||
getter: getter,
|
||||
updater: updater,
|
||||
}
|
||||
}
|
||||
|
||||
// GetClient returns the UniFi client
|
||||
func (b *BaseSettingResource[T]) GetClient() *base.Client {
|
||||
return b.client
|
||||
}
|
||||
|
||||
// SetClient sets the UniFi client
|
||||
func (b *BaseSettingResource[T]) SetClient(client *base.Client) {
|
||||
b.client = client
|
||||
}
|
||||
|
||||
func (b *BaseSettingResource[T]) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
base.ConfigureResource(b, req, resp)
|
||||
}
|
||||
|
||||
func (b *BaseSettingResource[T]) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = b.typeName
|
||||
}
|
||||
|
||||
func (b *BaseSettingResource[T]) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
id, site := base.ImportIDWithSite(req, resp)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
state := b.modelFactory()
|
||||
state.SetID(id)
|
||||
state.SetSite(site)
|
||||
b.read(ctx, site, state, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (b *BaseSettingResource[T]) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
if b.client == nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Client Not Configured",
|
||||
"Expected configured client. Please report this issue to the provider developers.",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var plan T
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
body, diags := plan.AsUnifiModel()
|
||||
|
||||
if diags.HasError() {
|
||||
resp.Diagnostics.Append(diags...)
|
||||
return
|
||||
}
|
||||
site := b.client.ResolveSite(plan)
|
||||
|
||||
res, err := b.updater(ctx, b.client, site, body)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error creating settings", err.Error())
|
||||
return
|
||||
}
|
||||
if res == nil {
|
||||
resp.Diagnostics.AddError("Error creating settings", fmt.Sprintf("No %[1]s settings returned from the UniFi controller. %[1]s might not be supported on this controller", b.typeName))
|
||||
return
|
||||
}
|
||||
plan.Merge(res)
|
||||
plan.SetSite(site)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (b *BaseSettingResource[T]) read(ctx context.Context, site string, state T, diag *diag.Diagnostics) {
|
||||
if b.client == nil {
|
||||
diag.AddError(
|
||||
"Client Not Configured",
|
||||
"Expected configured client. Please report this issue to the provider developers.",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := b.getter(ctx, b.client, site)
|
||||
if err != nil {
|
||||
if errors.Is(err, unifi.ErrNotFound) {
|
||||
diag.AddError("Settings not found", "The settings were not found in the UniFi controller")
|
||||
} else {
|
||||
diag.AddError("Error reading settings", err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
if res != nil {
|
||||
state.Merge(res)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BaseSettingResource[T]) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
if b.client == nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Client Not Configured",
|
||||
"Expected configured client. Please report this issue to the provider developers.",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var state T
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
site := b.client.ResolveSite(state)
|
||||
b.read(ctx, site, state, &resp.Diagnostics)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
state.SetSite(site)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (b *BaseSettingResource[T]) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
if b.client == nil {
|
||||
resp.Diagnostics.AddError(
|
||||
"Client Not Configured",
|
||||
"Expected configured client. Please report this issue to the provider developers.",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var plan, state T
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
body, diags := plan.AsUnifiModel()
|
||||
if diags.HasError() {
|
||||
resp.Diagnostics.Append(diags...)
|
||||
return
|
||||
}
|
||||
site := b.client.ResolveSite(plan)
|
||||
|
||||
res, err := b.updater(ctx, b.client, site, body)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error updating settings", err.Error())
|
||||
return
|
||||
}
|
||||
state.Merge(res)
|
||||
state.SetSite(site)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (b *BaseSettingResource[T]) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) {
|
||||
// Not supported
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/filipowm/go-unifi/unifi"
|
||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||
"github.com/hashicorp/terraform-plugin-framework/diag"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource"
|
||||
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
var (
|
||||
_ resource.Resource = &autoSpeedtestResource{}
|
||||
_ resource.ResourceWithConfigure = &autoSpeedtestResource{}
|
||||
_ resource.ResourceWithImportState = &autoSpeedtestResource{}
|
||||
_ base.Resource = &autoSpeedtestResource{}
|
||||
)
|
||||
|
||||
type autoSpeedtestModel struct {
|
||||
base.Model
|
||||
CronExpression types.String `tfsdk:"cron"`
|
||||
Enabled types.Bool `tfsdk:"enabled"`
|
||||
}
|
||||
|
||||
func (d *autoSpeedtestModel) AsUnifiModel() (interface{}, diag.Diagnostics) {
|
||||
return &unifi.SettingAutoSpeedtest{
|
||||
ID: d.ID.ValueString(),
|
||||
CronExpr: d.CronExpression.ValueString(),
|
||||
Enabled: d.Enabled.ValueBool(),
|
||||
}, diag.Diagnostics{}
|
||||
}
|
||||
|
||||
func (d *autoSpeedtestModel) Merge(other interface{}) diag.Diagnostics {
|
||||
if typed, ok := other.(*unifi.SettingAutoSpeedtest); ok {
|
||||
d.ID = types.StringValue(typed.ID)
|
||||
d.CronExpression = types.StringValue(typed.CronExpr)
|
||||
d.Enabled = types.BoolValue(typed.Enabled)
|
||||
}
|
||||
return diag.Diagnostics{}
|
||||
}
|
||||
|
||||
type autoSpeedtestResource struct {
|
||||
*BaseSettingResource[*autoSpeedtestModel]
|
||||
}
|
||||
|
||||
func checkAutoSpeedtestUnsupportedError(err error) error {
|
||||
if base.IsServerErrorContains(err, "api.err.SpeedTestNotSupported") {
|
||||
return fmt.Errorf("Auto Speedtest is not supported on this controller")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func NewAutoSpeedtestResource() resource.Resource {
|
||||
r := &autoSpeedtestResource{}
|
||||
r.BaseSettingResource = NewBaseSettingResource(
|
||||
"unifi_setting_auto_speedtest",
|
||||
func() *autoSpeedtestModel { return &autoSpeedtestModel{} },
|
||||
func(ctx context.Context, client *base.Client, site string) (interface{}, error) {
|
||||
res, err := client.GetSettingAutoSpeedtest(ctx, site)
|
||||
if err != nil {
|
||||
return nil, checkAutoSpeedtestUnsupportedError(err)
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
func(ctx context.Context, client *base.Client, site string, body interface{}) (interface{}, error) {
|
||||
res, err := client.UpdateSettingAutoSpeedtest(ctx, site, body.(*unifi.SettingAutoSpeedtest))
|
||||
if err != nil {
|
||||
return nil, checkAutoSpeedtestUnsupportedError(err)
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
)
|
||||
return r
|
||||
}
|
||||
|
||||
func (a *autoSpeedtestResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
resp.Schema = schema.Schema{
|
||||
MarkdownDescription: "The `unifi_setting_auto_speedtest` resource manages the automatic speedtest settings in the UniFi controller." +
|
||||
"Automatic speedtests can be scheduled to run at regular intervals to monitor the network performance.\n\n" +
|
||||
"**NOTE:** Automatic speedtests where not verified and tested on all UniFi controller versions due to limitations of controller used in acceptance testing. ",
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"id": base.ID(),
|
||||
"site": base.SiteAttribute(),
|
||||
"cron": schema.StringAttribute{
|
||||
MarkdownDescription: "Cron expression defining the schedule for automatic speedtests.",
|
||||
Optional: true,
|
||||
},
|
||||
"enabled": schema.BoolAttribute{
|
||||
MarkdownDescription: "Whether the automatic speedtest is enabled.",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/biter777/countries"
|
||||
"github.com/filipowm/go-unifi/unifi"
|
||||
"github.com/filipowm/terraform-provider-unifi/internal/provider/base"
|
||||
@@ -14,70 +13,54 @@ import (
|
||||
"github.com/hashicorp/terraform-plugin-framework/types"
|
||||
)
|
||||
|
||||
type countryModel struct {
|
||||
base.Site
|
||||
ID types.String `tfsdk:"id"`
|
||||
Code types.String `tfsdk:"code"`
|
||||
CodeNumeric types.Int32 `tfsdk:"code_numeric"`
|
||||
}
|
||||
|
||||
func (d *countryModel) asUnifiModel() *unifi.SettingCountry {
|
||||
code := countries.ByName(d.Code.ValueString())
|
||||
return &unifi.SettingCountry{
|
||||
ID: d.ID.ValueString(),
|
||||
Code: int(code),
|
||||
}
|
||||
}
|
||||
|
||||
func (d *countryModel) merge(other *unifi.SettingCountry) {
|
||||
d.ID = types.StringValue(other.ID)
|
||||
// UniFi uses numeric codes, so we need to convert the alpha-2 code to the numeric code, but we store both
|
||||
code := countries.ByNumeric(other.Code)
|
||||
d.Code = types.StringValue(code.Alpha2())
|
||||
d.CodeNumeric = types.Int32Value(int32(code))
|
||||
}
|
||||
|
||||
var (
|
||||
_ resource.Resource = &countryResource{}
|
||||
_ resource.ResourceWithConfigure = &countryResource{}
|
||||
_ resource.ResourceWithImportState = &countryResource{}
|
||||
_ base.BaseData = &countryResource{}
|
||||
_ base.Resource = &countryResource{}
|
||||
)
|
||||
|
||||
type countryResource struct {
|
||||
client *base.Client
|
||||
type countryModel struct {
|
||||
base.Model
|
||||
Code types.String `tfsdk:"code"`
|
||||
CodeNumeric types.Int32 `tfsdk:"code_numeric"`
|
||||
}
|
||||
|
||||
func (c *countryResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
|
||||
id, site := base.ImportIDWithSite(req, resp)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
func (d *countryModel) AsUnifiModel() (interface{}, diag.Diagnostics) {
|
||||
code := countries.ByName(d.Code.ValueString())
|
||||
return &unifi.SettingCountry{
|
||||
ID: d.ID.ValueString(),
|
||||
Code: int(code),
|
||||
}, diag.Diagnostics{}
|
||||
}
|
||||
state := countryModel{
|
||||
ID: types.StringValue(id),
|
||||
Site: base.NewSite(site),
|
||||
|
||||
func (d *countryModel) Merge(other interface{}) diag.Diagnostics {
|
||||
if typed, ok := other.(*unifi.SettingCountry); ok {
|
||||
d.ID = types.StringValue(typed.ID)
|
||||
code := countries.ByNumeric(typed.Code)
|
||||
d.Code = types.StringValue(code.Alpha2())
|
||||
d.CodeNumeric = types.Int32Value(int32(code))
|
||||
}
|
||||
c.read(ctx, site, &state, &resp.Diagnostics)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
return diag.Diagnostics{}
|
||||
}
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
|
||||
type countryResource struct {
|
||||
*BaseSettingResource[*countryModel]
|
||||
}
|
||||
|
||||
func NewCountryResource() resource.Resource {
|
||||
return &countryResource{}
|
||||
}
|
||||
|
||||
func (c *countryResource) SetClient(client *base.Client) {
|
||||
c.client = client
|
||||
}
|
||||
|
||||
func (c *countryResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
|
||||
base.ConfigureResource(c, req, resp)
|
||||
}
|
||||
|
||||
func (c *countryResource) Metadata(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) {
|
||||
resp.TypeName = "unifi_setting_country"
|
||||
r := &countryResource{}
|
||||
r.BaseSettingResource = NewBaseSettingResource(
|
||||
"unifi_setting_country",
|
||||
func() *countryModel { return &countryModel{} },
|
||||
func(ctx context.Context, client *base.Client, site string) (interface{}, error) {
|
||||
return client.GetSettingCountry(ctx, site)
|
||||
},
|
||||
func(ctx context.Context, client *base.Client, site string, body interface{}) (interface{}, error) {
|
||||
return client.UpdateSettingCountry(ctx, site, body.(*unifi.SettingCountry))
|
||||
},
|
||||
)
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *countryResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
|
||||
@@ -101,76 +84,3 @@ func (c *countryResource) Schema(_ context.Context, _ resource.SchemaRequest, re
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *countryResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
|
||||
var plan countryModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
body := plan.asUnifiModel()
|
||||
site := c.client.ResolveSite(&plan.Site)
|
||||
|
||||
res, err := c.client.UpdateSettingCountry(ctx, site, body)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error creating country settings", err.Error())
|
||||
return
|
||||
}
|
||||
plan.merge(res)
|
||||
plan.Site.SetSite(site)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
|
||||
}
|
||||
|
||||
func (c *countryResource) read(ctx context.Context, site string, state *countryModel, diag *diag.Diagnostics) {
|
||||
res, err := c.client.GetSettingCountry(ctx, site)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, unifi.ErrNotFound) {
|
||||
diag.AddError("Country settings not found", "The country settings were not found in the UniFi controller")
|
||||
} else {
|
||||
diag.AddError("Error reading country settings", err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
state.merge(res)
|
||||
}
|
||||
|
||||
func (c *countryResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
|
||||
var state countryModel
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
site := c.client.ResolveSite(&state.Site)
|
||||
c.read(ctx, site, &state, &resp.Diagnostics)
|
||||
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
(&state).Site.SetSite(site)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (c *countryResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
|
||||
var plan, state countryModel
|
||||
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
|
||||
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
|
||||
if resp.Diagnostics.HasError() {
|
||||
return
|
||||
}
|
||||
body := plan.asUnifiModel()
|
||||
site := c.client.ResolveSite(&plan.Site)
|
||||
|
||||
res, err := c.client.UpdateSettingCountry(ctx, site, body)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error updating country settings", err.Error())
|
||||
return
|
||||
}
|
||||
state.merge(res)
|
||||
state.Site.SetSite(site)
|
||||
resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
|
||||
}
|
||||
|
||||
func (c *countryResource) Delete(_ context.Context, _ resource.DeleteRequest, _ *resource.DeleteResponse) {
|
||||
// Not supported
|
||||
}
|
||||
|
||||
@@ -26,8 +26,10 @@ func TestSettingCountry_ProperCountryCodeMappingFromModel(t *testing.T) {
|
||||
model := countryModel{
|
||||
Code: types.StringValue(tc.code),
|
||||
}
|
||||
unifiModel := model.asUnifiModel()
|
||||
assert.Equal(t, tc.expectedNumericCode, unifiModel.Code)
|
||||
unifiModel, _ := model.AsUnifiModel()
|
||||
typed, ok := unifiModel.(*unifi.SettingCountry)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, tc.expectedNumericCode, typed.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -52,7 +54,7 @@ func TestSettingCountry_ProperCountryCodeMappingToModel(t *testing.T) {
|
||||
Code: tc.numericCode,
|
||||
}
|
||||
model := countryModel{}
|
||||
model.merge(unifiModel)
|
||||
model.Merge(unifiModel)
|
||||
assert.Equal(t, tc.expectedCode, model.Code.ValueString())
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user