Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,8 @@ PRETTYGOTEST := $(shell command -v gotest 2> /dev/null)
# run all tests
.PHONY: test-all
test-all:
go test -coverpkg=./... -v -race -covermode atomic -failfast -coverprofile=coverage.out ./...
go test -tags "unit" -coverpkg=./... -v -race -covermode atomic -failfast -coverprofile=coverage.out ./...


# run all tests
.PHONY: test-integration
Expand Down
41 changes: 41 additions & 0 deletions cni/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package api

import (
"encoding/json"
"net"
"os"

"github.com/Azure/azure-container-networking/log"
)

type PodNetworkInterfaceInfo struct {
PodName string
PodNamespace string
PodEndpointId string
ContainerID string
IPAddresses []net.IPNet
}

type CNIState interface {
PrintResult() error
}

type AzureCNIState struct {
ContainerInterfaces map[string]PodNetworkInterfaceInfo
}

func (a *AzureCNIState) PrintResult() error {
b, err := json.MarshalIndent(a, "", " ")
if err != nil {
log.Errorf("Failed to unmarshall Azure CNI state, err:%v.\n", err)
}

// write result to stdout to be captured by caller
_, err = os.Stdout.Write(b)
if err != nil {
log.Printf("Failed to write response to stdout %v", err)
return err
}

return nil
}
54 changes: 54 additions & 0 deletions cni/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package client

import (
"encoding/json"
"fmt"
"os"

"github.com/Azure/azure-container-networking/cni"
"github.com/Azure/azure-container-networking/cni/api"
"github.com/Azure/azure-container-networking/log"
utilexec "k8s.io/utils/exec"
)

const (
azureVnetBinName = "./azure-vnet"
azureVnetBinDirectory = "/opt/cni/bin"
)

type CNIClient interface {
GetEndpointState() (api.CNIState, error)
}

type AzureCNIClient struct {
exec utilexec.Interface
}

func NewCNIClient(exec utilexec.Interface) *AzureCNIClient {
return &AzureCNIClient{
exec: exec,
}
}

func (c *AzureCNIClient) GetEndpointState() (api.CNIState, error) {
cmd := c.exec.Command(azureVnetBinName)
cmd.SetDir(azureVnetBinDirectory)

envs := os.Environ()
cmdenv := fmt.Sprintf("%s=%s", cni.Cmd, cni.CmdGetEndpointsState)
log.Printf("Setting cmd to %s", cmdenv)
envs = append(envs, cmdenv)
cmd.SetEnv(envs)

output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("failed to call Azure CNI bin with err: [%w], output: [%s]", err, string(output))
}

state := &api.AzureCNIState{}
if err := json.Unmarshal(output, state); err != nil {
return nil, fmt.Errorf("failed to decode response from Azure CNI when retrieving state: [%w], response from CNI: [%s]", err, string(output))
}

return state, nil
}
23 changes: 23 additions & 0 deletions cni/client/client_common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// +build unit integration

package client

import (
"net"

"github.com/Azure/azure-container-networking/cni/api"
)

func testGetPodNetworkInterfaceInfo(podendpointid, podname, podnamespace, containerid, ipwithcidr string) api.PodNetworkInterfaceInfo {
ip, ipnet, _ := net.ParseCIDR(ipwithcidr)
ipnet.IP = ip
return api.PodNetworkInterfaceInfo{
PodName: podname,
PodNamespace: podnamespace,
PodEndpointId: podendpointid,
ContainerID: containerid,
IPAddresses: []net.IPNet{
*ipnet,
},
}
}
54 changes: 54 additions & 0 deletions cni/client/client_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// +build linux
// +build integration

package client

import (
"io"
"os"
"testing"

"github.com/Azure/azure-container-networking/cni/api"
testutils "github.com/Azure/azure-container-networking/test/utils"
"github.com/stretchr/testify/require"
"k8s.io/utils/exec"
)

