func cmdDel(args *skel.CmdArgs) error { conf := utils.NetConf{} if err := json.Unmarshal(args.StdinData, &conf); err != nil { return fmt.Errorf("failed to load netconf: %v", err) } utils.ConfigureLogging(conf.LogLevel) calicoClient, err := utils.CreateClient(conf) if err != nil { return err } // Release the IP address by using the handle - which is workloadID. workloadID, _, err := utils.GetIdentifiers(args) if err != nil { return err } logger := utils.CreateContextLogger(workloadID) logger.Info("Releasing address using workloadID") if err := calicoClient.IPAM().ReleaseByHandle(workloadID); err != nil { return err } logger.Info("Released address using workloadID") return nil }
// CmdAddK8s performs the "ADD" operation on a kubernetes pod // Having kubernetes code in its own file avoids polluting the mainline code. It's expected that the kubernetes case will // more special casing than the mainline code. func CmdAddK8s(args *skel.CmdArgs, conf utils.NetConf, hostname string, calicoClient *calicoclient.Client, endpoint *api.WorkloadEndpoint) (*types.Result, error) { var err error var result *types.Result k8sArgs := utils.K8sArgs{} err = types.LoadArgs(args.Args, &k8sArgs) if err != nil { return nil, err } utils.ConfigureLogging(conf.LogLevel) workload, orchestrator, err := utils.GetIdentifiers(args) if err != nil { return nil, err } logger := utils.CreateContextLogger(workload) logger.WithFields(log.Fields{ "Orchestrator": orchestrator, "Node": hostname, }).Info("Extracted identifiers for CmdAddK8s") if endpoint != nil { // This happens when Docker or the node restarts. K8s calls CNI with the same parameters as before. // Do the networking (since the network namespace was destroyed and recreated). // There's an existing endpoint - no need to create another. Find the IP address from the endpoint // and use that in the response. result, err = utils.CreateResultFromEndpoint(endpoint) if err != nil { return nil, err } logger.WithField("result", result).Debug("Created result from existing endpoint") // If any labels changed whilst the container was being restarted, they will be picked up by the policy // controller so there's no need to update the labels here. } else { client, err := newK8sClient(conf, logger) if err != nil { return nil, err } logger.WithField("client", client).Debug("Created Kubernetes client") if conf.IPAM.Type == "host-local" && strings.EqualFold(conf.IPAM.Subnet, "usePodCidr") { // We've been told to use the "host-local" IPAM plugin with the Kubernetes podCidr for this node. // Replace the actual value in the args.StdinData as that's what's passed to the IPAM plugin. fmt.Fprintf(os.Stderr, "Calico CNI fetching podCidr from Kubernetes\n") var stdinData map[string]interface{} if err := json.Unmarshal(args.StdinData, &stdinData); err != nil { return nil, err } podCidr, err := getPodCidr(client, conf, hostname) if err != nil { return nil, err } logger.WithField("podCidr", podCidr).Info("Fetched podCidr") stdinData["ipam"].(map[string]interface{})["subnet"] = podCidr fmt.Fprintf(os.Stderr, "Calico CNI passing podCidr to host-local IPAM: %s\n", podCidr) args.StdinData, err = json.Marshal(stdinData) if err != nil { return nil, err } logger.WithField("stdin", args.StdinData).Debug("Updated stdin data") } // Run the IPAM plugin logger.Debugf("Calling IPAM plugin %s", conf.IPAM.Type) result, err = ipam.ExecAdd(conf.IPAM.Type, args.StdinData) if err != nil { return nil, err } logger.Debugf("IPAM plugin returned: %+v", result) // Create the endpoint object and configure it. endpoint = api.NewWorkloadEndpoint() endpoint.Metadata.Name = args.IfName endpoint.Metadata.Node = hostname endpoint.Metadata.Orchestrator = orchestrator endpoint.Metadata.Workload = workload endpoint.Metadata.Labels = make(map[string]string) // Set the profileID according to whether Kubernetes policy is required. // If it's not, then just use the network name (which is the normal behavior) // otherwise use one based on the Kubernetes pod's Namespace. if conf.Policy.PolicyType == "k8s" { endpoint.Spec.Profiles = []string{fmt.Sprintf("k8s_ns.%s", k8sArgs.K8S_POD_NAMESPACE)} } else { endpoint.Spec.Profiles = []string{conf.Name} } // Populate the endpoint with the output from the IPAM plugin. if err = utils.PopulateEndpointNets(endpoint, result); err != nil { // Cleanup IP allocation and return the error. utils.ReleaseIPAllocation(logger, conf.IPAM.Type, args.StdinData) return nil, err } logger.WithField("endpoint", endpoint).Info("Populated endpoint") // Only attempt to fetch the labels from Kubernetes if the policy type has been set to "k8s" // This allows users to run the plugin under Kubernetes without needing it to access the Kubernetes API if conf.Policy.PolicyType == "k8s" { labels, err := getK8sLabels(client, k8sArgs) if err != nil { // Cleanup IP allocation and return the error. utils.ReleaseIPAllocation(logger, conf.IPAM.Type, args.StdinData) return nil, err } logger.WithField("labels", labels).Info("Fetched K8s labels") endpoint.Metadata.Labels = labels } } fmt.Fprintf(os.Stderr, "Calico CNI using IPs: %s\n", endpoint.Spec.IPNetworks) // Whether the endpoint existed or not, the veth needs (re)creating. hostVethName := k8sbackend.VethNameForWorkload(workload) _, contVethMac, err := utils.DoNetworking(args, conf, result, logger, hostVethName) if err != nil { // Cleanup IP allocation and return the error. logger.Errorf("Error setting up networking: %s", err) utils.ReleaseIPAllocation(logger, conf.IPAM.Type, args.StdinData) return nil, err } mac, err := net.ParseMAC(contVethMac) if err != nil { // Cleanup IP allocation and return the error. logger.Errorf("Error parsing MAC (%s): %s", contVethMac, err) utils.ReleaseIPAllocation(logger, conf.IPAM.Type, args.StdinData) return nil, err } endpoint.Spec.MAC = &cnet.MAC{HardwareAddr: mac} endpoint.Spec.InterfaceName = hostVethName logger.WithField("endpoint", endpoint).Info("Added Mac and interface name to endpoint") // Write the endpoint object (either the newly created one, or the updated one) if _, err := calicoClient.WorkloadEndpoints().Apply(endpoint); err != nil { // Cleanup IP allocation and return the error. utils.ReleaseIPAllocation(logger, conf.IPAM.Type, args.StdinData) return nil, err } logger.Info("Wrote updated endpoint to datastore") return result, nil }
func cmdAdd(args *skel.CmdArgs) error { conf := utils.NetConf{} if err := json.Unmarshal(args.StdinData, &conf); err != nil { return fmt.Errorf("failed to load netconf: %v", err) } utils.ConfigureLogging(conf.LogLevel) calicoClient, err := utils.CreateClient(conf) if err != nil { return err } workloadID, _, err := utils.GetIdentifiers(args) if err != nil { return err } logger := utils.CreateContextLogger(workloadID) ipamArgs := ipamArgs{} if err = types.LoadArgs(args.Args, &ipamArgs); err != nil { return err } r := &types.Result{} if ipamArgs.IP != nil { fmt.Fprintf(os.Stderr, "Calico CNI IPAM request IP: %v\n", ipamArgs.IP) // The hostname will be defaulted to the actual hostname if cong.Hostname is empty assignArgs := client.AssignIPArgs{IP: cnet.IP{ipamArgs.IP}, HandleID: &workloadID, Hostname: conf.Hostname} logger.WithField("assignArgs", assignArgs).Info("Assigning provided IP") err := calicoClient.IPAM().AssignIP(assignArgs) if err != nil { return err } ipV4Network := net.IPNet{IP: ipamArgs.IP, Mask: net.CIDRMask(32, 32)} r.IP4 = &types.IPConfig{IP: ipV4Network} logger.WithField("result.IP4", r.IP4).Info("Result IPv4") } else { // Default to assigning an IPv4 address num4 := 1 if conf.IPAM.AssignIpv4 != nil && *conf.IPAM.AssignIpv4 == "false" { num4 = 0 } // Default to NOT assigning an IPv6 address num6 := 0 if conf.IPAM.AssignIpv6 != nil && *conf.IPAM.AssignIpv6 == "true" { num6 = 1 } fmt.Fprintf(os.Stderr, "Calico CNI IPAM request count IPv4=%d IPv6=%d\n", num4, num6) assignArgs := client.AutoAssignArgs{Num4: num4, Num6: num6, HandleID: &workloadID, Hostname: conf.Hostname} logger.WithField("assignArgs", assignArgs).Info("Auto assigning IP") assignedV4, assignedV6, err := calicoClient.IPAM().AutoAssign(assignArgs) fmt.Fprintf(os.Stderr, "Calico CNI IPAM assigned addresses IPv4=%v IPv6=%v\n", assignedV4, assignedV6) if err != nil { return err } if num4 == 1 { if len(assignedV4) != num4 { return fmt.Errorf("Failed to request %d IPv4 addresses. IPAM allocated only %d.", num4, len(assignedV4)) } ipV4Network := net.IPNet{IP: assignedV4[0].IP, Mask: net.CIDRMask(32, 32)} r.IP4 = &types.IPConfig{IP: ipV4Network} } if num6 == 1 { if len(assignedV6) != num6 { return fmt.Errorf("Failed to request %d IPv6 addresses. IPAM allocated only %d.", num6, len(assignedV6)) } ipV6Network := net.IPNet{IP: assignedV6[0].IP, Mask: net.CIDRMask(128, 128)} r.IP6 = &types.IPConfig{IP: ipV6Network} } logger.WithFields(log.Fields{"result.IP4": r.IP4, "result.IP6": r.IP6}).Info("IPAM Result") } return r.Print() }