129 lines
3.8 KiB
Go
129 lines
3.8 KiB
Go
package base
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/filipowm/terraform-provider-unifi/internal/provider/types"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/attr"
|
|
"github.com/hashicorp/terraform-plugin-framework/diag"
|
|
"github.com/hashicorp/terraform-plugin-framework/path"
|
|
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
|
|
)
|
|
|
|
const (
|
|
featureEnabled featureStatus = iota
|
|
featureDisabled
|
|
)
|
|
|
|
type featureStatus int
|
|
|
|
type FeatureValidator interface {
|
|
RequireFeaturesEnabled(ctx context.Context, site string, features ...string) diag.Diagnostics
|
|
RequireFeaturesEnabledForPath(ctx context.Context, site string, attrPath path.Path, config tfsdk.Config, features ...string) diag.Diagnostics
|
|
}
|
|
|
|
type Features map[string]featureStatus
|
|
|
|
func (v Features) IsEnabled(feature string) bool {
|
|
return !v.IsUnavailable(feature) && v[feature] == featureEnabled
|
|
}
|
|
|
|
func (v Features) IsDisabled(feature string) bool {
|
|
return !v.IsUnavailable(feature) && v[feature] == featureDisabled
|
|
}
|
|
|
|
func (v Features) IsUnavailable(feature string) bool {
|
|
if _, ok := v[feature]; ok {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
type featureEnabledValidator struct {
|
|
client *Client
|
|
cache map[string]Features
|
|
|
|
lock sync.Mutex
|
|
}
|
|
|
|
func NewFeatureValidator(client *Client) FeatureValidator {
|
|
return &featureEnabledValidator{client: client, cache: make(map[string]Features), lock: sync.Mutex{}}
|
|
}
|
|
|
|
func (v *featureEnabledValidator) getFeatures(ctx context.Context, site string) Features {
|
|
if v.cache[site] != nil {
|
|
return v.cache[site]
|
|
}
|
|
v.lock.Lock()
|
|
defer v.lock.Unlock()
|
|
if v.cache[site] != nil {
|
|
return v.cache[site]
|
|
}
|
|
cache := make(map[string]featureStatus)
|
|
features, err := v.client.ListFeatures(ctx, site)
|
|
if err != nil {
|
|
// Return an empty Features map instead of nil to avoid potential nil pointer dereference
|
|
return Features{}
|
|
}
|
|
for _, feature := range features {
|
|
if feature.FeatureExists {
|
|
cache[feature.Name] = featureEnabled
|
|
} else {
|
|
cache[feature.Name] = featureDisabled
|
|
}
|
|
}
|
|
v.cache[site] = cache
|
|
return v.cache[site]
|
|
}
|
|
|
|
func (v *featureEnabledValidator) requireFeatures(ctx context.Context, site string, attrPath *path.Path, features ...string) diag.Diagnostics {
|
|
diags := diag.Diagnostics{}
|
|
if len(features) == 0 {
|
|
return diags
|
|
}
|
|
|
|
f := v.getFeatures(ctx, site)
|
|
var unavailableFeatures, disabledFeatures []string
|
|
for _, feature := range features {
|
|
if f.IsUnavailable(feature) {
|
|
unavailableFeatures = append(unavailableFeatures, feature)
|
|
}
|
|
if f.IsDisabled(feature) {
|
|
disabledFeatures = append(disabledFeatures, feature)
|
|
}
|
|
}
|
|
pathInfo := ""
|
|
if attrPath != nil {
|
|
pathInfo = fmt.Sprintf("%s is not supported. ", attrPath.String())
|
|
}
|
|
if len(unavailableFeatures) > 0 {
|
|
diags.AddError("Controller features not available", fmt.Sprintf("%sFeatures %s must be available on controller, but %s are not", pathInfo, strings.Join(features, ", "), strings.Join(unavailableFeatures, ", ")))
|
|
}
|
|
if len(disabledFeatures) > 0 {
|
|
diags.AddError("Controller features not disabled", fmt.Sprintf("%sFeatures %s must be enabled on controller, but %s are disabled", pathInfo, strings.Join(features, ", "), strings.Join(disabledFeatures, ", ")))
|
|
}
|
|
return diags
|
|
|
|
}
|
|
|
|
func (v *featureEnabledValidator) RequireFeaturesEnabled(ctx context.Context, site string, features ...string) diag.Diagnostics {
|
|
return v.requireFeatures(ctx, site, nil, features...)
|
|
}
|
|
|
|
func (v *featureEnabledValidator) RequireFeaturesEnabledForPath(ctx context.Context, site string, attrPath path.Path, config tfsdk.Config, features ...string) diag.Diagnostics {
|
|
diags := diag.Diagnostics{}
|
|
var val attr.Value
|
|
diags.Append(config.GetAttribute(context.Background(), attrPath, &val)...)
|
|
if diags.HasError() {
|
|
return diags
|
|
}
|
|
if !types.IsDefined(val) {
|
|
return diags
|
|
}
|
|
diags.Append(v.requireFeatures(ctx, site, &attrPath, features...)...)
|
|
return diags
|
|
}
|