// todo: enable this test in CI, requires built azure vnet
func TestGetStateFromAzureCNI(t *testing.T) {
testutils.RequireRootforTest(t)

// copy test state file to /var/run/azure-vnet.json
in, err := os.Open("./testresources/azure-vnet-test.json")
require.NoError(t, err)

defer in.Close()

out, err := os.Create("/var/run/azure-vnet.json")
require.NoError(t, err)

defer func() {
out.Close()
err := os.Remove("/var/run/azure-vnet.json")
require.NoError(t, err)
}()

_, err = io.Copy(out, in)
require.NoError(t, err)

out.Close()

realexec := exec.New()
c := NewCNIClient(realexec)
state, err := c.GetEndpointState()
require.NoError(t, err)

res := &api.AzureCNIState{
ContainerInterfaces: map[string]api.PodNetworkInterfaceInfo{
"3f813b02-eth0": testGetPodNetworkInterfaceInfo("3f813b02-eth0", "metrics-server-77c8679d7d-6ksdh", "kube-system", "3f813b029429b4e41a09ab33b6f6d365d2ed704017524c78d1d0dece33cdaf46", "10.241.0.17/16"),
"6e688597-eth0": testGetPodNetworkInterfaceInfo("6e688597-eth0", "tunnelfront-5d96f9b987-65xbn", "kube-system", "6e688597eafb97c83c84e402cc72b299bfb8aeb02021e4c99307a037352c0bed", "10.241.0.13/16"),
},
}

require.Exactly(t, res, state)
}
32 changes: 32 additions & 0 deletions cni/client/client_unit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// +build unit

package client

import (
"testing"

"github.com/Azure/azure-container-networking/cni/api"
testutils "github.com/Azure/azure-container-networking/test/utils"
"github.com/stretchr/testify/require"
)

