mirror of
				https://github.com/containers/podman.git
				synced 2025-10-26 02:35:43 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			999 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			999 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package libpod
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/containernetworking/cni/pkg/types"
 | |
| 	cnitypes "github.com/containernetworking/cni/pkg/types/current"
 | |
| 	"github.com/containers/storage"
 | |
| 	"github.com/cri-o/ocicni/pkg/ocicni"
 | |
| 	spec "github.com/opencontainers/runtime-spec/specs-go"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/ulule/deepcopier"
 | |
| )
 | |
| 
 | |
| // ContainerStatus represents the current state of a container
 | |
| type ContainerStatus int
 | |
| 
 | |
| const (
 | |
| 	// ContainerStateUnknown indicates that the container is in an error
 | |
| 	// state where information about it cannot be retrieved
 | |
| 	ContainerStateUnknown ContainerStatus = iota
 | |
| 	// ContainerStateConfigured indicates that the container has had its
 | |
| 	// storage configured but it has not been created in the OCI runtime
 | |
| 	ContainerStateConfigured ContainerStatus = iota
 | |
| 	// ContainerStateCreated indicates the container has been created in
 | |
| 	// the OCI runtime but not started
 | |
| 	ContainerStateCreated ContainerStatus = iota
 | |
| 	// ContainerStateRunning indicates the container is currently executing
 | |
| 	ContainerStateRunning ContainerStatus = iota
 | |
| 	// ContainerStateStopped indicates that the container was running but has
 | |
| 	// exited
 | |
| 	ContainerStateStopped ContainerStatus = iota
 | |
| 	// ContainerStatePaused indicates that the container has been paused
 | |
| 	ContainerStatePaused ContainerStatus = iota
 | |
| 	// ContainerStateExited indicates the the container has stopped and been
 | |
| 	// cleaned up
 | |
| 	ContainerStateExited ContainerStatus = iota
 | |
| )
 | |
| 
 | |
| // CgroupfsDefaultCgroupParent is the cgroup parent for CGroupFS in libpod
 | |
| const CgroupfsDefaultCgroupParent = "/libpod_parent"
 | |
| 
 | |
| // SystemdDefaultCgroupParent is the cgroup parent for the systemd cgroup
 | |
| // manager in libpod
 | |
| const SystemdDefaultCgroupParent = "machine.slice"
 | |
| 
 | |
| // DefaultWaitInterval is the default interval between container status checks
 | |
| // while waiting.
 | |
| const DefaultWaitInterval = 250 * time.Millisecond
 | |
| 
 | |
| // LinuxNS represents a Linux namespace
 | |
| type LinuxNS int
 | |
| 
 | |
| const (
 | |
| 	// InvalidNS is an invalid namespace
 | |
| 	InvalidNS LinuxNS = iota
 | |
| 	// IPCNS is the IPC namespace
 | |
| 	IPCNS LinuxNS = iota
 | |
| 	// MountNS is the mount namespace
 | |
| 	MountNS LinuxNS = iota
 | |
| 	// NetNS is the network namespace
 | |
| 	NetNS LinuxNS = iota
 | |
| 	// PIDNS is the PID namespace
 | |
| 	PIDNS LinuxNS = iota
 | |
| 	// UserNS is the user namespace
 | |
| 	UserNS LinuxNS = iota
 | |
| 	// UTSNS is the UTS namespace
 | |
| 	UTSNS LinuxNS = iota
 | |
| 	// CgroupNS is the CGroup namespace
 | |
| 	CgroupNS LinuxNS = iota
 | |
| )
 | |
| 
 | |
| // String returns a string representation of a Linux namespace
 | |
| // It is guaranteed to be the name of the namespace in /proc for valid ns types
 | |
