From cc6f0e85f994cab66fb63c4dd8b77b4332151748 Mon Sep 17 00:00:00 2001 From: baude Date: Wed, 4 Jul 2018 10:51:20 -0500 Subject: more changes to compile darwin this should represent the last major changes to get darwin to **compile**. again, the purpose here is to get darwin to compile so that we can eventually implement a ci task that would protect against regressions for darwin compilation. i have left the manual darwin compilation largely static still and in fact now only interject (manually) two build tags to assist with the build. trevor king has great ideas on how to make this better and i will defer final implementation of those to him. Signed-off-by: baude Closes: #1047 Approved by: rhatdan --- libpod/boltdb_state.go | 33 +--- libpod/boltdb_state_internal.go | 60 +------ libpod/boltdb_state_linux.go | 67 ++++++++ libpod/boltdb_state_unsupported.go | 11 ++ libpod/container_inspect.go | 31 +--- libpod/container_internal.go | 249 +---------------------------- libpod/container_internal_linux.go | 265 +++++++++++++++++++++++++++++++ libpod/container_internal_unsupported.go | 26 +++ libpod/container_linux.go | 38 +++++ libpod/container_unsupported.go | 8 + libpod/finished_64.go | 2 +- libpod/finished_unsupported.go | 12 ++ libpod/networking_linux.go | 33 ++++ libpod/networking_unsupported.go | 27 ++++ libpod/oci.go | 61 ------- libpod/oci_linux.go | 63 ++++++++ libpod/oci_unsupported.go | 15 +- 17 files changed, 573 insertions(+), 428 deletions(-) create mode 100644 libpod/boltdb_state_linux.go create mode 100644 libpod/boltdb_state_unsupported.go create mode 100644 libpod/finished_unsupported.go create mode 100644 libpod/networking_unsupported.go (limited to 'libpod') 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 +} -- cgit v1.2.3-54-g00ecf