Files
weave/prog/weaveutil/attach.go
T
Martynas Pumputis 7670fed72c Add AWSVPC setup routines to the attach script
When running in the AWSVPC mode, we need to do the following:

- Keep the HW TX offloading on.
- Check that the allocated address belongs to the default subnet,
  because only one subnet is allowed and it should be the default one.

If it is not possible to determine whether AWSVPC is on, we ignore
all the steps, because the attachment routines have to work
regardless whether the router is running and the AWSVPC detection
is done by querying the /ipinfo/tracker endpoint served by
the router.
2016-06-08 16:44:54 +01:00

145 lines
4.1 KiB
Go

package main
import (
"fmt"
"net"
"os"
"strconv"
"syscall"
docker "github.com/fsouza/go-dockerclient"
"github.com/vishvananda/netns"
"github.com/weaveworks/weave/api"
"github.com/weaveworks/weave/common"
weavenet "github.com/weaveworks/weave/net"
)
func attach(args []string) error {
if len(args) < 4 {
cmdUsage("attach-container", "[--no-multicast-route] <container-id> <bridge-name> <mtu> <cidr>...")
}
client := api.NewClient(os.Getenv("WEAVE_HTTP_ADDR"), common.Log)
keepTXOn := false
isAWSVPC := false
// In a case of an error, we skip applying necessary steps for AWSVPC, because
// "attach" should work without the weave router running.
if t, err := client.LocalRangeTracker(); err != nil {
fmt.Fprintf(os.Stderr, "unable to determine tracker: %s; skipping AWSVPC initialization\n", err)
} else if t == "awsvpc" {
isAWSVPC = true
keepTXOn = true
}
withMulticastRoute := true
if args[0] == "--no-multicast-route" {
withMulticastRoute = false
args = args[1:]
}
if isAWSVPC {
withMulticastRoute = false
}
pid, nsContainer, err := containerPidAndNs(args[0])
if err != nil {
return err
}
if nsHost, err := netns.GetFromPid(1); err != nil {
return fmt.Errorf("unable to open host namespace: %s", err)
} else if nsHost.Equal(nsContainer) {
return fmt.Errorf("Container is running in the host network namespace, and therefore cannot be\nconnected to weave. Perhaps the container was started with --net=host.")
}
mtu, err := strconv.Atoi(args[2])
if err != nil && args[3] != "" {
return fmt.Errorf("unable to parse mtu %q: %s", args[2], err)
}
cidrs, err := parseCIDRs(args[3:])
if err != nil {
return err
}
if isAWSVPC {
// Currently, we allow only IP addresses from the default subnet
if defaultSubnet, err := client.DefaultSubnet(); err != nil {
fmt.Fprintf(os.Stderr, "unable to retrieve default subnet: %s; skipping AWSVPC checks\n", err)
} else {
for _, cidr := range cidrs {
if !sameSubnet(cidr, defaultSubnet) {
format := "AWSVPC constraints violation: %s does not belong to the default subnet %s"
return fmt.Errorf(format, cidr, defaultSubnet)
}
}
}
}
err = weavenet.AttachContainer(nsContainer, fmt.Sprint(pid), weavenet.VethName, args[1], mtu, withMulticastRoute, cidrs, keepTXOn)
// If we detected an error but the container has died, tell the user that instead.
if err != nil && !processExists(pid) {
err = fmt.Errorf("Container %s died", args[0])
}
return err
}
// sameSubnet checks whether ip belongs to network's subnet
func sameSubnet(ip *net.IPNet, network *net.IPNet) bool {
if network.Contains(ip.IP) {
i1, i2 := ip.Mask.Size()
n1, n2 := network.Mask.Size()
return i1 == n1 && i2 == n2
}
return false
}
func containerPidAndNs(containerID string) (int, netns.NsHandle, error) {
c, err := docker.NewVersionedClientFromEnv("1.18")
if err != nil {
return 0, 0, fmt.Errorf("unable to connect to docker: %s", err)
}
container, err := c.InspectContainer(containerID)
if err != nil {
return 0, 0, fmt.Errorf("unable to inspect container %s: %s", containerID, err)
}
if container.State.Pid == 0 {
return 0, 0, fmt.Errorf("container %s not running", containerID)
}
ns, err := netns.GetFromPid(container.State.Pid)
if err != nil {
return 0, 0, fmt.Errorf("unable to open namespace for container %s: %s", containerID, err)
}
return container.State.Pid, ns, nil
}
func processExists(pid int) bool {
err := syscall.Kill(pid, syscall.Signal(0))
return err == nil || err == syscall.EPERM
}
func parseCIDRs(args []string) (cidrs []*net.IPNet, err error) {
for _, ipstr := range args {
ip, ipnet, err := net.ParseCIDR(ipstr)
if err != nil {
return nil, err
}
ipnet.IP = ip // we want the specific IP plus the mask
cidrs = append(cidrs, ipnet)
}
return
}
func detach(args []string) error {
if len(args) < 2 {
cmdUsage("detach-container", "<container-id> <cidr>...")
}
_, ns, err := containerPidAndNs(args[0])
if err != nil {
return err
}
cidrs, err := parseCIDRs(args[1:])
if err != nil {
return err
}
return weavenet.DetachContainer(ns, args[0], weavenet.VethName, cidrs)
}