| func (ns LinuxNS) String() string {
 | |
| 	switch ns {
 | |
| 	case InvalidNS:
 | |
| 		return "invalid"
 | |
| 	case IPCNS:
 | |
| 		return "ipc"
 | |
| 	case MountNS:
 | |
| 		return "mnt"
 | |
| 	case NetNS:
 | |
| 		return "net"
 | |
| 	case PIDNS:
 | |
| 		return "pid"
 | |
| 	case UserNS:
 | |
| 		return "user"
 | |
| 	case UTSNS:
 | |
| 		return "uts"
 | |
| 	case CgroupNS:
 | |
| 		return "cgroup"
 | |
| 	default:
 | |
| 		return "unknown"
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Container is a single OCI container.
 | |
| // All operations on a Container that access state must begin with a call to
 | |
| // syncContainer().
 | |
| // There is no guarantee that state exists in a readable state before
 | |
| // syncContainer() is run, and even if it does, its contents will be out of date
 | |
| // and must be refreshed from the database.
 | |
| // Generally, this requirement applies only to top-level functions; helpers can
 | |
| // assume that their callers handled this requirement. Generally speaking, if a
 | |
| // function takes the container lock and accesses any part of state, it should
 | |
| // syncContainer() immediately after locking.
 | |
| // ffjson: skip
 | |
| type Container struct {
 | |
| 	config *ContainerConfig
 | |
| 
 | |
| 	state *containerState
 | |
| 
 | |
| 	// Batched indicates that a container has been locked as part of a
 | |
| 	// Batch() operation
 | |
| 	// Functions called on a batched container will not lock or sync
 | |
| 	batched bool
 | |
| 
 | |
| 	valid   bool
 | |
| 	lock    storage.Locker
 | |
| 	runtime *Runtime
 | |
| 
 | |
| 	rootlessSlirpSyncR *os.File
 | |
| 	rootlessSlirpSyncW *os.File
 | |
| }
 | |
| 
 | |
| // containerState contains the current state of the container
 | |
| // It is stored on disk in a tmpfs and recreated on reboot
 | |
| // easyjson:json
 | |
| type containerState struct {
 | |
| 	// The current state of the running container
 | |
| 	State ContainerStatus `json:"state"`
 | |
| 	// The path to the JSON OCI runtime spec for this container
 | |
| 	ConfigPath string `json:"configPath,omitempty"`
 | |
| 	// RunDir is a per-boot directory for container content
 | |
| 	RunDir string `json:"runDir,omitempty"`
 | |
| 	// DestinationRunDir is where the files in RunDir will be accessible for the container.
 | |
| 	// It is different than RunDir when using userNS
 | |
| 	DestinationRunDir string `json:"destinationRunDir,omitempty"`
 | |
| 	// Mounted indicates whether the container's storage has been mounted
 | |
| 	// for use
 | |
| 	Mounted bool `json:"mounted,omitempty"`
 | |
| 	// Mountpoint contains the path to the container's mounted storage as given
 | |
| 	// by containers/storage.  It can be different than RealMountpoint when
 | |
| 	// usernamespaces are used
 | |
| 	Mountpoint string `json:"mountPoint,omitempty"`
 | |
| 	// RealMountpoint contains the path to the container's mounted storage
 | |
| 	RealMountpoint string `json:"realMountPoint,omitempty"`
 | |
| 	// StartedTime is the time the container was started
 | |
| 	StartedTime time.Time `json:"startedTime,omitempty"`
 | |
| 	// FinishedTime is the time the container finished executing
 | |
| 	FinishedTime time.Time `json:"finishedTime,omitempty"`
 | |
| 	// ExitCode is the exit code returned when the container stopped
 | |
| 	ExitCode int32 `json:"exitCode,omitempty"`
 | |
| 	// Exited is whether the container has exited
 | |
| 	Exited bool `json:"exited,omitempty"`
 | |
| 	// OOMKilled indicates that the container was killed as it ran out of
 | |
| 	// memory
 | |
| 	OOMKilled bool `json:"oomKilled,omitempty"`
 | |
| 	// PID is the PID of a running container
 | |
| 	PID int `json:"pid,omitempty"`
 | |
| 	// ExecSessions contains active exec sessions for container
 | |
| 	// Exec session ID is mapped to PID of exec process
 | |
| 	ExecSessions map[string]*ExecSession `json:"execSessions,omitempty"`
 | |
| 	// NetworkStatus contains the configuration results for all networks
 | |
| 	// the pod is attached to. Only populated if we created a network
 | |
| 	// namespace for the container, and the network namespace is currently
 | |
| 	// active
 | |
| 	NetworkStatus []*cnitypes.Result `json:"networkResults,omitempty"`
 | |
| 	// BindMounts contains files that will be bind-mounted into the
 | |
| 	// container when it is mounted.
 | |
| 	// These include /etc/hosts and /etc/resolv.conf
 | |
| 	// This maps the path the file will be mounted to in the container to
 | |
| 	// the path of the file on disk outside the container
 | |
| 	BindMounts map[string]string `json:"bindMounts,omitempty"`
 | |
| 
 | |
| 	// UserNSRoot is the directory used as root for the container when using
 | |
| 	// user namespaces.
 | |
| 	UserNSRoot string `json:"userNSRoot,omitempty"`
 | |
| 
 | |
| 	// ExtensionStageHooks holds hooks which will be executed by libpod
 | |
| 	// and not delegated to the OCI runtime.
 | |
| 	ExtensionStageHooks map[string][]spec.Hook `json:"extensionStageHooks,omitempty"`
 | |
| 
 | |
| 	// containerPlatformState holds platform-specific container state.
 | |
| 	containerPlatformState
 | |
| }
 | |
| 
 | |
| // ExecSession contains information on an active exec session
 | |
| // easyjson:json
 | |
| type ExecSession struct {
 | |
| 	ID      string   `json:"id"`
 | |
| 	Command []string `json:"command"`
 | |
| 	PID     int      `json:"pid"`
 | |
| }
 | |
| 
 | |
| // ContainerConfig contains all information that was used to create the
 | |
| // container. It may not be changed once created.
 | |
| // It is stored, read-only, on disk
 | |
| // easyjson:json
 | |
| type ContainerConfig struct {
 | |
| 	Spec *spec.Spec `json:"spec"`
 | |
| 	ID   string     `json:"id"`
 | |
| 	Name string     `json:"name"`
 | |
| 	// Full ID of the pood the container belongs to
 | |
| 	Pod string `json:"pod,omitempty"`
 | |
| 	// Namespace the container is in
 | |
| 	Namespace string `json:"namespace,omitempty"`
 | |
| 
 | |
| 	// TODO consider breaking these subsections up into smaller structs
 | |
| 
 | |
| 	// UID/GID mappings used by the storage
 | |
| 	IDMappings storage.IDMappingOptions `json:"idMappingsOptions,omitempty"`
 | |
| 
 | |
| 	// Information on the image used for the root filesystem/
 | |
| 	RootfsImageID   string `json:"rootfsImageID,omitempty"`
 | |
| 	RootfsImageName string `json:"rootfsImageName,omitempty"`
 | |
| 	// Rootfs to use for the container, this conflicts with RootfsImageID
 | |
| 	Rootfs string `json:"rootfs,omitempty"`
 | |
| 	// Whether to mount volumes specified in the image.
 | |
| 	ImageVolumes bool `json:"imageVolumes"`
 | |
| 	// Src path to be mounted on /dev/shm in container.
 | |
| 	ShmDir string `json:"ShmDir,omitempty"`
 | |
| 	// Size of the container's SHM.
 | |
| 	ShmSize int64 `json:"shmSize"`
 | |
| 	// Static directory for container content that will persist across
 | |
| 	// reboot.
 | |
| 	StaticDir string `json:"staticDir"`
 | |
| 	// Mounts list contains all additional mounts into the container rootfs.
 | |
| 	// These include the SHM mount.
 | |
| 	// These must be unmounted before the container's rootfs is unmounted.
 | |
| 	Mounts []string `json:"mounts,omitempty"`
 | |
| 
 | |
| 	// Security Config
 | |
| 
 | |
| 	// Whether the container is privileged
 | |
| 	Privileged bool `json:"privileged"`
 | |
| 	// SELinux process label for container
 | |
| 	ProcessLabel string `json:"ProcessLabel,omitempty"`
 | |
| 	// SELinux mount label for root filesystem
 | |
| 	MountLabel string `json:"MountLabel,omitempty"`
 | |
| 	// LabelOpts are options passed in by the user to setup SELinux labels
 | |
| 	LabelOpts []string `json:"labelopts,omitempty"`
 | |
| 	// User and group to use in the container
 | |
| 	// Can be specified by name or UID/GID
 | |
| 	User string `json:"user,omitempty"`
 | |
| 	// Additional groups to add
 | |
| 	Groups []string `json:"groups,omitempty"`
 | |
| 
 | |
| 	// Namespace Config
 | |
| 	// IDs of container to share namespaces with
 | |
| 	// NetNsCtr conflicts with the CreateNetNS bool
 | |
| 	// These containers are considered dependencies of the given container
 | |
| 	// They must be started before the given container is started
 | |
| 	IPCNsCtr    string `json:"ipcNsCtr,omitempty"`
 | |
| 	MountNsCtr  string `json:"mountNsCtr,omitempty"`
 | |
| 	NetNsCtr    string `json:"netNsCtr,omitempty"`
 | |
| 	PIDNsCtr    string `json:"pidNsCtr,omitempty"`
 | |
| 	UserNsCtr   string `json:"userNsCtr,omitempty"`
 | |
| 	UTSNsCtr    string `json:"utsNsCtr,omitempty"`
 | |
| 	CgroupNsCtr string `json:"cgroupNsCtr,omitempty"`
 | |
| 
 | |
| 	// IDs of dependency containers.
 | |
| 	// These containers must be started before this container is started.
 | |
| 	Dependencies []string
 | |
| 
 | |
| 	// Network Config
 | |
| 
 | |
| 	// CreateNetNS indicates that libpod should create and configure a new
 | |
| 	// network namespace for the container.
 | |
| 	// This cannot be set if NetNsCtr is also set.
 | |
| 	CreateNetNS bool `json:"createNetNS"`
 | |
| 	// StaticIP is a static IP to request for the container.
 | |
| 	// This cannot be set unless CreateNetNS is set.
 | |
| 	// If not set, the container will be dynamically assigned an IP by CNI.
 | |
| 	StaticIP net.IP `json:"staticIP"`
 | |
| 	// PortMappings are the ports forwarded to the container's network
 | |
| 	// namespace
 | |
| 	// These are not used unless CreateNetNS is true
 | |
| 	PortMappings []ocicni.PortMapping `json:"portMappings,omitempty"`
 | |
| 	// DNS servers to use in container resolv.conf
 | |
| 	// Will override servers in host resolv if set
 | |
| 	DNSServer []net.IP `json:"dnsServer,omitempty"`
 | |
| 	// DNS Search domains to use in container resolv.conf
 | |
| 	// Will override search domains in host resolv if set
 | |
| 	DNSSearch []string `json:"dnsSearch,omitempty"`
 | |
| 	// DNS options to be set in container resolv.conf
 | |
| 	// With override options in host resolv if set
 | |
| 	DNSOption []string `json:"dnsOption,omitempty"`
 | |
| 	// Hosts to add in container
 | |
| 	// Will be appended to host's host file
 | |
| 	HostAdd []string `json:"hostsAdd,omitempty"`
 | |
| 	// Network names (CNI) to add container to. Empty to use default network.
 | |
| 	Networks []string `json:"networks,omitempty"`
 | |
| 
 | |
| 	// Image Config
 | |
| 
 | |
| 	// UserVolumes contains user-added volume mounts in the container.
 | |
| 	// These will not be added to the container's spec, as it is assumed
 | |
| 	// they are already present in the spec given to Libpod. Instead, it is
 | |
| 	// used when committing containers to generate the VOLUMES field of the
 | |
| 	// image that is created, and for triggering some OCI hooks which do not
 | |
| 	// fire unless user-added volume mounts are present.
 | |
| 	UserVolumes []string `json:"userVolumes,omitempty"`
 | |
| 	// Entrypoint is the container's entrypoint.
 | |
| 	// It is not used in spec generation, but will be used when the
 | |
| 	// container is committed to populate the entrypoint of the new image.
 | |
| 	Entrypoint []string `json:"entrypoint,omitempty"`
 | |
| 	// Command is the container's command.
 | |
| 	// It is not used in spec generation, but will be used when the
 | |
| 	// container is committed to populate the command of the new image.
 | |
| 	Command []string `json:"command,omitempty"`
 | |
| 
 | |
| 	// Misc Options
 | |
| 
 | |
| 	// Whether to keep container STDIN open
 | |
| 	Stdin bool `json:"stdin,omitempty"`
 | |
| 	// Labels is a set of key-value pairs providing additional information
 | |
| 	// about a container
 | |
| 	Labels map[string]string `json:"labels,omitempty"`
 | |
| 	// StopSignal is the signal that will be used to stop the container
 | |
| 	StopSignal uint `json:"stopSignal,omitempty"`
 | |
| 	// StopTimeout is the signal that will be used to stop the container
 | |
| 	StopTimeout uint `json:"stopTimeout,omitempty"`
 | |
| 	// Time container was created
 | |
| 	CreatedTime time.Time `json:"createdTime"`
 | |
| 	// Cgroup parent of the container
 | |
| 	CgroupParent string `json:"cgroupParent"`
 | |
| 	// LogPath log location
 | |
| 	LogPath string `json:"logPath"`
 | |
| 	// File containing the conmon PID
 | |
| 	ConmonPidFile string `json:"conmonPidFile,omitempty"`
 | |
| 	// TODO log options for log drivers
 | |
| 
 | |
| 	PostConfigureNetNS bool `json:"postConfigureNetNS"`
 | |
| 
 | |
| 	// ExitCommand is the container's exit command.
 | |
| 	// This Command will be executed when the container exits
 | |
| 	ExitCommand []string `json:"exitCommand,omitempty"`
 | |
| 	// LocalVolumes are the built-in volumes we get from the --volumes-from flag
 | |
| 	// It picks up the built-in volumes of the container used by --volumes-from
 | |
| 	LocalVolumes []string
 | |
| 
 | |
| 	// IsInfra is a bool indicating whether this container is an infra container used for
 | |
| 	// sharing kernel namespaces in a pod
 | |
| 	IsInfra bool `json:"pause"`
 | |
| 
 | |
| 	// Systemd tells libpod to setup the container in systemd mode
 | |
| 	Systemd bool `json:"systemd"`
 | |
| }
 | |
| 
 | |
| // ContainerStatus returns a string representation for users
 | |
| // of a container state
 | |
| func (t ContainerStatus) String() string {
 | |
| 	switch t {
 | |
| 	case ContainerStateUnknown:
 | |
| 		return "unknown"
 | |
| 	case ContainerStateConfigured:
 | |
| 		return "configured"
 | |
| 	case ContainerStateCreated:
 | |
| 		return "created"
 | |
| 	case ContainerStateRunning:
 | |
| 		return "running"
 | |
| 	case ContainerStateStopped:
 | |
| 		return "stopped"
 | |
| 	case ContainerStatePaused:
 | |
| 		return "paused"
 | |
| 	case ContainerStateExited:
 | |
| 		return "exited"
 | |
| 	}
 | |
| 	return "bad state"
 | |
| }
 | |
| 
 | |
| // Config accessors
 | |
| // Unlocked
 | |
| 
 | |
| // Config returns the configuration used to create the container
 | |
| func (c *Container) Config() *ContainerConfig {
 | |
| 	returnConfig := new(ContainerConfig)
 | |
| 	deepcopier.Copy(c.config).To(returnConfig)
 | |
| 
 | |
| 	return returnConfig
 | |
| }
 | |
| 
 | |
| // Spec returns the container's OCI runtime spec
 | |
| // The spec returned is the one used to create the container. The running
 | |
| // spec may differ slightly as mounts are added based on the image
 | |
| func (c *Container) Spec() *spec.Spec {
 | |
| 	returnSpec := new(spec.Spec)
 | |
| 	deepcopier.Copy(c.config.Spec).To(returnSpec)
 | |
| 
 | |
| 	return returnSpec
 | |
| }
 | |
| 
 | |
| // ID returns the container's ID
 | |
| func (c *Container) ID() string {
 | |
| 	return c.config.ID
 | |
| }
 | |
| 
 | |
| // Name returns the container's name
 | |
| func (c *Container) Name() string {
 | |
| 	return c.config.Name
 | |
| }
 | |
| 
 | |
| // PodID returns the full ID of the pod the container belongs to, or "" if it
 | |
| // does not belong to a pod
 | |
| func (c *Container) PodID() string {
 | |
| 	return c.config.Pod
 | |
| }
 | |
| 
 | |
| // Namespace returns the libpod namespace the container is in.
 | |
| // Namespaces are used to logically separate containers and pods in the state.
 | |
| func (c *Container) Namespace() string {
 | |
| 	return c.config.Namespace
 | |
| }
 | |
| 
 | |
| // Image returns the ID and name of the image used as the container's rootfs
 | |
| func (c *Container) Image() (string, string) {
 | |
| 	return c.config.RootfsImageID, c.config.RootfsImageName
 | |
| }
 | |
| 
 | |
| // ImageVolumes returns whether the container is configured to create
 | |
| // persistent volumes requested by the image
 | |
| func (c *Container) ImageVolumes() bool {
 | |
| 	return c.config.ImageVolumes
 | |
| }
 | |
| 
 | |
| // ShmDir returns the sources path to be mounted on /dev/shm in container
 | |
| func (c *Container) ShmDir() string {
 | |
| 	return c.config.ShmDir
 | |
| }
 | |
| 
 | |
| // ShmSize returns the size of SHM device to be mounted into the container
 | |
| func (c *Container) ShmSize() int64 {
 | |
| 	return c.config.ShmSize
 | |
| }
 | |
| 
 | |
| // StaticDir returns the directory used to store persistent container files
 | |
| func (c *Container) StaticDir() string {
 | |
| 	return c.config.StaticDir
 | |
| }
 | |
| 
 | |
| // Privileged returns whether the container is privileged
 | |
| func (c *Container) Privileged() bool {
 | |
| 	return c.config.Privileged
 | |
| }
 | |
| 
 | |
| // ProcessLabel returns the selinux ProcessLabel of the container
 | |
| func (c *Container) ProcessLabel() string {
 | |
| 	return c.config.ProcessLabel
 | |
| }
 | |
| 
 | |
| // MountLabel returns the SELinux mount label of the container
 | |
| func (c *Container) MountLabel() string {
 | |
| 	return c.config.MountLabel
 | |
| }
 | |
| 
 | |
| // User returns the user who the container is run as
 | |
| func (c *Container) User() string {
 | |
| 	return c.config.User
 | |
| }
 | |
| 
 | |
| // Dependencies gets the containers this container depends upon
 | |
| func (c *Container) Dependencies() []string {
 | |
| 	// Collect in a map first to remove dupes
 | |
| 	dependsCtrs := map[string]bool{}
 | |
| 
 | |
| 	// First add all namespace containers
 | |
| 	if c.config.IPCNsCtr != "" {
 | |
| 		dependsCtrs[c.config.IPCNsCtr] = true
 | |
| 	}
 | |
| 	if c.config.MountNsCtr != "" {
 | |
| 		dependsCtrs[c.config.MountNsCtr] = true
 | |
| 	}
 | |
| 	if c.config.NetNsCtr != "" {
 | |
| 		dependsCtrs[c.config.NetNsCtr] = true
 | |
| 	}
 | |
| 	if c.config.PIDNsCtr != "" {
 | |
| 		dependsCtrs[c.config.PIDNsCtr] = true
 | |
| 	}
 | |
| 	if c.config.UserNsCtr != "" {
 | |
| 		dependsCtrs[c.config.UserNsCtr] = true
 | |
| 	}
 | |
| 	if c.config.UTSNsCtr != "" {
 | |
| 		dependsCtrs[c.config.UTSNsCtr] = true
 | |
| 	}
 | |
| 	if c.config.CgroupNsCtr != "" {
 | |
| 		dependsCtrs[c.config.CgroupNsCtr] = true
 | |
| 	}
 | |
| 
 | |
| 	// Add all generic dependencies
 | |
| 	for _, id := range c.config.Dependencies {
 | |
| 		dependsCtrs[id] = true
 | |
| 	}
 | |
| 
 | |
| 	if len(dependsCtrs) == 0 {
 | |
| 		return []string{}
 | |
| 	}
 | |
| 
 | |
| 	depends := make([]string, 0, len(dependsCtrs))
 | |
| 	for ctr := range dependsCtrs {
 | |
| 		depends = append(depends, ctr)
 | |
| 	}
 | |
| 
 | |
| 	return depends
 | |
| }
 | |
| 
 | |
| // NewNetNS returns whether the container will create a new network namespace
 | |
| func (c *Container) NewNetNS() bool {
 | |
| 	return c.config.CreateNetNS
 | |
| }
 | |
| 
 | |
| // PortMappings returns the ports that will be mapped into a container if
 | |
| // a new network namespace is created
 | |
| // If NewNetNS() is false, this value is unused
 | |
| func (c *Container) PortMappings() []ocicni.PortMapping {
 | |
| 	return c.config.PortMappings
 | |
| }
 | |
| 
 | |
| // DNSServers returns DNS servers that will be used in the container's
 | |
| // resolv.conf
 | |
| // If empty, DNS server from the host's resolv.conf will be used instead
 | |
| func (c *Container) DNSServers() []net.IP {
 | |
| 	return c.config.DNSServer
 | |
| }
 | |
| 
 | |
| // DNSSearch returns the DNS search domains that will be used in the container's
 | |
| // resolv.conf
 | |
| // If empty, DNS Search domains from the host's resolv.conf will be used instead
 | |
| func (c *Container) DNSSearch() []string {
 | |
| 	return c.config.DNSSearch
 | |
| }
 | |
| 
 | |
| // DNSOption returns the DNS options that will be used in the container's
 | |
| // resolv.conf
 | |
| // If empty, options from the host's resolv.conf will be used instead
 | |
| func (c *Container) DNSOption() []string {
 | |
| 	return c.config.DNSOption
 | |
| }
 | |
| 
 | |
| // HostsAdd returns hosts that will be added to the container's hosts file
 | |
| // The host system's hosts file is used as a base, and these are appended to it
 | |
| func (c *Container) HostsAdd() []string {
 | |
| 	return c.config.HostAdd
 | |
| }
 | |
| 
 | |
| // UserVolumes returns user-added volume mounts in the container.
 | |
| // These are not added to the spec, but are used during image commit and to
 | |
| // trigger some OCI hooks.
 | |
| func (c *Container) UserVolumes() []string {
 | |
| 	volumes := make([]string, 0, len(c.config.UserVolumes))
 | |
| 	for _, vol := range c.config.UserVolumes {
 | |
| 		volumes = append(volumes, vol)
 | |
| 	}
 | |
| 
 | |
| 	return volumes
 | |
| }
 | |
| 
 | |
| // Entrypoint is the container's entrypoint.
 | |
| // This is not added to the spec, but is instead used during image commit.
 | |
| func (c *Container) Entrypoint() []string {
 | |
| 	entrypoint := make([]string, 0, len(c.config.Entrypoint))
 | |
| 	for _, str := range c.config.Entrypoint {
 | |
| 		entrypoint = append(entrypoint, str)
 | |
| 	}
 | |
| 
 | |
| 	return entrypoint
 | |
| }
 | |
| 
 | |
| // Command is the container's command
 | |
| // This is not added to the spec, but is instead used during image commit
 | |
| func (c *Container) Command() []string {
 | |
| 	command := make([]string, 0, len(c.config.Command))
 | |
| 	for _, str := range c.config.Command {
 | |
| 		command = append(command, str)
 | |
| 	}
 | |
| 
 | |
| 	return command
 | |
| }
 | |
| 
 | |
| // Stdin returns whether STDIN on the container will be kept open
 | |
| func (c *Container) Stdin() bool {
 | |
| 	return c.config.Stdin
 | |
| }
 | |
| 
 | |
| // Labels returns the container's labels
 | |
| func (c *Container) Labels() map[string]string {
 | |
| 	labels := make(map[string]string)
 | |
| 	for key, value := range c.config.Labels {
 | |
| 		labels[key] = value
 | |
| 	}
 | |
| 	return labels
 | |
| }
 | |
| 
 | |
| // StopSignal is the signal that will be used to stop the container
 | |
| // If it fails to stop the container, SIGKILL will be used after a timeout
 | |
| // If StopSignal is 0, the default signal of SIGTERM will be used
 | |
| func (c *Container) StopSignal() uint {
 | |
| 	return c.config.StopSignal
 | |
| }
 | |
| 
 | |
| // StopTimeout returns the container's stop timeout
 | |
| // If the container's default stop signal fails to kill the container, SIGKILL
 | |
| // will be used after this timeout
 | |
| func (c *Container) StopTimeout() uint {
 | |
| 	return c.config.StopTimeout
 | |
| }
 | |
| 
 | |
| // CreatedTime gets the time when the container was created
 | |
| func (c *Container) CreatedTime() time.Time {
 | |
| 	return c.config.CreatedTime
 | |
| }
 | |
| 
 | |
| // CgroupParent gets the container's CGroup parent
 | |
| func (c *Container) CgroupParent() string {
 | |
| 	return c.config.CgroupParent
 | |
| }
 | |
| 
 | |
| // LogPath returns the path to the container's log file
 | |
| // This file will only be present after Init() is called to create the container
 | |
| // in the runtime
 | |
| func (c *Container) LogPath() string {
 | |
| 	return c.config.LogPath
 | |
| }
 | |
| 
 | |
| // RuntimeName returns the name of the runtime
 | |
| func (c *Container) RuntimeName() string {
 | |
| 	return c.runtime.ociRuntime.name
 | |
| }
 | |
| 
 | |
| // Runtime spec accessors
 | |
| // Unlocked
 | |
| 
 | |
| // Hostname gets the container's hostname
 | |
| func (c *Container) Hostname() string {
 | |
| 	if c.config.Spec.Hostname != "" {
 | |
| 		return c.config.Spec.Hostname
 | |
| 	}
 | |
| 
 | |
| 	if len(c.ID()) < 11 {
 | |
| 		return c.ID()
 | |
| 	}
 | |
| 	return c.ID()[:12]
 | |
| }
 | |
| 
 | |
| // WorkingDir returns the containers working dir
 | |
| func (c *Container) WorkingDir() string {
 | |
| 	return c.config.Spec.Process.Cwd
 | |
| }
 | |
| 
 | |
| // State Accessors
 | |
| // Require locking
 | |
| 
 | |
| // State returns the current state of the container
 | |
| func (c *Container) State() (ContainerStatus, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return ContainerStateUnknown, err
 | |
| 		}
 | |
| 	}
 | |
| 	return c.state.State, nil
 | |
| }
 | |
