diff options
author | Matthew Heon <matthew.heon@gmail.com> | 2017-11-01 11:24:59 -0400 |
---|---|---|
committer | Matthew Heon <matthew.heon@gmail.com> | 2017-11-01 11:24:59 -0400 |
commit | a031b83a09a8628435317a03f199cdc18b78262f (patch) | |
tree | bc017a96769ce6de33745b8b0b1304ccf38e9df0 /libkpod/sandbox | |
parent | 2b74391cd5281f6fdf391ff8ad50fd1490f6bf89 (diff) | |
download | podman-a031b83a09a8628435317a03f199cdc18b78262f.tar.gz podman-a031b83a09a8628435317a03f199cdc18b78262f.tar.bz2 podman-a031b83a09a8628435317a03f199cdc18b78262f.zip |
Initial checkin from CRI-O repo
Signed-off-by: Matthew Heon <matthew.heon@gmail.com>
Diffstat (limited to 'libkpod/sandbox')
-rw-r--r-- | libkpod/sandbox/sandbox.go | 484 |
1 files changed, 484 insertions, 0 deletions
diff --git a/libkpod/sandbox/sandbox.go b/libkpod/sandbox/sandbox.go new file mode 100644 index 000000000..d7d6569d9 --- /dev/null +++ b/libkpod/sandbox/sandbox.go @@ -0,0 +1,484 @@ +package sandbox + +import ( + "crypto/rand" + "errors" + "fmt" + "os" + "path/filepath" + "sync" + + "github.com/containernetworking/plugins/pkg/ns" + "github.com/docker/docker/pkg/mount" + "github.com/docker/docker/pkg/symlink" + "github.com/kubernetes-incubator/cri-o/oci" + "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" + "k8s.io/apimachinery/pkg/fields" + pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" + "k8s.io/kubernetes/pkg/kubelet/network/hostport" +) + +// NetNs handles data pertaining a network namespace +type NetNs struct { + sync.Mutex + ns ns.NetNS + symlink *os.File + closed bool + restored bool +} + +func (ns *NetNs) symlinkCreate(name string) error { + b := make([]byte, 4) + _, randErr := rand.Reader.Read(b) + if randErr != nil { + return randErr + } + + nsName := fmt.Sprintf("%s-%x", name, b) + symlinkPath := filepath.Join(NsRunDir, nsName) + + if err := os.Symlink(ns.ns.Path(), symlinkPath); err != nil { + return err + } + + fd, err := os.Open(symlinkPath) + if err != nil { + if removeErr := os.RemoveAll(symlinkPath); removeErr != nil { + return removeErr + } + + return err + } + + ns.symlink = fd + + return nil +} + +func (ns *NetNs) symlinkRemove() error { + if err := ns.symlink.Close(); err != nil { + return err + } + + return os.RemoveAll(ns.symlink.Name()) +} + +func isSymbolicLink(path string) (bool, error) { + fi, err := os.Lstat(path) + if err != nil { + return false, err + } + + return fi.Mode()&os.ModeSymlink == os.ModeSymlink, nil +} + +// NetNsGet returns the NetNs associated with the given nspath and name +func NetNsGet(nspath, name string) (*NetNs, error) { + if err := ns.IsNSorErr(nspath); err != nil { + return nil, ErrClosedNetNS + } + + symlink, symlinkErr := isSymbolicLink(nspath) + if symlinkErr != nil { + return nil, symlinkErr + } + + var resolvedNsPath string + if symlink { + path, err := os.Readlink(nspath) + if err != nil { + return nil, err + } + resolvedNsPath = path + } else { + resolvedNsPath = nspath + } + + netNS, err := ns.GetNS(resolvedNsPath) + if err != nil { + return nil, err + } + + netNs := &NetNs{ns: netNS, closed: false, restored: true} + + if symlink { + fd, err := os.Open(nspath) + if err != nil { + return nil, err + } + + netNs.symlink = fd + } else { + if err := netNs.symlinkCreate(name); err != nil { + return nil, err + } + } + + return netNs, nil +} + +// HostNetNsPath returns the current network namespace for the host +func HostNetNsPath() (string, error) { + netNS, err := ns.GetCurrentNS() + if err != nil { + return "", err + } + + defer netNS.Close() + return netNS.Path(), nil +} + +// Sandbox contains data surrounding kubernetes sandboxes on the server +type Sandbox struct { + id string + namespace string + // OCI pod name (eg "<namespace>-<name>-<attempt>") + name string + // Kubernetes pod name (eg, "<name>") + kubeName string + logDir string + labels fields.Set + annotations map[string]string + infraContainer *oci.Container + containers oci.ContainerStorer + processLabel string + mountLabel string + netns *NetNs + metadata *pb.PodSandboxMetadata + shmPath string + cgroupParent string + privileged bool + trusted bool + resolvPath string + hostnamePath string + hostname string + portMappings []*hostport.PortMapping + stopped bool + // ipv4 or ipv6 cache + ip string +} + +const ( + // DefaultShmSize is the default shm size + DefaultShmSize = 64 * 1024 * 1024 + // NsRunDir is the default directory in which running network namespaces + // are stored + NsRunDir = "/var/run/netns" + // PodInfraCommand is the default command when starting a pod infrastructure + // container + PodInfraCommand = "/pause" +) + +var ( + // ErrIDEmpty is the erro returned when the id of the sandbox is empty + ErrIDEmpty = errors.New("PodSandboxId should not be empty") + // ErrClosedNetNS is the error returned when the network namespace of the + // sandbox is closed + ErrClosedNetNS = errors.New("PodSandbox networking namespace is closed") +) + +// New creates and populates a new pod sandbox +// New sandboxes have no containers, no infra container, and no network namespaces associated with them +// An infra container must be attached before the sandbox is added to the state +func New(id, namespace, name, kubeName, logDir string, labels, annotations map[string]string, processLabel, mountLabel string, metadata *pb.PodSandboxMetadata, shmPath, cgroupParent string, privileged, trusted bool, resolvPath, hostname string, portMappings []*hostport.PortMapping) (*Sandbox, error) { + sb := new(Sandbox) + sb.id = id + sb.namespace = namespace + sb.name = name + sb.kubeName = kubeName + sb.logDir = logDir + sb.labels = labels + sb.annotations = annotations + sb.containers = oci.NewMemoryStore() + sb.processLabel = processLabel + sb.mountLabel = mountLabel + sb.metadata = metadata + sb.shmPath = shmPath + sb.cgroupParent = cgroupParent + sb.privileged = privileged + sb.trusted = trusted + sb.resolvPath = resolvPath + sb.hostname = hostname + sb.portMappings = portMappings + + return sb, nil +} + +// AddIP stores the ip in the sandbox +func (s *Sandbox) AddIP(ip string) { + s.ip = ip +} + +// IP returns the ip of the sandbox +func (s *Sandbox) IP() string { + return s.ip +} + +// ID returns the id of the sandbox +func (s *Sandbox) ID() string { + return s.id +} + +// Namespace returns the namespace for the sandbox +func (s *Sandbox) Namespace() string { + return s.namespace +} + +// Name returns the name of the sandbox +func (s *Sandbox) Name() string { + return s.name +} + +// KubeName returns the kubernetes name for the sandbox +func (s *Sandbox) KubeName() string { + return s.kubeName +} + +// LogDir returns the location of the logging directory for the sandbox +func (s *Sandbox) LogDir() string { + return s.logDir +} + +// Labels returns the labels associated with the sandbox +func (s *Sandbox) Labels() fields.Set { + return s.labels +} + +// Annotations returns a list of annotations for the sandbox +func (s *Sandbox) Annotations() map[string]string { + return s.annotations +} + +// InfraContainer returns the infrastructure container for the sandbox +func (s *Sandbox) InfraContainer() *oci.Container { + return s.infraContainer +} + +// Containers returns the ContainerStorer that contains information on all +// of the containers in the sandbox +func (s *Sandbox) Containers() oci.ContainerStorer { + return s.containers +} + +// ProcessLabel returns the process label for the sandbox +func (s *Sandbox) ProcessLabel() string { + return s.processLabel +} + +// MountLabel returns the mount label for the sandbox +func (s *Sandbox) MountLabel() string { + return s.mountLabel +} + +// Metadata returns a set of metadata about the sandbox +func (s *Sandbox) Metadata() *pb.PodSandboxMetadata { + return s.metadata +} + +// ShmPath returns the shm path of the sandbox +func (s *Sandbox) ShmPath() string { + return s.shmPath +} + +// CgroupParent returns the cgroup parent of the sandbox +func (s *Sandbox) CgroupParent() string { + return s.cgroupParent +} + +// Privileged returns whether or not the containers in the sandbox are +// privileged containers +func (s *Sandbox) Privileged() bool { + return s.privileged +} + +// Trusted returns whether or not the containers in the sandbox are trusted +func (s *Sandbox) Trusted() bool { + return s.trusted +} + +// ResolvPath returns the resolv path for the sandbox +func (s *Sandbox) ResolvPath() string { + return s.resolvPath +} + +// AddHostnamePath adds the hostname path to the sandbox +func (s *Sandbox) AddHostnamePath(hostname string) { + s.hostnamePath = hostname +} + +// HostnamePath retrieves the hostname path from a sandbox +func (s *Sandbox) HostnamePath() string { + return s.hostnamePath +} + +// Hostname returns the hsotname of the sandbox +func (s *Sandbox) Hostname() string { + return s.hostname +} + +// PortMappings returns a list of port mappings between the host and the sandbox +func (s *Sandbox) PortMappings() []*hostport.PortMapping { + return s.portMappings +} + +// AddContainer adds a container to the sandbox +func (s *Sandbox) AddContainer(c *oci.Container) { + s.containers.Add(c.Name(), c) +} + +// GetContainer retrieves a container from the sandbox +func (s *Sandbox) GetContainer(name string) *oci.Container { + return s.containers.Get(name) +} + +// RemoveContainer deletes a container from the sandbox +func (s *Sandbox) RemoveContainer(c *oci.Container) { + s.containers.Delete(c.Name()) +} + +// SetInfraContainer sets the infrastructure container of a sandbox +// Attempts to set the infrastructure container after one is already present will throw an error +func (s *Sandbox) SetInfraContainer(infraCtr *oci.Container) error { + if s.infraContainer != nil { + return fmt.Errorf("sandbox already has an infra container") + } else if infraCtr == nil { + return fmt.Errorf("must provide non-nil infra container") + } + + s.infraContainer = infraCtr + + return nil +} + +// RemoveInfraContainer removes the infrastructure container of a sandbox +func (s *Sandbox) RemoveInfraContainer() { + s.infraContainer = nil +} + +// NetNs retrieves the network namespace of the sandbox +// If the sandbox uses the host namespace, nil is returned +func (s *Sandbox) NetNs() ns.NetNS { + if s.netns == nil { + return nil + } + + return s.netns.ns +} + +// NetNsPath returns the path to the network namespace of the sandbox. +// If the sandbox uses the host namespace, nil is returned +func (s *Sandbox) NetNsPath() string { + if s.netns == nil { + return "" + } + + return s.netns.symlink.Name() +} + +// NetNsCreate creates a new network namespace for the sandbox +func (s *Sandbox) NetNsCreate() error { + if s.netns != nil { + return fmt.Errorf("net NS already created") + } + + netNS, err := ns.NewNS() + if err != nil { + return err + } + + s.netns = &NetNs{ + ns: netNS, + closed: false, + } + + if err := s.netns.symlinkCreate(s.name); err != nil { + logrus.Warnf("Could not create nentns symlink %v", err) + + if err1 := s.netns.ns.Close(); err1 != nil { + return err1 + } + + return err + } + + return nil +} + +// SetStopped sets the sandbox state to stopped. +// This should be set after a stop operation succeeds +// so that subsequent stops can return fast. +func (s *Sandbox) SetStopped() { + s.stopped = true +} + +// Stopped returns whether the sandbox state has been +// set to stopped. +func (s *Sandbox) Stopped() bool { + return s.stopped +} + +// NetNsJoin attempts to join the sandbox to an existing network namespace +// This will fail if the sandbox is already part of a network namespace +func (s *Sandbox) NetNsJoin(nspath, name string) error { + if s.netns != nil { + return fmt.Errorf("sandbox already has a network namespace, cannot join another") + } + + netNS, err := NetNsGet(nspath, name) + if err != nil { + return err + } + + s.netns = netNS + + return nil +} + +// NetNsRemove removes the network namespace associated with the sandbox +func (s *Sandbox) NetNsRemove() error { + if s.netns == nil { + logrus.Warn("no networking namespace") + return nil + } + + s.netns.Lock() + defer s.netns.Unlock() + + if s.netns.closed { + // netNsRemove() can be called multiple + // times without returning an error. + return nil + } + + if err := s.netns.symlinkRemove(); err != nil { + return err + } + + if err := s.netns.ns.Close(); err != nil { + return err + } + + if s.netns.restored { + // we got namespaces in the form of + // /var/run/netns/cni-0d08effa-06eb-a963-f51a-e2b0eceffc5d + // but /var/run on most system is symlinked to /run so we first resolve + // the symlink and then try and see if it's mounted + fp, err := symlink.FollowSymlinkInScope(s.netns.ns.Path(), "/") + if err != nil { + return err + } + if mounted, err := mount.Mounted(fp); err == nil && mounted { + if err := unix.Unmount(fp, unix.MNT_DETACH); err != nil { + return err + } + } + + if err := os.RemoveAll(s.netns.ns.Path()); err != nil { + return err + } + } + + s.netns.closed = true + return nil +} |