Skip to content

Commit 95155ca

Browse files
authored
Support NetworkContainers of Static or Dynamic types (#1325)
* add overlay static nnc to nc listener Signed-off-by: Evan Baker <[email protected]> * inline err check Signed-off-by: Evan Baker <[email protected]> * pass NodeNetworkConfigs to Update method by value Signed-off-by: Evan Baker <[email protected]> * rework nnc reconcile flow for NetworkContainer modes Signed-off-by: Evan Baker <[email protected]>
1 parent 3b876d4 commit 95155ca

File tree

9 files changed

+321
-195
lines changed

9 files changed

+321
-195
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,11 +500,11 @@ COVER_PKG ?= .
500500
test-all: ## run all unit tests.
501501
@$(eval COVER_FILTER=`go list --tags ignore_uncovered,ignore_autogenerated $(COVER_PKG)/... | tr '\n' ','`)
502502
@echo Test coverpkg: $(COVER_FILTER)
503-
go test -buildvcs=false -tags "unit" -coverpkg=$(COVER_FILTER) -v -race -covermode atomic -failfast -coverprofile=coverage.out $(COVER_PKG)/...
503+
go test -buildvcs=false -tags "unit" -coverpkg=$(COVER_FILTER) -race -covermode atomic -failfast -coverprofile=coverage.out $(COVER_PKG)/...
504504

505505

506506
test-integration: ## run all integration tests.
507-
go test -buildvcs=false -timeout 1h -coverpkg=./... -v -race -covermode atomic -coverprofile=coverage.out -tags=integration ./test/integration...
507+
go test -buildvcs=false -timeout 1h -coverpkg=./... -race -covermode atomic -coverprofile=coverage.out -tags=integration ./test/integration...
508508

509509
test-cyclonus: ## run the cyclonus test for npm.
510510
cd test/cyclonus && bash ./test-cyclonus.sh

cns/client/client_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func addTestStateToRestServer(t *testing.T, secondaryIps []string) {
9797
t.Fatalf("Failed to createNetworkContainerRequest, req: %+v, err: %d", req, returnCode)
9898
}
9999

100-
svc.IPAMPoolMonitor.Update(&v1alpha.NodeNetworkConfig{
100+
_ = svc.IPAMPoolMonitor.Update(&v1alpha.NodeNetworkConfig{
101101
Spec: v1alpha.NodeNetworkConfigSpec{
102102
RequestedIPCount: 16,
103103
IPsNotInUse: []string{"abc"},

cns/fakes/requestcontrollerfake.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (rc *RequestControllerFake) Reconcile(removePendingReleaseIPs bool) error {
115115
}
116116

117117
// update
118-
rc.cnscli.PoolMonitor.Update(rc.NNC)
118+
_ = rc.cnscli.PoolMonitor.Update(rc.NNC)
119119
return nil
120120
}
121121

cns/restserver/internalapi_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ func createAndValidateNCRequest(t *testing.T, secondaryIPConfigs map[string]cns.
425425
if returnCode != 0 {
426426
t.Fatalf("Failed to createNetworkContainerRequest, req: %+v, err: %d", req, returnCode)
427427
}
428-
svc.IPAMPoolMonitor.Update(&v1alpha.NodeNetworkConfig{
428+
_ = svc.IPAMPoolMonitor.Update(&v1alpha.NodeNetworkConfig{
429429
Status: v1alpha.NodeNetworkConfigStatus{
430430
Scaler: v1alpha.Scaler{
431431
BatchSize: batchSize,
@@ -598,7 +598,7 @@ func createNCReqInternal(t *testing.T, secondaryIPConfigs map[string]cns.Seconda
598598
if returnCode != 0 {
599599
t.Fatalf("Failed to createNetworkContainerRequest, req: %+v, err: %d", req, returnCode)
600600
}
601-
svc.IPAMPoolMonitor.Update(&v1alpha.NodeNetworkConfig{
601+
_ = svc.IPAMPoolMonitor.Update(&v1alpha.NodeNetworkConfig{
602602
Status: v1alpha.NodeNetworkConfigStatus{
603603
Scaler: v1alpha.Scaler{
604604
BatchSize: batchSize,

cns/service/main.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -865,19 +865,23 @@ func reconcileInitialCNSState(ctx context.Context, cli nodeNetworkConfigGetter,
865865
}
866866

867867
// Convert to CreateNetworkContainerRequest
868-
ncRequest, err := kubecontroller.CRDStatusToNCRequest(&nnc.Status)
869-
if err != nil {
870-
return errors.Wrap(err, "failed to convert NNC status to network container request")
871-
}
872-
// rebuild CNS state
873-
podInfoByIP, err := podInfoByIPProvider.PodInfoByIP()
874-
if err != nil {
875-
return errors.Wrap(err, "provider failed to provide PodInfoByIP")
876-
}
868+
for i := range nnc.Status.NetworkContainers {
869+
ncRequest, err := kubecontroller.CreateNCRequestFromDynamicNC(nnc.Status.NetworkContainers[i])
870+
if err != nil {
871+
return errors.Wrap(err, "failed to convert NNC status to network container request")
872+
}
873+
// rebuild CNS state
874+
podInfoByIP, err := podInfoByIPProvider.PodInfoByIP()
875+
if err != nil {
876+
return errors.Wrap(err, "provider failed to provide PodInfoByIP")
877+
}
877878

878-
// Call cnsclient init cns passing those two things.
879-
err = restserver.ResponseCodeToError(ncReconciler.ReconcileNCState(&ncRequest, podInfoByIP, nnc))
880-
return errors.Wrap(err, "failed to reconcile NC state")
879+
// Call cnsclient init cns passing those two things.
880+
if err := restserver.ResponseCodeToError(ncReconciler.ReconcileNCState(ncRequest, podInfoByIP, nnc)); err != nil {
881+
return errors.Wrap(err, "failed to reconcile NC state")
882+
}
883+
}
884+
return nil
881885
}
882886

883887
// InitializeCRDState builds and starts the CRD controllers.
@@ -1010,7 +1014,7 @@ func InitializeCRDState(ctx context.Context, httpRestService cns.HTTPService, cn
10101014
return errors.Wrapf(err, "failed to get node %s", nodeName)
10111015
}
10121016

1013-
reconciler := kubecontroller.NewReconciler(nnccli, kubecontroller.SwiftNodeNetworkConfigListener(httpRestServiceImplementation), poolMonitor)
1017+
reconciler := kubecontroller.NewReconciler(httpRestServiceImplementation, nnccli, poolMonitor)
10141018
// pass Node to the Reconciler for Controller xref
10151019
if err := reconciler.SetupWithManager(manager, node); err != nil {
10161020
return errors.Wrapf(err, "failed to setup reconciler with manager")

cns/singletenantcontroller/conversion.go

Lines changed: 50 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ import (
77
"strings"
88

99
"github.com/Azure/azure-container-networking/cns"
10-
"github.com/Azure/azure-container-networking/cns/logger"
11-
"github.com/Azure/azure-container-networking/cns/restserver"
12-
cnstypes "github.com/Azure/azure-container-networking/cns/types"
1310
"github.com/Azure/azure-container-networking/crd/nodenetworkconfig/api/v1alpha"
1411
"github.com/pkg/errors"
1512
)
@@ -23,56 +20,9 @@ var (
2320
ErrUnsupportedNCQuantity = errors.New("unsupported number of network containers")
2421
)
2522

26-
type cnsClient interface {
27-
CreateOrUpdateNetworkContainerInternal(*cns.CreateNetworkContainerRequest) cnstypes.ResponseCode
28-
}
29-
30-
var _ nodeNetworkConfigListener = (NodeNetworkConfigListenerFunc)(nil) //nolint:gocritic // clarity
31-
32-
type NodeNetworkConfigListenerFunc func(*v1alpha.NodeNetworkConfig) error
33-
34-
func (f NodeNetworkConfigListenerFunc) Update(nnc *v1alpha.NodeNetworkConfig) error {
35-
return f(nnc)
36-
}
37-
38-
// SwiftNodeNetworkConfigListener return a function which satisfies the NodeNetworkConfigListener
39-
// interface. It accepts a CreateOrUpdateNetworkContainerInternal implementation, and when Update
40-
// is called, transforms the NNC in to an NC Request and calls the CNS Service implementation with
41-
// that request.
42-
func SwiftNodeNetworkConfigListener(cnscli cnsClient) NodeNetworkConfigListenerFunc {
43-
return func(nnc *v1alpha.NodeNetworkConfig) error {
44-
// Create NC request and hand it off to CNS
45-
ncRequest, err := CRDStatusToNCRequest(&nnc.Status)
46-
if err != nil {
47-
return errors.Wrap(err, "failed to convert NNC status to network container request")
48-
}
49-
responseCode := cnscli.CreateOrUpdateNetworkContainerInternal(&ncRequest)
50-
err = restserver.ResponseCodeToError(responseCode)
51-
if err != nil {
52-
logger.Errorf("[cns-rc] Error creating or updating NC in reconcile: %v", err)
53-
return errors.Wrap(err, "failed to create or update network container")
54-
}
55-
56-
// record assigned IPs metric
57-
allocatedIPs.Set(float64(len(nnc.Status.NetworkContainers[0].IPAssignments)))
58-
return nil
59-
}
60-
}
61-
62-
// CRDStatusToNCRequest translates a crd status to createnetworkcontainer request
63-
func CRDStatusToNCRequest(status *v1alpha.NodeNetworkConfigStatus) (cns.CreateNetworkContainerRequest, error) {
64-
// if NNC has no NC, return an empty request
65-
if len(status.NetworkContainers) == 0 {
66-
return cns.CreateNetworkContainerRequest{}, nil
67-
}
68-
69-
// only support a single NC per node, error on more
70-
if len(status.NetworkContainers) > 1 {
71-
return cns.CreateNetworkContainerRequest{}, errors.Wrapf(ErrUnsupportedNCQuantity, "count: %d", len(status.NetworkContainers))
72-
}
73-
74-
nc := status.NetworkContainers[0]
75-
23+
// CreateNCRequestFromDynamicNC generates a CreateNetworkContainerRequest from a dynamic NetworkContainer.
24+
//nolint:gocritic //ignore hugeparam
25+
func CreateNCRequestFromDynamicNC(nc v1alpha.NetworkContainer) (*cns.CreateNetworkContainerRequest, error) {
7626
primaryIP := nc.PrimaryIP
7727
// if the PrimaryIP is not a CIDR, append a /32
7828
if !strings.Contains(primaryIP, "/") {
@@ -81,35 +31,75 @@ func CRDStatusToNCRequest(status *v1alpha.NodeNetworkConfigStatus) (cns.CreateNe
8131

8232
primaryPrefix, err := netip.ParsePrefix(primaryIP)
8333
if err != nil {
84-
return cns.CreateNetworkContainerRequest{}, errors.Wrapf(err, "IP: %s", primaryIP)
34+
return nil, errors.Wrapf(err, "IP: %s", primaryIP)
8535
}
8636

87-
secondaryPrefix, err := netip.ParsePrefix(nc.SubnetAddressSpace)
37+
subnetPrefix, err := netip.ParsePrefix(nc.SubnetAddressSpace)
8838
if err != nil {
89-
return cns.CreateNetworkContainerRequest{}, errors.Wrapf(err, "invalid SubnetAddressSpace %s", nc.SubnetAddressSpace)
39+
return nil, errors.Wrapf(err, "invalid SubnetAddressSpace %s", nc.SubnetAddressSpace)
9040
}
9141

9242
subnet := cns.IPSubnet{
9343
IPAddress: primaryPrefix.Addr().String(),
94-
PrefixLength: uint8(secondaryPrefix.Bits()),
44+
PrefixLength: uint8(subnetPrefix.Bits()),
9545
}
9646

9747
secondaryIPConfigs := map[string]cns.SecondaryIPConfig{}
9848
for _, ipAssignment := range nc.IPAssignments {
9949
secondaryIP := net.ParseIP(ipAssignment.IP)
10050
if secondaryIP == nil {
101-
return cns.CreateNetworkContainerRequest{}, errors.Wrapf(ErrInvalidSecondaryIP, "IP: %s", ipAssignment.IP)
51+
return nil, errors.Wrapf(ErrInvalidSecondaryIP, "IP: %s", ipAssignment.IP)
10252
}
10353
secondaryIPConfigs[ipAssignment.Name] = cns.SecondaryIPConfig{
10454
IPAddress: secondaryIP.String(),
10555
NCVersion: int(nc.Version),
10656
}
10757
}
108-
return cns.CreateNetworkContainerRequest{
58+
return &cns.CreateNetworkContainerRequest{
59+
SecondaryIPConfigs: secondaryIPConfigs,
60+
NetworkContainerid: nc.ID,
61+
NetworkContainerType: cns.Docker,
62+
Version: strconv.FormatInt(nc.Version, 10), //nolint:gomnd // it's decimal
63+
IPConfiguration: cns.IPConfiguration{
64+
IPSubnet: subnet,
65+
GatewayIPAddress: nc.DefaultGateway,
66+
},
67+
}, nil
68+
}
69+
70+
// CreateNCRequestFromStaticNC generates a CreateNetworkContainerRequest from a static NetworkContainer.
71+
//nolint:gocritic //ignore hugeparam
72+
func CreateNCRequestFromStaticNC(nc v1alpha.NetworkContainer) (*cns.CreateNetworkContainerRequest, error) {
73+
primaryPrefix, err := netip.ParsePrefix(nc.PrimaryIP)
74+
if err != nil {
75+
return nil, errors.Wrapf(err, "IP: %s", nc.PrimaryIP)
76+
}
77+
78+
subnetPrefix, err := netip.ParsePrefix(nc.SubnetAddressSpace)
79+
if err != nil {
80+
return nil, errors.Wrapf(err, "invalid SubnetAddressSpace %s", nc.SubnetAddressSpace)
81+
}
82+
subnet := cns.IPSubnet{
83+
IPAddress: primaryPrefix.Addr().String(),
84+
PrefixLength: uint8(subnetPrefix.Bits()),
85+
}
86+
87+
secondaryIPConfigs := map[string]cns.SecondaryIPConfig{}
88+
89+
// iterate through all IP addresses in the subnet described by primaryPrefix and
90+
// add them to the request as secondary IPConfigs.
91+
zeroAddr := primaryPrefix.Masked().Addr() // the masked address is the 0th IP in the subnet
92+
for addr := zeroAddr.Next(); primaryPrefix.Contains(addr); addr = addr.Next() {
93+
secondaryIPConfigs[addr.String()] = cns.SecondaryIPConfig{
94+
IPAddress: addr.String(),
95+
NCVersion: int(nc.Version),
96+
}
97+
}
98+
return &cns.CreateNetworkContainerRequest{
10999
SecondaryIPConfigs: secondaryIPConfigs,
110100
NetworkContainerid: nc.ID,
111101
NetworkContainerType: cns.Docker,
112-
Version: strconv.FormatInt(nc.Version, 10),
102+
Version: strconv.FormatInt(nc.Version, 10), //nolint:gomnd // it's decimal
113103
IPConfiguration: cns.IPConfiguration{
114104
IPSubnet: subnet,
115105
GatewayIPAddress: nc.DefaultGateway,

0 commit comments

Comments
 (0)