| 
 | |
| // Mounted returns whether the container is mounted and the path it is mounted
 | |
| // at (if it is mounted).
 | |
| // If the container is not mounted, no error is returned, and the mountpoint
 | |
| // will be set to "".
 | |
| func (c *Container) Mounted() (bool, string, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return false, "", errors.Wrapf(err, "error updating container %s state", c.ID())
 | |
| 		}
 | |
| 	}
 | |
| 	// We cannot directly return c.state.Mountpoint as it is not guaranteed
 | |
| 	// to be set if the container is mounted, only if the container has been
 | |
| 	// prepared with c.prepare().
 | |
| 	// Instead, let's call into c/storage
 | |
| 	mountedTimes, err := c.runtime.storageService.MountedContainerImage(c.ID())
 | |
| 	if err != nil {
 | |
| 		return false, "", err
 | |
| 	}
 | |
| 
 | |
| 	if mountedTimes > 0 {
 | |
| 		mountPoint, err := c.runtime.storageService.GetMountpoint(c.ID())
 | |
| 		if err != nil {
 | |
| 			return false, "", err
 | |
| 		}
 | |
| 
 | |
| 		return true, mountPoint, nil
 | |
| 	}
 | |
| 
 | |
| 	return false, "", nil
 | |
| }
 | |
