Implement CNI update for Azure CNI (#265)
* Implement CNI Update for Azure CNI (#21)
This commit is contained in:
Родитель
5f123a0c15
Коммит
6e6260afe5
10
cni/cni.go
10
cni/cni.go
|
@ -9,10 +9,11 @@ import (
|
|||
|
||||
const (
|
||||
// CNI commands.
|
||||
Cmd = "CNI_COMMAND"
|
||||
CmdAdd = "ADD"
|
||||
CmdGet = "GET"
|
||||
CmdDel = "DEL"
|
||||
Cmd = "CNI_COMMAND"
|
||||
CmdAdd = "ADD"
|
||||
CmdGet = "GET"
|
||||
CmdDel = "DEL"
|
||||
CmdUpdate = "UPDATE"
|
||||
|
||||
// CNI errors.
|
||||
ErrRuntime = 100
|
||||
|
@ -29,4 +30,5 @@ type PluginApi interface {
|
|||
Add(args *cniSkel.CmdArgs) error
|
||||
Get(args *cniSkel.CmdArgs) error
|
||||
Delete(args *cniSkel.CmdArgs) error
|
||||
Update(args *cniSkel.CmdArgs) error
|
||||
}
|
||||
|
|
|
@ -290,3 +290,8 @@ func (plugin *ipamPlugin) Delete(args *cniSkel.CmdArgs) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update handles CNI update command.
|
||||
func (plugin *ipamPlugin) Update(args *cniSkel.CmdArgs) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ type NetworkConfig struct {
|
|||
MultiTenancy bool `json:"multiTenancy,omitempty"`
|
||||
EnableSnatOnHost bool `json:"enableSnatOnHost,omitempty"`
|
||||
EnableExactMatchForPodName bool `json:"enableExactMatchForPodName,omitempty"`
|
||||
CNSUrl string `json:"cnsurl,omitempty"`
|
||||
Ipam struct {
|
||||
Type string `json:"type"`
|
||||
Environment string `json:"environment,omitempty"`
|
||||
|
|
|
@ -205,6 +205,7 @@ func checkIfSubnetOverlaps(enableInfraVnet bool, nwCfg *cni.NetworkConfig, cnsNe
|
|||
return false
|
||||
}
|
||||
|
||||
// GetMultiTenancyCNIResult retrieves network goal state of a container from CNS
|
||||
func GetMultiTenancyCNIResult(
|
||||
enableInfraVnet bool,
|
||||
nwCfg *cni.NetworkConfig,
|
||||
|
@ -214,7 +215,7 @@ func GetMultiTenancyCNIResult(
|
|||
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, *cniTypesCurr.Result, error) {
|
||||
|
||||
if nwCfg.MultiTenancy {
|
||||
result, cnsNetworkConfig, subnetPrefix, err := getContainerNetworkConfiguration(nwCfg, "", k8sPodName, k8sNamespace, ifName)
|
||||
result, cnsNetworkConfig, subnetPrefix, err := getContainerNetworkConfiguration(nwCfg, nwCfg.CNSUrl, k8sPodName, k8sNamespace, ifName)
|
||||
if err != nil {
|
||||
log.Printf("GetContainerNetworkConfiguration failed for podname %v namespace %v with error %v", k8sPodName, k8sNamespace, err)
|
||||
return nil, nil, net.IPNet{}, nil, err
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
package network
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/Azure/azure-container-networking/cni"
|
||||
"github.com/Azure/azure-container-networking/cns"
|
||||
"github.com/Azure/azure-container-networking/cns/cnsclient"
|
||||
"github.com/Azure/azure-container-networking/common"
|
||||
"github.com/Azure/azure-container-networking/log"
|
||||
"github.com/Azure/azure-container-networking/network"
|
||||
|
@ -397,17 +399,20 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error {
|
|||
}
|
||||
|
||||
epInfo = &network.EndpointInfo{
|
||||
Id: endpointId,
|
||||
ContainerID: args.ContainerID,
|
||||
NetNsPath: args.Netns,
|
||||
IfName: args.IfName,
|
||||
EnableSnatOnHost: nwCfg.EnableSnatOnHost,
|
||||
EnableInfraVnet: enableInfraVnet,
|
||||
Data: make(map[string]interface{}),
|
||||
DNS: epDNSInfo,
|
||||
Policies: policies,
|
||||
Id: endpointId,
|
||||
ContainerID: args.ContainerID,
|
||||
NetNsPath: args.Netns,
|
||||
IfName: args.IfName,
|
||||
Data: make(map[string]interface{}),
|
||||
DNS: epDNSInfo,
|
||||
Policies: policies,
|
||||
EnableSnatOnHost: nwCfg.EnableSnatOnHost,
|
||||
EnableMultiTenancy: nwCfg.MultiTenancy,
|
||||
EnableInfraVnet: enableInfraVnet,
|
||||
PODName: k8sPodName,
|
||||
PODNameSpace: k8sNamespace,
|
||||
}
|
||||
|
||||
|
||||
epPolicies := getPoliciesFromRuntimeCfg(nwCfg)
|
||||
for _, epPolicy := range epPolicies {
|
||||
epInfo.Policies = append(epInfo.Policies, epPolicy)
|
||||
|
@ -599,3 +604,159 @@ func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update handles CNI update commands.
|
||||
// Update is only supported for multitenancy and to update routes.
|
||||
func (plugin *netPlugin) Update(args *cniSkel.CmdArgs) error {
|
||||
var (
|
||||
result *cniTypesCurr.Result
|
||||
err error
|
||||
nwCfg *cni.NetworkConfig
|
||||
existingEpInfo *network.EndpointInfo
|
||||
)
|
||||
|
||||
log.Printf("[cni-net] Processing UPDATE command with args {Netns:%v Args:%v Path:%v}.",
|
||||
args.Netns, args.Args, args.Path)
|
||||
|
||||
// Parse network configuration from stdin.
|
||||
nwCfg, err = cni.ParseNetworkConfig(args.StdinData)
|
||||
if err != nil {
|
||||
err = plugin.Errorf("Failed to parse network configuration: %v.", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[cni-net] Read network configuration %+v.", nwCfg)
|
||||
|
||||
defer func() {
|
||||
if result == nil {
|
||||
result = &cniTypesCurr.Result{}
|
||||
}
|
||||
|
||||
// Convert result to the requested CNI version.
|
||||
res, vererr := result.GetAsVersion(nwCfg.CNIVersion)
|
||||
if vererr != nil {
|
||||
log.Printf("GetAsVersion failed with error %v", vererr)
|
||||
plugin.Error(vererr)
|
||||
}
|
||||
|
||||
if err == nil && res != nil {
|
||||
// Output the result to stdout.
|
||||
res.Print()
|
||||
}
|
||||
|
||||
log.Printf("[cni-net] UPDATE command completed with result:%+v err:%v.", result, err)
|
||||
}()
|
||||
|
||||
// Parse Pod arguments.
|
||||
podCfg, err := cni.ParseCniArgs(args.Args)
|
||||
if err != nil {
|
||||
log.Printf("Error while parsing CNI Args during UPDATE %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
k8sNamespace := string(podCfg.K8S_POD_NAMESPACE)
|
||||
if len(k8sNamespace) == 0 {
|
||||
errMsg := "Required parameter Pod Namespace not specified in CNI Args during UPDATE"
|
||||
log.Printf(errMsg)
|
||||
return plugin.Errorf(errMsg)
|
||||
}
|
||||
|
||||
k8sPodName := string(podCfg.K8S_POD_NAME)
|
||||
if len(k8sPodName) == 0 {
|
||||
errMsg := "Required parameter Pod Name not specified in CNI Args during UPDATE"
|
||||
log.Printf(errMsg)
|
||||
return plugin.Errorf(errMsg)
|
||||
}
|
||||
|
||||
// Initialize values from network config.
|
||||
networkID := nwCfg.Name
|
||||
|
||||
// Query the network.
|
||||
_, err = plugin.nm.GetNetworkInfo(networkID)
|
||||
if err != nil {
|
||||
errMsg := fmt.Sprintf("Failed to query network during CNI UPDATE: %v", err)
|
||||
log.Printf(errMsg)
|
||||
return plugin.Errorf(errMsg)
|
||||
}
|
||||
|
||||
// Query the existing endpoint since this is an update.
|
||||
// Right now, we do not support updating pods that have multiple endpoints.
|
||||
existingEpInfo, err = plugin.nm.GetEndpointInfoBasedOnPODDetails(networkID, k8sPodName, k8sNamespace)
|
||||
if err != nil {
|
||||
plugin.Errorf("Failed to retrieve target endpoint for CNI UPDATE [name=%v, namespace=%v]: %v", k8sPodName, k8sNamespace, err)
|
||||
return err
|
||||
} else {
|
||||
log.Printf("Retrieved existing endpoint from state that may get update: %+v", existingEpInfo)
|
||||
}
|
||||
|
||||
// now query CNS to get the target routes that should be there in the networknamespace (as a result of update)
|
||||
log.Printf("Going to collect target routes for [name=%v, namespace=%v] from CNS.", k8sPodName, k8sNamespace)
|
||||
cnsClient, err := cnsclient.NewCnsClient(nwCfg.CNSUrl)
|
||||
if err != nil {
|
||||
log.Printf("Initializing CNS client error in CNI Update%v", err)
|
||||
log.Printf(err.Error())
|
||||
return plugin.Errorf(err.Error())
|
||||
}
|
||||
|
||||
// create struct with info for target POD
|
||||
podInfo := cns.KubernetesPodInfo{PodName: k8sPodName, PodNamespace: k8sNamespace}
|
||||
orchestratorContext, err := json.Marshal(podInfo)
|
||||
if err != nil {
|
||||
log.Printf("Marshalling KubernetesPodInfo failed with %v", err)
|
||||
return plugin.Errorf(err.Error())
|
||||
}
|
||||
|
||||
targetNetworkConfig, err := cnsClient.GetNetworkConfiguration(orchestratorContext)
|
||||
if err != nil {
|
||||
log.Printf("GetNetworkConfiguration failed with %v", err)
|
||||
return plugin.Errorf(err.Error())
|
||||
}
|
||||
|
||||
log.Printf("Network config received from cns for [name=%v, namespace=%v] is as follows -> %+v", k8sPodName, k8sNamespace, targetNetworkConfig)
|
||||
targetEpInfo := &network.EndpointInfo{}
|
||||
|
||||
// get the target routes that should replace existingEpInfo.Routes inside the network namespace
|
||||
log.Printf("Going to collect target routes for [name=%v, namespace=%v] from targetNetworkConfig.", k8sPodName, k8sNamespace)
|
||||
if targetNetworkConfig.Routes != nil && len(targetNetworkConfig.Routes) > 0 {
|
||||
for _, route := range targetNetworkConfig.Routes {
|
||||
log.Printf("Adding route from routes to targetEpInfo %+v", route)
|
||||
_, dstIPNet, _ := net.ParseCIDR(route.IPAddress)
|
||||
gwIP := net.ParseIP(route.GatewayIPAddress)
|
||||
targetEpInfo.Routes = append(targetEpInfo.Routes, network.RouteInfo{Dst: *dstIPNet, Gw: gwIP, DevName: existingEpInfo.IfName})
|
||||
log.Printf("Successfully added route from routes to targetEpInfo %+v", route)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Going to collect target routes based on Cnetaddressspace for [name=%v, namespace=%v] from targetNetworkConfig.", k8sPodName, k8sNamespace)
|
||||
ipconfig := targetNetworkConfig.IPConfiguration
|
||||
for _, ipRouteSubnet := range targetNetworkConfig.CnetAddressSpace {
|
||||
log.Printf("Adding route from cnetAddressspace to targetEpInfo %+v", ipRouteSubnet)
|
||||
dstIPNet := net.IPNet{IP: net.ParseIP(ipRouteSubnet.IPAddress), Mask: net.CIDRMask(int(ipRouteSubnet.PrefixLength), 32)}
|
||||
gwIP := net.ParseIP(ipconfig.GatewayIPAddress)
|
||||
route := network.RouteInfo{Dst: dstIPNet, Gw: gwIP, DevName: existingEpInfo.IfName}
|
||||
targetEpInfo.Routes = append(targetEpInfo.Routes, route)
|
||||
log.Printf("Successfully added route from cnetAddressspace to targetEpInfo %+v", ipRouteSubnet)
|
||||
}
|
||||
|
||||
log.Printf("Finished collecting new routes in targetEpInfo as follows: %+v", targetEpInfo.Routes)
|
||||
log.Printf("Now saving existing infravnetaddress space if needed.")
|
||||
for _, ns := range nwCfg.PodNamespaceForDualNetwork {
|
||||
if k8sNamespace == ns {
|
||||
targetEpInfo.EnableInfraVnet = true
|
||||
targetEpInfo.InfraVnetAddressSpace = nwCfg.InfraVnetAddressSpace
|
||||
log.Printf("Saving infravnet address space %s for [%s-%s]",
|
||||
targetEpInfo.InfraVnetAddressSpace, existingEpInfo.PODNameSpace, existingEpInfo.PODName)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Update the endpoint.
|
||||
log.Printf("Now updating existing endpoint %v with targetNetworkConfig %+v.", existingEpInfo.Id, targetNetworkConfig)
|
||||
err = plugin.nm.UpdateEndpoint(networkID, existingEpInfo, targetEpInfo)
|
||||
if err != nil {
|
||||
err = plugin.Errorf("Failed to update endpoint: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/Azure/azure-container-networking/cni"
|
||||
"github.com/Azure/azure-container-networking/cni/network"
|
||||
"github.com/Azure/azure-container-networking/common"
|
||||
acn "github.com/Azure/azure-container-networking/common"
|
||||
"github.com/Azure/azure-container-networking/log"
|
||||
"github.com/Azure/azure-container-networking/telemetry"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -23,6 +28,22 @@ const (
|
|||
// Version is populated by make during build.
|
||||
var version string
|
||||
|
||||
// Command line arguments for CNI plugin.
|
||||
var args = acn.ArgumentList{
|
||||
{
|
||||
Name: acn.OptVersion,
|
||||
Shorthand: acn.OptVersionAlias,
|
||||
Description: "Print version information",
|
||||
Type: "bool",
|
||||
DefaultValue: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Prints version information.
|
||||
func printVersion() {
|
||||
fmt.Printf("Azure CNI Version %v\n", version)
|
||||
}
|
||||
|
||||
// If report write succeeded, mark the report flag state to false.
|
||||
func markSendReport(reportManager *telemetry.ReportManager) {
|
||||
if err := reportManager.SetReportState(telemetry.CNITelemetryFile); err != nil {
|
||||
|
@ -48,8 +69,82 @@ func reportPluginError(reportManager *telemetry.ReportManager, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
func validateConfig(jsonBytes []byte) error {
|
||||
var conf struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonBytes, &conf); err != nil {
|
||||
return fmt.Errorf("error reading network config: %s", err)
|
||||
}
|
||||
if conf.Name == "" {
|
||||
return fmt.Errorf("missing network name")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCmdArgsFromEnv() (string, *skel.CmdArgs, error) {
|
||||
log.Printf("Going to read from stdin")
|
||||
stdinData, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error reading from stdin: %v", err)
|
||||
}
|
||||
|
||||
cmdArgs := &skel.CmdArgs{
|
||||
ContainerID: os.Getenv("CNI_CONTAINERID"),
|
||||
Netns: os.Getenv("CNI_NETNS"),
|
||||
IfName: os.Getenv("CNI_IFNAME"),
|
||||
Args: os.Getenv("CNI_ARGS"),
|
||||
Path: os.Getenv("CNI_PATH"),
|
||||
StdinData: stdinData,
|
||||
}
|
||||
|
||||
cmd := os.Getenv("CNI_COMMAND")
|
||||
return cmd, cmdArgs, nil
|
||||
}
|
||||
|
||||
func handleIfCniUpdate(update func(*skel.CmdArgs) error) (bool, error) {
|
||||
isupdate := true
|
||||
|
||||
if os.Getenv("CNI_COMMAND") != cni.CmdUpdate {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
log.Printf("CNI UPDATE received.")
|
||||
|
||||
_, cmdArgs, err := getCmdArgsFromEnv()
|
||||
if err != nil {
|
||||
log.Printf("Received error while retrieving cmds from environment: %+v", err)
|
||||
return isupdate, err
|
||||
}
|
||||
|
||||
log.Printf("Retrieved command args for update +%v", cmdArgs)
|
||||
err = validateConfig(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
log.Printf("Failed to handle CNI UPDATE, err:%v.", err)
|
||||
return isupdate, err
|
||||
}
|
||||
|
||||
err = update(cmdArgs)
|
||||
if err != nil {
|
||||
log.Printf("Failed to handle CNI UPDATE, err:%v.", err)
|
||||
return isupdate, err
|
||||
}
|
||||
|
||||
return isupdate, nil
|
||||
}
|
||||
|
||||
// Main is the entry point for CNI network plugin.
|
||||
func main() {
|
||||
|
||||
// Initialize and parse command line arguments.
|
||||
acn.ParseArgs(&args, printVersion)
|
||||
vers := acn.GetArg(acn.OptVersion).(bool)
|
||||
|
||||
if vers {
|
||||
printVersion()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
var (
|
||||
config common.PluginConfig
|
||||
err error
|
||||
|
@ -109,7 +204,10 @@ func main() {
|
|||
panic("network plugin fatal error")
|
||||
}
|
||||
|
||||
if err = netPlugin.Execute(cni.PluginApi(netPlugin)); err != nil {
|
||||
handled, err := handleIfCniUpdate(netPlugin.Update)
|
||||
if handled == true {
|
||||
log.Printf("CNI UPDATE finished.")
|
||||
} else if err = netPlugin.Execute(cni.PluginApi(netPlugin)); err != nil {
|
||||
log.Printf("Failed to execute network plugin, err:%v.\n", err)
|
||||
reportPluginError(reportManager, err)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ const (
|
|||
// Version is populated by make during build.
|
||||
var version string
|
||||
|
||||
// Command line arguments for CNM plugin.
|
||||
// Command line arguments for CNS.
|
||||
var args = acn.ArgumentList{
|
||||
{
|
||||
Name: acn.OptEnvironment,
|
||||
|
|
|
@ -9,12 +9,14 @@ import (
|
|||
|
||||
var (
|
||||
// Error responses returned by NetworkManager.
|
||||
errSubnetNotFound = fmt.Errorf("Subnet not found")
|
||||
errNetworkModeInvalid = fmt.Errorf("Network mode is invalid")
|
||||
errNetworkExists = fmt.Errorf("Network already exists")
|
||||
errNetworkNotFound = fmt.Errorf("Network not found")
|
||||
errEndpointExists = fmt.Errorf("Endpoint already exists")
|
||||
errEndpointNotFound = fmt.Errorf("Endpoint not found")
|
||||
errEndpointInUse = fmt.Errorf("Endpoint is already joined to a sandbox")
|
||||
errEndpointNotInUse = fmt.Errorf("Endpoint is not joined to a sandbox")
|
||||
errSubnetNotFound = fmt.Errorf("Subnet not found")
|
||||
errNetworkModeInvalid = fmt.Errorf("Network mode is invalid")
|
||||
errNetworkExists = fmt.Errorf("Network already exists")
|
||||
errNetworkNotFound = fmt.Errorf("Network not found")
|
||||
errEndpointExists = fmt.Errorf("Endpoint already exists")
|
||||
errEndpointNotFound = fmt.Errorf("Endpoint not found")
|
||||
errNamespaceNotFound = fmt.Errorf("Namespace not found")
|
||||
errMultipleEndpointsFound = fmt.Errorf("Multiple endpoints found")
|
||||
errEndpointInUse = fmt.Errorf("Endpoint is already joined to a sandbox")
|
||||
errEndpointNotInUse = fmt.Errorf("Endpoint is not joined to a sandbox")
|
||||
)
|
||||
|
|
|
@ -16,40 +16,50 @@ const (
|
|||
|
||||
// Endpoint represents a container network interface.
|
||||
type endpoint struct {
|
||||
Id string
|
||||
HnsId string `json:",omitempty"`
|
||||
SandboxKey string
|
||||
IfName string
|
||||
HostIfName string
|
||||
MacAddress net.HardwareAddr
|
||||
InfraVnetIP net.IPNet
|
||||
IPAddresses []net.IPNet
|
||||
Gateways []net.IP
|
||||
DNS DNSInfo
|
||||
Routes []RouteInfo
|
||||
VlanID int
|
||||
EnableSnatOnHost bool
|
||||
EnableInfraVnet bool
|
||||
Id string
|
||||
HnsId string `json:",omitempty"`
|
||||
SandboxKey string
|
||||
IfName string
|
||||
HostIfName string
|
||||
MacAddress net.HardwareAddr
|
||||
InfraVnetIP net.IPNet
|
||||
IPAddresses []net.IPNet
|
||||
Gateways []net.IP
|
||||
DNS DNSInfo
|
||||
Routes []RouteInfo
|
||||
VlanID int
|
||||
EnableSnatOnHost bool
|
||||
EnableInfraVnet bool
|
||||
EnableMultitenancy bool
|
||||
NetworkNameSpace string `json:",omitempty"`
|
||||
ContainerID string
|
||||
PODName string `json:",omitempty"`
|
||||
PODNameSpace string `json:",omitempty"`
|
||||
InfraVnetAddressSpace string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// EndpointInfo contains read-only information about an endpoint.
|
||||
type EndpointInfo struct {
|
||||
Id string
|
||||
ContainerID string
|
||||
NetNsPath string
|
||||
IfName string
|
||||
SandboxKey string
|
||||
IfIndex int
|
||||
MacAddress net.HardwareAddr
|
||||
DNS DNSInfo
|
||||
IPAddresses []net.IPNet
|
||||
InfraVnetIP net.IPNet
|
||||
Routes []RouteInfo
|
||||
Policies []policy.Policy
|
||||
Gateways []net.IP
|
||||
EnableSnatOnHost bool
|
||||
EnableInfraVnet bool
|
||||
Data map[string]interface{}
|
||||
Id string
|
||||
ContainerID string
|
||||
NetNsPath string
|
||||
IfName string
|
||||
SandboxKey string
|
||||
IfIndex int
|
||||
MacAddress net.HardwareAddr
|
||||
DNS DNSInfo
|
||||
IPAddresses []net.IPNet
|
||||
InfraVnetIP net.IPNet
|
||||
Routes []RouteInfo
|
||||
Policies []policy.Policy
|
||||
Gateways []net.IP
|
||||
EnableSnatOnHost bool
|
||||
EnableInfraVnet bool
|
||||
EnableMultiTenancy bool
|
||||
PODName string
|
||||
PODNameSpace string
|
||||
Data map[string]interface{}
|
||||
InfraVnetAddressSpace string
|
||||
}
|
||||
|
||||
// RouteInfo contains information about an IP route.
|
||||
|
@ -128,6 +138,29 @@ func (nw *network) getEndpoint(endpointId string) (*endpoint, error) {
|
|||
return ep, nil
|
||||
}
|
||||
|
||||
// GetEndpointByPOD returns the endpoint with the given ID.
|
||||
func (nw *network) getEndpointByPOD(podName string, podNameSpace string) (*endpoint, error) {
|
||||
log.Printf("Trying to retrieve endpoint for pod name: %v in namespace: %v", podName, podNameSpace)
|
||||
|
||||
var ep *endpoint
|
||||
|
||||
for _, endpoint := range nw.Endpoints {
|
||||
if endpoint.PODName == podName && endpoint.PODNameSpace == podNameSpace {
|
||||
if ep == nil {
|
||||
ep = endpoint
|
||||
} else {
|
||||
return nil, errMultipleEndpointsFound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ep == nil {
|
||||
return nil, errEndpointNotFound
|
||||
}
|
||||
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
//
|
||||
// Endpoint
|
||||
//
|
||||
|
@ -135,16 +168,22 @@ func (nw *network) getEndpoint(endpointId string) (*endpoint, error) {
|
|||
// GetInfo returns information about the endpoint.
|
||||
func (ep *endpoint) getInfo() *EndpointInfo {
|
||||
info := &EndpointInfo{
|
||||
Id: ep.Id,
|
||||
IPAddresses: ep.IPAddresses,
|
||||
InfraVnetIP: ep.InfraVnetIP,
|
||||
Data: make(map[string]interface{}),
|
||||
MacAddress: ep.MacAddress,
|
||||
SandboxKey: ep.SandboxKey,
|
||||
IfIndex: 0, // Azure CNI supports only one interface
|
||||
DNS: ep.DNS,
|
||||
EnableSnatOnHost: ep.EnableSnatOnHost,
|
||||
EnableInfraVnet: ep.EnableInfraVnet,
|
||||
Id: ep.Id,
|
||||
IPAddresses: ep.IPAddresses,
|
||||
InfraVnetIP: ep.InfraVnetIP,
|
||||
Data: make(map[string]interface{}),
|
||||
MacAddress: ep.MacAddress,
|
||||
SandboxKey: ep.SandboxKey,
|
||||
IfIndex: 0, // Azure CNI supports only one interface
|
||||
DNS: ep.DNS,
|
||||
EnableSnatOnHost: ep.EnableSnatOnHost,
|
||||
EnableInfraVnet: ep.EnableInfraVnet,
|
||||
EnableMultiTenancy: ep.EnableMultitenancy,
|
||||
IfName: ep.IfName,
|
||||
ContainerID: ep.ContainerID,
|
||||
NetNsPath: ep.NetworkNameSpace,
|
||||
PODName: ep.PODName,
|
||||
PODNameSpace: ep.PODNameSpace,
|
||||
}
|
||||
|
||||
for _, route := range ep.Routes {
|
||||
|
@ -186,3 +225,35 @@ func (ep *endpoint) detach() error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateEndpoint updates an existing endpoint in the network.
|
||||
func (nw *network) updateEndpoint(exsitingEpInfo *EndpointInfo, targetEpInfo *EndpointInfo) (*endpoint, error) {
|
||||
var err error
|
||||
|
||||
log.Printf("[net] Updating existing endpoint [%+v] in network %v to target [%+v].", exsitingEpInfo, nw.Id, targetEpInfo)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.Printf("[net] Failed to update endpoint %v, err:%v.", exsitingEpInfo.Id, err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf("Trying to retrieve endpoint id %v", exsitingEpInfo.Id)
|
||||
|
||||
ep := nw.Endpoints[exsitingEpInfo.Id]
|
||||
if ep == nil {
|
||||
return nil, errEndpointNotFound
|
||||
}
|
||||
|
||||
log.Printf("[net] Retrieved endpoint to update %+v.", ep)
|
||||
|
||||
// Call the platform implementation.
|
||||
ep, err = nw.updateEndpointImpl(exsitingEpInfo, targetEpInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update routes for existing endpoint
|
||||
nw.Endpoints[exsitingEpInfo.Id].Routes = ep.Routes
|
||||
|
||||
return ep, nil
|
||||
}
|
||||
|
|
|
@ -96,14 +96,15 @@ func (nw *network) newEndpointImpl(epInfo *EndpointInfo) (*endpoint, error) {
|
|||
if err != nil {
|
||||
log.Printf("CNI error. Delete Endpoint %v and rules that are created.", contIfName)
|
||||
endpt := &endpoint{
|
||||
Id: epInfo.Id,
|
||||
IfName: contIfName,
|
||||
HostIfName: hostIfName,
|
||||
IPAddresses: epInfo.IPAddresses,
|
||||
Gateways: []net.IP{nw.extIf.IPv4Gateway},
|
||||
DNS: epInfo.DNS,
|
||||
VlanID: vlanid,
|
||||
EnableSnatOnHost: epInfo.EnableSnatOnHost,
|
||||
Id: epInfo.Id,
|
||||
IfName: contIfName,
|
||||
HostIfName: hostIfName,
|
||||
IPAddresses: epInfo.IPAddresses,
|
||||
Gateways: []net.IP{nw.extIf.IPv4Gateway},
|
||||
DNS: epInfo.DNS,
|
||||
VlanID: vlanid,
|
||||
EnableSnatOnHost: epInfo.EnableSnatOnHost,
|
||||
EnableMultitenancy: epInfo.EnableMultiTenancy,
|
||||
}
|
||||
|
||||
if containerIf != nil {
|
||||
|
@ -171,17 +172,22 @@ func (nw *network) newEndpointImpl(epInfo *EndpointInfo) (*endpoint, error) {
|
|||
|
||||
// Create the endpoint object.
|
||||
ep = &endpoint{
|
||||
Id: epInfo.Id,
|
||||
IfName: epInfo.IfName,
|
||||
HostIfName: hostIfName,
|
||||
MacAddress: containerIf.HardwareAddr,
|
||||
InfraVnetIP: epInfo.InfraVnetIP,
|
||||
IPAddresses: epInfo.IPAddresses,
|
||||
Gateways: []net.IP{nw.extIf.IPv4Gateway},
|
||||
DNS: epInfo.DNS,
|
||||
VlanID: vlanid,
|
||||
EnableSnatOnHost: epInfo.EnableSnatOnHost,
|
||||
EnableInfraVnet: epInfo.EnableInfraVnet,
|
||||
Id: epInfo.Id,
|
||||
IfName: epInfo.IfName,
|
||||
HostIfName: hostIfName,
|
||||
MacAddress: containerIf.HardwareAddr,
|
||||
InfraVnetIP: epInfo.InfraVnetIP,
|
||||
IPAddresses: epInfo.IPAddresses,
|
||||
Gateways: []net.IP{nw.extIf.IPv4Gateway},
|
||||
DNS: epInfo.DNS,
|
||||
VlanID: vlanid,
|
||||
EnableSnatOnHost: epInfo.EnableSnatOnHost,
|
||||
EnableInfraVnet: epInfo.EnableInfraVnet,
|
||||
EnableMultitenancy: epInfo.EnableMultiTenancy,
|
||||
NetworkNameSpace: epInfo.NetNsPath,
|
||||
ContainerID: epInfo.ContainerID,
|
||||
PODName: epInfo.PODName,
|
||||
PODNameSpace: epInfo.PODNameSpace,
|
||||
}
|
||||
|
||||
for _, route := range epInfo.Routes {
|
||||
|
@ -253,7 +259,7 @@ func deleteRoutes(interfaceName string, routes []RouteInfo) error {
|
|||
interfaceIf, _ := net.InterfaceByName(interfaceName)
|
||||
|
||||
for _, route := range routes {
|
||||
log.Printf("[ovs] Adding IP route %+v to link %v.", route, interfaceName)
|
||||
log.Printf("[ovs] Deleting IP route %+v from link %v.", route, interfaceName)
|
||||
|
||||
if route.DevName != "" {
|
||||
devIf, _ := net.InterfaceByName(route.DevName)
|
||||
|
@ -276,3 +282,137 @@ func deleteRoutes(interfaceName string, routes []RouteInfo) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateEndpointImpl updates an existing endpoint in the network.
|
||||
func (nw *network) updateEndpointImpl(existingEpInfo *EndpointInfo, targetEpInfo *EndpointInfo) (*endpoint, error) {
|
||||
var ns *Namespace
|
||||
var ep *endpoint
|
||||
var err error
|
||||
|
||||
existingEpFromRepository := nw.Endpoints[existingEpInfo.Id]
|
||||
log.Printf("[updateEndpointImpl] Going to retrieve endpoint with Id %+v to update.", existingEpInfo.Id)
|
||||
if existingEpFromRepository == nil {
|
||||
log.Printf("[updateEndpointImpl] Endpoint cannot be updated as it does not exist.")
|
||||
err = errEndpointNotFound
|
||||
return nil, err
|
||||
}
|
||||
|
||||
netns := existingEpFromRepository.NetworkNameSpace
|
||||
// Network namespace for the container interface has to be specified
|
||||
if netns != "" {
|
||||
// Open the network namespace.
|
||||
log.Printf("[updateEndpointImpl] Opening netns %v.", netns)
|
||||
ns, err = OpenNamespace(netns)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ns.Close()
|
||||
|
||||
// Enter the container network namespace.
|
||||
log.Printf("[updateEndpointImpl] Entering netns %v.", netns)
|
||||
if err = ns.Enter(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return to host network namespace.
|
||||
defer func() {
|
||||
log.Printf("[updateEndpointImpl] Exiting netns %v.", netns)
|
||||
if err := ns.Exit(); err != nil {
|
||||
log.Printf("[updateEndpointImpl] Failed to exit netns, err:%v.", err)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
log.Printf("[updateEndpointImpl] Endpoint cannot be updated as the network namespace does not exist: Epid: %v", existingEpInfo.Id)
|
||||
err = errNamespaceNotFound
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[updateEndpointImpl] Going to update routes in netns %v.", netns)
|
||||
if err = updateRoutes(existingEpInfo, targetEpInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the endpoint object.
|
||||
ep = &endpoint{
|
||||
Id: existingEpInfo.Id,
|
||||
}
|
||||
|
||||
// Update existing endpoint state with the new routes to persist
|
||||
for _, route := range targetEpInfo.Routes {
|
||||
ep.Routes = append(ep.Routes, route)
|
||||
}
|
||||
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
func updateRoutes(existingEp *EndpointInfo, targetEp *EndpointInfo) error {
|
||||
log.Printf("Updating routes for the endpoint %+v.", existingEp)
|
||||
log.Printf("Target endpoint is %+v", targetEp)
|
||||
|
||||
existingRoutes := make(map[string]RouteInfo)
|
||||
targetRoutes := make(map[string]RouteInfo)
|
||||
var tobeDeletedRoutes []RouteInfo
|
||||
var tobeAddedRoutes []RouteInfo
|
||||
|
||||
// we should not remove default route from container if it exists
|
||||
// we do not support enable/disable snat for now
|
||||
defaultDst := net.ParseIP("0.0.0.0")
|
||||
|
||||
log.Printf("Going to collect routes and skip default and infravnet routes if applicable.")
|
||||
log.Printf("Key for default route: %+v", defaultDst.String())
|
||||
|
||||
infraVnetKey := ""
|
||||
if targetEp.EnableInfraVnet {
|
||||
infraVnetSubnet := targetEp.InfraVnetAddressSpace
|
||||
if infraVnetSubnet != "" {
|
||||
infraVnetKey = strings.Split(infraVnetSubnet, "/")[0]
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Key for route to infra vnet: %+v", infraVnetKey)
|
||||
for _, route := range existingEp.Routes {
|
||||
destination := route.Dst.IP.String()
|
||||
log.Printf("Checking destination as %+v to skip or not", destination)
|
||||
isDefaultRoute := destination == defaultDst.String()
|
||||
isInfraVnetRoute := targetEp.EnableInfraVnet && (destination == infraVnetKey)
|
||||
if !isDefaultRoute && !isInfraVnetRoute {
|
||||
existingRoutes[route.Dst.String()] = route
|
||||
log.Printf("%+v was skipped", destination)
|
||||
}
|
||||
}
|
||||
|
||||
for _, route := range targetEp.Routes {
|
||||
targetRoutes[route.Dst.String()] = route
|
||||
}
|
||||
|
||||
for _, existingRoute := range existingRoutes {
|
||||
dst := existingRoute.Dst.String()
|
||||
if _, ok := targetRoutes[dst]; !ok {
|
||||
tobeDeletedRoutes = append(tobeDeletedRoutes, existingRoute)
|
||||
log.Printf("Adding following route to the tobeDeleted list: %+v", existingRoute)
|
||||
}
|
||||
}
|
||||
|
||||
for _, targetRoute := range targetRoutes {
|
||||
dst := targetRoute.Dst.String()
|
||||
if _, ok := existingRoutes[dst]; !ok {
|
||||
tobeAddedRoutes = append(tobeAddedRoutes, targetRoute)
|
||||
log.Printf("Adding following route to the tobeAdded list: %+v", targetRoute)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
err := deleteRoutes(existingEp.IfName, tobeDeletedRoutes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = addRoutes(existingEp.IfName, tobeAddedRoutes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Successfully updated routes for the endpoint %+v using target: %+v", existingEp, targetEp)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -128,3 +128,8 @@ func (nw *network) deleteEndpointImpl(ep *endpoint) error {
|
|||
func (ep *endpoint) getInfoImpl(epInfo *EndpointInfo) {
|
||||
epInfo.Data["hnsid"] = ep.HnsId
|
||||
}
|
||||
|
||||
// updateEndpointImpl in windows does nothing for now
|
||||
func (nw *network) updateEndpointImpl(existingEpInfo *EndpointInfo, targetEpInfo *EndpointInfo) (*endpoint, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -61,8 +61,10 @@ type NetworkManager interface {
|
|||
CreateEndpoint(networkId string, epInfo *EndpointInfo) error
|
||||
DeleteEndpoint(networkId string, endpointId string) error
|
||||
GetEndpointInfo(networkId string, endpointId string) (*EndpointInfo, error)
|
||||
GetEndpointInfoBasedOnPODDetails(networkId string, podName string, podNameSpace string) (*EndpointInfo, error)
|
||||
AttachEndpoint(networkId string, endpointId string, sandboxKey string) (*endpoint, error)
|
||||
DetachEndpoint(networkId string, endpointId string) error
|
||||
UpdateEndpoint(networkId string, existingEpInfo *EndpointInfo, targetEpInfo *EndpointInfo) error
|
||||
}
|
||||
|
||||
// Creates a new network manager.
|
||||
|
@ -153,11 +155,11 @@ func (nm *networkManager) restore() error {
|
|||
|
||||
log.Printf("[net] Restored state, %+v\n", nm)
|
||||
for _, extIf := range nm.ExternalInterfaces {
|
||||
log.Printf("External Interface %v", extIf)
|
||||
log.Printf("External Interface %+v", extIf)
|
||||
for _, nw := range extIf.Networks {
|
||||
log.Printf("network %v", nw)
|
||||
log.Printf("network %+v", nw)
|
||||
for _, ep := range nw.Endpoints {
|
||||
log.Printf("endpoint %v", ep)
|
||||
log.Printf("endpoint %+v", ep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -341,6 +343,25 @@ func (nm *networkManager) GetEndpointInfo(networkId string, endpointId string) (
|
|||
return ep.getInfo(), nil
|
||||
}
|
||||
|
||||
// GetEndpointInfoBasedOnPODDetails returns information about the given endpoint.
|
||||
// It returns an error if a single pod has multiple endpoints.
|
||||
func (nm *networkManager) GetEndpointInfoBasedOnPODDetails(networkID string, podName string, podNameSpace string) (*EndpointInfo, error) {
|
||||
nm.Lock()
|
||||
defer nm.Unlock()
|
||||
|
||||
nw, err := nm.getNetwork(networkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ep, err := nw.getEndpointByPOD(podName, podNameSpace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ep.getInfo(), nil
|
||||
}
|
||||
|
||||
// AttachEndpoint attaches an endpoint to a sandbox.
|
||||
func (nm *networkManager) AttachEndpoint(networkId string, endpointId string, sandboxKey string) (*endpoint, error) {
|
||||
nm.Lock()
|
||||
|
@ -396,3 +417,26 @@ func (nm *networkManager) DetachEndpoint(networkId string, endpointId string) er
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEndpoint updates an existing container endpoint.
|
||||
func (nm *networkManager) UpdateEndpoint(networkID string, existingEpInfo *EndpointInfo, targetEpInfo *EndpointInfo) error {
|
||||
nm.Lock()
|
||||
defer nm.Unlock()
|
||||
|
||||
nw, err := nm.getNetwork(networkID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = nw.updateEndpoint(existingEpInfo, targetEpInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = nm.save()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче