|
4 | 4 | package network |
5 | 5 |
|
6 | 6 | import ( |
| 7 | + "fmt" |
| 8 | + "log" |
| 9 | + "net" |
| 10 | + "regexp" |
7 | 11 | "testing" |
8 | 12 |
|
| 13 | + "github.com/Azure/azure-container-networking/cni" |
9 | 14 | "github.com/Azure/azure-container-networking/cns" |
10 | 15 | "github.com/Azure/azure-container-networking/network" |
| 16 | + "github.com/Azure/azure-container-networking/platform" |
| 17 | + "github.com/Azure/azure-container-networking/telemetry" |
| 18 | + cniSkel "github.com/containernetworking/cni/pkg/skel" |
11 | 19 | "github.com/stretchr/testify/assert" |
12 | 20 | "github.com/stretchr/testify/require" |
13 | 21 | ) |
@@ -160,3 +168,141 @@ func TestAddSnatForDns(t *testing.T) { |
160 | 168 | }) |
161 | 169 | } |
162 | 170 | } |
| 171 | + |
| 172 | +// Happy path scenario for add and delete |
| 173 | +func TestPluginLinuxAdd(t *testing.T) { |
| 174 | + resources := GetTestResources() |
| 175 | + localNwCfg := cni.NetworkConfig{ |
| 176 | + CNIVersion: "0.3.0", |
| 177 | + Name: "mulnet", |
| 178 | + MultiTenancy: true, |
| 179 | + EnableExactMatchForPodName: true, |
| 180 | + Master: "eth0", |
| 181 | + } |
| 182 | + type endpointEntry struct { |
| 183 | + epInfo *network.EndpointInfo |
| 184 | + epIDRegex string |
| 185 | + } |
| 186 | + |
| 187 | + tests := []struct { |
| 188 | + name string |
| 189 | + plugin *NetPlugin |
| 190 | + args *cniSkel.CmdArgs |
| 191 | + want []endpointEntry |
| 192 | + match func(*network.EndpointInfo, *network.EndpointInfo) bool |
| 193 | + }{ |
| 194 | + { |
| 195 | + // in swiftv1 linux multitenancy, we only get 1 response from cns at a time |
| 196 | + name: "Add Happy Path Swiftv1 Multitenancy", |
| 197 | + plugin: &NetPlugin{ |
| 198 | + Plugin: resources.Plugin, |
| 199 | + nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), |
| 200 | + tb: &telemetry.TelemetryBuffer{}, |
| 201 | + report: &telemetry.CNIReport{}, |
| 202 | + multitenancyClient: NewMockMultitenancy(false, []*cns.GetNetworkContainerResponse{GetTestCNSResponse3()}), |
| 203 | + }, |
| 204 | + args: &cniSkel.CmdArgs{ |
| 205 | + StdinData: localNwCfg.Serialize(), |
| 206 | + ContainerID: "test-container", |
| 207 | + Netns: "bc526fae-4ba0-4e80-bc90-ad721e5850bf", |
| 208 | + Args: fmt.Sprintf("K8S_POD_NAME=%v;K8S_POD_NAMESPACE=%v", "test-pod", "test-pod-ns"), |
| 209 | + IfName: eth0IfName, |
| 210 | + }, |
| 211 | + match: func(ei1, ei2 *network.EndpointInfo) bool { |
| 212 | + return ei1.NetworkContainerID == ei2.NetworkContainerID |
| 213 | + }, |
| 214 | + want: []endpointEntry{ |
| 215 | + // should match with GetTestCNSResponse3 |
| 216 | + { |
| 217 | + epInfo: &network.EndpointInfo{ |
| 218 | + ContainerID: "test-container", |
| 219 | + Data: map[string]interface{}{ |
| 220 | + "VlanID": 1, // Vlan ID used here |
| 221 | + "localIP": "168.254.0.4/17", |
| 222 | + "snatBridgeIP": "168.254.0.1/17", |
| 223 | + "vethname": "mulnettest-containereth0", |
| 224 | + }, |
| 225 | + Routes: []network.RouteInfo{ |
| 226 | + { |
| 227 | + Dst: *parseCIDR("192.168.0.4/24"), |
| 228 | + Gw: net.ParseIP("192.168.0.1"), |
| 229 | + // interface to use is NOT propagated to ep info |
| 230 | + }, |
| 231 | + }, |
| 232 | + AllowInboundFromHostToNC: true, |
| 233 | + EnableSnatOnHost: true, |
| 234 | + EnableMultiTenancy: true, |
| 235 | + EnableSnatForDns: true, |
| 236 | + PODName: "test-pod", |
| 237 | + PODNameSpace: "test-pod-ns", |
| 238 | + NICType: cns.InfraNIC, |
| 239 | + MasterIfName: eth0IfName, |
| 240 | + NetworkContainerID: "Swift_74b34111-6e92-49ee-a82a-8881c850ce0e", |
| 241 | + NetworkID: "mulnet", |
| 242 | + NetNsPath: "bc526fae-4ba0-4e80-bc90-ad721e5850bf", |
| 243 | + NetNs: "bc526fae-4ba0-4e80-bc90-ad721e5850bf", |
| 244 | + HostSubnetPrefix: "20.240.0.0/24", |
| 245 | + Options: map[string]interface{}{ |
| 246 | + dockerNetworkOption: map[string]interface{}{ |
| 247 | + "VlanID": "1", // doesn't seem to be used in linux |
| 248 | + "snatBridgeIP": "168.254.0.1/17", |
| 249 | + }, |
| 250 | + }, |
| 251 | + // matches with cns ip configuration |
| 252 | + IPAddresses: []net.IPNet{ |
| 253 | + { |
| 254 | + IP: net.ParseIP("20.0.0.10"), |
| 255 | + Mask: getIPNetWithString("20.0.0.10/24").Mask, |
| 256 | + }, |
| 257 | + }, |
| 258 | + NATInfo: nil, |
| 259 | + // ip config pod ip + mask(s) from cns > interface info > subnet info |
| 260 | + Subnets: []network.SubnetInfo{ |
| 261 | + { |
| 262 | + Family: platform.AfINET, |
| 263 | + // matches cns ip configuration (20.0.0.1/24 == 20.0.0.0/24) |
| 264 | + Prefix: *getIPNetWithString("20.0.0.0/24"), |
| 265 | + // matches cns ip configuration gateway ip address |
| 266 | + Gateway: net.ParseIP("20.0.0.1"), |
| 267 | + }, |
| 268 | + }, |
| 269 | + }, |
| 270 | + epIDRegex: `test-con-eth0`, |
| 271 | + }, |
| 272 | + }, |
| 273 | + }, |
| 274 | + } |
| 275 | + |
| 276 | + for _, tt := range tests { |
| 277 | + tt := tt |
| 278 | + t.Run(tt.name, func(t *testing.T) { |
| 279 | + err := tt.plugin.Add(tt.args) |
| 280 | + require.NoError(t, err) |
| 281 | + allEndpoints, _ := tt.plugin.nm.GetAllEndpoints("") |
| 282 | + require.Len(t, allEndpoints, len(tt.want)) |
| 283 | + for _, wantedEndpointEntry := range tt.want { |
| 284 | + epId := "none" |
| 285 | + for _, endpointInfo := range allEndpoints { |
| 286 | + log.Printf("%v", endpointInfo.NetworkID) |
| 287 | + if tt.match(wantedEndpointEntry.epInfo, endpointInfo) { |
| 288 | + // save the endpoint id before removing it |
| 289 | + epId = endpointInfo.EndpointID |
| 290 | + require.Regexp(t, regexp.MustCompile(wantedEndpointEntry.epIDRegex), epId) |
| 291 | + |
| 292 | + // omit endpoint id and ifname fields as they are nondeterministic |
| 293 | + endpointInfo.EndpointID = "" |
| 294 | + endpointInfo.IfName = "" |
| 295 | + |
| 296 | + require.Equal(t, wantedEndpointEntry.epInfo, endpointInfo) |
| 297 | + break |
| 298 | + } |
| 299 | + } |
| 300 | + if epId == "none" { |
| 301 | + t.Fail() |
| 302 | + } |
| 303 | + tt.plugin.nm.DeleteEndpoint("", epId, nil) |
| 304 | + } |
| 305 | + |
| 306 | + }) |
| 307 | + } |
| 308 | +} |
0 commit comments