| 
 | |
| // StartedTime is the time the container was started
 | |
| func (c *Container) StartedTime() (time.Time, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return time.Time{}, errors.Wrapf(err, "error updating container %s state", c.ID())
 | |
| 		}
 | |
| 	}
 | |
| 	return c.state.StartedTime, nil
 | |
| }
 | |
| 
 | |
| // FinishedTime is the time the container was stopped
 | |
| func (c *Container) FinishedTime() (time.Time, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return time.Time{}, errors.Wrapf(err, "error updating container %s state", c.ID())
 | |
| 		}
 | |
| 	}
 | |
| 	return c.state.FinishedTime, nil
 | |
| }
 | |
| 
 | |
| // ExitCode returns the exit code of the container as
 | |
| // an int32, and whether the container has exited.
 | |
| // If the container has not exited, exit code will always be 0.
 | |
| // If the container restarts, the exit code is reset to 0.
 | |
| func (c *Container) ExitCode() (int32, bool, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return 0, false, errors.Wrapf(err, "error updating container %s state", c.ID())
 | |
| 		}
 | |
| 	}
 | |
| 	return c.state.ExitCode, c.state.Exited, nil
 | |
| }
 | |
| 
 | |
| // OOMKilled returns whether the container was killed by an OOM condition
 | |
