Skip to content

Commit 3812715

Browse files
authored
Provide network type migration as preparation for MEP-4. (#620)
1 parent 3f0dd4f commit 3812715

File tree

4 files changed

+270
-21
lines changed

4 files changed

+270
-21
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package migrations
2+
3+
import (
4+
"slices"
5+
6+
"github.com/metal-stack/metal-lib/pkg/pointer"
7+
8+
"github.com/metal-stack/metal-api/cmd/metal-api/internal/datastore"
9+
"github.com/metal-stack/metal-api/cmd/metal-api/internal/metal"
10+
11+
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
12+
)
13+
14+
func init() {
15+
datastore.MustRegisterMigration(datastore.Migration{
16+
Name: "migrate networks to have proper networkType set",
17+
Version: 9,
18+
Up: func(db *r.Term, session r.QueryExecutor, ds *datastore.RethinkStore) error {
19+
nws, err := ds.ListNetworks()
20+
if err != nil {
21+
return err
22+
}
23+
24+
// first detect all shared vrf ids
25+
// maps vrfid > count
26+
var (
27+
vrfCount = make(map[uint]int)
28+
sharedVrfs []uint
29+
)
30+
31+
for _, nw := range nws {
32+
if nw.Vrf == 0 {
33+
continue
34+
}
35+
count, ok := vrfCount[nw.Vrf]
36+
if !ok {
37+
vrfCount[nw.Vrf] = 1
38+
} else {
39+
vrfCount[nw.Vrf] = count + 1
40+
}
41+
}
42+
43+
for vrf, count := range vrfCount {
44+
if count > 1 {
45+
sharedVrfs = append(sharedVrfs, vrf)
46+
}
47+
}
48+
49+
// now convert all networks
50+
for _, old := range nws {
51+
new := old
52+
53+
// assume external network by default
54+
new.NetworkType = pointer.Pointer(metal.ExternalNetworkType)
55+
56+
if old.Shared && old.ParentNetworkID != "" {
57+
new.NetworkType = pointer.Pointer(metal.ChildSharedNetworkType)
58+
}
59+
if old.Shared && old.ParentNetworkID == "" {
60+
new.NetworkType = pointer.Pointer(metal.ExternalNetworkType)
61+
}
62+
if !old.Shared && old.ParentNetworkID != "" && !slices.Contains(sharedVrfs, old.Vrf) {
63+
new.NetworkType = pointer.Pointer(metal.ChildNetworkType)
64+
}
65+
if old.ProjectID == "" && old.ParentNetworkID == "" && !slices.Contains(sharedVrfs, old.Vrf) {
66+
new.NetworkType = pointer.Pointer(metal.ExternalNetworkType)
67+
}
68+
if old.PrivateSuper {
69+
new.NetworkType = pointer.Pointer(metal.SuperNetworkType)
70+
}
71+
if old.Underlay {
72+
new.NetworkType = pointer.Pointer(metal.UnderlayNetworkType)
73+
}
74+
75+
if old.Nat {
76+
new.NATType = pointer.Pointer(metal.IPv4MasqueradeNATType)
77+
} else {
78+
new.NATType = pointer.Pointer(metal.NoneNATType)
79+
}
80+
81+
err := ds.UpdateNetwork(&old, &new)
82+
if err != nil {
83+
return err
84+
}
85+
}
86+
87+
return nil
88+
},
89+
})
90+
}

cmd/metal-api/internal/datastore/migrations_integration/migrate_integration_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,94 @@ func Test_MigrationChildPrefixLength(t *testing.T) {
330330
require.NotNil(t, n4fetched.DefaultChildPrefixLength)
331331
require.Equal(t, uint8(22), n4fetched.DefaultChildPrefixLength[metal.IPv4AddressFamily])
332332
}
333+
334+
func Test_MigrationNetworkType(t *testing.T) {
335+
container, c, err := test.StartRethink(t)
336+
require.NoError(t, err)
337+
defer func() {
338+
_ = container.Terminate(context.Background())
339+
}()
340+
341+
log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
342+
343+
rs := datastore.New(log, c.IP+":"+c.Port, c.DB, c.User, c.Password)
344+
rs.VRFPoolRangeMin = 10000
345+
rs.VRFPoolRangeMax = 10010
346+
rs.ASNPoolRangeMin = 10000
347+
rs.ASNPoolRangeMax = 10010
348+
349+
err = rs.Connect()
350+
require.NoError(t, err)
351+
err = rs.Initialize()
352+
require.NoError(t, err)
353+
354+
nws := []*metal.Network{
355+
{Base: metal.Base{ID: "internet"}, Vrf: 10, Nat: true, Shared: false},
356+
{Base: metal.Base{ID: "underlay"}, Underlay: true},
357+
{Base: metal.Base{ID: "dc-interconnect"}, Vrf: 201},
358+
{Base: metal.Base{ID: "dc-interconnect-child-1"}, ParentNetworkID: "tenant-super", Vrf: 201},
359+
{Base: metal.Base{ID: "dc-interconnect-child-2"}, ParentNetworkID: "tenant-super", Vrf: 201},
360+
{Base: metal.Base{ID: "tenant-super"}, PrivateSuper: true},
361+
{Base: metal.Base{ID: "private-network-1"}, ParentNetworkID: "tenant-super", Vrf: 101},
362+
{Base: metal.Base{ID: "private-network-2"}, ParentNetworkID: "tenant-super", Vrf: 102},
363+
{Base: metal.Base{ID: "private-network-3"}, ParentNetworkID: "tenant-super", Vrf: 103},
364+
{Base: metal.Base{ID: "private-network-4"}, ParentNetworkID: "tenant-super", Vrf: 104},
365+
{Base: metal.Base{ID: "partition-storage"}, ParentNetworkID: "tenant-super", Vrf: 105, Shared: true},
366+
}
367+
368+
for _, nw := range nws {
369+
err := rs.CreateNetwork(nw)
370+
require.NoError(t, err)
371+
}
372+
373+
err = rs.Migrate(nil, false)
374+
require.NoError(t, err)
375+
376+
internet, err := rs.FindNetworkByID("internet")
377+
require.NoError(t, err)
378+
require.NotNil(t, internet)
379+
require.Equal(t, metal.IPv4MasqueradeNATType, *internet.NATType)
380+
require.Equal(t, metal.ExternalNetworkType, *internet.NetworkType)
381+
382+
underlay, err := rs.FindNetworkByID("underlay")
383+
require.NoError(t, err)
384+
require.NotNil(t, underlay)
385+
require.Equal(t, metal.NoneNATType, *underlay.NATType)
386+
require.Equal(t, metal.UnderlayNetworkType, *underlay.NetworkType)
387+
388+
tenantSuper, err := rs.FindNetworkByID("tenant-super")
389+
require.NoError(t, err)
390+
require.NotNil(t, tenantSuper)
391+
require.Equal(t, metal.NoneNATType, *tenantSuper.NATType)
392+
require.Equal(t, metal.SuperNetworkType, *tenantSuper.NetworkType)
393+
394+
private1, err := rs.FindNetworkByID("private-network-1")
395+
require.NoError(t, err)
396+
require.NotNil(t, private1)
397+
require.Equal(t, metal.NoneNATType, *private1.NATType)
398+
require.Equal(t, metal.ChildNetworkType, *private1.NetworkType)
399+
400+
private2, err := rs.FindNetworkByID("private-network-2")
401+
require.NoError(t, err)
402+
require.NotNil(t, private2)
403+
require.Equal(t, metal.NoneNATType, *private2.NATType)
404+
require.Equal(t, metal.ChildNetworkType, *private2.NetworkType)
405+
406+
private3, err := rs.FindNetworkByID("private-network-3")
407+
require.NoError(t, err)
408+
require.NotNil(t, private3)
409+
require.Equal(t, metal.NoneNATType, *private3.NATType)
410+
require.Equal(t, metal.ChildNetworkType, *private3.NetworkType)
411+
412+
private4, err := rs.FindNetworkByID("private-network-4")
413+
require.NoError(t, err)
414+
require.NotNil(t, private4)
415+
require.Equal(t, metal.NoneNATType, *private4.NATType)
416+
require.Equal(t, metal.ChildNetworkType, *private4.NetworkType)
417+
418+
partitionStorage, err := rs.FindNetworkByID("partition-storage")
419+
require.NoError(t, err)
420+
require.NotNil(t, partitionStorage)
421+
require.Equal(t, metal.NoneNATType, *partitionStorage.NATType)
422+
require.Equal(t, metal.ChildSharedNetworkType, *partitionStorage.NetworkType)
423+
}

cmd/metal-api/internal/metal/network.go

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -283,29 +283,48 @@ func (p *Prefix) equals(other *Prefix) bool {
283283

284284
// Network is a network in a metal as a service infrastructure.
285285
// TODO specify rethinkdb restrictions.
286-
type Network struct {
287-
Base
288-
Prefixes Prefixes `rethinkdb:"prefixes" json:"prefixes"`
289-
DestinationPrefixes Prefixes `rethinkdb:"destinationprefixes" json:"destinationprefixes"`
290-
DefaultChildPrefixLength ChildPrefixLength `rethinkdb:"defaultchildprefixlength" json:"defaultchildprefixlength" description:"if privatesuper, this defines the bitlen of child prefixes per addressfamily if not nil"`
291-
PartitionID string `rethinkdb:"partitionid" json:"partitionid"`
292-
ProjectID string `rethinkdb:"projectid" json:"projectid"`
293-
ParentNetworkID string `rethinkdb:"parentnetworkid" json:"parentnetworkid"`
294-
Vrf uint `rethinkdb:"vrf" json:"vrf"`
295-
PrivateSuper bool `rethinkdb:"privatesuper" json:"privatesuper"`
296-
Nat bool `rethinkdb:"nat" json:"nat"`
297-
Underlay bool `rethinkdb:"underlay" json:"underlay"`
298-
Shared bool `rethinkdb:"shared" json:"shared"`
299-
Labels map[string]string `rethinkdb:"labels" json:"labels"`
300-
// AddressFamilies AddressFamilies `rethinkdb:"addressfamilies" json:"addressfamilies"`
301-
AdditionalAnnouncableCIDRs []string `rethinkdb:"additionalannouncablecidrs" json:"additionalannouncablecidrs" description:"list of cidrs which are added to the route maps per tenant private network, these are typically pod- and service cidrs, can only be set in a supernetwork"`
302-
}
286+
type (
287+
Network struct {
288+
Base
289+
Prefixes Prefixes `rethinkdb:"prefixes" json:"prefixes"`
290+
DestinationPrefixes Prefixes `rethinkdb:"destinationprefixes" json:"destinationprefixes"`
291+
DefaultChildPrefixLength ChildPrefixLength `rethinkdb:"defaultchildprefixlength" json:"defaultchildprefixlength" description:"if privatesuper, this defines the bitlen of child prefixes per addressfamily if not nil"`
292+
PartitionID string `rethinkdb:"partitionid" json:"partitionid"`
293+
ProjectID string `rethinkdb:"projectid" json:"projectid"`
294+
ParentNetworkID string `rethinkdb:"parentnetworkid" json:"parentnetworkid"`
295+
Vrf uint `rethinkdb:"vrf" json:"vrf"`
296+
Labels map[string]string `rethinkdb:"labels" json:"labels"`
297+
AdditionalAnnouncableCIDRs []string `rethinkdb:"additionalannouncablecidrs" json:"additionalannouncablecidrs" description:"list of cidrs which are added to the route maps per tenant private network, these are typically pod- and service cidrs, can only be set in a supernetwork"`
298+
NetworkType *NetworkTypeV2 `rethinkdb:"networktype"`
299+
NATType *NATType `rethinkdb:"nattype"`
300+
301+
// PrivateSuper if set identifies this Network as a Super Network for private networks
302+
//
303+
// Deprecated: use SuperNetworkType instead
304+
PrivateSuper bool `rethinkdb:"privatesuper"`
305+
// Underlay if set indicates as a underlay network for firewalls and switches
306+
//
307+
// Deprecated: use UnderlayNetworkType instead
308+
Underlay bool `rethinkdb:"underlay"`
309+
// Shared if set indicates that this network can be used from other projects to acquire ips from
310+
//
311+
// Deprecated: use ChildSharedNetworkType instead
312+
Shared bool `rethinkdb:"shared"`
313+
// Nat if set, traffic entering this network is masqueraded behind the interface entering this network
314+
//
315+
// Deprecated: use IPv4MasqueradeNATType instead
316+
Nat bool `rethinkdb:"nat"`
317+
}
303318

304-
type ChildPrefixLength map[AddressFamily]uint8
319+
ChildPrefixLength map[AddressFamily]uint8
305320

306-
// AddressFamily identifies IPv4/IPv6
307-
type AddressFamily string
308-
type AddressFamilies []AddressFamily
321+
// AddressFamily identifies IPv4/IPv6
322+
AddressFamily string
323+
AddressFamilies []AddressFamily
324+
325+
NATType string
326+
NetworkTypeV2 string
327+
)
309328

310329
const (
311330
// InvalidAddressFamily identifies a invalid Addressfamily
@@ -314,6 +333,28 @@ const (
314333
IPv4AddressFamily = AddressFamily("IPv4")
315334
// IPv6AddressFamily identifies IPv6
316335
IPv6AddressFamily = AddressFamily("IPv6")
336+
337+
// NetworkType
338+
// ExternalNetworkType identifies a network where ips can be allocated from different projects
339+
ExternalNetworkType = NetworkTypeV2("external")
340+
// UnderlayNetworkType identifies a underlay network
341+
UnderlayNetworkType = NetworkTypeV2("underlay")
342+
343+
// SuperNetworkType identifies a super network where child networks can be allocated from
344+
SuperNetworkType = NetworkTypeV2("super")
345+
// SuperNamespacedNetworkType identifies a super network where child networks can be allocated from, namespaced per project
346+
SuperNamespacedNetworkType = NetworkTypeV2("super-namespaced")
347+
// ChildNetworkType identifies a child network which is only used in one project for machines and firewalls without external connectivity
348+
ChildNetworkType = NetworkTypeV2("child")
349+
// ChildSharedNetworkType identifies a child network which can be shared, e.g. ips allocated from different projects
350+
ChildSharedNetworkType = NetworkTypeV2("child-shared")
351+
352+
// NATType
353+
InvalidNATType = NATType("invalid")
354+
// NoneNATType no nat in place when traffic leaves this network
355+
NoneNATType = NATType("none")
356+
// IPv4MasqueradeNATType masquerade ipv4 behind gateway ip
357+
IPv4MasqueradeNATType = NATType("ipv4-masq")
317358
)
318359

319360
// ToAddressFamily will convert a string af to a AddressFamily

cmd/metal-api/internal/service/network-service.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,19 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
412412
}
413413
}
414414

415+
nwType := metal.ExternalNetworkType
416+
if privateSuper {
417+
nwType = metal.SuperNetworkType
418+
}
419+
if underlay {
420+
nwType = metal.UnderlayNetworkType
421+
}
422+
423+
natType := metal.NoneNATType
424+
if nat {
425+
natType = metal.IPv4MasqueradeNATType
426+
}
427+
415428
nw := &metal.Network{
416429
Base: metal.Base{
417430
ID: id,
@@ -429,6 +442,8 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
429442
Vrf: vrf,
430443
Labels: labels,
431444
AdditionalAnnouncableCIDRs: requestPayload.AdditionalAnnouncableCIDRs,
445+
NetworkType: &nwType,
446+
NATType: &natType,
432447
}
433448

434449
ctx := request.Request.Context()
@@ -686,6 +701,16 @@ func (r *networkResource) createChildNetwork(ctx context.Context, nwSpec *metal.
686701
childPrefixes = append(childPrefixes, *childPrefix)
687702
}
688703

704+
nwType := metal.ChildNetworkType
705+
if nwSpec.Shared {
706+
nwType = metal.ChildSharedNetworkType
707+
}
708+
709+
natType := metal.NoneNATType
710+
if nwSpec.Nat {
711+
natType = metal.IPv4MasqueradeNATType
712+
}
713+
689714
nw := &metal.Network{
690715
Base: metal.Base{
691716
Name: nwSpec.Name,
@@ -702,6 +727,8 @@ func (r *networkResource) createChildNetwork(ctx context.Context, nwSpec *metal.
702727
Vrf: *vrf,
703728
ParentNetworkID: parent.ID,
704729
Labels: nwSpec.Labels,
730+
NetworkType: &nwType,
731+
NATType: &natType,
705732
}
706733

707734
err = r.ds.CreateNetwork(nw)

0 commit comments

Comments
 (0)