|
4 | 4 | package network |
5 | 5 |
|
6 | 6 | import ( |
| 7 | + "encoding/json" |
7 | 8 | "fmt" |
8 | 9 | "net" |
9 | 10 |
|
10 | 11 | "github.com/Azure/azure-container-networking/cni" |
11 | 12 | "github.com/Azure/azure-container-networking/cns" |
| 13 | + "github.com/Azure/azure-container-networking/cns/cnsclient" |
12 | 14 | "github.com/Azure/azure-container-networking/common" |
13 | 15 | "github.com/Azure/azure-container-networking/log" |
14 | 16 | "github.com/Azure/azure-container-networking/network" |
@@ -397,17 +399,20 @@ func (plugin *netPlugin) Add(args *cniSkel.CmdArgs) error { |
397 | 399 | } |
398 | 400 |
|
399 | 401 | epInfo = &network.EndpointInfo{ |
400 | | - Id: endpointId, |
401 | | - ContainerID: args.ContainerID, |
402 | | - NetNsPath: args.Netns, |
403 | | - IfName: args.IfName, |
404 | | - EnableSnatOnHost: nwCfg.EnableSnatOnHost, |
405 | | - EnableInfraVnet: enableInfraVnet, |
406 | | - Data: make(map[string]interface{}), |
407 | | - DNS: epDNSInfo, |
408 | | - Policies: policies, |
409 | | - } |
410 | | - |
| 402 | + Id: endpointId, |
| 403 | + ContainerID: args.ContainerID, |
| 404 | + NetNsPath: args.Netns, |
| 405 | + IfName: args.IfName, |
| 406 | + Data: make(map[string]interface{}), |
| 407 | + DNS: epDNSInfo, |
| 408 | + Policies: policies, |
| 409 | + EnableSnatOnHost: nwCfg.EnableSnatOnHost, |
| 410 | + EnableMultiTenancy: nwCfg.MultiTenancy, |
| 411 | + EnableInfraVnet: enableInfraVnet, |
| 412 | + PODName: k8sPodName, |
| 413 | + PODNameSpace: k8sNamespace, |
| 414 | + } |
| 415 | + |
411 | 416 | epPolicies := getPoliciesFromRuntimeCfg(nwCfg) |
412 | 417 | for _, epPolicy := range epPolicies { |
413 | 418 | epInfo.Policies = append(epInfo.Policies, epPolicy) |
@@ -599,3 +604,159 @@ func (plugin *netPlugin) Delete(args *cniSkel.CmdArgs) error { |
599 | 604 |
|
600 | 605 | return nil |
601 | 606 | } |
| 607 | + |
| 608 | +// Update handles CNI update commands. |
| 609 | +// Update is only supported for multitenancy and to update routes. |
| 610 | +func (plugin *netPlugin) Update(args *cniSkel.CmdArgs) error { |
| 611 | + var ( |
| 612 | + result *cniTypesCurr.Result |
| 613 | + err error |
| 614 | + nwCfg *cni.NetworkConfig |
| 615 | + existingEpInfo *network.EndpointInfo |
| 616 | + ) |
| 617 | + |
| 618 | + log.Printf("[cni-net] Processing UPDATE command with args {Netns:%v Args:%v Path:%v}.", |
| 619 | + args.Netns, args.Args, args.Path) |
| 620 | + |
| 621 | + // Parse network configuration from stdin. |
| 622 | + nwCfg, err = cni.ParseNetworkConfig(args.StdinData) |
| 623 | + if err != nil { |
| 624 | + err = plugin.Errorf("Failed to parse network configuration: %v.", err) |
| 625 | + return err |
| 626 | + } |
| 627 | + |
| 628 | + log.Printf("[cni-net] Read network configuration %+v.", nwCfg) |
| 629 | + |
| 630 | + defer func() { |
| 631 | + if result == nil { |
| 632 | + result = &cniTypesCurr.Result{} |
| 633 | + } |
| 634 | + |
| 635 | + // Convert result to the requested CNI version. |
| 636 | + res, vererr := result.GetAsVersion(nwCfg.CNIVersion) |
| 637 | + if vererr != nil { |
| 638 | + log.Printf("GetAsVersion failed with error %v", vererr) |
| 639 | + plugin.Error(vererr) |
| 640 | + } |
| 641 | + |
| 642 | + if err == nil && res != nil { |
| 643 | + // Output the result to stdout. |
| 644 | + res.Print() |
| 645 | + } |
| 646 | + |
| 647 | + log.Printf("[cni-net] UPDATE command completed with result:%+v err:%v.", result, err) |
| 648 | + }() |
| 649 | + |
| 650 | + // Parse Pod arguments. |
| 651 | + podCfg, err := cni.ParseCniArgs(args.Args) |
| 652 | + if err != nil { |
| 653 | + log.Printf("Error while parsing CNI Args during UPDATE %v", err) |
| 654 | + return err |
| 655 | + } |
| 656 | + |
| 657 | + k8sNamespace := string(podCfg.K8S_POD_NAMESPACE) |
| 658 | + if len(k8sNamespace) == 0 { |
| 659 | + errMsg := "Required parameter Pod Namespace not specified in CNI Args during UPDATE" |
| 660 | + log.Printf(errMsg) |
| 661 | + return plugin.Errorf(errMsg) |
| 662 | + } |
| 663 | + |
| 664 | + k8sPodName := string(podCfg.K8S_POD_NAME) |
| 665 | + if len(k8sPodName) == 0 { |
| 666 | + errMsg := "Required parameter Pod Name not specified in CNI Args during UPDATE" |
| 667 | + log.Printf(errMsg) |
| 668 | + return plugin.Errorf(errMsg) |
| 669 | + } |
| 670 | + |
| 671 | + // Initialize values from network config. |
| 672 | + networkID := nwCfg.Name |
| 673 | + |
| 674 | + // Query the network. |
| 675 | + _, err = plugin.nm.GetNetworkInfo(networkID) |
| 676 | + if err != nil { |
| 677 | + errMsg := fmt.Sprintf("Failed to query network during CNI UPDATE: %v", err) |
| 678 | + log.Printf(errMsg) |
| 679 | + return plugin.Errorf(errMsg) |
| 680 | + } |
| 681 | + |
| 682 | + // Query the existing endpoint since this is an update. |
| 683 | + // Right now, we do not support updating pods that have multiple endpoints. |
| 684 | + existingEpInfo, err = plugin.nm.GetEndpointInfoBasedOnPODDetails(networkID, k8sPodName, k8sNamespace) |
| 685 | + if err != nil { |
| 686 | + plugin.Errorf("Failed to retrieve target endpoint for CNI UPDATE [name=%v, namespace=%v]: %v", k8sPodName, k8sNamespace, err) |
| 687 | + return err |
| 688 | + } else { |
| 689 | + log.Printf("Retrieved existing endpoint from state that may get update: %+v", existingEpInfo) |
| 690 | + } |
| 691 | + |
| 692 | + // now query CNS to get the target routes that should be there in the networknamespace (as a result of update) |
| 693 | + log.Printf("Going to collect target routes for [name=%v, namespace=%v] from CNS.", k8sPodName, k8sNamespace) |
| 694 | + cnsClient, err := cnsclient.NewCnsClient(nwCfg.CNSUrl) |
| 695 | + if err != nil { |
| 696 | + log.Printf("Initializing CNS client error in CNI Update%v", err) |
| 697 | + log.Printf(err.Error()) |
| 698 | + return plugin.Errorf(err.Error()) |
| 699 | + } |
| 700 | + |
| 701 | + // create struct with info for target POD |
| 702 | + podInfo := cns.KubernetesPodInfo{PodName: k8sPodName, PodNamespace: k8sNamespace} |
| 703 | + orchestratorContext, err := json.Marshal(podInfo) |
| 704 | + if err != nil { |
| 705 | + log.Printf("Marshalling KubernetesPodInfo failed with %v", err) |
| 706 | + return plugin.Errorf(err.Error()) |
| 707 | + } |
| 708 | + |
| 709 | + targetNetworkConfig, err := cnsClient.GetNetworkConfiguration(orchestratorContext) |
| 710 | + if err != nil { |
| 711 | + log.Printf("GetNetworkConfiguration failed with %v", err) |
| 712 | + return plugin.Errorf(err.Error()) |
| 713 | + } |
| 714 | + |
| 715 | + log.Printf("Network config received from cns for [name=%v, namespace=%v] is as follows -> %+v", k8sPodName, k8sNamespace, targetNetworkConfig) |
| 716 | + targetEpInfo := &network.EndpointInfo{} |
| 717 | + |
| 718 | + // get the target routes that should replace existingEpInfo.Routes inside the network namespace |
| 719 | + log.Printf("Going to collect target routes for [name=%v, namespace=%v] from targetNetworkConfig.", k8sPodName, k8sNamespace) |
| 720 | + if targetNetworkConfig.Routes != nil && len(targetNetworkConfig.Routes) > 0 { |
| 721 | + for _, route := range targetNetworkConfig.Routes { |
| 722 | + log.Printf("Adding route from routes to targetEpInfo %+v", route) |
| 723 | + _, dstIPNet, _ := net.ParseCIDR(route.IPAddress) |
| 724 | + gwIP := net.ParseIP(route.GatewayIPAddress) |
| 725 | + targetEpInfo.Routes = append(targetEpInfo.Routes, network.RouteInfo{Dst: *dstIPNet, Gw: gwIP, DevName: existingEpInfo.IfName}) |
| 726 | + log.Printf("Successfully added route from routes to targetEpInfo %+v", route) |
| 727 | + } |
| 728 | + } |
| 729 | + |
| 730 | + log.Printf("Going to collect target routes based on Cnetaddressspace for [name=%v, namespace=%v] from targetNetworkConfig.", k8sPodName, k8sNamespace) |
| 731 | + ipconfig := targetNetworkConfig.IPConfiguration |
| 732 | + for _, ipRouteSubnet := range targetNetworkConfig.CnetAddressSpace { |
| 733 | + log.Printf("Adding route from cnetAddressspace to targetEpInfo %+v", ipRouteSubnet) |
| 734 | + dstIPNet := net.IPNet{IP: net.ParseIP(ipRouteSubnet.IPAddress), Mask: net.CIDRMask(int(ipRouteSubnet.PrefixLength), 32)} |
| 735 | + gwIP := net.ParseIP(ipconfig.GatewayIPAddress) |
| 736 | + route := network.RouteInfo{Dst: dstIPNet, Gw: gwIP, DevName: existingEpInfo.IfName} |
| 737 | + targetEpInfo.Routes = append(targetEpInfo.Routes, route) |
| 738 | + log.Printf("Successfully added route from cnetAddressspace to targetEpInfo %+v", ipRouteSubnet) |
| 739 | + } |
| 740 | + |
| 741 | + log.Printf("Finished collecting new routes in targetEpInfo as follows: %+v", targetEpInfo.Routes) |
| 742 | + log.Printf("Now saving existing infravnetaddress space if needed.") |
| 743 | + for _, ns := range nwCfg.PodNamespaceForDualNetwork { |
| 744 | + if k8sNamespace == ns { |
| 745 | + targetEpInfo.EnableInfraVnet = true |
| 746 | + targetEpInfo.InfraVnetAddressSpace = nwCfg.InfraVnetAddressSpace |
| 747 | + log.Printf("Saving infravnet address space %s for [%s-%s]", |
| 748 | + targetEpInfo.InfraVnetAddressSpace, existingEpInfo.PODNameSpace, existingEpInfo.PODName) |
| 749 | + break |
| 750 | + } |
| 751 | + } |
| 752 | + |
| 753 | + // Update the endpoint. |
| 754 | + log.Printf("Now updating existing endpoint %v with targetNetworkConfig %+v.", existingEpInfo.Id, targetNetworkConfig) |
| 755 | + err = plugin.nm.UpdateEndpoint(networkID, existingEpInfo, targetEpInfo) |
| 756 | + if err != nil { |
| 757 | + err = plugin.Errorf("Failed to update endpoint: %v", err) |
| 758 | + return err |
| 759 | + } |
| 760 | + |
| 761 | + return nil |
| 762 | +} |
0 commit comments