Skip to content

Commit 22d916e

Browse files
committed
More tests
1 parent 5ea7ea6 commit 22d916e

File tree

3 files changed

+157
-105
lines changed

3 files changed

+157
-105
lines changed

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

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
v1 "github.com/metal-stack/metal-api/cmd/metal-api/internal/service/v1"
2121
"github.com/metal-stack/metal-lib/auditing"
2222
"github.com/metal-stack/metal-lib/httperrors"
23+
"github.com/metal-stack/metal-lib/pkg/pointer"
2324
)
2425

2526
type networkResource struct {
@@ -263,6 +264,7 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
263264

264265
prefixes := metal.Prefixes{}
265266
addressFamilies := make(map[string]bool)
267+
var addressFamily v1.AddressFamily
266268
// all Prefixes must be valid and from the same addressfamily
267269
for i := range requestPayload.Prefixes {
268270
p := requestPayload.Prefixes[i]
@@ -278,9 +280,11 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
278280
}
279281
if ipprefix.Addr().Is4() {
280282
addressFamilies["ipv4"] = true
283+
addressFamily = v1.IPv4AddressFamily
281284
}
282285
if ipprefix.Addr().Is6() {
283286
addressFamilies["ipv6"] = true
287+
addressFamily = v1.IPv6AddressFamily
284288
}
285289
prefixes = append(prefixes, *prefix)
286290
}
@@ -290,6 +294,17 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
290294
return
291295
}
292296

297+
if privateSuper && requestPayload.ChildPrefixLength == nil {
298+
var childprefixlength *uint8
299+
if addressFamily == v1.IPv4AddressFamily {
300+
childprefixlength = pointer.Pointer(uint8(22))
301+
}
302+
if addressFamily == v1.IPv6AddressFamily {
303+
childprefixlength = pointer.Pointer(uint8(64))
304+
}
305+
r.log.Info("createnetwork childprefixlength not set for private super network, using default", "addressfamily", addressFamily, "childprefixlength", childprefixlength)
306+
}
307+
293308
destPrefixes := metal.Prefixes{}
294309
for i := range requestPayload.DestinationPrefixes {
295310
p := requestPayload.DestinationPrefixes[i]
@@ -474,6 +489,7 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
474489
return
475490
}
476491

492+
r.log.Info("allocateNetwork", "request", requestPayload)
477493
var name string
478494
if requestPayload.Name != nil {
479495
name = *requestPayload.Name
@@ -520,14 +536,6 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
520536
return
521537
}
522538

