diff --git a/cmd/metal-api/internal/datastore/migrations/03_childprefixlength.go b/cmd/metal-api/internal/datastore/migrations/03_childprefixlength.go new file mode 100644 index 000000000..c61c842a9 --- /dev/null +++ b/cmd/metal-api/internal/datastore/migrations/03_childprefixlength.go @@ -0,0 +1,53 @@ +package migrations + +import ( + r "gopkg.in/rethinkdb/rethinkdb-go.v6" + + "github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore" +) + +func init() { + type tmpPartition struct { + PrivateNetworkPrefixLength uint8 `rethinkdb:"privatenetworkprefixlength"` + } + datastore.MustRegisterMigration(datastore.Migration{ + Name: "migrate partition.childprefixlength to tenant super network", + Version: 3, + Up: func(db *r.Term, session r.QueryExecutor, rs *datastore.RethinkStore) error { + nws, err := rs.ListNetworks() + if err != nil { + return err + } + + for _, old := range nws { + if !old.PrivateSuper { + continue + } + + cursor, err := db.Table("partition").Get(old.PartitionID).Run(session) + if err != nil { + return err + } + var partition tmpPartition + err = cursor.One(&partition) + if err != nil { + return err + } + + new := old + new.ChildPrefixLength = &partition.PrivateNetworkPrefixLength + err = rs.UpdateNetwork(&old, &new) + if err != nil { + return err + } + err = cursor.Close() + if err != nil { + return err + } + } + + _, err = db.Table("partition").Replace(r.Row.Without("privatenetworkprefixlength")).RunWrite(session) + return err + }, + }) +} diff --git a/cmd/metal-api/internal/ipam/ipam.go b/cmd/metal-api/internal/ipam/ipam.go index ad2c5b53d..4e5d48225 100644 --- a/cmd/metal-api/internal/ipam/ipam.go +++ b/cmd/metal-api/internal/ipam/ipam.go @@ -20,7 +20,7 @@ func New(ip ipam.Ipamer) *Ipam { } // AllocateChildPrefix creates a child prefix from a parent prefix in the IPAM. -func (i *Ipam) AllocateChildPrefix(parentPrefix metal.Prefix, childLength int) (*metal.Prefix, error) { +func (i *Ipam) AllocateChildPrefix(parentPrefix metal.Prefix, childLength uint8) (*metal.Prefix, error) { ipamParentPrefix := i.ip.PrefixFrom(parentPrefix.String()) if ipamParentPrefix == nil { @@ -131,12 +131,12 @@ func (i *Ipam) PrefixUsage(cidr string) (*metal.NetworkUsage, error) { return nil, fmt.Errorf("prefix for cidr:%s not found", cidr) } usage := prefix.Usage() - return &metal.NetworkUsage{ - AvailableIPs: usage.AvailableIPs, - UsedIPs: usage.AcquiredIPs, - AvailablePrefixes: usage.AvailablePrefixes, - UsedPrefixes: usage.AcquiredPrefixes, + AvailableIPs: usage.AvailableIPs, + UsedIPs: usage.AcquiredIPs, + AvailablePrefixes: usage.AvailableSmallestPrefixes, + AvailablePrefixList: usage.AvailablePrefixes, + UsedPrefixes: usage.AcquiredPrefixes, }, nil } diff --git a/cmd/metal-api/internal/ipam/wrapper.go b/cmd/metal-api/internal/ipam/wrapper.go index 1225d9bc6..c9ff8ad1e 100644 --- a/cmd/metal-api/internal/ipam/wrapper.go +++ b/cmd/metal-api/internal/ipam/wrapper.go @@ -11,7 +11,7 @@ type IPAMer interface { AllocateIP(prefix metal.Prefix) (string, error) AllocateSpecificIP(prefix metal.Prefix, specificIP string) (string, error) ReleaseIP(ip metal.IP) error - AllocateChildPrefix(parentPrefix metal.Prefix, childLength int) (*metal.Prefix, error) + AllocateChildPrefix(parentPrefix metal.Prefix, childLength uint8) (*metal.Prefix, error) ReleaseChildPrefix(childPrefix metal.Prefix) error CreatePrefix(prefix metal.Prefix) error DeletePrefix(prefix metal.Prefix) error diff --git a/cmd/metal-api/internal/metal/network.go b/cmd/metal-api/internal/metal/network.go index 9cdfd60bf..6ec947ea7 100644 --- a/cmd/metal-api/internal/metal/network.go +++ b/cmd/metal-api/internal/metal/network.go @@ -3,7 +3,8 @@ package metal import ( "fmt" "net" - "strings" + + "inet.af/netaddr" ) // A MacAddress is the type for mac adresses. When using a @@ -32,15 +33,13 @@ type Prefixes []Prefix // NewPrefixFromCIDR returns a new prefix from a given cidr. func NewPrefixFromCIDR(cidr string) (*Prefix, error) { - parts := strings.Split(cidr, "/") - if len(parts) != 2 { - return nil, fmt.Errorf("cannot split cidr into pieces: %v", cidr) + ipprefix, err := netaddr.ParseIPPrefix(cidr) + if err != nil { + return nil, err } - ip := strings.TrimSpace(parts[0]) - length := strings.TrimSpace(parts[1]) return &Prefix{ - IP: ip, - Length: length, + IP: ipprefix.IP.String(), + Length: fmt.Sprintf("%d", ipprefix.Bits), }, nil } @@ -73,6 +72,7 @@ type Network struct { ParentNetworkID string `rethinkdb:"parentnetworkid" json:"parentnetworkid"` Vrf uint `rethinkdb:"vrf" json:"vrf"` PrivateSuper bool `rethinkdb:"privatesuper" json:"privatesuper"` + ChildPrefixLength *uint8 `rethinkdb:"childprefixlength" json:"childprefixlength" description:"if privatesuper, this defines the bitlen of child prefixes if not nil"` Nat bool `rethinkdb:"nat" json:"nat"` Underlay bool `rethinkdb:"underlay" json:"underlay"` Shared bool `rethinkdb:"shared" json:"shared"` @@ -87,10 +87,11 @@ type NetworkMap map[string]Network // NetworkUsage contains usage information of a network type NetworkUsage struct { - AvailableIPs uint64 `json:"available_ips" description:"the total available IPs" readonly:"true"` - UsedIPs uint64 `json:"used_ips" description:"the total used IPs" readonly:"true"` - AvailablePrefixes uint64 `json:"available_prefixes" description:"the total available Prefixes" readonly:"true"` - UsedPrefixes uint64 `json:"used_prefixes" description:"the total used Prefixes" readonly:"true"` + AvailableIPs uint64 `json:"available_ips" description:"the total available IPs" readonly:"true"` + UsedIPs uint64 `json:"used_ips" description:"the total used IPs" readonly:"true"` + AvailablePrefixes uint64 `json:"available_prefixes" description:"the total available 2 bit Prefixes" readonly:"true"` + AvailablePrefixList []string `json:"available_prefix_list" description:"a list of possible child prefixes"` + UsedPrefixes uint64 `json:"used_prefixes" description:"the total used Prefixes" readonly:"true"` } // ByID creates an indexed map of partitions whre the id is the index. @@ -147,6 +148,7 @@ func (n *Network) SubstractPrefixes(prefixes ...Prefix) []Prefix { return result } +// NicMap returns the nic for a given macaddress type NicMap map[MacAddress]*Nic // ByMac creates a indexed map from a nic list. diff --git a/cmd/metal-api/internal/metal/partition.go b/cmd/metal-api/internal/metal/partition.go index a1b89378e..fc565d1a9 100644 --- a/cmd/metal-api/internal/metal/partition.go +++ b/cmd/metal-api/internal/metal/partition.go @@ -3,9 +3,8 @@ package metal // A Partition represents a location. type Partition struct { Base - BootConfiguration BootConfiguration `rethinkdb:"bootconfig" json:"bootconfig"` - MgmtServiceAddress string `rethinkdb:"mgmtserviceaddr" json:"mgmtserviceaddr"` - PrivateNetworkPrefixLength int `rethinkdb:"privatenetworkprefixlength" json:"privatenetworkprefixlength"` + BootConfiguration BootConfiguration `rethinkdb:"bootconfig" json:"bootconfig"` + MgmtServiceAddress string `rethinkdb:"mgmtserviceaddr" json:"mgmtserviceaddr"` } // BootConfiguration defines the metal-hammer initrd, kernel and commandline diff --git a/cmd/metal-api/internal/service/ip-service_test.go b/cmd/metal-api/internal/service/ip-service_test.go index 0adcceae9..08de143d6 100644 --- a/cmd/metal-api/internal/service/ip-service_test.go +++ b/cmd/metal-api/internal/service/ip-service_test.go @@ -49,13 +49,15 @@ func TestGetIPs(t *testing.T) { err = json.NewDecoder(resp.Body).Decode(&result) require.Nil(t, err) - require.Len(t, result, 3) + require.Len(t, result, 4) require.Equal(t, testdata.IP1.IPAddress, result[0].IPAddress) require.Equal(t, testdata.IP1.Name, *result[0].Name) require.Equal(t, testdata.IP2.IPAddress, result[1].IPAddress) require.Equal(t, testdata.IP2.Name, *result[1].Name) require.Equal(t, testdata.IP3.IPAddress, result[2].IPAddress) require.Equal(t, testdata.IP3.Name, *result[2].Name) + require.Equal(t, testdata.IP4.IPAddress, result[3].IPAddress) + require.Equal(t, testdata.IP4.Name, *result[3].Name) } func TestGetIP(t *testing.T) { @@ -80,6 +82,28 @@ func TestGetIP(t *testing.T) { require.Equal(t, testdata.IP1.Name, *result.Name) } +func TestGetIPv6(t *testing.T) { + ds, mock := datastore.InitMockDB() + testdata.InitMockDBData(mock) + + ipservice, err := NewIP(ds, bus.DirectEndpoints(), ipam.New(goipam.New()), nil) + require.NoError(t, err) + container := restful.NewContainer().Add(ipservice) + req := httptest.NewRequest("GET", "/v1/ip/2001:0db8:85a3::1", nil) + container = injectViewer(container, req) + w := httptest.NewRecorder() + container.ServeHTTP(w, req) + + resp := w.Result() + require.Equal(t, http.StatusOK, resp.StatusCode, w.Body.String()) + var result v1.IPResponse + err = json.NewDecoder(resp.Body).Decode(&result) + + require.Nil(t, err) + require.Equal(t, testdata.IP4.IPAddress, result.IPAddress) + require.Equal(t, testdata.IP4.Name, *result.Name) +} + func TestGetIPNotFound(t *testing.T) { ds, mock := datastore.InitMockDB() testdata.InitMockDBData(mock) @@ -127,6 +151,11 @@ func TestDeleteIP(t *testing.T) { ip: testdata.IP3.IPAddress, wantedStatus: http.StatusUnprocessableEntity, }, + { + name: "free a ipv6 ip should work", + ip: testdata.IP4.IPAddress, + wantedStatus: http.StatusUnprocessableEntity, + }, { name: "free an cluster-ip should fail", ip: testdata.IP2.IPAddress, diff --git a/cmd/metal-api/internal/service/network-service.go b/cmd/metal-api/internal/service/network-service.go index 7cc9ae2ac..f71abf370 100644 --- a/cmd/metal-api/internal/service/network-service.go +++ b/cmd/metal-api/internal/service/network-service.go @@ -9,6 +9,7 @@ import ( "github.com/metal-stack/metal-api/cmd/metal-api/internal/utils" "go.uber.org/zap" + "inet.af/netaddr" mdmv1 "github.com/metal-stack/masterdata-api/api/v1" mdm "github.com/metal-stack/masterdata-api/pkg/client" @@ -196,6 +197,7 @@ func (r networkResource) findNetworks(request *restful.Request, response *restfu } } +// TODO allow creation of networks with childprefixlength which are not privatesuper func (r networkResource) createNetwork(request *restful.Request, response *restful.Response) { var requestPayload v1.NetworkCreateRequest err := request.ReadEntity(&requestPayload) @@ -250,23 +252,42 @@ func (r networkResource) createNetwork(request *restful.Request, response *restf } } prefixes := metal.Prefixes{} - // all Prefixes must be valid + addressFamilies := make(map[string]bool) + // all Prefixes must be valid and from the same addressfamily for _, p := range requestPayload.Prefixes { prefix, err := metal.NewPrefixFromCIDR(p) // TODO: Should return a bad request 401 if err != nil { - if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefix %v is not a valid ip with mask: %v", p, err)) { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefix %s is not valid: %v", p, err)) { return } } + ipprefix, err := netaddr.ParseIPPrefix(p) + if err != nil { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefix %s is not valid: %v", p, err)) { + return + } + } + if ipprefix.IP.Is4() { + addressFamilies["ipv4"] = true + } + if ipprefix.IP.Is6() { + addressFamilies["ipv6"] = true + } prefixes = append(prefixes, *prefix) } + if len(addressFamilies) > 1 { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefixes have different addressfamilies")) { + return + } + } + destPrefixes := metal.Prefixes{} for _, p := range requestPayload.DestinationPrefixes { prefix, err := metal.NewPrefixFromCIDR(p) if err != nil { - if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefix %v is not a valid ip with mask: %v", p, err)) { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefix %v is not valid: %v", p, err)) { return } } @@ -301,9 +322,12 @@ func (r networkResource) createNetwork(request *restful.Request, response *restf return } + // We should support two private super per partition, one per addressfamily + // the network allocate request must be configurable with addressfamily if privateSuper { boolTrue := true - err := r.ds.FindNetwork(&datastore.NetworkSearchQuery{PartitionID: &partition.ID, PrivateSuper: &boolTrue}, &metal.Network{}) + var nw metal.Network + err := r.ds.FindNetwork(&datastore.NetworkSearchQuery{PartitionID: &partition.ID, PrivateSuper: &boolTrue}, &nw) if err != nil { if !metal.IsNotFound(err) { if checkError(request, response, utils.CurrentFuncName(), err) { @@ -311,8 +335,24 @@ func (r networkResource) createNetwork(request *restful.Request, response *restf } } } else { - if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("partition with id %q already has a private super network", partition.ID)) { - return + existingsuper := nw.Prefixes[0].String() + ipprefxexistingsuper, err := netaddr.ParseIPPrefix(existingsuper) + if err != nil { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefix %v is not valid: %v", existingsuper, err)) { + return + } + } + newsuper := prefixes[0].String() + ipprefixnew, err := netaddr.ParseIPPrefix(newsuper) + if err != nil { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefix %v is not valid: %v", newsuper, err)) { + return + } + } + if (ipprefixnew.IP.Is4() && ipprefxexistingsuper.IP.Is4()) || (ipprefixnew.IP.Is6() && ipprefxexistingsuper.IP.Is6()) { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("partition with id %q already has a private super network for this addressfamily", partition.ID)) { + return + } } } } @@ -372,6 +412,25 @@ func (r networkResource) createNetwork(request *restful.Request, response *restf Labels: labels, } + // check if childprefixlength is set and matches addressfamily + if requestPayload.ChildPrefixLength != nil && privateSuper { + cpl := *requestPayload.ChildPrefixLength + for _, p := range prefixes { + ipprefix, err := netaddr.ParseIPPrefix(p.String()) + if err != nil { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given prefix %v is not a valid ip with mask: %v", p, err)) { + return + } + } + if cpl <= ipprefix.Bits { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("given childprefixlength %d is not greater than prefix length of:%s", cpl, p.String())) { + return + } + } + } + nw.ChildPrefixLength = requestPayload.ChildPrefixLength + } + for _, p := range nw.Prefixes { err := r.ipamer.CreatePrefix(p) if checkError(request, response, utils.CurrentFuncName(), err) { @@ -392,6 +451,7 @@ func (r networkResource) createNetwork(request *restful.Request, response *restf } } +// TODO add possibility to allocate from a non super network if given in the AllocateRequest and super has childprefixlength func (r networkResource) allocateNetwork(request *restful.Request, response *restful.Response) { var requestPayload v1.NetworkAllocateRequest err := request.ReadEntity(&requestPayload) @@ -441,25 +501,67 @@ func (r networkResource) allocateNetwork(request *restful.Request, response *res return } - var superNetwork metal.Network + var superNetworks metal.Networks boolTrue := true - err = r.ds.FindNetwork(&datastore.NetworkSearchQuery{PartitionID: &partition.ID, PrivateSuper: &boolTrue}, &superNetwork) + err = r.ds.SearchNetworks(&datastore.NetworkSearchQuery{PartitionID: &partition.ID, PrivateSuper: &boolTrue}, &superNetworks) if checkError(request, response, utils.CurrentFuncName(), err) { return } + addressFamily := v1.IPv4AddressFamily + if requestPayload.AddressFamily != nil { + addressFamily = v1.ToAddressFamily(*requestPayload.AddressFamily) + } + zapup.MustRootLogger().Sugar().Infow("network allocate", "family", addressFamily) + var ( + superNetwork metal.Network + superNetworkFound bool + ) + for _, snw := range superNetworks { + ipprefix, err := netaddr.ParseIPPrefix(snw.Prefixes[0].String()) + if checkError(request, response, utils.CurrentFuncName(), err) { + return + } + if addressFamily == v1.IPv4AddressFamily && ipprefix.IP.Is4() { + superNetwork = snw + superNetworkFound = true + } + if addressFamily == v1.IPv6AddressFamily && ipprefix.IP.Is6() { + superNetwork = snw + superNetworkFound = true + } + } + if !superNetworkFound { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("no supernetwork for addressfamily:%s found", addressFamily)) { + return + } + } + zapup.MustRootLogger().Sugar().Infow("network allocate", "supernetwork", superNetwork.ID) + nwSpec := &metal.Network{ Base: metal.Base{ Name: name, Description: description, }, PartitionID: partition.ID, - ProjectID: project.GetProject().GetMeta().GetId(), + ProjectID: project.GetProject().GetMeta().Id, Labels: requestPayload.Labels, Shared: shared, } - nw, err := createChildNetwork(r.ds, r.ipamer, nwSpec, &superNetwork, partition.PrivateNetworkPrefixLength) + if superNetwork.ChildPrefixLength == nil { + if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("supernetwork %s has no childprefixlength specified", superNetwork.ID)) { + return + } + } + + // Allow configurable prefix length + length := *superNetwork.ChildPrefixLength + if requestPayload.Length != nil { + length = *requestPayload.Length + } + + nw, err := createChildNetwork(r.ds, r.ipamer, nwSpec, &superNetwork, length) if checkError(request, response, utils.CurrentFuncName(), err) { return } @@ -472,7 +574,7 @@ func (r networkResource) allocateNetwork(request *restful.Request, response *res } } -func createChildNetwork(ds *datastore.RethinkStore, ipamer ipam.IPAMer, nwSpec *metal.Network, parent *metal.Network, childLength int) (*metal.Network, error) { +func createChildNetwork(ds *datastore.RethinkStore, ipamer ipam.IPAMer, nwSpec *metal.Network, parent *metal.Network, childLength uint8) (*metal.Network, error) { vrf, err := acquireRandomVRF(ds) if err != nil { return nil, fmt.Errorf("Could not acquire a vrf: %v", err) @@ -723,13 +825,14 @@ func getNetworkUsage(nw *metal.Network, ipamer ipam.IPAMer) *metal.NetworkUsage } usage.AvailableIPs = usage.AvailableIPs + u.AvailableIPs usage.UsedIPs = usage.UsedIPs + u.UsedIPs + usage.AvailablePrefixList = append(usage.AvailablePrefixList, u.AvailablePrefixList...) usage.AvailablePrefixes = usage.AvailablePrefixes + u.AvailablePrefixes usage.UsedPrefixes = usage.UsedPrefixes + u.UsedPrefixes } return usage } -func createChildPrefix(parentPrefixes metal.Prefixes, childLength int, ipamer ipam.IPAMer) (*metal.Prefix, error) { +func createChildPrefix(parentPrefixes metal.Prefixes, childLength uint8, ipamer ipam.IPAMer) (*metal.Prefix, error) { var errors []error var err error var childPrefix *metal.Prefix @@ -787,7 +890,7 @@ var ( ) availablePrefixesDesc = prometheus.NewDesc( "metal_network_prefix_available", - "The total number of available prefixes of the network", + "The total number of available 2 bit prefixes of the network", []string{"networkId", "prefixes", "destPrefixes", "partitionId", "projectId", "parentNetworkID", "vrf", "isPrivateSuper", "useNat", "isUnderlay"}, nil, ) ) diff --git a/cmd/metal-api/internal/service/network-service_test.go b/cmd/metal-api/internal/service/network-service_test.go index 46ded66d3..a37d9f1d8 100644 --- a/cmd/metal-api/internal/service/network-service_test.go +++ b/cmd/metal-api/internal/service/network-service_test.go @@ -2,25 +2,26 @@ package service import ( "bytes" + "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" - "github.com/metal-stack/metal-lib/httperrors" - r "gopkg.in/rethinkdb/rethinkdb-go.v6" - + restful "github.com/emicklei/go-restful/v3" + goipam "github.com/metal-stack/go-ipam" + mdmv1 "github.com/metal-stack/masterdata-api/api/v1" + mdmock "github.com/metal-stack/masterdata-api/api/v1/mocks" + mdm "github.com/metal-stack/masterdata-api/pkg/client" "github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore" "github.com/metal-stack/metal-api/cmd/metal-api/internal/ipam" - "github.com/metal-stack/metal-api/cmd/metal-api/internal/metal" v1 "github.com/metal-stack/metal-api/cmd/metal-api/internal/service/v1" - - restful "github.com/emicklei/go-restful/v3" - goipam "github.com/metal-stack/go-ipam" "github.com/metal-stack/metal-api/cmd/metal-api/internal/testdata" + "github.com/metal-stack/metal-lib/httperrors" "github.com/stretchr/testify/require" + r "gopkg.in/rethinkdb/rethinkdb-go.v6" ) func TestGetNetworks(t *testing.T) { @@ -176,6 +177,272 @@ func TestCreateNetwork(t *testing.T) { require.Equal(t, destPrefixes, result.DestinationPrefixes) } +func Test_networkResource_createNetwork(t *testing.T) { + tests := []struct { + name string + networkName string + networkID string + partitionID string + projectID string + prefixes []string + destinationPrefixes []string + vrf uint + childprefixlength *uint8 + privateSuper bool + underlay bool + nat bool + expectedStatus int + expectedErrorMessage string + }{ + { + name: "simple IPv4", + networkName: testdata.Nw1.Name, + partitionID: testdata.Nw1.PartitionID, + projectID: testdata.Nw1.ProjectID, + prefixes: []string{"172.0.0.0/24"}, + destinationPrefixes: []string{"0.0.0.0/0"}, + vrf: uint(10000), + expectedStatus: http.StatusCreated, + }, + { + name: "privatesuper IPv4", + networkName: testdata.Nw1.Name, + partitionID: testdata.Nw1.PartitionID, + projectID: testdata.Nw1.ProjectID, + prefixes: []string{"172.0.0.0/24"}, + destinationPrefixes: []string{"0.0.0.0/0"}, + privateSuper: true, + vrf: uint(10000), + expectedStatus: http.StatusUnprocessableEntity, + expectedErrorMessage: "partition with id \"1\" already has a private super network for this addressfamily", + }, + { + name: "privatesuper IPv6", + networkName: testdata.Nw1.Name, + partitionID: testdata.Nw1.PartitionID, + projectID: testdata.Nw1.ProjectID, + prefixes: []string{"fdaa:bbcc::/50"}, + destinationPrefixes: []string{"::/0"}, + privateSuper: true, + vrf: uint(10000), + expectedStatus: http.StatusCreated, + }, + { + name: "broken IPv4", + networkName: testdata.Nw1.Name, + partitionID: testdata.Nw1.PartitionID, + projectID: testdata.Nw1.ProjectID, + prefixes: []string{"192.168.265.0/24"}, + destinationPrefixes: []string{"0.0.0.0/0"}, + privateSuper: true, + vrf: uint(10000), + expectedStatus: http.StatusUnprocessableEntity, + expectedErrorMessage: "given prefix 192.168.265.0/24 is not valid: netaddr.ParseIPPrefix(\"192.168.265.0/24\"): ParseIP(\"192.168.265.0\"): IPv4 field has value >255", + }, + { + name: "broken IPv6", + networkName: testdata.Nw1.Name, + partitionID: testdata.Nw1.PartitionID, + projectID: testdata.Nw1.ProjectID, + prefixes: []string{"fdaa:::/50"}, + destinationPrefixes: []string{"::/0"}, + privateSuper: true, + vrf: uint(10000), + expectedStatus: http.StatusUnprocessableEntity, + expectedErrorMessage: "given prefix fdaa:::/50 is not valid: netaddr.ParseIPPrefix(\"fdaa:::/50\"): ParseIP(\"fdaa:::\"): each colon-separated field must have at least one digit (at \":\")", + }, + { + name: "mixed prefix addressfamilies", + networkName: testdata.Nw1.Name, + partitionID: testdata.Nw1.PartitionID, + projectID: testdata.Nw1.ProjectID, + prefixes: []string{"172.0.0.0/24", "fdaa:bbcc::/50"}, + destinationPrefixes: []string{"0.0.0.0/0"}, + vrf: uint(10000), + expectedStatus: http.StatusUnprocessableEntity, + expectedErrorMessage: "given prefixes have different addressfamilies", + }, + { + name: "broken destinationprefix", + networkName: testdata.Nw1.Name, + partitionID: testdata.Nw1.PartitionID, + projectID: testdata.Nw1.ProjectID, + prefixes: []string{"172.0.0.0/24"}, + destinationPrefixes: []string{"0.0.0.0/33"}, + vrf: uint(10000), + expectedStatus: http.StatusUnprocessableEntity, + expectedErrorMessage: "given prefix 0.0.0.0/33 is not valid: netaddr.ParseIPPrefix(\"33\"): prefix length out of range", + }, + { + name: "broken childprefixlength", + networkName: testdata.Nw1.Name, + partitionID: testdata.Nw1.PartitionID, + projectID: testdata.Nw1.ProjectID, + prefixes: []string{"fdaa:bbcc::/50"}, + childprefixlength: uint8Ptr(50), + privateSuper: true, + vrf: uint(10000), + expectedStatus: http.StatusUnprocessableEntity, + expectedErrorMessage: "given childprefixlength 50 is not greater than prefix length of:fdaa:bbcc::/50", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ds, mock := datastore.InitMockDB() + ipamer, err := testdata.InitMockIpamData(mock, false) + require.Nil(t, err) + testdata.InitMockDBData(mock) + + networkservice := NewNetwork(ds, ipamer, nil) + container := restful.NewContainer().Add(networkservice) + + createRequest := &v1.NetworkCreateRequest{ + Describable: v1.Describable{Name: &tt.networkName}, + NetworkBase: v1.NetworkBase{PartitionID: &tt.partitionID, ProjectID: &tt.projectID}, + NetworkImmutable: v1.NetworkImmutable{ + Prefixes: tt.prefixes, + DestinationPrefixes: tt.destinationPrefixes, + Vrf: &tt.vrf, Nat: tt.nat, PrivateSuper: tt.privateSuper, Underlay: tt.underlay, + }, + } + if tt.childprefixlength != nil { + createRequest.ChildPrefixLength = tt.childprefixlength + } + js, _ := json.Marshal(createRequest) + body := bytes.NewBuffer(js) + req := httptest.NewRequest("PUT", "/v1/network", body) + req.Header.Add("Content-Type", "application/json") + container = injectAdmin(container, req) + w := httptest.NewRecorder() + container.ServeHTTP(w, req) + + resp := w.Result() + require.Equal(t, tt.expectedStatus, resp.StatusCode, w.Body.String()) + if tt.expectedStatus > 300 { + var result httperrors.HTTPErrorResponse + err := json.NewDecoder(resp.Body).Decode(&result) + + require.Nil(t, err) + require.Equal(t, tt.expectedErrorMessage, result.Message) + } else { + var result v1.NetworkResponse + err = json.NewDecoder(resp.Body).Decode(&result) + require.Nil(t, err) + require.Equal(t, tt.networkName, *result.Name) + require.Equal(t, tt.partitionID, *result.PartitionID) + require.Equal(t, tt.projectID, *result.ProjectID) + require.Equal(t, tt.destinationPrefixes, result.DestinationPrefixes) + if tt.childprefixlength != nil { + require.Equal(t, tt.childprefixlength, result.ChildPrefixLength) + } + } + }) + } +} + +func Test_networkResource_allocateNetwork(t *testing.T) { + tests := []struct { + name string + networkName string + partitionID string + projectID string + childprefixlength *uint8 + addressFamily *string + shared bool + expectedStatus int + expectedErrorMessage string + }{ + { + name: "simple ipv4", + networkName: "tenantv4", + partitionID: "1", + projectID: "project-1", + expectedStatus: http.StatusCreated, + }, + { + name: "ipv6 without ipv6 super", + networkName: "tenantv6", + partitionID: "1", + projectID: "project-1", + addressFamily: strPtr("ipv6"), + expectedStatus: http.StatusUnprocessableEntity, + expectedErrorMessage: "no supernetwork for addressfamily:IPv6 found", + }, + } + for _, tt := range tests { + ds, mock := datastore.InitMockDB() + + ipamer, err := testdata.InitMockIpamData(mock, false) + require.Nil(t, err) + mock.On(r.DB("mockdb").Table("network").Filter(r.MockAnything()).Filter(r.MockAnything())).Return(metal.Networks{testdata.Nw1, testdata.Nw2}, nil) + changes := []r.ChangeResponse{{OldValue: map[string]interface{}{"id": float64(42)}}} + mock.On(r.DB("mockdb").Table("integerpool").Limit(1).Delete(r. + DeleteOpts{ReturnChanges: true})).Return(r.WriteResponse{Changes: changes}, nil) + + mock.On(r.DB("mockdb").Table("partition").Get(r.MockAnything())).Return( + metal.Partition{ + Base: metal.Base{ID: tt.partitionID}, + }, + nil, + ) + testdata.InitMockDBData(mock) + + psc := mdmock.ProjectServiceClient{} + psc.On("Get", context.Background(), &mdmv1.ProjectGetRequest{Id: "project-1"}).Return(&mdmv1.ProjectResponse{ + Project: &mdmv1.Project{ + Meta: &mdmv1.Meta{Id: tt.projectID}, + }, + }, nil, + ) + tsc := mdmock.TenantServiceClient{} + + mdc := mdm.NewMock(&psc, &tsc) + + networkservice := NewNetwork(ds, ipamer, mdc) + container := restful.NewContainer().Add(networkservice) + + allocateRequest := &v1.NetworkAllocateRequest{ + Describable: v1.Describable{Name: &tt.networkName}, + NetworkBase: v1.NetworkBase{PartitionID: &tt.partitionID, ProjectID: &tt.projectID}, + AddressFamily: tt.addressFamily, + Length: tt.childprefixlength, + } + + js, _ := json.Marshal(allocateRequest) + body := bytes.NewBuffer(js) + req := httptest.NewRequest("POST", "/v1/network/allocate", body) + req.Header.Add("Content-Type", "application/json") + container = injectAdmin(container, req) + w := httptest.NewRecorder() + container.ServeHTTP(w, req) + + resp := w.Result() + require.Equal(t, tt.expectedStatus, resp.StatusCode, w.Body.String()) + if tt.expectedStatus > 300 { + var result httperrors.HTTPErrorResponse + err := json.NewDecoder(resp.Body).Decode(&result) + + require.Nil(t, err) + require.Equal(t, tt.expectedErrorMessage, result.Message) + } else { + var result v1.NetworkResponse + err = json.NewDecoder(resp.Body).Decode(&result) + require.Nil(t, err) + require.Equal(t, tt.networkName, *result.Name) + require.Equal(t, tt.partitionID, *result.PartitionID) + require.Equal(t, tt.projectID, *result.ProjectID) + // TODO check af and length + } + } +} + +func uint8Ptr(u uint8) *uint8 { + return &u +} +func strPtr(s string) *string { + return &s +} + func TestUpdateNetwork(t *testing.T) { ds, mock := datastore.InitMockDB() testdata.InitMockDBData(mock) diff --git a/cmd/metal-api/internal/service/partition-service.go b/cmd/metal-api/internal/service/partition-service.go index 43bad6fba..498560422 100644 --- a/cmd/metal-api/internal/service/partition-service.go +++ b/cmd/metal-api/internal/service/partition-service.go @@ -172,15 +172,7 @@ func (r partitionResource) createPartition(request *restful.Request, response *r if requestPayload.MgmtServiceAddress != nil { mgmtServiceAddress = *requestPayload.MgmtServiceAddress } - prefixLength := 22 - if requestPayload.PrivateNetworkPrefixLength != nil { - prefixLength = *requestPayload.PrivateNetworkPrefixLength - if prefixLength < 16 || prefixLength > 30 { - if checkError(request, response, utils.CurrentFuncName(), fmt.Errorf("private network prefix length is out of range")) { - return - } - } - } + var imageURL string if requestPayload.PartitionBootConfiguration.ImageURL != nil { imageURL = *requestPayload.PartitionBootConfiguration.ImageURL @@ -200,8 +192,7 @@ func (r partitionResource) createPartition(request *restful.Request, response *r Name: name, Description: description, }, - MgmtServiceAddress: mgmtServiceAddress, - PrivateNetworkPrefixLength: prefixLength, + MgmtServiceAddress: mgmtServiceAddress, BootConfiguration: metal.BootConfiguration{ ImageURL: imageURL, KernelURL: kernelURL, diff --git a/cmd/metal-api/internal/service/v1/network.go b/cmd/metal-api/internal/service/v1/network.go index e3919c838..db0eb8f04 100644 --- a/cmd/metal-api/internal/service/v1/network.go +++ b/cmd/metal-api/internal/service/v1/network.go @@ -19,6 +19,7 @@ type NetworkImmutable struct { DestinationPrefixes []string `json:"destinationprefixes" modelDescription:"prefixes that are reachable within this network" description:"the destination prefixes of this network"` Nat bool `json:"nat" description:"if set to true, packets leaving this network get masqueraded behind interface ip"` PrivateSuper bool `json:"privatesuper" description:"if set to true, this network will serve as a partition's super network for the internal machine networks,there can only be one privatesuper network per partition"` + ChildPrefixLength *uint8 `json:"childprefixlength" description:"if privatesuper, this defines the bitlen of child prefixes if not nil" optional:"true"` Underlay bool `json:"underlay" description:"if set to true, this network can be used for underlay communication"` Vrf *uint `json:"vrf" description:"the vrf this network is associated with" optional:"true"` VrfShared *bool `json:"vrfshared" description:"if set to true, given vrf can be used by multiple networks, which is sometimes useful for network partioning (default: false)" optional:"true"` @@ -27,10 +28,11 @@ type NetworkImmutable struct { // NetworkUsage reports core metrics about available and used IPs or Prefixes in a Network. type NetworkUsage struct { - AvailableIPs uint64 `json:"available_ips" description:"the total available IPs" readonly:"true"` - UsedIPs uint64 `json:"used_ips" description:"the total used IPs" readonly:"true"` - AvailablePrefixes uint64 `json:"available_prefixes" description:"the total available Prefixes" readonly:"true"` - UsedPrefixes uint64 `json:"used_prefixes" description:"the total used Prefixes" readonly:"true"` + AvailableIPs uint64 `json:"available_ips" description:"the total available IPs" readonly:"true"` + UsedIPs uint64 `json:"used_ips" description:"the total used IPs" readonly:"true"` + AvailablePrefixes uint64 `json:"available_prefixes" description:"the total available 2 bit Prefixes" readonly:"true"` + AvailablePrefixList []string `json:"available_prefix_list" description:"a list of possible child prefixes"` + UsedPrefixes uint64 `json:"used_prefixes" description:"the total used Prefixes" readonly:"true"` } // NetworkCreateRequest is used to create a new Network. @@ -45,6 +47,29 @@ type NetworkCreateRequest struct { type NetworkAllocateRequest struct { Describable NetworkBase + AddressFamily *string `json:"address_family" description:"can be ipv4 or ipv6, defaults to ipv4" optional:"true"` + Length *uint8 `json:"length" description:"the bitlen of the prefix to allocate, defaults to childprefixlength of super prefix" optional:"true"` +} + +// AddressFamily identifies IPv4/IPv6 +type AddressFamily string + +const ( + // IPv4AddressFamily identifies IPv4 + IPv4AddressFamily = AddressFamily("IPv4") + // IPv6AddressFamily identifies IPv6 + IPv6AddressFamily = AddressFamily("IPv6") +) + +// ToAddressFamily will convert a string af to a AddressFamily +func ToAddressFamily(af string) AddressFamily { + switch af { + case "IPv4", "ipv4": + return IPv4AddressFamily + case "IPv6", "ipv6": + return IPv6AddressFamily + } + return IPv4AddressFamily } // NetworkFindRequest is used to find a Network with different criteria. @@ -105,15 +130,17 @@ func NewNetworkResponse(network *metal.Network, usage *metal.NetworkUsage) *Netw DestinationPrefixes: network.DestinationPrefixes.String(), Nat: network.Nat, PrivateSuper: network.PrivateSuper, + ChildPrefixLength: network.ChildPrefixLength, Underlay: network.Underlay, Vrf: &network.Vrf, ParentNetworkID: parentNetworkID, }, Usage: NetworkUsage{ - AvailableIPs: usage.AvailableIPs, - UsedIPs: usage.UsedIPs, - AvailablePrefixes: usage.AvailablePrefixes, - UsedPrefixes: usage.UsedPrefixes, + AvailableIPs: usage.AvailableIPs, + UsedIPs: usage.UsedIPs, + AvailablePrefixes: usage.AvailablePrefixes, + AvailablePrefixList: usage.AvailablePrefixList, + UsedPrefixes: usage.UsedPrefixes, }, Timestamps: Timestamps{ Created: network.Created, diff --git a/cmd/metal-api/internal/service/v1/partition.go b/cmd/metal-api/internal/service/v1/partition.go index b8e8e9abf..af41153ee 100644 --- a/cmd/metal-api/internal/service/v1/partition.go +++ b/cmd/metal-api/internal/service/v1/partition.go @@ -5,8 +5,7 @@ import ( ) type PartitionBase struct { - MgmtServiceAddress *string `json:"mgmtserviceaddress" description:"the address to the management service of this partition" optional:"true"` - PrivateNetworkPrefixLength *int `json:"privatenetworkprefixlength" description:"the length of private networks for the machine's child networks in this partition, default 22" optional:"true" minimum:"16" maximum:"30"` + MgmtServiceAddress *string `json:"mgmtserviceaddress" description:"the address to the management service of this partition" optional:"true"` } type PartitionBootConfiguration struct { @@ -65,8 +64,7 @@ func NewPartitionResponse(p *metal.Partition) *PartitionResponse { }, }, PartitionBase: PartitionBase{ - MgmtServiceAddress: &p.MgmtServiceAddress, - PrivateNetworkPrefixLength: &p.PrivateNetworkPrefixLength, + MgmtServiceAddress: &p.MgmtServiceAddress, }, PartitionBootConfiguration: PartitionBootConfiguration{ ImageURL: &p.BootConfiguration.ImageURL, diff --git a/cmd/metal-api/internal/testdata/testdata.go b/cmd/metal-api/internal/testdata/testdata.go index 4ff331b72..3d213020c 100644 --- a/cmd/metal-api/internal/testdata/testdata.go +++ b/cmd/metal-api/internal/testdata/testdata.go @@ -268,6 +268,8 @@ var ( prefixes2 = []metal.Prefix{prefix2} prefixes3 = []metal.Prefix{prefix3} prefixesIPAM = []metal.Prefix{prefixIPAM} + cpl1 = uint8(28) + cpl2 = uint8(22) Nw1 = metal.Network{ Base: metal.Base{ @@ -275,9 +277,10 @@ var ( Name: "Network 1", Description: "description 1", }, - PartitionID: Partition1.ID, - Prefixes: prefixes1, - PrivateSuper: true, + PartitionID: Partition1.ID, + Prefixes: prefixes1, + PrivateSuper: true, + ChildPrefixLength: &cpl1, } Nw2 = metal.Network{ Base: metal.Base{ @@ -285,8 +288,10 @@ var ( Name: "Network 2", Description: "description 2", }, - Prefixes: prefixes2, - Underlay: true, + PartitionID: Partition1.ID, + Prefixes: prefixes2, + Underlay: true, + ChildPrefixLength: &cpl2, } Nw3 = metal.Network{ Base: metal.Base{ @@ -461,6 +466,13 @@ var ( Tags: []string{tag.MachineID}, ProjectID: "1", } + IP4 = metal.IP{ + IPAddress: "2001:0db8:85a3::1", + Name: "IPv6 4", + Description: "description 4", + Type: "ephemeral", + ProjectID: "1", + } IPAMIP = metal.IP{ Name: "IPAM IP", Description: "description IPAM", @@ -494,7 +506,6 @@ var ( Name: "partition1", Description: "description 1", }, - PrivateNetworkPrefixLength: 22, } Partition2 = metal.Partition{ Base: metal.Base{ @@ -502,7 +513,6 @@ var ( Name: "partition2", Description: "description 2", }, - PrivateNetworkPrefixLength: 22, } Partition3 = metal.Partition{ Base: metal.Base{ @@ -510,7 +520,6 @@ var ( Name: "partition3", Description: "description 3", }, - PrivateNetworkPrefixLength: 22, } // Switches @@ -685,7 +694,7 @@ var ( } // All IPs TestIPs = []metal.IP{ - IP1, IP2, IP3, + IP1, IP2, IP3, IP4, } // All Events @@ -798,11 +807,14 @@ func InitMockDBData(mock *r.Mock) { mock.On(r.DB("mockdb").Table("network").Get("404")).Return(nil, fmt.Errorf("Test Error")) mock.On(r.DB("mockdb").Table("network").Get("999")).Return(nil, nil) + // mock.On(r.DB("mockdb").Table("network").Filter(r.MockAnything()).Filter(r.MockAnything())).Return(metal.Networks{Nw1, Nw2}, nil) + mock.On(r.DB("mockdb").Table("network").Filter(func(var_3 r.Term) r.Term { return var_3.Field("partitionid").Eq("1") }).Filter(func(var_4 r.Term) r.Term { return var_4.Field("privatesuper").Eq(true) })).Return(Nw3, nil) mock.On(r.DB("mockdb").Table("ip").Get("1.2.3.4")).Return(IP1, nil) mock.On(r.DB("mockdb").Table("ip").Get("2.3.4.5")).Return(IP2, nil) mock.On(r.DB("mockdb").Table("ip").Get("3.4.5.6")).Return(IP3, nil) + mock.On(r.DB("mockdb").Table("ip").Get("2001:0db8:85a3::1")).Return(IP4, nil) mock.On(r.DB("mockdb").Table("ip").Get("8.8.8.8")).Return(nil, fmt.Errorf("Test Error")) mock.On(r.DB("mockdb").Table("ip").Get("9.9.9.9")).Return(nil, nil) mock.On(r.DB("mockdb").Table("ip").Get(Partition1InternetIP.IPAddress)).Return(Partition1InternetIP, nil) diff --git a/go.mod b/go.mod index 754f14fac..efc3b614e 100644 --- a/go.mod +++ b/go.mod @@ -4,47 +4,48 @@ go 1.15 require ( github.com/Masterminds/semver/v3 v3.1.1 - github.com/Microsoft/hcsshim v0.8.10 // indirect - github.com/containerd/continuity v0.0.0-20201119173150-04c754faca46 // indirect + github.com/Microsoft/hcsshim v0.8.14 // indirect + github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7 // indirect github.com/dustin/go-humanize v1.0.0 github.com/emicklei/go-restful-openapi/v2 v2.2.1 github.com/emicklei/go-restful/v3 v3.4.0 - github.com/go-openapi/spec v0.19.15 + github.com/go-openapi/spec v0.20.3 github.com/go-stack/stack v1.8.0 github.com/golang/protobuf v1.4.3 github.com/google/go-cmp v0.5.4 - github.com/google/uuid v1.1.2 + github.com/google/uuid v1.2.0 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/kr/pretty v0.2.1 // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect - github.com/metal-stack/go-ipam v1.7.0 + github.com/metal-stack/go-ipam v1.8.1 github.com/metal-stack/masterdata-api v0.8.4 - github.com/metal-stack/metal-lib v0.6.6 + github.com/metal-stack/metal-lib v0.6.9 github.com/metal-stack/security v0.4.0 github.com/metal-stack/v v1.0.2 github.com/morikuni/aec v1.0.0 // indirect github.com/nsqio/go-nsq v1.0.8 github.com/pelletier/go-toml v1.8.1 // indirect github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.8.0 + github.com/prometheus/client_golang v1.9.0 github.com/smartystreets/assertions v1.2.0 // indirect - github.com/spf13/afero v1.4.1 // indirect + github.com/spf13/afero v1.5.1 // indirect github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v1.1.1 github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/viper v1.7.1 - github.com/stretchr/testify v1.6.1 + github.com/stretchr/testify v1.7.0 github.com/testcontainers/testcontainers-go v0.9.0 github.com/tidwall/pretty v1.0.2 // indirect go.uber.org/zap v1.16.0 go4.org v0.0.0-20201209231011-d4a079459e60 - golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 - golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect - google.golang.org/grpc v1.33.2 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad + golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect + google.golang.org/grpc v1.35.0 google.golang.org/protobuf v1.25.0 gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 + inet.af/netaddr v0.0.0-20210203214853-aa702495c49b ) diff --git a/go.sum b/go.sum index 688f7f0f8..0578c68f8 100644 --- a/go.sum +++ b/go.sum @@ -44,11 +44,11 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/squirrel v1.4.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab h1:9pygWVFqbY9lPxM0peffumuVDyMuIMzNLyO9uFjJuQo= -github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331 h1:3YnB7Hpmh1lPecPE8doMOtYCrMdrpedZOvxfuNES/Vk= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.10 h1:k5wTrpnVU2/xv8ZuzGkbXVd3js5zJ8RnumPo5RxiIxU= -github.com/Microsoft/hcsshim v0.8.10/go.mod h1:g5uw8EV2mAlzqe94tfNBNdr89fnbD/n3HV0OhsddkmM= +github.com/Microsoft/hcsshim v0.8.14 h1:lbPVK25c1cu5xTLITwpUcxoA9vKrKErASPYygvouJns= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -112,6 +112,7 @@ github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmE github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 h1:qWj4qVYZ95vLWwqyNJCQg7rDsG5wPdze0UaPolH7DUk= @@ -121,8 +122,8 @@ github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20201119173150-04c754faca46 h1:yCza5Vm4QxrP97DfM7W2U0ks430c9pqr7lFeZYZixQI= -github.com/containerd/continuity v0.0.0-20201119173150-04c754faca46/go.mod h1:W0qIOTD7mp2He++YVq+kgfXezRYqzP1uDuMVH1bITDY= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7 h1:6ejg6Lkk8dskcM7wQ28gONkukbQkM4qpj4RnYbpFzrI= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= @@ -159,6 +160,7 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dvyukov/go-fuzz v0.0.0-20201127111758-49e582c6c23d/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -174,6 +176,7 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= @@ -221,8 +224,6 @@ github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3Hfo github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= -github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= @@ -246,10 +247,8 @@ github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHK github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.19.8 h1:qAdZLh1r6QF/hI/gTq+TJTvsQUodZsM7KLqkAJdiJNg= github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/spec v0.19.14 h1:r4fbYFo6N4ZelmSX8G6p+cv/hZRXzcuqQIADGT1iNKM= -github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA= -github.com/go-openapi/spec v0.19.15 h1:uxh8miNJEfMm8l8ekpY7i39LcORm1xSRtoipEGl1JPk= -github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU= +github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= +github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= @@ -266,10 +265,8 @@ github.com/go-openapi/swag v0.19.6/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfT github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE= github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY= -github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc= -github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= -github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI= -github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo= @@ -387,6 +384,8 @@ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -445,6 +444,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE= +github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -498,8 +499,11 @@ github.com/lestrrat-go/jwx v1.0.4 h1:IkJICAolgmoutGs5go/loBHWtmiUSDADv3NUuN5dk8A github.com/lestrrat-go/jwx v1.0.4/go.mod h1:TPF17WiSFegZo+c20fdpw49QD+/7n4/IsGvEmCSWwT0= github.com/lestrrat-go/pdebug v0.0.0-20200204225717-4d6bd78da58d/go.mod h1:B06CSso/AWxiPejj+fheUINGeBKeeEZNt8w+EoU7+L8= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= +github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lopezator/migrator v0.3.0/go.mod h1:bpVAVPkWSvTw8ya2Pk7E/KiNAyDWNImgivQY79o8/8I= @@ -522,18 +526,18 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/metal-stack/go-ipam v1.7.0 h1:OwezGvR5xWTUlJ0LoxULXCfx5PjUB9OK4gT1keioabI= -github.com/metal-stack/go-ipam v1.7.0/go.mod h1:d4odlA8XAoF+OV0NBcWGtG9RItONHMctIW3Y/jP/vzY= +github.com/metal-stack/go-ipam v1.8.1 h1:s+nlV0SjAE6Xx+jI9jplDddSllb3WtL8mpwC5zGXKsY= +github.com/metal-stack/go-ipam v1.8.1/go.mod h1:Hf+BjZGraDarRfpGmfO1MvsSTpYNyAazVwdtwTwJ7Q0= github.com/metal-stack/masterdata-api v0.8.4 h1:CnrhxiS9XL5PQN+J0yFv5KNvuan3JMx5omofEwHVweQ= github.com/metal-stack/masterdata-api v0.8.4/go.mod h1:4EDS04thTWdxcTpLsaLdG1PEUdnvKJFIdf7GEMduDdI= github.com/metal-stack/metal-lib v0.6.3/go.mod h1:r8qhfX72eAzClR/pEaQvdwM//Otx9gegYoOphLPmmQ4= -github.com/metal-stack/metal-lib v0.6.6 h1:5ajMDUGHruYOmRqn3r373rbIKbTtQkfVfKLKmRatJnE= -github.com/metal-stack/metal-lib v0.6.6/go.mod h1:r8qhfX72eAzClR/pEaQvdwM//Otx9gegYoOphLPmmQ4= +github.com/metal-stack/metal-lib v0.6.9 h1:6AvJ8RKJqjed2GdKIZlN9Qvt8sXhu3r6yfYKv++pLaU= +github.com/metal-stack/metal-lib v0.6.9/go.mod h1:r8qhfX72eAzClR/pEaQvdwM//Otx9gegYoOphLPmmQ4= github.com/metal-stack/security v0.4.0 h1:NrPm5srgmgeS9UdQmGKLEJ3P7BSsV2Gm7P781LmM0Xo= github.com/metal-stack/security v0.4.0/go.mod h1:C7kSrHwRcG+47375RJjhakN1LenbEJF9uQd4I50nZlY= github.com/metal-stack/v v1.0.2 h1:IGtLAGtazQd8r0i/5+YNjBJUEIZYrbVxynY9EXrlTV4= @@ -650,6 +654,8 @@ github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNja github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw= github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= +github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU= +github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -666,6 +672,8 @@ github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lN github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -705,8 +713,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.4.1 h1:asw9sl74539yqavKaglDM5hFpdJVK0Y5Dr/JOgQ89nQ= -github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg= +github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -741,6 +749,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/testcontainers/testcontainers-go v0.9.0 h1:ZyftCfROjGrKlxk3MOUn2DAzWrUtzY/mj17iAkdUIvI= @@ -795,6 +805,11 @@ go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= +go4.org/intern v0.0.0-20210108033219-3eb7198706b2 h1:VFTf+jjIgsldaz/Mr00VaCSswHJrI2hIjQygE/W4IMg= +go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 h1:1tk03FUNpulq2cuWpXZWj649rwJpk0d20rxWiopKRmc= +go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -813,9 +828,8 @@ golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc= -golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -897,10 +911,8 @@ golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOL golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201026091529-146b70c837a4 h1:awiuzyrRjJDb+OXi9ceHO3SDxVoN3JER57mhtqkdQBs= golang.org/x/net v0.0.0-20201026091529-146b70c837a4/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnpk5GfY7InR8rczoMqRM= -golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -979,8 +991,15 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e h1:AyodaIpKjppX+cBfTASF2E1US3H2JFBj920Ot3rtDjs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -988,14 +1007,14 @@ golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1139,8 +1158,8 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.35.0 h1:TwIQcH3es+MojMVojxxfQ3l3OF2KzlRxML2xZq0kRo8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc/examples v0.0.0-20201026143318-e8e22874d164/go.mod h1:IBqQ7wSUJ2Ep09a8rMWFsg4fmI2r38zwsq8a0GgxXpM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1212,6 +1231,10 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k= honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +inet.af/netaddr v0.0.0-20210129021658-06debf945877 h1:kiGwZoOS5kyefrTgIAvPzyfTuVfensOlU73GJN9OQKY= +inet.af/netaddr v0.0.0-20210129021658-06debf945877/go.mod h1:I2i9ONCXRZDnG1+7O8fSuYzjcPxHQXrIfzD/IkR87x4= +inet.af/netaddr v0.0.0-20210203214853-aa702495c49b h1:WFohjcZUndlBrz8/Yy4dG7CSvLWWedlPPNfA1BDUWBQ= +inet.af/netaddr v0.0.0-20210203214853-aa702495c49b/go.mod h1:I2i9ONCXRZDnG1+7O8fSuYzjcPxHQXrIfzD/IkR87x4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/spec/metal-api.json b/spec/metal-api.json index f34bc5b47..9615a5365 100644 --- a/spec/metal-api.json +++ b/spec/metal-api.json @@ -1996,6 +1996,10 @@ }, "v1.NetworkAllocateRequest": { "properties": { + "address_family": { + "description": "can be ipv4 or ipv6, defaults to ipv4", + "type": "string" + }, "description": { "description": "a description for this entity", "type": "string" @@ -2007,6 +2011,11 @@ "description": "free labels that you associate with this network.", "type": "object" }, + "length": { + "description": "the bitlen of the prefix to allocate, defaults to childprefixlength of super prefix", + "format": "byte", + "type": "integer" + }, "name": { "description": "a readable name for this entity", "type": "string" @@ -2027,6 +2036,11 @@ }, "v1.NetworkCreateRequest": { "properties": { + "childprefixlength": { + "description": "if privatesuper, this defines the bitlen of child prefixes if not nil", + "format": "byte", + "type": "integer" + }, "description": { "description": "a description for this entity", "type": "string" @@ -2166,6 +2180,11 @@ "readOnly": true, "type": "string" }, + "childprefixlength": { + "description": "if privatesuper, this defines the bitlen of child prefixes if not nil", + "format": "byte", + "type": "integer" + }, "created": { "description": "the creation time of this entity", "format": "date-time", @@ -2303,8 +2322,15 @@ "format": "integer", "type": "integer" }, + "available_prefix_list": { + "description": "a list of possible child prefixes", + "items": { + "type": "string" + }, + "type": "array" + }, "available_prefixes": { - "description": "the total available Prefixes", + "description": "the total available 2 bit Prefixes", "format": "integer", "type": "integer" }, @@ -2321,6 +2347,7 @@ }, "required": [ "available_ips", + "available_prefix_list", "available_prefixes", "used_ips", "used_prefixes" @@ -2393,13 +2420,6 @@ "name": { "description": "a readable name for this entity", "type": "string" - }, - "privatenetworkprefixlength": { - "description": "the length of private networks for the machine's child networks in this partition, default 22", - "format": "int32", - "maximum": 30, - "minimum": 16, - "type": "integer" } }, "required": [ @@ -2441,13 +2461,6 @@ "name": { "description": "a readable name for this entity", "type": "string" - }, - "privatenetworkprefixlength": { - "description": "the length of private networks for the machine's child networks in this partition, default 22", - "format": "int32", - "maximum": 30, - "minimum": 16, - "type": "integer" } }, "required": [