Skip to content

Commit 67debca

Browse files
authored
Add ability in CNS to setup networking for a docker container (#298)
* Limiting the size of our buffered payload to ~2MB * CNI Update operation support * Adding APIs for attach/detach network container to/from network. * Updating new apis. * Addressing Tamilmani's review/comments. * Renaming Batch APIs and Request members for better clarity. * Adding check for pluginsSection length before accessing element. * Renaming ConfigureNetworkContainerNetworkingRequest to ConfigureContainerNetworkRequest. * Adding default k8s cni paths for windows. * Adding AzureFirstParty orchestrator type and Basic container type. * Addressing Sushant's comments.
1 parent e5f6b0d commit 67debca

16 files changed

+539
-38
lines changed

cni/network/mutlitenancy.go cni/network/multitenancy.go

+1-15
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"errors"
66
"fmt"
77
"net"
8-
"strings"
98

109
"github.com/Azure/azure-container-networking/cni"
1110
"github.com/Azure/azure-container-networking/cns"
@@ -50,7 +49,7 @@ func getContainerNetworkConfiguration(
5049
var podNameWithoutSuffix string
5150

5251
if !nwCfg.EnableExactMatchForPodName {
53-
podNameWithoutSuffix = getPodNameWithoutSuffix(podName)
52+
podNameWithoutSuffix = network.GetPodNameWithoutSuffix(podName)
5453
} else {
5554
podNameWithoutSuffix = podName
5655
}
@@ -134,19 +133,6 @@ func convertToCniResult(networkConfig *cns.GetNetworkContainerResponse, ifName s
134133
return result
135134
}
136135

137-
func getPodNameWithoutSuffix(podName string) string {
138-
nameSplit := strings.Split(podName, "-")
139-
log.Printf("namesplit %v", nameSplit)
140-
if len(nameSplit) > 2 {
141-
nameSplit = nameSplit[:len(nameSplit)-2]
142-
} else {
143-
return podName
144-
}
145-
146-
log.Printf("Pod name after splitting based on - : %v", nameSplit)
147-
return strings.Join(nameSplit, "-")
148-
}
149-
150136
func getInfraVnetIP(
151137
enableInfraVnet bool,
152138
infraSubnet string,

cni/network/network.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ func (plugin *netPlugin) Update(args *cniSkel.CmdArgs) error {
760760

761761
// Query the existing endpoint since this is an update.
762762
// Right now, we do not support updating pods that have multiple endpoints.
763-
existingEpInfo, err = plugin.nm.GetEndpointInfoBasedOnPODDetails(networkID, k8sPodName, k8sNamespace)
763+
existingEpInfo, err = plugin.nm.GetEndpointInfoBasedOnPODDetails(networkID, k8sPodName, k8sNamespace, nwCfg.EnableExactMatchForPodName)
764764
if err != nil {
765765
plugin.Errorf("Failed to retrieve target endpoint for CNI UPDATE [name=%v, namespace=%v]: %v", k8sPodName, k8sNamespace, err)
766766
return err

cns/NetworkContainerContract.go

+30-2
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,31 @@ const (
1010
GetNetworkContainerStatus = "/network/getnetworkcontainerstatus"
1111
GetInterfaceForContainer = "/network/getinterfaceforcontainer"
1212
GetNetworkContainerByOrchestratorContext = "/network/getnetworkcontainerbyorchestratorcontext"
13+
AttachContainerToNetwork = "/network/attachcontainertonetwork"
14+
DetachContainerFromNetwork = "/network/detachcontainerfromnetwork"
15+
)
16+
17+
// NetworkContainer Prefixes
18+
const (
19+
SwiftPrefix = "Swift_"
1320
)
1421

1522
// NetworkContainer Types
1623
const (
1724
AzureContainerInstance = "AzureContainerInstance"
1825
WebApps = "WebApps"
1926
ClearContainer = "ClearContainer"
27+
Docker = "Docker"
28+
Basic = "Basic"
2029
)
2130

2231
// Orchestrator Types
2332
const (
24-
Kubernetes = "Kubernetes"
25-
ServiceFabric = "ServiceFabric"
33+
Kubernetes = "Kubernetes"
34+
ServiceFabric = "ServiceFabric"
35+
Batch = "Batch"
36+
DBforPostgreSQL = "DBforPostgreSQL"
37+
AzureFirstParty = "AzureFirstParty"
2638
)
2739

2840
// Encap Types
@@ -46,6 +58,12 @@ type CreateNetworkContainerRequest struct {
4658
Routes []Route
4759
}
4860

61+
// ConfigureContainerNetworkingRequest - specifies request to attach/detach container to network.
62+
type ConfigureContainerNetworkingRequest struct {
63+
Containerid string
64+
NetworkContainerid string
65+
}
66+
4967
// KubernetesPodInfo is an OrchestratorContext that holds PodName and PodNamespace.
5068
type KubernetesPodInfo struct {
5169
PodName string
@@ -143,6 +161,16 @@ type GetInterfaceForContainerResponse struct {
143161
Response Response
144162
}
145163

164+
// AttachContainerToNetworkResponse specifies response of attaching network container to network.
165+
type AttachContainerToNetworkResponse struct {
166+
Response Response
167+
}
168+
169+
// DetachNetworkContainerToNetworkResponse specifies response of detaching network container from network.
170+
type DetachContainerFromNetworkResponse struct {
171+
Response Response
172+
}
173+
146174
// NetworkInterface specifies the information that can be used to unquely identify an interface.
147175
type NetworkInterface struct {
148176
Name string

cns/networkcontainers/networkcontainers.go

+138-2
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,53 @@
44
package networkcontainers
55

66
import (
7+
"bytes"
8+
"encoding/json"
79
"errors"
810
"fmt"
11+
"go/types"
12+
"io/ioutil"
913
"net"
14+
"os"
15+
"os/exec"
1016

1117
"github.com/Azure/azure-container-networking/cns"
1218
"github.com/Azure/azure-container-networking/log"
19+
"github.com/containernetworking/cni/libcni"
20+
"github.com/containernetworking/cni/pkg/invoke"
21+
)
22+
23+
const (
24+
versionStr = "cniVersion"
25+
pluginsStr = "plugins"
26+
nameStr = "name"
27+
k8sPodNamespaceStr = "K8S_POD_NAMESPACE"
28+
k8sPodNameStr = "K8S_POD_NAME"
29+
k8sPodInfraContainerStr = "K8S_POD_INFRA_CONTAINER_ID"
30+
cniAdd = "ADD"
31+
cniDelete = "DEL"
32+
cniUpdate = "UPDATE"
1333
)
1434

1535
// NetworkContainers can be used to perform operations on network containers.
1636
type NetworkContainers struct {
1737
logpath string
1838
}
1939

40+
// NetPluginConfiguration represent network plugin configuration that is used during Update operation
41+
type NetPluginConfiguration struct {
42+
path string
43+
networkConfigPath string
44+
}
45+
46+
// NewNetPluginConfiguration create a new netplugin configuration.
47+
func NewNetPluginConfiguration(binPath string, configPath string) *NetPluginConfiguration {
48+
return &NetPluginConfiguration{
49+
path: binPath,
50+
networkConfigPath: configPath,
51+
}
52+
}
53+
2054
func interfaceExists(iFaceName string) (bool, error) {
2155
_, err := net.InterfaceByName(iFaceName)
2256
if err != nil {
@@ -40,9 +74,9 @@ func (cn *NetworkContainers) Create(createNetworkContainerRequest cns.CreateNetw
4074
}
4175

4276
// Update updates a network container.
43-
func (cn *NetworkContainers) Update(createNetworkContainerRequest cns.CreateNetworkContainerRequest) error {
77+
func (cn *NetworkContainers) Update(createNetworkContainerRequest cns.CreateNetworkContainerRequest, netpluginConfig *NetPluginConfiguration) error {
4478
log.Printf("[Azure CNS] NetworkContainers.Update called")
45-
err := createOrUpdateInterface(createNetworkContainerRequest)
79+
err := updateInterface(createNetworkContainerRequest, netpluginConfig)
4680
if err == nil {
4781
err = setWeakHostOnInterface(createNetworkContainerRequest.PrimaryInterfaceIdentifier)
4882
}
@@ -57,3 +91,105 @@ func (cn *NetworkContainers) Delete(networkContainerID string) error {
5791
log.Printf("[Azure CNS] NetworkContainers.Delete finished.")
5892
return err
5993
}
94+
95+
// This function gets the flattened network configuration (compliant with azure cni) in byte array format
96+
func getNetworkConfig(configFilePath string) ([]byte, error) {
97+
content, err := ioutil.ReadFile(configFilePath)
98+
if err != nil {
99+
return nil, err
100+
}
101+
102+
var configMap map[string]interface{}
103+
if err = json.Unmarshal(content, &configMap); err != nil {
104+
log.Printf("[Azure CNS] Failed to unmarshal network configuration with error %v", err)
105+
return nil, err
106+
}
107+
108+
// Get the plugins section
109+
var flatNetConfigMap map[string]interface{}
110+
if pluginsSection, ok := configMap[pluginsStr]; ok && len(pluginsSection.([]interface{})) > 0 {
111+
flatNetConfigMap = pluginsSection.([]interface{})[0].(map[string]interface{})
112+
}
113+
114+
if flatNetConfigMap == nil {
115+
msg := "[Azure CNS] " + pluginsStr + " section of the network configuration cannot be empty."
116+
log.Printf(msg)
117+
return nil, errors.New(msg)
118+
}
119+
120+
// insert version and name fields
121+
flatNetConfigMap[versionStr] = configMap[versionStr].(string)
122+
flatNetConfigMap[nameStr] = configMap[nameStr].(string)
123+
124+
// convert into bytes format
125+
netConfig, err := json.Marshal(flatNetConfigMap)
126+
if err != nil {
127+
log.Printf("[Azure CNS] Failed to marshal flat network configuration with error %v", err)
128+
return nil, err
129+
}
130+
131+
return netConfig, nil
132+
}
133+
134+
func args(action, path string, rt *libcni.RuntimeConf) *invoke.Args {
135+
return &invoke.Args{
136+
Command: action,
137+
ContainerID: rt.ContainerID,
138+
NetNS: rt.NetNS,
139+
PluginArgs: rt.Args,
140+
IfName: rt.IfName,
141+
Path: path,
142+
}
143+
}
144+
145+
func pluginErr(err error, output []byte) error {
146+
if err != nil {
147+
if _, ok := err.(*exec.ExitError); ok {
148+
emsg := types.Error{}
149+
if err := json.Unmarshal(output, &emsg); err != nil {
150+
emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %s: %+v", string(output), err)
151+
}
152+
153+
return &emsg
154+
}
155+
}
156+
157+
return err
158+
}
159+
160+
func execPlugin(rt *libcni.RuntimeConf, netconf []byte, operation, path string) error {
161+
switch operation {
162+
case cniAdd:
163+
fallthrough
164+
case cniDelete:
165+
fallthrough
166+
case cniUpdate:
167+
environ := args(operation, path, rt).AsEnv()
168+
log.Printf("[Azure CNS] CNI called with environ variables %v", environ)
169+
stdout := &bytes.Buffer{}
170+
command := exec.Command(path + string(os.PathSeparator) + "azure-vnet")
171+
command.Env = environ
172+
command.Stdin = bytes.NewBuffer(netconf)
173+
command.Stdout = stdout
174+
command.Stderr = os.Stderr
175+
return pluginErr(command.Run(), stdout.Bytes())
176+
default:
177+
return fmt.Errorf("[Azure CNS] Invalid operation being passed to CNI: %s", operation)
178+
}
179+
}
180+
181+
// Attach - attaches network container to network.
182+
func (cn *NetworkContainers) Attach(podName, podNamespace, dockerContainerid string, netPluginConfig *NetPluginConfiguration) error {
183+
log.Printf("[Azure CNS] NetworkContainers.Attach called")
184+
err := configureNetworkContainerNetworking(cniAdd, podName, podNamespace, dockerContainerid, netPluginConfig)
185+
log.Printf("[Azure CNS] NetworkContainers.Attach finished")
186+
return err
187+
}
188+
189+
// Detach - attaches network container to network.
190+
func (cn *NetworkContainers) Detach(podName, podNamespace, dockerContainerid string, netPluginConfig *NetPluginConfiguration) error {
191+
log.Printf("[Azure CNS] NetworkContainers.Detach called")
192+
err := configureNetworkContainerNetworking(cniDelete, podName, podNamespace, dockerContainerid, netPluginConfig)
193+
log.Printf("[Azure CNS] NetworkContainers.Detach finished")
194+
return err
195+
}

cns/networkcontainers/networkcontainers_linux.go

+71-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,15 @@
33

44
package networkcontainers
55

6-
import "github.com/Azure/azure-container-networking/cns"
6+
import (
7+
"encoding/json"
8+
"errors"
9+
"os"
10+
11+
"github.com/Azure/azure-container-networking/cns"
12+
"github.com/Azure/azure-container-networking/log"
13+
"github.com/containernetworking/cni/libcni"
14+
)
715

816
func createOrUpdateInterface(createNetworkContainerRequest cns.CreateNetworkContainerRequest) error {
917
return nil
@@ -13,10 +21,71 @@ func setWeakHostOnInterface(ipAddress string) error {
1321
return nil
1422
}
1523

16-
func createOrUpdateWithOperation(createNetworkContainerRequest cns.CreateNetworkContainerRequest, operation string) error {
24+
func updateInterface(createNetworkContainerRequest cns.CreateNetworkContainerRequest, netpluginConfig *NetPluginConfiguration) error {
25+
log.Printf("[Azure CNS] update interface operation called.")
26+
27+
// Currently update via CNI is only supported for ACI type
28+
if createNetworkContainerRequest.NetworkContainerType != cns.AzureContainerInstance {
29+
log.Printf("[Azure CNS] operation is only supported for AzureContainerInstance types.")
30+
return nil
31+
}
32+
33+
if netpluginConfig == nil {
34+
err := errors.New("Network plugin configuration cannot be nil.")
35+
log.Printf("[Azure CNS] Update interface failed with error %v", err)
36+
return err
37+
}
38+
39+
if _, err := os.Stat(netpluginConfig.path); err != nil {
40+
if os.IsNotExist(err) {
41+
msg := "[Azure CNS] Unable to find " + netpluginConfig.path + ", cannot continue."
42+
log.Printf(msg)
43+
return errors.New(msg)
44+
}
45+
}
46+
47+
var podInfo cns.KubernetesPodInfo
48+
err := json.Unmarshal(createNetworkContainerRequest.OrchestratorContext, &podInfo)
49+
if err != nil {
50+
log.Printf("[Azure CNS] Unmarshalling %s failed with error %v", createNetworkContainerRequest.NetworkContainerType, err)
51+
return err
52+
}
53+
54+
log.Printf("[Azure CNS] Going to update networking for the pod with Pod info %+v", podInfo)
55+
56+
rt := &libcni.RuntimeConf{
57+
ContainerID: "", // Not needed for CNI update operation
58+
NetNS: "", // Not needed for CNI update operation
59+
IfName: createNetworkContainerRequest.NetworkContainerid,
60+
Args: [][2]string{
61+
{k8sPodNamespaceStr, podInfo.PodNamespace},
62+
{k8sPodNameStr, podInfo.PodName},
63+
},
64+
}
65+
66+
log.Printf("[Azure CNS] run time configuration for CNI plugin info %+v", rt)
67+
68+
netConfig, err := getNetworkConfig(netpluginConfig.networkConfigPath)
69+
if err != nil {
70+
log.Printf("[Azure CNS] Failed to build network configuration with error %v", err)
71+
return err
72+
}
73+
74+
log.Printf("[Azure CNS] network configuration info %v", string(netConfig))
75+
76+
err = execPlugin(rt, netConfig, cniUpdate, netpluginConfig.path)
77+
if err != nil {
78+
log.Printf("[Azure CNS] Failed to update network with error %v", err)
79+
return err
80+
}
81+
1782
return nil
1883
}
1984

2085
func deleteInterface(networkContainerID string) error {
2186
return nil
2287
}
88+
89+
func configureNetworkContainerNetworking(operation, podName, podNamespace, dockerContainerid string, netPluginConfig *NetPluginConfiguration) (err error) {
90+
return err
91+
}

0 commit comments

Comments
 (0)