| func (c *Container) OOMKilled() (bool, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return false, errors.Wrapf(err, "error updating container %s state", c.ID())
 | |
| 		}
 | |
| 	}
 | |
| 	return c.state.OOMKilled, nil
 | |
| }
 | |
| 
 | |
| // PID returns the PID of the container
 | |
| // If the container is not running, a pid of 0 will be returned. No error will
 | |
| // occur.
 | |
| func (c *Container) PID() (int, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return -1, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return c.state.PID, nil
 | |
| }
 | |
| 
 | |
| // ExecSessions retrieves active exec sessions running in the container
 | |
| func (c *Container) ExecSessions() ([]string, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ids := make([]string, 0, len(c.state.ExecSessions))
 | |
| 	for id := range c.state.ExecSessions {
 | |
| 		ids = append(ids, id)
 | |
| 	}
 | |
| 
 | |
| 	return ids, nil
 | |
| }
 | |
| 
 | |
| // ExecSession retrieves detailed information on a single active exec session in
 | |
| // a container
 | |
| func (c *Container) ExecSession(id string) (*ExecSession, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	session, ok := c.state.ExecSessions[id]
 | |
| 	if !ok {
 | |
| 		return nil, errors.Wrapf(ErrNoSuchCtr, "no exec session with ID %s found in container %s", id, c.ID())
 | |
| 	}
 | |
| 
 | |
| 	returnSession := new(ExecSession)
 | |
| 	returnSession.ID = session.ID
 | |
| 	returnSession.Command = session.Command
 | |
| 	returnSession.PID = session.PID
 | |
| 
 | |
| 	return returnSession, nil
 | |
| }
 | |
