Skip to content

Commit 5e1a339

Browse files
authored
Dualstack support (#256)
1 parent 0c1a2d7 commit 5e1a339

19 files changed

+294
-199
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM alpine:3.20
1+
FROM alpine:3.21
22
LABEL maintainer="metal-stack authors <info@metal-stack.io>"
33
COPY bin/metalctl-linux-amd64 /metalctl
44
ENTRYPOINT ["/metalctl"]

Dockerfile.test

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.23
1+
FROM golang:1.24
22
WORKDIR /work
33
COPY go.* .
44
RUN go mod download

cmd/completion/ip.go

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package completion
22

33
import (
44
"github.com/metal-stack/metal-go/api/client/ip"
5+
"github.com/metal-stack/metal-go/api/models"
56
"github.com/spf13/cobra"
67
)
78

@@ -16,3 +17,7 @@ func (c *Completion) IpListCompletion(cmd *cobra.Command, args []string, toCompl
1617
}
1718
return names, cobra.ShellCompDirectiveNoFileComp
1819
}
20+
21+
func (c *Completion) IPAddressFamilyCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
22+
return []string{models.V1IPAllocateRequestAddressfamilyIPV4, models.V1IPAllocateRequestAddressfamilyIPV6}, cobra.ShellCompDirectiveNoFileComp
23+
}

cmd/completion/network.go

+5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package completion
22

33
import (
44
"github.com/metal-stack/metal-go/api/client/network"
5+
"github.com/metal-stack/metal-go/api/models"
6+
57
"github.com/spf13/cobra"
68
)
79

@@ -28,3 +30,6 @@ func (c *Completion) NetworkDestinationPrefixesCompletion(cmd *cobra.Command, ar
2830
}
2931
return prefixes, cobra.ShellCompDirectiveNoFileComp
3032
}
33+
func (c *Completion) NetworkAddressFamilyCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
34+
return []string{models.V1NetworkAllocateRequestAddressfamilyIPV4, models.V1NetworkAllocateRequestAddressfamilyIPV6}, cobra.ShellCompDirectiveNoFileComp
35+
}

cmd/ip.go

