Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 25 additions & 15 deletions stackit/internal/services/ske/cluster/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,9 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
"enabled": schema.BoolAttribute{
Description: "Flag to enable/disable Observability extensions.",
Required: true,
Validators: []validator.Bool{
validate.EnablesElseDeprecationWarning(path.MatchRelative().AtParent().AtName("instance_id"), "05/2027"),
},
},
"instance_id": schema.StringAttribute{
Description: "Observability instance ID to choose which Observability instance is used. Required when enabled is set to `true`.",
Expand All @@ -826,6 +829,9 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
"enabled": schema.BoolAttribute{
Description: "Is ACL enabled?",
Required: true,
Validators: []validator.Bool{
validate.EnablesElseDeprecationWarning(path.MatchRelative().AtParent().AtName("allowed_cidrs"), "05/2027"),
},
},
"allowed_cidrs": schema.ListAttribute{
Description: "Specify a list of CIDRs to whitelist.",
Expand All @@ -841,6 +847,10 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
"enabled": schema.BoolAttribute{
Description: "Flag to enable/disable DNS extensions",
Required: true,
Validators: []validator.Bool{
validate.EnablesElseDeprecationWarning(path.MatchRelative().AtParent().AtName("zones"), "05/2027"),
validate.EnablesElseDeprecationWarning(path.MatchRelative().AtParent().AtName("gateway_api"), "05/2027"),
},
},
"zones": schema.ListAttribute{
Description: "Specify a list of domain filters for externalDNS (e.g., `foo.runs.onstackit.cloud`)",
Expand Down Expand Up @@ -1214,7 +1224,7 @@ func toNodepoolsPayload(ctx context.Context, m *Model, availableMachineVersions
// zones
zs := []string{}
for _, v := range nodePool.AvailabilityZones.Elements() {
if v.IsNull() || v.IsUnknown() {
if utils.IsUndefined(v) {
continue
}
s, err := conversion.ToString(ctx, v)
Expand Down Expand Up @@ -1357,7 +1367,7 @@ func toHibernationsPayload(ctx context.Context, m *Model) (*ske.Hibernation, err
Start: h.Start.ValueString(),
End: h.End.ValueString(),
}
if !h.Timezone.IsNull() && !h.Timezone.IsUnknown() {
if !utils.IsUndefined(h.Timezone) {
tz := h.Timezone.ValueString()
sc.Timezone = &tz
}
Expand Down Expand Up @@ -1386,7 +1396,7 @@ func toAuditPayload(ctx context.Context, m *Model) (*ske.Audit, error) {
}

func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error) {
if m.Extensions.IsNull() || m.Extensions.IsUnknown() {
if utils.IsUndefined(m.Extensions) {
return nil, nil
}
ex := extensions{}
Expand All @@ -1396,7 +1406,7 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
}

var skeAcl *ske.ACL
if !(ex.ACL.IsNull() || ex.ACL.IsUnknown()) {
if !utils.IsUndefined(ex.ACL) {
acl := acl{}
diags = ex.ACL.As(ctx, &acl, basetypes.ObjectAsOptions{})
if diags.HasError() {
Expand Down Expand Up @@ -1443,7 +1453,7 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
}

var skeApplicationLoadBalancer *ske.ApplicationLoadBalancer
if !(ex.ApplicationLoadBalancer.IsNull() || ex.ApplicationLoadBalancer.IsUnknown()) {
if !utils.IsUndefined(ex.ApplicationLoadBalancer) {
applicationLoadBalancer := applicationLoadBalancer{}
diags = ex.ApplicationLoadBalancer.As(ctx, &applicationLoadBalancer, basetypes.ObjectAsOptions{})
if diags.HasError() {
Expand All @@ -1456,7 +1466,7 @@ func toExtensionsPayload(ctx context.Context, m *Model) (*ske.Extension, error)
}

var skeDNS *ske.DNS
if !(ex.DNS.IsNull() || ex.DNS.IsUnknown()) {
if !utils.IsUndefined(ex.DNS) {
dns := dns{}
diags = ex.DNS.As(ctx, &dns, basetypes.ObjectAsOptions{})
if diags.HasError() {
Expand Down Expand Up @@ -1514,7 +1524,7 @@ func parseMaintenanceWindowTime(t string) (time.Time, error) {
}

func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, error) {
if m.Maintenance.IsNull() || m.Maintenance.IsUnknown() {
if utils.IsUndefined(m.Maintenance) {
return nil, nil
}

Expand All @@ -1525,7 +1535,7 @@ func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, erro
}

var timeWindowStart time.Time
if !(maintenance.Start.IsNull() || maintenance.Start.IsUnknown()) {
if !utils.IsUndefined(maintenance.Start) {
tempTime, err := parseMaintenanceWindowTime(maintenance.Start.ValueString())
if err != nil {
return nil, fmt.Errorf("converting maintenance object: %w", err)
Expand All @@ -1534,7 +1544,7 @@ func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, erro
}

var timeWindowEnd time.Time
if !(maintenance.End.IsNull() || maintenance.End.IsUnknown()) {
if !utils.IsUndefined(maintenance.End) {
tempTime, err := parseMaintenanceWindowTime(maintenance.End.ValueString())
if err != nil {
return nil, fmt.Errorf("converting maintenance object: %w", err)
Expand All @@ -1555,7 +1565,7 @@ func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, erro
}

func toNetworkPayload(ctx context.Context, m *Model) (*ske.Network, error) {
if m.Network.IsNull() || m.Network.IsUnknown() {
if utils.IsUndefined(m.Network) {
return nil, nil
}

Expand Down Expand Up @@ -1665,7 +1675,7 @@ func mapNodePools(ctx context.Context, cl *ske.Cluster, model *Model) error {
modelNodePoolOSVersionMin := map[string]basetypes.StringValue{}

modelNodePools := []nodePool{}
if !model.NodePools.IsNull() && !model.NodePools.IsUnknown() {
if !utils.IsUndefined(model.NodePools) {
diags := model.NodePools.ElementsAs(ctx, &modelNodePools, false)
if diags.HasError() {
return core.DiagsToError(diags)
Expand Down Expand Up @@ -1708,7 +1718,7 @@ func mapNodePools(ctx context.Context, cl *ske.Cluster, model *Model) error {
}

taintsInModel := false
if i < len(modelNodePools) && !modelNodePools[i].Taints.IsNull() && !modelNodePools[i].Taints.IsUnknown() {
if i < len(modelNodePools) && !utils.IsUndefined(modelNodePools[i].Taints) {
taintsInModel = true
}
err := mapTaints(nodePoolResp.Taints, nodePool, taintsInModel)
Expand Down Expand Up @@ -1933,7 +1943,7 @@ func getMaintenanceTimes(ctx context.Context, cl *ske.Cluster, m *Model) (startT
endTimeAPI = cl.Maintenance.TimeWindow.End
}

if m.Maintenance.IsNull() || m.Maintenance.IsUnknown() {
if utils.IsUndefined(m.Maintenance) {
return startTimeAPI.Format("15:04:05Z07:00"), endTimeAPI.Format("15:04:05Z07:00"), nil
}

Expand All @@ -1944,7 +1954,7 @@ func getMaintenanceTimes(ctx context.Context, cl *ske.Cluster, m *Model) (startT
}

startTime = startTimeAPI.Format("15:04:05Z07:00")
if !(maintenance.Start.IsNull() || maintenance.Start.IsUnknown()) {
if !utils.IsUndefined(maintenance.Start) {
startTimeTF, err := time.Parse("15:04:05Z07:00", maintenance.Start.ValueString())
if err != nil {
return "", "", fmt.Errorf("parsing start time '%s' from TF config as RFC time: %w", maintenance.Start.ValueString(), err)
Expand All @@ -1956,7 +1966,7 @@ func getMaintenanceTimes(ctx context.Context, cl *ske.Cluster, m *Model) (startT
}

endTime = endTimeAPI.Format("15:04:05Z07:00")
if !(maintenance.End.IsNull() || maintenance.End.IsUnknown()) {
if !utils.IsUndefined(maintenance.End) {
endTimeTF, err := time.Parse("15:04:05Z07:00", maintenance.End.ValueString())
if err != nil {
return "", "", fmt.Errorf("parsing end time '%s' from TF config as RFC time: %w", maintenance.End.ValueString(), err)
Expand Down
93 changes: 93 additions & 0 deletions stackit/internal/validate/validate_bool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package validate

import (
"context"
"fmt"

"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/schema/validator"
)

// Bool validator factory
type BoolValidator struct {
description string
markdownDescription string
validate BoolValidationFn
}

type BoolValidationFn func(context.Context, validator.BoolRequest, *validator.BoolResponse)

var _ = validator.Bool(&BoolValidator{})

func (v *BoolValidator) Description(_ context.Context) string {
return v.description
}

func (v *BoolValidator) MarkdownDescription(_ context.Context) string {
return v.markdownDescription
}

func (v *BoolValidator) ValidateBool(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) { // nolint:gocritic // function signature required by Terraform
if req.ConfigValue.IsUnknown() {
return
}
v.validate(ctx, req, resp)
}

// "enables" returns a bool validator that prevents another attribute from being set if
// the own value is not true or not set. This attributed enables another attribute.
func enables(target path.Expression, callback func(*diag.Diagnostics, path.Path, path.Path)) *BoolValidator {
description := "this attribute enables another attribute to be set by being true"

return &BoolValidator{
description: description,
validate: func(ctx context.Context, req validator.BoolRequest, resp *validator.BoolResponse) {
expression := req.PathExpression.Merge(target)

matchedPaths, diags := req.Config.PathMatches(ctx, expression)
Comment thread
Manuelvaas marked this conversation as resolved.
Resolved
resp.Diagnostics.Append(diags...)

for _, targetPath := range matchedPaths {
var target attr.Value

diags := req.Config.GetAttribute(ctx, targetPath, &target)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

if target.IsNull() || target.IsUnknown() {
continue
}

if req.ConfigValue.IsNull() || !req.ConfigValue.ValueBool() {
callback(&resp.Diagnostics, req.Path, targetPath)
}
}
},
}
}

// Returns a bool validator based on "enables", which throws a deprecation warning instead of an error
func EnablesElseDeprecationWarning(target path.Expression, deprecationDate string) *BoolValidator {
return enables(target, func(diagnostics *diag.Diagnostics, ownPath path.Path, targetPath path.Path) {
diagnostics.AddAttributeWarning(
targetPath,
"Deprecation: Attribute can not be set",
fmt.Sprintf("This attribute %q can only be configured when %q is true. Will be enforced after %s.", targetPath.String(), ownPath.String(), deprecationDate),
)
})
}

// Returns a bool validator based on "enables", which throws an error
func EnablesElseError(target path.Expression) *BoolValidator {
return enables(target, func(diagnostics *diag.Diagnostics, ownPath path.Path, targetPath path.Path) {
diagnostics.AddAttributeError(
targetPath,
"Attribute can not be set",
fmt.Sprintf("This attribute %q can only be configured when %q is true.", targetPath.String(), ownPath.String()),
)
})
}
Loading
Loading