Skip to content

Commit

Permalink
More tests
Browse files Browse the repository at this point in the history
  • Loading branch information
majst01 committed Jul 4, 2024
1 parent 5ea7ea6 commit 22d916e
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 105 deletions.
33 changes: 25 additions & 8 deletions cmd/metal-api/internal/service/network-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
v1 "github.com/metal-stack/metal-api/cmd/metal-api/internal/service/v1"
"github.com/metal-stack/metal-lib/auditing"
"github.com/metal-stack/metal-lib/httperrors"
"github.com/metal-stack/metal-lib/pkg/pointer"
)

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

prefixes := metal.Prefixes{}
addressFamilies := make(map[string]bool)
var addressFamily v1.AddressFamily
// all Prefixes must be valid and from the same addressfamily
for i := range requestPayload.Prefixes {
p := requestPayload.Prefixes[i]
Expand All @@ -278,9 +280,11 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
}
if ipprefix.Addr().Is4() {
addressFamilies["ipv4"] = true
addressFamily = v1.IPv4AddressFamily
}
if ipprefix.Addr().Is6() {
addressFamilies["ipv6"] = true
addressFamily = v1.IPv6AddressFamily
}
prefixes = append(prefixes, *prefix)
}
Expand All @@ -290,6 +294,17 @@ func (r *networkResource) createNetwork(request *restful.Request, response *rest
return
}

if privateSuper && requestPayload.ChildPrefixLength == nil {
var childprefixlength *uint8
if addressFamily == v1.IPv4AddressFamily {
childprefixlength = pointer.Pointer(uint8(22))
}
if addressFamily == v1.IPv6AddressFamily {
childprefixlength = pointer.Pointer(uint8(64))
}
r.log.Info("createnetwork childprefixlength not set for private super network, using default", "addressfamily", addressFamily, "childprefixlength", childprefixlength)
}

destPrefixes := metal.Prefixes{}
for i := range requestPayload.DestinationPrefixes {
p := requestPayload.DestinationPrefixes[i]
Expand Down Expand Up @@ -474,6 +489,7 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
return
}

r.log.Info("allocateNetwork", "request", requestPayload)
var name string
if requestPayload.Name != nil {
name = *requestPayload.Name
Expand Down Expand Up @@ -520,14 +536,6 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
return
}

var superNetworks metal.Networks
boolTrue := true
err = r.ds.SearchNetworks(&datastore.NetworkSearchQuery{PartitionID: &partition.ID, PrivateSuper: &boolTrue}, &superNetworks)
if err != nil {
r.sendError(request, response, defaultError(err))
return
}

