<|start_filename|>driver/package.go<|end_filename|>
package driver

import (
	"log"
	"os"
)

const (
	// Calico IPAM module does not allow selection of pools from which to allocate
	// IP addresses.  The pool ID, which has to be supplied in the libnetwork IPAM
	// API is therefore fixed.  We use different values for IPv4 and IPv6 so that
	// during allocation we know which IP version to use.
	PoolIDV4 = "CalicoPoolIPv4"
	PoolIDV6 = "CalicoPoolIPv6"

	CalicoLocalAddressSpace  = "CalicoLocalAddressSpace"
	CalicoGlobalAddressSpace = "CalicoGlobalAddressSpace"
)

var IFPrefix = "cali"

func init() {
	if os.Getenv("CALICO_LIBNETWORK_IFPREFIX") != "" {
		IFPrefix = os.Getenv("CALICO_LIBNETWORK_IFPREFIX")
		log.Println("Updated CALICO_LIBNETWORK_IFPREFIX to ", IFPrefix)
	}
}


<|start_filename|>tests/custom_wep_labelling/libnetwork_env_var_test.go<|end_filename|>
package custom_wep_labelling

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"os"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	mathutils "github.com/projectcalico/libnetwork-plugin/utils/math"
	. "github.com/projectcalico/libnetwork-plugin/utils/test"
)

