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 } if err = d.client.WorkloadEndpoints().Delete( api.WorkloadEndpointMetadata{ Name: request.EndpointID, Node: hostname, Orchestrator: d.orchestratorID, Workload: d.containerName}); err != nil { err = errors.Wrapf(err, "Endpoint %v removal error", request.EndpointID) log.Errorln(err) return err } logutils.JSONMessage("DeleteEndpoint response JSON=%v", map[string]string{}) return err }
func (d NetworkDriver) CreateNetwork(request *network.CreateNetworkRequest) error { logutils.JSONMessage("CreateNetwork", request) genericOpts, ok := request.Options["com.docker.network.generic"] if ok { opts, ok := genericOpts.(map[string]interface{}) if ok && len(opts) != 0 { err := errors.New("Arbitrary options are not supported") log.Println(err) return err } } 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. // 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") log.Errorln(err) return err } } logutils.JSONMessage("CreateNetwork response", map[string]string{}) return nil }
func (d NetworkDriver) Join(request *network.JoinRequest) (*network.JoinResponse, error) { logutils.JSONMessage("Join", request) // 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); err != nil { err = errors.Wrapf( err, "Veth creation error, hostInterfaceName=%v, tempInterfaceName=%v", hostInterfaceName, tempInterfaceName) log.Errorln(err) return nil, err } // libnetwork doesn't set the MAC address properly, so set it here. if err = netns.SetVethMac(tempInterfaceName, d.fixedMac); err != nil { log.Debugf("Veth mac setting for %v failed, removing veth for %v\n", tempInterfaceName, hostInterfaceName) err = netns.RemoveVeth(hostInterfaceName) err = errors.Wrapf(err, "Veth removing for %v error", hostInterfaceName) 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: "", }) logutils.JSONMessage("Join 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) 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([]caliconet.IP{ip}) if err != nil { err = errors.Wrapf(err, "IP releasing error, ip: %v", ip) log.Errorln(err) return err } return nil }
func (d NetworkDriver) CreateEndpoint(request *network.CreateEndpointRequest) (*network.CreateEndpointResponse, error) { logutils.JSONMessage("CreateEndpoint", request) 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 == "" { 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)}}) } endpoint := api.NewWorkloadEndpoint() endpoint.Metadata.Node = hostname endpoint.Metadata.Orchestrator = d.orchestratorID endpoint.Metadata.Workload = d.containerName endpoint.Metadata.Name = request.EndpointID endpoint.Spec.InterfaceName = "cali" + request.EndpointID[:mathutils.MinInt(11, len(request.EndpointID))] mac, _ := net.ParseMAC(d.fixedMac) endpoint.Spec.MAC = &caliconet.MAC{HardwareAddr: mac} endpoint.Spec.IPNetworks = append(endpoint.Spec.IPNetworks, addresses...) // Use the Docker API to fetch the network name (so we don't have to use an ID everywhere) dockerCli, err := dockerClient.NewEnvClient() if err != nil { err = errors.Wrap(err, "Error while attempting to instantiate docker client from env") log.Errorln(err) return nil, err } defer dockerCli.Close() networkData, err := dockerCli.NetworkInspect(context.Background(), request.NetworkID) if err != nil { err = errors.Wrapf(err, "Network %v inspection error", request.NetworkID) log.Errorln(err) return nil, err } // Now that we know the network name, set it on the endpoint. endpoint.Spec.Profiles = append(endpoint.Spec.Profiles, networkData.Name) // 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{ Metadata: api.ProfileMetadata{ Name: networkData.Name, Tags: []string{networkData.Name}, }, Spec: api.ProfileSpec{ EgressRules: []api.Rule{{Action: "allow"}}, IngressRules: []api.Rule{{Action: "allow", Source: api.EntityRule{Tag: networkData.Name}}}, }, } if _, err := d.client.Profiles().Create(profile); 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. _, err = d.client.WorkloadEndpoints().Create(endpoint) 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) response := &network.CreateEndpointResponse{ Interface: &network.EndpointInterface{ MacAddress: string(d.fixedMac), }, } logutils.JSONMessage("CreateEndpoint response", response) return response, nil }
func (d NetworkDriver) DeleteNetwork(request *network.DeleteNetworkRequest) error { logutils.JSONMessage("DeleteNetwork", request) return nil }
func (d NetworkDriver) GetCapabilities() (*network.CapabilitiesResponse, error) { resp := network.CapabilitiesResponse{Scope: "global"} logutils.JSONMessage("GetCapabilities response", resp) return &resp, nil }
func (d NetworkDriver) DiscoverDelete(request *network.DiscoveryNotification) error { logutils.JSONMessage("DiscoverNew", request) log.Debugln("DiscoverDelete response JSON={}") return 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) EndpointInfo(request *network.InfoRequest) (*network.InfoResponse, error) { logutils.JSONMessage("EndpointInfo", request) return nil, 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 request.V6 { err := errors.New("IPv6 isn't supported") log.Errorln(err) return nil, err } // Default the poolID to the fixed value. poolID := i.poolIDV4 pool := "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(api.IPPoolMetadata{CIDR: *ipNet}) if err != nil || len(pools.Items) < 1 { err := errors.New("The requested subnet must match the CIDR of a " + "configured Calico IP Pool.", ) log.Errorln(err) return nil, err } pool = request.Pool poolID = request.Pool } // 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": "0.0.0.0/0"}, } logutils.JSONMessage("RequestPool response", resp) return resp, nil }
func (i IpamDriver) GetCapabilities() (*ipam.CapabilitiesResponse, error) { resp := ipam.CapabilitiesResponse{} logutils.JSONMessage("GetCapabilities response", resp) return &resp, 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 } 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 if request.PoolID != PoolIDV4 { poolsClient := i.client.IPPools() _, ipNet, err := caliconet.ParseCIDR(request.PoolID) if err != nil { err = errors.Wrapf(err, "Invalid CIDR - %v", request.PoolID) log.Errorln(err) return nil, err } pool, err := poolsClient.Get(api.IPPoolMetadata{CIDR: *ipNet}) if err != nil { err := errors.New("The network references a Calico pool which " + "has been deleted. Please re-instate the " + "Calico pool before using the network.") log.Errorln(err) return nil, err } poolV4 = []caliconet.IPNet{caliconet.IPNet{IPNet: pool.Metadata.CIDR.IPNet}} log.Debugln("Using specific pool ", poolV4) } // Auto assign an IP address. // IPv4 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( datastoreClient.AutoAssignArgs{ Num4: 1, Num6: 0, Hostname: hostname, IPv4Pools: poolV4, }, ) 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 := datastoreClient.AssignIPArgs{ IP: caliconet.IP{IP: ip}, Hostname: hostname, } err := i.client.IPAM().AssignIP(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. resp := &ipam.RequestAddressResponse{ Address: fmt.Sprintf("%v/%v", IPs[0], "32"), } logutils.JSONMessage("RequestAddress response", resp) return resp, nil }
func (i IpamDriver) ReleasePool(request *ipam.ReleasePoolRequest) error { logutils.JSONMessage("ReleasePool", request) return nil }