destPrefixes := metal.Prefixes{}
for _, p := range requestPayload.DestinationPrefixes {
prefix, err := metal.NewPrefixFromCIDR(p)
Expand All @@ -547,8 +555,16 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
r.log.Info("network allocate", "family", addressFamily)
var (
superNetwork metal.Network
superNetworks metal.Networks
superNetworkFound bool
)

err = r.ds.SearchNetworks(&datastore.NetworkSearchQuery{PartitionID: &partition.ID, PrivateSuper: pointer.Pointer(true)}, &superNetworks)
if err != nil {
r.sendError(request, response, defaultError(err))
return
}

for _, snw := range superNetworks {
ipprefix, err := netip.ParsePrefix(snw.Prefixes[0].String())
if err != nil {
Expand Down Expand Up @@ -592,6 +608,7 @@ func (r *networkResource) allocateNetwork(request *restful.Request, response *re
if requestPayload.Length != nil {
length = *requestPayload.Length
}
r.log.Info("network allocate", "supernetwork", superNetwork.Name, "length", length)

ctx := request.Request.Context()
nw, err := createChildNetwork(ctx, r.ds, r.ipamer, nwSpec, &superNetwork, length)
Expand Down
226 changes: 130 additions & 96 deletions cmd/metal-api/internal/service/network-service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import (
"log/slog"
"net/http"
"net/http/httptest"
"net/netip"
"testing"

mdmv1 "github.com/metal-stack/masterdata-api/api/v1"
mdmv1mock "github.com/metal-stack/masterdata-api/api/v1/mocks"
mdm "github.com/metal-stack/masterdata-api/pkg/client"
"github.com/metal-stack/metal-lib/httperrors"
"github.com/metal-stack/metal-lib/pkg/pointer"
r "gopkg.in/rethinkdb/rethinkdb-go.v6"
Expand All @@ -21,6 +25,7 @@ import (

restful "github.com/emicklei/go-restful/v3"
"github.com/metal-stack/metal-api/cmd/metal-api/internal/testdata"
testifymock "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -420,99 +425,128 @@ func Test_networkResource_createNetwork(t *testing.T) {
}
}

// func Test_networkResource_allocateNetwork(t *testing.T) {
// log := slog.Default()
// 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: pointer.Pointer("ipv6"),
// expectedStatus: http.StatusUnprocessableEntity,
// expectedErrorMessage: "no supernetwork for addressfamily:IPv6 found",
// },
// }
// for _, tt := range tests {
// ds, mock := datastore.InitMockDB(t)

// 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(log, 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(log, 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 Test_networkResource_allocateNetwork(t *testing.T) {
log := slog.Default()
tests := []struct {
name string
networkName string
partitionID string
projectID string
childprefixlength *uint8
addressFamily *string
shared bool
expectedStatus int
expectedErrorMessage string
}{
{
name: "simple ipv4, default childprefixlength",
networkName: "tenantv4",
partitionID: testdata.Partition1.ID,
projectID: "project-1",
expectedStatus: http.StatusCreated,
},
{
name: "simple ipv4, specific childprefixlength",
networkName: "tenantv4.2",
partitionID: testdata.Partition1.ID,
projectID: "project-1",
childprefixlength: pointer.Pointer(uint8(29)),
expectedStatus: http.StatusCreated,
},
{
name: "ipv6 without ipv6 super",
networkName: "tenantv6",
partitionID: testdata.Partition1.ID,
projectID: "project-1",
addressFamily: pointer.Pointer("ipv6"),
expectedStatus: http.StatusBadRequest,
expectedErrorMessage: "no supernetwork for addressfamily:IPv6 found",
},
}
for _, tt := range tests {
ds, mock := datastore.InitMockDB(t)

supernetwork := testdata.Nw1
ipamer, err := testdata.InitMockIpamData(mock, false)
require.NoError(t, err)
mock.On(r.DB("mockdb").Table("network").Filter(r.MockAnything()).Filter(r.MockAnything())).Return(metal.Networks{supernetwork}, 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 := mdmv1mock.ProjectServiceClient{}
psc.On("Get", testifymock.Anything, &mdmv1.ProjectGetRequest{Id: "project-1"}).Return(&mdmv1.ProjectResponse{
Project: &mdmv1.Project{
Meta: &mdmv1.Meta{Id: tt.projectID},
},
}, nil,
)
tsc := mdmv1mock.TenantServiceClient{}

mdc := mdm.NewMock(&psc, &tsc, nil, nil)

networkservice := NewNetwork(log, 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, err := json.Marshal(allocateRequest)
require.NoError(t, err)

body := bytes.NewBuffer(js)
req := httptest.NewRequest("POST", "/v1/network/allocate", body)
req.Header.Add("Content-Type", "application/json")
container = injectAdmin(log, container, req)
w := httptest.NewRecorder()
container.ServeHTTP(w, req)

resp := w.Result()
defer resp.Body.Close()
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.NoError(t, err)
require.Equal(t, tt.expectedErrorMessage, result.Message)
} else {
var result v1.NetworkResponse
err = json.NewDecoder(resp.Body).Decode(&result)

requestAF := "ipv4"
if tt.addressFamily != nil {
requestAF = "ipv6"
}

require.GreaterOrEqual(t, len(result.Prefixes), 1)
resultFirstPrefix := netip.MustParsePrefix(result.Prefixes[0])
af := "ipv4"
if resultFirstPrefix.Addr().Is6() {
af = "ipv6"
}
expectedLength := *supernetwork.ChildPrefixLength
if tt.childprefixlength != nil {
expectedLength = *tt.childprefixlength
}
require.NoError(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, requestAF, af)
require.Equal(t, int(expectedLength), resultFirstPrefix.Bits())
}
}
}
3 changes: 2 additions & 1 deletion cmd/metal-api/internal/testdata/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ var (
}
// Networks
prefix1 = metal.Prefix{IP: "185.1.2.0", Length: "26"}
prefix2 = metal.Prefix{IP: "100.64.2.0", Length: "16"}
prefix2 = metal.Prefix{IP: "100.64.0.0", Length: "16"}
prefix3 = metal.Prefix{IP: "192.0.0.0", Length: "16"}
prefixIPAM = metal.Prefix{IP: "10.0.0.0", Length: "16"}
cpl1 = uint8(28)
Expand Down Expand Up @@ -308,6 +308,7 @@ var (
Name: "Network 2",
Description: "description 2",
},
PartitionID: Partition1.ID,
Prefixes: prefixes2,
Underlay: true,
ChildPrefixLength: &cpl2,
Expand Down

0 comments on commit 22d916e

Please sign in to comment.