diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state.go | 33 | ||||
-rw-r--r-- | libpod/boltdb_state_internal.go | 60 | ||||
-rw-r--r-- | libpod/boltdb_state_linux.go | 67 | ||||
-rw-r--r-- | libpod/boltdb_state_unsupported.go | 11 | ||||
-rw-r--r-- | libpod/container_inspect.go | 31 | ||||
-rw-r--r-- | libpod/container_internal.go | 249 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 265 | ||||
-rw-r--r-- | libpod/container_internal_unsupported.go | 26 | ||||
-rw-r--r-- | libpod/container_linux.go | 38 | ||||
-rw-r--r-- | libpod/container_unsupported.go | 8 | ||||
-rw-r--r-- | libpod/finished_64.go | 2 | ||||
-rw-r--r-- | libpod/finished_unsupported.go | 12 | ||||
-rw-r--r-- | libpod/networking_linux.go | 33 | ||||
-rw-r--r-- | libpod/networking_unsupported.go | 27 | ||||
-rw-r--r-- | libpod/oci.go | 61 | ||||
-rw-r--r-- | libpod/oci_linux.go | 63 | ||||
-rw-r--r-- | libpod/oci_unsupported.go | 15 |
17 files changed, 573 insertions, 428 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index d7c950fa0..45d09348e 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -7,7 +7,6 @@ import ( "github.com/boltdb/bolt" "github.com/pkg/errors" - "github.com/sirupsen/logrus" ) // BoltState is a state implementation backed by a Bolt DB @@ -443,32 +442,7 @@ func (s *BoltState) UpdateContainer(ctr *Container) error { } // Do we need to replace the container's netns? - if netNSPath != "" { - // Check if the container's old state has a good netns - if ctr.state.NetNS != nil && netNSPath == ctr.state.NetNS.Path() { - newState.NetNS = ctr.state.NetNS - } else { - // Tear down the existing namespace - if err := s.runtime.teardownNetNS(ctr); err != nil { - logrus.Warnf(err.Error()) - } - - // Open the new network namespace - ns, err := joinNetNS(netNSPath) - if err == nil { - newState.NetNS = ns - } else { - logrus.Errorf("error joining network namespace for container %s", ctr.ID()) - ctr.valid = false - } - } - } else { - // The container no longer has a network namespace - // Tear down the old one - if err := s.runtime.teardownNetNS(ctr); err != nil { - logrus.Warnf(err.Error()) - } - } + ctr.setNamespace(netNSPath, newState) // New state compiled successfully, swap it into the current state ctr.state = newState @@ -490,10 +464,7 @@ func (s *BoltState) SaveContainer(ctr *Container) error { if err != nil { return errors.Wrapf(err, "error marshalling container %s state to JSON", ctr.ID()) } - netNSPath := "" - if ctr.state.NetNS != nil { - netNSPath = ctr.state.NetNS.Path() - } + netNSPath := ctr.setNamespaceStatePath() ctrID := []byte(ctr.ID()) diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index e8aa6860b..69e7bee21 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -205,60 +205,6 @@ func getRuntimeConfigBucket(tx *bolt.Tx) (*bolt.Bucket, error) { return bkt, nil } -func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error { - valid := true - ctrBkt := ctrsBkt.Bucket(id) - if ctrBkt == nil { - return errors.Wrapf(ErrNoSuchCtr, "container %s not found in DB", string(id)) - } - - configBytes := ctrBkt.Get(configKey) - if configBytes == nil { - return errors.Wrapf(ErrInternal, "container %s missing config key in DB", string(id)) - } - - stateBytes := ctrBkt.Get(stateKey) - if stateBytes == nil { - return errors.Wrapf(ErrInternal, "container %s missing state key in DB", string(id)) - } - - netNSBytes := ctrBkt.Get(netNSKey) - - if err := json.Unmarshal(configBytes, ctr.config); err != nil { - return errors.Wrapf(err, "error unmarshalling container %s config", string(id)) - } - - if err := json.Unmarshal(stateBytes, ctr.state); err != nil { - return errors.Wrapf(err, "error unmarshalling container %s state", string(id)) - } - - // The container may not have a network namespace, so it's OK if this is - // nil - if netNSBytes != nil { - nsPath := string(netNSBytes) - netNS, err := joinNetNS(nsPath) - if err == nil { - ctr.state.NetNS = netNS - } else { - logrus.Errorf("error joining network namespace for container %s", ctr.ID()) - valid = false - } - } - - // Get the lock - lockPath := filepath.Join(s.lockDir, string(id)) - lock, err := storage.GetLockfile(lockPath) - if err != nil { - return errors.Wrapf(err, "error retrieving lockfile for container %s", string(id)) - } - ctr.lock = lock - - ctr.runtime = s.runtime - ctr.valid = valid - - return nil -} - func (s *BoltState) getPodFromDB(id []byte, pod *Pod, podBkt *bolt.Bucket) error { podDB := podBkt.Bucket(id) if podDB == nil { @@ -310,11 +256,7 @@ func (s *BoltState) addContainer(ctr *Container, pod *Pod) error { if err != nil { return errors.Wrapf(err, "error marshalling container %s state to JSON", ctr.ID()) } - netNSPath := "" - if ctr.state.NetNS != nil { - netNSPath = ctr.state.NetNS.Path() - } - + netNSPath := ctr.setNamespaceStatePath() dependsCtrs := ctr.Dependencies() ctrID := []byte(ctr.ID()) diff --git a/libpod/boltdb_state_linux.go b/libpod/boltdb_state_linux.go new file mode 100644 index 000000000..ceea955bd --- /dev/null +++ b/libpod/boltdb_state_linux.go @@ -0,0 +1,67 @@ +// +build linux + +package libpod + +import ( + "encoding/json" + "path/filepath" + + "github.com/boltdb/bolt" + "github.com/containers/storage" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error { + valid := true + ctrBkt := ctrsBkt.Bucket(id) + if ctrBkt == nil { + return errors.Wrapf(ErrNoSuchCtr, "container %s not found in DB", string(id)) + } + + configBytes := ctrBkt.Get(configKey) + if configBytes == nil { + return errors.Wrapf(ErrInternal, "container %s missing config key in DB", string(id)) + } + + stateBytes := ctrBkt.Get(stateKey) + if stateBytes == nil { + return errors.Wrapf(ErrInternal, "container %s missing state key in DB", string(id)) + } + + netNSBytes := ctrBkt.Get(netNSKey) + + if err := json.Unmarshal(configBytes, ctr.config); err != nil { + return errors.Wrapf(err, "error unmarshalling container %s config", string(id)) + } + + if err := json.Unmarshal(stateBytes, ctr.state); err != nil { + return errors.Wrapf(err, "error unmarshalling container %s state", string(id)) + } + + // The container may not have a network namespace, so it's OK if this is + // nil + if netNSBytes != nil { + nsPath := string(netNSBytes) + netNS, err := joinNetNS(nsPath) + if err == nil { + ctr.state.NetNS = netNS + } else { + logrus.Errorf("error joining network namespace for container %s", ctr.ID()) + valid = false + } + } + + // Get the lock + lockPath := filepath.Join(s.lockDir, string(id)) + lock, err := storage.GetLockfile(lockPath) + if err != nil { + return errors.Wrapf(err, "error retrieving lockfile for container %s", string(id)) + } + ctr.lock = lock + + ctr.runtime = s.runtime + ctr.valid = valid + + return nil +} diff --git a/libpod/boltdb_state_unsupported.go b/libpod/boltdb_state_unsupported.go new file mode 100644 index 000000000..8d3749881 --- /dev/null +++ b/libpod/boltdb_state_unsupported.go @@ -0,0 +1,11 @@ +// +build !linux + +package libpod + +import ( + "github.com/boltdb/bolt" +) + +func (s *BoltState) getContainerFromDB(id []byte, ctr *Container, ctrsBkt *bolt.Bucket) error { + return ErrNotImplemented +} diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 1381341c2..a1070cf99 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -1,9 +1,6 @@ package libpod import ( - "strconv" - "strings" - "github.com/cri-o/ocicni/pkg/ocicni" "github.com/projectatomic/libpod/pkg/inspect" "github.com/sirupsen/logrus" @@ -114,33 +111,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data) } // Get information on the container's network namespace (if present) - if runtimeInfo.NetNS != nil { - // Go through our IP addresses - for _, ctrIP := range c.state.IPs { - ipWithMask := ctrIP.Address.String() - splitIP := strings.Split(ipWithMask, "/") - mask, _ := strconv.Atoi(splitIP[1]) - if ctrIP.Version == "4" { - data.NetworkSettings.IPAddress = splitIP[0] - data.NetworkSettings.IPPrefixLen = mask - data.NetworkSettings.Gateway = ctrIP.Gateway.String() - } else { - data.NetworkSettings.GlobalIPv6Address = splitIP[0] - data.NetworkSettings.GlobalIPv6PrefixLen = mask - data.NetworkSettings.IPv6Gateway = ctrIP.Gateway.String() - } - } - - // Set network namespace path - data.NetworkSettings.SandboxKey = runtimeInfo.NetNS.Path() - - // Set MAC address of interface linked with network namespace path - for _, i := range c.state.Interfaces { - if i.Sandbox == data.NetworkSettings.SandboxKey { - data.NetworkSettings.MacAddress = i.Mac - } - } - } + data = c.getContainerNetworkInfo(data) if size { rootFsSize, err := c.rootFsSize() diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 22b398709..452be176c 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -8,7 +8,6 @@ import ( "io" "io/ioutil" "os" - "path" "path/filepath" "strings" "syscall" @@ -17,14 +16,12 @@ import ( "github.com/containers/storage" "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/chrootarchive" - "github.com/containers/storage/pkg/idtools" "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/stringid" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" - crioAnnotations "github.com/projectatomic/libpod/pkg/annotations" "github.com/projectatomic/libpod/pkg/chrootuser" "github.com/projectatomic/libpod/pkg/hooks" "github.com/projectatomic/libpod/pkg/hooks/exec" @@ -33,7 +30,6 @@ import ( "github.com/projectatomic/libpod/pkg/util" "github.com/sirupsen/logrus" "github.com/ulule/deepcopier" - "golang.org/x/sys/unix" "golang.org/x/text/language" ) @@ -748,9 +744,8 @@ func (c *Container) mountStorage() (err error) { if !mounted { shmOptions := fmt.Sprintf("mode=1777,size=%d", c.config.ShmSize) - if err := unix.Mount("shm", c.config.ShmDir, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, - label.FormatMountLabel(shmOptions, c.config.MountLabel)); err != nil { - return errors.Wrapf(err, "failed to mount shm tmpfs %q", c.config.ShmDir) + if err := c.mountSHM(shmOptions); err != nil { + return err } if err := os.Chown(c.config.ShmDir, c.RootUID(), c.RootGID()); err != nil { return errors.Wrapf(err, "failed to chown %s", c.config.ShmDir) @@ -786,53 +781,6 @@ func (c *Container) mountStorage() (err error) { return c.save() } -// prepare mounts the container and sets up other required resources like net -// namespaces -func (c *Container) prepare() (err error) { - // Mount storage if not mounted - if err := c.mountStorage(); err != nil { - return err - } - - // Set up network namespace if not already set up - if c.config.CreateNetNS && c.state.NetNS == nil && !c.config.PostConfigureNetNS { - if err := c.runtime.createNetNS(c); err != nil { - // Tear down storage before exiting to make sure we - // don't leak mounts - if err2 := c.cleanupStorage(); err2 != nil { - logrus.Errorf("Error cleaning up storage for container %s: %v", c.ID(), err2) - } - return err - } - } - - return nil -} - -// cleanupNetwork unmounts and cleans up the container's network -func (c *Container) cleanupNetwork() error { - if c.state.NetNS == nil { - logrus.Debugf("Network is already cleaned up, skipping...") - return nil - } - - // Stop the container's network namespace (if it has one) - if err := c.runtime.teardownNetNS(c); err != nil { - logrus.Errorf("unable to cleanup network for container %s: %q", c.ID(), err) - } - - c.state.NetNS = nil - c.state.IPs = nil - c.state.Interfaces = nil - c.state.Routes = nil - - if c.valid { - return c.save() - } - - return nil -} - // cleanupStorage unmounts and cleans up the container's root filesystem func (c *Container) cleanupStorage() error { if !c.state.Mounted { @@ -842,10 +790,8 @@ func (c *Container) cleanupStorage() error { } for _, mount := range c.config.Mounts { - if err := unix.Unmount(mount, unix.MNT_DETACH); err != nil { - if err != syscall.EINVAL { - logrus.Warnf("container %s failed to unmount %s : %v", c.ID(), mount, err) - } + if err := c.unmountSHM(mount); err != nil { + return err } } if c.config.Rootfs != "" { @@ -1178,193 +1124,6 @@ func (c *Container) generateHosts() (string, error) { return c.writeStringToRundir("hosts", hosts) } -// Generate spec for a container -// Accepts a map of the container's dependencies -func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { - g := generate.NewFromSpec(c.config.Spec) - - // If network namespace was requested, add it now - if c.config.CreateNetNS { - if c.config.PostConfigureNetNS { - g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, "") - } else { - g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, c.state.NetNS.Path()) - } - } - - // Remove the default /dev/shm mount to ensure we overwrite it - g.RemoveMount("/dev/shm") - - // Add bind mounts to container - for dstPath, srcPath := range c.state.BindMounts { - newMount := spec.Mount{ - Type: "bind", - Source: srcPath, - Destination: dstPath, - Options: []string{"rw", "bind"}, - } - if !MountExists(g.Mounts(), dstPath) { - g.AddMount(newMount) - } else { - logrus.Warnf("User mount overriding libpod mount at %q", dstPath) - } - } - - var err error - if !rootless.IsRootless() { - if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil { - return nil, errors.Wrapf(err, "error setting up OCI Hooks") - } - } - - // Bind builtin image volumes - if c.config.Rootfs == "" && c.config.ImageVolumes { - if err := c.addImageVolumes(ctx, &g); err != nil { - return nil, errors.Wrapf(err, "error mounting image volumes") - } - } - - if c.config.User != "" { - if !c.state.Mounted { - return nil, errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate User field", c.ID()) - } - uid, gid, err := chrootuser.GetUser(c.state.Mountpoint, c.config.User) - if err != nil { - return nil, err - } - // User and Group must go together - g.SetProcessUID(uid) - g.SetProcessGID(gid) - } - - // Add addition groups if c.config.GroupAdd is not empty - if len(c.config.Groups) > 0 { - if !c.state.Mounted { - return nil, errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to add additional groups", c.ID()) - } - for _, group := range c.config.Groups { - gid, err := chrootuser.GetGroup(c.state.Mountpoint, group) - if err != nil { - return nil, err - } - g.AddProcessAdditionalGid(gid) - } - } - - // Look up and add groups the user belongs to, if a group wasn't directly specified - if !rootless.IsRootless() && !strings.Contains(c.config.User, ":") { - groups, err := chrootuser.GetAdditionalGroupsForUser(c.state.Mountpoint, uint64(g.Config.Process.User.UID)) - if err != nil && errors.Cause(err) != chrootuser.ErrNoSuchUser { - return nil, err - } - for _, gid := range groups { - g.AddProcessAdditionalGid(gid) - } - } - - // Add shared namespaces from other containers - if c.config.IPCNsCtr != "" { - if err := c.addNamespaceContainer(&g, IPCNS, c.config.IPCNsCtr, spec.IPCNamespace); err != nil { - return nil, err - } - } - if c.config.MountNsCtr != "" { - if err := c.addNamespaceContainer(&g, MountNS, c.config.MountNsCtr, spec.MountNamespace); err != nil { - return nil, err - } - } - if c.config.NetNsCtr != "" { - if err := c.addNamespaceContainer(&g, NetNS, c.config.NetNsCtr, spec.NetworkNamespace); err != nil { - return nil, err - } - } - if c.config.PIDNsCtr != "" { - if err := c.addNamespaceContainer(&g, PIDNS, c.config.PIDNsCtr, string(spec.PIDNamespace)); err != nil { - return nil, err - } - } - if c.config.UserNsCtr != "" { - if err := c.addNamespaceContainer(&g, UserNS, c.config.UserNsCtr, spec.UserNamespace); err != nil { - return nil, err - } - } - if c.config.UTSNsCtr != "" { - if err := c.addNamespaceContainer(&g, UTSNS, c.config.UTSNsCtr, spec.UTSNamespace); err != nil { - return nil, err - } - } - if c.config.CgroupNsCtr != "" { - if err := c.addNamespaceContainer(&g, CgroupNS, c.config.CgroupNsCtr, spec.CgroupNamespace); err != nil { - return nil, err - } - } - - if c.config.Rootfs == "" { - if err := idtools.MkdirAllAs(c.state.RealMountpoint, 0700, c.RootUID(), c.RootGID()); err != nil { - return nil, err - } - } - - g.SetRootPath(c.state.RealMountpoint) - g.AddAnnotation(crioAnnotations.Created, c.config.CreatedTime.Format(time.RFC3339Nano)) - g.AddAnnotation("org.opencontainers.image.stopSignal", fmt.Sprintf("%d", c.config.StopSignal)) - - g.SetHostname(c.Hostname()) - g.AddProcessEnv("HOSTNAME", g.Config.Hostname) - - // Only add container environment variable if not already present - foundContainerEnv := false - for _, env := range g.Config.Process.Env { - if strings.HasPrefix(env, "container=") { - foundContainerEnv = true - break - } - } - if !foundContainerEnv { - g.AddProcessEnv("container", "libpod") - } - - if rootless.IsRootless() { - g.SetLinuxCgroupsPath("") - } else if c.runtime.config.CgroupManager == SystemdCgroupsManager { - // When runc is set to use Systemd as a cgroup manager, it - // expects cgroups to be passed as follows: - // slice:prefix:name - systemdCgroups := fmt.Sprintf("%s:libpod:%s", path.Base(c.config.CgroupParent), c.ID()) - logrus.Debugf("Setting CGroups for container %s to %s", c.ID(), systemdCgroups) - g.SetLinuxCgroupsPath(systemdCgroups) - } else { - cgroupPath, err := c.CGroupPath() - if err != nil { - return nil, err - } - logrus.Debugf("Setting CGroup path for container %s to %s", c.ID(), cgroupPath) - g.SetLinuxCgroupsPath(cgroupPath) - } - - return g.Config, nil -} - -// Add an existing container's namespace to the spec -func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr string, specNS string) error { - nsCtr, err := c.runtime.state.Container(ctr) - if err != nil { - return errors.Wrapf(err, "error retrieving dependency %s of container %s from state", ctr, c.ID()) - } - - // TODO need unlocked version of this for use in pods - nsPath, err := nsCtr.NamespacePath(ns) - if err != nil { - return err - } - - if err := g.AddOrReplaceLinuxNamespace(specNS, nsPath); err != nil { - return err - } - - return nil -} - func (c *Container) addImageVolumes(ctx context.Context, g *generate.Generator) error { mountPoint := c.state.Mountpoint if !c.state.Mounted { diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 41b8a86fc..327c78045 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -3,11 +3,25 @@ package libpod import ( + "context" "fmt" + "path" "path/filepath" + "strings" + "syscall" + "time" "github.com/containerd/cgroups" + "github.com/containers/storage/pkg/idtools" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" + crioAnnotations "github.com/projectatomic/libpod/pkg/annotations" + "github.com/projectatomic/libpod/pkg/chrootuser" + "github.com/projectatomic/libpod/pkg/rootless" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) // cleanupCgroup cleans up residual CGroups after container execution @@ -50,3 +64,254 @@ func (c *Container) cleanupCgroups() error { return nil } + +func (c *Container) mountSHM(shmOptions string) error { + if err := unix.Mount("shm", c.config.ShmDir, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, + label.FormatMountLabel(shmOptions, c.config.MountLabel)); err != nil { + return errors.Wrapf(err, "failed to mount shm tmpfs %q", c.config.ShmDir) + } + return nil +} + +func (c *Container) unmountSHM(mount string) error { + if err := unix.Unmount(mount, unix.MNT_DETACH); err != nil { + if err != syscall.EINVAL { + logrus.Warnf("container %s failed to unmount %s : %v", c.ID(), mount, err) + } + } + return nil +} + +// prepare mounts the container and sets up other required resources like net +// namespaces +func (c *Container) prepare() (err error) { + // Mount storage if not mounted + if err := c.mountStorage(); err != nil { + return err + } + + // Set up network namespace if not already set up + if c.config.CreateNetNS && c.state.NetNS == nil && !c.config.PostConfigureNetNS { + if err := c.runtime.createNetNS(c); err != nil { + // Tear down storage before exiting to make sure we + // don't leak mounts + if err2 := c.cleanupStorage(); err2 != nil { + logrus.Errorf("Error cleaning up storage for container %s: %v", c.ID(), err2) + } + return err + } + } + + return nil +} + +// cleanupNetwork unmounts and cleans up the container's network +func (c *Container) cleanupNetwork() error { + if c.state.NetNS == nil { + logrus.Debugf("Network is already cleaned up, skipping...") + return nil + } + + // Stop the container's network namespace (if it has one) + if err := c.runtime.teardownNetNS(c); err != nil { + logrus.Errorf("unable to cleanup network for container %s: %q", c.ID(), err) + } + + c.state.NetNS = nil + c.state.IPs = nil + c.state.Interfaces = nil + c.state.Routes = nil + + if c.valid { + return c.save() + } + + return nil +} + +// Generate spec for a container +// Accepts a map of the container's dependencies +func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { + g := generate.NewFromSpec(c.config.Spec) + + // If network namespace was requested, add it now + if c.config.CreateNetNS { + if c.config.PostConfigureNetNS { + g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, "") + } else { + g.AddOrReplaceLinuxNamespace(spec.NetworkNamespace, c.state.NetNS.Path()) + } + } + + // Remove the default /dev/shm mount to ensure we overwrite it + g.RemoveMount("/dev/shm") + + // Add bind mounts to container + for dstPath, srcPath := range c.state.BindMounts { + newMount := spec.Mount{ + Type: "bind", + Source: srcPath, + Destination: dstPath, + Options: []string{"rw", "bind"}, + } + if !MountExists(g.Mounts(), dstPath) { + g.AddMount(newMount) + } else { + logrus.Warnf("User mount overriding libpod mount at %q", dstPath) + } + } + + var err error + if !rootless.IsRootless() { + if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil { + return nil, errors.Wrapf(err, "error setting up OCI Hooks") + } + } + + // Bind builtin image volumes + if c.config.Rootfs == "" && c.config.ImageVolumes { + if err := c.addImageVolumes(ctx, &g); err != nil { + return nil, errors.Wrapf(err, "error mounting image volumes") + } + } + + if c.config.User != "" { + if !c.state.Mounted { + return nil, errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate User field", c.ID()) + } + uid, gid, err := chrootuser.GetUser(c.state.Mountpoint, c.config.User) + if err != nil { + return nil, err + } + // User and Group must go together + g.SetProcessUID(uid) + g.SetProcessGID(gid) + } + + // Add addition groups if c.config.GroupAdd is not empty + if len(c.config.Groups) > 0 { + if !c.state.Mounted { + return nil, errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to add additional groups", c.ID()) + } + for _, group := range c.config.Groups { + gid, err := chrootuser.GetGroup(c.state.Mountpoint, group) + if err != nil { + return nil, err + } + g.AddProcessAdditionalGid(gid) + } + } + + // Look up and add groups the user belongs to, if a group wasn't directly specified + if !rootless.IsRootless() && !strings.Contains(c.config.User, ":") { + groups, err := chrootuser.GetAdditionalGroupsForUser(c.state.Mountpoint, uint64(g.Config.Process.User.UID)) + if err != nil && errors.Cause(err) != chrootuser.ErrNoSuchUser { + return nil, err + } + for _, gid := range groups { + g.AddProcessAdditionalGid(gid) + } + } + + // Add shared namespaces from other containers + if c.config.IPCNsCtr != "" { + if err := c.addNamespaceContainer(&g, IPCNS, c.config.IPCNsCtr, spec.IPCNamespace); err != nil { + return nil, err + } + } + if c.config.MountNsCtr != "" { + if err := c.addNamespaceContainer(&g, MountNS, c.config.MountNsCtr, spec.MountNamespace); err != nil { + return nil, err + } + } + if c.config.NetNsCtr != "" { + if err := c.addNamespaceContainer(&g, NetNS, c.config.NetNsCtr, spec.NetworkNamespace); err != nil { + return nil, err + } + } + if c.config.PIDNsCtr != "" { + if err := c.addNamespaceContainer(&g, PIDNS, c.config.PIDNsCtr, string(spec.PIDNamespace)); err != nil { + return nil, err + } + } + if c.config.UserNsCtr != "" { + if err := c.addNamespaceContainer(&g, UserNS, c.config.UserNsCtr, spec.UserNamespace); err != nil { + return nil, err + } + } + if c.config.UTSNsCtr != "" { + if err := c.addNamespaceContainer(&g, UTSNS, c.config.UTSNsCtr, spec.UTSNamespace); err != nil { + return nil, err + } + } + if c.config.CgroupNsCtr != "" { + if err := c.addNamespaceContainer(&g, CgroupNS, c.config.CgroupNsCtr, spec.CgroupNamespace); err != nil { + return nil, err + } + } + + if c.config.Rootfs == "" { + if err := idtools.MkdirAllAs(c.state.RealMountpoint, 0700, c.RootUID(), c.RootGID()); err != nil { + return nil, err + } + } + + g.SetRootPath(c.state.RealMountpoint) + g.AddAnnotation(crioAnnotations.Created, c.config.CreatedTime.Format(time.RFC3339Nano)) + g.AddAnnotation("org.opencontainers.image.stopSignal", fmt.Sprintf("%d", c.config.StopSignal)) + + g.SetHostname(c.Hostname()) + g.AddProcessEnv("HOSTNAME", g.Config.Hostname) + + // Only add container environment variable if not already present + foundContainerEnv := false + for _, env := range g.Config.Process.Env { + if strings.HasPrefix(env, "container=") { + foundContainerEnv = true + break + } + } + if !foundContainerEnv { + g.AddProcessEnv("container", "libpod") + } + + if rootless.IsRootless() { + g.SetLinuxCgroupsPath("") + } else if c.runtime.config.CgroupManager == SystemdCgroupsManager { + // When runc is set to use Systemd as a cgroup manager, it + // expects cgroups to be passed as follows: + // slice:prefix:name + systemdCgroups := fmt.Sprintf("%s:libpod:%s", path.Base(c.config.CgroupParent), c.ID()) + logrus.Debugf("Setting CGroups for container %s to %s", c.ID(), systemdCgroups) + g.SetLinuxCgroupsPath(systemdCgroups) + } else { + cgroupPath, err := c.CGroupPath() + if err != nil { + return nil, err + } + logrus.Debugf("Setting CGroup path for container %s to %s", c.ID(), cgroupPath) + g.SetLinuxCgroupsPath(cgroupPath) + } + + return g.Config, nil +} + +// Add an existing container's namespace to the spec +func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr string, specNS string) error { + nsCtr, err := c.runtime.state.Container(ctr) + if err != nil { + return errors.Wrapf(err, "error retrieving dependency %s of container %s from state", ctr, c.ID()) + } + + // TODO need unlocked version of this for use in pods + nsPath, err := nsCtr.NamespacePath(ns) + if err != nil { + return err + } + + if err := g.AddOrReplaceLinuxNamespace(specNS, nsPath); err != nil { + return err + } + + return nil +} diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go index 015536850..975d6d714 100644 --- a/libpod/container_internal_unsupported.go +++ b/libpod/container_internal_unsupported.go @@ -2,6 +2,32 @@ package libpod +import ( + "context" + + spec "github.com/opencontainers/runtime-spec/specs-go" +) + func (c *Container) cleanupCgroups() error { return ErrOSNotSupported } + +func (c *Container) mountSHM(shmOptions string) error { + return ErrNotImplemented +} + +func (c *Container) unmountSHM(mount string) error { + return ErrNotImplemented +} + +func (c *Container) prepare() (err error) { + return ErrNotImplemented +} + +func (c *Container) cleanupNetwork() error { + return ErrNotImplemented +} + +func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { + return nil, ErrNotImplemented +} diff --git a/libpod/container_linux.go b/libpod/container_linux.go index 823a590dd..2330f27a7 100644 --- a/libpod/container_linux.go +++ b/libpod/container_linux.go @@ -4,6 +4,7 @@ package libpod import ( "github.com/containernetworking/plugins/pkg/ns" + "github.com/sirupsen/logrus" ) type containerPlatformState struct { @@ -13,3 +14,40 @@ type containerPlatformState struct { // told to join another container's network namespace NetNS ns.NetNS `json:"-"` } + +func (ctr *Container) setNamespace(netNSPath string, newState *containerState) error { + if netNSPath != "" { + // Check if the container's old state has a good netns + if ctr.state.NetNS != nil && netNSPath == ctr.state.NetNS.Path() { + newState.NetNS = ctr.state.NetNS + } else { + // Tear down the existing namespace + if err := ctr.runtime.teardownNetNS(ctr); err != nil { + logrus.Warnf(err.Error()) + } + + // Open the new network namespace + ns, err := joinNetNS(netNSPath) + if err == nil { + newState.NetNS = ns + } else { + logrus.Errorf("error joining network namespace for container %s", ctr.ID()) + ctr.valid = false + } + } + } else { + // The container no longer has a network namespace + // Tear down the old one + if err := ctr.runtime.teardownNetNS(ctr); err != nil { + logrus.Warnf(err.Error()) + } + } + return nil +} + +func (ctr *Container) setNamespaceStatePath() string { + if ctr.state.NetNS != nil { + return ctr.state.NetNS.Path() + } + return "" +} diff --git a/libpod/container_unsupported.go b/libpod/container_unsupported.go index e214b9465..5b923b52a 100644 --- a/libpod/container_unsupported.go +++ b/libpod/container_unsupported.go @@ -3,3 +3,11 @@ package libpod type containerPlatformState struct{} + +func (ctr *Container) setNamespace(netNSPath string, newState *containerState) error { + return ErrNotImplemented +} + +func (ctr *Container) setNamespaceStatePath() string { + return "" +} diff --git a/libpod/finished_64.go b/libpod/finished_64.go index 11dbc9181..3688afa84 100644 --- a/libpod/finished_64.go +++ b/libpod/finished_64.go @@ -1,4 +1,4 @@ -// +build !arm,!386 +// +build !arm,!386,linux package libpod diff --git a/libpod/finished_unsupported.go b/libpod/finished_unsupported.go new file mode 100644 index 000000000..a893d668c --- /dev/null +++ b/libpod/finished_unsupported.go @@ -0,0 +1,12 @@ +// +build darwin,!linux + +package libpod + +import ( + "os" + "time" +) + +func getFinishedTime(fi os.FileInfo) time.Time { + return time.Time{} +} diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index ee90b765c..59666b534 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -8,6 +8,7 @@ import ( "net" "os" "path/filepath" + "strconv" "strings" "syscall" @@ -15,6 +16,7 @@ import ( "github.com/containernetworking/plugins/pkg/ns" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" + "github.com/projectatomic/libpod/pkg/inspect" "github.com/projectatomic/libpod/utils" "github.com/sirupsen/logrus" "github.com/vishvananda/netlink" @@ -228,3 +230,34 @@ func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) { }) return netStats, err } + +func (c *Container) getContainerNetworkInfo(data *inspect.ContainerInspectData) *inspect.ContainerInspectData { + if c.state.NetNS != nil { + // Go through our IP addresses + for _, ctrIP := range c.state.IPs { + ipWithMask := ctrIP.Address.String() + splitIP := strings.Split(ipWithMask, "/") + mask, _ := strconv.Atoi(splitIP[1]) + if ctrIP.Version == "4" { + data.NetworkSettings.IPAddress = splitIP[0] + data.NetworkSettings.IPPrefixLen = mask + data.NetworkSettings.Gateway = ctrIP.Gateway.String() + } else { + data.NetworkSettings.GlobalIPv6Address = splitIP[0] + data.NetworkSettings.GlobalIPv6PrefixLen = mask + data.NetworkSettings.IPv6Gateway = ctrIP.Gateway.String() + } + } + + // Set network namespace path + data.NetworkSettings.SandboxKey = c.state.NetNS.Path() + + // Set MAC address of interface linked with network namespace path + for _, i := range c.state.Interfaces { + if i.Sandbox == data.NetworkSettings.SandboxKey { + data.NetworkSettings.MacAddress = i.Mac + } + } + } + return data +} diff --git a/libpod/networking_unsupported.go b/libpod/networking_unsupported.go new file mode 100644 index 000000000..4ff8ecd62 --- /dev/null +++ b/libpod/networking_unsupported.go @@ -0,0 +1,27 @@ +// +build !linux + +package libpod + +import ( + "github.com/projectatomic/libpod/pkg/inspect" +) + +func JoinNetworkNameSpace(netNSBytes []byte) (*Container, bool, error) { + return nil, false, ErrNotImplemented +} + +func (r *Runtime) setupNetNS(ctr *Container) (err error) { + return ErrNotImplemented +} + +func (r *Runtime) teardownNetNS(ctr *Container) error { + return ErrNotImplemented +} + +func (r *Runtime) createNetNS(ctr *Container) (err error) { + return ErrNotImplemented +} + +func (c *Container) getContainerNetworkInfo(data *inspect.ContainerInspectData) *inspect.ContainerInspectData { + return nil +} diff --git a/libpod/oci.go b/libpod/oci.go index 20d533726..fcb36241b 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -11,11 +11,9 @@ import ( "runtime" "strconv" "strings" - "sync" "syscall" "time" - "github.com/containers/storage/pkg/idtools" "github.com/coreos/go-systemd/activation" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" @@ -110,15 +108,6 @@ func newOCIRuntime(name string, path string, conmonPath string, conmonEnv []stri return runtime, nil } -// newPipe creates a unix socket pair for communication -func newPipe() (parent *os.File, child *os.File, err error) { - fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) - if err != nil { - return nil, nil, err - } - return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil -} - // Create systemd unit name for cgroup scopes func createUnitName(prefix string, name string) string { return fmt.Sprintf("%s-%s.scope", prefix, name) @@ -187,56 +176,6 @@ func waitPidsStop(pids []int, timeout time.Duration) error { } } -// CreateContainer creates a container in the OCI runtime -// TODO terminal support for container -// Presently just ignoring conmon opts related to it -func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) (err error) { - if ctr.state.UserNSRoot == "" { - // no need of an intermediate mount ns - return r.createOCIContainer(ctr, cgroupParent) - } - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - runtime.LockOSThread() - - fd, err := os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid())) - if err != nil { - return - } - defer fd.Close() - - // create a new mountns on the current thread - if err = unix.Unshare(unix.CLONE_NEWNS); err != nil { - return - } - defer unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS) - - // don't spread our mounts around - err = unix.Mount("/", "/", "none", unix.MS_REC|unix.MS_SLAVE, "") - if err != nil { - return - } - err = unix.Mount(ctr.state.Mountpoint, ctr.state.RealMountpoint, "none", unix.MS_BIND, "") - if err != nil { - return - } - if err := idtools.MkdirAllAs(ctr.state.DestinationRunDir, 0700, ctr.RootUID(), ctr.RootGID()); err != nil { - return - } - - err = unix.Mount(ctr.state.RunDir, ctr.state.DestinationRunDir, "none", unix.MS_BIND, "") - if err != nil { - return - } - err = r.createOCIContainer(ctr, cgroupParent) - }() - wg.Wait() - - return err -} - func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string) (err error) { var stderrBuf bytes.Buffer diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index 14373cbb2..2128b7481 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -7,11 +7,15 @@ import ( "os" "os/exec" "path/filepath" + "runtime" + "sync" "github.com/containerd/cgroups" + "github.com/containers/storage/pkg/idtools" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/projectatomic/libpod/utils" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) func (r *OCIRuntime) moveConmonToCgroup(ctr *Container, cgroupParent string, cmd *exec.Cmd) error { @@ -39,3 +43,62 @@ func (r *OCIRuntime) moveConmonToCgroup(ctr *Container, cgroupParent string, cmd } return nil } + +// newPipe creates a unix socket pair for communication +func newPipe() (parent *os.File, child *os.File, err error) { + fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0) + if err != nil { + return nil, nil, err + } + return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil +} + +// CreateContainer creates a container in the OCI runtime +// TODO terminal support for container +// Presently just ignoring conmon opts related to it +func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) (err error) { + if ctr.state.UserNSRoot == "" { + // no need of an intermediate mount ns + return r.createOCIContainer(ctr, cgroupParent) + } + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + runtime.LockOSThread() + + fd, err := os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid())) + if err != nil { + return + } + defer fd.Close() + + // create a new mountns on the current thread + if err = unix.Unshare(unix.CLONE_NEWNS); err != nil { + return + } + defer unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS) + + // don't spread our mounts around + err = unix.Mount("/", "/", "none", unix.MS_REC|unix.MS_SLAVE, "") + if err != nil { + return + } + err = unix.Mount(ctr.state.Mountpoint, ctr.state.RealMountpoint, "none", unix.MS_BIND, "") + if err != nil { + return + } + if err := idtools.MkdirAllAs(ctr.state.DestinationRunDir, 0700, ctr.RootUID(), ctr.RootGID()); err != nil { + return + } + + err = unix.Mount(ctr.state.RunDir, ctr.state.DestinationRunDir, "none", unix.MS_BIND, "") + if err != nil { + return + } + err = r.createOCIContainer(ctr, cgroupParent) + }() + wg.Wait() + + return err +} diff --git a/libpod/oci_unsupported.go b/libpod/oci_unsupported.go index 6e5601eac..409bb117e 100644 --- a/libpod/oci_unsupported.go +++ b/libpod/oci_unsupported.go @@ -2,6 +2,19 @@ package libpod -func moveConmonToCgroup() error { +import ( + "os" + "os/exec" +) + +func (r *OCIRuntime) moveConmonToCgroup(ctr *Container, cgroupParent string, cmd *exec.Cmd) error { return ErrOSNotSupported } + +func newPipe() (parent *os.File, child *os.File, err error) { + return nil, nil, ErrNotImplemented +} + +func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string) (err error) { + return ErrNotImplemented +} |