var _ = Describe("Running plugin with custom ENV", func() {
	Describe("docker run", func() {
		It("creates a container on a network with WEP labelling enabled", func() {
			RunPlugin("-e CALICO_LIBNETWORK_LABEL_ENDPOINTS=true")

			pool := "test"
			subnet := "172.16.58.3/24"
			// Since running the plugin starts etcd, the pool needs to be created after.
			CreatePool(pool, subnet)

			name := fmt.Sprintf("run%d", rand.Uint32())
			nid := DockerString(fmt.Sprintf("docker network create -d calico --ipam-driver calico-ipam --subnet %s %s ", subnet, pool))
			UpdatePool(pool, subnet, nid)

			// Create a container that will just sit in the background
			DockerString(fmt.Sprintf("docker run --net %s -tid --label not=expected --label org.projectcalico.label.foo=bar --label org.projectcalico.label.baz=quux --name %s %s", pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Gather information for assertions
			dockerEndpoint := GetDockerEndpoint(name, pool)
			endpointID := dockerEndpoint.EndpointID
			vethName := "cali" + endpointID[:mathutils.MinInt(11, len(endpointID))]

			// Sleep to allow the plugin to query the started container and update the WEP
			// Alternative: query etcd until we hit jackpot or timeout
			time.Sleep(time.Second)

			// Check that the endpoint is created in etcd
			key := fmt.Sprintf("/calico/resources/v3/projectcalico.org/workloadendpoints/libnetwork/test-libnetwork-libnetwork-%s", endpointID)
			endpointJSON := GetEtcd(key)
			wep := api.NewWorkloadEndpoint()
			json.Unmarshal(endpointJSON, &wep)
			Expect(wep.Spec.InterfaceName).Should(Equal(vethName))
			v, ok := wep.ObjectMeta.Labels["foo"]
			Expect(ok).Should(Equal(true))
			Expect(v).Should(Equal("bar"))
			v, ok = wep.ObjectMeta.Labels["baz"]
			Expect(ok).Should(Equal(true))
			Expect(v).Should(Equal("quux"))
			_, ok = wep.ObjectMeta.Labels["not"]
			Expect(ok).Should(Equal(false))

			// Check profile
			profile := api.NewProfile()
			json.Unmarshal(GetEtcd(fmt.Sprintf("/calico/resources/v3/projectcalico.org/profiles/%s", pool)), &profile)
			Expect(profile.Name).Should(Equal(pool))
			Expect(len(profile.Labels)).Should(Equal(0))
			//tags deprecated
			Expect(profile.Spec.Ingress[0].Action).Should(Equal(api.Allow))
			Expect(profile.Spec.Egress[0].Action).Should(Equal(api.Allow))

			// Delete container
			DockerString(fmt.Sprintf("docker rm -f %s", name))
		})
	})

	Describe("docker run", func() {
		It("creates a container on a network with WEP labelling enabled and profile creation disabled", func() {
			// Run the plugin with custom IFPREFIX
			RunPlugin("-e CALICO_LIBNETWORK_LABEL_ENDPOINTS=true -e CALICO_LIBNETWORK_CREATE_PROFILES=false")

			pool := "test2"
			subnet := "192.168.3.11/24"
			// Since running the plugin starts etcd, the pool needs to be created after.
			CreatePool(pool, subnet)

			name := fmt.Sprintf("run%d", rand.Uint32())
			nid := DockerString(fmt.Sprintf("docker network create -d calico --ipam-driver calico-ipam --subnet %s %s ", subnet, pool))
			UpdatePool(pool, subnet, nid)

			// Create a container that will just sit in the background
			DockerString(fmt.Sprintf("docker run --net %s -tid --label not=expected --label org.projectcalico.label.foo=bar --label org.projectcalico.label.baz=quux --name %s %s", pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Gather information for assertions
			dockerEndpoint := GetDockerEndpoint(name, pool)
			endpointID := dockerEndpoint.EndpointID
			vethName := "cali" + endpointID[:mathutils.MinInt(11, len(endpointID))]

			// Sleep to allow the plugin to query the started container and update the WEP
			// Alternative: query etcd until we hit jackpot or timeout
			time.Sleep(time.Second)

			// Check that the endpoint is created in etcd
			key := fmt.Sprintf("/calico/resources/v3/projectcalico.org/workloadendpoints/libnetwork/test-libnetwork-libnetwork-%s", endpointID)
			endpointJSON := GetEtcd(key)
			wep := api.NewWorkloadEndpoint()
			json.Unmarshal(endpointJSON, &wep)
			Expect(wep.Spec.InterfaceName).Should(Equal(vethName))
			v, ok := wep.ObjectMeta.Labels["foo"]
			Expect(ok).Should(Equal(true))
			Expect(v).Should(Equal("bar"))
			v, ok = wep.ObjectMeta.Labels["baz"]
			Expect(ok).Should(Equal(true))
			Expect(v).Should(Equal("quux"))
			_, ok = wep.ObjectMeta.Labels["not"]
			Expect(ok).Should(Equal(false))

			// Chech profile not created
			notExists := GetNotExists(fmt.Sprintf("/calico/resources/v3/projectcalico.org/profiles/%s", pool))
			Expect(notExists).Should(BeTrue())

			// Check that the endpoint is created in etcd
			// Delete container
			DockerString(fmt.Sprintf("docker rm -f %s", name))
		})
	})
})


<|start_filename|>utils/log/log.go<|end_filename|>
package log

import (
	"encoding/json"

	logger "github.com/sirupsen/logrus"
)

func JSONMessage(formattedMessage string, data interface{}) {
	requestJSON, err := json.Marshal(data)
	if err != nil {
		logger.Fatal(err)
		return
	}
	logger.WithField("JSON", string(requestJSON)).Info(formattedMessage)
}


<|start_filename|>tests/default_environment/libnetwork_test.go<|end_filename|>
package default_environment

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"os"
	"regexp"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	mathutils "github.com/projectcalico/libnetwork-plugin/utils/math"
	. "github.com/projectcalico/libnetwork-plugin/utils/test"
)

var _ = Describe("Libnetwork Tests", func() {
	BeforeEach(func() {
		WipeEtcd()
		CreatePool("poolv4", "172.16.31.10/16")
		CreatePool("poolv6", "2001:db8::/32")
	})

	// Run the plugin just once for all tests in this file.
	RunPlugin("")

	// Test the docker network commands - no need to test inspect or ls
	Describe("docker network create", func() {
		// TODO There is no coverage of the following two options. I can't see how to make them get passed to the plugin.
		// --label value
		// --aux-address
		Context("checking failure cases", func() {
			It("needs both network and IPAM drivers to be calico", func() {
				session := DockerSession("docker network create $RANDOM -d calico")
				Eventually(session).Should(Exit(1))
				Eventually(session.Err).Should(Say("Error response from daemon: NetworkDriver.CreateNetwork: Non-Calico IPAM driver is used"))
			})
			It("doesn't allow a gateway to be specified", func() {
				session := DockerSession("docker network create $RANDOM -d calico --ipam-driver calico-ipam --subnet=172.16.31.10/16 --gateway 172.16.31.10")
				Eventually(session).Should(Exit(1))
				expectedError := regexp.QuoteMeta("Error response from daemon: failed to allocate gateway (172.16.31.10): IpamDriver.RequestAddress: Calico IPAM does not support specifying a gateway.")
				Eventually(session.Err).Should(Say(expectedError))
			})
			It("requires the subnet to match the calico pool", func() {
				// I'm trying for a /24 but calico is configured with a /16 so it will fail.
				session := DockerSession("docker network create $RANDOM -d calico --ipam-driver calico-ipam --subnet=172.16.31.10/24")
				Eventually(session).Should(Exit(1))
				Eventually(session.Err).Should(Say("Error response from daemon: IpamDriver.RequestPool: The requested subnet must match the CIDR of a configured Calico IP Pool."))
			})
			It("rejects --internal being used", func() {
				session := DockerSession("docker network create $RANDOM --internal -d calico --ipam-driver calico-ipam")
				Eventually(session).Should(Exit(1))
				Eventually(session.Err).Should(Say("Error response from daemon: NetworkDriver.CreateNetwork: Calico driver does not support the flag --internal."))
			})
			It("rejects --ip-range being used", func() {
				session := DockerSession("docker network create $RANDOM --ip-range 192.168.127.12/24 --subnet=172.16.31.10/16 -d calico --ipam-driver calico-ipam")
				Eventually(session).Should(Exit(1))
				Eventually(session.Err).Should(Say("Error response from daemon: IpamDriver.RequestPool: Calico IPAM does not support sub pool configuration on 'docker create network'. Calico IP Pools should be configured first and IP assignment is from those pre-configured pools."))
			})
			It("rejects --ipam-opt being used", func() {
				session := DockerSession("docker network create $RANDOM --ipam-opt REJECT -d calico --ipam-driver calico-ipam")
				Eventually(session).Should(Exit(1))
				Eventually(session.Err).Should(Say("Error response from daemon: IpamDriver.RequestPool: Arbitrary options are not supported"))
			})
			It("rejects --opt being used", func() {
				session := DockerSession("docker network create $RANDOM --opt REJECT -d calico --ipam-driver calico-ipam")
				Eventually(session).Should(Exit(1))
				Eventually(session.Err).Should(Say("NetworkDriver.CreateNetwork: Calico driver does not support the flag REJECT."))
			})
			It("rejects multiple --opt being used", func() {
				session := DockerSession("docker network create $RANDOM --opt REJECT --opt REJECT2 -d calico --ipam-driver calico-ipam")
				Eventually(session).Should(Exit(1))
				Eventually(session.Err).Should(Say("NetworkDriver.CreateNetwork: Calico driver does not support the flags REJECT, REJECT2."))
			})
		})
		Context("checking success cases", func() {
			It("creates a network", func() {
				session := DockerSession("docker network create success$RANDOM -d calico --ipam-driver calico-ipam")
				Eventually(session).Should(Exit(0))
				// There are no observable side effects. We could verify that nothing changed under /calico in etcd?
				// I would like to verify that the correct pools were returned to Docker but it doesn't let us observe that information - https://github.com/docker/docker/issues/28567
			})
			It("creates a network with a subnet", func() {
				session := DockerSession("docker network create success$RANDOM --subnet 172.16.31.10/16 -d calico --ipam-driver calico-ipam")
				Eventually(session).Should(Exit(0))
			})
			It("creates a network with IPv6", func() {
				session := DockerSession("docker network create --ipv6 success$RANDOM -d calico --ipam-driver calico-ipam")
				Eventually(session).Should(Exit(0))
			})
			PIt("creates a network with IPv6 from a specific subnet", func() {
			})

			//TODO - allow multiple networks from the same pool
		})
	})
	Describe("docker network rm", func() {
		// No options and no side effects
	})
	Describe("docker network connect", func() {
		// TODO
		// Usage:	docker network connect [OPTIONS] NETWORK CONTAINER
		//
		//Connect a container to a network
		//
		//Options:
		//      --alias value           Add network-scoped alias for the container (default [])
		//      --help                  Print usage
		//      --ip string             IP Address
		//      --ip6 string            IPv6 Address
		//      --link value            Add link to another container (default [])
		//      --link-local-ip value   Add a link-local address for the container (default [])
		//
	})
	Describe("docker network disconnect", func() {
		// TODO - no significant options but we should observe the veth going and the endpoint removed from etcd
	})

	//docker create/run
	// create - doesn't have any network interactions until the container is started
	// run - Run a container, check the following
	//		- etcd contains correct info
	//		- host namespace contains the right veth with the right info
	//    - container - container the right routes and interface
	//	run can have the following variations -
	//    --mac-address
	//    --link-local-ip
	//    --ip and --ip6
	Describe("docker run", func() {
		var name string
		var pool string

		BeforeEach(func() {
			name = fmt.Sprintf("run%d", rand.Uint32())
			pool = fmt.Sprintf("testp%d", rand.Uint32())
			subnet := "172.16.31.10/16"
			CreatePool(pool, subnet)
			nid := DockerString(fmt.Sprintf("docker network create --driver calico --ipam-driver calico-ipam --subnet %s %s ", subnet, pool))
			UpdatePool(pool, subnet, nid)
		})

		AfterEach(func() {
			DockerString(fmt.Sprintf("docker rm -f %s", name))
			DockerString(fmt.Sprintf("docker network rm %s", pool))
		})

		It("creates a container on a network and checks all assertions", func() {
			// Create a container that will just sit in the background
			DockerString(fmt.Sprintf("docker run --net %s -tid --name %s %s", pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Gather information for assertions
			dockerEndpoint := GetDockerEndpoint(name, pool)
			ip := dockerEndpoint.IPAddress
			mac := dockerEndpoint.MacAddress
			endpointID := dockerEndpoint.EndpointID
			vethName := "cali" + endpointID[:mathutils.MinInt(11, len(endpointID))]

			// Check that the endpoint is created in etcd
			key := fmt.Sprintf("/calico/resources/v3/projectcalico.org/workloadendpoints/libnetwork/test-libnetwork-libnetwork-%s", endpointID)
			endpointJSON := GetEtcd(key)
			wep := api.NewWorkloadEndpoint()
			json.Unmarshal(endpointJSON, &wep)
			Expect(wep.Spec.InterfaceName).Should(Equal(vethName))

			// Check profile
			profile := api.NewProfile()
			json.Unmarshal(GetEtcd(fmt.Sprintf("/calico/resources/v3/projectcalico.org/profiles/%s", pool)), &profile)
			Expect(profile.Name).Should(Equal(pool))
			Expect(len(profile.Labels)).Should(Equal(0))
			//tags deprecated
			Expect(profile.Spec.Ingress[0].Action).Should(Equal(api.Allow))
			Expect(profile.Spec.Egress[0].Action).Should(Equal(api.Allow))

			// Check the interface exists on the Host - it has an autoassigned
			// mac and ip, so don't check anything!
			DockerString(fmt.Sprintf("ip addr show %s", vethName))

			// Make sure the interface in the container exists and has the  assigned ip and mac
			containerNICString := DockerString(fmt.Sprintf("docker exec -i %s ip addr", name))
			Expect(containerNICString).Should(ContainSubstring(ip))
			Expect(containerNICString).Should(ContainSubstring(mac))

			// Make sure the container has the routes we expect
			routes := DockerString(fmt.Sprintf("docker exec -i %s ip route", name))
			Expect(routes).Should(Equal("default via 169.254.1.1 dev cali0 \n169.254.1.1 dev cali0 scope link"))
		})

		It("creates a container with specific MAC", func() {
			// Create a container that will just sit in the background
			chosen_mac := "00:22:33:44:55:66"
			DockerString(fmt.Sprintf("docker run --mac-address %s --net %s -tid --name %s %s", chosen_mac, pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Gather information for assertions
			dockerEndpoint := GetDockerEndpoint(name, pool)
			ip := dockerEndpoint.IPAddress
			mac := dockerEndpoint.MacAddress
			endpointID := dockerEndpoint.EndpointID
			vethName := "cali" + endpointID[:mathutils.MinInt(11, len(endpointID))]

			// Make sure the discovered MAC is what we asked for
			Expect(mac).Should(Equal(chosen_mac))

			// Check that the endpoint is created in etcd
			key := fmt.Sprintf("/calico/resources/v3/projectcalico.org/workloadendpoints/libnetwork/test-libnetwork-libnetwork-%s", endpointID)
			endpointJSON := GetEtcd(key)
			wep := api.NewWorkloadEndpoint()
			json.Unmarshal(endpointJSON, &wep)
			Expect(wep.Spec.InterfaceName).Should(Equal(vethName))

			// Check the interface exists on the Host - it has an autoassigned
			// mac and ip, so don't check anything!
			DockerString(fmt.Sprintf("ip addr show %s", vethName))

			// Make sure the interface in the container exists and has the  assigned ip and mac
			containerNICString := DockerString(fmt.Sprintf("docker exec -i %s ip addr", name))
			Expect(containerNICString).Should(ContainSubstring(ip))
			Expect(containerNICString).Should(ContainSubstring(mac))

			// Make sure the container has the routes we expect
			routes := DockerString(fmt.Sprintf("docker exec -i %s ip route", name))
			Expect(routes).Should(Equal("default via 169.254.1.1 dev cali0 \n169.254.1.1 dev cali0 scope link"))
		})

		PIt("creates a container with specific link local address", func() { // https://github.com/docker/docker/issues/28606
			// Create a container that will just sit in the background
			DockerString(fmt.Sprintf("docker run --link-local-ip 169.254.0.50 --net %s -tid --name %s %s", pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Delete container
			DockerString(fmt.Sprintf("docker rm -f %s", name))
		})

		// TODO Ensure that  a specific IP isn't possible without a user specified subnet
		// TODO allocate specific IPs from specific pools - see test cases in https://github.com/projectcalico/libnetwork-plugin/pull/101/files/c8c0386a41a569fbef33fae545ad97fa061470ed#diff-3bca4eb4bf01d8f50e7babc5c90236cc
		// TODO auto alloc IPs from a specific pool - see https://github.com/projectcalico/libnetwork-plugin/pull/101/files/c8c0386a41a569fbef33fae545ad97fa061470ed#diff-2667baf0dbc5ac5027aa29690f306535
		It("creates a container with specific IP", func() {
			// Create a container that will just sit in the background
			chosen_ip := "172.16.31.10"
			DockerString(fmt.Sprintf("docker run --ip %s --net %s -tid --name %s %s", chosen_ip, pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Gather information for assertions
			dockerEndpoint := GetDockerEndpoint(name, pool)
			ip := dockerEndpoint.IPAddress
			mac := dockerEndpoint.MacAddress
			endpointID := dockerEndpoint.EndpointID
			vethName := "cali" + endpointID[:mathutils.MinInt(11, len(endpointID))]

			Expect(ip).Should(Equal(chosen_ip))

			// Check that the endpoint is created in etcd
			key := fmt.Sprintf("/calico/resources/v3/projectcalico.org/workloadendpoints/libnetwork/test-libnetwork-libnetwork-%s", endpointID)
			endpointJSON := GetEtcd(key)
			wep := api.NewWorkloadEndpoint()
			json.Unmarshal(endpointJSON, &wep)
			Expect(wep.Spec.InterfaceName).Should(Equal(vethName))

			// Check the interface exists on the Host - it has an autoassigned
			// mac and ip, so don't check anything!
			DockerString(fmt.Sprintf("ip addr show %s", vethName))

			// Make sure the interface in the container exists and has the  assigned ip and mac
			containerNICString := DockerString(fmt.Sprintf("docker exec -i %s ip addr", name))
			Expect(containerNICString).Should(ContainSubstring(ip))
			Expect(containerNICString).Should(ContainSubstring(mac))

			// Make sure the container has the routes we expect
			routes := DockerString(fmt.Sprintf("docker exec -i %s ip route", name))
			Expect(routes).Should(Equal("default via 169.254.1.1 dev cali0 \n169.254.1.1 dev cali0 scope link"))
		})

		It("creates a container with labels, but do not expect those in endpoint", func() {
			// Create a container that will just sit in the background
			DockerString(fmt.Sprintf("docker run --net %s -tid --label not=expected --label org.projectcalico.label.foo=bar --label org.projectcalico.label.baz=quux --name %s %s", pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Gather information for assertions
			dockerEndpoint := GetDockerEndpoint(name, pool)
			endpointID := dockerEndpoint.EndpointID
			vethName := "cali" + endpointID[:mathutils.MinInt(11, len(endpointID))]

			// Sleep to allow the plugin to query the started container and update the WEP
			// Alternative: query etcd until we hit jackpot or timeout
			time.Sleep(time.Second)

			// Check that the endpoint is created in etcd
			key := fmt.Sprintf("/calico/resources/v3/projectcalico.org/workloadendpoints/libnetwork/test-libnetwork-libnetwork-%s", endpointID)
			endpointJSON := GetEtcd(key)
			wep := api.NewWorkloadEndpoint()
			json.Unmarshal(endpointJSON, &wep)
			Expect(wep.Spec.InterfaceName).Should(Equal(vethName))
			_, ok := wep.ObjectMeta.Labels["foo"]
			Expect(ok).Should(Equal(false))
			_, ok = wep.ObjectMeta.Labels["baz"]
			Expect(ok).Should(Equal(false))
			_, ok = wep.ObjectMeta.Labels["not"]
			Expect(ok).Should(Equal(false))
		})
	})

	Describe("docker run ipv6", func() {
		var name string
		var pool string

		BeforeEach(func() {
			name = fmt.Sprintf("run%d", rand.Uint32())
			pool = fmt.Sprintf("test6p%d", rand.Uint32())
			subnet := "fdb7:472d:ff0b::/48"
			CreatePool(pool, subnet)
			nid := DockerString(fmt.Sprintf("docker network create --driver calico --ipam-driver calico-ipam --subnet %s --ipv6 %s ", subnet, pool))
			UpdatePool(pool, subnet, nid)
		})

		AfterEach(func() {
			DockerString(fmt.Sprintf("docker rm -f %s", name))
			DockerString(fmt.Sprintf("docker network rm %s", pool))
		})

		It("creates a container on a network and checks all assertions", func() {
			// Create a container that will just sit in the background
			DockerString(fmt.Sprintf("docker run --net %s -tid --name %s %s", pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Gather information for assertions
			dockerEndpoint := GetDockerEndpoint(name, pool)
			ipv6 := dockerEndpoint.GlobalIPv6Address
			mac := dockerEndpoint.MacAddress
			endpointID := dockerEndpoint.EndpointID
			vethName := "cali" + endpointID[:mathutils.MinInt(11, len(endpointID))]

			// Check that the endpoint is created in etcd
			key := fmt.Sprintf("/calico/resources/v3/projectcalico.org/workloadendpoints/libnetwork/test-libnetwork-libnetwork-%s", endpointID)
			endpointJSON := GetEtcd(key)
			wep := api.NewWorkloadEndpoint()
			json.Unmarshal(endpointJSON, &wep)
			Expect(wep.Spec.InterfaceName).Should(Equal(vethName))

			// Check the interface exists on the Host - it has an autoassigned
			// mac and ip, so don't check anything!
			DockerString(fmt.Sprintf("ip addr show %s", vethName))
			DockerString(fmt.Sprintf("ip -6 addr show %s", vethName))

			// Make sure the interface in the container exists and has the  assigned ip and mac
			containerNICString := DockerString(fmt.Sprintf("docker exec -i %s ip addr", name))
			Expect(containerNICString).Should(ContainSubstring(ipv6))
			Expect(containerNICString).Should(ContainSubstring(mac))

			// Make sure the container has the routes we expect
			routes := DockerString(fmt.Sprintf("docker exec -i %s ip route", name))
			Expect(routes).Should(Equal("default via 169.254.1.1 dev cali0 \n169.254.1.1 dev cali0 scope link"))
			routes6 := DockerString(fmt.Sprintf("docker exec -i %s ip -6 route", name))
			Expect(routes6).Should(MatchRegexp("default via fe80::.* dev cali0  metric 1024"))

		})
	})
	//docker stop/rm - stop and rm are the same as far as the plugin is concerned
	// TODO - check that the endpoint is removed from etcd and that the  veth is removed
})


<|start_filename|>utils/netns/netns.go<|end_filename|>
package netns

import (
	"fmt"
	"net"
	"syscall"

	log "github.com/sirupsen/logrus"

	"github.com/pkg/errors"
	"github.com/vishvananda/netlink"
)

func CreateVeth(vethNameHost, vethNameNSTemp string, vethMTU uint16) error {
	veth := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name: vethNameHost,
			MTU: int(vethMTU),
		},
		PeerName: vethNameNSTemp,
	}
	if err := netlink.LinkAdd(veth); err != nil {
		return err
	}
	vethTemp := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name: vethNameNSTemp,
		},
		PeerName: vethNameHost,
	}
	// Set link state to up for both host and temp,
	if err := netlink.LinkSetUp(vethTemp); err != nil {
		return err
	}
	return netlink.LinkSetUp(veth)
}

func SetVethMac(vethNameHost, mac string) error {
	addr, err := net.ParseMAC(mac)
	if err != nil {
		return errors.Wrap(err, "Veth setting error")
	}
	return netlink.LinkSetHardwareAddr(&netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name: vethNameHost,
		},
	}, addr)
}

func RemoveVeth(vethNameHost string) error {
	if ok, err := IsVethExists(vethNameHost); err != nil {
		return errors.Wrap(err, "Veth removal error")
	} else if !ok {
		return nil
	}
	return netlink.LinkDel(&netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{
			Name: vethNameHost,
		},
	})
}

func IsVethExists(vethHostName string) (bool, error) {
	links, err := netlink.LinkList()
	if err != nil {
		return false, errors.Wrap(err, "Veth existing check error")
	}
	for _, link := range links {
		if link.Attrs().Name == vethHostName {
			return true, nil
		}
	}
	return false, nil
}

// GetLinkLocalAddr Get the IPv6 link local address of interfaceName
func GetLinkLocalAddr(interfaceName string) net.IP {
	link, err := netlink.LinkByName(interfaceName)
	if err != nil {
		log.Fatal(err)
	}
	addrs, err := netlink.AddrList(link, netlink.FAMILY_V6)
	if err != nil {
		log.Fatal(err)
	}
	var linkLocalAddr net.IP
	for _, addr := range addrs {
		if addr.Scope == syscall.RT_SCOPE_LINK {
			linkLocalAddr = addr.IP
			break
		}
	}
	if linkLocalAddr == nil {
		log.Warn(fmt.Sprintf("No IPv6 link local address found for interface: %s", interfaceName))
	}
	return linkLocalAddr
}


<|start_filename|>tests/default_environment/libnetwork_suite_test.go<|end_filename|>
package default_environment

// Tests in this suite are for testing when the plugin without additinal
// environemnt variables

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

func TestLibnetwork(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Default Libnetwork Suite")
}


<|start_filename|>tests/custom_if_prefix/libnetwork_suite_test.go<|end_filename|>
package custom_if_prefix

// Tests in this suite are for when the plugin has been run with a custom IF prefix

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

func TestLibnetwork(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Custom IF Prefix Libnetwork Suite")
}


<|start_filename|>tests/custom_if_prefix/libnetwork_env_var_test.go<|end_filename|>
package custom_if_prefix

import (
	"encoding/json"
	"fmt"
	"math/rand"
	"os"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	mathutils "github.com/projectcalico/libnetwork-plugin/utils/math"
	. "github.com/projectcalico/libnetwork-plugin/utils/test"
)

var _ = Describe("Running plugin with custom ENV", func() {
	Describe("docker run", func() {
		It("creates a container on a network with correct IFPREFIX", func() {
			// Run the plugin with custom IFPREFIX
			RunPlugin("-e CALICO_LIBNETWORK_IFPREFIX=test")

			pool := "test"
			subnet := "192.168.127.12/16"
			// Since running the plugin starts etcd, the pool needs to be created after.
			CreatePool(pool, subnet)

			name := fmt.Sprintf("run%d", rand.Uint32())
			nid := DockerString(fmt.Sprintf("docker network create --driver calico --ipam-driver calico-ipam --subnet %s %s ", subnet, pool))
			UpdatePool(pool, subnet, nid)

			// Create a container that will just sit in the background
			DockerString(fmt.Sprintf("docker run --net %s -tid --name %s %s", pool, name, os.Getenv("BUSYBOX_IMAGE")))

			// Gather information for assertions
			dockerEndpoint := GetDockerEndpoint(name, pool)
			ip := dockerEndpoint.IPAddress
			mac := dockerEndpoint.MacAddress
			endpointID := dockerEndpoint.EndpointID
			vethName := "cali" + endpointID[:mathutils.MinInt(11, len(endpointID))]

			// Check that the endpoint is created in etcd
			key := fmt.Sprintf("/calico/resources/v3/projectcalico.org/workloadendpoints/libnetwork/test-libnetwork-libnetwork-%s", endpointID)
			endpointJSON := GetEtcd(key)
			wep := api.NewWorkloadEndpoint()
			json.Unmarshal(endpointJSON, &wep)
			Expect(wep.Spec.InterfaceName).Should(Equal(vethName))

			// Check profile
			profile := api.NewProfile()
			json.Unmarshal(GetEtcd(fmt.Sprintf("/calico/resources/v3/projectcalico.org/profiles/%s", pool)), &profile)
			Expect(profile.Name).Should(Equal(pool))
			Expect(len(profile.Labels)).Should(Equal(0))
			//tags deprecated
			Expect(profile.Spec.Ingress[0].Action).Should(Equal(api.Allow))
			Expect(profile.Spec.Egress[0].Action).Should(Equal(api.Allow))

			// Check the interface exists on the Host - it has an autoassigned
			// mac and ip, so don't check anything!
			DockerString(fmt.Sprintf("ip addr show %s", vethName))

			// Make sure the interface in the container exists and has the  assigned ip and mac
			containerNICString := DockerString(fmt.Sprintf("docker exec -i %s ip addr", name))
			Expect(containerNICString).Should(ContainSubstring(ip))
			Expect(containerNICString).Should(ContainSubstring(mac))

			// Make sure the container has the routes we expect
			routes := DockerString(fmt.Sprintf("docker exec -i %s ip route", name))
			Expect(routes).Should(Equal("default via 169.254.1.1 dev test0 \n169.254.1.1 dev test0 scope link"))

			// Delete container
			DockerString(fmt.Sprintf("docker rm -f %s", name))
		})
	})
})


<|start_filename|>driver/network_driver.go<|end_filename|>
package driver

import (
	"context"
	"fmt"
	"net"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"

	dockerClient "github.com/docker/docker/client"
	"github.com/docker/go-plugins-helpers/network"
	"github.com/pkg/errors"
	api "github.com/projectcalico/libcalico-go/lib/apis/v3"
	"github.com/projectcalico/libcalico-go/lib/clientv3"
	caliconet "github.com/projectcalico/libcalico-go/lib/net"
	log "github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	dockertypes "github.com/docker/docker/api/types"
	libcalicoErrors "github.com/projectcalico/libcalico-go/lib/errors"
	wepname "github.com/projectcalico/libcalico-go/lib/names"
	"github.com/projectcalico/libcalico-go/lib/options"
	logutils "github.com/projectcalico/libnetwork-plugin/utils/log"
	mathutils "github.com/projectcalico/libnetwork-plugin/utils/math"
	"github.com/projectcalico/libnetwork-plugin/utils/netns"
	osutils "github.com/projectcalico/libnetwork-plugin/utils/os"
	netlink "github.com/vishvananda/netlink"
)

const (
	DOCKER_LABEL_PREFIX       = "org.projectcalico.label."
	LABEL_POLL_TIMEOUT_ENVKEY = "CALICO_LIBNETWORK_LABEL_POLL_TIMEOUT"
	CREATE_PROFILES_ENVKEY    = "CALICO_LIBNETWORK_CREATE_PROFILES"
	LABEL_ENDPOINTS_ENVKEY    = "CALICO_LIBNETWORK_LABEL_ENDPOINTS"
	VETH_MTU_ENVKEY           = "CALICO_LIBNETWORK_VETH_MTU"
)

type NetworkDriver struct {
	client         clientv3.Interface
	containerName  string
	orchestratorID string

	ifPrefix string

	DummyIPV4Nexthop string

	vethMTU uint16

	labelPollTimeout time.Duration

	createProfiles bool
	labelEndpoints bool
}

func NewNetworkDriver(client clientv3.Interface) network.Driver {
	driver := NetworkDriver{
		client: client,

		// Orchestrator and container IDs used in our endpoint identification. These
		// are fixed for libnetwork.  Unique endpoint identification is provided by
		// hostname and endpoint ID.
		containerName:  "libnetwork",
		orchestratorID: "libnetwork",

		ifPrefix:         IFPrefix,
		DummyIPV4Nexthop: "169.254.1.1",

		// default: enabled, disable by setting env key to false (case insensitive)
		createProfiles: !strings.EqualFold(os.Getenv(CREATE_PROFILES_ENVKEY), "false"),

		// default: disabled, enable by setting env key to true (case insensitive)
		labelEndpoints: strings.EqualFold(os.Getenv(LABEL_ENDPOINTS_ENVKEY), "true"),
	}

	// Check if MTU environment variable is given, parse into uint16
	// and override the default in the NetworkDriver.
	if mtuStr, ok := os.LookupEnv(VETH_MTU_ENVKEY); ok {
		mtu, err := strconv.ParseUint(mtuStr, 10, 16)
		if err != nil {
			log.Fatalf("Failed to parse %v '%v' into uint16: %v",
				VETH_MTU_ENVKEY, mtuStr, err)
		}

		driver.vethMTU = uint16(mtu)

		log.WithField("mtu", mtu).Info("Parsed veth MTU")
	}

	if !driver.createProfiles {
		log.Info("Feature disabled: no Calico profiles will be created per network")
	}
	if driver.labelEndpoints {
		log.Info("Feature enabled: Calico workloadendpoints will be labelled with Docker labels")
		driver.labelPollTimeout = getLabelPollTimeout()
	}
	return driver
}

func (d NetworkDriver) GetCapabilities() (*network.CapabilitiesResponse, error) {
	resp := network.CapabilitiesResponse{Scope: "global"}
	logutils.JSONMessage("GetCapabilities response", resp)
	return &resp, nil
}

// AllocateNetwork is used for swarm-mode support in remote plugins, which
// Calico's libnetwork-plugin doesn't currently support.
func (d NetworkDriver) AllocateNetwork(request *network.AllocateNetworkRequest) (*network.AllocateNetworkResponse, error) {
	var resp network.AllocateNetworkResponse
	logutils.JSONMessage("AllocateNetwork response", resp)
	return &resp, nil
}

// FreeNetwork is used for swarm-mode support in remote plugins, which
// Calico's libnetwork-plugin doesn't currently support.
func (d NetworkDriver) FreeNetwork(request *network.FreeNetworkRequest) error {
	logutils.JSONMessage("FreeNetwork request", request)
	return nil
}

func (d NetworkDriver) CreateNetwork(request *network.CreateNetworkRequest) error {
	logutils.JSONMessage("CreateNetwork", request)
	knownOpts := map[string]bool{"com.docker.network.enable_ipv6": true}
	// Reject all options (--internal, --enable_ipv6, etc)
	for k, v := range request.Options {
		skip := false
		for known, _ := range knownOpts {
			if k == known {
				skip = true
				break
			}
		}
		if skip {
			continue
		}
		optionSet := false
		flagName := k
		flagValue := fmt.Sprintf("%s", v)
		multipleFlags := false
		switch v := v.(type) {
		case bool:
			// if v == true then optionSet = true
			optionSet = v
			flagName = "--" + strings.TrimPrefix(k, "com.docker.network.")
			flagValue = ""
			break
		case map[string]interface{}:
			optionSet = len(v) != 0
			flagName = ""
			numFlags := 0
			// Sort flags for consistent error reporting
			flags := []string{}
			for flag := range v {
				flags = append(flags, flag)
			}
			sort.Strings(flags)

			for _, flag := range flags {
				flagName = flagName + flag + ", "
				numFlags++
			}
			multipleFlags = numFlags > 1
			flagName = strings.TrimSuffix(flagName, ", ")
			flagValue = ""
			break
		default:
			// for unknown case let optionSet = true
			optionSet = true
		}
		if optionSet {
			if flagValue != "" {
				flagValue = " (" + flagValue + ")"
			}
			f := "flag"
			if multipleFlags {
				f = "flags"
			}
			err := errors.New("Calico driver does not support the " + f + " " + flagName + flagValue + ".")
			log.Errorln(err)
			return err
		}
	}

	ps := []string{}
	for _, ipData := range request.IPv4Data {
		// Older version of Docker have a bug where they don't provide the correct AddressSpace
		// so we can't check for calico IPAM using our known address space.
		// The Docker issue, https://github.com/projectcalico/libnetwork-plugin/issues/77,
		// was fixed sometime between 1.11.2 and 1.12.3.
		// Also the pool might not have a fixed values if --subnet was passed
		// So the only safe thing is to check for our special gateway value
		if ipData.Gateway != "0.0.0.0/0" {
			err := errors.New("Non-Calico IPAM driver is used. Note: Docker before 1.12.3 is unsupported")
			log.Errorln(err)
			return err
		}
		ps = append(ps, ipData.Pool)
	}

	for _, ipData := range request.IPv6Data {
		// Don't support older versions of Docker which have a bug where the correct AddressSpace isn't provided
		if ipData.AddressSpace != CalicoGlobalAddressSpace {
			err := errors.New("Non-Calico IPAM driver is used")
			log.Errorln(err)
			return err
		}
		ps = append(ps, ipData.Pool)
	}

	logutils.JSONMessage("CreateNetwork response", map[string]string{})
	return d.populatePoolAnnotation(ps, request.NetworkID)
}

func (d NetworkDriver) populatePoolAnnotation(pools []string, networkID string) error {
	ctx := context.Background()
	poolClient := d.client.IPPools()
	ipPools, err := poolClient.List(ctx, options.ListOptions{})
	if err != nil {
		log.Errorln(err)
		return err
	}
	for _, ipPool := range ipPools.Items {
		for _, cidr := range pools {
			if ipPool.Spec.CIDR == cidr {
				ann := ipPool.GetAnnotations()
				if ann == nil {
					ann = map[string]string{}
				}
				ann[DOCKER_LABEL_PREFIX+"network.ID"] = networkID
				ipPool.SetAnnotations(ann)
				_, err = poolClient.Update(ctx, &ipPool, options.SetOptions{})
				if err != nil {
					log.Errorln(err)
					return err
				}
			}
		}
	}
	return nil
}

func (d NetworkDriver) DeleteNetwork(request *network.DeleteNetworkRequest) error {
	logutils.JSONMessage("DeleteNetwork", request)
	return nil
}

func (d NetworkDriver) CreateEndpoint(request *network.CreateEndpointRequest) (*network.CreateEndpointResponse, error) {
	logutils.JSONMessage("CreateEndpoint", request)

	ctx := context.Background()
	hostname, err := osutils.GetHostname()
	if err != nil {
		err = errors.Wrap(err, "Hostname fetching error")
		log.Errorln(err)
		return nil, err
	}

	log.Debugf("Creating endpoint %v\n", request.EndpointID)
	if request.Interface.Address == "" && request.Interface.AddressIPv6 == "" {
		err := errors.New("No address assigned for endpoint")
		log.Errorln(err)
		return nil, err
	}

	var addresses []caliconet.IPNet
	if request.Interface.Address != "" {
		// Parse the address this function was passed. Ignore the subnet - Calico always uses /32 (for IPv4)
		ip4, _, err := net.ParseCIDR(request.Interface.Address)
		log.Debugf("Parsed IP %v from (%v) \n", ip4, request.Interface.Address)

		if err != nil {
			err = errors.Wrapf(err, "Parsing %v as CIDR failed", request.Interface.Address)
			log.Errorln(err)
			return nil, err
		}

		addresses = append(addresses, caliconet.IPNet{IPNet: net.IPNet{IP: ip4, Mask: net.CIDRMask(32, 32)}})
	}

	if request.Interface.AddressIPv6 != "" {
		// Parse the address this function was passed.
		ip6, ipnet, err := net.ParseCIDR(request.Interface.AddressIPv6)
		log.Debugf("Parsed IP %v from (%v) \n", ip6, request.Interface.AddressIPv6)
		if err != nil {
			err = errors.Wrapf(err, "Parsing %v as CIDR failed", request.Interface.AddressIPv6)
			log.Errorln(err)
			return nil, err
		}
		addresses = append(addresses, caliconet.IPNet{IPNet: *ipnet})
	}

	wepName, err := d.generateEndpointName(hostname, request.EndpointID)
	if err != nil {
		log.Errorln(err)
		return nil, err
	}

	endpoint := api.NewWorkloadEndpoint()
	endpoint.Name = wepName
	endpoint.ObjectMeta.Namespace = d.orchestratorID
	endpoint.Spec.Endpoint = request.EndpointID
	endpoint.Spec.Node = hostname
	endpoint.Spec.Orchestrator = d.orchestratorID
	endpoint.Spec.Workload = d.containerName
	endpoint.Spec.InterfaceName = "cali" + request.EndpointID[:mathutils.MinInt(11, len(request.EndpointID))]
	var mac net.HardwareAddr
	if request.Interface.MacAddress != "" {
		if mac, err = net.ParseMAC(request.Interface.MacAddress); err != nil {
			err = errors.Wrap(err, "Error parsing MAC address")
			log.Errorln(err)
			return nil, err
		}
	}
	endpoint.Spec.MAC = mac.String()
	for _, addr := range addresses {
		endpoint.Spec.IPNetworks = append(endpoint.Spec.IPNetworks, addr.String())
	}

	pools, err := d.client.IPPools().List(ctx, options.ListOptions{})
	if err != nil {
		err = errors.Wrapf(err, "Network %v gather error", request.NetworkID)
		log.Errorln(err)
		return nil, err
	}

	f := false
	networkName := ""
	for _, p := range pools.Items {
		if nid, ok := p.Annotations[DOCKER_LABEL_PREFIX+"network.ID"]; ok && nid == request.NetworkID {
			f = true
			networkName = p.ObjectMeta.Name
			log.Debugf("Find ippool : %v\n", p.Name)
			break
		}
	}
	if !f {
		err := errors.New("The requested subnet must match the CIDR of a configured Calico IP Pool.")
		log.Errorln(err)
		return nil, err
	}

	if d.createProfiles {
		// Now that we know the network name, set it on the endpoint.
		endpoint.Spec.Profiles = append(endpoint.Spec.Profiles, networkName)

		// Check if exists
		if _, err := d.client.Profiles().Get(ctx, networkName, options.GetOptions{}); err != nil {
			// If a profile for the network name doesn't exist then it needs to be created.
			// We always attempt to create the profile and rely on the datastore to reject
			// the request if the profile already exists.
			profile := &api.Profile{
				ObjectMeta: metav1.ObjectMeta{
					Name: networkName,
				},
				Spec: api.ProfileSpec{
					Egress: []api.Rule{{Action: "Allow"}},
					Ingress: []api.Rule{{Action: "Allow",
						Source: api.EntityRule{
							Selector: fmt.Sprintf("has(%s)", networkName),
						}}},
				},
			}
			if _, err := d.client.Profiles().Create(ctx, profile, options.SetOptions{}); err != nil {
				if _, ok := err.(libcalicoErrors.ErrorResourceAlreadyExists); !ok {
					log.Errorln(err)
					return nil, err
				}
			}
		}
	}

	// Create the endpoint last to minimize side-effects if something goes wrong.
	endpoint, err = d.client.WorkloadEndpoints().Create(ctx, endpoint, options.SetOptions{})
	if err != nil {
		err = errors.Wrapf(err, "Workload endpoints creation error, data: %+v", endpoint)
		log.Errorln(err)
		return nil, err
	}

	log.Debugf("Workload created, data: %+v\n", endpoint)

	if d.labelEndpoints {
		go d.populateWorkloadEndpointWithLabels(request, endpoint)
	}

	response := &network.CreateEndpointResponse{Interface: &network.EndpointInterface{}}
	logutils.JSONMessage("CreateEndpoint response", response)
	return response, nil
}

func (d NetworkDriver) DeleteEndpoint(request *network.DeleteEndpointRequest) error {
	logutils.JSONMessage("DeleteEndpoint", request)
	log.Debugf("Removing endpoint %v\n", request.EndpointID)

	hostname, err := osutils.GetHostname()
	if err != nil {
		err = errors.Wrap(err, "Hostname fetching error")
		log.Errorln(err)
		return err
	}

	wepName, err := d.generateEndpointName(hostname, request.EndpointID)
	if err != nil {
		log.Errorln(err)
		return err
	}

	if _, err = d.client.WorkloadEndpoints().Delete(
		context.Background(),
		d.orchestratorID,
		wepName,
		options.DeleteOptions{}); err != nil {
		err = errors.Wrapf(err, "Endpoint %v removal error", request.EndpointID)
		log.Errorln(err)
		return err
	}

	logutils.JSONMessage("DeleteEndpoint response JSON={}", map[string]string{})

	return err
}

func (d NetworkDriver) EndpointInfo(request *network.InfoRequest) (*network.InfoResponse, error) {
	logutils.JSONMessage("EndpointInfo", request)
	return nil, nil
}

func (d NetworkDriver) Join(request *network.JoinRequest) (*network.JoinResponse, error) {
	logutils.JSONMessage("Join", request)

	ctx := context.Background()
	// 1) Set up a veth pair
	// 	The one end will stay in the host network namespace - named caliXXXXX
	//	The other end is given a temporary name. It's moved into the final network namespace by libnetwork itself.
	var err error
	prefix := request.EndpointID[:mathutils.MinInt(11, len(request.EndpointID))]
	hostInterfaceName := "cali" + prefix
	tempInterfaceName := "temp" + prefix

	if err = netns.CreateVeth(hostInterfaceName, tempInterfaceName, d.vethMTU); err != nil {
		err = errors.Wrapf(
			err, "Veth creation error, hostInterfaceName=%v, tempInterfaceName=%v, vethMTU=%v",
			hostInterfaceName, tempInterfaceName, d.vethMTU)
		log.Errorln(err)
		return nil, err
	}

	// 2) update workloads
	hostname, err := os.Hostname()
	if err != nil {
		log.Errorln(err)
		return nil, err
	}
	weps := d.client.WorkloadEndpoints()
	wepName, err := d.generateEndpointName(hostname, request.EndpointID)
	if err != nil {
		log.Errorln(err)
		return nil, err
	}
	wep, err := weps.Get(ctx, d.orchestratorID, wepName, options.GetOptions{})
	if err != nil {
		log.Errorln(err)
		return nil, err
	}
	tempNIC, err := netlink.LinkByName(tempInterfaceName)
	if err != nil {
		log.Errorln(err)
		return nil, err
	}
	wep.Spec.MAC = tempNIC.Attrs().HardwareAddr.String()
	_, err = weps.Update(ctx, wep, options.SetOptions{})
	if err != nil {
		log.Errorln(err)
		return nil, err
	}

	resp := &network.JoinResponse{
		InterfaceName: network.InterfaceName{
			SrcName:   tempInterfaceName,
			DstPrefix: IFPrefix,
		},
	}

	// One of the network gateway addresses indicate that we are using
	// Calico IPAM driver.  In this case we setup routes using the gateways
	// configured on the endpoint (which will be our host IPs).
	log.Debugln("Using Calico IPAM driver, configure gateway and static routes to the host")

	resp.Gateway = d.DummyIPV4Nexthop
	resp.StaticRoutes = append(resp.StaticRoutes, &network.StaticRoute{
		Destination: d.DummyIPV4Nexthop + "/32",
		RouteType:   1, // 1 = CONNECTED
		NextHop:     "",
	})

	linkLocalAddr := netns.GetLinkLocalAddr(hostInterfaceName)
	if linkLocalAddr == nil {
		log.Warnf("No IPv6 link local address for %s", hostInterfaceName)
	} else {
		resp.GatewayIPv6 = fmt.Sprintf("%s", linkLocalAddr)
		nextHopIPv6 := fmt.Sprintf("%s/128", linkLocalAddr)
		resp.StaticRoutes = append(resp.StaticRoutes, &network.StaticRoute{
			Destination: nextHopIPv6,
			RouteType:   1, // 1 = CONNECTED
			NextHop:     "",
		})
	}

	logutils.JSONMessage("Join response", resp)

	return resp, nil
}

func (d NetworkDriver) Leave(request *network.LeaveRequest) error {
	logutils.JSONMessage("Leave response", request)
	caliName := "cali" + request.EndpointID[:mathutils.MinInt(11, len(request.EndpointID))]
	err := netns.RemoveVeth(caliName)
	return err
}

func (d NetworkDriver) DiscoverNew(request *network.DiscoveryNotification) error {
	logutils.JSONMessage("DiscoverNew", request)
	log.Debugln("DiscoverNew response JSON={}")
	return nil
}

func (d NetworkDriver) DiscoverDelete(request *network.DiscoveryNotification) error {
	logutils.JSONMessage("DiscoverDelete", request)
	log.Debugln("DiscoverDelete response JSON={}")
	return nil
}

func (d NetworkDriver) ProgramExternalConnectivity(*network.ProgramExternalConnectivityRequest) error {
	return nil
}

func (d NetworkDriver) RevokeExternalConnectivity(*network.RevokeExternalConnectivityRequest) error {
	return nil
}

// Try to get the container's labels and update the WorkloadEndpoint with them
// Since we do not get container info in the libnetwork API methods we need to
// get them ourselves.
//
// This is how:
// - first we try to get a list of containers attached to the custom network
// - if there is a container with our endpointID, we try to inspect that container
// - any labels for that container prefixed by our 'magic' prefix are added to
//   our WorkloadEndpoint resource
//
// Above may take 1 or more retries, because Docker has to update the
// container list in the NetworkInspect and make the Container available
// for inspecting.
func (d NetworkDriver) populateWorkloadEndpointWithLabels(request *network.CreateEndpointRequest, endpoint *api.WorkloadEndpoint) {
	ctx := context.Background()

	networkID := request.NetworkID
	endpointID := request.EndpointID

	retrySleep := time.Duration(100 * time.Millisecond)

	start := time.Now()
	deadline := start.Add(d.labelPollTimeout)

	os.Setenv("DOCKER_API_VERSION", "1.25")
	dockerCli, err := dockerClient.NewEnvClient()
	if err != nil {
		err = errors.Wrap(err, "Error while attempting to instantiate docker client from env")
		log.Errorln(err)
		return
	}
	defer dockerCli.Close()

RETRY_NETWORK_INSPECT:
	if time.Now().After(deadline) {
		log.Errorf("Getting labels for workloadEndpoint timed out in network inspect loop. Took %s", time.Since(start))
		return
	}

	// inspect our custom network
	networkData, err := dockerCli.NetworkInspect(ctx, networkID, dockertypes.NetworkInspectOptions{})
	if err != nil {
		err = errors.Wrapf(err, "Error inspecting network %s - retrying (T=%s)", networkID, time.Since(start))
		log.Warningln(err)
		// was unable to inspect network, let's retry
		time.Sleep(retrySleep)
		goto RETRY_NETWORK_INSPECT
	}
	logutils.JSONMessage("NetworkInspect response", networkData)

	// try to find the container for which we created an endpoint
	containerID := ""
	for id, containerInNetwork := range networkData.Containers {
		if containerInNetwork.EndpointID == endpointID {
			// skip funky identified containers - observed with dind 1.13.0-rc3, gone in -rc5
			// {
			//   "Containers": {
			//     "ep-736ccfa7cd61ced93b67f7465ddb79633ea6d1f718a8ca7d9d19226f5d3521b0": {
			//       "Name": "run1466946597",
			//       "EndpointID": "736ccfa7cd61ced93b67f7465ddb79633ea6d1f718a8ca7d9d19226f5d3521b0",
			//       ...
			//     }
			//   }
			// }
			if strings.HasPrefix(id, "ep-") {
				log.Debugf("Skipping container entry with matching endpointID, but illegal id: %s", id)
			} else {
				containerID = id
				log.Debugf("Container %s found in NetworkInspect result (T=%s)", containerID, time.Since(start))
				break
			}
		}
	}

	if containerID == "" {
		// cause: Docker has not yet processed the libnetwork CreateEndpoint response.
		log.Warnf("Container not found in NetworkInspect result - retrying (T=%s)", time.Since(start))
		// let's retry
		time.Sleep(retrySleep)
		goto RETRY_NETWORK_INSPECT
	}

RETRY_CONTAINER_INSPECT:
	if time.Now().After(deadline) {
		log.Errorf("Getting labels for workloadEndpoint timed out in container inspect loop. Took %s", time.Since(start))
		return
	}

	containerInfo, err := dockerCli.ContainerInspect(ctx, containerID)
	if err != nil {
		err = errors.Wrapf(err, "Error inspecting container %s for labels - retrying (T=%s)", containerID, time.Since(start))
		log.Warningln(err)
		// was unable to inspect container, let's retry
		time.Sleep(100 * time.Millisecond)
		goto RETRY_CONTAINER_INSPECT
	}

	log.Debugf("Container inspected, processing labels now (T=%s)", time.Since(start))

RETRY_UPDATE_ENDPOINT:
	if time.Now().After(deadline) {
		log.Errorf("Updating endpoint timed out. Took %s", time.Since(start))
		return
	}

	// make sure we have a labels map in the workloadEndpoint
	if endpoint.ObjectMeta.Labels == nil {
		endpoint.ObjectMeta.Labels = map[string]string{}
	}

	labelsFound := 0
	for label, labelValue := range containerInfo.Config.Labels {
		if !strings.HasPrefix(label, DOCKER_LABEL_PREFIX) {
			continue
		}
		labelsFound++
		labelClean := strings.TrimPrefix(label, DOCKER_LABEL_PREFIX)
		endpoint.ObjectMeta.Labels[labelClean] = labelValue
		log.Debugf("Found label for WorkloadEndpoint: %s=%s", labelClean, labelValue)
	}

	if labelsFound == 0 {
		log.Debugf("No labels found for container (T=%s)", time.Since(start))
		return
	}

	// lets update the workloadEndpoint
	_, err = d.client.WorkloadEndpoints().Update(ctx, endpoint, options.SetOptions{})
	if err != nil {
		err = errors.Wrapf(err, "Unable to update WorkloadEndpoint with labels (T=%s)", time.Since(start))
		log.Warningln(err)
		endpoint, err = d.client.WorkloadEndpoints().Get(ctx, endpoint.Namespace, endpoint.Name, options.GetOptions{})
		if err != nil {
			err = errors.Wrapf(err, "Unable to get WorkloadEndpoint (T=%s)", time.Since(start))
			log.Errorln(err)
			return
		}
		time.Sleep(100 * time.Millisecond)
		goto RETRY_UPDATE_ENDPOINT
	}

	log.Infof("WorkloadEndpoint %s updated with labels: %v (T=%s)",
		endpointID, endpoint.ObjectMeta.Labels, time.Since(start))

}

// Returns the label poll timeout. Default is returned unless an environment
// key is set to a valid time.Duration.
func getLabelPollTimeout() time.Duration {
	// 5 seconds should be more than enough for this plugin to get the
	// container labels. More info in func populateWorkloadEndpointWithLabels
	defaultTimeout := time.Duration(5 * time.Second)

	timeoutVal := os.Getenv(LABEL_POLL_TIMEOUT_ENVKEY)
	if timeoutVal == "" {
		return defaultTimeout
	}

	labelPollTimeout, err := time.ParseDuration(timeoutVal)
	if err != nil {
		err = errors.Wrapf(err, "Label poll timeout specified via env key %s is invalid, using default %s",
			LABEL_POLL_TIMEOUT_ENVKEY, defaultTimeout)
		log.Warningln(err)
		return defaultTimeout
	}
	log.Infof("Using custom label poll timeout: %s", labelPollTimeout)
	return labelPollTimeout
}

func (d NetworkDriver) generateEndpointName(hostname, endpointID string) (string, error) {
	wepNameIdent := wepname.WorkloadEndpointIdentifiers{
		Node:         hostname,
		Orchestrator: d.orchestratorID,
		Endpoint:     endpointID,
	}
	return wepNameIdent.CalculateWorkloadEndpointName(false)
}


<|start_filename|>driver/ipam_driver.go<|end_filename|>
package driver

import (
	"context"
	"fmt"
	"net"

	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"

	"github.com/docker/go-plugins-helpers/ipam"
	"github.com/projectcalico/libcalico-go/lib/clientv3"
	calicoipam "github.com/projectcalico/libcalico-go/lib/ipam"
	caliconet "github.com/projectcalico/libcalico-go/lib/net"
	"github.com/projectcalico/libcalico-go/lib/options"
	logutils "github.com/projectcalico/libnetwork-plugin/utils/log"
	osutils "github.com/projectcalico/libnetwork-plugin/utils/os"
)

type IpamDriver struct {
	client clientv3.Interface

	poolIDV4 string
	poolIDV6 string
}

func NewIpamDriver(client clientv3.Interface) ipam.Ipam {
	return IpamDriver{
		client: client,

		poolIDV4: PoolIDV4,
		poolIDV6: PoolIDV6,
	}
}

func (i IpamDriver) GetCapabilities() (*ipam.CapabilitiesResponse, error) {
	resp := ipam.CapabilitiesResponse{}
	logutils.JSONMessage("GetCapabilities response", resp)
	return &resp, nil
}

func (i IpamDriver) GetDefaultAddressSpaces() (*ipam.AddressSpacesResponse, error) {
	resp := &ipam.AddressSpacesResponse{
		LocalDefaultAddressSpace:  CalicoLocalAddressSpace,
		GlobalDefaultAddressSpace: CalicoGlobalAddressSpace,
	}
	logutils.JSONMessage("GetDefaultAddressSpace response", resp)
	return resp, nil
}

func (i IpamDriver) RequestPool(request *ipam.RequestPoolRequest) (*ipam.RequestPoolResponse, error) {
	logutils.JSONMessage("RequestPool", request)

	// Calico IPAM does not allow you to request a SubPool.
	if request.SubPool != "" {
		err := errors.New(
			"Calico IPAM does not support sub pool configuration " +
				"on 'docker create network'. Calico IP Pools " +
				"should be configured first and IP assignment is " +
				"from those pre-configured pools.",
		)
		log.Errorln(err)
		return nil, err
	}

	if len(request.Options) != 0 {
		err := errors.New("Arbitrary options are not supported")
		log.Errorln(err)
		return nil, err
	}
	var poolID string
	var pool string
	var gateway string
	if request.V6 {
		// Default the poolID to the fixed value.
		poolID = i.poolIDV6
		pool = "::/0"
		gateway = "::/0"
	} else {
		// Default the poolID to the fixed value.
		poolID = i.poolIDV4
		pool = "0.0.0.0/0"
		gateway = "0.0.0.0/0"
	}

	// If a pool (subnet on the CLI) is specified, it must match one of the
	// preconfigured Calico pools.
	if request.Pool != "" {
		poolsClient := i.client.IPPools()
		_, ipNet, err := caliconet.ParseCIDR(request.Pool)
		if err != nil {
			err := errors.New("Invalid CIDR")
			log.Errorln(err)
			return nil, err
		}

		pools, err := poolsClient.List(context.Background(), options.ListOptions{})
		if err != nil {
			log.Errorln(err)
			return nil, err
		}

		f := false
		for _, p := range pools.Items {
			if p.Spec.CIDR == ipNet.String() {
				f = true
				pool = p.Spec.CIDR
				poolID = p.Name
				break
			}
		}

		if !f {
			err := errors.New("The requested subnet must match the CIDR of a " +
				"configured Calico IP Pool.",
			)
			log.Errorln(err)
			return nil, err
		}
	}

	// We use static pool ID and CIDR. We don't need to signal the
	// The meta data includes a dummy gateway address. This prevents libnetwork
	// from requesting a gateway address from the pool since for a Calico
	// network our gateway is set to a special IP.
	resp := &ipam.RequestPoolResponse{
		PoolID: poolID,
		Pool:   pool,
		Data:   map[string]string{"com.docker.network.gateway": gateway},
	}

	logutils.JSONMessage("RequestPool response", resp)

	return resp, nil
}

func (i IpamDriver) ReleasePool(request *ipam.ReleasePoolRequest) error {
	logutils.JSONMessage("ReleasePool", request)
	return nil
}

func (i IpamDriver) RequestAddress(request *ipam.RequestAddressRequest) (*ipam.RequestAddressResponse, error) {
	logutils.JSONMessage("RequestAddress", request)

	hostname, err := osutils.GetHostname()
	if err != nil {
		log.Errorln(err)
		return nil, err
	}

	// Calico IPAM does not allow you to choose a gateway.
	if request.Options["RequestAddressType"] == "com.docker.network.gateway" {
		err := errors.New("Calico IPAM does not support specifying a gateway.")
		log.Errorln(err)
		return nil, err
	}

	var IPs []caliconet.IP

	if request.Address == "" {
		// No address requested, so auto assign from our pools.
		log.Println("Auto assigning IP from Calico pools")

		// If the poolID isn't the fixed one then find the pool to assign from.
		// poolV4 defaults to nil to assign from across all pools.
		var poolV4 []caliconet.IPNet

		var poolV6 []caliconet.IPNet
		var numIPv4, numIPv6, version int
		if request.PoolID == PoolIDV4 {
			version = 4
			numIPv4 = 1
			numIPv6 = 0
		} else if request.PoolID == PoolIDV6 {
			version = 6
			numIPv4 = 0
			numIPv6 = 1
		} else {
			poolsClient := i.client.IPPools()
			ipPool, err := poolsClient.Get(context.Background(), request.PoolID, options.GetOptions{})
			if err != nil {
				err = errors.Wrapf(err, "Invalid Pool - %v", request.PoolID)
				log.Errorln(err)
				return nil, err
			}

			_, ipNet, err := caliconet.ParseCIDR(ipPool.Spec.CIDR)
			if err != nil {
				err = errors.Wrapf(err, "Invalid CIDR - %v", request.PoolID)
				log.Errorln(err)
				return nil, err
			}

			version = ipNet.Version()
			if version == 4 {
				poolV4 = []caliconet.IPNet{caliconet.IPNet{IPNet: ipNet.IPNet}}
				numIPv4 = 1
				log.Debugln("Using specific pool ", poolV4)
			} else if version == 6 {
				poolV6 = []caliconet.IPNet{caliconet.IPNet{IPNet: ipNet.IPNet}}
				numIPv6 = 1
				log.Debugln("Using specific pool ", poolV6)
			}
		}

		// Auto assign an IP address.
		// IPv4/v6 pool will be nil if the docker network doesn't have a subnet associated with.
		// Otherwise, it will be set to the Calico pool to assign from.
		IPsV4, IPsV6, err := i.client.IPAM().AutoAssign(
			context.Background(),
			calicoipam.AutoAssignArgs{
				Num4:      numIPv4,
				Num6:      numIPv6,
				Hostname:  hostname,
				IPv4Pools: poolV4,
				IPv6Pools: poolV6,
			},
		)

		if err != nil {
			err = errors.Wrapf(err, "IP assignment error")
			log.Errorln(err)
			return nil, err
		}
		IPs = append(IPsV4, IPsV6...)
	} else {
		// Docker allows the users to specify any address.
		// We'll return an error if the address isn't in a Calico pool, but we don't care which pool it's in
		// (i.e. it doesn't need to match the subnet from the docker network).
		log.Debugln("Reserving a specific address in Calico pools")
		ip := net.ParseIP(request.Address)
		ipArgs := calicoipam.AssignIPArgs{
			IP:       caliconet.IP{IP: ip},
			Hostname: hostname,
		}
		err := i.client.IPAM().AssignIP(context.Background(), ipArgs)
		if err != nil {
			err = errors.Wrapf(err, "IP assignment error, data: %+v", ipArgs)
			log.Errorln(err)
			return nil, err
		}
		IPs = []caliconet.IP{{IP: ip}}
	}

	// We should only have one IP address assigned at this point.
	if len(IPs) != 1 {
		err := errors.New(fmt.Sprintf("Unexpected number of assigned IP addresses. "+
			"A single address should be assigned. Got %v", IPs))
		log.Errorln(err)
		return nil, err
	}

	// Return the IP as a CIDR.
	var respAddr string
	if IPs[0].Version() == 4 {
		// IPv4 address
		respAddr = fmt.Sprintf("%v/%v", IPs[0], "32")
	} else {
		// IPv6 address
		respAddr = fmt.Sprintf("%v/%v", IPs[0], "128")
	}
	resp := &ipam.RequestAddressResponse{
		Address: respAddr,
	}

	logutils.JSONMessage("RequestAddress response", resp)

	return resp, nil
}

func (i IpamDriver) ReleaseAddress(request *ipam.ReleaseAddressRequest) error {
	logutils.JSONMessage("ReleaseAddress", request)

	ip := caliconet.IP{IP: net.ParseIP(request.Address)}

	// Unassign the address.  This handles the address already being unassigned
	// in which case it is a no-op.
	_, err := i.client.IPAM().ReleaseIPs(context.Background(), []caliconet.IP{ip})
	if err != nil {
		err = errors.Wrapf(err, "IP releasing error, ip: %v", ip)
		log.Errorln(err)
		return err
	}

	return nil
}


<|start_filename|>main.go<|end_filename|>
package main

import (
	"os"

	"github.com/docker/go-plugins-helpers/ipam"
	"github.com/docker/go-plugins-helpers/network"
	"github.com/projectcalico/libcalico-go/lib/apiconfig"
	"github.com/projectcalico/libcalico-go/lib/clientv3"
	"github.com/projectcalico/libnetwork-plugin/driver"
	log "github.com/sirupsen/logrus"

	"flag"

	"fmt"
)

const (
	ipamPluginName    = "calico-ipam"
	networkPluginName = "calico"
)

var (
	config *apiconfig.CalicoAPIConfig
	client clientv3.Interface
)

func init() {
	// Output to stderr instead of stdout, could also be a file.
	log.SetOutput(os.Stderr)
}

func initializeClient() {
	var err error

	if config, err = apiconfig.LoadClientConfig(""); err != nil {
		panic(err)
	}
	if client, err = clientv3.New(*config); err != nil {
		panic(err)
	}

	if os.Getenv("CALICO_DEBUG") != "" {
		log.SetLevel(log.DebugLevel)
		log.Debugln("Debug logging enabled")
	}
}

// VERSION is filled out during the build process (using git describe output)
var VERSION string

const (
	// The root user group id is 0
	ROOT_GID = 0
)

func main() {
	// Display the version on "-v"
	// Use a new flag set so as not to conflict with existing libraries which use "flag"
	flagSet := flag.NewFlagSet("Calico", flag.ExitOnError)

	version := flagSet.Bool("v", false, "Display version")
	err := flagSet.Parse(os.Args[1:])
	if err != nil {
		log.Fatalln(err)
	}
	if *version {
		fmt.Println(VERSION)
		os.Exit(0)
	}

	initializeClient()

	errChannel := make(chan error)
	networkHandler := network.NewHandler(driver.NewNetworkDriver(client))
	ipamHandler := ipam.NewHandler(driver.NewIpamDriver(client))

	go func(c chan error) {
		log.Infoln("calico-net has started.")
		err := networkHandler.ServeUnix(networkPluginName, ROOT_GID)
		log.Infoln("calico-net has stopped working.")
		c <- err
	}(errChannel)

	go func(c chan error) {
		log.Infoln("calico-ipam has started.")
		err := ipamHandler.ServeUnix(ipamPluginName, ROOT_GID)
		log.Infoln("calico-ipam has stopped working.")
		c <- err
	}(errChannel)

	err = <-errChannel

	log.Fatalln(err)
}


<|start_filename|>utils/os/utils.go<|end_filename|>
package os

import "os"

const hostnameEnv = "HOSTNAME"

func GetHostname() (string, error) {
	hostnameFromEnv := os.Getenv(hostnameEnv)
	if hostname, err := os.Hostname(); hostnameFromEnv == "" {
		return hostname, err
	}
	return hostnameFromEnv, nil
}