523-
var superNetworks metal.Networks
524-
boolTrue := true
525-
err = r.ds.SearchNetworks(&datastore.NetworkSearchQuery{PartitionID: &partition.ID, PrivateSuper: &boolTrue}, &superNetworks)
526-
if err != nil {
527-
r.sendError(request, response, defaultError(err))
528-
return
529-
}
530-
531539
destPrefixes := metal.Prefixes{}
532540
for _, p := range requestPayload.DestinationPrefixes {
533541
prefix, err := metal.NewPrefixFromCIDR(p)
@@ -547,8 +555,16 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
547555
r.log.Info("network allocate", "family", addressFamily)
548556
var (
549557
superNetwork metal.Network
558+
superNetworks metal.Networks
550559
superNetworkFound bool
551560
)
561+
562+
err = r.ds.SearchNetworks(&datastore.NetworkSearchQuery{PartitionID: &partition.ID, PrivateSuper: pointer.Pointer(true)}, &superNetworks)
563+
if err != nil {
564+
r.sendError(request, response, defaultError(err))
565+
return
566+
}
567+
552568
for _, snw := range superNetworks {
553569
ipprefix, err := netip.ParsePrefix(snw.Prefixes[0].String())
554570
if err != nil {
@@ -592,6 +608,7 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
592608
if requestPayload.Length != nil {
593609
length = *requestPayload.Length
594610
}
611+
r.log.Info("network allocate", "supernetwork", superNetwork.Name, "length", length)
595612

596613
ctx := request.Request.Context()
597614
nw, err := createChildNetwork(ctx, r.ds, r.ipamer, nwSpec, &superNetwork, length)

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

Lines changed: 130 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import (
77
"log/slog"
88
"net/http"
99
"net/http/httptest"
10+
"net/netip"
1011
"testing"
1112

13+
mdmv1 "github.com/metal-stack/masterdata-api/api/v1"
14+
mdmv1mock "github.com/metal-stack/masterdata-api/api/v1/mocks"
15+
mdm "github.com/metal-stack/masterdata-api/pkg/client"
1216
"github.com/metal-stack/metal-lib/httperrors"
1317
"github.com/metal-stack/metal-lib/pkg/pointer"
1418
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
@@ -21,6 +25,7 @@ import (
2125

2226
restful "github.com/emicklei/go-restful/v3"
2327
"github.com/metal-stack/metal-api/cmd/metal-api/internal/testdata"
28+
testifymock "github.com/stretchr/testify/mock"
2429
"github.com/stretchr/testify/require"
2530
)
2631

@@ -420,99 +425,128 @@ func Test_networkResource_createNetwork(t *testing.T) {
420425
}
421426
}
422427

423-
// func Test_networkResource_allocateNetwork(t *testing.T) {
424-
// log := slog.Default()
425-
// tests := []struct {
426-
// name string
427-
// networkName string
428-
// partitionID string
429-
// projectID string
430-
// childprefixlength *uint8
431-
// addressFamily *string
432-
// shared bool
433-
// expectedStatus int
434-
// expectedErrorMessage string
435-
// }{
436-
// {
437-
// name: "simple ipv4",
438-
// networkName: "tenantv4",
439-
// partitionID: "1",
440-
// projectID: "project-1",
441-
// expectedStatus: http.StatusCreated,
442-
// },
443-
// {
444-
// name: "ipv6 without ipv6 super",
445-
// networkName: "tenantv6",
446-
// partitionID: "1",
447-
// projectID: "project-1",
448-
// addressFamily: pointer.Pointer("ipv6"),
449-
// expectedStatus: http.StatusUnprocessableEntity,
450-
// expectedErrorMessage: "no supernetwork for addressfamily:IPv6 found",
451-
// },
452-
// }
453-
// for _, tt := range tests {
454-
// ds, mock := datastore.InitMockDB(t)
455-
456-
// ipamer, err := testdata.InitMockIpamData(mock, false)
457-
// require.Nil(t, err)
458-
// mock.On(r.DB("mockdb").Table("network").Filter(r.MockAnything()).Filter(r.MockAnything())).Return(metal.Networks{testdata.Nw1, testdata.Nw2}, nil)
459-
// changes := []r.ChangeResponse{{OldValue: map[string]interface{}{"id": float64(42)}}}
460-
// mock.On(r.DB("mockdb").Table("integerpool").Limit(1).Delete(r.
461-
// DeleteOpts{ReturnChanges: true})).Return(r.WriteResponse{Changes: changes}, nil)
462-
463-
// mock.On(r.DB("mockdb").Table("partition").Get(r.MockAnything())).Return(
464-
// metal.Partition{
465-
// Base: metal.Base{ID: tt.partitionID},
466-
// },
467-
// nil,
468-
// )
469-
// testdata.InitMockDBData(mock)
470-
471-
// psc := mdmock.ProjectServiceClient{}
472-
// psc.On("Get", context.Background(), &mdmv1.ProjectGetRequest{Id: "project-1"}).Return(&mdmv1.ProjectResponse{
473-
// Project: &mdmv1.Project{
474-
// Meta: &mdmv1.Meta{Id: tt.projectID},
475-
// },
476-
// }, nil,
477-
// )
478-
// tsc := mdmock.TenantServiceClient{}
479-
480-
// mdc := mdm.NewMock(&psc, &tsc)
481-
482-
// networkservice := NewNetwork(log, ds, ipamer, mdc)
483-
// container := restful.NewContainer().Add(networkservice)
484-
485-
// allocateRequest := &v1.NetworkAllocateRequest{
486-
// Describable: v1.Describable{Name: &tt.networkName},
487-
// NetworkBase: v1.NetworkBase{PartitionID: &tt.partitionID, ProjectID: &tt.projectID},
488-
// AddressFamily: tt.addressFamily,
489-
// Length: tt.childprefixlength,
490-
// }
491-
492-
// js, _ := json.Marshal(allocateRequest)
493-
// body := bytes.NewBuffer(js)
494-
// req := httptest.NewRequest("POST", "/v1/network/allocate", body)
495-
// req.Header.Add("Content-Type", "application/json")
496-
// container = injectAdmin(log, container, req)
497-
// w := httptest.NewRecorder()
498-
// container.ServeHTTP(w, req)
499-
500-
// resp := w.Result()
501-
// require.Equal(t, tt.expectedStatus, resp.StatusCode, w.Body.String())
502-
// if tt.expectedStatus > 300 {
503-
// var result httperrors.HTTPErrorResponse
504-
// err := json.NewDecoder(resp.Body).Decode(&result)
505-
506-
// require.Nil(t, err)
507-
// require.Equal(t, tt.expectedErrorMessage, result.Message)
508-
// } else {
509-
// var result v1.NetworkResponse
510-
// err = json.NewDecoder(resp.Body).Decode(&result)
511-
// require.Nil(t, err)
512-
// require.Equal(t, tt.networkName, *result.Name)
513-
// require.Equal(t, tt.partitionID, *result.PartitionID)
514-
// require.Equal(t, tt.projectID, *result.ProjectID)
515-
// // TODO check af and length
516-
// }
517-
// }
518-
// }
428+
func Test_networkResource_allocateNetwork(t *testing.T) {
429+
log := slog.Default()
430+
tests := []struct {
431+
name string
432+
networkName string
433+
partitionID string
434+
projectID string
435+
childprefixlength *uint8
436+
addressFamily *string
437+
shared bool
438+
expectedStatus int
439+
expectedErrorMessage string
440+
}{
441+
{
442+
name: "simple ipv4, default childprefixlength",
443+
networkName: "tenantv4",
444+
partitionID: testdata.Partition1.ID,
445+
projectID: "project-1",
446+
expectedStatus: http.StatusCreated,
447+
},
448+
{
449+
name: "simple ipv4, specific childprefixlength",
450+
networkName: "tenantv4.2",
451+
partitionID: testdata.Partition1.ID,
452+
projectID: "project-1",
453+
childprefixlength: pointer.Pointer(uint8(29)),
454+
expectedStatus: http.StatusCreated,
455+
},
456+
{
457+
name: "ipv6 without ipv6 super",
458+
networkName: "tenantv6",
459+
partitionID: testdata.Partition1.ID,
460+
projectID: "project-1",
461+
addressFamily: pointer.Pointer("ipv6"),
462+
expectedStatus: http.StatusBadRequest,
463+
expectedErrorMessage: "no supernetwork for addressfamily:IPv6 found",
464+
},
465+
}
466+
for _, tt := range tests {
467+
ds, mock := datastore.InitMockDB(t)
468+
469+
supernetwork := testdata.Nw1
470+
ipamer, err := testdata.InitMockIpamData(mock, false)
471+
require.NoError(t, err)
472+
mock.On(r.DB("mockdb").Table("network").Filter(r.MockAnything()).Filter(r.MockAnything())).Return(metal.Networks{supernetwork}, nil)
473+
changes := []r.ChangeResponse{{OldValue: map[string]interface{}{"id": float64(42)}}}
474+
mock.On(r.DB("mockdb").Table("integerpool").Limit(1).Delete(r.
475+
DeleteOpts{ReturnChanges: true})).Return(r.WriteResponse{Changes: changes}, nil)
476+
477+
mock.On(r.DB("mockdb").Table("partition").Get(r.MockAnything())).Return(
478+
metal.Partition{
479+
Base: metal.Base{ID: tt.partitionID},
480+
},
481+
nil,
482+
)
483+
testdata.InitMockDBData(mock)
484+
485+
psc := mdmv1mock.ProjectServiceClient{}
486+
psc.On("Get", testifymock.Anything, &mdmv1.ProjectGetRequest{Id: "project-1"}).Return(&mdmv1.ProjectResponse{
487+
Project: &mdmv1.Project{
488+
Meta: &mdmv1.Meta{Id: tt.projectID},
489+
},
490+
}, nil,
491+
)
492+
tsc := mdmv1mock.TenantServiceClient{}
493+
494+
mdc := mdm.NewMock(&psc, &tsc, nil, nil)
495+
496+
networkservice := NewNetwork(log, ds, ipamer, mdc)
497+
container := restful.NewContainer().Add(networkservice)
498+
499+
allocateRequest := &v1.NetworkAllocateRequest{
500+
Describable: v1.Describable{Name: &tt.networkName},
501+
NetworkBase: v1.NetworkBase{PartitionID: &tt.partitionID, ProjectID: &tt.projectID},
502+
AddressFamily: tt.addressFamily,
503+
Length: tt.childprefixlength,
504+
}
505+
506+
js, err := json.Marshal(allocateRequest)
507+
require.NoError(t, err)
508+
509+
body := bytes.NewBuffer(js)
510+
req := httptest.NewRequest("POST", "/v1/network/allocate", body)
511+
req.Header.Add("Content-Type", "application/json")
512+
container = injectAdmin(log, container, req)
513+
w := httptest.NewRecorder()
514+
container.ServeHTTP(w, req)
515+
516+
resp := w.Result()
517+
defer resp.Body.Close()
518+
require.Equal(t, tt.expectedStatus, resp.StatusCode, w.Body.String())
519+
if tt.expectedStatus > 300 {
520+
var result httperrors.HTTPErrorResponse
521+
err := json.NewDecoder(resp.Body).Decode(&result)
522+
523+
require.NoError(t, err)
524+
require.Equal(t, tt.expectedErrorMessage, result.Message)
525+
} else {
526+
var result v1.NetworkResponse
527+
err = json.NewDecoder(resp.Body).Decode(&result)
528+
529+
requestAF := "ipv4"
530+
if tt.addressFamily != nil {
531+
requestAF = "ipv6"
532+
}
533+
534+
require.GreaterOrEqual(t, len(result.Prefixes), 1)
535+
resultFirstPrefix := netip.MustParsePrefix(result.Prefixes[0])
536+
af := "ipv4"
537+
if resultFirstPrefix.Addr().Is6() {
538+
af = "ipv6"
539+
}
540+
expectedLength := *supernetwork.ChildPrefixLength
541+
if tt.childprefixlength != nil {
542+
expectedLength = *tt.childprefixlength
543+
}
544+
require.NoError(t, err)
545+
require.Equal(t, tt.networkName, *result.Name)
546+
require.Equal(t, tt.partitionID, *result.PartitionID)
547+
require.Equal(t, tt.projectID, *result.ProjectID)
548+
require.Equal(t, requestAF, af)
549+
require.Equal(t, int(expectedLength), resultFirstPrefix.Bits())
550+
}
551+
}
552+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ var (
280280
}
281281
// Networks
282282
prefix1 = metal.Prefix{IP: "185.1.2.0", Length: "26"}
283-
prefix2 = metal.Prefix{IP: "100.64.2.0", Length: "16"}
283+
prefix2 = metal.Prefix{IP: "100.64.0.0", Length: "16"}
284284
prefix3 = metal.Prefix{IP: "192.0.0.0", Length: "16"}
285285
prefixIPAM = metal.Prefix{IP: "10.0.0.0", Length: "16"}
286286
cpl1 = uint8(28)
@@ -308,6 +308,7 @@ var (
308308
Name: "Network 2",
309309
Description: "description 2",
310310
},
311+
PartitionID: Partition1.ID,
311312
Prefixes: prefixes2,
312313
Underlay: true,
313314
ChildPrefixLength: &cpl2,

0 commit comments

Comments
 (0)