diff --git a/internal/cmd/image/create/create.go b/internal/cmd/image/create/create.go index 930811f51..d25c6adc4 100644 --- a/internal/cmd/image/create/create.go +++ b/internal/cmd/image/create/create.go @@ -340,18 +340,10 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli } func createPayload(_ context.Context, model *inputModel) iaas.CreateImagePayload { - var labelsMap *map[string]any - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } payload := iaas.CreateImagePayload{ DiskFormat: &model.DiskFormat, Name: &model.Name, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), MinDiskSize: model.MinDiskSize, MinRam: model.MinRam, Protected: model.Protected, diff --git a/internal/cmd/image/update/update.go b/internal/cmd/image/update/update.go index e46e90790..02cd034fb 100644 --- a/internal/cmd/image/update/update.go +++ b/internal/cmd/image/update/update.go @@ -243,17 +243,10 @@ func parseInput(p *print.Printer, cmd *cobra.Command, cliArgs []string) (*inputM func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiUpdateImageRequest { request := apiClient.UpdateImage(ctx, model.ProjectId, model.Id) payload := iaas.NewUpdateImagePayload() - var labelsMap *map[string]any - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } + // Config *ImageConfig `json:"config,omitempty"` payload.DiskFormat = model.DiskFormat - payload.Labels = labelsMap + payload.Labels = utils.ConvertStringMapToInterfaceMap(model.Labels) payload.MinDiskSize = model.MinDiskSize payload.MinRam = model.MinRam payload.Name = model.Name diff --git a/internal/cmd/key-pair/create/create.go b/internal/cmd/key-pair/create/create.go index 5c4fb9196..a95ee34ec 100644 --- a/internal/cmd/key-pair/create/create.go +++ b/internal/cmd/key-pair/create/create.go @@ -124,18 +124,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiCreateKeyPairRequest { req := apiClient.CreateKeyPair(ctx) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - payload := iaas.CreateKeyPairPayload{ Name: model.Name, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), PublicKey: model.PublicKey, } diff --git a/internal/cmd/key-pair/update/update.go b/internal/cmd/key-pair/update/update.go index c34c1362b..b8b7ffbbd 100644 --- a/internal/cmd/key-pair/update/update.go +++ b/internal/cmd/key-pair/update/update.go @@ -87,16 +87,8 @@ func configureFlags(cmd *cobra.Command) { func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiUpdateKeyPairRequest { req := apiClient.UpdateKeyPair(ctx, *model.KeyPairName) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } payload := iaas.UpdateKeyPairPayload{ - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), } return req.UpdateKeyPairPayload(payload) } diff --git a/internal/cmd/network-area/create/create.go b/internal/cmd/network-area/create/create.go index 0bc3ae47a..870e6a439 100644 --- a/internal/cmd/network-area/create/create.go +++ b/internal/cmd/network-area/create/create.go @@ -172,18 +172,9 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli } } - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - payload := iaas.CreateNetworkAreaPayload{ Name: model.Name, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), AddressFamily: &iaas.CreateAreaAddressFamily{ Ipv4: &iaas.CreateAreaIPv4{ DefaultNameservers: model.DnsNameServers, diff --git a/internal/cmd/network-area/route/create/create.go b/internal/cmd/network-area/route/create/create.go index 4cd29e663..781a4c3d0 100644 --- a/internal/cmd/network-area/route/create/create.go +++ b/internal/cmd/network-area/route/create/create.go @@ -147,21 +147,12 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiCreateNetworkAreaRouteRequest { req := apiClient.CreateNetworkAreaRoute(ctx, *model.OrganizationId, *model.NetworkAreaId) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - payload := iaas.CreateNetworkAreaRoutePayload{ Ipv4: &[]iaas.Route{ { Prefix: model.Prefix, Nexthop: model.Nexthop, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), }, }, } diff --git a/internal/cmd/network-area/route/update/update.go b/internal/cmd/network-area/route/update/update.go index 1fb9f13f6..2866508b2 100644 --- a/internal/cmd/network-area/route/update/update.go +++ b/internal/cmd/network-area/route/update/update.go @@ -130,14 +130,8 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiUpdateNetworkAreaRouteRequest { req := apiClient.UpdateNetworkAreaRoute(ctx, *model.OrganizationId, *model.NetworkAreaId, model.RouteId) - // convert map[string]string to map[string]interface{} - labelsMap := make(map[string]interface{}) - for k, v := range *model.Labels { - labelsMap[k] = v - } - payload := iaas.UpdateNetworkAreaRoutePayload{ - Labels: &labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), } req = req.UpdateNetworkAreaRoutePayload(payload) diff --git a/internal/cmd/network-area/update/update.go b/internal/cmd/network-area/update/update.go index 122e94f33..0cc0e2e1e 100644 --- a/internal/cmd/network-area/update/update.go +++ b/internal/cmd/network-area/update/update.go @@ -153,18 +153,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiPartialUpdateNetworkAreaRequest { req := apiClient.PartialUpdateNetworkArea(ctx, *model.OrganizationId, model.AreaId) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - payload := iaas.PartialUpdateNetworkAreaPayload{ Name: model.Name, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), AddressFamily: &iaas.UpdateAreaAddressFamily{ Ipv4: &iaas.UpdateAreaIPv4{ DefaultNameservers: model.DnsNameServers, diff --git a/internal/cmd/network-interface/create/create.go b/internal/cmd/network-interface/create/create.go index f1024f844..f96687591 100644 --- a/internal/cmd/network-interface/create/create.go +++ b/internal/cmd/network-interface/create/create.go @@ -207,21 +207,11 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiCreateNicRequest { req := apiClient.CreateNic(ctx, model.ProjectId, *model.NetworkId) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - convertedMap := make(map[string]interface{}, len(*model.Labels)) - for k, v := range *model.Labels { - convertedMap[k] = v - } - labelsMap = &convertedMap - } - payload := iaas.CreateNicPayload{ AllowedAddresses: model.AllowedAddresses, Ipv4: model.Ipv4, Ipv6: model.Ipv6, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), Name: model.Name, NicSecurity: model.NicSecurity, SecurityGroups: model.SecurityGroups, diff --git a/internal/cmd/network-interface/update/update.go b/internal/cmd/network-interface/update/update.go index e92c5b8be..92b3e02ab 100644 --- a/internal/cmd/network-interface/update/update.go +++ b/internal/cmd/network-interface/update/update.go @@ -199,19 +199,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiUpdateNicRequest { req := apiClient.UpdateNic(ctx, model.ProjectId, *model.NetworkId, model.NicId) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - convertedMap := make(map[string]interface{}, len(*model.Labels)) - for k, v := range *model.Labels { - convertedMap[k] = v - } - labelsMap = &convertedMap - } - payload := iaas.UpdateNicPayload{ AllowedAddresses: model.AllowedAddresses, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), Name: model.Name, NicSecurity: model.NicSecurity, SecurityGroups: model.SecurityGroups, diff --git a/internal/cmd/network/create/create.go b/internal/cmd/network/create/create.go index 08e4e20b2..ce5871590 100644 --- a/internal/cmd/network/create/create.go +++ b/internal/cmd/network/create/create.go @@ -229,15 +229,6 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli } } - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - routed := true if model.NonRouted { routed = false @@ -245,7 +236,7 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli payload := iaas.CreateNetworkPayload{ Name: model.Name, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), Routed: &routed, } diff --git a/internal/cmd/network/update/update.go b/internal/cmd/network/update/update.go index 22a8cf87a..712342cfc 100644 --- a/internal/cmd/network/update/update.go +++ b/internal/cmd/network/update/update.go @@ -179,15 +179,6 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli req := apiClient.PartialUpdateNetwork(ctx, model.ProjectId, model.NetworkId) addressFamily := &iaas.UpdateNetworkAddressFamily{} - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - if model.IPv6DnsNameServers != nil || model.NoIPv6Gateway || model.IPv6Gateway != nil { addressFamily.Ipv6 = &iaas.UpdateNetworkIPv6Body{ Nameservers: model.IPv6DnsNameServers, @@ -214,7 +205,7 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli payload := iaas.PartialUpdateNetworkPayload{ Name: model.Name, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), } if addressFamily.Ipv4 != nil || addressFamily.Ipv6 != nil { diff --git a/internal/cmd/public-ip/create/create.go b/internal/cmd/public-ip/create/create.go index 68252751f..9d29b78d2 100644 --- a/internal/cmd/public-ip/create/create.go +++ b/internal/cmd/public-ip/create/create.go @@ -126,18 +126,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiCreatePublicIPRequest { req := apiClient.CreatePublicIP(ctx, model.ProjectId) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - payload := iaas.CreatePublicIPPayload{ NetworkInterface: iaas.NewNullableString(model.AssociatedResourceId), - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), } return req.CreatePublicIPPayload(payload) diff --git a/internal/cmd/public-ip/update/update.go b/internal/cmd/public-ip/update/update.go index 939b91ccf..78ae5d13e 100644 --- a/internal/cmd/public-ip/update/update.go +++ b/internal/cmd/public-ip/update/update.go @@ -130,17 +130,8 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiUpdatePublicIPRequest { req := apiClient.UpdatePublicIP(ctx, model.ProjectId, model.PublicIpId) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - payload := iaas.UpdatePublicIPPayload{ - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), } return req.UpdatePublicIPPayload(payload) diff --git a/internal/cmd/security-group/create/create.go b/internal/cmd/security-group/create/create.go index 54f521d26..5bce0c52b 100644 --- a/internal/cmd/security-group/create/create.go +++ b/internal/cmd/security-group/create/create.go @@ -127,17 +127,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiCreateSecurityGroupRequest { request := apiClient.CreateSecurityGroup(ctx, model.ProjectId) - var labelsMap *map[string]any - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } payload := iaas.CreateSecurityGroupPayload{ Description: model.Description, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), Name: model.Name, Stateful: model.Stateful, } diff --git a/internal/cmd/security-group/update/update.go b/internal/cmd/security-group/update/update.go index 083470467..487dca9e0 100644 --- a/internal/cmd/security-group/update/update.go +++ b/internal/cmd/security-group/update/update.go @@ -135,15 +135,7 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli request := apiClient.UpdateSecurityGroup(ctx, model.ProjectId, model.SecurityGroupId) payload := iaas.NewUpdateSecurityGroupPayload() payload.Description = model.Description - var labelsMap *map[string]any - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - payload.Labels = labelsMap + payload.Labels = utils.ConvertStringMapToInterfaceMap(model.Labels) payload.Name = model.Name request = request.UpdateSecurityGroupPayload(*payload) diff --git a/internal/cmd/server/create/create.go b/internal/cmd/server/create/create.go index 33b20c247..882c971cb 100644 --- a/internal/cmd/server/create/create.go +++ b/internal/cmd/server/create/create.go @@ -281,14 +281,6 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiCreateServerRequest { req := apiClient.CreateServer(ctx, model.ProjectId) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } var userData *[]byte if model.UserData != nil { @@ -307,7 +299,7 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli ServiceAccountMails: model.ServiceAccountMails, UserData: userData, Volumes: model.Volumes, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), } if model.BootVolumePerformanceClass != nil || model.BootVolumeSize != nil || model.BootVolumeDeleteOnTermination != nil || model.BootVolumeSourceId != nil || model.BootVolumeSourceType != nil { diff --git a/internal/cmd/server/update/update.go b/internal/cmd/server/update/update.go index 7d4c56241..bc6f821a1 100644 --- a/internal/cmd/server/update/update.go +++ b/internal/cmd/server/update/update.go @@ -129,18 +129,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiUpdateServerRequest { req := apiClient.UpdateServer(ctx, model.ProjectId, model.ServerId) - var labelsMap *map[string]interface{} - if model.Labels != nil && len(*model.Labels) > 0 { - // convert map[string]string to map[string]interface{} - labelsMap = utils.Ptr(map[string]interface{}{}) - for k, v := range *model.Labels { - (*labelsMap)[k] = v - } - } - payload := iaas.UpdateServerPayload{ Name: model.Name, - Labels: labelsMap, + Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), } return req.UpdateServerPayload(payload) diff --git a/internal/pkg/utils/utils.go b/internal/pkg/utils/utils.go index 7aff6e713..a26d7b4b2 100644 --- a/internal/pkg/utils/utils.go +++ b/internal/pkg/utils/utils.go @@ -129,3 +129,18 @@ func Base64Encode(message []byte) string { func UserAgentConfigOption(cliVersion string) sdkConfig.ConfigurationOption { return sdkConfig.WithUserAgent(fmt.Sprintf("stackit-cli/%s", cliVersion)) } + +// ConvertStringMapToInterfaceMap converts a map[string]string to a pointer to map[string]interface{}. +// Returns nil if the input map is empty. +// +//nolint:gocritic // Linter wants to have a non-pointer type for the map, but this would mean a nil check has to be done before every usage of this func. +func ConvertStringMapToInterfaceMap(m *map[string]string) *map[string]interface{} { + if m == nil || len(*m) == 0 { + return nil + } + result := make(map[string]interface{}, len(*m)) + for k, v := range *m { + result[k] = v + } + return &result +} diff --git a/internal/pkg/utils/utils_test.go b/internal/pkg/utils/utils_test.go index 894f4f2e2..86588bad8 100644 --- a/internal/pkg/utils/utils_test.go +++ b/internal/pkg/utils/utils_test.go @@ -149,3 +149,102 @@ func TestUserAgentConfigOption(t *testing.T) { }) } } + +func TestConvertStringMapToInterfaceMap(t *testing.T) { + tests := []struct { + name string + input *map[string]string + expected *map[string]interface{} + }{ + { + name: "nil input", + input: nil, + expected: nil, + }, + { + name: "empty map", + input: &map[string]string{}, + expected: nil, + }, + { + name: "single key-value pair", + input: &map[string]string{ + "key1": "value1", + }, + expected: &map[string]interface{}{ + "key1": "value1", + }, + }, + { + name: "multiple key-value pairs", + input: &map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + expected: &map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "special characters in values", + input: &map[string]string{ + "key1": "value with spaces", + "key2": "value,with,commas", + "key3": "value\nwith\nnewlines", + }, + expected: &map[string]interface{}{ + "key1": "value with spaces", + "key2": "value,with,commas", + "key3": "value\nwith\nnewlines", + }, + }, + { + name: "empty values", + input: &map[string]string{ + "key1": "", + "key2": "value2", + }, + expected: &map[string]interface{}{ + "key1": "", + "key2": "value2", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := ConvertStringMapToInterfaceMap(tt.input) + + // Check if both are nil + if result == nil && tt.expected == nil { + return + } + + // Check if one is nil and other isn't + if (result == nil && tt.expected != nil) || (result != nil && tt.expected == nil) { + t.Errorf("ConvertStringMapToInterfaceMap() = %v, want %v", result, tt.expected) + return + } + + // Compare maps + if len(*result) != len(*tt.expected) { + t.Errorf("ConvertStringMapToInterfaceMap() map length = %d, want %d", len(*result), len(*tt.expected)) + return + } + + for k, v := range *result { + expectedVal, ok := (*tt.expected)[k] + if !ok { + t.Errorf("ConvertStringMapToInterfaceMap() unexpected key %s in result", k) + continue + } + if v != expectedVal { + t.Errorf("ConvertStringMapToInterfaceMap() value for key %s = %v, want %v", k, v, expectedVal) + } + } + }) + } +}