SKE Cluster - Change field model lists to use types.List (#118)

* Change cluster model lists to types.List

* Lint fix

* Fix inconsistent state

* Acc test fixes

---------

Co-authored-by: Henrique Santos <henrique.santos@freiheit.com>
This commit is contained in:
Henrique Santos 2023-10-31 17:35:03 +00:00 committed by GitHub
parent 7188e13e92
commit 852516e081
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 302 additions and 144 deletions

View file

@ -54,20 +54,21 @@ var (
)
type Cluster struct {
Id types.String `tfsdk:"id"` // needed by TF
ProjectId types.String `tfsdk:"project_id"`
Name types.String `tfsdk:"name"`
KubernetesVersion types.String `tfsdk:"kubernetes_version"`
KubernetesVersionUsed types.String `tfsdk:"kubernetes_version_used"`
AllowPrivilegedContainers types.Bool `tfsdk:"allow_privileged_containers"`
NodePools []NodePool `tfsdk:"node_pools"`
Maintenance types.Object `tfsdk:"maintenance"`
Hibernations []Hibernation `tfsdk:"hibernations"`
Extensions *Extensions `tfsdk:"extensions"`
KubeConfig types.String `tfsdk:"kube_config"`
Id types.String `tfsdk:"id"` // needed by TF
ProjectId types.String `tfsdk:"project_id"`
Name types.String `tfsdk:"name"`
KubernetesVersion types.String `tfsdk:"kubernetes_version"`
KubernetesVersionUsed types.String `tfsdk:"kubernetes_version_used"`
AllowPrivilegedContainers types.Bool `tfsdk:"allow_privileged_containers"`
NodePools types.List `tfsdk:"node_pools"`
Maintenance types.Object `tfsdk:"maintenance"`
Hibernations types.List `tfsdk:"hibernations"`
Extensions *Extensions `tfsdk:"extensions"`
KubeConfig types.String `tfsdk:"kube_config"`
}
type NodePool struct {
// Struct corresponding to Cluster.NodePools[i]
type nodePool struct {
Name types.String `tfsdk:"name"`
MachineType types.String `tfsdk:"machine_type"`
OSName types.String `tfsdk:"os_name"`
@ -79,17 +80,44 @@ type NodePool struct {
VolumeType types.String `tfsdk:"volume_type"`
VolumeSize types.Int64 `tfsdk:"volume_size"`
Labels types.Map `tfsdk:"labels"`
Taints []Taint `tfsdk:"taints"`
Taints types.List `tfsdk:"taints"`
CRI types.String `tfsdk:"cri"`
AvailabilityZones types.List `tfsdk:"availability_zones"`
}
type Taint struct {
// Types corresponding to odePool
var nodePoolTypes = map[string]attr.Type{
"name": basetypes.StringType{},
"machine_type": basetypes.StringType{},
"os_name": basetypes.StringType{},
"os_version": basetypes.StringType{},
"minimum": basetypes.Int64Type{},
"maximum": basetypes.Int64Type{},
"max_surge": basetypes.Int64Type{},
"max_unavailable": basetypes.Int64Type{},
"volume_type": basetypes.StringType{},
"volume_size": basetypes.Int64Type{},
"labels": basetypes.MapType{ElemType: types.StringType},
"taints": basetypes.ListType{ElemType: types.ObjectType{AttrTypes: taintTypes}},
"cri": basetypes.StringType{},
"availability_zones": basetypes.ListType{ElemType: types.StringType},
}
// Struct corresponding to nodePool.Taints[i]
type taint struct {
Effect types.String `tfsdk:"effect"`
Key types.String `tfsdk:"key"`
Value types.String `tfsdk:"value"`
}
// Types corresponding to taint
var taintTypes = map[string]attr.Type{
"effect": basetypes.StringType{},
"key": basetypes.StringType{},
"value": basetypes.StringType{},
}
// Struct corresponding to Cluster.Maintenance
type Maintenance struct {
EnableKubernetesVersionUpdates types.Bool `tfsdk:"enable_kubernetes_version_updates"`
EnableMachineImageVersionUpdates types.Bool `tfsdk:"enable_machine_image_version_updates"`
@ -97,6 +125,7 @@ type Maintenance struct {
End types.String `tfsdk:"end"`
}
// Types corresponding to Maintenance
var maintenanceTypes = map[string]attr.Type{
"enable_kubernetes_version_updates": basetypes.BoolType{},
"enable_machine_image_version_updates": basetypes.BoolType{},
@ -104,12 +133,20 @@ var maintenanceTypes = map[string]attr.Type{
"end": basetypes.StringType{},
}
type Hibernation struct {
// Struct corresponding to Cluster.Hibernations[i]
type hibernation struct {
Start types.String `tfsdk:"start"`
End types.String `tfsdk:"end"`
Timezone types.String `tfsdk:"timezone"`
}
// Types corresponding to hibernation
var hibernationTypes = map[string]attr.Type{
"start": basetypes.StringType{},
"end": basetypes.StringType{},
"timezone": basetypes.StringType{},
}
type Extensions struct {
Argus *ArgusExtension `tfsdk:"argus"`
ACL *ACL `tfsdk:"acl"`
@ -549,13 +586,21 @@ func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag
if hasDeprecatedVersion {
diags.AddWarning("Deprecated Kubernetes version", fmt.Sprintf("Version %s of Kubernetes is deprecated, please update it", *kubernetes.Version))
}
nodePools := toNodepoolsPayload(ctx, model)
nodePools, err := toNodepoolsPayload(ctx, model)
if err != nil {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating node pools API payload: %v", err))
return
}
maintenance, err := toMaintenancePayload(ctx, model)
if err != nil {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating maintenance API payload: %v", err))
return
}
hibernations := toHibernationsPayload(model)
hibernations, err := toHibernationsPayload(ctx, model)
if err != nil {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating hibernations API payload: %v", err))
return
}
extensions, err := toExtensionsPayload(ctx, model)
if err != nil {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating extension API payload: %v", err))
@ -608,13 +653,26 @@ func (r *clusterResource) getCredential(ctx context.Context, model *Cluster) err
return nil
}
func toNodepoolsPayload(ctx context.Context, m *Cluster) []ske.Nodepool {
func toNodepoolsPayload(ctx context.Context, m *Cluster) ([]ske.Nodepool, error) {
nodePools := []nodePool{}
diags := m.NodePools.ElementsAs(ctx, &nodePools, false)
if diags.HasError() {
return nil, core.DiagsToError(diags)
}
cnps := []ske.Nodepool{}
for i := range m.NodePools {
for i := range nodePools {
nodePool := nodePools[i]
// taints
taintsModel := []taint{}
diags := nodePool.Taints.ElementsAs(ctx, &taintsModel, false)
if diags.HasError() {
return nil, core.DiagsToError(diags)
}
ts := []ske.Taint{}
nodePool := m.NodePools[i]
for _, v := range nodePool.Taints {
for _, v := range taintsModel {
t := ske.Taint{
Effect: v.Effect.ValueStringPointer(),
Key: v.Key.ValueStringPointer(),
@ -680,12 +738,22 @@ func toNodepoolsPayload(ctx context.Context, m *Cluster) []ske.Nodepool {
}
cnps = append(cnps, cnp)
}
return cnps
return cnps, nil
}
func toHibernationsPayload(m *Cluster) *ske.Hibernation {
func toHibernationsPayload(ctx context.Context, m *Cluster) (*ske.Hibernation, error) {
hibernation := []hibernation{}
diags := m.Hibernations.ElementsAs(ctx, &hibernation, false)
if diags.HasError() {
return nil, core.DiagsToError(diags)
}
if len(hibernation) == 0 {
return nil, nil
}
scs := []ske.HibernationSchedule{}
for _, h := range m.Hibernations {
for _, h := range hibernation {
sc := ske.HibernationSchedule{
Start: h.Start.ValueStringPointer(),
End: h.End.ValueStringPointer(),
@ -697,13 +765,9 @@ func toHibernationsPayload(m *Cluster) *ske.Hibernation {
scs = append(scs, sc)
}
if len(scs) == 0 {
return nil
}
return &ske.Hibernation{
Schedules: &scs,
}
}, nil
}
func toExtensionsPayload(ctx context.Context, m *Cluster) (*ske.Extension, error) {
@ -805,104 +869,167 @@ func mapFields(ctx context.Context, cl *ske.ClusterResponse, m *Cluster) error {
m.KubernetesVersionUsed = types.StringPointerValue(cl.Kubernetes.Version)
m.AllowPrivilegedContainers = types.BoolPointerValue(cl.Kubernetes.AllowPrivilegedContainers)
}
if cl.Nodepools == nil {
m.NodePools = []NodePool{}
} else {
nodepools := *cl.Nodepools
m.NodePools = []NodePool{}
for i := range nodepools {
np := nodepools[i]
maimna := types.StringNull()
maimver := types.StringNull()
if np.Machine != nil && np.Machine.Image != nil {
maimna = types.StringPointerValue(np.Machine.Image.Name)
maimver = types.StringPointerValue(np.Machine.Image.Version)
}
vt := types.StringNull()
if np.Volume != nil {
vt = types.StringPointerValue(np.Volume.Type)
}
crin := types.StringNull()
if np.Cri != nil {
crin = types.StringPointerValue(np.Cri.Name)
}
n := NodePool{
Name: types.StringPointerValue(np.Name),
MachineType: types.StringPointerValue(np.Machine.Type),
OSName: maimna,
OSVersion: maimver,
Minimum: types.Int64PointerValue(np.Minimum),
Maximum: types.Int64PointerValue(np.Maximum),
MaxSurge: types.Int64PointerValue(np.MaxSurge),
MaxUnavailable: types.Int64PointerValue(np.MaxUnavailable),
VolumeType: vt,
VolumeSize: types.Int64PointerValue(np.Volume.Size),
Labels: types.MapNull(types.StringType),
Taints: nil,
CRI: crin,
AvailabilityZones: types.ListNull(types.StringType),
}
if np.Labels != nil {
elems := map[string]attr.Value{}
for k, v := range *np.Labels {
elems[k] = types.StringValue(v)
}
n.Labels = types.MapValueMust(types.StringType, elems)
}
if np.Taints != nil {
for _, v := range *np.Taints {
if n.Taints == nil {
n.Taints = []Taint{}
}
n.Taints = append(n.Taints, Taint{
Effect: types.StringPointerValue(v.Effect),
Key: types.StringPointerValue(v.Key),
Value: types.StringPointerValue(v.Value),
})
}
}
if np.AvailabilityZones == nil {
n.AvailabilityZones = types.ListNull(types.StringType)
} else {
elems := []attr.Value{}
for _, v := range *np.AvailabilityZones {
elems = append(elems, types.StringValue(v))
}
n.AvailabilityZones = types.ListValueMust(types.StringType, elems)
}
m.NodePools = append(m.NodePools, n)
}
}
err := mapMaintenance(ctx, cl, m)
err := mapNodePools(cl, m)
if err != nil {
return err
return fmt.Errorf("mapping node_pools: %w", err)
}
err = mapMaintenance(ctx, cl, m)
if err != nil {
return fmt.Errorf("mapping maintenance: %w", err)
}
err = mapHibernations(cl, m)
if err != nil {
return fmt.Errorf("mapping hibernations: %w", err)
}
mapHibernations(cl, m)
mapExtensions(cl, m)
return nil
}
func mapHibernations(cl *ske.ClusterResponse, m *Cluster) {
if cl.Hibernation == nil || cl.Hibernation.Schedules == nil {
return
func mapNodePools(cl *ske.ClusterResponse, m *Cluster) error {
if cl.Nodepools == nil {
m.NodePools = types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes})
return nil
}
m.Hibernations = []Hibernation{}
for _, h := range *cl.Hibernation.Schedules {
m.Hibernations = append(m.Hibernations, Hibernation{
Start: types.StringPointerValue(h.Start),
End: types.StringPointerValue(h.End),
Timezone: types.StringPointerValue(h.Timezone),
})
nodePools := []attr.Value{}
for i, nodePoolResp := range *cl.Nodepools {
nodePool := map[string]attr.Value{
"name": types.StringPointerValue(nodePoolResp.Name),
"machine_type": types.StringPointerValue(nodePoolResp.Machine.Type),
"os_name": types.StringNull(),
"os_version": types.StringNull(),
"minimum": types.Int64PointerValue(nodePoolResp.Minimum),
"maximum": types.Int64PointerValue(nodePoolResp.Maximum),
"max_surge": types.Int64PointerValue(nodePoolResp.MaxSurge),
"max_unavailable": types.Int64PointerValue(nodePoolResp.MaxUnavailable),
"volume_type": types.StringNull(),
"volume_size": types.Int64PointerValue(nodePoolResp.Volume.Size),
"labels": types.MapNull(types.StringType),
"cri": types.StringNull(),
"availability_zones": types.ListNull(types.StringType),
}
if nodePoolResp.Machine != nil && nodePoolResp.Machine.Image != nil {
nodePool["os_name"] = types.StringPointerValue(nodePoolResp.Machine.Image.Name)
nodePool["os_version"] = types.StringPointerValue(nodePoolResp.Machine.Image.Version)
}
if nodePoolResp.Volume != nil {
nodePool["volume_type"] = types.StringPointerValue(nodePoolResp.Volume.Type)
}
if nodePoolResp.Cri != nil {
nodePool["cri"] = types.StringPointerValue(nodePoolResp.Cri.Name)
}
err := mapTaints(nodePoolResp.Taints, nodePool)
if err != nil {
return fmt.Errorf("mapping index %d, field taints: %w", i, err)
}
if nodePoolResp.Labels != nil {
elems := map[string]attr.Value{}
for k, v := range *nodePoolResp.Labels {
elems[k] = types.StringValue(v)
}
elemsTF, diags := types.MapValue(types.StringType, elems)
if diags.HasError() {
return fmt.Errorf("mapping index %d, field labels: %w", i, core.DiagsToError(diags))
}
nodePool["labels"] = elemsTF
}
if nodePoolResp.AvailabilityZones != nil {
elems := []attr.Value{}
for _, v := range *nodePoolResp.AvailabilityZones {
elems = append(elems, types.StringValue(v))
}
elemsTF, diags := types.ListValue(types.StringType, elems)
if diags.HasError() {
return fmt.Errorf("mapping index %d, field availability_zones: %w", i, core.DiagsToError(diags))
}
nodePool["availability_zones"] = elemsTF
}
nodePoolTF, diags := basetypes.NewObjectValue(nodePoolTypes, nodePool)
if diags.HasError() {
return fmt.Errorf("mapping index %d: %w", i, core.DiagsToError(diags))
}
nodePools = append(nodePools, nodePoolTF)
}
nodePoolsTF, diags := basetypes.NewListValue(types.ObjectType{AttrTypes: nodePoolTypes}, nodePools)
if diags.HasError() {
return core.DiagsToError(diags)
}
m.NodePools = nodePoolsTF
return nil
}
func mapTaints(t *[]ske.Taint, nodePool map[string]attr.Value) error {
if t == nil || len(*t) == 0 {
nodePool["taints"] = types.ListNull(types.ObjectType{AttrTypes: taintTypes})
return nil
}
taints := []attr.Value{}
for i, taintResp := range *t {
taint := map[string]attr.Value{
"effect": types.StringPointerValue(taintResp.Effect),
"key": types.StringPointerValue(taintResp.Key),
"value": types.StringPointerValue(taintResp.Value),
}
taintTF, diags := basetypes.NewObjectValue(taintTypes, taint)
if diags.HasError() {
return fmt.Errorf("mapping index %d: %w", i, core.DiagsToError(diags))
}
taints = append(taints, taintTF)
}
taintsTF, diags := basetypes.NewListValue(types.ObjectType{AttrTypes: taintTypes}, taints)
if diags.HasError() {
return core.DiagsToError(diags)
}
nodePool["taints"] = taintsTF
return nil
}
func mapHibernations(cl *ske.ClusterResponse, m *Cluster) error {
if cl.Hibernation == nil || cl.Hibernation.Schedules == nil {
m.Hibernations = basetypes.NewListNull(basetypes.ObjectType{AttrTypes: hibernationTypes})
return nil
}
hibernations := []attr.Value{}
for i, hibernationResp := range *cl.Hibernation.Schedules {
hibernation := map[string]attr.Value{
"start": types.StringPointerValue(hibernationResp.Start),
"end": types.StringPointerValue(hibernationResp.End),
"timezone": types.StringPointerValue(hibernationResp.Timezone),
}
hibernationTF, diags := basetypes.NewObjectValue(hibernationTypes, hibernation)
if diags.HasError() {
return fmt.Errorf("mapping index %d: %w", i, core.DiagsToError(diags))
}
hibernations = append(hibernations, hibernationTF)
}
hibernationsTF, diags := basetypes.NewListValue(types.ObjectType{AttrTypes: hibernationTypes}, hibernations)
if diags.HasError() {
return core.DiagsToError(diags)
}
m.Hibernations = hibernationsTF
return nil
}
func mapMaintenance(ctx context.Context, cl *ske.ClusterResponse, m *Cluster) error {
// Aligned with SKE team that a flattened data structure is fine, because not extensions are planned.
if cl.Maintenance == nil {
m.Maintenance = types.ObjectNull(map[string]attr.Type{})
m.Maintenance = types.ObjectNull(maintenanceTypes)
return nil
}
ekvu := types.BoolNull()

View file

@ -31,9 +31,9 @@ func TestMapFields(t *testing.T) {
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
AllowPrivilegedContainers: types.BoolNull(),
NodePools: []NodePool{},
Maintenance: types.ObjectNull(map[string]attr.Type{}),
Hibernations: nil,
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: nil,
KubeConfig: types.StringNull(),
},
@ -122,43 +122,72 @@ func TestMapFields(t *testing.T) {
KubernetesVersionUsed: types.StringValue("1.2.3"),
AllowPrivilegedContainers: types.BoolValue(true),
NodePools: []NodePool{
{
Name: types.StringValue("node"),
MachineType: types.StringValue("B"),
OSName: types.StringValue("os"),
OSVersion: types.StringValue("os-ver"),
Minimum: types.Int64Value(1),
Maximum: types.Int64Value(5),
MaxSurge: types.Int64Value(3),
MaxUnavailable: types.Int64Null(),
VolumeType: types.StringValue("type"),
VolumeSize: types.Int64Value(3),
Labels: types.MapValueMust(types.StringType, map[string]attr.Value{"k": types.StringValue("v")}),
Taints: []Taint{
{
Effect: types.StringValue("effect"),
Key: types.StringValue("key"),
Value: types.StringValue("value"),
NodePools: types.ListValueMust(
types.ObjectType{AttrTypes: nodePoolTypes},
[]attr.Value{
types.ObjectValueMust(
nodePoolTypes,
map[string]attr.Value{
"name": types.StringValue("node"),
"machine_type": types.StringValue("B"),
"os_name": types.StringValue("os"),
"os_version": types.StringValue("os-ver"),
"minimum": types.Int64Value(1),
"maximum": types.Int64Value(5),
"max_surge": types.Int64Value(3),
"max_unavailable": types.Int64Null(),
"volume_type": types.StringValue("type"),
"volume_size": types.Int64Value(3),
"labels": types.MapValueMust(
types.StringType,
map[string]attr.Value{
"k": types.StringValue("v"),
},
),
"taints": types.ListValueMust(
types.ObjectType{AttrTypes: taintTypes},
[]attr.Value{
types.ObjectValueMust(
taintTypes,
map[string]attr.Value{
"effect": types.StringValue("effect"),
"key": types.StringValue("key"),
"value": types.StringValue("value"),
},
),
},
),
"cri": types.StringValue("cri"),
"availability_zones": types.ListValueMust(
types.StringType,
[]attr.Value{
types.StringValue("z1"),
types.StringValue("z2"),
},
),
},
},
CRI: types.StringValue("cri"),
AvailabilityZones: types.ListValueMust(types.StringType, []attr.Value{types.StringValue("z1"), types.StringValue("z2")}),
),
},
},
),
Maintenance: types.ObjectValueMust(maintenanceTypes, map[string]attr.Value{
"enable_kubernetes_version_updates": types.BoolValue(true),
"enable_machine_image_version_updates": types.BoolValue(true),
"start": types.StringValue("03:04:05+06:00"),
"end": types.StringValue("13:14:15Z"),
}),
Hibernations: []Hibernation{
{
Start: types.StringValue("1"),
End: types.StringValue("2"),
Timezone: types.StringValue("CET"),
Hibernations: types.ListValueMust(
types.ObjectType{AttrTypes: hibernationTypes},
[]attr.Value{
types.ObjectValueMust(
hibernationTypes,
map[string]attr.Value{
"start": types.StringValue("1"),
"end": types.StringValue("2"),
"timezone": types.StringValue("CET"),
},
),
},
},
),
Extensions: &Extensions{
Argus: &ArgusExtension{
Enabled: types.BoolValue(true),

View file

@ -28,7 +28,7 @@ var clusterResource = map[string]string{
"kubernetes_version": "1.24",
"kubernetes_version_used": "1.24.17",
"kubernetes_version_new": "1.25",
"kubernetes_version_used_new": "1.25.14",
"kubernetes_version_used_new": "1.25.15",
"allowPrivilegedContainers": "true",
"nodepool_name": "np-acc-test",
"nodepool_name_min": "np-acc-min-test",
@ -142,6 +142,7 @@ func getConfig(version string, apc *bool, maintenanceEnd *string) string {
os_version = "%s"
minimum = "%s"
maximum = "%s"
max_surge = "%s"
availability_zones = ["%s"]
}]
}
@ -188,6 +189,7 @@ func getConfig(version string, apc *bool, maintenanceEnd *string) string {
clusterResource["nodepool_os_version_min"],
clusterResource["nodepool_minimum"],
clusterResource["nodepool_maximum"],
clusterResource["nodepool_max_surge"],
clusterResource["nodepool_zone"],
)
}