func TestGetState(t *testing.T) {
calls := []testutils.TestCmd{
{Cmd: []string{"./azure-vnet"}, Stdout: `{"ContainerInterfaces":{"3f813b02-eth0":{"PodName":"metrics-server-77c8679d7d-6ksdh","PodNamespace":"kube-system","PodEndpointID":"3f813b02-eth0","ContainerID":"3f813b029429b4e41a09ab33b6f6d365d2ed704017524c78d1d0dece33cdaf46","IPAddresses":[{"IP":"10.241.0.17","Mask":"//8AAA=="}]},"6e688597-eth0":{"PodName":"tunnelfront-5d96f9b987-65xbn","PodNamespace":"kube-system","PodEndpointID":"6e688597-eth0","ContainerID":"6e688597eafb97c83c84e402cc72b299bfb8aeb02021e4c99307a037352c0bed","IPAddresses":[{"IP":"10.241.0.13","Mask":"//8AAA=="}]}}}`},
}

fakeexec, _ := testutils.GetFakeExecWithScripts(calls)

c := NewCNIClient(fakeexec)
state, err := c.GetEndpointState()
require.NoError(t, err)

res := &api.AzureCNIState{
ContainerInterfaces: map[string]api.PodNetworkInterfaceInfo{
"3f813b02-eth0": testGetPodNetworkInterfaceInfo("3f813b02-eth0", "metrics-server-77c8679d7d-6ksdh", "kube-system", "3f813b029429b4e41a09ab33b6f6d365d2ed704017524c78d1d0dece33cdaf46", "10.241.0.17/16"),
"6e688597-eth0": testGetPodNetworkInterfaceInfo("6e688597-eth0", "tunnelfront-5d96f9b987-65xbn", "kube-system", "6e688597eafb97c83c84e402cc72b299bfb8aeb02021e4c99307a037352c0bed", "10.241.0.13/16"),
},
}

require.Equal(t, res, state)
}
155 changes: 155 additions & 0 deletions cni/client/testresources/azure-vnet-test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
{
"Network": {
"Version": "v1.2.6",
"TimeStamp": "2021-06-04T17:15:58.638215441Z",
"ExternalInterfaces": {
"eth0": {
"Name": "eth0",
"Networks": {
"azure": {
"Id": "azure",
"Mode": "transparent",
"VlanId": 0,
"Subnets": [
{
"Family": 2,
"Prefix": {
"IP": "10.240.0.0",
"Mask": "//8AAA=="
},
"Gateway": "10.241.0.1",
"PrimaryIP": ""
}
],
"Endpoints": {
"3f813b02-eth0": {
"Id": "3f813b02-eth0",
"SandboxKey": "",
"IfName": "azvd805fb1b0f82",
"HostIfName": "azvd805fb1b0f8",
"MacAddress": "ipNAs8jK",
"InfraVnetIP": {
"IP": "",
"Mask": null
},
"LocalIP": "",
"IPAddresses": [
{
"IP": "10.241.0.17",
"Mask": "//8AAA=="
}
],
"Gateways": [
"0.0.0.0"
],
"DNS": {
"Suffix": "",
"Servers": null,
"Options": null
},
"Routes": [
{
"Dst": {
"IP": "0.0.0.0",
"Mask": "AAAAAA=="
},
"Src": "",
"Gw": "10.241.0.1",
"Protocol": 0,
"DevName": "",
"Scope": 0,
"Priority": 0
}
],
"VlanID": 0,
"EnableSnatOnHost": false,
"EnableInfraVnet": false,
"EnableMultitenancy": false,
"AllowInboundFromHostToNC": false,
"AllowInboundFromNCToHost": false,
"NetworkContainerID": "",
"NetworkNameSpace": "/var/run/netns/cni-e66c52d6-44be-555b-fb65-0b36296b6861",
"ContainerID": "3f813b029429b4e41a09ab33b6f6d365d2ed704017524c78d1d0dece33cdaf46",
"PODName": "metrics-server-77c8679d7d-6ksdh",
"PODNameSpace": "kube-system"
},
"6e688597-eth0": {
"Id": "6e688597-eth0",
"SandboxKey": "",
"IfName": "azvc214c1237ce2",
"HostIfName": "azvc214c1237ce",
"MacAddress": "ZjL4HSJ+",
"InfraVnetIP": {
"IP": "",
"Mask": null
},
"LocalIP": "",
"IPAddresses": [
{
"IP": "10.241.0.13",
"Mask": "//8AAA=="
}
],
"Gateways": [
"0.0.0.0"
],
"DNS": {
"Suffix": "",
"Servers": null,
"Options": null
},
"Routes": [
{
"Dst": {
"IP": "0.0.0.0",
"Mask": "AAAAAA=="
},
"Src": "",
"Gw": "10.241.0.1",
"Protocol": 0,
"DevName": "",
"Scope": 0,
"Priority": 0
}
],
"VlanID": 0,
"EnableSnatOnHost": false,
"EnableInfraVnet": false,
"EnableMultitenancy": false,
"AllowInboundFromHostToNC": false,
"AllowInboundFromNCToHost": false,
"NetworkContainerID": "",
"NetworkNameSpace": "/var/run/netns/cni-56872dcd-b3ab-fc90-ccd8-6a6dd11d56cf",
"ContainerID": "6e688597eafb97c83c84e402cc72b299bfb8aeb02021e4c99307a037352c0bed",
"PODName": "tunnelfront-5d96f9b987-65xbn",
"PODNameSpace": "kube-system"
}
},
"DNS": {
"Suffix": "",
"Servers": null,
"Options": null
},
"EnableSnatOnHost": false,
"NetNs": "",
"SnatBridgeIP": ""
}
},
"Subnets": [
"10.240.0.0/16"
],
"BridgeName": "",
"DNSInfo": {
"Suffix": "",
"Servers": null,
"Options": null
},
"MacAddress": "AA06xXdb",
"IPAddresses": null,
"Routes": null,
"IPv4Gateway": "0.0.0.0",
"IPv6Gateway": "::"
}
}
}
}
3 changes: 3 additions & 0 deletions cni/cni.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const (
CmdDel = "DEL"
CmdUpdate = "UPDATE"

// nonstandard CNI spec command, used to dump CNI state to stdout
CmdGetEndpointsState = "GET_ENDPOINT_STATE"

// CNI errors.
ErrRuntime = 100

Expand Down
Loading