| 
 | |
| // IPs retrieves a container's IP address(es)
 | |
| // This will only be populated if the container is configured to created a new
 | |
| // network namespace, and that namespace is presently active
 | |
| func (c *Container) IPs() ([]net.IPNet, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if !c.config.CreateNetNS {
 | |
| 		return nil, errors.Wrapf(ErrInvalidArg, "container %s network namespace is not managed by libpod")
 | |
| 	}
 | |
| 
 | |
| 	ips := make([]net.IPNet, 0)
 | |
| 
 | |
| 	for _, r := range c.state.NetworkStatus {
 | |
| 		for _, ip := range r.IPs {
 | |
| 			ips = append(ips, ip.Address)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ips, nil
 | |
| }
 | |
| 
 | |
| // Routes retrieves a container's routes
 | |
| // This will only be populated if the container is configured to created a new
 | |
| // network namespace, and that namespace is presently active
 | |
| func (c *Container) Routes() ([]types.Route, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if !c.config.CreateNetNS {
 | |
| 		return nil, errors.Wrapf(ErrInvalidArg, "container %s network namespace is not managed by libpod")
 | |
| 	}
 | |
| 
 | |
| 	routes := make([]types.Route, 0)
 | |
| 
 | |
| 	for _, r := range c.state.NetworkStatus {
 | |
| 		for _, route := range r.Routes {
 | |
| 			newRoute := types.Route{
 | |
| 				Dst: route.Dst,
 | |
| 				GW:  route.GW,
 | |
| 			}
 | |
| 			routes = append(routes, newRoute)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return routes, nil
 | |
| }
 | |
| 
 | |
| // BindMounts retrieves bind mounts that were created by libpod and will be
 | |
| // added to the container
 | |
| // All these mounts except /dev/shm are ignored if a mount in the given spec has
 | |
| // the same destination
 | |
| // These mounts include /etc/resolv.conf, /etc/hosts, and /etc/hostname
 | |
| // The return is formatted as a map from destination (mountpoint in the
 | |
| // container) to source (path of the file that will be mounted into the
 | |
| // container)
 | |
| // If the container has not been started yet, an empty map will be returned, as
 | |
| // the files in question are only created when the container is started.
 | |
| func (c *Container) BindMounts() (map[string]string, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	newMap := make(map[string]string, len(c.state.BindMounts))
 | |
| 
 | |
| 	for key, val := range c.state.BindMounts {
 | |
| 		newMap[key] = val
 | |
| 	}
 | |
| 
 | |
| 	return newMap, nil
 | |
| }
 | |
| 
 | |
| // Misc Accessors
 | |
| // Most will require locking
 | |
| 
 | |
| // NamespacePath returns the path of one of the container's namespaces
 | |
| // If the container is not running, an error will be returned
 | |
| func (c *Container) NamespacePath(ns LinuxNS) (string, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return "", errors.Wrapf(err, "error updating container %s state", c.ID())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if c.state.State != ContainerStateRunning && c.state.State != ContainerStatePaused {
 | |
| 		return "", errors.Wrapf(ErrCtrStopped, "cannot get namespace path unless container %s is running", c.ID())
 | |
| 	}
 | |
| 
 | |
| 	if ns == InvalidNS {
 | |
| 		return "", errors.Wrapf(ErrInvalidArg, "invalid namespace requested from container %s", c.ID())
 | |
| 	}
 | |
| 
 | |
| 	return fmt.Sprintf("/proc/%d/ns/%s", c.state.PID, ns.String()), nil
 | |
| }
 | |
| 
 | |
| // CGroupPath returns a cgroups "path" for a given container.
 | |
| func (c *Container) CGroupPath() (string, error) {
 | |
| 	switch c.runtime.config.CgroupManager {
 | |
| 	case CgroupfsCgroupsManager:
 | |
| 		return filepath.Join(c.config.CgroupParent, fmt.Sprintf("libpod-%s", c.ID())), nil
 | |
| 	case SystemdCgroupsManager:
 | |
| 		return filepath.Join(c.config.CgroupParent, createUnitName("libpod", c.ID())), nil
 | |
| 	default:
 | |
| 		return "", errors.Wrapf(ErrInvalidArg, "unsupported CGroup manager %s in use", c.runtime.config.CgroupManager)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // RootFsSize returns the root FS size of the container
 | |
| func (c *Container) RootFsSize() (int64, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return -1, errors.Wrapf(err, "error updating container %s state", c.ID())
 | |
| 		}
 | |
| 	}
 | |
| 	return c.rootFsSize()
 | |
| }
 | |
| 
 | |
| // RWSize returns the rw size of the container
 | |
| func (c *Container) RWSize() (int64, error) {
 | |
| 	if !c.batched {
 | |
| 		c.lock.Lock()
 | |
| 		defer c.lock.Unlock()
 | |
| 		if err := c.syncContainer(); err != nil {
 | |
| 			return -1, errors.Wrapf(err, "error updating container %s state", c.ID())
 | |
| 		}
 | |
| 	}
 | |
| 	return c.rwSize()
 | |
| }
 | |
| 
 | |
| // IDMappings returns the UID/GID mapping used for the container
 | |
| func (c *Container) IDMappings() (storage.IDMappingOptions, error) {
 | |
| 	return c.config.IDMappings, nil
 | |
| }
 | |
| 
 | |
| // RootUID returns the root user mapping from container
 | |
| func (c *Container) RootUID() int {
 | |
| 	for _, uidmap := range c.config.IDMappings.UIDMap {
 | |
| 		if uidmap.ContainerID == 0 {
 | |
| 			return uidmap.HostID
 | |
| 		}
 | |
| 	}
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| // RootGID returns the root user mapping from container
 | |
| func (c *Container) RootGID() int {
 | |
| 	for _, gidmap := range c.config.IDMappings.GIDMap {
 | |
| 		if gidmap.ContainerID == 0 {
 | |
| 			return gidmap.HostID
 | |
| 		}
 | |
| 	}
 | |
| 	return 0
 | |
| }
 | |
| 
 | |
| // IsInfra returns whether the container is an infra container
 | |
| func (c *Container) IsInfra() bool {
 | |
| 	return c.config.IsInfra
 | |
| }
 | |
| 
 | |
| // IsReadOnly returns whether the container is running in read only mode
 | |
| func (c *Container) IsReadOnly() bool {
 | |
| 	return c.config.Spec.Root.Readonly
 | |
| }
 | 
