func cmdDel(args *skel.CmdArgs) error { n, err := loadConf(args.StdinData) if err != nil { return err } err = ipam.ExecDel(n.IPAM.Type, args.StdinData) if err != nil { return err } linkFactory := &links.Factory{Netlinker: nl.Netlink} containerNS := namespace.NewNamespace(args.Netns) err = containerNS.Execute(func(ns *os.File) error { linkFactory.DeleteLinkByName(args.IfName) return nil }) if err != nil { return fmt.Errorf("failed to delete link in container namespace: %s", err) } sandboxRepo, err := getSandboxRepo() if err != nil { return fmt.Errorf("failed to open sandbox repository: %s", err) } sandboxNS, err := sandboxRepo.Get(fmt.Sprintf("vni-%d", vni)) if err != nil { return fmt.Errorf("failed to get sandbox namespace: %s", err) } var sandboxLinks []netlink.Link err = sandboxNS.Execute(func(ns *os.File) error { var err error sandboxLinks, err = linkFactory.ListLinks() return err }) if err != nil { return fmt.Errorf("failed to get sandbox links: %s", err) } for _, link := range sandboxLinks { if _, ok := link.(*netlink.Veth); ok { return nil // we still have a container attached } } err = sandboxNS.Destroy() if err != nil { return fmt.Errorf("failed to destroy sandbox namespace: %s", err) } return nil }
"os/exec" "path/filepath" "strings" "golang.org/x/sys/unix" "github.com/cloudfoundry-incubator/ducati-cni-plugins/lib/namespace" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Namespace", func() { Describe("Path", func() { It("returns the path used on the constructor", func() { ns := namespace.NewNamespace("/some/path/name") Expect(ns.Path()).To(Equal("/some/path/name")) }) }) Describe("Name", func() { It("returns the basename of the underlying path", func() { ns := namespace.NewNamespace("/var/run/netns/foo") Expect(ns.Name()).To(Equal("foo")) ns = namespace.NewNamespace("/foo") Expect(ns.Name()).To(Equal("foo")) ns = namespace.NewNamespace("/foo/bar") Expect(ns.Name()).To(Equal("bar")) })
func cmdAdd(args *skel.CmdArgs) error { netConf, err := loadConf(args.StdinData) if err != nil { return fmt.Errorf("loading config: %s", err) } if args.ContainerID == "" { return errors.New("CNI_CONTAINERID is required") } sandboxNS, err := getSandboxNS(fmt.Sprintf("vni-%d", vni)) if err != nil { return fmt.Errorf("getting vxlan sandbox: %s", err) } // run the IPAM plugin and get back the config to apply ipamResult, err := ipam.ExecAdd(netConf.IPAM.Type, args.StdinData) if err != nil { return fmt.Errorf("executing IPAM plugin: %s", err) } if ipamResult.IP4 == nil { return errors.New("IPAM plugin returned with missing IPv4 config") } linkFactory := &links.Factory{Netlinker: nl.Netlink} addressManager := &ip.AddressManager{Netlinker: nl.Netlink} containerNS := namespace.NewNamespace(args.Netns) containerNamespaceFile, err := containerNS.Open() if err != nil { return fmt.Errorf("opening container namespace: %s", err) } defer containerNamespaceFile.Close() sandboxNamespaceFile, err := sandboxNS.Open() if err != nil { return fmt.Errorf("opening sandbox namespace: %s", err) } defer sandboxNamespaceFile.Close() var sandboxLink netlink.Link err = containerNS.Execute(func(ns *os.File) error { var ( containerLink netlink.Link err error ) sandboxLink, containerLink, err = linkFactory.CreateVethPair(args.ContainerID, args.IfName, links.VxlanVethMTU) if err != nil { return fmt.Errorf("could not create veth pair: %s", err) } err = nl.Netlink.LinkSetNsFd(sandboxLink, int(sandboxNamespaceFile.Fd())) if err != nil { return fmt.Errorf("failed to move veth peer into sandbox: %s", err) } err = addressManager.AddAddress(containerLink, &ipamResult.IP4.IP) if err != nil { return fmt.Errorf("adding address to container veth end: %s", err) } err = nl.Netlink.LinkSetUp(containerLink) if err != nil { return fmt.Errorf("upping container veth end: %s", err) } for _, route := range ipamResult.IP4.Routes { // TODO supporting gateway assigned to a particular route nlRoute := &netlink.Route{ LinkIndex: containerLink.Attrs().Index, Scope: netlink.SCOPE_UNIVERSE, Dst: &route.Dst, Gw: ipamResult.IP4.Gateway, } err = nl.Netlink.RouteAdd(nlRoute) if err != nil { return fmt.Errorf("adding routes: %s", err) } } return nil }) if err != nil { return fmt.Errorf("configuring container namespace: %s", err) } vxlanName := fmt.Sprintf("vxlan%d", vni) var foundVxlanDevice bool err = sandboxNS.Execute(func(ns *os.File) error { if _, err := linkFactory.FindLink(vxlanName); err == nil { foundVxlanDevice = true } return nil }) if err != nil { return fmt.Errorf("failed attempting to find vxlan device in sandbox: %s", err) } // create vxlan device within host namespace if foundVxlanDevice == false { vxlan, err := linkFactory.CreateVxlan(vxlanName, vni) if err != nil { return fmt.Errorf("creating vxlan device on host namespace: %s", err) } // move vxlan device to sandbox namespace err = nl.Netlink.LinkSetNsFd(vxlan, int(sandboxNamespaceFile.Fd())) if err != nil { return fmt.Errorf("moving vxland device into sandbox: %s", err) } } err = sandboxNS.Execute(func(ns *os.File) error { vxlan, err := linkFactory.FindLink(vxlanName) if err != nil { return fmt.Errorf("finding vxlan device within sandbox: %s", err) } err = nl.Netlink.LinkSetUp(vxlan) if err != nil { return fmt.Errorf("upping sandbox veth end: %s", err) } vxlan, err = linkFactory.FindLink(vxlanName) if err != nil { return fmt.Errorf("finding vxlan device within sandbox after upping: %s", err) } sandboxLink, err = nl.Netlink.LinkByName(sandboxLink.Attrs().Name) if err != nil { return fmt.Errorf("find sandbox veth end by name: %s", err) } err = nl.Netlink.LinkSetUp(sandboxLink) if err != nil { return fmt.Errorf("upping sandbox veth end: %s", err) } var bridge *netlink.Bridge bridgeName := fmt.Sprintf("vxlanbr%d", vni) link, err := linkFactory.FindLink(bridgeName) if err != nil { bridge, err = linkFactory.CreateBridge(bridgeName, &net.IPNet{ IP: ipamResult.IP4.Gateway, Mask: ipamResult.IP4.IP.Mask, }) if err != nil { return fmt.Errorf("failed to create bridge: %s", err) } } else { bridge = link.(*netlink.Bridge) } err = nl.Netlink.LinkSetMaster(vxlan, bridge) if err != nil { return fmt.Errorf("slaving vxlan to bridge: %s", err) } err = nl.Netlink.LinkSetMaster(sandboxLink, bridge) if err != nil { return fmt.Errorf("slaving veth end to bridge: %s", err) } return nil }) if err != nil { return fmt.Errorf("configuring sandbox namespace: %s", err) } return ipamResult.Print() }
Network: "192.168.1.0/24", HostNetwork: "10.99.0.0/24", IPAM: IPAM{ Type: "host-local", Subnet: "192.168.1.0/24", Routes: []map[string]string{ {"dst": "0.0.0.0/0"}, }, }, } }) var execCNI = func(operation string, netConfig Config, containerNS namespace.Namespace, containerID, sandboxRepoDir string) { sandboxNS = namespace.NewNamespace(filepath.Join(sandboxRepoDir, fmt.Sprintf("vni-%d", vni))) input, err := json.Marshal(netConfig) Expect(err).NotTo(HaveOccurred()) cmd := exec.Command(pathToVxlan) cmd.Stdin = bytes.NewReader(input) cmd.Env = append( os.Environ(), fmt.Sprintf("CNI_COMMAND=%s", operation), fmt.Sprintf("CNI_CONTAINERID=%s", containerID), fmt.Sprintf("CNI_PATH=%s", cniPath), fmt.Sprintf("CNI_NETNS=%s", containerNS.Path()), fmt.Sprintf("CNI_IFNAME=%s", "vx-eth0"), fmt.Sprintf("DUCATI_OS_SANDBOX_REPO=%s", sandboxRepoDir), )