+13-6
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ func newIPCmd(c *config) *cobra.Command {
4949
cmd.Flags().StringP("network", "", "", "network from where the IP should be allocated.")
5050
cmd.Flags().StringP("project", "", "", "project for which the IP should be allocated.")
5151
cmd.Flags().StringSliceP("tags", "", nil, "tags to attach to the IP.")
52+
cmd.Flags().StringP("addressfamily", "", "", "addressfamily of the ip to acquire, defaults to IPv4 [optional]")
5253
genericcli.Must(cmd.RegisterFlagCompletionFunc("network", c.comp.NetworkListCompletion))
5354
genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.comp.ProjectListCompletion))
5455
genericcli.Must(cmd.RegisterFlagCompletionFunc("type", cobra.FixedCompletions([]string{models.V1IPAllocateRequestTypeEphemeral, models.V1IPAllocateRequestTypeStatic}, cobra.ShellCompDirectiveNoFileComp)))
56+
genericcli.Must(cmd.RegisterFlagCompletionFunc("addressfamily", c.comp.IPAddressFamilyCompletion))
5557
},
5658
ListCmdMutateFn: func(cmd *cobra.Command) {
5759
cmd.Flags().StringP("ipaddress", "", "", "ipaddress to filter [optional]")
@@ -62,11 +64,13 @@ func newIPCmd(c *config) *cobra.Command {
6264
cmd.Flags().StringP("network", "", "", "network to filter [optional]")
6365
cmd.Flags().StringP("name", "", "", "name to filter [optional]")
6466
cmd.Flags().StringSliceP("tags", "", nil, "tags to filter [optional]")
67+
cmd.Flags().StringP("addressfamily", "", "", "addressfamily of the ip to filter, defaults to all addressfamilies [optional]")
6568
genericcli.Must(cmd.RegisterFlagCompletionFunc("ipaddress", c.comp.IpListCompletion))
6669
genericcli.Must(cmd.RegisterFlagCompletionFunc("network", c.comp.NetworkListCompletion))
6770
genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.comp.ProjectListCompletion))
6871
genericcli.Must(cmd.RegisterFlagCompletionFunc("type", cobra.FixedCompletions([]string{models.V1IPAllocateRequestTypeEphemeral, models.V1IPAllocateRequestTypeStatic}, cobra.ShellCompDirectiveNoFileComp)))
6972
genericcli.Must(cmd.RegisterFlagCompletionFunc("machineid", c.comp.MachineListCompletion))
73+
genericcli.Must(cmd.RegisterFlagCompletionFunc("addressfamily", c.comp.IPAddressFamilyCompletion))
7074
},
7175
DeleteCmdMutateFn: func(cmd *cobra.Command) {
7276
cmd.Aliases = append(cmd.Aliases, "free")
@@ -105,6 +109,7 @@ func (c *ipCmd) List() ([]*models.V1IPResponse, error) {
105109
Machineid: viper.GetString("machineid"),
106110
Networkprefix: viper.GetString("prefix"),
107111
Tags: viper.GetStringSlice("tags"),
112+
Addressfamily: viper.GetString("addressfamily"),
108113
}), nil)
109114
if err != nil {
110115
return nil, err
@@ -169,6 +174,7 @@ func ipResponseToCreate(r *models.V1IPResponse) *ipAllocateRequest {
169174
if r.Ipaddress != nil {
170175
ip = *r.Ipaddress
171176
}
177+
172178
return &ipAllocateRequest{
173179
SpecificIP: ip,
174180
V1IPAllocateRequest: &models.V1IPAllocateRequest{
@@ -196,12 +202,13 @@ func (c *ipCmd) createRequestFromCLI() (*ipAllocateRequest, error) {
196202
return &ipAllocateRequest{
197203
SpecificIP: viper.GetString("ipaddress"),
198204
V1IPAllocateRequest: &models.V1IPAllocateRequest{
199-
Description: viper.GetString("description"),
200-
Name: viper.GetString("name"),
201-
Networkid: pointer.Pointer(viper.GetString("network")),
202-
Projectid: pointer.Pointer(viper.GetString("project")),
203-
Type: pointer.Pointer(viper.GetString("type")),
204-
Tags: viper.GetStringSlice("tags"),
205+
Description: viper.GetString("description"),
206+
Name: viper.GetString("name"),
207+
Networkid: pointer.Pointer(viper.GetString("network")),
208+
Projectid: pointer.Pointer(viper.GetString("project")),
209+
Type: pointer.Pointer(viper.GetString("type")),
210+
Tags: viper.GetStringSlice("tags"),
211+
Addressfamily: viper.GetString("addressfamily"),
205212
},
206213
}, nil
207214
}

cmd/ip_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ IP ALLOCATION UUID DESCRIPTION NAME NETWORK PROJECT TYPE
229229
"--type", *want.Type,
230230
"--tags", strings.Join(want.Tags, ","),
231231
}
232-
assertExhaustiveArgs(t, args, commonExcludedFileArgs()...)
232+
assertExhaustiveArgs(t, args, append(commonExcludedFileArgs(), "addressfamily")...)
233233
return args
234234
},
235235
mocks: &client.MetalMockFns{

cmd/network.go

+37
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ func newNetworkCmd(c *config) *cobra.Command {
4646
cmd.Flags().StringP("name", "n", "", "name of the network to create. [optional]")
4747
cmd.Flags().StringP("partition", "p", "", "partition where this network should exist.")
4848
cmd.Flags().StringP("project", "", "", "project of the network to create. [optional]")
49+
cmd.Flags().Int64("default-ipv4-child-prefix-length", 0, "default child prefix length for ipv4 prefixes for private super networks.")
50+
cmd.Flags().Int64("default-ipv6-child-prefix-length", 0, "default child prefix length for ipv6 prefixes for private super networks.")
4951
cmd.Flags().StringSlice("prefixes", []string{}, "prefixes in this network.")
5052
cmd.Flags().StringSlice("labels", []string{}, "add initial labels, must be in the form of key=value, use it like: --labels \"key1=value1,key2=value2\".")
5153
cmd.Flags().StringSlice("destination-prefixes", []string{}, "destination prefixes in this network.")
@@ -70,8 +72,10 @@ func newNetworkCmd(c *config) *cobra.Command {
7072
cmd.Flags().Int64P("vrf", "", 0, "vrf to filter [optional]")
7173
cmd.Flags().StringSlice("prefixes", []string{}, "prefixes to filter, use it like: --prefixes prefix1,prefix2.")
7274
cmd.Flags().StringSlice("destination-prefixes", []string{}, "destination prefixes to filter, use it like: --destination-prefixes prefix1,prefix2.")
75+
cmd.Flags().String("addressfamily", "", "addressfamily to filter, either ipv4 or ipv6 [optional]")
7376
genericcli.Must(cmd.RegisterFlagCompletionFunc("project", c.comp.ProjectListCompletion))
7477
genericcli.Must(cmd.RegisterFlagCompletionFunc("partition", c.comp.PartitionListCompletion))
78+
genericcli.Must(cmd.RegisterFlagCompletionFunc("addressfamily", c.comp.NetworkAddressFamilyCompletion))
7579
},
7680
UpdateCmdMutateFn: func(cmd *cobra.Command) {
7781
cmd.Flags().String("name", "", "the name of the network [optional]")
@@ -105,6 +109,16 @@ func newNetworkCmd(c *config) *cobra.Command {
105109
return err
106110
}
107111

112+
var (
113+
length = make(map[string]int64)
114+
)
115+
if viper.IsSet("ipv4-prefix-length") {
116+
length[models.V1IPAllocateRequestAddressfamilyIPV4] = viper.GetInt64("ipv4-prefix-length")
117+
}
118+
if viper.IsSet("ipv6-prefix-length") {
119+
length[models.V1IPAllocateRequestAddressfamilyIPV6] = viper.GetInt64("ipv6-prefix-length")
120+
}
121+
108122
return w.childCLI.CreateAndPrint(&models.V1NetworkAllocateRequest{
109123
Description: viper.GetString("description"),
110124
Name: viper.GetString("name"),
@@ -114,6 +128,8 @@ func newNetworkCmd(c *config) *cobra.Command {
114128
Labels: labels,
115129
Destinationprefixes: destinationPrefixes,
116130
Nat: nat,
131+
Addressfamily: viper.GetString("addressfamily"),
132+
Length: length,
117133
}, c.describePrinter)
118134
}
119135

@@ -142,8 +158,12 @@ func newNetworkCmd(c *config) *cobra.Command {
142158
allocateCmd.Flags().StringSlice("labels", []string{}, "labels for this network. [optional]")
143159
allocateCmd.Flags().BoolP("dmz", "", false, "use this private network as dmz. [optional]")
144160
allocateCmd.Flags().BoolP("shared", "", false, "shared allows usage of this private network from other networks")
161+
allocateCmd.Flags().StringP("addressfamily", "", "", "addressfamily of the network to acquire, if not specified the network inherits the address families from the parent [optional]")
162+
allocateCmd.Flags().Int64P("ipv4-prefix-length", "", 0, "ipv4 prefix bit length of the network to create, defaults to default child prefix length of the parent network. [optional]")
163+
allocateCmd.Flags().Int64P("ipv6-prefix-length", "", 0, "ipv6 prefix bit length of the network to create, defaults to default child prefix length of the parent network. [optional]")
145164
genericcli.Must(allocateCmd.RegisterFlagCompletionFunc("project", c.comp.ProjectListCompletion))
146165
genericcli.Must(allocateCmd.RegisterFlagCompletionFunc("partition", c.comp.PartitionListCompletion))
166+
genericcli.Must(allocateCmd.RegisterFlagCompletionFunc("addressfamily", c.comp.NetworkAddressFamilyCompletion))
147167

148168
genericcli.Must(allocateCmd.MarkFlagRequired("name"))
149169
genericcli.Must(allocateCmd.MarkFlagRequired("project"))
@@ -179,6 +199,7 @@ func (c *networkCmd) List() ([]*models.V1NetworkResponse, error) {
179199
Prefixes: viper.GetStringSlice("prefixes"),
180200
Destinationprefixes: viper.GetStringSlice("destination-prefixes"),
181201
Parentnetworkid: viper.GetString("parent"),
202+
Addressfamily: viper.GetString("addressfamily"),
182203
}), nil)
183204
if err != nil {
184205
return nil, err
@@ -235,6 +256,7 @@ func networkResponseToCreate(r *models.V1NetworkResponse) *models.V1NetworkCreat
235256
Nat: r.Nat,
236257
Parentnetworkid: r.Parentnetworkid,
237258
Partitionid: r.Partitionid,
259+
Defaultchildprefixlength: r.Defaultchildprefixlength,
238260
Prefixes: r.Prefixes,
239261
Privatesuper: r.Privatesuper,
240262
Projectid: r.Projectid,
@@ -256,6 +278,7 @@ func networkResponseToUpdate(r *models.V1NetworkResponse) *models.V1NetworkUpdat
256278
Prefixes: r.Prefixes,
257279
Shared: r.Shared,
258280
AdditionalAnnouncableCIDRs: r.AdditionalAnnouncableCIDRs,
281+
Defaultchildprefixlength: r.Defaultchildprefixlength,
259282
}
260283
}
261284

@@ -265,6 +288,18 @@ func (c *networkCmd) createRequestFromCLI() (*models.V1NetworkCreateRequest, err
265288
return nil, err
266289
}
267290

291+
var defaultChildPrefixLengths map[string]int64
292+
if viper.GetBool("privatesuper") {
293+
defaultChildPrefixLengths = map[string]int64{}
294+
295+
if length := viper.GetInt64("default-ipv4-child-prefix-length"); length > 0 {
296+
defaultChildPrefixLengths[models.V1IPAllocateRequestAddressfamilyIPV4] = length
297+
}
298+
if length := viper.GetInt64("default-ipv6-child-prefix-length"); length > 0 {
299+
defaultChildPrefixLengths[models.V1IPAllocateRequestAddressfamilyIPV6] = length
300+
}
301+
}
302+
268303
return &models.V1NetworkCreateRequest{
269304
ID: pointer.Pointer(viper.GetString("id")),
270305
Description: viper.GetString("description"),
@@ -280,6 +315,7 @@ func (c *networkCmd) createRequestFromCLI() (*models.V1NetworkCreateRequest, err
280315
Vrfshared: viper.GetBool("vrfshared"),
281316
Labels: lbs,
282317
AdditionalAnnouncableCIDRs: viper.GetStringSlice("additional-announcable-cidrs"),
318+
Defaultchildprefixlength: defaultChildPrefixLengths,
283319
}, nil
284320
}
285321

@@ -375,6 +411,7 @@ func (c *networkCmd) updateRequestFromCLI(args []string) (*models.V1NetworkUpdat
375411
Prefixes: nil,
376412
Shared: shared,
377413
AdditionalAnnouncableCIDRs: additionalCidrs,
414+
Defaultchildprefixlength: resp.Defaultchildprefixlength,
378415
}
379416
addPrefixes = sets.New(viper.GetStringSlice("add-prefixes")...)
380417
removePrefixes = sets.New(viper.GetStringSlice("remove-prefixes")...)

0 commit comments

Comments
 (0)