Allow specifying network ID in SKE cluster (#368)

This commit is contained in:
João Palet 2024-05-21 11:27:23 +01:00 committed by GitHub
parent e2e5f19a29
commit ac0840fceb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 204 additions and 7 deletions

View file

@ -228,6 +228,20 @@ func (r *clusterDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
},
},
"network": schema.SingleNestedAttribute{
Description: "Network block as defined below.",
Computed: true,
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "ID of the STACKIT Network Area (SNA) network into which the cluster will be deployed.",
Computed: true,
Validators: []validator.String{
validate.UUID(),
},
},
},
},
"hibernations": schema.ListNestedAttribute{
Description: "One or more hibernation block as defined below.",
Computed: true,

View file

@ -73,6 +73,7 @@ type Model struct {
AllowPrivilegedContainers types.Bool `tfsdk:"allow_privileged_containers"`
NodePools types.List `tfsdk:"node_pools"`
Maintenance types.Object `tfsdk:"maintenance"`
Network types.Object `tfsdk:"network"`
Hibernations types.List `tfsdk:"hibernations"`
Extensions types.Object `tfsdk:"extensions"`
KubeConfig types.String `tfsdk:"kube_config"`
@ -148,6 +149,16 @@ var maintenanceTypes = map[string]attr.Type{
"end": basetypes.StringType{},
}
// Struct corresponding to Model.Network
type network struct {
ID types.String `tfsdk:"id"`
}
// Types corresponding to network
var networkTypes = map[string]attr.Type{
"id": basetypes.StringType{},
}
// Struct corresponding to Model.Hibernations[i]
type hibernation struct {
Start types.String `tfsdk:"start"`
@ -491,6 +502,22 @@ func (r *clusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
},
"network": schema.SingleNestedAttribute{
Description: "Network block as defined below.",
Optional: true,
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: "ID of the STACKIT Network Area (SNA) network into which the cluster will be deployed.",
Optional: true,
Validators: []validator.String{
validate.UUID(),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
},
},
"hibernations": schema.ListNestedAttribute{
Description: "One or more hibernation block as defined below.",
Optional: true,
@ -731,6 +758,11 @@ func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating maintenance API payload: %v", err))
return
}
network, err := toNetworkPayload(ctx, model)
if err != nil {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating network API payload: %v", err))
return
}
hibernations, err := toHibernationsPayload(ctx, model)
if err != nil {
core.LogAndAddError(ctx, diags, "Error creating/updating cluster", fmt.Sprintf("Creating hibernations API payload: %v", err))
@ -747,6 +779,7 @@ func (r *clusterResource) createOrUpdateCluster(ctx context.Context, diags *diag
Hibernation: hibernations,
Kubernetes: kubernetes,
Maintenance: maintenance,
Network: network,
Nodepools: &nodePools,
}
_, err = r.client.CreateOrUpdateCluster(ctx, projectId, name).CreateOrUpdateClusterPayload(payload).Execute()
@ -1174,6 +1207,22 @@ func toMaintenancePayload(ctx context.Context, m *Model) (*ske.Maintenance, erro
}, nil
}
func toNetworkPayload(ctx context.Context, m *Model) (*ske.V1Network, error) {
if m.Network.IsNull() || m.Network.IsUnknown() {
return nil, nil
}
network := network{}
diags := m.Network.As(ctx, &network, basetypes.ObjectAsOptions{})
if diags.HasError() {
return nil, fmt.Errorf("converting network object: %v", diags.Errors())
}
return &ske.V1Network{
Id: conversion.StringValueToPointer(network.ID),
}, nil
}
func mapFields(ctx context.Context, cl *ske.Cluster, m *Model) error {
if cl == nil {
return fmt.Errorf("response input is nil")
@ -1206,19 +1255,23 @@ func mapFields(ctx context.Context, cl *ske.Cluster, m *Model) error {
err := mapNodePools(ctx, cl, m)
if err != nil {
return fmt.Errorf("mapping node_pools: %w", err)
return fmt.Errorf("map node_pools: %w", err)
}
err = mapMaintenance(ctx, cl, m)
if err != nil {
return fmt.Errorf("mapping maintenance: %w", err)
return fmt.Errorf("map maintenance: %w", err)
}
err = mapNetwork(cl, m)
if err != nil {
return fmt.Errorf("map network: %w", err)
}
err = mapHibernations(cl, m)
if err != nil {
return fmt.Errorf("mapping hibernations: %w", err)
return fmt.Errorf("map hibernations: %w", err)
}
err = mapExtensions(ctx, cl, m)
if err != nil {
return fmt.Errorf("mapping extensions: %w", err)
return fmt.Errorf("map extensions: %w", err)
}
return nil
}
@ -1421,12 +1474,33 @@ func mapMaintenance(ctx context.Context, cl *ske.Cluster, m *Model) error {
}
maintenanceObject, diags := types.ObjectValue(maintenanceTypes, maintenanceValues)
if diags.HasError() {
return fmt.Errorf("creating flavor: %w", core.DiagsToError(diags))
return fmt.Errorf("create maintenance object: %w", core.DiagsToError(diags))
}
m.Maintenance = maintenanceObject
return nil
}
func mapNetwork(cl *ske.Cluster, m *Model) error {
if cl.Network == nil {
m.Network = types.ObjectNull(networkTypes)
return nil
}
id := types.StringNull()
if cl.Network.Id != nil {
id = types.StringValue(*cl.Network.Id)
}
networkValues := map[string]attr.Value{
"id": id,
}
networkObject, diags := types.ObjectValue(networkTypes, networkValues)
if diags.HasError() {
return fmt.Errorf("create network object: %w", core.DiagsToError(diags))
}
m.Network = networkObject
return nil
}
func getMaintenanceTimes(ctx context.Context, cl *ske.Cluster, m *Model) (startTime, endTime string, err error) {
startTimeAPI, err := time.Parse(time.RFC3339, *cl.Maintenance.TimeWindow.Start)
if err != nil {

View file

@ -49,6 +49,7 @@ func TestMapFields(t *testing.T) {
AllowPrivilegedContainers: types.BoolNull(),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Network: types.ObjectNull(networkTypes),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: types.ObjectNull(extensionsTypes),
KubeConfig: types.StringNull(),
@ -92,6 +93,9 @@ func TestMapFields(t *testing.T) {
End: utils.Ptr("0010-11-12T13:14:15Z"),
},
},
Network: &ske.V1Network{
Id: utils.Ptr("nid"),
},
Name: utils.Ptr("name"),
Nodepools: &[]ske.Nodepool{
{
@ -194,6 +198,9 @@ func TestMapFields(t *testing.T) {
"start": types.StringValue("03:04:05+06:00"),
"end": types.StringValue("13:14:15Z"),
}),
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
"id": types.StringValue("nid"),
}),
Hibernations: types.ListValueMust(
types.ObjectType{AttrTypes: hibernationTypes},
[]attr.Value{
@ -223,6 +230,30 @@ func TestMapFields(t *testing.T) {
},
true,
},
{
"nil_network_id",
types.ObjectNull(extensionsTypes),
&ske.Cluster{
Name: utils.Ptr("name"),
Network: &ske.V1Network{},
},
Model{
Id: types.StringValue("pid,name"),
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
KubernetesVersion: types.StringNull(),
AllowPrivilegedContainers: types.BoolNull(),
NodePools: types.ListNull(types.ObjectType{AttrTypes: nodePoolTypes}),
Maintenance: types.ObjectNull(maintenanceTypes),
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
"id": types.StringNull(),
}),
Hibernations: types.ListNull(types.ObjectType{AttrTypes: hibernationTypes}),
Extensions: types.ObjectNull(extensionsTypes),
KubeConfig: types.StringNull(),
},
true,
},
{
"extensions_mixed_values",
types.ObjectNull(extensionsTypes),
@ -1936,3 +1967,63 @@ func TestGetLatestSupportedMachineVersion(t *testing.T) {
})
}
}
func TestToNetworkPayload(t *testing.T) {
tests := []struct {
description string
model *Model
expected *ske.V1Network
isValid bool
}{
{
"base",
&Model{
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
"id": types.StringValue("nid"),
}),
},
&ske.V1Network{
Id: utils.Ptr("nid"),
},
true,
},
{
"no_id",
&Model{
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
Network: types.ObjectValueMust(networkTypes, map[string]attr.Value{
"id": types.StringNull(),
}),
},
&ske.V1Network{},
true,
},
{
"no_network",
&Model{
ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"),
Network: types.ObjectNull(networkTypes),
},
nil,
true,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
payload, err := toNetworkPayload(context.Background(), tt.model)
if !tt.isValid && err == nil {
t.Fatalf("Should have failed")
}
if tt.isValid {
diff := cmp.Diff(payload, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}