diff --git a/docs/stackit_beta_network_create.md b/docs/stackit_beta_network_create.md index e9ac60921..bdca2eca6 100644 --- a/docs/stackit_beta_network_create.md +++ b/docs/stackit_beta_network_create.md @@ -16,22 +16,35 @@ stackit beta network create [flags] Create a network with name "network-1" $ stackit beta network create --name network-1 - Create an IPv4 network with name "network-1" with DNS name servers and a prefix length - $ stackit beta network create --name network-1 --ipv4-dns-name-servers "1.1.1.1,8.8.8.8,9.9.9.9" --ipv4-prefix-length 25 + Create a non-routed network with name "network-1" + $ stackit beta network create --name network-1 --non-routed - Create an IPv6 network with name "network-1" with DNS name servers and a prefix length - $ stackit beta network create --name network-1 --ipv6-dns-name-servers "2001:4860:4860::8888,2001:4860:4860::8844" --ipv6-prefix-length 56 + Create a network with name "network-1" and no gateway + $ stackit beta network create --name network-1 --no-ipv4-gateway + + Create an IPv4 network with name "network-1" with DNS name servers, a prefix and a gateway + $ stackit beta network create --name network-1 --ipv4-dns-name-servers "1.1.1.1,8.8.8.8,9.9.9.9" --ipv4-prefix "10.1.2.0/24" --ipv4-gateway "10.1.2.3" + + Create an IPv6 network with name "network-1" with DNS name servers, a prefix and a gateway + $ stackit beta network create --name network-1 --ipv6-dns-name-servers "2001:4860:4860::8888,2001:4860:4860::8844" --ipv6-prefix "2001:4860:4860::8888" --ipv6-gateway "2001:4860:4860::8888" ``` ### Options ``` -h, --help Help for "stackit beta network create" - --ipv4-dns-name-servers strings List of DNS name servers for IPv4 + --ipv4-dns-name-servers strings List of DNS name servers for IPv4. Nameservers cannot be defined for routed networks + --ipv4-gateway string The IPv4 gateway of a network. If not specified, the first IP of the network will be assigned as the gateway + --ipv4-prefix string The IPv4 prefix of the network (CIDR) --ipv4-prefix-length int The prefix length of the IPv4 network - --ipv6-dns-name-servers strings List of DNS name servers for IPv6 + --ipv6-dns-name-servers strings List of DNS name servers for IPv6. Nameservers cannot be defined for routed networks + --ipv6-gateway string The IPv6 gateway of a network. If not specified, the first IP of the network will be assigned as the gateway + --ipv6-prefix string The IPv6 prefix of the network (CIDR) --ipv6-prefix-length int The prefix length of the IPv6 network -n, --name string Network name + --no-ipv4-gateway If set to true, the network doesn't have an IPv4 gateway + --no-ipv6-gateway If set to true, the network doesn't have an IPv6 gateway + --non-routed If set to true, the network is not routed and therefore not accessible from other networks ``` ### Options inherited from parent commands diff --git a/docs/stackit_beta_network_update.md b/docs/stackit_beta_network_update.md index b14bc9a8d..99ac5f728 100644 --- a/docs/stackit_beta_network_update.md +++ b/docs/stackit_beta_network_update.md @@ -16,20 +16,27 @@ stackit beta network update [flags] Update network with ID "xxx" with new name "network-1-new" $ stackit beta network update xxx --name network-1-new - Update IPv4 network with ID "xxx" with new name "network-1-new" and new DNS name servers - $ stackit beta network update xxx --name network-1-new --ipv4-dns-name-servers "2.2.2.2" + Update network with ID "xxx" with no gateway + $ stackit beta network update --no-ipv4-gateway - Update IPv6 network with ID "xxx" with new name "network-1-new" and new DNS name servers - $ stackit beta network update xxx --name network-1-new --ipv6-dns-name-servers "2001:4860:4860::8888" + Update IPv4 network with ID "xxx" with new name "network-1-new", new gateway and new DNS name servers + $ stackit beta network update xxx --name network-1-new --ipv4-dns-name-servers "2.2.2.2" --ipv4-gateway "10.1.2.3" + + Update IPv6 network with ID "xxx" with new name "network-1-new", new gateway and new DNS name servers + $ stackit beta network update xxx --name network-1-new --ipv6-dns-name-servers "2001:4860:4860::8888" --ipv6-gateway "2001:4860:4860::8888" ``` ### Options ``` -h, --help Help for "stackit beta network update" - --ipv4-dns-name-servers strings List of DNS name servers IPv4 - --ipv6-dns-name-servers strings List of DNS name servers for IPv6 + --ipv4-dns-name-servers strings List of DNS name servers IPv4. Nameservers cannot be defined for routed networks + --ipv4-gateway string The IPv4 gateway of a network. If not specified, the first IP of the network will be assigned as the gateway + --ipv6-dns-name-servers strings List of DNS name servers for IPv6. Nameservers cannot be defined for routed networks + --ipv6-gateway string The IPv6 gateway of a network. If not specified, the first IP of the network will be assigned as the gateway -n, --name string Network name + --no-ipv4-gateway If set to true, the network doesn't have an IPv4 gateway + --no-ipv6-gateway If set to true, the network doesn't have an IPv6 gateway ``` ### Options inherited from parent commands diff --git a/internal/cmd/beta/network/create/create.go b/internal/cmd/beta/network/create/create.go index c057098ec..74abb08a4 100644 --- a/internal/cmd/beta/network/create/create.go +++ b/internal/cmd/beta/network/create/create.go @@ -25,8 +25,15 @@ const ( nameFlag = "name" ipv4DnsNameServersFlag = "ipv4-dns-name-servers" ipv4PrefixLengthFlag = "ipv4-prefix-length" + ipv4PrefixFlag = "ipv4-prefix" + ipv4GatewayFlag = "ipv4-gateway" ipv6DnsNameServersFlag = "ipv6-dns-name-servers" ipv6PrefixLengthFlag = "ipv6-prefix-length" + ipv6PrefixFlag = "ipv6-prefix" + ipv6GatewayFlag = "ipv6-gateway" + nonRoutedFlag = "non-routed" + noIpv4GatewayFlag = "no-ipv4-gateway" + noIpv6GatewayFlag = "no-ipv6-gateway" ) type inputModel struct { @@ -34,8 +41,15 @@ type inputModel struct { Name *string IPv4DnsNameServers *[]string IPv4PrefixLength *int64 + IPv4Prefix *string + IPv4Gateway *string IPv6DnsNameServers *[]string IPv6PrefixLength *int64 + IPv6Prefix *string + IPv6Gateway *string + NonRouted bool + NoIPv4Gateway bool + NoIPv6Gateway bool } func NewCmd(p *print.Printer) *cobra.Command { @@ -50,12 +64,20 @@ func NewCmd(p *print.Printer) *cobra.Command { `$ stackit beta network create --name network-1`, ), examples.NewExample( - `Create an IPv4 network with name "network-1" with DNS name servers and a prefix length`, - `$ stackit beta network create --name network-1 --ipv4-dns-name-servers "1.1.1.1,8.8.8.8,9.9.9.9" --ipv4-prefix-length 25`, + `Create a non-routed network with name "network-1"`, + `$ stackit beta network create --name network-1 --non-routed`, ), examples.NewExample( - `Create an IPv6 network with name "network-1" with DNS name servers and a prefix length`, - `$ stackit beta network create --name network-1 --ipv6-dns-name-servers "2001:4860:4860::8888,2001:4860:4860::8844" --ipv6-prefix-length 56`, + `Create a network with name "network-1" and no gateway`, + `$ stackit beta network create --name network-1 --no-ipv4-gateway`, + ), + examples.NewExample( + `Create an IPv4 network with name "network-1" with DNS name servers, a prefix and a gateway`, + `$ stackit beta network create --name network-1 --ipv4-dns-name-servers "1.1.1.1,8.8.8.8,9.9.9.9" --ipv4-prefix "10.1.2.0/24" --ipv4-gateway "10.1.2.3"`, + ), + examples.NewExample( + `Create an IPv6 network with name "network-1" with DNS name servers, a prefix and a gateway`, + `$ stackit beta network create --name network-1 --ipv6-dns-name-servers "2001:4860:4860::8888,2001:4860:4860::8844" --ipv6-prefix "2001:4860:4860::8888" --ipv6-gateway "2001:4860:4860::8888"`, ), ), RunE: func(cmd *cobra.Command, _ []string) error { @@ -113,10 +135,17 @@ func NewCmd(p *print.Printer) *cobra.Command { func configureFlags(cmd *cobra.Command) { cmd.Flags().StringP(nameFlag, "n", "", "Network name") - cmd.Flags().StringSlice(ipv4DnsNameServersFlag, []string{}, "List of DNS name servers for IPv4") + cmd.Flags().StringSlice(ipv4DnsNameServersFlag, []string{}, "List of DNS name servers for IPv4. Nameservers cannot be defined for routed networks") cmd.Flags().Int64(ipv4PrefixLengthFlag, 0, "The prefix length of the IPv4 network") - cmd.Flags().StringSlice(ipv6DnsNameServersFlag, []string{}, "List of DNS name servers for IPv6") + cmd.Flags().String(ipv4PrefixFlag, "", "The IPv4 prefix of the network (CIDR)") + cmd.Flags().String(ipv4GatewayFlag, "", "The IPv4 gateway of a network. If not specified, the first IP of the network will be assigned as the gateway") + cmd.Flags().StringSlice(ipv6DnsNameServersFlag, []string{}, "List of DNS name servers for IPv6. Nameservers cannot be defined for routed networks") cmd.Flags().Int64(ipv6PrefixLengthFlag, 0, "The prefix length of the IPv6 network") + cmd.Flags().String(ipv6PrefixFlag, "", "The IPv6 prefix of the network (CIDR)") + cmd.Flags().String(ipv6GatewayFlag, "", "The IPv6 gateway of a network. If not specified, the first IP of the network will be assigned as the gateway") + cmd.Flags().Bool(nonRoutedFlag, false, "If set to true, the network is not routed and therefore not accessible from other networks") + cmd.Flags().Bool(noIpv4GatewayFlag, false, "If set to true, the network doesn't have an IPv4 gateway") + cmd.Flags().Bool(noIpv6GatewayFlag, false, "If set to true, the network doesn't have an IPv6 gateway") err := flags.MarkFlagsRequired(cmd, nameFlag) cobra.CheckErr(err) @@ -133,8 +162,15 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) { Name: flags.FlagToStringPointer(p, cmd, nameFlag), IPv4DnsNameServers: flags.FlagToStringSlicePointer(p, cmd, ipv4DnsNameServersFlag), IPv4PrefixLength: flags.FlagToInt64Pointer(p, cmd, ipv4PrefixLengthFlag), + IPv4Prefix: flags.FlagToStringPointer(p, cmd, ipv4PrefixFlag), + IPv4Gateway: flags.FlagToStringPointer(p, cmd, ipv4GatewayFlag), IPv6DnsNameServers: flags.FlagToStringSlicePointer(p, cmd, ipv6DnsNameServersFlag), IPv6PrefixLength: flags.FlagToInt64Pointer(p, cmd, ipv6PrefixLengthFlag), + IPv6Prefix: flags.FlagToStringPointer(p, cmd, ipv6PrefixFlag), + IPv6Gateway: flags.FlagToStringPointer(p, cmd, ipv6GatewayFlag), + NonRouted: flags.FlagToBoolValue(p, cmd, nonRoutedFlag), + NoIPv4Gateway: flags.FlagToBoolValue(p, cmd, noIpv4GatewayFlag), + NoIPv6Gateway: flags.FlagToBoolValue(p, cmd, noIpv6GatewayFlag), } if p.IsVerbosityDebug() { @@ -153,22 +189,42 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli req := apiClient.CreateNetwork(ctx, model.ProjectId) addressFamily := &iaas.CreateNetworkAddressFamily{} - if model.IPv6DnsNameServers != nil || model.IPv6PrefixLength != nil { + if model.IPv6DnsNameServers != nil || model.IPv6PrefixLength != nil || model.IPv6Prefix != nil || model.NoIPv6Gateway || model.IPv6Gateway != nil { addressFamily.Ipv6 = &iaas.CreateNetworkIPv6Body{ Nameservers: model.IPv6DnsNameServers, PrefixLength: model.IPv6PrefixLength, + Prefix: model.IPv6Prefix, + } + + if model.NoIPv6Gateway { + addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil) + } else if model.IPv6Gateway != nil { + addressFamily.Ipv6.Gateway = iaas.NewNullableString(model.IPv6Gateway) } } - if model.IPv4DnsNameServers != nil || model.IPv4PrefixLength != nil { + if model.IPv4DnsNameServers != nil || model.IPv4PrefixLength != nil || model.IPv4Prefix != nil || model.NoIPv4Gateway || model.IPv4Gateway != nil { addressFamily.Ipv4 = &iaas.CreateNetworkIPv4Body{ Nameservers: model.IPv4DnsNameServers, PrefixLength: model.IPv4PrefixLength, + Prefix: model.IPv4Prefix, + } + + if model.NoIPv4Gateway { + addressFamily.Ipv4.Gateway = iaas.NewNullableString(nil) + } else if model.IPv4Gateway != nil { + addressFamily.Ipv4.Gateway = iaas.NewNullableString(model.IPv4Gateway) } } + routed := true + if model.NonRouted { + routed = false + } + payload := iaas.CreateNetworkPayload{ - Name: model.Name, + Name: model.Name, + Routed: &routed, } if addressFamily.Ipv4 != nil || addressFamily.Ipv6 != nil { @@ -197,7 +253,11 @@ func outputResult(p *print.Printer, model *inputModel, projectLabel string, netw return nil default: - p.Outputf("Created network for project %q.\nNetwork ID: %s\n", projectLabel, *network.NetworkId) + operationState := "Created" + if model.Async { + operationState = "Triggered creation of" + } + p.Outputf("%s network for project %q.\nNetwork ID: %s\n", operationState, projectLabel, *network.NetworkId) return nil } } diff --git a/internal/cmd/beta/network/create/create_test.go b/internal/cmd/beta/network/create/create_test.go index d1284558d..2cd2d606e 100644 --- a/internal/cmd/beta/network/create/create_test.go +++ b/internal/cmd/beta/network/create/create_test.go @@ -29,8 +29,13 @@ func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]st nameFlag: "example-network-name", ipv4DnsNameServersFlag: "1.1.1.0,1.1.2.0", ipv4PrefixLengthFlag: "24", + ipv4PrefixFlag: "10.1.2.0/24", + ipv4GatewayFlag: "10.1.2.3", ipv6DnsNameServersFlag: "2001:4860:4860::8888,2001:4860:4860::8844", ipv6PrefixLengthFlag: "24", + ipv6PrefixFlag: "2001:4860:4860::8888", + ipv6GatewayFlag: "2001:4860:4860::8888", + nonRoutedFlag: "false", } for _, mod := range mods { mod(flagValues) @@ -47,8 +52,13 @@ func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { Name: utils.Ptr("example-network-name"), IPv4DnsNameServers: utils.Ptr([]string{"1.1.1.0", "1.1.2.0"}), IPv4PrefixLength: utils.Ptr(int64(24)), + IPv4Prefix: utils.Ptr("10.1.2.0/24"), + IPv4Gateway: utils.Ptr("10.1.2.3"), IPv6DnsNameServers: utils.Ptr([]string{"2001:4860:4860::8888", "2001:4860:4860::8844"}), IPv6PrefixLength: utils.Ptr(int64(24)), + IPv6Prefix: utils.Ptr("2001:4860:4860::8888"), + IPv6Gateway: utils.Ptr("2001:4860:4860::8888"), + NonRouted: false, } for _, mod := range mods { mod(model) @@ -68,7 +78,8 @@ func fixtureRequest(mods ...func(request *iaas.ApiCreateNetworkRequest)) iaas.Ap func fixtureRequiredRequest(mods ...func(request *iaas.ApiCreateNetworkRequest)) iaas.ApiCreateNetworkRequest { request := testClient.CreateNetwork(testCtx, testProjectId) request = request.CreateNetworkPayload(iaas.CreateNetworkPayload{ - Name: utils.Ptr("example-network-name"), + Name: utils.Ptr("example-network-name"), + Routed: utils.Ptr(true), }) for _, mod := range mods { mod(&request) @@ -78,15 +89,20 @@ func fixtureRequiredRequest(mods ...func(request *iaas.ApiCreateNetworkRequest)) func fixturePayload(mods ...func(payload *iaas.CreateNetworkPayload)) iaas.CreateNetworkPayload { payload := iaas.CreateNetworkPayload{ - Name: utils.Ptr("example-network-name"), + Name: utils.Ptr("example-network-name"), + Routed: utils.Ptr(true), AddressFamily: &iaas.CreateNetworkAddressFamily{ Ipv4: &iaas.CreateNetworkIPv4Body{ Nameservers: utils.Ptr([]string{"1.1.1.0", "1.1.2.0"}), PrefixLength: utils.Ptr(int64(24)), + Prefix: utils.Ptr("10.1.2.0/24"), + Gateway: iaas.NewNullableString(utils.Ptr("10.1.2.3")), }, Ipv6: &iaas.CreateNetworkIPv6Body{ Nameservers: utils.Ptr([]string{"2001:4860:4860::8888", "2001:4860:4860::8844"}), PrefixLength: utils.Ptr(int64(24)), + Prefix: utils.Ptr("2001:4860:4860::8888"), + Gateway: iaas.NewNullableString(utils.Ptr("2001:4860:4860::8888")), }, }, } @@ -155,27 +171,69 @@ func TestParseInput(t *testing.T) { isValid: false, }, { - description: "use dns servers and prefix", + description: "use dns servers, prefix, gateway and prefix length", flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[ipv4DnsNameServersFlag] = "1.1.1.1" flagValues[ipv4PrefixLengthFlag] = "25" + flagValues[ipv4PrefixFlag] = "10.1.2.0/24" + flagValues[ipv4GatewayFlag] = "10.1.2.3" }), isValid: true, expectedModel: fixtureInputModel(func(model *inputModel) { model.IPv4DnsNameServers = utils.Ptr([]string{"1.1.1.1"}) model.IPv4PrefixLength = utils.Ptr(int64(25)) + model.IPv4Prefix = utils.Ptr("10.1.2.0/24") + model.IPv4Gateway = utils.Ptr("10.1.2.3") }), }, { - description: "use ipv6 dns servers and prefix", + description: "use ipv4 gateway nil", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[noIpv4GatewayFlag] = "true" + delete(flagValues, ipv4GatewayFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.NoIPv4Gateway = true + model.IPv4Gateway = nil + }), + }, + { + description: "use ipv6 dns servers, prefix, gateway and prefix length", flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[ipv6DnsNameServersFlag] = "2001:4860:4860::8888" flagValues[ipv6PrefixLengthFlag] = "25" + flagValues[ipv6PrefixFlag] = "2001:4860:4860::8888" + flagValues[ipv6GatewayFlag] = "2001:4860:4860::8888" }), isValid: true, expectedModel: fixtureInputModel(func(model *inputModel) { model.IPv6DnsNameServers = utils.Ptr([]string{"2001:4860:4860::8888"}) model.IPv6PrefixLength = utils.Ptr(int64(25)) + model.IPv6Prefix = utils.Ptr("2001:4860:4860::8888") + model.IPv6Gateway = utils.Ptr("2001:4860:4860::8888") + }), + }, + { + description: "use ipv6 gateway nil", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[noIpv6GatewayFlag] = "true" + delete(flagValues, ipv6GatewayFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.NoIPv6Gateway = true + model.IPv6Gateway = nil + }), + }, + { + description: "non-routed network", + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[nonRoutedFlag] = "true" + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.NonRouted = true }), }, } @@ -227,7 +285,7 @@ func TestParseInput(t *testing.T) { } func TestBuildRequest(t *testing.T) { - tests := []struct { + var tests = []struct { description string model *inputModel expectedRequest iaas.ApiCreateNetworkRequest @@ -248,8 +306,108 @@ func TestBuildRequest(t *testing.T) { }, expectedRequest: fixtureRequiredRequest(), }, + { + description: "non-routed network", + model: &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + Name: utils.Ptr("example-network-name"), + NonRouted: true, + }, + expectedRequest: testClient.CreateNetwork(testCtx, testProjectId).CreateNetworkPayload(iaas.CreateNetworkPayload{ + Name: utils.Ptr("example-network-name"), + Routed: utils.Ptr(false), + }), + }, + { + description: "use dns servers, prefix, gateway and prefix length", + model: &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + IPv4DnsNameServers: utils.Ptr([]string{"1.1.1.1"}), + IPv4PrefixLength: utils.Ptr(int64(25)), + IPv4Prefix: utils.Ptr("10.1.2.0/24"), + IPv4Gateway: utils.Ptr("10.1.2.3"), + }, + expectedRequest: testClient.CreateNetwork(testCtx, testProjectId).CreateNetworkPayload(iaas.CreateNetworkPayload{ + AddressFamily: &iaas.CreateNetworkAddressFamily{ + Ipv4: &iaas.CreateNetworkIPv4Body{ + Nameservers: utils.Ptr([]string{"1.1.1.1"}), + PrefixLength: utils.Ptr(int64(25)), + Prefix: utils.Ptr("10.1.2.0/24"), + Gateway: iaas.NewNullableString(utils.Ptr("10.1.2.3")), + }, + }, + Routed: utils.Ptr(true), + }), + }, + { + description: "use ipv4 gateway nil", + model: &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + NoIPv4Gateway: true, + IPv4Gateway: nil, + }, + expectedRequest: testClient.CreateNetwork(testCtx, testProjectId).CreateNetworkPayload(iaas.CreateNetworkPayload{ + AddressFamily: &iaas.CreateNetworkAddressFamily{ + Ipv4: &iaas.CreateNetworkIPv4Body{ + Gateway: iaas.NewNullableString(nil), + }, + }, + Routed: utils.Ptr(true), + }), + }, + { + description: "use ipv6 dns servers, prefix, gateway and prefix length", + model: &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + IPv6DnsNameServers: utils.Ptr([]string{"2001:4860:4860::8888"}), + IPv6PrefixLength: utils.Ptr(int64(25)), + IPv6Prefix: utils.Ptr("2001:4860:4860::8888"), + IPv6Gateway: utils.Ptr("2001:4860:4860::8888"), + }, + expectedRequest: testClient.CreateNetwork(testCtx, testProjectId).CreateNetworkPayload(iaas.CreateNetworkPayload{ + AddressFamily: &iaas.CreateNetworkAddressFamily{ + Ipv6: &iaas.CreateNetworkIPv6Body{ + Nameservers: utils.Ptr([]string{"2001:4860:4860::8888"}), + PrefixLength: utils.Ptr(int64(25)), + Prefix: utils.Ptr("2001:4860:4860::8888"), + Gateway: iaas.NewNullableString(utils.Ptr("2001:4860:4860::8888")), + }, + }, + Routed: utils.Ptr(true), + }), + }, + { + description: "use ipv6 gateway nil", + model: &inputModel{ + GlobalFlagModel: &globalflags.GlobalFlagModel{ + ProjectId: testProjectId, + Verbosity: globalflags.VerbosityDefault, + }, + NoIPv6Gateway: true, + IPv6Gateway: nil, + }, + expectedRequest: testClient.CreateNetwork(testCtx, testProjectId).CreateNetworkPayload(iaas.CreateNetworkPayload{ + AddressFamily: &iaas.CreateNetworkAddressFamily{ + Ipv6: &iaas.CreateNetworkIPv6Body{ + Gateway: iaas.NewNullableString(nil), + }, + }, + Routed: utils.Ptr(true), + }), + }, } - for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { request := buildRequest(testCtx, tt.model, testClient) @@ -257,6 +415,7 @@ func TestBuildRequest(t *testing.T) { diff := cmp.Diff(request, tt.expectedRequest, cmp.AllowUnexported(tt.expectedRequest), cmpopts.EquateComparable(testCtx), + cmp.AllowUnexported(iaas.NullableString{}), ) if diff != "" { t.Fatalf("Data does not match: %s", diff) diff --git a/internal/cmd/beta/network/describe/describe.go b/internal/cmd/beta/network/describe/describe.go index bbd2a3fa4..854fd65b0 100644 --- a/internal/cmd/beta/network/describe/describe.go +++ b/internal/cmd/beta/network/describe/describe.go @@ -152,6 +152,19 @@ func outputResult(p *print.Printer, outputFormat string, network *iaas.Network) table.AddSeparator() } + routed := false + if network.Routed != nil { + routed = *network.Routed + } + + table.AddRow("ROUTED", routed) + table.AddSeparator() + + if network.Gateway != nil { + table.AddRow("IPv4 GATEWAY", *network.Gateway.Get()) + table.AddSeparator() + } + if len(ipv4nameservers) > 0 { table.AddRow("IPv4 NAME SERVERS", strings.Join(ipv4nameservers, ", ")) } @@ -160,6 +173,12 @@ func outputResult(p *print.Printer, outputFormat string, network *iaas.Network) table.AddRow("IPv4 PREFIXES", strings.Join(ipv4prefixes, ", ")) } table.AddSeparator() + + if network.Gatewayv6 != nil { + table.AddRow("IPv6 GATEWAY", *network.Gatewayv6.Get()) + table.AddSeparator() + } + if len(ipv6nameservers) > 0 { table.AddRow("IPv6 NAME SERVERS", strings.Join(ipv6nameservers, ", ")) } diff --git a/internal/cmd/beta/network/list/list.go b/internal/cmd/beta/network/list/list.go index af91a4a1c..889f9e307 100644 --- a/internal/cmd/beta/network/list/list.go +++ b/internal/cmd/beta/network/list/list.go @@ -151,7 +151,7 @@ func outputResult(p *print.Printer, outputFormat string, networks []iaas.Network return nil default: table := tables.NewTable() - table.SetHeader("ID", "Name", "Status", "Public IP") + table.SetHeader("ID", "NAME", "STATUS", "PUBLIC IP", "ROUTED") for _, network := range networks { publicIp := "" @@ -159,7 +159,12 @@ func outputResult(p *print.Printer, outputFormat string, networks []iaas.Network publicIp = *network.PublicIp } - table.AddRow(*network.NetworkId, *network.Name, *network.State, publicIp) + routed := false + if network.Routed != nil { + routed = *network.Routed + } + + table.AddRow(*network.NetworkId, *network.Name, *network.State, publicIp, routed) table.AddSeparator() } diff --git a/internal/cmd/beta/network/update/update.go b/internal/cmd/beta/network/update/update.go index 748828778..710205cd7 100644 --- a/internal/cmd/beta/network/update/update.go +++ b/internal/cmd/beta/network/update/update.go @@ -25,7 +25,11 @@ const ( nameFlag = "name" ipv4DnsNameServersFlag = "ipv4-dns-name-servers" + ipv4GatewayFlag = "ipv4-gateway" ipv6DnsNameServersFlag = "ipv6-dns-name-servers" + ipv6GatewayFlag = "ipv6-gateway" + noIpv4GatewayFlag = "no-ipv4-gateway" + noIpv6GatewayFlag = "no-ipv6-gateway" ) type inputModel struct { @@ -33,7 +37,11 @@ type inputModel struct { NetworkId string Name *string IPv4DnsNameServers *[]string + IPv4Gateway *string IPv6DnsNameServers *[]string + IPv6Gateway *string + NoIPv4Gateway bool + NoIPv6Gateway bool } func NewCmd(p *print.Printer) *cobra.Command { @@ -48,12 +56,16 @@ func NewCmd(p *print.Printer) *cobra.Command { `$ stackit beta network update xxx --name network-1-new`, ), examples.NewExample( - `Update IPv4 network with ID "xxx" with new name "network-1-new" and new DNS name servers`, - `$ stackit beta network update xxx --name network-1-new --ipv4-dns-name-servers "2.2.2.2"`, + `Update network with ID "xxx" with no gateway`, + `$ stackit beta network update --no-ipv4-gateway`, ), examples.NewExample( - `Update IPv6 network with ID "xxx" with new name "network-1-new" and new DNS name servers`, - `$ stackit beta network update xxx --name network-1-new --ipv6-dns-name-servers "2001:4860:4860::8888"`, + `Update IPv4 network with ID "xxx" with new name "network-1-new", new gateway and new DNS name servers`, + `$ stackit beta network update xxx --name network-1-new --ipv4-dns-name-servers "2.2.2.2" --ipv4-gateway "10.1.2.3"`, + ), + examples.NewExample( + `Update IPv6 network with ID "xxx" with new name "network-1-new", new gateway and new DNS name servers`, + `$ stackit beta network update xxx --name network-1-new --ipv6-dns-name-servers "2001:4860:4860::8888" --ipv6-gateway "2001:4860:4860::8888"`, ), ), RunE: func(cmd *cobra.Command, args []string) error { @@ -116,8 +128,12 @@ func NewCmd(p *print.Printer) *cobra.Command { func configureFlags(cmd *cobra.Command) { cmd.Flags().StringP(nameFlag, "n", "", "Network name") - cmd.Flags().StringSlice(ipv4DnsNameServersFlag, nil, "List of DNS name servers IPv4") - cmd.Flags().StringSlice(ipv6DnsNameServersFlag, nil, "List of DNS name servers for IPv6") + cmd.Flags().StringSlice(ipv4DnsNameServersFlag, nil, "List of DNS name servers IPv4. Nameservers cannot be defined for routed networks") + cmd.Flags().String(ipv4GatewayFlag, "", "The IPv4 gateway of a network. If not specified, the first IP of the network will be assigned as the gateway") + cmd.Flags().StringSlice(ipv6DnsNameServersFlag, nil, "List of DNS name servers for IPv6. Nameservers cannot be defined for routed networks") + cmd.Flags().String(ipv6GatewayFlag, "", "The IPv6 gateway of a network. If not specified, the first IP of the network will be assigned as the gateway") + cmd.Flags().Bool(noIpv4GatewayFlag, false, "If set to true, the network doesn't have an IPv4 gateway") + cmd.Flags().Bool(noIpv6GatewayFlag, false, "If set to true, the network doesn't have an IPv6 gateway") } func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { @@ -133,7 +149,11 @@ func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inpu Name: flags.FlagToStringPointer(p, cmd, nameFlag), NetworkId: networkId, IPv4DnsNameServers: flags.FlagToStringSlicePointer(p, cmd, ipv4DnsNameServersFlag), + IPv4Gateway: flags.FlagToStringPointer(p, cmd, ipv4GatewayFlag), IPv6DnsNameServers: flags.FlagToStringSlicePointer(p, cmd, ipv6DnsNameServersFlag), + IPv6Gateway: flags.FlagToStringPointer(p, cmd, ipv6GatewayFlag), + NoIPv4Gateway: flags.FlagToBoolValue(p, cmd, noIpv4GatewayFlag), + NoIPv6Gateway: flags.FlagToBoolValue(p, cmd, noIpv6GatewayFlag), } if p.IsVerbosityDebug() { @@ -152,16 +172,28 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APICli req := apiClient.PartialUpdateNetwork(ctx, model.ProjectId, model.NetworkId) addressFamily := &iaas.UpdateNetworkAddressFamily{} - if model.IPv6DnsNameServers != nil { + if model.IPv6DnsNameServers != nil || model.NoIPv6Gateway || model.IPv6Gateway != nil { addressFamily.Ipv6 = &iaas.UpdateNetworkIPv6Body{ Nameservers: model.IPv6DnsNameServers, } + + if model.NoIPv6Gateway { + addressFamily.Ipv6.Gateway = iaas.NewNullableString(nil) + } else if model.IPv6Gateway != nil { + addressFamily.Ipv6.Gateway = iaas.NewNullableString(model.IPv6Gateway) + } } - if model.IPv4DnsNameServers != nil { + if model.IPv4DnsNameServers != nil || model.NoIPv4Gateway || model.IPv4Gateway != nil { addressFamily.Ipv4 = &iaas.UpdateNetworkIPv4Body{ Nameservers: model.IPv4DnsNameServers, } + + if model.NoIPv4Gateway { + addressFamily.Ipv4.Gateway = iaas.NewNullableString(nil) + } else if model.IPv4Gateway != nil { + addressFamily.Ipv4.Gateway = iaas.NewNullableString(model.IPv4Gateway) + } } payload := iaas.PartialUpdateNetworkPayload{ diff --git a/internal/cmd/beta/network/update/update_test.go b/internal/cmd/beta/network/update/update_test.go index bc2c28e4e..99b3e8ba6 100644 --- a/internal/cmd/beta/network/update/update_test.go +++ b/internal/cmd/beta/network/update/update_test.go @@ -39,7 +39,9 @@ func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]st nameFlag: "example-network-name", projectIdFlag: testProjectId, ipv4DnsNameServersFlag: "1.1.1.0,1.1.2.0", + ipv4GatewayFlag: "10.1.2.3", ipv6DnsNameServersFlag: "2001:4860:4860::8888,2001:4860:4860::8844", + ipv6GatewayFlag: "2001:4860:4860::8888", } for _, mod := range mods { mod(flagValues) @@ -56,7 +58,9 @@ func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { Name: utils.Ptr("example-network-name"), NetworkId: testNetworkId, IPv4DnsNameServers: utils.Ptr([]string{"1.1.1.0", "1.1.2.0"}), + IPv4Gateway: utils.Ptr("10.1.2.3"), IPv6DnsNameServers: utils.Ptr([]string{"2001:4860:4860::8888", "2001:4860:4860::8844"}), + IPv6Gateway: utils.Ptr("2001:4860:4860::8888"), } for _, mod := range mods { mod(model) @@ -79,9 +83,11 @@ func fixturePayload(mods ...func(payload *iaas.PartialUpdateNetworkPayload)) iaa AddressFamily: &iaas.UpdateNetworkAddressFamily{ Ipv4: &iaas.UpdateNetworkIPv4Body{ Nameservers: utils.Ptr([]string{"1.1.1.0", "1.1.2.0"}), + Gateway: iaas.NewNullableString(utils.Ptr("10.1.2.3")), }, Ipv6: &iaas.UpdateNetworkIPv6Body{ Nameservers: utils.Ptr([]string{"2001:4860:4860::8888", "2001:4860:4860::8844"}), + Gateway: iaas.NewNullableString(utils.Ptr("2001:4860:4860::8888")), }, }, } @@ -161,25 +167,55 @@ func TestParseInput(t *testing.T) { isValid: false, }, { - description: "use dns servers and prefix", + description: "use dns servers and gateway", argValues: fixtureArgValues(), flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[ipv4DnsNameServersFlag] = "1.1.1.1" + flagValues[ipv4GatewayFlag] = "10.1.2.3" }), isValid: true, expectedModel: fixtureInputModel(func(model *inputModel) { model.IPv4DnsNameServers = utils.Ptr([]string{"1.1.1.1"}) + model.IPv4Gateway = utils.Ptr("10.1.2.3") }), }, { - description: "use ipv6 dns servers and prefix", + description: "use ipv4 gateway nil", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[noIpv4GatewayFlag] = "true" + delete(flagValues, ipv4GatewayFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.NoIPv4Gateway = true + model.IPv4Gateway = nil + }), + }, + { + description: "use ipv6 dns servers and gateway", argValues: fixtureArgValues(), flagValues: fixtureFlagValues(func(flagValues map[string]string) { flagValues[ipv6DnsNameServersFlag] = "2001:4860:4860::8888" + flagValues[ipv6GatewayFlag] = "2001:4860:4860::8888" }), isValid: true, expectedModel: fixtureInputModel(func(model *inputModel) { model.IPv6DnsNameServers = utils.Ptr([]string{"2001:4860:4860::8888"}) + model.IPv6Gateway = utils.Ptr("2001:4860:4860::8888") + }), + }, + { + description: "use ipv6 gateway nil", + argValues: fixtureArgValues(), + flagValues: fixtureFlagValues(func(flagValues map[string]string) { + flagValues[noIpv6GatewayFlag] = "true" + delete(flagValues, ipv6GatewayFlag) + }), + isValid: true, + expectedModel: fixtureInputModel(func(model *inputModel) { + model.NoIPv6Gateway = true + model.IPv6Gateway = nil }), }, } @@ -258,6 +294,7 @@ func TestBuildRequest(t *testing.T) { diff := cmp.Diff(request, tt.expectedRequest, cmp.AllowUnexported(tt.expectedRequest), cmpopts.EquateComparable(testCtx), + cmp.AllowUnexported(iaas.NullableString{}), ) if diff != "" { t.Fatalf("Data does not match: %s", diff)