Skip to content

Commit d34c61f

Browse files
authored
feat: CNS RequestIPAddress branching for MT/V2 (#2300)
* fix overlay IPAM not reporting version * revert file and var naming, add correct path to makefile * proposal design for multitenant IPAM flow * change podipinfo + linter issue * pointer issues for printf * update IPAM branching * remove comments * pod client placeholder * address lint issue for httpservicefake * getting pod info in validator * linter issue * update network container contract * renaming * mtpnc changes * rebase * revert file and var naming, add correct path to makefile * add default route * add unit tests * update unit tests for ipam * go get to fix linter * go mod tidy * update routes * update routes * remove stale comments + remove redundant method * add contexts + change address type * addressed review * embedded client to mock + enum for address type * fix error * change addressType to NICType * change isDefaultRoute to SkipDefaultRoutes * address comments * refractor: make changes according to cni/cns contract * refractor: make adding route its own func + move swift v2 ipam branching to after normal ipam flow * refractor: change vars naming * refractor: more var naming * test: add test for podv6cidr * refractor: make the returning podIpInfo init cleaner in swiftv2.go * refractor + tests: add contexts to ipconfigs req validators + set route tests * refractor: change labels for swift v2 pods * fix: fix swift v2 UT * refractor: add v4/v6 distinction for service cidr * rebase * revert file and var naming, add correct path to makefile * rebase * revert file and var naming, add correct path to makefile * change podipinfo + linter issue * update IPAM branching * pod client placeholder * getting pod info in validator * linter issue * rebase * revert file and var naming, add correct path to makefile * refractor: fix conflicts * refractor: revert podwatcher code changes * docs: change comment * refractor: change CIDR to CDIRs * refractor: parse CIDRs as semicolons separated string from env in SetRoutes * docs: add minor comment * refractor: change separator for parsing CIDRs * feat: add rbac roles * fix: gofumpt * fix: update clusterrole * fix: add namespace to clusterrolebinding * fix: UT * fix: add labels toswift v2 clusterrole * fix: release default ipconfig early if getting swiftv2 ipconfig failed * test: add more UT * fix: parsing MTPNC as CIDR instead * fix: toggle skipDefaultRoutes for infraNic to true * fix: add route for node cidr in ipv4 podipconfig * feat: add node cidrs route * fix: linter * address comments * fix: minor logs formatting * feat: move cns yaml for swiftv2 scenario to a diff file + more logging for swiftv2middleware * fix: log debugf to printf * fix: add testmain to avoid nil pointer error for loggers * Update azure-cns.yaml Signed-off-by: Quang Nguyen <[email protected]> * fix: move parseCIDRs to a common package, use net/netip instead of net * fix: exhaustive all switch case for nic type * fix: exhaustive all switch case for nic type * refractor: change fmt.Errorf to errors.Wrapf * fix: add mtpnc status check in validator + use netip package * fix: minor * revert: azure-cns.yaml --------- Signed-off-by: Quang Nguyen <[email protected]> Signed-off-by: Quang Nguyen <[email protected]>
1 parent 7e5994d commit d34c61f

File tree

21 files changed

+1184
-55
lines changed

21 files changed

+1184
-55
lines changed

cni/plugin.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import (
2424
"go.uber.org/zap"
2525
)
2626

27-
var logger = log.CNILogger.With(zap.String("component", "cni-plugin"))
28-
var storeLogger = log.CNILogger.With(zap.String("component", "cni-store"))
27+
var (
28+
logger = log.CNILogger.With(zap.String("component", "cni-plugin"))
29+
storeLogger = log.CNILogger.With(zap.String("component", "cni-store"))
30+
)
2931

3032
var errEmptyContent = errors.New("read content is zero bytes")
3133

cns/NetworkContainerContract.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ type PodInfo interface {
187187
Equals(PodInfo) bool
188188
// String implements string for logging PodInfos
189189
String() string
190+
// SecondaryInterfacesExist returns true if there exist a secondary interface for this pod
191+
SecondaryInterfacesExist() bool
190192
}
191193

192194
type KubernetesPodInfo struct {
@@ -199,9 +201,10 @@ var _ PodInfo = (*podInfo)(nil)
199201
// podInfo implements PodInfo for multiple schemas of Key
200202
type podInfo struct {
201203
KubernetesPodInfo
202-
PodInfraContainerID string
203-
PodInterfaceID string
204-
Version podInfoScheme
204+
PodInfraContainerID string
205+
PodInterfaceID string
206+
Version podInfoScheme
207+
SecondaryInterfaceSet bool
205208
}
206209

207210
func (p podInfo) String() string {
@@ -255,6 +258,10 @@ func (p *podInfo) OrchestratorContext() (json.RawMessage, error) {
255258
return jsonContext, nil
256259
}
257260

261+
func (p *podInfo) SecondaryInterfacesExist() bool {
262+
return p.SecondaryInterfaceSet
263+
}
264+
258265
// NewPodInfo returns an implementation of PodInfo that returns the passed
259266
// configuration for their namesake functions.
260267
func NewPodInfo(infraContainerID, interfaceID, name, namespace string) PodInfo {
@@ -292,6 +299,7 @@ func NewPodInfoFromIPConfigsRequest(req IPConfigsRequest) (PodInfo, error) {
292299
}
293300
p.(*podInfo).PodInfraContainerID = req.InfraContainerID
294301
p.(*podInfo).PodInterfaceID = req.PodInterfaceID
302+
p.(*podInfo).SecondaryInterfaceSet = req.SecondaryInterfacesExist
295303
return p, nil
296304
}
297305

@@ -453,11 +461,12 @@ type IPConfigRequest struct {
453461

454462
// Same as IPConfigRequest except that DesiredIPAddresses is passed in as a slice
455463
type IPConfigsRequest struct {
456-
DesiredIPAddresses []string `json:"desiredIPAddresses"`
457-
PodInterfaceID string `json:"podInterfaceID"`
458-
InfraContainerID string `json:"infraContainerID"`
459-
OrchestratorContext json.RawMessage `json:"orchestratorContext"`
460-
Ifname string `json:"ifname"` // Used by delegated IPAM
464+
DesiredIPAddresses []string `json:"desiredIPAddresses"`
465+
PodInterfaceID string `json:"podInterfaceID"`
466+
InfraContainerID string `json:"infraContainerID"`
467+
OrchestratorContext json.RawMessage `json:"orchestratorContext"`
468+
Ifname string `json:"ifname"` // Used by delegated IPAM
469+
SecondaryInterfacesExist bool `json:"secondaryInterfacesExist"` // will be set by SWIFT v2 validator func
461470
}
462471

463472
// IPConfigResponse is used in CNS IPAM mode as a response to CNI ADD

cns/api.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,18 @@ type HTTPService interface {
4848
GetPendingReleaseIPConfigs() []IPConfigurationStatus
4949
GetPodIPConfigState() map[string]IPConfigurationStatus
5050
MarkIPAsPendingRelease(numberToMark int) (map[string]IPConfigurationStatus, error)
51+
AttachSWIFTv2Middleware(middleware SWIFTv2Middleware)
5152
}
5253

54+
// Middleware interface for testing later on
55+
type SWIFTv2Middleware interface {
56+
ValidateIPConfigsRequest(context.Context, *IPConfigsRequest) (types.ResponseCode, string)
57+
GetIPConfig(context.Context, PodInfo) (PodIpInfo, error)
58+
SetRoutes(*PodIpInfo) error
59+
}
60+
61+
type IPConfigsRequestValidator func(context.Context, *IPConfigsRequest) (types.ResponseCode, string)
62+
5363
// This is used for KubernetesCRD orchestrator Type where NC has multiple ips.
5464
// This struct captures the state for SecondaryIPs associated to a given NC
5565
type IPConfigurationStatus struct {

cns/azure-cns.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,4 @@ data:
189189
"ManageEndpointState": false,
190190
"ProgramSNATIPTables" : false
191191
}
192-
# Toggle ManageEndpointState and ProgramSNATIPTables to true for delegated IPAM use case.
192+
# Toggle ManageEndpointState and ProgramSNATIPTables to true for delegated IPAM use case.

cns/configuration/env.go

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,27 @@ const (
1111
EnvNodeName = "NODENAME"
1212
// EnvNodeIP is the IP of the node running this CNS binary
1313
EnvNodeIP = "NODE_IP"
14-
// LabelSwiftV2 is the Node label for Swift V2
15-
LabelSwiftV2 = "kubernetes.azure.com/podnetwork-multi-tenancy"
14+
// LabelNodeSwiftV2 is the Node label for Swift V2
15+
LabelNodeSwiftV2 = "kubernetes.azure.com/podnetwork-multi-tenancy-enabled"
16+
// LabelPodSwiftV2 is the Pod label for Swift V2
17+
LabelPodSwiftV2 = "kubernetes.azure.com/pod-network"
18+
EnvPodCIDRs = "POD_CIDRs"
19+
EnvServiceCIDRs = "SERVICE_CIDRs"
20+
EnvNodeCIDRs = "NODE_CIDRs"
1621
)
1722

1823
// ErrNodeNameUnset indicates the the $EnvNodeName variable is unset in the environment.
1924
var ErrNodeNameUnset = errors.Errorf("must declare %s environment variable", EnvNodeName)
2025

26+
// ErrPodCIDRsUnset indicates the the $EnvPodCIDRs variable is unset in the environment.
27+
var ErrPodCIDRsUnset = errors.Errorf("must declare %s environment variable", EnvPodCIDRs)
28+
29+
// ErrServiceCIDRsUnset indicates the the $EnvServiceCIDRs variable is unset in the environment.
30+
var ErrServiceCIDRsUnset = errors.Errorf("must declare %s environment variable", EnvServiceCIDRs)
31+
32+
// ErrNodeCIDRsUnset indicates the the $EnvNodeCIDRs variable is unset in the environment.
33+
var ErrNodeCIDRsUnset = errors.Errorf("must declare %s environment variable", EnvNodeCIDRs)
34+
2135
// NodeName checks the environment variables for the NODENAME and returns it or an error if unset.
2236
func NodeName() (string, error) {
2337
nodeName := os.Getenv(EnvNodeName)
@@ -31,3 +45,27 @@ func NodeName() (string, error) {
3145
func NodeIP() string {
3246
return os.Getenv(EnvNodeIP)
3347
}
48+
49+
func PodCIDRs() (string, error) {
50+
podCIDRs := os.Getenv(EnvPodCIDRs)
51+
if podCIDRs == "" {
52+
return "", ErrPodCIDRsUnset
53+
}
54+
return podCIDRs, nil
55+
}
56+
57+
func ServiceCIDRs() (string, error) {
58+
serviceCIDRs := os.Getenv(EnvServiceCIDRs)
59+
if serviceCIDRs == "" {
60+
return "", ErrServiceCIDRsUnset
61+
}
62+
return serviceCIDRs, nil
63+
}
64+
65+
func NodeCIDRs() (string, error) {
66+
nodeCIDRs := os.Getenv(EnvNodeCIDRs)
67+
if nodeCIDRs == "" {
68+
return "", ErrNodeCIDRsUnset
69+
}
70+
return nodeCIDRs, nil
71+
}

cns/configuration/env_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,23 @@ func TestNodeName(t *testing.T) {
1717
assert.NoError(t, err)
1818
assert.Equal(t, "test", name)
1919
}
20+
21+
func TestPodCIDRs(t *testing.T) {
22+
_, err := PodCIDRs()
23+
require.Error(t, err)
24+
require.ErrorIs(t, err, ErrPodCIDRsUnset)
25+
os.Setenv(EnvPodCIDRs, "test")
26+
cidr, err := PodCIDRs()
27+
assert.NoError(t, err)
28+
assert.Equal(t, "test", cidr)
29+
}
30+
31+
func TestServiceCIDRs(t *testing.T) {
32+
_, err := ServiceCIDRs()
33+
require.Error(t, err)
34+
require.ErrorIs(t, err, ErrServiceCIDRsUnset)
35+
os.Setenv(EnvServiceCIDRs, "test")
36+
cidr, err := ServiceCIDRs()
37+
assert.NoError(t, err)
38+
assert.Equal(t, "test", cidr)
39+
}

cns/fakes/cnsfake.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,5 @@ func (fake *HTTPServiceFake) Init(*common.ServiceConfig) error {
276276
}
277277

278278
func (fake *HTTPServiceFake) Stop() {}
279+
280+
func (fake *HTTPServiceFake) AttachSWIFTv2Middleware(cns.SWIFTv2Middleware) {}

cns/middlewares/mock/mockClient.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package mock
2+
3+
import (
4+
"context"
5+
6+
"github.com/Azure/azure-container-networking/cns/configuration"
7+
"github.com/Azure/azure-container-networking/crd/multitenancy/api/v1alpha1"
8+
"github.com/pkg/errors"
9+
v1 "k8s.io/api/core/v1"
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
)
12+
13+
var (
14+
ErrPodNotFound = errors.New("pod not found")
15+
ErrMTPNCNotFound = errors.New("mtpnc not found")
16+
)
17+
18+
// Client implements the client.Client interface for testing. We only care about Get, the rest is nil ops.
19+
type Client struct {
20+
client.Client
21+
mtPodCache map[string]*v1.Pod
22+
mtpncCache map[string]*v1alpha1.MultitenantPodNetworkConfig
23+
}
24+
25+
// NewClient returns a new MockClient.
26+
func NewClient() *Client {
27+
const podNetwork = "azure"
28+
29+
testPod1 := v1.Pod{}
30+
testPod1.Labels = make(map[string]string)
31+
testPod1.Labels[configuration.LabelPodSwiftV2] = podNetwork
32+
33+
testPod3 := v1.Pod{}
34+
testPod3.Labels = make(map[string]string)
35+
testPod3.Labels[configuration.LabelPodSwiftV2] = podNetwork
36+
37+
testPod4 := v1.Pod{}
38+
testPod4.Labels = make(map[string]string)
39+
testPod4.Labels[configuration.LabelPodSwiftV2] = podNetwork
40+
41+
testMTPNC1 := v1alpha1.MultitenantPodNetworkConfig{}
42+
43+
testMTPNC1.Status.PrimaryIP = "192.168.0.1/32"
44+
testMTPNC1.Status.MacAddress = "00:00:00:00:00:00"
45+
testMTPNC1.Status.GatewayIP = "10.0.0.1"
46+
testMTPNC1.Status.NCID = "testncid"
47+
48+
testMTPNC4 := v1alpha1.MultitenantPodNetworkConfig{}
49+
50+
return &Client{
51+
mtPodCache: map[string]*v1.Pod{"testpod1namespace/testpod1": &testPod1, "testpod3namespace/testpod3": &testPod3, "testpod4namespace/testpod4": &testPod4},
52+
mtpncCache: map[string]*v1alpha1.MultitenantPodNetworkConfig{
53+
"testpod1namespace/testpod1": &testMTPNC1,
54+
"testpod4namespace/testpod4": &testMTPNC4,
55+
},
56+
}
57+
}
58+
59+
// Get implements client.Client.Get.
60+
func (c *Client) Get(_ context.Context, key client.ObjectKey, obj client.Object, _ ...client.GetOption) error {
61+
switch o := obj.(type) {
62+
case *v1.Pod:
63+
if pod, ok := c.mtPodCache[key.String()]; ok {
64+
*o = *pod
65+
} else {
66+
return ErrPodNotFound
67+
}
68+
case *v1alpha1.MultitenantPodNetworkConfig:
69+
if mtpnc, ok := c.mtpncCache[key.String()]; ok {
70+
*o = *mtpnc
71+
} else {
72+
return ErrMTPNCNotFound
73+
}
74+
}
75+
return nil
76+
}

0 commit comments

Comments
 (0)