mirror of
https://github.com/containers/podman.git
synced 2025-10-11 16:26:00 +08:00

kpod inspect now uses the new libpod container state and closely matches the output of docker inspect some aspects of it are still WIP as the libpod container state is still being worked on Signed-off-by: umohnani8 <umohnani@redhat.com>
508 lines
16 KiB
Go
508 lines
16 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/docker/docker/api/types/container"
|
|
"github.com/docker/go-units"
|
|
"github.com/opencontainers/selinux/go-selinux/label"
|
|
"github.com/pkg/errors"
|
|
"github.com/projectatomic/libpod/libpod"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/urfave/cli"
|
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
|
)
|
|
|
|
type mountType string
|
|
|
|
// Type constants
|
|
const (
|
|
// TypeBind is the type for mounting host dir
|
|
TypeBind mountType = "bind"
|
|
// TypeVolume is the type for remote storage volumes
|
|
// TypeVolume mountType = "volume" // re-enable upon use
|
|
// TypeTmpfs is the type for mounting tmpfs
|
|
TypeTmpfs mountType = "tmpfs"
|
|
)
|
|
|
|
var (
|
|
defaultEnvVariables = map[string]string{
|
|
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
"TERM": "xterm",
|
|
}
|
|
)
|
|
|
|
type createResourceConfig struct {
|
|
BlkioWeight uint16 // blkio-weight
|
|
BlkioWeightDevice []string // blkio-weight-device
|
|
CpuPeriod uint64 // cpu-period
|
|
CpuQuota int64 // cpu-quota
|
|
CpuRtPeriod uint64 // cpu-rt-period
|
|
CpuRtRuntime int64 // cpu-rt-runtime
|
|
CpuShares uint64 // cpu-shares
|
|
Cpus string // cpus
|
|
CpusetCpus string
|
|
CpusetMems string // cpuset-mems
|
|
DeviceReadBps []string // device-read-bps
|
|
DeviceReadIOps []string // device-read-iops
|
|
DeviceWriteBps []string // device-write-bps
|
|
DeviceWriteIOps []string // device-write-iops
|
|
DisableOomKiller bool // oom-kill-disable
|
|
KernelMemory int64 // kernel-memory
|
|
Memory int64 //memory
|
|
MemoryReservation int64 // memory-reservation
|
|
MemorySwap int64 //memory-swap
|
|
MemorySwappiness int // memory-swappiness
|
|
OomScoreAdj int //oom-score-adj
|
|
PidsLimit int64 // pids-limit
|
|
ShmSize string
|
|
Ulimit []string //ulimit
|
|
}
|
|
|
|
type createConfig struct {
|
|
Runtime *libpod.Runtime
|
|
Args []string
|
|
CapAdd []string // cap-add
|
|
CapDrop []string // cap-drop
|
|
CidFile string
|
|
CgroupParent string // cgroup-parent
|
|
Command []string
|
|
Detach bool // detach
|
|
Devices []*pb.Device // device
|
|
DnsOpt []string //dns-opt
|
|
DnsSearch []string //dns-search
|
|
DnsServers []string //dns
|
|
Entrypoint string //entrypoint
|
|
Env map[string]string //env
|
|
Expose []string //expose
|
|
GroupAdd []uint32 // group-add
|
|
Hostname string //hostname
|
|
Image string
|
|
Interactive bool //interactive
|
|
IpcMode container.IpcMode //ipc
|
|
Ip6Address string //ipv6
|
|
IpAddress string //ip
|
|
Labels map[string]string //label
|
|
LinkLocalIP []string // link-local-ip
|
|
LogDriver string // log-driver
|
|
LogDriverOpt []string // log-opt
|
|
MacAddress string //mac-address
|
|
Name string //name
|
|
NetMode container.NetworkMode //net
|
|
Network string //network
|
|
NetworkAlias []string //network-alias
|
|
PidMode container.PidMode //pid
|
|
NsUser string
|
|
Pod string //pod
|
|
Privileged bool //privileged
|
|
Publish []string //publish
|
|
PublishAll bool //publish-all
|
|
ReadOnlyRootfs bool //read-only
|
|
Resources createResourceConfig
|
|
Rm bool //rm
|
|
ShmDir string
|
|
SigProxy bool //sig-proxy
|
|
StopSignal string // stop-signal
|
|
StopTimeout int64 // stop-timeout
|
|
StorageOpts []string //storage-opt
|
|
Sysctl map[string]string //sysctl
|
|
Tmpfs []string // tmpfs
|
|
Tty bool //tty
|
|
User uint32 //user
|
|
Group uint32 // group
|
|
UtsMode container.UTSMode //uts
|
|
Volumes []string //volume
|
|
WorkDir string //workdir
|
|
MountLabel string //SecurityOpts
|
|
ProcessLabel string //SecurityOpts
|
|
NoNewPrivileges bool //SecurityOpts
|
|
ApparmorProfile string //SecurityOpts
|
|
SeccompProfilePath string //SecurityOpts
|
|
SecurityOpts []string
|
|
}
|
|
|
|
var createDescription = "Creates a new container from the given image or" +
|
|
" storage and prepares it for running the specified command. The" +
|
|
" container ID is then printed to stdout. You can then start it at" +
|
|
" any time with the kpod start <container_id> command. The container" +
|
|
" will be created with the initial state 'created'."
|
|
|
|
var createCommand = cli.Command{
|
|
Name: "create",
|
|
Usage: "create but do not start a container",
|
|
Description: createDescription,
|
|
Flags: createFlags,
|
|
Action: createCmd,
|
|
ArgsUsage: "IMAGE [COMMAND [ARG...]]",
|
|
SkipArgReorder: true,
|
|
UseShortOptionHandling: true,
|
|
}
|
|
|
|
func createCmd(c *cli.Context) error {
|
|
// TODO should allow user to create based off a directory on the host not just image
|
|
// Need CLI support for this
|
|
var imageName string
|
|
if err := validateFlags(c, createFlags); err != nil {
|
|
return err
|
|
}
|
|
|
|
runtime, err := getRuntime(c)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error creating libpod runtime")
|
|
}
|
|
defer runtime.Shutdown(false)
|
|
|
|
createConfig, err := parseCreateOpts(c, runtime)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Deal with the image after all the args have been checked
|
|
createImage := runtime.NewImage(createConfig.Image)
|
|
createImage.LocalName, _ = createImage.GetLocalImageName()
|
|
if createImage.LocalName == "" {
|
|
// The image wasnt found by the user input'd name or its fqname
|
|
// Pull the image
|
|
fmt.Printf("Trying to pull %s...", createImage.PullName)
|
|
createImage.Pull()
|
|
}
|
|
|
|
runtimeSpec, err := createConfigToOCISpec(createConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if createImage.LocalName != "" {
|
|
nameIsID, err := runtime.IsImageID(createImage.LocalName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if nameIsID {
|
|
// If the input from the user is an ID, then we need to get the image
|
|
// name for cstorage
|
|
createImage.LocalName, err = createImage.GetNameByID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
imageName = createImage.LocalName
|
|
} else {
|
|
imageName, err = createImage.GetFQName()
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
imageID, err := createImage.GetImageID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
options, err := createConfig.GetContainerCreateOptions()
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to parse new container options")
|
|
}
|
|
// Gather up the options for NewContainer which consist of With... funcs
|
|
options = append(options, libpod.WithRootFSFromImage(imageID, imageName, false))
|
|
options = append(options, libpod.WithSELinuxLabels(createConfig.ProcessLabel, createConfig.MountLabel))
|
|
options = append(options, libpod.WithShmDir(createConfig.ShmDir))
|
|
ctr, err := runtime.NewContainer(runtimeSpec, options...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
createConfigJSON, err := json.Marshal(createConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
|
|
return err
|
|
}
|
|
|
|
logrus.Debug("new container created ", ctr.ID())
|
|
|
|
if c.String("cidfile") != "" {
|
|
libpod.WriteFile(ctr.ID(), c.String("cidfile"))
|
|
} else {
|
|
fmt.Printf("%s\n", ctr.ID())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const seccompDefaultPath = "/etc/crio/seccomp.json"
|
|
|
|
func parseSecurityOpt(config *createConfig, securityOpts []string) error {
|
|
var (
|
|
labelOpts []string
|
|
err error
|
|
)
|
|
|
|
if config.PidMode.IsHost() {
|
|
labelOpts = append(labelOpts, label.DisableSecOpt()...)
|
|
} else if config.PidMode.IsContainer() {
|
|
ctr, err := config.Runtime.LookupContainer(config.PidMode.Container())
|
|
if err != nil {
|
|
return errors.Wrapf(err, "container %q not found", config.PidMode.Container())
|
|
}
|
|
labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...)
|
|
}
|
|
|
|
if config.IpcMode.IsHost() {
|
|
labelOpts = append(labelOpts, label.DisableSecOpt()...)
|
|
} else if config.IpcMode.IsContainer() {
|
|
ctr, err := config.Runtime.LookupContainer(config.IpcMode.Container())
|
|
if err != nil {
|
|
return errors.Wrapf(err, "container %q not found", config.IpcMode.Container())
|
|
}
|
|
labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...)
|
|
}
|
|
|
|
for _, opt := range securityOpts {
|
|
if opt == "no-new-privileges" {
|
|
config.NoNewPrivileges = true
|
|
} else {
|
|
con := strings.SplitN(opt, "=", 2)
|
|
if len(con) != 2 {
|
|
return fmt.Errorf("Invalid --security-opt 1: %q", opt)
|
|
}
|
|
|
|
switch con[0] {
|
|
case "label":
|
|
labelOpts = append(labelOpts, con[1])
|
|
case "apparmor":
|
|
config.ApparmorProfile = con[1]
|
|
case "seccomp":
|
|
config.SeccompProfilePath = con[1]
|
|
default:
|
|
return fmt.Errorf("Invalid --security-opt 2: %q", opt)
|
|
}
|
|
}
|
|
}
|
|
|
|
if config.SeccompProfilePath == "" {
|
|
if _, err := os.Stat(seccompDefaultPath); err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return errors.Wrapf(err, "can't check if %q exists", seccompDefaultPath)
|
|
}
|
|
} else {
|
|
config.SeccompProfilePath = seccompDefaultPath
|
|
}
|
|
}
|
|
config.ProcessLabel, config.MountLabel, err = label.InitLabels(labelOpts)
|
|
return err
|
|
}
|
|
|
|
// Parses CLI options related to container creation into a config which can be
|
|
// parsed into an OCI runtime spec
|
|
func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime) (*createConfig, error) {
|
|
var command []string
|
|
var memoryLimit, memoryReservation, memorySwap, memoryKernel int64
|
|
var blkioWeight uint16
|
|
var uid, gid uint32
|
|
|
|
if len(c.Args()) < 1 {
|
|
return nil, errors.Errorf("image name or ID is required")
|
|
}
|
|
image := c.Args()[0]
|
|
|
|
if len(c.Args()) > 1 {
|
|
command = c.Args()[1:]
|
|
}
|
|
|
|
// LABEL VARIABLES
|
|
labels, err := getAllLabels(c.StringSlice("label-file"), c.StringSlice("labels"))
|
|
if err != nil {
|
|
return &createConfig{}, errors.Wrapf(err, "unable to process labels")
|
|
}
|
|
// ENVIRONMENT VARIABLES
|
|
env := defaultEnvVariables
|
|
if err := readKVStrings(env, c.StringSlice("env-file"), c.StringSlice("env")); err != nil {
|
|
return &createConfig{}, errors.Wrapf(err, "unable to process environment variables")
|
|
}
|
|
|
|
sysctl, err := convertStringSliceToMap(c.StringSlice("sysctl"), "=")
|
|
if err != nil {
|
|
return &createConfig{}, errors.Wrapf(err, "sysctl values must be in the form of KEY=VALUE")
|
|
}
|
|
|
|
groupAdd, err := stringSlicetoUint32Slice(c.StringSlice("group-add"))
|
|
if err != nil {
|
|
return &createConfig{}, errors.Wrapf(err, "invalid value for groups provided")
|
|
}
|
|
|
|
if c.String("user") != "" {
|
|
// TODO
|
|
// We need to mount the imagefs and get the uid/gid
|
|
// For now, user zeros
|
|
uid = 0
|
|
gid = 0
|
|
}
|
|
|
|
if c.String("memory") != "" {
|
|
memoryLimit, err = units.RAMInBytes(c.String("memory"))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "invalid value for memory")
|
|
}
|
|
}
|
|
if c.String("memory-reservation") != "" {
|
|
memoryReservation, err = units.RAMInBytes(c.String("memory-reservation"))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "invalid value for memory-reservation")
|
|
}
|
|
}
|
|
if c.String("memory-swap") != "" {
|
|
memorySwap, err = units.RAMInBytes(c.String("memory-swap"))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "invalid value for memory-swap")
|
|
}
|
|
}
|
|
if c.String("kernel-memory") != "" {
|
|
memoryKernel, err = units.RAMInBytes(c.String("kernel-memory"))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "invalid value for kernel-memory")
|
|
}
|
|
}
|
|
if c.String("blkio-weight") != "" {
|
|
u, err := strconv.ParseUint(c.String("blkio-weight"), 10, 16)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "invalid value for blkio-weight")
|
|
}
|
|
blkioWeight = uint16(u)
|
|
}
|
|
|
|
if err = parseVolumes(c.StringSlice("volume")); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Because we cannot do a non-terminal attach, we need to set tty to true
|
|
// if detach is not false
|
|
// TODO Allow non-terminal attach
|
|
tty := c.Bool("tty")
|
|
if !c.Bool("detach") && !tty {
|
|
tty = true
|
|
}
|
|
|
|
pidMode := container.PidMode(c.String("pid"))
|
|
if !pidMode.Valid() {
|
|
return nil, errors.Errorf("--pid %q is not valid", c.String("pid"))
|
|
}
|
|
|
|
if c.Bool("detach") && c.Bool("rm") {
|
|
return nil, errors.Errorf("--rm and --detach can not be specified together")
|
|
}
|
|
|
|
utsMode := container.UTSMode(c.String("uts"))
|
|
if !utsMode.Valid() {
|
|
return nil, errors.Errorf("--uts %q is not valid", c.String("uts"))
|
|
}
|
|
ipcMode := container.IpcMode(c.String("ipc"))
|
|
if !ipcMode.Valid() {
|
|
return nil, errors.Errorf("--ipc %q is not valid", ipcMode)
|
|
}
|
|
shmDir := ""
|
|
if ipcMode.IsHost() {
|
|
shmDir = "/dev/shm"
|
|
} else if ipcMode.IsContainer() {
|
|
ctr, err := runtime.LookupContainer(ipcMode.Container())
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "container %q not found", ipcMode.Container())
|
|
}
|
|
shmDir = ctr.ShmDir()
|
|
}
|
|
|
|
config := &createConfig{
|
|
Runtime: runtime,
|
|
CapAdd: c.StringSlice("cap-add"),
|
|
CapDrop: c.StringSlice("cap-drop"),
|
|
CgroupParent: c.String("cgroup-parent"),
|
|
Command: command,
|
|
Detach: c.Bool("detach"),
|
|
DnsOpt: c.StringSlice("dns-opt"),
|
|
DnsSearch: c.StringSlice("dns-search"),
|
|
DnsServers: c.StringSlice("dns"),
|
|
Entrypoint: c.String("entrypoint"),
|
|
Env: env,
|
|
Expose: c.StringSlice("expose"),
|
|
GroupAdd: groupAdd,
|
|
Hostname: c.String("hostname"),
|
|
Image: image,
|
|
Interactive: c.Bool("interactive"),
|
|
Ip6Address: c.String("ipv6"),
|
|
IpAddress: c.String("ip"),
|
|
Labels: labels,
|
|
LinkLocalIP: c.StringSlice("link-local-ip"),
|
|
LogDriver: c.String("log-driver"),
|
|
LogDriverOpt: c.StringSlice("log-opt"),
|
|
MacAddress: c.String("mac-address"),
|
|
Name: c.String("name"),
|
|
Network: c.String("network"),
|
|
NetworkAlias: c.StringSlice("network-alias"),
|
|
IpcMode: ipcMode,
|
|
NetMode: container.NetworkMode(c.String("network")),
|
|
UtsMode: utsMode,
|
|
PidMode: pidMode,
|
|
Pod: c.String("pod"),
|
|
Privileged: c.Bool("privileged"),
|
|
Publish: c.StringSlice("publish"),
|
|
PublishAll: c.Bool("publish-all"),
|
|
ReadOnlyRootfs: c.Bool("read-only"),
|
|
Resources: createResourceConfig{
|
|
BlkioWeight: blkioWeight,
|
|
BlkioWeightDevice: c.StringSlice("blkio-weight-device"),
|
|
CpuShares: c.Uint64("cpu-shares"),
|
|
CpuPeriod: c.Uint64("cpu-period"),
|
|
CpusetCpus: c.String("cpu-period"),
|
|
CpusetMems: c.String("cpuset-mems"),
|
|
CpuQuota: c.Int64("cpu-quota"),
|
|
CpuRtPeriod: c.Uint64("cpu-rt-period"),
|
|
CpuRtRuntime: c.Int64("cpu-rt-runtime"),
|
|
Cpus: c.String("cpus"),
|
|
DeviceReadBps: c.StringSlice("device-read-bps"),
|
|
DeviceReadIOps: c.StringSlice("device-read-iops"),
|
|
DeviceWriteBps: c.StringSlice("device-write-bps"),
|
|
DeviceWriteIOps: c.StringSlice("device-write-iops"),
|
|
DisableOomKiller: c.Bool("oom-kill-disable"),
|
|
ShmSize: c.String("shm-size"),
|
|
Memory: memoryLimit,
|
|
MemoryReservation: memoryReservation,
|
|
MemorySwap: memorySwap,
|
|
MemorySwappiness: c.Int("memory-swappiness"),
|
|
KernelMemory: memoryKernel,
|
|
OomScoreAdj: c.Int("oom-score-adj"),
|
|
|
|
PidsLimit: c.Int64("pids-limit"),
|
|
Ulimit: c.StringSlice("ulimit"),
|
|
},
|
|
Rm: c.Bool("rm"),
|
|
ShmDir: shmDir,
|
|
SigProxy: c.Bool("sig-proxy"),
|
|
StopSignal: c.String("stop-signal"),
|
|
StopTimeout: c.Int64("stop-timeout"),
|
|
StorageOpts: c.StringSlice("storage-opt"),
|
|
Sysctl: sysctl,
|
|
Tmpfs: c.StringSlice("tmpfs"),
|
|
Tty: tty,
|
|
User: uid,
|
|
Group: gid,
|
|
Volumes: c.StringSlice("volume"),
|
|
WorkDir: c.String("workdir"),
|
|
}
|
|
|
|
if !config.Privileged {
|
|
if err := parseSecurityOpt(config, c.StringSlice("security-opt")); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
config.SecurityOpts = c.StringSlice("security-opt")
|
|
warnings, err := verifyContainerResources(config, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, warning := range warnings {
|
|
fmt.Fprintln(os.Stderr, warning)
|
|
}
|
|
return config, nil
|
|
}
|