Remove kpod code after repository move

Signed-off-by: Matthew Heon <mheon@redhat.com>
This commit is contained in:
Matthew Heon 2017-11-02 11:53:01 -04:00
parent 19d90e7c23
commit 1bf6d20309
125 changed files with 3 additions and 36015 deletions

View File

@ -1 +0,0 @@
0.1

View File

@ -23,8 +23,6 @@ COMMIT_NO := $(shell git rev-parse HEAD 2> /dev/null || true)
GIT_COMMIT := $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}")
BUILD_INFO := $(shell date +%s)
KPOD_VERSION := ${shell cat ./KPOD_VERSION}
# If GOPATH not specified, use one in the local directory
ifeq ($(GOPATH),)
export GOPATH := $(CURDIR)/_output
@ -37,9 +35,7 @@ GOPKGBASEDIR := $(shell dirname "$(GOPKGDIR)")
VPATH := $(VPATH):$(GOPATH)
SHRINKFLAGS := -s -w
BASE_LDFLAGS := ${SHRINKFLAGS} -X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO}
KPOD_LDFLAGS := -X main.kpodVersion=${KPOD_VERSION}
LDFLAGS := -ldflags '${BASE_LDFLAGS}'
LDFLAGS_KPOD := -ldflags '${BASE_LDFLAGS} ${KPOD_LDFLAGS}'
all: binaries crio.conf docs
@ -49,7 +45,7 @@ help:
@echo "Usage: make <target>"
@echo
@echo " * 'install' - Install binaries to system locations"
@echo " * 'binaries' - Build crio, conmon, pause, crioctl and kpod"
@echo " * 'binaries' - Build crio, conmon, pause, and crioctl"
@echo " * 'integration' - Execute integration tests"
@echo " * 'clean' - Clean artifacts"
@echo " * 'lint' - Execute the source code linter"
@ -90,9 +86,6 @@ crio: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crio $(PROJECT))
crioctl: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crioctl $(PROJECT))
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o bin/$@ $(PROJECT)/cmd/crioctl
kpod: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/kpod $(PROJECT))
$(GO) build $(LDFLAGS_KPOD) -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/kpod
crio.conf: crio
./bin/crio --config="" config --default > crio.conf
@ -105,7 +98,7 @@ endif
rm -fr test/testdata/redis-image
find . -name \*~ -delete
find . -name \#\* -delete
rm -f bin/crioctl bin/crio bin/kpod
rm -f bin/crioctl bin/crio
make -C conmon clean
make -C pause clean
rm -f test/bin2img/bin2img
@ -127,7 +120,7 @@ testunit:
localintegration: clean binaries test-binaries
./test/test_runner.sh ${TESTFLAGS}
binaries: crio conmon pause kpod crioctl
binaries: crio conmon pause crioctl
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
MANPAGES_MD := $(wildcard docs/*.md)
@ -149,7 +142,6 @@ install: .gopathok install.bin install.man
install.bin:
install ${SELINUXOPT} -D -m 755 bin/crio $(BINDIR)/crio
install ${SELINUXOPT} -D -m 755 bin/crioctl $(BINDIR)/crioctl
install ${SELINUXOPT} -D -m 755 bin/kpod $(BINDIR)/kpod
install ${SELINUXOPT} -D -m 755 bin/conmon $(LIBEXECDIR)/crio/conmon
install ${SELINUXOPT} -D -m 755 bin/pause $(LIBEXECDIR)/crio/pause
@ -168,7 +160,6 @@ install.config:
install.completions:
install ${SELINUXOPT} -d -m 755 ${BASHINSTALLDIR}
install ${SELINUXOPT} -m 644 -D completions/bash/kpod ${BASHINSTALLDIR}
install.systemd:
install ${SELINUXOPT} -D -m 644 contrib/systemd/crio.service $(PREFIX)/lib/systemd/system/crio.service

View File

@ -40,38 +40,6 @@ It is currently in active development in the Kubernetes community through the [d
| Command | Description | Demo|
| ---------------------------------------------------- | --------------------------------------------------------------------------|-----|
| [crio(8)](/docs/crio.8.md) | OCI Kubernetes Container Runtime daemon ||
| [kpod(1)](/docs/kpod.1.md) | Simple management tool for pods and images ||
| [kpod-attach(1)](/docs/kpod-attach.1.md) | Instead of providing a `kpod attach` command, the man page `kpod-attach` describes how to use the `kpod logs` and `kpod exec` commands to achieve the same goals as `kpod attach`.||
| [kpod-cp(1)](/docs/kpod-cp.1.md) | Instead of providing a `kpod cp` command, the man page `kpod-cp` describes how to use the `kpod mount` command to have even more flexibility and functionality.||
| [kpod-create(1)](/docs/kpod-create.1.md) | Create a new container ||
| [kpod-diff(1)](/docs/kpod-diff.1.md) | Inspect changes on a container or image's filesystem ||
| [kpod-export(1)](/docs/kpod-export.1.md) | Export container's filesystem contents as a tar archive |[![...](/docs/play.png)](https://asciinema.org/a/913lBIRAg5hK8asyIhhkQVLtV)|
| [kpod-history(1)](/docs/kpod-history.1.md) | Shows the history of an image |[![...](/docs/play.png)](https://asciinema.org/a/bCvUQJ6DkxInMELZdc5DinNSx)|
| [kpod-images(1)](/docs/kpod-images.1.md) | List images in local storage |[![...](/docs/play.png)](https://asciinema.org/a/133649)|
| [kpod-info(1)](/docs/kpod-info.1.md) | Display system information ||
| [kpod-inspect(1)](/docs/kpod-inspect.1.md) | Display the configuration of a container or image |[![...](/docs/play.png)](https://asciinema.org/a/133418)|
| [kpod-kill(1)](/docs/kpod-kill.1.md) | Kill the main process in one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/3jNos0A5yzO4hChu7ddKkUPw7)|
| [kpod-load(1)](/docs/kpod-load.1.md) | Load an image from docker archive or oci |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)|
| [kpod-login(1)](/docs/kpod-login.1.md) | Login to a container registry |[![...](/docs/play.png)](https://asciinema.org/a/oNiPgmfo1FjV2YdesiLpvihtV)|
| [kpod-logout(1)](/docs/kpod-logout.1.md) | Logout of a container registry |[![...](/docs/play.png)](https://asciinema.org/a/oNiPgmfo1FjV2YdesiLpvihtV)|
| [kpod-logs(1)](/docs/kpod-logs.1.md) | Display the logs of a container ||
| [kpod-mount(1)](/docs/kpod-mount.1.md) | Mount a working container's root filesystem ||
| [kpod-pause(1)](/docs/kpod-pause.1.md) | Pause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)|
| [kpod-ps(1)](/docs/kpod-ps.1.md) | Prints out information about containers |[![...](/docs/play.png)](https://asciinema.org/a/bbT41kac6CwZ5giESmZLIaTLR)|
| [kpod-pull(1)](/docs/kpod-pull.1.md) | Pull an image from a registry |[![...](/docs/play.png)](https://asciinema.org/a/lr4zfoynHJOUNu1KaXa1dwG2X)|
| [kpod-push(1)](/docs/kpod-push.1.md) | Push an image to a specified destination |[![...](/docs/play.png)](https://asciinema.org/a/133276)|
| [kpod-rename(1)](/docs/kpod-rename.1.md) | Rename a container ||
| [kpod-rm(1)](/docs/kpod-rm.1.md) | Removes one or more containers |[![...](/docs/play.png)](https://asciinema.org/a/7EMk22WrfGtKWmgHJX9Nze1Qp)|
| [kpod-rmi(1)](/docs/kpod-rmi.1.md) | Removes one or more images |[![...](/docs/play.png)](https://asciinema.org/a/133799)|
| [kpod-run(1)](/docs/kpod-run.1.md) | Run a command in a new container ||
| [kpod-save(1)](/docs/kpod-save.1.md) | Saves an image to an archive |[![...](/docs/play.png)](https://asciinema.org/a/kp8kOaexEhEa20P1KLZ3L5X4g)|
| [kpod-stats(1)](/docs/kpod-stats.1.md) | Display a live stream of one or more containers' resource usage statistics||
| [kpod-stop(1)](/docs/kpod-stop.1.md) | Stops one or more running containers ||
| [kpod-tag(1)](/docs/kpod-tag.1.md) | Add an additional name to a local image |[![...](/docs/play.png)](https://asciinema.org/a/133803)|
| [kpod-umount(1)](/docs/kpod-umount.1.md) | Unmount a working container's root filesystem ||
| [kpod-unpause(1)](/docs/kpod-unpause.1.md) | Unpause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)|
| [kpod-version(1)](/docs/kpod-version.1.md) | Display the version information |[![...](/docs/play.png)](https://asciinema.org/a/mfrn61pjZT9Fc8L4NbfdSqfgu)|
| [kpod-wait(1)](/docs/kpod-wait.1.md) | Wait on one or more containers to stop and print their exit codes||
## Configuration
| File | Description |

View File

@ -1,16 +0,0 @@
# kpod - Simple debugging tool for pods and images
kpod is a simple client only tool to help with debugging issues when daemons such as CRI runtime and the kubelet are not responding or
failing. A shared API layer could be created to share code between the daemon and kpod. kpod does not require any daemon running. kpod
utilizes the same underlying components that crio uses i.e. containers/image, container/storage, oci-runtime-tool/generate, runc or
any other OCI compatible runtime. kpod shares state with crio and so has the capability to debug pods/images created by crio.
## Use cases
1. List pods.
2. Launch simple pods (that require no daemon support).
3. Exec commands in a container in a pod.
4. Launch additional containers in a pod.
5. List images.
6. Remove images not in use.
7. Pull images.
8. Check image size.
9. Report pod disk resource usage.

View File

@ -1,450 +0,0 @@
package main
import (
"os"
"reflect"
"regexp"
"strings"
is "github.com/containers/image/storage"
"github.com/containers/storage"
"github.com/fatih/camelcase"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/kubernetes-incubator/cri-o/server"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
stores = make(map[storage.Store]struct{})
)
func getStore(c *libkpod.Config) (storage.Store, error) {
options := storage.DefaultStoreOptions
options.GraphRoot = c.Root
options.RunRoot = c.RunRoot
options.GraphDriverName = c.Storage
options.GraphDriverOptions = c.StorageOptions
store, err := storage.GetStore(options)
if err != nil {
return nil, err
}
is.Transport.SetStore(store)
stores[store] = struct{}{}
return store, nil
}
func getRuntime(c *cli.Context) (*libpod.Runtime, error) {
config, err := getConfig(c)
if err != nil {
return nil, errors.Wrapf(err, "could not get config")
}
options := storage.DefaultStoreOptions
options.GraphRoot = config.Root
options.RunRoot = config.RunRoot
options.GraphDriverName = config.Storage
options.GraphDriverOptions = config.StorageOptions
return libpod.NewRuntime(libpod.WithStorageConfig(options), libpod.WithConmonPath(config.Conmon), libpod.WithOCIRuntime(config.Runtime))
}
func shutdownStores() {
for store := range stores {
if _, err := store.Shutdown(false); err != nil {
break
}
}
}
func getConfig(c *cli.Context) (*libkpod.Config, error) {
config := libkpod.DefaultConfig()
var configFile string
if c.GlobalIsSet("config") {
configFile = c.GlobalString("config")
} else if _, err := os.Stat(server.CrioConfigPath); err == nil {
configFile = server.CrioConfigPath
}
// load and merge the configfile from the commandline or use
// the default crio config file
if configFile != "" {
err := config.UpdateFromFile(configFile)
if err != nil {
return config, err
}
}
if c.GlobalIsSet("root") {
config.Root = c.GlobalString("root")
}
if c.GlobalIsSet("runroot") {
config.RunRoot = c.GlobalString("runroot")
}
if c.GlobalIsSet("conmon") {
config.Conmon = c.GlobalString("conmon")
}
if c.GlobalIsSet("storage-driver") {
config.Storage = c.GlobalString("storage-driver")
}
if c.GlobalIsSet("storage-opt") {
opts := c.GlobalStringSlice("storage-opt")
if len(opts) > 0 {
config.StorageOptions = opts
}
}
if c.GlobalIsSet("runtime") {
config.Runtime = c.GlobalString("runtime")
}
return config, nil
}
func splitCamelCase(src string) string {
entries := camelcase.Split(src)
return strings.Join(entries, " ")
}
// validateFlags searches for StringFlags or StringSlice flags that never had
// a value set. This commonly occurs when the CLI mistakenly takes the next
// option and uses it as a value.
func validateFlags(c *cli.Context, flags []cli.Flag) error {
for _, flag := range flags {
switch reflect.TypeOf(flag).String() {
case "cli.StringSliceFlag":
{
f := flag.(cli.StringSliceFlag)
name := strings.Split(f.Name, ",")
val := c.StringSlice(name[0])
for _, v := range val {
if ok, _ := regexp.MatchString("^-.+", v); ok {
return errors.Errorf("option --%s requires a value", name[0])
}
}
}
case "cli.StringFlag":
{
f := flag.(cli.StringFlag)
name := strings.Split(f.Name, ",")
val := c.String(name[0])
if ok, _ := regexp.MatchString("^-.+", val); ok {
return errors.Errorf("option --%s requires a value", name[0])
}
}
}
}
return nil
}
// Common flags shared between commands
var createFlags = []cli.Flag{
cli.StringSliceFlag{
Name: "add-host",
Usage: "Add a custom host-to-IP mapping (host:ip) (default [])",
},
cli.StringSliceFlag{
Name: "attach, a",
Usage: "Attach to STDIN, STDOUT or STDERR (default [])",
},
cli.StringFlag{
Name: "blkio-weight",
Usage: "Block IO weight (relative weight) accepts a weight value between 10 and 1000.",
},
cli.StringSliceFlag{
Name: "blkio-weight-device",
Usage: "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)",
},
cli.StringSliceFlag{
Name: "cap-add",
Usage: "Add capabilities to the container",
},
cli.StringSliceFlag{
Name: "cap-drop",
Usage: "Drop capabilities from the container",
},
cli.StringFlag{
Name: "cgroup-parent",
Usage: "Optional parent cgroup for the container",
},
cli.StringFlag{
Name: "cidfile",
Usage: "Write the container ID to the file",
},
cli.Uint64Flag{
Name: "cpu-period",
Usage: "Limit the CPU CFS (Completely Fair Scheduler) period",
},
cli.Int64Flag{
Name: "cpu-quota",
Usage: "Limit the CPU CFS (Completely Fair Scheduler) quota",
},
cli.Uint64Flag{
Name: "cpu-rt-period",
Usage: "Limit the CPU real-time period in microseconds",
},
cli.Int64Flag{
Name: "cpu-rt-runtime",
Usage: "Limit the CPU real-time runtime in microseconds",
},
cli.Uint64Flag{
Name: "cpu-shares",
Usage: "CPU shares (relative weight)",
},
cli.StringFlag{
Name: "cpus",
Usage: "Number of CPUs. The default is 0.000 which means no limit",
},
cli.StringFlag{
Name: "cpuset-cpus",
Usage: "CPUs in which to allow execution (0-3, 0,1)",
},
cli.StringFlag{
Name: "cpuset-mems",
Usage: "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.",
},
cli.BoolFlag{
Name: "detach, d",
Usage: "Run container in background and print container ID",
},
cli.StringFlag{
Name: "detach-keys",
Usage: "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`",
},
cli.StringSliceFlag{
Name: "device",
Usage: "Add a host device to the container (default [])",
},
cli.StringSliceFlag{
Name: "device-read-bps",
Usage: "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
},
cli.StringSliceFlag{
Name: "device-read-iops",
Usage: "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)",
},
cli.StringSliceFlag{
Name: "device-write-bps",
Usage: "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)",
},
cli.StringSliceFlag{
Name: "device-write-iops",
Usage: "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)",
},
cli.StringSliceFlag{
Name: "dns",
Usage: "Set custom DNS servers",
},
cli.StringSliceFlag{
Name: "dns-opt",
Usage: "Set custom DNS options",
},
cli.StringSliceFlag{
Name: "dns-search",
Usage: "Set custom DNS search domains",
},
cli.StringFlag{
Name: "entrypoint",
Usage: "Overwrite the default ENTRYPOINT of the image",
},
cli.StringSliceFlag{
Name: "env, e",
Usage: "Set environment variables in container",
},
cli.StringSliceFlag{
Name: "env-file",
Usage: "Read in a file of environment variables",
},
cli.StringSliceFlag{
Name: "expose",
Usage: "Expose a port or a range of ports (default [])",
},
cli.StringSliceFlag{
Name: "group-add",
Usage: "Add additional groups to join (default [])",
},
cli.StringFlag{
Name: "hostname",
Usage: "Set container hostname",
},
cli.BoolFlag{
Name: "interactive, i",
Usage: "Keep STDIN open even if not attached",
},
cli.StringFlag{
Name: "ip",
Usage: "Container IPv4 address (e.g. 172.23.0.9)",
},
cli.StringFlag{
Name: "ip6",
Usage: "Container IPv6 address (e.g. 2001:db8::1b99)",
},
cli.StringFlag{
Name: "ipc",
Usage: "IPC Namespace to use",
},
cli.StringFlag{
Name: "kernel-memory",
Usage: "Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)",
},
cli.StringSliceFlag{
Name: "label",
Usage: "Set metadata on container (default [])",
},
cli.StringSliceFlag{
Name: "label-file",
Usage: "Read in a line delimited file of labels (default [])",
},
cli.StringSliceFlag{
Name: "link-local-ip",
Usage: "Container IPv4/IPv6 link-local addresses (default [])",
},
cli.StringFlag{
Name: "log-driver",
Usage: "Logging driver for the container",
},
cli.StringSliceFlag{
Name: "log-opt",
Usage: "Logging driver options (default [])",
},
cli.StringFlag{
Name: "mac-address",
Usage: "Container MAC address (e.g. 92:d0:c6:0a:29:33)",
},
cli.StringFlag{
Name: "memory, m",
Usage: "Memory limit (format: <number>[<unit>], where unit = b, k, m or g)",
},
cli.StringFlag{
Name: "memory-reservation",
Usage: "Memory soft limit (format: <number>[<unit>], where unit = b, k, m or g)",
},
cli.StringFlag{
Name: "memory-swap",
Usage: "Swap limit equal to memory plus swap: '-1' to enable unlimited swap",
},
cli.Int64Flag{
Name: "memory-swappiness",
Usage: "Tune container memory swappiness (0 to 100) (default -1)",
},
cli.StringFlag{
Name: "name",
Usage: "Assign a name to the container",
},
cli.StringFlag{
Name: "net",
Usage: "Setup the network namespace",
},
cli.StringFlag{
Name: "network",
Usage: "Connect a container to a network (default 'default')",
},
cli.StringSliceFlag{
Name: "network-alias",
Usage: "Add network-scoped alias for the container (default [])",
},
cli.BoolFlag{
Name: "oom-kill-disable",
Usage: "Disable OOM Killer",
},
cli.StringFlag{
Name: "oom-score-adj",
Usage: "Tune the host's OOM preferences (-1000 to 1000)",
},
cli.StringFlag{
Name: "pid",
Usage: "PID Namespace to use",
},
cli.Int64Flag{
Name: "pids-limit",
Usage: "Tune container pids limit (set -1 for unlimited)",
},
cli.StringFlag{
Name: "pod",
Usage: "Run container in an existing pod",
},
cli.BoolFlag{
Name: "privileged",
Usage: "Give extended privileges to container",
},
cli.StringSliceFlag{
Name: "publish, p",
Usage: "Publish a container's port, or a range of ports, to the host (default [])",
},
cli.BoolFlag{
Name: "publish-all, P",
Usage: "Publish all exposed ports to random ports on the host interface",
},
cli.BoolFlag{
Name: "read-only",
Usage: "Make containers root filesystem read-only",
},
cli.BoolFlag{
Name: "rm",
Usage: "Remove container (and pod if created) after exit",
},
cli.StringSliceFlag{
Name: "security-opt",
Usage: "Security Options (default [])",
},
cli.StringFlag{
Name: "shm-size",
Usage: "Size of `/dev/shm`. The format is `<number><unit>`. default is 64 MB",
},
cli.BoolFlag{
Name: "sig-proxy",
Usage: "Proxy received signals to the process (default true)",
},
cli.StringFlag{
Name: "stop-signal",
Usage: "Signal to stop a container. Default is SIGTERM",
},
cli.IntFlag{
Name: "stop-timeout",
Usage: "Timeout (in seconds) to stop a container. Default is 10",
},
cli.StringSliceFlag{
Name: "storage-opt",
Usage: "Storage driver options per container (default [])",
},
cli.StringSliceFlag{
Name: "sysctl",
Usage: "Sysctl options (default [])",
},
cli.StringSliceFlag{
Name: "tmpfs",
Usage: "Mount a temporary filesystem (`tmpfs`) into a container (default [])",
},
cli.BoolFlag{
Name: "tty, t",
Usage: "Allocate a pseudo-TTY for container",
},
cli.StringSliceFlag{
Name: "ulimit",
Usage: "Ulimit options (default [])",
},
cli.StringFlag{
Name: "user, u",
Usage: "Username or UID (format: <name|uid>[:<group|gid>])",
},
cli.StringFlag{
Name: "userns",
Usage: "User namespace to use",
},
cli.StringFlag{
Name: "uts",
Usage: "UTS namespace to use",
},
cli.StringSliceFlag{
Name: "volume, v",
Usage: "Bind mount a volume into the container (default [])",
},
cli.StringSliceFlag{
Name: "volumes-from",
Usage: "Mount volumes from the specified container(s) (default [])",
},
cli.StringFlag{
Name: "workdir, w",
Usage: "Working `directory inside the container",
Value: "/",
},
}

View File

@ -1,51 +0,0 @@
package main
import (
"os/exec"
"os/user"
"testing"
"flag"
"github.com/urfave/cli"
)
func TestGetStore(t *testing.T) {
t.Skip("FIX THIS!")
//cmd/kpod/common_test.go:27: cannot use c (type *cli.Context) as type *libkpod.Config in argument to getStore
// Make sure the tests are running as root
skipTestIfNotRoot(t)
set := flag.NewFlagSet("test", 0)
globalSet := flag.NewFlagSet("test", 0)
globalSet.String("root", "", "path to the root directory in which data, including images, is stored")
globalCtx := cli.NewContext(nil, globalSet, nil)
command := cli.Command{Name: "imagesCommand"}
c := cli.NewContext(nil, set, globalCtx)
c.Command = command
//_, err := getStore(c)
//if err != nil {
//t.Error(err)
//}
}
func skipTestIfNotRoot(t *testing.T) {
u, err := user.Current()
if err != nil {
t.Skip("Could not determine user. Running without root may cause tests to fail")
} else if u.Uid != "0" {
t.Skip("tests will fail unless run as root")
}
}
func pullTestImage(name string) error {
cmd := exec.Command("crioctl", "image", "pull", name)
err := cmd.Run()
if err != nil {
return err
}
return nil
}

View File

@ -1,343 +0,0 @@
package main
import (
"fmt"
"strconv"
"github.com/docker/go-units"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/pkg/errors"
"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 = []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM=xterm"}
)
type createResourceConfig struct {
blkioDevice []string // blkio-weight-device
blkioWeight uint16 // blkio-weight
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
memorySwapiness uint64 // memory-swappiness
oomScoreAdj int //oom-score-adj
pidsLimit int64 // pids-limit
shmSize string
ulimit []string //ulimit
}
type createConfig struct {
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 []string //env
expose []string //expose
groupAdd []uint32 // group-add
hostname string //hostname
image string
interactive bool //interactive
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
network string //network
networkAlias []string //network-alias
nsIPC string // ipc
nsNet string //net
nsPID string //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
securityOpts []string //security-opt
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
volumes []string //volume
volumesFrom []string //volumes-from
workDir string //workdir
}
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...]]",
}
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
if err := validateFlags(c, createFlags); err != nil {
return err
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
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)
if !createImage.HasImageLocal() {
// 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
}
defer runtime.Shutdown(false)
imageName, err := createImage.GetFQName()
if err != nil {
return err
}
imageID, err := createImage.GetImageID()
if err != nil {
return err
}
options, err := createConfig.GetContainerCreateOptions(c)
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))
ctr, err := runtime.NewContainer(runtimeSpec, options...)
if err != nil {
return err
}
if c.String("cidfile") != "" {
libpod.WriteFile(ctr.ID(), c.String("cidfile"))
} else {
fmt.Printf("%s\n", ctr.ID())
}
return nil
}
// 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
image := c.Args()[0]
if len(c.Args()) < 1 {
return nil, errors.Errorf("image name or ID is required")
}
if len(c.Args()) > 1 {
command = c.Args()[1:]
}
// LABEL VARIABLES
labels, err := getAllLabels(c)
if err != nil {
return &createConfig{}, errors.Wrapf(err, "unable to process labels")
}
// ENVIRONMENT VARIABLES
// TODO where should env variables be verified to be x=y format
env, err := getAllEnvironmentVariables(c)
if 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)
}
config := &createConfig{
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("env"),
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"),
nsIPC: c.String("ipc"),
nsNet: c.String("net"),
nsPID: c.String("pid"),
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,
blkioDevice: 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,
memorySwapiness: c.Uint64("memory-swapiness"),
kernelMemory: memoryKernel,
oomScoreAdj: c.Int("oom-score-adj"),
pidsLimit: c.Int64("pids-limit"),
ulimit: c.StringSlice("ulimit"),
},
rm: c.Bool("rm"),
securityOpts: c.StringSlice("security-opt"),
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: c.Bool("tty"),
user: uid,
group: gid,
volumes: c.StringSlice("volume"),
volumesFrom: c.StringSlice("volumes-from"),
workDir: c.String("workdir"),
}
return config, nil
}

View File

@ -1,52 +0,0 @@
package main
import (
"strings"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
func getAllLabels(cli *cli.Context) (map[string]string, error) {
var labelValues []string
labels := make(map[string]string)
labelValues, labelErr := readKVStrings(cli.StringSlice("label-file"), cli.StringSlice("label"))
if labelErr != nil {
return labels, errors.Wrapf(labelErr, "unable to process labels from --label and label-file")
}
// Process KEY=VALUE stringslice in string map for WithLabels func
if len(labelValues) > 0 {
for _, i := range labelValues {
spliti := strings.Split(i, "=")
if len(spliti) > 1 {
return labels, errors.Errorf("labels must be in KEY=VALUE format: %s is invalid", i)
}
labels[spliti[0]] = spliti[1]
}
}
return labels, nil
}
func getAllEnvironmentVariables(cli *cli.Context) ([]string, error) {
env, err := readKVStrings(cli.StringSlice("env-file"), cli.StringSlice("env"))
if err != nil {
return []string{}, errors.Wrapf(err, "unable to process variables from --env and --env-file")
}
// Add default environment variables if nothing defined
if len(env) == 0 {
env = append(env, defaultEnvVariables...)
}
return env, nil
}
func convertStringSliceToMap(strSlice []string, delimiter string) (map[string]string, error) {
sysctl := make(map[string]string)
for _, inputSysctl := range strSlice {
values := strings.Split(inputSysctl, delimiter)
if len(values) < 2 {
return sysctl, errors.Errorf("%s in an invalid sysctl value", inputSysctl)
}
sysctl[values[0]] = values[1]
}
return sysctl, nil
}

View File

@ -1,128 +0,0 @@
package main
import (
"fmt"
"github.com/containers/storage/pkg/archive"
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
type diffJSONOutput struct {
Changed []string `json:"changed,omitempty"`
Added []string `json:"added,omitempty"`
Deleted []string `json:"deleted,omitempty"`
}
type diffOutputParams struct {
Change archive.ChangeType
Path string
}
type stdoutStruct struct {
output []diffOutputParams
}
func (so stdoutStruct) Out() error {
for _, d := range so.output {
fmt.Printf("%s %s\n", d.Change, d.Path)
}
return nil
}
var (
diffFlags = []cli.Flag{
cli.BoolFlag{
Name: "archive",
Usage: "Save the diff as a tar archive",
Hidden: true,
},
cli.StringFlag{
Name: "format",
Usage: "Change the output format.",
},
}
diffDescription = fmt.Sprint(`Displays changes on a container or image's filesystem. The
container or image will be compared to its parent layer`)
diffCommand = cli.Command{
Name: "diff",
Usage: "Inspect changes on container's file systems",
Description: diffDescription,
Flags: diffFlags,
Action: diffCmd,
ArgsUsage: "ID-NAME",
}
)
func formatJSON(output []diffOutputParams) (diffJSONOutput, error) {
jsonStruct := diffJSONOutput{}
for _, output := range output {
switch output.Change {
case archive.ChangeModify:
jsonStruct.Changed = append(jsonStruct.Changed, output.Path)
case archive.ChangeAdd:
jsonStruct.Added = append(jsonStruct.Added, output.Path)
case archive.ChangeDelete:
jsonStruct.Deleted = append(jsonStruct.Deleted, output.Path)
default:
return jsonStruct, errors.Errorf("output kind %q not recognized", output.Change.String())
}
}
return jsonStruct, nil
}
func diffCmd(c *cli.Context) error {
if err := validateFlags(c, diffFlags); err != nil {
return err
}
if len(c.Args()) != 1 {
return errors.Errorf("container, image, or layer name must be specified: kpod diff [options [...]] ID-NAME")
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
to := c.Args().Get(0)
changes, err := runtime.GetDiff("", to)
if err != nil {
return errors.Wrapf(err, "could not get changes for %q", to)
}
diffOutput := []diffOutputParams{}
outputFormat := c.String("format")
for _, change := range changes {
params := diffOutputParams{
Change: change.Kind,
Path: change.Path,
}
diffOutput = append(diffOutput, params)
}
var out formats.Writer
if outputFormat != "" {
switch outputFormat {
case formats.JSONString:
data, err := formatJSON(diffOutput)
if err != nil {
return err
}
out = formats.JSONStruct{Output: data}
default:
return errors.New("only valid format for diff is 'json'")
}
} else {
out = stdoutStruct{output: diffOutput}
}
formats.Writer(out).Out()
return nil
}

View File

@ -1,271 +0,0 @@
package docker
//
// Types extracted from Docker
//
import (
"time"
"github.com/containers/image/pkg/strslice"
"github.com/opencontainers/go-digest"
)
// TypeLayers github.com/docker/docker/image/rootfs.go
const TypeLayers = "layers"
// V2S2MediaTypeManifest github.com/docker/distribution/manifest/schema2/manifest.go
const V2S2MediaTypeManifest = "application/vnd.docker.distribution.manifest.v2+json"
// V2S2MediaTypeImageConfig github.com/docker/distribution/manifest/schema2/manifest.go
const V2S2MediaTypeImageConfig = "application/vnd.docker.container.image.v1+json"
// V2S2MediaTypeLayer github.com/docker/distribution/manifest/schema2/manifest.go
const V2S2MediaTypeLayer = "application/vnd.docker.image.rootfs.diff.tar.gzip"
// V2S2MediaTypeUncompressedLayer github.com/docker/distribution/manifest/schema2/manifest.go
const V2S2MediaTypeUncompressedLayer = "application/vnd.docker.image.rootfs.diff.tar"
// V2S2RootFS describes images root filesystem
// This is currently a placeholder that only supports layers. In the future
// this can be made into an interface that supports different implementations.
// github.com/docker/docker/image/rootfs.go
type V2S2RootFS struct {
Type string `json:"type"`
DiffIDs []digest.Digest `json:"diff_ids,omitempty"`
}
// V2S2History stores build commands that were used to create an image
// github.com/docker/docker/image/image.go
type V2S2History struct {
// Created is the timestamp at which the image was created
Created time.Time `json:"created"`
// Author is the name of the author that was specified when committing the image
Author string `json:"author,omitempty"`
// CreatedBy keeps the Dockerfile command used while building the image
CreatedBy string `json:"created_by,omitempty"`
// Comment is the commit message that was set when committing the image
Comment string `json:"comment,omitempty"`
// EmptyLayer is set to true if this history item did not generate a
// layer. Otherwise, the history item is associated with the next
// layer in the RootFS section.
EmptyLayer bool `json:"empty_layer,omitempty"`
}
// ID is the content-addressable ID of an image.
// github.com/docker/docker/image/image.go
type ID digest.Digest
// HealthConfig holds configuration settings for the HEALTHCHECK feature.
// github.com/docker/docker/api/types/container/config.go
type HealthConfig struct {
// Test is the test to perform to check that the container is healthy.
// An empty slice means to inherit the default.
// The options are:
// {} : inherit healthcheck
// {"NONE"} : disable healthcheck
// {"CMD", args...} : exec arguments directly
// {"CMD-SHELL", command} : run command with system's default shell
Test []string `json:",omitempty"`
// Zero means to inherit. Durations are expressed as integer nanoseconds.
Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
Timeout time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.
// Retries is the number of consecutive failures needed to consider a container as unhealthy.
// Zero means inherit.
Retries int `json:",omitempty"`
}
// PortSet is a collection of structs indexed by Port
// github.com/docker/go-connections/nat/nat.go
type PortSet map[Port]struct{}
// Port is a string containing port number and protocol in the format "80/tcp"
// github.com/docker/go-connections/nat/nat.go
type Port string
// Config contains the configuration data about a container.
// It should hold only portable information about the container.
// Here, "portable" means "independent from the host we are running on".
// Non-portable information *should* appear in HostConfig.
// All fields added to this struct must be marked `omitempty` to keep getting
// predictable hashes from the old `v1Compatibility` configuration.
// github.com/docker/docker/api/types/container/config.go
type Config struct {
Hostname string // Hostname
Domainname string // Domainname
User string // User that will run the command(s) inside the container, also support user:group
AttachStdin bool // Attach the standard input, makes possible user interaction
AttachStdout bool // Attach the standard output
AttachStderr bool // Attach the standard error
ExposedPorts PortSet `json:",omitempty"` // List of exposed ports
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
OpenStdin bool // Open stdin
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
Env []string // List of environment variable to set in the container
Cmd strslice.StrSlice // Command to run when starting the container
Healthcheck *HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
ArgsEscaped bool `json:",omitempty"` // True if command is already escaped (Windows specific)
Image string // Name of the image as it was passed by the operator (e.g. could be symbolic)
Volumes map[string]struct{} // List of volumes (mounts) used for the container
WorkingDir string // Current directory (PWD) in the command will be launched
Entrypoint strslice.StrSlice // Entrypoint to run when starting the container
NetworkDisabled bool `json:",omitempty"` // Is network disabled
MacAddress string `json:",omitempty"` // Mac Address of the container
OnBuild []string // ONBUILD metadata that were defined on the image Dockerfile
Labels map[string]string // List of labels set to this container
StopSignal string `json:",omitempty"` // Signal to stop a container
StopTimeout *int `json:",omitempty"` // Timeout (in seconds) to stop a container
Shell strslice.StrSlice `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
}
// V1Compatibility - For non-top-level layers, create fake V1Compatibility
// strings that fit the format and don't collide with anything else, but
// don't result in runnable images on their own.
// github.com/docker/distribution/manifest/schema1/config_builder.go
type V1Compatibility struct {
ID string `json:"id"`
Parent string `json:"parent,omitempty"`
Comment string `json:"comment,omitempty"`
Created time.Time `json:"created"`
ContainerConfig struct {
Cmd []string
} `json:"container_config,omitempty"`
Author string `json:"author,omitempty"`
ThrowAway bool `json:"throwaway,omitempty"`
}
// V1Image stores the V1 image configuration.
// github.com/docker/docker/image/image.go
type V1Image struct {
// ID is a unique 64 character identifier of the image
ID string `json:"id,omitempty"`
// Parent is the ID of the parent image
Parent string `json:"parent,omitempty"`
// Comment is the commit message that was set when committing the image
Comment string `json:"comment,omitempty"`
// Created is the timestamp at which the image was created
Created time.Time `json:"created"`
// Container is the id of the container used to commit
Container string `json:"container,omitempty"`
// ContainerConfig is the configuration of the container that is committed into the image
ContainerConfig Config `json:"container_config,omitempty"`
// DockerVersion specifies the version of Docker that was used to build the image
DockerVersion string `json:"docker_version,omitempty"`
// Author is the name of the author that was specified when committing the image
Author string `json:"author,omitempty"`
// Config is the configuration of the container received from the client
Config *Config `json:"config,omitempty"`
// Architecture is the hardware that the image is build and runs on
Architecture string `json:"architecture,omitempty"`
// OS is the operating system used to build and run the image
OS string `json:"os,omitempty"`
// Size is the total size of the image including all layers it is composed of
Size int64 `json:",omitempty"`
}
// V2Image stores the image configuration
// github.com/docker/docker/image/image.go
type V2Image struct {
V1Image
Parent ID `json:"parent,omitempty"`
RootFS *V2S2RootFS `json:"rootfs,omitempty"`
History []V2S2History `json:"history,omitempty"`
OSVersion string `json:"os.version,omitempty"`
OSFeatures []string `json:"os.features,omitempty"`
// rawJSON caches the immutable JSON associated with this image.
//rawJSON []byte
// computedID is the ID computed from the hash of the image config.
// Not to be confused with the legacy V1 ID in V1Image.
//computedID ID
}
// V2Versioned provides a struct with the manifest schemaVersion and mediaType.
// Incoming content with unknown schema version can be decoded against this
// struct to check the version.
// github.com/docker/distribution/manifest/versioned.go
type V2Versioned struct {
// SchemaVersion is the image manifest schema that this image follows
SchemaVersion int `json:"schemaVersion"`
// MediaType is the media type of this schema.
MediaType string `json:"mediaType,omitempty"`
}
// V2S1FSLayer is a container struct for BlobSums defined in an image manifest
// github.com/docker/distribution/manifest/schema1/manifest.go
type V2S1FSLayer struct {
// BlobSum is the tarsum of the referenced filesystem image layer
BlobSum digest.Digest `json:"blobSum"`
}
// V2S1History stores unstructured v1 compatibility information
// github.com/docker/distribution/manifest/schema1/manifest.go
type V2S1History struct {
// V1Compatibility is the raw v1 compatibility information
V1Compatibility string `json:"v1Compatibility"`
}
// V2S1Manifest provides the base accessible fields for working with V2 image
// format in the registry.
// github.com/docker/distribution/manifest/schema1/manifest.go
type V2S1Manifest struct {
V2Versioned
// Name is the name of the image's repository
Name string `json:"name"`
// Tag is the tag of the image specified by this manifest
Tag string `json:"tag"`
// Architecture is the host architecture on which this image is intended to
// run
Architecture string `json:"architecture"`
// FSLayers is a list of filesystem layer blobSums contained in this image
FSLayers []V2S1FSLayer `json:"fsLayers"`
// History is a list of unstructured historical data for v1 compatibility
History []V2S1History `json:"history"`
}
// V2S2Descriptor describes targeted content. Used in conjunction with a blob
// store, a descriptor can be used to fetch, store and target any kind of
// blob. The struct also describes the wire protocol format. Fields should
// only be added but never changed.
// github.com/docker/distribution/blobs.go
type V2S2Descriptor struct {
// MediaType describe the type of the content. All text based formats are
// encoded as utf-8.
MediaType string `json:"mediaType,omitempty"`
// Size in bytes of content.
Size int64 `json:"size,omitempty"`
// Digest uniquely identifies the content. A byte stream can be verified
// against against this digest.
Digest digest.Digest `json:"digest,omitempty"`
// URLs contains the source URLs of this content.
URLs []string `json:"urls,omitempty"`
// NOTE: Before adding a field here, please ensure that all
// other options have been exhausted. Much of the type relationships
// depend on the simplicity of this type.
}
// V2S2Manifest defines a schema2 manifest.
// github.com/docker/distribution/manifest/schema2/manifest.go
type V2S2Manifest struct {
V2Versioned
// Config references the image configuration as a blob.
Config V2S2Descriptor `json:"config"`
// Layers lists descriptors for the layers referenced by the
// configuration.
Layers []V2S2Descriptor `json:"layers"`
}

View File

@ -1,106 +0,0 @@
package main
import (
"io"
"os"
"fmt"
"github.com/containers/storage"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
type exportOptions struct {
output string
container string
}
var (
exportFlags = []cli.Flag{
cli.StringFlag{
Name: "output, o",
Usage: "Write to a file, default is STDOUT",
Value: "/dev/stdout",
},
}
exportDescription = "Exports container's filesystem contents as a tar archive" +
" and saves it on the local machine."
exportCommand = cli.Command{
Name: "export",
Usage: "Export container's filesystem contents as a tar archive",
Description: exportDescription,
Flags: exportFlags,
Action: exportCmd,
ArgsUsage: "CONTAINER",
}
)
// exportCmd saves a container to a tarball on disk
func exportCmd(c *cli.Context) error {
args := c.Args()
if len(args) == 0 {
return errors.Errorf("container id must be specified")
}
if len(args) > 1 {
return errors.Errorf("too many arguments given, need 1 at most.")
}
container := args[0]
if err := validateFlags(c, exportFlags); err != nil {
return err
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
store, err := getStore(config)
if err != nil {
return err
}
output := c.String("output")
if output == "/dev/stdout" {
file := os.Stdout
if logrus.IsTerminal(file) {
return errors.Errorf("refusing to export to terminal. Use -o flag or redirect")
}
}
opts := exportOptions{
output: output,
container: container,
}
return exportContainer(store, opts)
}
// exportContainer exports the contents of a container and saves it as
// a tarball on disk
func exportContainer(store storage.Store, opts exportOptions) error {
mountPoint, err := store.Mount(opts.container, "")
if err != nil {
return errors.Wrapf(err, "error finding container %q", opts.container)
}
defer func() {
if err := store.Unmount(opts.container); err != nil {
fmt.Printf("error unmounting container %q: %v\n", opts.container, err)
}
}()
input, err := archive.Tar(mountPoint, archive.Uncompressed)
if err != nil {
return errors.Wrapf(err, "error reading container directory %q", opts.container)
}
outFile, err := os.Create(opts.output)
if err != nil {
return errors.Wrapf(err, "error creating file %q", opts.output)
}
defer outFile.Close()
_, err = io.Copy(outFile, input)
return err
}

View File

@ -1,143 +0,0 @@
package formats
import (
"encoding/json"
"fmt"
"os"
"strings"
"text/tabwriter"
"text/template"
"bytes"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
)
const (
// JSONString const to save on duplicate variable names
JSONString = "json"
// IDString const to save on duplicates for Go templates
IDString = "{{.ID}}"
)
// Writer interface for outputs
type Writer interface {
Out() error
}
// JSONStructArray for JSON output
type JSONStructArray struct {
Output []interface{}
}
// StdoutTemplateArray for Go template output
type StdoutTemplateArray struct {
Output []interface{}
Template string
Fields map[string]string
}
// JSONStruct for JSON output
type JSONStruct struct {
Output interface{}
}
// StdoutTemplate for Go template output
type StdoutTemplate struct {
Output interface{}
Template string
Fields map[string]string
}
// YAMLStruct for YAML output
type YAMLStruct struct {
Output interface{}
}
// Out method for JSON Arrays
func (j JSONStructArray) Out() error {
data, err := json.MarshalIndent(j.Output, "", " ")
if err != nil {
return err
}
// JSON returns a byte array with a literal null [110 117 108 108] in it
// if it is passed empty data. We used bytes.Compare to see if that is
// the case.
if diff := bytes.Compare(data, []byte("null")); diff == 0 {
data = []byte("[]")
}
// If the we did get NULL back, we should spit out {} which is
// at least valid JSON for the consumer.
fmt.Printf("%s\n", data)
return nil
}
// Out method for Go templates
func (t StdoutTemplateArray) Out() error {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
if strings.HasPrefix(t.Template, "table") {
// replace any spaces with tabs in template so that tabwriter can align it
t.Template = strings.Replace(strings.TrimSpace(t.Template[5:]), " ", "\t", -1)
headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template)
if err != nil {
return errors.Wrapf(err, "Template parsing error")
}
err = headerTmpl.Execute(w, t.Fields)
if err != nil {
return err
}
fmt.Fprintln(w, "")
}
t.Template = strings.Replace(t.Template, " ", "\t", -1)
tmpl, err := template.New("image").Funcs(basicFunctions).Parse(t.Template)
if err != nil {
return errors.Wrapf(err, "Template parsing error")
}
for _, img := range t.Output {
basicTmpl := tmpl.Funcs(basicFunctions)
err = basicTmpl.Execute(w, img)
if err != nil {
return err
}
fmt.Fprintln(w, "")
}
return w.Flush()
}
// Out method for JSON struct
func (j JSONStruct) Out() error {
data, err := json.MarshalIndent(j.Output, "", " ")
if err != nil {
return err
}
fmt.Printf("%s\n", data)
return nil
}
//Out method for Go templates
func (t StdoutTemplate) Out() error {
tmpl, err := template.New("image").Parse(t.Template)
if err != nil {
return errors.Wrapf(err, "template parsing error")
}
err = tmpl.Execute(os.Stdout, t.Output)
if err != nil {
return err
}
fmt.Println()
return nil
}
// Out method for YAML
func (y YAMLStruct) Out() error {
var buf []byte
var err error
buf, err = yaml.Marshal(y.Output)
if err != nil {
return err
}
fmt.Println(string(buf))
return nil
}

View File

@ -1,78 +0,0 @@
package formats
import (
"bytes"
"encoding/json"
"strings"
"text/template"
)
// basicFunctions are the set of initial
// functions provided to every template.
var basicFunctions = template.FuncMap{
"json": func(v interface{}) string {
buf := &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
_ = enc.Encode(v)
// Remove the trailing new line added by the encoder
return strings.TrimSpace(buf.String())
},
"split": strings.Split,
"join": strings.Join,
"title": strings.Title,
"lower": strings.ToLower,
"upper": strings.ToUpper,
"pad": padWithSpace,
"truncate": truncateWithLength,
}
// HeaderFunctions are used to created headers of a table.
// This is a replacement of basicFunctions for header generation
// because we want the header to remain intact.
// Some functions like `split` are irrelevant so not added.
var headerFunctions = template.FuncMap{
"json": func(v string) string {
return v
},
"title": func(v string) string {
return v
},
"lower": func(v string) string {
return v
},
"upper": func(v string) string {
return v
},
"truncate": func(v string, l int) string {
return v
},
}
// Parse creates a new anonymous template with the basic functions
// and parses the given format.
func Parse(format string) (*template.Template, error) {
return NewParse("", format)
}
// NewParse creates a new tagged template with the basic functions
// and parses the given format.
func NewParse(tag, format string) (*template.Template, error) {
return template.New(tag).Funcs(basicFunctions).Parse(format)
}
// padWithSpace adds whitespace to the input if the input is non-empty
func padWithSpace(source string, prefix, suffix int) string {
if source == "" {
return source
}
return strings.Repeat(" ", prefix) + source + strings.Repeat(" ", suffix)
}
// truncateWithLength truncates the source string up to the length provided by the input
func truncateWithLength(source string, length int) string {
if len(source) < length {
return source
}
return source[:length]
}

View File

@ -1,243 +0,0 @@
package main
import (
"reflect"
"strconv"
"strings"
"time"
"github.com/containers/image/types"
units "github.com/docker/go-units"
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
const (
createdByTruncLength = 45
idTruncLength = 13
)
// historyTemplateParams stores info about each layer
type historyTemplateParams struct {
ID string
Created string
CreatedBy string
Size string
Comment string
}
// historyJSONParams is only used when the JSON format is specified,
// and is better for data processing from JSON.
// historyJSONParams will be populated by data from v1.History and types.BlobInfo,
// the members of the struct are the sama data types as their sources.
type historyJSONParams struct {
ID string `json:"id"`
Created *time.Time `json:"created"`
CreatedBy string `json:"createdBy"`
Size int64 `json:"size"`
Comment string `json:"comment"`
}
// historyOptions stores cli flag values
type historyOptions struct {
human bool
noTrunc bool
quiet bool
format string
}
var (
historyFlags = []cli.Flag{
cli.BoolTFlag{
Name: "human, H",
Usage: "Display sizes and dates in human readable format",
},
cli.BoolFlag{
Name: "no-trunc, notruncate",
Usage: "Do not truncate the output",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "Display the numeric IDs only",
},
cli.StringFlag{
Name: "format",
Usage: "Change the output to JSON or a Go template",
},
}
historyDescription = "Displays the history of an image. The information can be printed out in an easy to read, " +
"or user specified format, and can be truncated."
historyCommand = cli.Command{
Name: "history",
Usage: "Show history of a specified image",
Description: historyDescription,
Flags: historyFlags,
Action: historyCmd,
ArgsUsage: "",
}
)
func historyCmd(c *cli.Context) error {
if err := validateFlags(c, historyFlags); err != nil {
return err
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "Could not get config")
}
defer runtime.Shutdown(false)
format := genHistoryFormat(c.Bool("quiet"))
if c.IsSet("format") {
format = c.String("format")
}
args := c.Args()
if len(args) == 0 {
return errors.Errorf("an image name must be specified")
}
if len(args) > 1 {
return errors.Errorf("Kpod history takes at most 1 argument")
}
imgName := args[0]
opts := historyOptions{
human: c.BoolT("human"),
noTrunc: c.Bool("no-trunc"),
quiet: c.Bool("quiet"),
format: format,
}
history, layers, imageID, err := runtime.GetHistory(imgName)
if err != nil {
return errors.Wrapf(err, "error getting history of image %q", imgName)
}
return generateHistoryOutput(history, layers, imageID, opts)
}
func genHistoryFormat(quiet bool) (format string) {
if quiet {
return formats.IDString
}
return "table {{.ID}}\t{{.Created}}\t{{.CreatedBy}}\t{{.Size}}\t{{.Comment}}\t"
}
// historyToGeneric makes an empty array of interfaces for output
func historyToGeneric(templParams []historyTemplateParams, JSONParams []historyJSONParams) (genericParams []interface{}) {
if len(templParams) > 0 {
for _, v := range templParams {
genericParams = append(genericParams, interface{}(v))
}
return
}
for _, v := range JSONParams {
genericParams = append(genericParams, interface{}(v))
}
return
}
// generate the header based on the template provided
func (h *historyTemplateParams) headerMap() map[string]string {
v := reflect.Indirect(reflect.ValueOf(h))
values := make(map[string]string)
for h := 0; h < v.NumField(); h++ {
key := v.Type().Field(h).Name
value := key
values[key] = strings.ToUpper(splitCamelCase(value))
}
return values
}
// getHistorytemplateOutput gets the modified history information to be printed in human readable format
func getHistoryTemplateOutput(history []v1.History, layers []types.BlobInfo, imageID string, opts historyOptions) (historyOutput []historyTemplateParams) {
var (
outputSize string
createdTime string
createdBy string
count = 1
)
for i := len(history) - 1; i >= 0; i-- {
if i != len(history)-1 {
imageID = "<missing>"
}
if !opts.noTrunc && i == len(history)-1 {
imageID = imageID[:idTruncLength]
}
var size int64
if !history[i].EmptyLayer {
size = layers[len(layers)-count].Size
count++
}
if opts.human {
createdTime = units.HumanDuration(time.Since((*history[i].Created))) + " ago"
outputSize = units.HumanSize(float64(size))
} else {
createdTime = (history[i].Created).Format(time.RFC3339)
outputSize = strconv.FormatInt(size, 10)
}
createdBy = strings.Join(strings.Fields(history[i].CreatedBy), " ")
if !opts.noTrunc && len(createdBy) > createdByTruncLength {
createdBy = createdBy[:createdByTruncLength-3] + "..."
}
params := historyTemplateParams{
ID: imageID,
Created: createdTime,
CreatedBy: createdBy,
Size: outputSize,
Comment: history[i].Comment,
}
historyOutput = append(historyOutput, params)
}
return
}
// getHistoryJSONOutput returns the history information in its raw form
func getHistoryJSONOutput(history []v1.History, layers []types.BlobInfo, imageID string) (historyOutput []historyJSONParams) {
count := 1
for i := len(history) - 1; i >= 0; i-- {
var size int64
if !history[i].EmptyLayer {
size = layers[len(layers)-count].Size
count++
}
params := historyJSONParams{
ID: imageID,
Created: history[i].Created,
CreatedBy: history[i].CreatedBy,
Size: size,
Comment: history[i].Comment,
}
historyOutput = append(historyOutput, params)
}
return
}
// generateHistoryOutput generates the history based on the format given
func generateHistoryOutput(history []v1.History, layers []types.BlobInfo, imageID string, opts historyOptions) error {
if len(history) == 0 {
return nil
}
var out formats.Writer
switch opts.format {
case formats.JSONString:
historyOutput := getHistoryJSONOutput(history, layers, imageID)
out = formats.JSONStructArray{Output: historyToGeneric([]historyTemplateParams{}, historyOutput)}
default:
historyOutput := getHistoryTemplateOutput(history, layers, imageID, opts)
out = formats.StdoutTemplateArray{Output: historyToGeneric(historyOutput, []historyJSONParams{}), Template: opts.format, Fields: historyOutput[0].headerMap()}
}
return formats.Writer(out).Out()
}

View File

@ -1,330 +0,0 @@
package main
import (
"fmt"
"reflect"
"strings"
"time"
"github.com/containers/image/types"
"github.com/containers/storage"
"github.com/docker/go-units"
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/kubernetes-incubator/cri-o/libpod/common"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
type imagesTemplateParams struct {
ID string
Name string
Digest digest.Digest
CreatedAt string
Size string
}
type imagesJSONParams struct {
ID string `json:"id"`
Name []string `json:"names"`
Digest digest.Digest `json:"digest"`
CreatedAt time.Time `json:"created"`
Size int64 `json:"size"`
}
type imagesOptions struct {
quiet bool
noHeading bool
noTrunc bool
digests bool
format string
}
var (
imagesFlags = []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "display only image IDs",
},
cli.BoolFlag{
Name: "noheading, n",
Usage: "do not print column headings",
},
cli.BoolFlag{
Name: "no-trunc, notruncate",
Usage: "do not truncate output",
},
cli.BoolFlag{
Name: "digests",
Usage: "show digests",
},
cli.StringFlag{
Name: "format",
Usage: "Change the output format to JSON or a Go template",
},
cli.StringFlag{
Name: "filter, f",
Usage: "filter output based on conditions provided (default [])",
},
}
imagesDescription = "lists locally stored images."
imagesCommand = cli.Command{
Name: "images",
Usage: "list images in local storage",
Description: imagesDescription,
Flags: imagesFlags,
Action: imagesCmd,
ArgsUsage: "",
}
)
func imagesCmd(c *cli.Context) error {
if err := validateFlags(c, imagesFlags); err != nil {
return err
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "Could not get runtime")
}
defer runtime.Shutdown(false)
var format string
if c.IsSet("format") {
format = c.String("format")
} else {
format = genImagesFormat(c.Bool("quiet"), c.Bool("noheading"), c.Bool("digests"))
}
opts := imagesOptions{
quiet: c.Bool("quiet"),
noHeading: c.Bool("noheading"),
noTrunc: c.Bool("no-trunc"),
digests: c.Bool("digests"),
format: format,
}
var imageInput string
if len(c.Args()) == 1 {
imageInput = c.Args().Get(0)
}
if len(c.Args()) > 1 {
return errors.New("'kpod images' requires at most 1 argument")
}
params, err := runtime.ParseImageFilter(imageInput, c.String("filter"))
if err != nil {
return errors.Wrapf(err, "error parsing filter")
}
// generate the different filters
labelFilter := generateImagesFilter(params, "label")
beforeImageFilter := generateImagesFilter(params, "before-image")
sinceImageFilter := generateImagesFilter(params, "since-image")
danglingFilter := generateImagesFilter(params, "dangling")
referenceFilter := generateImagesFilter(params, "reference")
imageInputFilter := generateImagesFilter(params, "image-input")
images, err := runtime.GetImages(params, labelFilter, beforeImageFilter, sinceImageFilter, danglingFilter, referenceFilter, imageInputFilter)
if err != nil {
return errors.Wrapf(err, "could not get list of images matching filter")
}
return generateImagesOutput(runtime, images, opts)
}
func genImagesFormat(quiet, noHeading, digests bool) (format string) {
if quiet {
return formats.IDString
}
format = "table {{.ID}}\t{{.Name}}\t"
if noHeading {
format = "{{.ID}}\t{{.Name}}\t"
}
if digests {
format += "{{.Digest}}\t"
}
format += "{{.CreatedAt}}\t{{.Size}}\t"
return
}
// imagesToGeneric creates an empty array of interfaces for output
func imagesToGeneric(templParams []imagesTemplateParams, JSONParams []imagesJSONParams) (genericParams []interface{}) {
if len(templParams) > 0 {
for _, v := range templParams {
genericParams = append(genericParams, interface{}(v))
}
return
}
for _, v := range JSONParams {
genericParams = append(genericParams, interface{}(v))
}
return
}
// generate the header based on the template provided
func (i *imagesTemplateParams) headerMap() map[string]string {
v := reflect.Indirect(reflect.ValueOf(i))
values := make(map[string]string)
for i := 0; i < v.NumField(); i++ {
key := v.Type().Field(i).Name
value := key
if value == "ID" || value == "Name" {
value = "Image" + value
}
values[key] = strings.ToUpper(splitCamelCase(value))
}
return values
}
// getImagesTemplateOutput returns the images information to be printed in human readable format
func getImagesTemplateOutput(runtime *libpod.Runtime, images []*storage.Image, opts imagesOptions) (imagesOutput []imagesTemplateParams) {
var (
lastID string
)
for _, img := range images {
if opts.quiet && lastID == img.ID {
continue // quiet should not show the same ID multiple times
}
createdTime := img.Created
imageID := img.ID
if !opts.noTrunc {
imageID = imageID[:idTruncLength]
}
imageName := "<none>"
if len(img.Names) > 0 {
imageName = img.Names[0]
}
info, imageDigest, size, _ := runtime.InfoAndDigestAndSize(*img)
if info != nil {
createdTime = info.Created
}
params := imagesTemplateParams{
ID: imageID,
Name: imageName,
Digest: imageDigest,
CreatedAt: units.HumanDuration(time.Since((createdTime))) + " ago",
Size: units.HumanSize(float64(size)),
}
imagesOutput = append(imagesOutput, params)
}
return
}
// getImagesJSONOutput returns the images information in its raw form
func getImagesJSONOutput(runtime *libpod.Runtime, images []*storage.Image) (imagesOutput []imagesJSONParams) {
for _, img := range images {
createdTime := img.Created
info, imageDigest, size, _ := runtime.InfoAndDigestAndSize(*img)
if info != nil {
createdTime = info.Created
}
params := imagesJSONParams{
ID: img.ID,
Name: img.Names,
Digest: imageDigest,
CreatedAt: createdTime,
Size: size,
}
imagesOutput = append(imagesOutput, params)
}
return
}
// generateImagesOutput generates the images based on the format provided
func generateImagesOutput(runtime *libpod.Runtime, images []*storage.Image, opts imagesOptions) error {
if len(images) == 0 {
return nil
}
var out formats.Writer
switch opts.format {
case formats.JSONString:
imagesOutput := getImagesJSONOutput(runtime, images)
out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)}
default:
imagesOutput := getImagesTemplateOutput(runtime, images, opts)
out = formats.StdoutTemplateArray{Output: imagesToGeneric(imagesOutput, []imagesJSONParams{}), Template: opts.format, Fields: imagesOutput[0].headerMap()}
}
return formats.Writer(out).Out()
}
// generateImagesFilter returns an ImageFilter based on filterType
// to add more filters, define a new case and write what the ImageFilter function should do
func generateImagesFilter(params *libpod.ImageFilterParams, filterType string) libpod.ImageFilter {
switch filterType {
case "label":
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
if params == nil || params.Label == "" {
return true
}
pair := strings.SplitN(params.Label, "=", 2)
if val, ok := info.Labels[pair[0]]; ok {
if len(pair) == 2 && val == pair[1] {
return true
}
if len(pair) == 1 {
return true
}
}
return false
}
case "before-image":
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
if params == nil || params.BeforeImage.IsZero() {
return true
}
return info.Created.Before(params.BeforeImage)
}
case "since-image":
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
if params == nil || params.SinceImage.IsZero() {
return true
}
return info.Created.After(params.SinceImage)
}
case "dangling":
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
if params == nil || params.Dangling == "" {
return true
}
if common.IsFalse(params.Dangling) && params.ImageName != "<none>" {
return true
}
if common.IsTrue(params.Dangling) && params.ImageName == "<none>" {
return true
}
return false
}
case "reference":
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
if params == nil || params.ReferencePattern == "" {
return true
}
return libpod.MatchesReference(params.ImageName, params.ReferencePattern)
}
case "image-input":
return func(image *storage.Image, info *types.ImageInspectInfo) bool {
if params == nil || params.ImageInput == "" {
return true
}
return libpod.MatchesReference(params.ImageName, params.ImageInput)
}
default:
fmt.Println("invalid filter type", filterType)
return nil
}
}

View File

@ -1,200 +0,0 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"runtime"
"github.com/docker/docker/pkg/system"
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
infoDescription = "display system information"
infoCommand = cli.Command{
Name: "info",
Usage: infoDescription,
Description: `Information display here pertain to the host, current storage stats, and build of kpod. Useful for the user and when reporting issues.`,
Flags: infoFlags,
Action: infoCmd,
ArgsUsage: "",
}
infoFlags = []cli.Flag{
cli.BoolFlag{
Name: "debug, D",
Usage: "display additional debug information",
},
cli.StringFlag{
Name: "format",
Usage: "Change the output format to JSON or a Go template",
},
}
)
func infoCmd(c *cli.Context) error {
if err := validateFlags(c, infoFlags); err != nil {
return err
}
info := map[string]interface{}{}
infoGivers := []infoGiverFunc{
storeInfo,
hostInfo,
}
if c.Bool("debug") {
infoGivers = append(infoGivers, debugInfo)
}
for _, giver := range infoGivers {
thisName, thisInfo, err := giver(c)
if err != nil {
info[thisName] = infoErr(err)
continue
}
info[thisName] = thisInfo
}
var out formats.Writer
infoOutputFormat := c.String("format")
switch infoOutputFormat {
case formats.JSONString:
out = formats.JSONStruct{Output: info}
case "":
out = formats.YAMLStruct{Output: info}
default:
out = formats.StdoutTemplate{Output: info, Template: infoOutputFormat}
}
formats.Writer(out).Out()
return nil
}
func infoErr(err error) map[string]interface{} {
return map[string]interface{}{
"error": err.Error(),
}
}
type infoGiverFunc func(c *cli.Context) (name string, info map[string]interface{}, err error)
// top-level "debug" info
func debugInfo(c *cli.Context) (string, map[string]interface{}, error) {
info := map[string]interface{}{}
info["compiler"] = runtime.Compiler
info["go version"] = runtime.Version()
info["kpod version"] = c.App.Version
info["git commit"] = gitCommit
return "debug", info, nil
}
// top-level "host" info
func hostInfo(c *cli.Context) (string, map[string]interface{}, error) {
// lets say OS, arch, number of cpus, amount of memory, maybe os distribution/version, hostname, kernel version, uptime
info := map[string]interface{}{}
info["os"] = runtime.GOOS
info["arch"] = runtime.GOARCH
info["cpus"] = runtime.NumCPU()
mi, err := system.ReadMemInfo()
if err != nil {
info["meminfo"] = infoErr(err)
} else {
// TODO this might be a place for github.com/dustin/go-humanize
info["MemTotal"] = mi.MemTotal
info["MemFree"] = mi.MemFree
info["SwapTotal"] = mi.SwapTotal
info["SwapFree"] = mi.SwapFree
}
if kv, err := readKernelVersion(); err != nil {
info["kernel"] = infoErr(err)
} else {
info["kernel"] = kv
}
if up, err := readUptime(); err != nil {
info["uptime"] = infoErr(err)
} else {
info["uptime"] = up
}
if host, err := os.Hostname(); err != nil {
info["hostname"] = infoErr(err)
} else {
info["hostname"] = host
}
return "host", info, nil
}
// top-level "store" info
func storeInfo(c *cli.Context) (string, map[string]interface{}, error) {
storeStr := "store"
config, err := getConfig(c)
if err != nil {
return storeStr, nil, errors.Wrapf(err, "Could not get config")
}
store, err := getStore(config)
if err != nil {
return storeStr, nil, err
}
// lets say storage driver in use, number of images, number of containers
info := map[string]interface{}{}
info["GraphRoot"] = store.GraphRoot()
info["RunRoot"] = store.RunRoot()
info["GraphDriverName"] = store.GraphDriverName()
info["GraphOptions"] = store.GraphOptions()
statusPairs, err := store.Status()
if err != nil {
return storeStr, nil, err
}
status := map[string]string{}
for _, pair := range statusPairs {
status[pair[0]] = pair[1]
}
info["GraphStatus"] = status
images, err := store.Images()
if err != nil {
info["ImageStore"] = infoErr(err)
} else {
info["ImageStore"] = map[string]interface{}{
"number": len(images),
}
}
containers, err := store.Containers()
if err != nil {
info["ContainerStore"] = infoErr(err)
} else {
info["ContainerStore"] = map[string]interface{}{
"number": len(containers),
}
}
return storeStr, info, nil
}
func readKernelVersion() (string, error) {
buf, err := ioutil.ReadFile("/proc/version")
if err != nil {
return "", err
}
f := bytes.Fields(buf)
if len(f) < 2 {
return string(bytes.TrimSpace(buf)), nil
}
return string(f[2]), nil
}
func readUptime() (string, error) {
buf, err := ioutil.ReadFile("/proc/uptime")
if err != nil {
return "", err
}
f := bytes.Fields(buf)
if len(f) < 1 {
return "", fmt.Errorf("invalid uptime")
}
return string(f[0]), nil
}

View File

@ -1,120 +0,0 @@
package main
import (
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/kubernetes-incubator/cri-o/libpod/images"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
const (
inspectTypeContainer = "container"
inspectTypeImage = "image"
inspectAll = "all"
)
var (
inspectFlags = []cli.Flag{
cli.StringFlag{
Name: "type, t",
Value: inspectAll,
Usage: "Return JSON for specified type, (e.g image, container or task)",
},
cli.StringFlag{
Name: "format, f",
Usage: "Change the output format to a Go template",
},
cli.BoolFlag{
Name: "size",
Usage: "Display total file size if the type is container",
},
}
inspectDescription = "This displays the low-level information on containers and images identified by name or ID. By default, this will render all results in a JSON array. If the container and image have the same name, this will return container JSON for unspecified type."
inspectCommand = cli.Command{
Name: "inspect",
Usage: "Displays the configuration of a container or image",
Description: inspectDescription,
Flags: inspectFlags,
Action: inspectCmd,
ArgsUsage: "CONTAINER-OR-IMAGE",
}
)
func inspectCmd(c *cli.Context) error {
args := c.Args()
if len(args) == 0 {
return errors.Errorf("container or image name must be specified: kpod inspect [options [...]] name")
}
if len(args) > 1 {
return errors.Errorf("too many arguments specified")
}
if err := validateFlags(c, inspectFlags); err != nil {
return err
}
itemType := c.String("type")
size := c.Bool("size")
switch itemType {
case inspectTypeContainer:
case inspectTypeImage:
case inspectAll:
default:
return errors.Errorf("the only recognized types are %q, %q, and %q", inspectTypeContainer, inspectTypeImage, inspectAll)
}
name := args[0]
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "Could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
defer server.Shutdown()
if err = server.Update(); err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
outputFormat := c.String("format")
var data interface{}
switch itemType {
case inspectTypeContainer:
data, err = server.GetContainerData(name, size)
if err != nil {
return errors.Wrapf(err, "error parsing container data")
}
case inspectTypeImage:
data, err = images.GetData(server.Store(), name)
if err != nil {
return errors.Wrapf(err, "error parsing image data")
}
case inspectAll:
ctrData, err := server.GetContainerData(name, size)
if err != nil {
imgData, err := images.GetData(server.Store(), name)
if err != nil {
return errors.Wrapf(err, "error parsing container or image data")
}
data = imgData
} else {
data = ctrData
}
}
var out formats.Writer
if outputFormat != "" && outputFormat != formats.JSONString {
//template
out = formats.StdoutTemplate{Output: data, Template: outputFormat}
} else {
// default is json output
out = formats.JSONStruct{Output: data}
}
formats.Writer(out).Out()
return nil
}

View File

@ -1,74 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/docker/docker/pkg/signal"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
killFlags = []cli.Flag{
cli.StringFlag{
Name: "signal, s",
Usage: "Signal to send to the container",
Value: "KILL",
},
}
killDescription = "The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal."
killCommand = cli.Command{
Name: "kill",
Usage: "Kill one or more running containers with a specific signal",
Description: killDescription,
Flags: killFlags,
Action: killCmd,
ArgsUsage: "[CONTAINER_NAME_OR_ID]",
}
)
// killCmd kills one or more containers with a signal
func killCmd(c *cli.Context) error {
args := c.Args()
if len(args) == 0 {
return errors.Errorf("specify one or more containers to kill")
}
if err := validateFlags(c, killFlags); err != nil {
return err
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
killSignal := c.String("signal")
// Check if the signalString provided by the user is valid
// Invalid signals will return err
sysSignal, err := signal.ParseSignal(killSignal)
if err != nil {
return err
}
defer server.Shutdown()
err = server.Update()
if err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
var lastError error
for _, container := range c.Args() {
id, err := server.ContainerKill(container, sysSignal)
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "unable to kill %v", container)
} else {
fmt.Println(id)
}
}
return lastError
}

View File

@ -1,116 +0,0 @@
package main
import (
"io"
"io/ioutil"
"os"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
loadFlags = []cli.Flag{
cli.StringFlag{
Name: "input, i",
Usage: "Read from archive file, default is STDIN",
Value: "/dev/stdin",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "Suppress the output",
},
cli.StringFlag{
Name: "signature-policy",
Usage: "`pathname` of signature policy file (not usually used)",
},
}
loadDescription = "Loads the image from docker-archive stored on the local machine."
loadCommand = cli.Command{
Name: "load",
Usage: "load an image from docker archive",
Description: loadDescription,
Flags: loadFlags,
Action: loadCmd,
ArgsUsage: "",
}
)
// loadCmd gets the image/file to be loaded from the command line
// and calls loadImage to load the image to containers-storage
func loadCmd(c *cli.Context) error {
args := c.Args()
var image string
if len(args) == 1 {
image = args[0]
}
if len(args) > 1 {
return errors.New("too many arguments. Requires exactly 1")
}
if err := validateFlags(c, loadFlags); err != nil {
return err
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
input := c.String("input")
if input == "/dev/stdin" {
fi, err := os.Stdin.Stat()
if err != nil {
return err
}
// checking if loading from pipe
if !fi.Mode().IsRegular() {
outFile, err := ioutil.TempFile("/var/tmp", "kpod")
if err != nil {
return errors.Errorf("error creating file %v", err)
}
defer outFile.Close()
defer os.Remove(outFile.Name())
inFile, err := os.OpenFile(input, 0, 0666)
if err != nil {
return errors.Errorf("error reading file %v", err)
}
defer inFile.Close()
_, err = io.Copy(outFile, inFile)
if err != nil {
return errors.Errorf("error copying file %v", err)
}
input = outFile.Name()
}
}
var writer io.Writer
if !c.Bool("quiet") {
writer = os.Stdout
}
options := libpod.CopyOptions{
SignaturePolicyPath: c.String("signature-policy"),
Writer: writer,
}
src := libpod.DockerArchive + ":" + input
if err := runtime.PullImage(src, options); err != nil {
src = libpod.OCIArchive + ":" + input
// generate full src name with specified image:tag
if image != "" {
src = src + ":" + image
}
if err := runtime.PullImage(src, options); err != nil {
return errors.Wrapf(err, "error pulling %q", src)
}
}
return nil
}

View File

@ -1,110 +0,0 @@
package main
import (
"bufio"
"context"
"fmt"
"os"
"strings"
"github.com/containers/image/docker"
"github.com/containers/image/pkg/docker/config"
"github.com/kubernetes-incubator/cri-o/libpod/common"
"github.com/pkg/errors"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
)
var (
loginFlags = []cli.Flag{
cli.StringFlag{
Name: "password, p",
Usage: "Password for registry",
},
cli.StringFlag{
Name: "username, u",
Usage: "Username for registry",
},
cli.StringFlag{
Name: "authfile",
Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
},
}
loginDescription = "Login to a container registry on a specified server."
loginCommand = cli.Command{
Name: "login",
Usage: "login to a container registry",
Description: loginDescription,
Flags: loginFlags,
Action: loginCmd,
ArgsUsage: "REGISTRY",
}
)
// loginCmd uses the authentication package to store a user's authenticated credentials
// in an auth.json file for future use
func loginCmd(c *cli.Context) error {
args := c.Args()
if len(args) > 1 {
return errors.Errorf("too many arguments, login takes only 1 argument")
}
if len(args) == 0 {
return errors.Errorf("registry must be given")
}
var server string
if len(args) == 1 {
server = args[0]
}
sc := common.GetSystemContext("", c.String("authfile"))
// username of user logged in to server (if one exists)
userFromAuthFile := config.GetUserLoggedIn(sc, server)
username, password, err := getUserAndPass(c.String("username"), c.String("password"), userFromAuthFile)
if err != nil {
return errors.Wrapf(err, "error getting username and password")
}
if err = docker.CheckAuth(context.TODO(), sc, username, password, server); err == nil {
if err := config.SetAuthentication(sc, server, username, password); err != nil {
return err
}
}
switch err {
case nil:
fmt.Println("Login Succeeded!")
return nil
case docker.ErrUnauthorizedForCredentials:
return errors.Errorf("error logging into %q: invalid username/password\n", server)
default:
return errors.Wrapf(err, "error authenticating creds for %q", server)
}
}
// getUserAndPass gets the username and password from STDIN if not given
// using the -u and -p flags
func getUserAndPass(username, password, userFromAuthFile string) (string, string, error) {
var err error
reader := bufio.NewReader(os.Stdin)
if username == "" {
if userFromAuthFile != "" {
fmt.Printf("Username (%s): ", userFromAuthFile)
} else {
fmt.Print("Username: ")
}
username, err = reader.ReadString('\n')
if err != nil {
return "", "", errors.Wrapf(err, "error reading username")
}
}
if password == "" {
fmt.Print("Password: ")
pass, err := terminal.ReadPassword(0)
if err != nil {
return "", "", errors.Wrapf(err, "error reading password")
}
password = string(pass)
fmt.Println()
}
return strings.TrimSpace(username), password, err
}

View File

@ -1,69 +0,0 @@
package main
import (
"fmt"
"github.com/containers/image/pkg/docker/config"
"github.com/kubernetes-incubator/cri-o/libpod/common"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
logoutFlags = []cli.Flag{
cli.StringFlag{
Name: "authfile",
Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
},
cli.BoolFlag{
Name: "all, a",
Usage: "Remove the cached credentials for all registries in the auth file",
},
}
logoutDescription = "Remove the cached username and password for the registry."
logoutCommand = cli.Command{
Name: "logout",
Usage: "logout of a container registry",
Description: logoutDescription,
Flags: logoutFlags,
Action: logoutCmd,
ArgsUsage: "REGISTRY",
}
)
// logoutCmd uses the authentication package to remove the authenticated of a registry
// stored in the auth.json file
func logoutCmd(c *cli.Context) error {
args := c.Args()
if len(args) > 1 {
return errors.Errorf("too many arguments, logout takes only 1 argument")
}
if len(args) == 0 {
return errors.Errorf("registry must be given")
}
var server string
if len(args) == 1 {
server = args[0]
}
sc := common.GetSystemContext("", c.String("authfile"))
if c.Bool("all") {
if err := config.RemoveAllAuthentication(sc); err != nil {
return err
}
fmt.Println("Remove login credentials for all registries")
return nil
}
err := config.RemoveAuthentication(sc, server)
switch err {
case nil:
fmt.Printf("Remove login credentials for %s\n", server)
return nil
case config.ErrNotLoggedIn:
return errors.Errorf("Not logged into %s\n", server)
default:
return errors.Wrapf(err, "error logging out of %q", server)
}
}

View File

@ -1,92 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
logsFlags = []cli.Flag{
cli.BoolFlag{
Name: "details",
Usage: "Show extra details provided to the logs",
Hidden: true,
},
cli.BoolFlag{
Name: "follow, f",
Usage: "Follow log output. The default is false",
},
cli.StringFlag{
Name: "since",
Usage: "Show logs since TIMESTAMP",
},
cli.Uint64Flag{
Name: "tail",
Usage: "Output the specified number of LINES at the end of the logs. Defaults to 0, which prints all lines",
},
}
logsDescription = "The kpod logs command batch-retrieves whatever logs are present for a container at the time of execution. This does not guarantee execution" +
"order when combined with kpod run (i.e. your run may not have generated any logs at the time you execute kpod logs"
logsCommand = cli.Command{
Name: "logs",
Usage: "Fetch the logs of a container",
Description: logsDescription,
Flags: logsFlags,
Action: logsCmd,
ArgsUsage: "CONTAINER",
}
)
func logsCmd(c *cli.Context) error {
args := c.Args()
if len(args) != 1 {
return errors.Errorf("'kpod logs' requires exactly one container name/ID")
}
if err := validateFlags(c, logsFlags); err != nil {
return err
}
container := c.Args().First()
var opts libkpod.LogOptions
opts.Details = c.Bool("details")
opts.Follow = c.Bool("follow")
opts.SinceTime = time.Time{}
if c.IsSet("since") {
// parse time, error out if something is wrong
since, err := time.Parse("2006-01-02T15:04:05.999999999-07:00", c.String("since"))
if err != nil {
return errors.Wrapf(err, "could not parse time: %q", c.String("since"))
}
opts.SinceTime = since
}
opts.Tail = c.Uint64("tail")
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not create container server")
}
defer server.Shutdown()
err = server.Update()
if err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
logs := make(chan string)
go func() {
err = server.GetLogs(container, logs, opts)
}()
printLogs(logs)
return err
}
func printLogs(logs chan string) {
for line := range logs {
fmt.Println(line)
}
}

View File

@ -1,135 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/containers/storage/pkg/reexec"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
// This is populated by the Makefile from the VERSION file
// in the repository
var kpodVersion = ""
func main() {
debug := false
if reexec.Init() {
return
}
app := cli.NewApp()
app.Name = "kpod"
app.Usage = "manage pods and images"
var v string
if kpodVersion != "" {
v = kpodVersion
}
app.Version = v
app.Commands = []cli.Command{
createCommand,
diffCommand,
exportCommand,
historyCommand,
imagesCommand,
infoCommand,
inspectCommand,
killCommand,
loadCommand,
loginCommand,
logoutCommand,
logsCommand,
mountCommand,
pauseCommand,
psCommand,
pullCommand,
pushCommand,
renameCommand,
rmCommand,
rmiCommand,
runCommand,
saveCommand,
statsCommand,
stopCommand,
tagCommand,
umountCommand,
unpauseCommand,
versionCommand,
waitCommand,
}
app.Before = func(c *cli.Context) error {
logLevel := c.GlobalString("log-level")
if logLevel != "" {
level, err := logrus.ParseLevel(logLevel)
if err != nil {
return err
}
logrus.SetLevel(level)
}
if logLevel == "debug" {
debug = true
}
return nil
}
app.After = func(*cli.Context) error {
// called by Run() when the command handler succeeds
shutdownStores()
return nil
}
cli.OsExiter = func(code int) {
// called by Run() when the command fails, bypassing After()
shutdownStores()
os.Exit(code)
}
app.Flags = []cli.Flag{
cli.StringFlag{
Name: "config, c",
Usage: "path of a config file detailing container server configuration options",
},
cli.StringFlag{
Name: "conmon",
Usage: "path of the conmon binary",
},
cli.StringFlag{
Name: "log-level",
Usage: "log messages above specified level: debug, info, warn, error (default), fatal or panic",
Value: "error",
},
cli.StringFlag{
Name: "root",
Usage: "path to the root directory in which data, including images, is stored",
},
cli.StringFlag{
Name: "runroot",
Usage: "path to the 'run directory' where all state information is stored",
},
cli.StringFlag{
Name: "runtime",
Usage: "path to the OCI-compatible binary used to run containers, default is /usr/bin/runc",
},
cli.StringFlag{
Name: "storage-driver, s",
Usage: "select which storage driver is used to manage storage of images and containers (default is overlay)",
},
cli.StringSliceFlag{
Name: "storage-opt",
Usage: "used to pass an option to the storage driver",
},
}
if err := app.Run(os.Args); err != nil {
if debug {
logrus.Errorf(err.Error())
} else {
fmt.Fprintln(os.Stderr, err.Error())
}
cli.OsExiter(1)
}
}

View File

@ -1,121 +0,0 @@
package main
import (
js "encoding/json"
"fmt"
of "github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
mountDescription = `
kpod mount
Lists all mounted containers mount points
kpod mount CONTAINER-NAME-OR-ID
Mounts the specified container and outputs the mountpoint
`
mountFlags = []cli.Flag{
cli.BoolFlag{
Name: "notruncate",
Usage: "do not truncate output",
},
cli.StringFlag{
Name: "label",
Usage: "SELinux label for the mount point",
},
cli.StringFlag{
Name: "format",
Usage: "Change the output format to Go template",
},
}
mountCommand = cli.Command{
Name: "mount",
Usage: "Mount a working container's root filesystem",
Description: mountDescription,
Action: mountCmd,
ArgsUsage: "[CONTAINER-NAME-OR-ID]",
Flags: mountFlags,
}
)
// MountOutputParams stores info about each layer
type jsonMountPoint struct {
ID string `json:"id"`
Names []string `json:"names"`
MountPoint string `json:"mountpoint"`
}
func mountCmd(c *cli.Context) error {
formats := map[string]bool{
"": true,
of.JSONString: true,
}
args := c.Args()
json := c.String("format") == of.JSONString
if !formats[c.String("format")] {
return errors.Errorf("%q is not a supported format", c.String("format"))
}
if len(args) > 1 {
return errors.Errorf("too many arguments specified")
}
if err := validateFlags(c, mountFlags); err != nil {
return err
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "Could not get config")
}
store, err := getStore(config)
if err != nil {
return errors.Wrapf(err, "error getting store")
}
if len(args) == 1 {
if json {
return errors.Wrapf(err, "json option can not be used with a container id")
}
mountPoint, err := store.Mount(args[0], c.String("label"))
if err != nil {
return errors.Wrapf(err, "error finding container %q", args[0])
}
fmt.Printf("%s\n", mountPoint)
} else {
jsonMountPoints := []jsonMountPoint{}
containers, err2 := store.Containers()
if err2 != nil {
return errors.Wrapf(err2, "error reading list of all containers")
}
for _, container := range containers {
layer, err := store.Layer(container.LayerID)
if err != nil {
return errors.Wrapf(err, "error finding layer %q for container %q", container.LayerID, container.ID)
}
if layer.MountPoint == "" {
continue
}
if json {
jsonMountPoints = append(jsonMountPoints, jsonMountPoint{ID: container.ID, Names: container.Names, MountPoint: layer.MountPoint})
continue
}
if c.Bool("notruncate") {
fmt.Printf("%-64s %s\n", container.ID, layer.MountPoint)
} else {
fmt.Printf("%-12.12s %s\n", container.ID, layer.MountPoint)
}
}
if json {
data, err := js.MarshalIndent(jsonMountPoints, "", " ")
if err != nil {
return err
}
fmt.Printf("%s\n", data)
}
}
return nil
}

View File

@ -1,886 +0,0 @@
//nolint
// most of these validate and parse functions have been taken from projectatomic/docker
// and modified for cri-o
package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"os"
"os/user"
"path"
"regexp"
"strconv"
"strings"
units "github.com/docker/go-units"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
)
// Note: for flags that are in the form <number><unit>, use the RAMInBytes function
// from the units package in docker/go-units/size.go
var (
whiteSpaces = " \t"
alphaRegexp = regexp.MustCompile(`[a-zA-Z]`)
domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
)
// validateExtraHost validates that the specified string is a valid extrahost and returns it.
// ExtraHost is in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
// for add-host flag
func validateExtraHost(val string) (string, error) { //nolint
// allow for IPv6 addresses in extra hosts by only splitting on first ":"
arr := strings.SplitN(val, ":", 2)
if len(arr) != 2 || len(arr[0]) == 0 {
return "", fmt.Errorf("bad format for add-host: %q", val)
}
if _, err := validateIPAddress(arr[1]); err != nil {
return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
}
return val, nil
}
// validateIPAddress validates an Ip address.
// for dns, ip, and ip6 flags also
func validateIPAddress(val string) (string, error) {
var ip = net.ParseIP(strings.TrimSpace(val))
if ip != nil {
return ip.String(), nil
}
return "", fmt.Errorf("%s is not an ip address", val)
}
// validateAttach validates that the specified string is a valid attach option.
// for attach flag
func validateAttach(val string) (string, error) { //nolint
s := strings.ToLower(val)
for _, str := range []string{"stdin", "stdout", "stderr"} {
if s == str {
return s, nil
}
}
return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR")
}
// validate the blkioWeight falls in the range of 10 to 1000
// for blkio-weight flag
func validateBlkioWeight(val int64) (int64, error) { //nolint
if val >= 10 && val <= 1000 {
return val, nil
}
return -1, errors.Errorf("invalid blkio weight %q, should be between 10 and 1000", val)
}
// weightDevice is a structure that holds device:weight pair
type weightDevice struct {
path string
weight uint16
}
func (w *weightDevice) String() string {
return fmt.Sprintf("%s:%d", w.path, w.weight)
}
// validateweightDevice validates that the specified string has a valid device-weight format
// for blkio-weight-device flag
func validateweightDevice(val string) (*weightDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
weight, err := strconv.ParseUint(split[1], 10, 0)
if err != nil {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
if weight > 0 && (weight < 10 || weight > 1000) {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
return &weightDevice{
path: split[0],
weight: uint16(weight),
}, nil
}
// parseDevice parses a device mapping string to a container.DeviceMapping struct
// for device flag
func parseDevice(device string) (*pb.Device, error) { //nolint
_, err := validateDevice(device)
if err != nil {
return nil, errors.Wrapf(err, "device string not valid %q", device)
}
src := ""
dst := ""
permissions := "rwm"
arr := strings.Split(device, ":")
switch len(arr) {
case 3:
permissions = arr[2]
fallthrough
case 2:
if validDeviceMode(arr[1]) {
permissions = arr[1]
} else {
dst = arr[1]
}
fallthrough
case 1:
src = arr[0]
default:
return nil, fmt.Errorf("invalid device specification: %s", device)
}
if dst == "" {
dst = src
}
deviceMapping := &pb.Device{
ContainerPath: dst,
HostPath: src,
Permissions: permissions,
}
return deviceMapping, nil
}
// validDeviceMode checks if the mode for device is valid or not.
// Valid mode is a composition of r (read), w (write), and m (mknod).
func validDeviceMode(mode string) bool {
var legalDeviceMode = map[rune]bool{
'r': true,
'w': true,
'm': true,
}
if mode == "" {
return false
}
for _, c := range mode {
if !legalDeviceMode[c] {
return false
}
legalDeviceMode[c] = false
}
return true
}
// validateDevice validates a path for devices
// It will make sure 'val' is in the form:
// [host-dir:]container-path[:mode]
// It also validates the device mode.
func validateDevice(val string) (string, error) {
return validatePath(val, validDeviceMode)
}
func validatePath(val string, validator func(string) bool) (string, error) {
var containerPath string
var mode string
if strings.Count(val, ":") > 2 {
return val, fmt.Errorf("bad format for path: %s", val)
}
split := strings.SplitN(val, ":", 3)
if split[0] == "" {
return val, fmt.Errorf("bad format for path: %s", val)
}
switch len(split) {
case 1:
containerPath = split[0]
val = path.Clean(containerPath)
case 2:
if isValid := validator(split[1]); isValid {
containerPath = split[0]
mode = split[1]
val = fmt.Sprintf("%s:%s", path.Clean(containerPath), mode)
} else {
containerPath = split[1]
val = fmt.Sprintf("%s:%s", split[0], path.Clean(containerPath))
}
case 3:
containerPath = split[1]
mode = split[2]
if isValid := validator(split[2]); !isValid {
return val, fmt.Errorf("bad mode specified: %s", mode)
}
val = fmt.Sprintf("%s:%s:%s", split[0], containerPath, mode)
}
if !path.IsAbs(containerPath) {
return val, fmt.Errorf("%s is not an absolute path", containerPath)
}
return val, nil
}
// throttleDevice is a structure that holds device:rate_per_second pair
type throttleDevice struct {
path string
rate uint64
}
func (t *throttleDevice) String() string {
return fmt.Sprintf("%s:%d", t.path, t.rate)
}
// validateBpsDevice validates that the specified string has a valid device-rate format
// for device-read-bps and device-write-bps flags
func validateBpsDevice(val string) (*throttleDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
rate, err := units.RAMInBytes(split[1])
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
}
if rate < 0 {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
}
return &throttleDevice{
path: split[0],
rate: uint64(rate),
}, nil
}
// validateIOpsDevice validates that the specified string has a valid device-rate format
// for device-write-iops and device-read-iops flags
func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
rate, err := strconv.ParseUint(split[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
}
if rate < 0 {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
}
return &throttleDevice{
path: split[0],
rate: uint64(rate),
}, nil
}
// validateDNSSearch validates domain for resolvconf search configuration.
// A zero length domain is represented by a dot (.).
// for dns-search flag
func validateDNSSearch(val string) (string, error) { //nolint
if val = strings.Trim(val, " "); val == "." {
return val, nil
}
return validateDomain(val)
}
func validateDomain(val string) (string, error) {
if alphaRegexp.FindString(val) == "" {
return "", fmt.Errorf("%s is not a valid domain", val)
}
ns := domainRegexp.FindSubmatch([]byte(val))
if len(ns) > 0 && len(ns[1]) < 255 {
return string(ns[1]), nil
}
return "", fmt.Errorf("%s is not a valid domain", val)
}
// validateEnv validates an environment variable and returns it.
// If no value is specified, it returns the current value using os.Getenv.
// for env flag
func validateEnv(val string) (string, error) { //nolint
arr := strings.Split(val, "=")
if len(arr) > 1 {
return val, nil
}
if !doesEnvExist(val) {
return val, nil
}
return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil
}
func doesEnvExist(name string) bool {
for _, entry := range os.Environ() {
parts := strings.SplitN(entry, "=", 2)
if parts[0] == name {
return true
}
}
return false
}
// reads a file of line terminated key=value pairs, and overrides any keys
// present in the file with additional pairs specified in the override parameter
// for env-file and labels-file flags
func readKVStrings(files []string, override []string) ([]string, error) {
envVariables := []string{}
for _, ef := range files {
parsedVars, err := parseEnvFile(ef)
if err != nil {
return nil, err
}
envVariables = append(envVariables, parsedVars...)
}
// parse the '-e' and '--env' after, to allow override
envVariables = append(envVariables, override...)
return envVariables, nil
}
// parseEnvFile reads a file with environment variables enumerated by lines
func parseEnvFile(filename string) ([]string, error) {
fh, err := os.Open(filename)
if err != nil {
return []string{}, err
}
defer fh.Close()
lines := []string{}
scanner := bufio.NewScanner(fh)
for scanner.Scan() {
// trim the line from all leading whitespace first
line := strings.TrimLeft(scanner.Text(), whiteSpaces)
// line is not empty, and not starting with '#'
if len(line) > 0 && !strings.HasPrefix(line, "#") {
data := strings.SplitN(line, "=", 2)
// trim the front of a variable, but nothing else
variable := strings.TrimLeft(data[0], whiteSpaces)
if strings.ContainsAny(variable, whiteSpaces) {
return []string{}, errors.Errorf("variable %q has white spaces, poorly formatted environment", variable)
}
if len(data) > 1 {
// pass the value through, no trimming
lines = append(lines, fmt.Sprintf("%s=%s", variable, data[1]))
} else {
// if only a pass-through variable is given, clean it up.
lines = append(lines, fmt.Sprintf("%s=%s", strings.TrimSpace(line), os.Getenv(line)))
}
}
}
return lines, scanner.Err()
}
// NsIpc represents the container ipc stack.
// for ipc flag
type NsIpc string
// IsPrivate indicates whether the container uses its private ipc stack.
func (n NsIpc) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
}
// IsHost indicates whether the container uses the host's ipc stack.
func (n NsIpc) IsHost() bool {
return n == "host"
}
// IsContainer indicates whether the container uses a container's ipc stack.
func (n NsIpc) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container"
}
// Valid indicates whether the ipc stack is valid.
func (n NsIpc) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
case "container":
if len(parts) != 2 || parts[1] == "" {
return false
}
default:
return false
}
return true
}
// Container returns the name of the container ipc stack is going to be used.
func (n NsIpc) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
}
// validateLabel validates that the specified string is a valid label, and returns it.
// Labels are in the form on key=value.
// for label flag
func validateLabel(val string) (string, error) { //nolint
if strings.Count(val, "=") < 1 {
return "", fmt.Errorf("bad attribute format: %s", val)
}
return val, nil
}
// validateMACAddress validates a MAC address.
// for mac-address flag
func validateMACAddress(val string) (string, error) { //nolint
_, err := net.ParseMAC(strings.TrimSpace(val))
if err != nil {
return "", err
}
return val, nil
}
// validateLink validates that the specified string has a valid link format (containerName:alias).
func validateLink(val string) (string, error) { //nolint
if _, _, err := parseLink(val); err != nil {
return val, err
}
return val, nil
}
// parseLink parses and validates the specified string as a link format (name:alias)
func parseLink(val string) (string, string, error) {
if val == "" {
return "", "", fmt.Errorf("empty string specified for links")
}
arr := strings.Split(val, ":")
if len(arr) > 2 {
return "", "", fmt.Errorf("bad format for links: %s", val)
}
if len(arr) == 1 {
return val, val, nil
}
// This is kept because we can actually get a HostConfig with links
// from an already created container and the format is not `foo:bar`
// but `/foo:/c1/bar`
if strings.HasPrefix(arr[0], "/") {
_, alias := path.Split(arr[1])
return arr[0][1:], alias, nil
}
return arr[0], arr[1], nil
}
// parseLoggingOpts validates the logDriver and logDriverOpts
// for log-opt and log-driver flags
func parseLoggingOpts(logDriver string, logDriverOpt []string) (map[string]string, error) { //nolint
logOptsMap := convertKVStringsToMap(logDriverOpt)
if logDriver == "none" && len(logDriverOpt) > 0 {
return map[string]string{}, errors.Errorf("invalid logging opts for driver %s", logDriver)
}
return logOptsMap, nil
}
// NsPid represents the pid namespace of the container.
//for pid flag
type NsPid string
// IsPrivate indicates whether the container uses its own new pid namespace.
func (n NsPid) IsPrivate() bool {
return !(n.IsHost() || n.IsContainer())
}
// IsHost indicates whether the container uses the host's pid namespace.
func (n NsPid) IsHost() bool {
return n == "host"
}
// IsContainer indicates whether the container uses a container's pid namespace.
func (n NsPid) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container"
}
// Valid indicates whether the pid namespace is valid.
func (n NsPid) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
case "container":
if len(parts) != 2 || parts[1] == "" {
return false
}
default:
return false
}
return true
}
// Container returns the name of the container whose pid namespace is going to be used.
func (n NsPid) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
}
// parsePortSpecs receives port specs in the format of ip:public:private/proto and parses
// these in to the internal types
// for publish, publish-all, and expose flags
func parsePortSpecs(ports []string) ([]*pb.PortMapping, error) { //nolint
var portMappings []*pb.PortMapping
for _, rawPort := range ports {
portMapping, err := parsePortSpec(rawPort)
if err != nil {
return nil, err
}
portMappings = append(portMappings, portMapping...)
}
return portMappings, nil
}
func validateProto(proto string) bool {
for _, availableProto := range []string{"tcp", "udp"} {
if availableProto == proto {
return true
}
}
return false
}
// parsePortSpec parses a port specification string into a slice of PortMappings
func parsePortSpec(rawPort string) ([]*pb.PortMapping, error) {
var proto string
rawIP, hostPort, containerPort := splitParts(rawPort)
proto, containerPort = splitProtoPort(containerPort)
// Strip [] from IPV6 addresses
ip, _, err := net.SplitHostPort(rawIP + ":")
if err != nil {
return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err)
}
if ip != "" && net.ParseIP(ip) == nil {
return nil, fmt.Errorf("Invalid ip address: %s", ip)
}
if containerPort == "" {
return nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
}
startPort, endPort, err := parsePortRange(containerPort)
if err != nil {
return nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
}
var startHostPort, endHostPort uint64 = 0, 0
if len(hostPort) > 0 {
startHostPort, endHostPort, err = parsePortRange(hostPort)
if err != nil {
return nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
}
}
if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
// Allow host port range iff containerPort is not a range.
// In this case, use the host port range as the dynamic
// host port range to allocate into.
if endPort != startPort {
return nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
}
}
if !validateProto(strings.ToLower(proto)) {
return nil, fmt.Errorf("invalid proto: %s", proto)
}
protocol := pb.Protocol_TCP
if strings.ToLower(proto) == "udp" {
protocol = pb.Protocol_UDP
}
var ports []*pb.PortMapping
for i := uint64(0); i <= (endPort - startPort); i++ {
containerPort = strconv.FormatUint(startPort+i, 10)
if len(hostPort) > 0 {
hostPort = strconv.FormatUint(startHostPort+i, 10)
}
// Set hostPort to a range only if there is a single container port
// and a dynamic host port.
if startPort == endPort && startHostPort != endHostPort {
hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10))
}
ctrPort, err := strconv.ParseInt(containerPort, 10, 32)
if err != nil {
return nil, err
}
hPort, err := strconv.ParseInt(hostPort, 10, 32)
if err != nil {
return nil, err
}
port := &pb.PortMapping{
Protocol: protocol,
ContainerPort: int32(ctrPort),
HostPort: int32(hPort),
HostIp: ip,
}
ports = append(ports, port)
}
return ports, nil
}
// parsePortRange parses and validates the specified string as a port-range (8000-9000)
func parsePortRange(ports string) (uint64, uint64, error) {
if ports == "" {
return 0, 0, fmt.Errorf("empty string specified for ports")
}
if !strings.Contains(ports, "-") {
start, err := strconv.ParseUint(ports, 10, 16)
end := start
return start, end, err
}
parts := strings.Split(ports, "-")
start, err := strconv.ParseUint(parts[0], 10, 16)
if err != nil {
return 0, 0, err
}
end, err := strconv.ParseUint(parts[1], 10, 16)
if err != nil {
return 0, 0, err
}
if end < start {
return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports)
}
return start, end, nil
}
// splitParts separates the different parts of rawPort
func splitParts(rawport string) (string, string, string) {
parts := strings.Split(rawport, ":")
n := len(parts)
containerport := parts[n-1]
switch n {
case 1:
return "", "", containerport
case 2:
return "", parts[0], containerport
case 3:
return parts[0], parts[1], containerport
default:
return strings.Join(parts[:n-2], ":"), parts[n-2], containerport
}
}
// splitProtoPort splits a port in the format of port/proto
func splitProtoPort(rawPort string) (string, string) {
parts := strings.Split(rawPort, "/")
l := len(parts)
if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 {
return "", ""
}
if l == 1 {
return "tcp", rawPort
}
if len(parts[1]) == 0 {
return "tcp", parts[0]
}
return parts[1], parts[0]
}
// takes a local seccomp file and reads its file contents
// for security-opt flag
func parseSecurityOpts(securityOpts []string) ([]string, error) { //nolint
for key, opt := range securityOpts {
con := strings.SplitN(opt, "=", 2)
if len(con) == 1 && con[0] != "no-new-privileges" {
if strings.Index(opt, ":") != -1 {
con = strings.SplitN(opt, ":", 2)
} else {
return securityOpts, fmt.Errorf("Invalid --security-opt: %q", opt)
}
}
if con[0] == "seccomp" && con[1] != "unconfined" {
f, err := ioutil.ReadFile(con[1])
if err != nil {
return securityOpts, fmt.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
}
b := bytes.NewBuffer(nil)
if err := json.Compact(b, f); err != nil {
return securityOpts, fmt.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
}
securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
}
}
return securityOpts, nil
}
// parses storage options per container into a map
// for storage-opt flag
func parseStorageOpts(storageOpts []string) (map[string]string, error) { //nolint
m := make(map[string]string)
for _, option := range storageOpts {
if strings.Contains(option, "=") {
opt := strings.SplitN(option, "=", 2)
m[opt[0]] = opt[1]
} else {
return nil, errors.Errorf("invalid storage option %q", option)
}
}
return m, nil
}
// parseUser parses the the uid and gid in the format <name|uid>[:<group|gid>]
// for user flag
// FIXME: Issue from https://github.com/projectatomic/buildah/issues/66
func parseUser(rootdir, userspec string) (specs.User, error) { //nolint
var gid64 uint64
var gerr error = user.UnknownGroupError("error looking up group")
spec := strings.SplitN(userspec, ":", 2)
userspec = spec[0]
groupspec := ""
if userspec == "" {
return specs.User{}, nil
}
if len(spec) > 1 {
groupspec = spec[1]
}
uid64, uerr := strconv.ParseUint(userspec, 10, 32)
if uerr == nil && groupspec == "" {
// We parsed the user name as a number, and there's no group
// component, so we need to look up the user's primary GID.
var name string
name, gid64, gerr = lookupGroupForUIDInContainer(rootdir, uid64)
if gerr == nil {
userspec = name
} else {
if userrec, err := user.LookupId(userspec); err == nil {
gid64, gerr = strconv.ParseUint(userrec.Gid, 10, 32)
userspec = userrec.Name
}
}
}
if uerr != nil {
uid64, gid64, uerr = lookupUserInContainer(rootdir, userspec)
gerr = uerr
}
if uerr != nil {
if userrec, err := user.Lookup(userspec); err == nil {
uid64, uerr = strconv.ParseUint(userrec.Uid, 10, 32)
gid64, gerr = strconv.ParseUint(userrec.Gid, 10, 32)
}
}
if groupspec != "" {
gid64, gerr = strconv.ParseUint(groupspec, 10, 32)
if gerr != nil {
gid64, gerr = lookupGroupInContainer(rootdir, groupspec)
}
if gerr != nil {
if group, err := user.LookupGroup(groupspec); err == nil {
gid64, gerr = strconv.ParseUint(group.Gid, 10, 32)
}
}
}
if uerr == nil && gerr == nil {
u := specs.User{
UID: uint32(uid64),
GID: uint32(gid64),
Username: userspec,
}
return u, nil
}
err := errors.Wrapf(uerr, "error determining run uid")
if uerr == nil {
err = errors.Wrapf(gerr, "error determining run gid")
}
return specs.User{}, err
}
// convertKVStringsToMap converts ["key=value"] to {"key":"value"}
func convertKVStringsToMap(values []string) map[string]string {
result := make(map[string]string, len(values))
for _, value := range values {
kv := strings.SplitN(value, "=", 2)
if len(kv) == 1 {
result[kv[0]] = ""
} else {
result[kv[0]] = kv[1]
}
}
return result
}
// NsUser represents userns mode in the container.
// for userns flag
type NsUser string
// IsHost indicates whether the container uses the host's userns.
func (n NsUser) IsHost() bool {
return n == "host"
}
// IsPrivate indicates whether the container uses the a private userns.
func (n NsUser) IsPrivate() bool {
return !(n.IsHost())
}
// Valid indicates whether the userns is valid.
func (n NsUser) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
default:
return false
}
return true
}
// NsUts represents the UTS namespace of the container.
// for uts flag
type NsUts string
// IsPrivate indicates whether the container uses its private UTS namespace.
func (n NsUts) IsPrivate() bool {
return !(n.IsHost())
}
// IsHost indicates whether the container uses the host's UTS namespace.
func (n NsUts) IsHost() bool {
return n == "host"
}
// Valid indicates whether the UTS namespace is valid.
func (n NsUts) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
default:
return false
}
return true
}
// Takes a stringslice and converts to a uint32slice
func stringSlicetoUint32Slice(inputSlice []string) ([]uint32, error) {
var outputSlice []uint32
for _, v := range inputSlice {
u, err := strconv.ParseUint(v, 10, 32)
if err != nil {
return outputSlice, err
}
outputSlice = append(outputSlice, uint32(u))
}
return outputSlice, nil
}

View File

@ -1,58 +0,0 @@
package main
import (
"fmt"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
"os"
)
var (
pauseDescription = `
kpod pause
Pauses one or more running containers. The container name or ID can be used.
`
pauseCommand = cli.Command{
Name: "pause",
Usage: "Pauses all the processes in one or more containers",
Description: pauseDescription,
Action: pauseCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
}
)
func pauseCmd(c *cli.Context) error {
args := c.Args()
if len(args) < 1 {
return errors.Errorf("you must provide at least one container name or id")
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
defer server.Shutdown()
if err := server.Update(); err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
var lastError error
for _, container := range c.Args() {
cid, err := server.ContainerPause(container)
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "failed to pause container %v", container)
} else {
fmt.Println(cid)
}
}
return lastError
}

View File

@ -1,665 +0,0 @@
package main
import (
"os"
"path/filepath"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"github.com/docker/go-units"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/fields"
"github.com/kubernetes-incubator/cri-o/cmd/kpod/formats"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/kubernetes-incubator/cri-o/oci"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
type psOptions struct {
all bool
filter string
format string
last int
latest bool
noTrunc bool
quiet bool
size bool
label string
namespace bool
}
type psTemplateParams struct {
ID string
Image string
Command string
CreatedAt string
RunningFor string
Status string
Ports string
Size string
Names string
Labels string
Mounts string
PID int
Cgroup string
IPC string
MNT string
NET string
PIDNS string
User string
UTS string
}
// psJSONParams is only used when the JSON format is specified,
// and is better for data processing from JSON.
// psJSONParams will be populated by data from libkpod.ContainerData,
// the members of the struct are the sama data types as their sources.
type psJSONParams struct {
ID string `json:"id"`
Image string `json:"image"`
ImageID string `json:"image_id"`
Command string `json:"command"`
CreatedAt time.Time `json:"createdAt"`
RunningFor time.Duration `json:"runningFor"`
Status string `json:"status"`
Ports map[string]struct{} `json:"ports"`
Size uint `json:"size"`
Names string `json:"names"`
Labels fields.Set `json:"labels"`
Mounts []specs.Mount `json:"mounts"`
ContainerRunning bool `json:"ctrRunning"`
Namespaces *namespace `json:"namespace,omitempty"`
}
type namespace struct {
PID string `json:"pid,omitempty"`
Cgroup string `json:"cgroup,omitempty"`
IPC string `json:"ipc,omitempty"`
MNT string `json:"mnt,omitempty"`
NET string `json:"net,omitempty"`
PIDNS string `json:"pidns,omitempty"`
User string `json:"user,omitempty"`
UTS string `json:"uts,omitempty"`
}
var (
psFlags = []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "Show all the containers, default is only running containers",
},
cli.StringFlag{
Name: "filter, f",
Usage: "Filter output based on conditions given",
},
cli.StringFlag{
Name: "format",
Usage: "Pretty-print containers to JSON or using a Go template",
},
cli.IntFlag{
Name: "last, n",
Usage: "Print the n last created containers (all states)",
Value: -1,
},
cli.BoolFlag{
Name: "latest, l",
Usage: "Show the latest container created (all states)",
},
cli.BoolFlag{
Name: "no-trunc",
Usage: "Display the extended information",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "Print the numeric IDs of the containers only",
},
cli.BoolFlag{
Name: "size, s",
Usage: "Display the total file sizes",
},
cli.BoolFlag{
Name: "namespace, ns",
Usage: "Display namespace information",
},
}
psDescription = "Prints out information about the containers"
psCommand = cli.Command{
Name: "ps",
Usage: "List containers",
Description: psDescription,
Flags: psFlags,
Action: psCmd,
ArgsUsage: "",
}
)
func psCmd(c *cli.Context) error {
if err := validateFlags(c, psFlags); err != nil {
return err
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "error creating server")
}
if err := server.Update(); err != nil {
return errors.Wrapf(err, "error updating list of containers")
}
if len(c.Args()) > 0 {
return errors.Errorf("too many arguments, ps takes no arguments")
}
format := genPsFormat(c.Bool("quiet"), c.Bool("size"), c.Bool("namespace"))
if c.IsSet("format") {
format = c.String("format")
}
opts := psOptions{
all: c.Bool("all"),
filter: c.String("filter"),
format: format,
last: c.Int("last"),
latest: c.Bool("latest"),
noTrunc: c.Bool("no-trunc"),
quiet: c.Bool("quiet"),
size: c.Bool("size"),
namespace: c.Bool("namespace"),
}
// all, latest, and last are mutually exclusive. Only one flag can be used at a time
exclusiveOpts := 0
if opts.last >= 0 {
exclusiveOpts++
}
if opts.latest {
exclusiveOpts++
}
if opts.all {
exclusiveOpts++
}
if exclusiveOpts > 1 {
return errors.Errorf("Last, latest and all are mutually exclusive")
}
containers, err := server.ListContainers()
if err != nil {
return errors.Wrapf(err, "error getting containers from server")
}
var params *FilterParamsPS
if opts.filter != "" {
params, err = parseFilter(opts.filter, containers)
if err != nil {
return errors.Wrapf(err, "error parsing filter")
}
} else {
params = nil
}
containerList := getContainersMatchingFilter(containers, params, server)
return generatePsOutput(containerList, server, opts)
}
// generate the template based on conditions given
func genPsFormat(quiet, size, namespace bool) (format string) {
if quiet {
return formats.IDString
}
if namespace {
format = "table {{.ID}}\t{{.Names}}\t{{.PID}}\t{{.Cgroup}}\t{{.IPC}}\t{{.MNT}}\t{{.NET}}\t{{.PIDNS}}\t{{.User}}\t{{.UTS}}\t"
return
}
format = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.CreatedAt}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}\t"
if size {
format += "{{.Size}}\t"
}
return
}
func psToGeneric(templParams []psTemplateParams, JSONParams []psJSONParams) (genericParams []interface{}) {
if len(templParams) > 0 {
for _, v := range templParams {
genericParams = append(genericParams, interface{}(v))
}
return
}
for _, v := range JSONParams {
genericParams = append(genericParams, interface{}(v))
}
return
}
// generate the accurate header based on template given
func (p *psTemplateParams) headerMap() map[string]string {
v := reflect.Indirect(reflect.ValueOf(p))
values := make(map[string]string)
for i := 0; i < v.NumField(); i++ {
key := v.Type().Field(i).Name
value := key
if value == "ID" {
value = "Container" + value
}
values[key] = strings.ToUpper(splitCamelCase(value))
}
return values
}
// getContainers gets the containers that match the flags given
func getContainers(containers []*libkpod.ContainerData, opts psOptions) []*libkpod.ContainerData {
var containersOutput []*libkpod.ContainerData
if opts.last >= 0 && opts.last < len(containers) {
for i := 0; i < opts.last; i++ {
containersOutput = append(containersOutput, containers[i])
}
return containersOutput
}
if opts.latest {
return []*libkpod.ContainerData{containers[0]}
}
if opts.all || opts.last >= len(containers) {
return containers
}
for _, ctr := range containers {
if ctr.State.Status == oci.ContainerStateRunning {
containersOutput = append(containersOutput, ctr)
}
}
return containersOutput
}
// getTemplateOutput returns the modified container information
func getTemplateOutput(containers []*libkpod.ContainerData, opts psOptions) (psOutput []psTemplateParams) {
var status string
for _, ctr := range containers {
ctrID := ctr.ID
runningFor := units.HumanDuration(time.Since(ctr.State.Created))
createdAt := runningFor + " ago"
command := getStrFromSquareBrackets(ctr.ImageCreatedBy)
imageName := ctr.FromImage
mounts := getMounts(ctr.Mounts, opts.noTrunc)
ports := getPorts(ctr.Config.ExposedPorts)
size := units.HumanSize(float64(ctr.SizeRootFs))
labels := getLabels(ctr.Labels)
ns := getNamespaces(ctr.State.Pid)
switch ctr.State.Status {
case oci.ContainerStateStopped:
status = "Exited (" + strconv.FormatInt(int64(ctr.State.ExitCode), 10) + ") " + runningFor + " ago"
case oci.ContainerStateRunning:
status = "Up " + runningFor + " ago"
case oci.ContainerStatePaused:
status = "Paused"
default:
status = "Created"
}
if !opts.noTrunc {
ctrID = ctr.ID[:idTruncLength]
imageName = getImageName(ctr.FromImage)
}
params := psTemplateParams{
ID: ctrID,
Image: imageName,
Command: command,
CreatedAt: createdAt,
RunningFor: runningFor,
Status: status,
Ports: ports,
Size: size,
Names: ctr.Name,
Labels: labels,
Mounts: mounts,
PID: ctr.State.Pid,
Cgroup: ns.Cgroup,
IPC: ns.IPC,
MNT: ns.MNT,
NET: ns.NET,
PIDNS: ns.PID,
User: ns.User,
UTS: ns.UTS,
}
psOutput = append(psOutput, params)
}
return
}
func getNamespaces(pid int) *namespace {
ctrPID := strconv.Itoa(pid)
cgroup, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
ipc, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
mnt, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
net, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
pidns, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
user, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
uts, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
return &namespace{
PID: ctrPID,
Cgroup: cgroup,
IPC: ipc,
MNT: mnt,
NET: net,
PIDNS: pidns,
User: user,
UTS: uts,
}
}
func getNamespaceInfo(path string) (string, error) {
val, err := os.Readlink(path)
if err != nil {
return "", errors.Wrapf(err, "error getting info from %q", path)
}
return getStrFromSquareBrackets(val), nil
}
// getJSONOutput returns the container info in its raw form
func getJSONOutput(containers []*libkpod.ContainerData, nSpace bool) (psOutput []psJSONParams) {
var ns *namespace
for _, ctr := range containers {
if nSpace {
ns = getNamespaces(ctr.State.Pid)
}
params := psJSONParams{
ID: ctr.ID,
Image: ctr.FromImage,
ImageID: ctr.FromImageID,
Command: getStrFromSquareBrackets(ctr.ImageCreatedBy),
CreatedAt: ctr.State.Created,
RunningFor: time.Since(ctr.State.Created),
Status: ctr.State.Status,
Ports: ctr.Config.ExposedPorts,
Size: ctr.SizeRootFs,
Names: ctr.Name,
Labels: ctr.Labels,
Mounts: ctr.Mounts,
ContainerRunning: ctr.State.Status == oci.ContainerStateRunning,
Namespaces: ns,
}
psOutput = append(psOutput, params)
}
return
}
func generatePsOutput(containers []*libkpod.ContainerData, server *libkpod.ContainerServer, opts psOptions) error {
containersOutput := getContainers(containers, opts)
// In the case of JSON, we want to continue so we at least pass
// {} --valid JSON-- to the consumer
if len(containersOutput) == 0 && opts.format != formats.JSONString {
return nil
}
var out formats.Writer
switch opts.format {
case formats.JSONString:
psOutput := getJSONOutput(containersOutput, opts.namespace)
out = formats.JSONStructArray{Output: psToGeneric([]psTemplateParams{}, psOutput)}
default:
psOutput := getTemplateOutput(containersOutput, opts)
out = formats.StdoutTemplateArray{Output: psToGeneric(psOutput, []psJSONParams{}), Template: opts.format, Fields: psOutput[0].headerMap()}
}
return formats.Writer(out).Out()
}
// getStrFromSquareBrackets gets the string inside [] from a string
func getStrFromSquareBrackets(cmd string) string {
reg, err := regexp.Compile(".*\\[|\\].*")
if err != nil {
return ""
}
arr := strings.Split(reg.ReplaceAllLiteralString(cmd, ""), ",")
return strings.Join(arr, ",")
}
// getImageName shortens the image name
func getImageName(img string) string {
arr := strings.Split(img, "/")
if arr[0] == "docker.io" && arr[1] == "library" {
img = strings.Join(arr[2:], "/")
} else if arr[0] == "docker.io" {
img = strings.Join(arr[1:], "/")
}
return img
}
// getLabels converts the labels to a string of the form "key=value, key2=value2"
func getLabels(labels fields.Set) string {
var arr []string
if len(labels) > 0 {
for key, val := range labels {
temp := key + "=" + val
arr = append(arr, temp)
}
return strings.Join(arr, ",")
}
return ""
}
// getMounts converts the volumes mounted to a string of the form "mount1, mount2"
// it truncates it if noTrunc is false
func getMounts(mounts []specs.Mount, noTrunc bool) string {
var arr []string
if len(mounts) == 0 {
return ""
}
for _, mount := range mounts {
if noTrunc {
arr = append(arr, mount.Source)
continue
}
tempArr := strings.SplitAfter(mount.Source, "/")
if len(tempArr) >= 3 {
arr = append(arr, strings.Join(tempArr[:3], ""))
} else {
arr = append(arr, mount.Source)
}
}
return strings.Join(arr, ",")
}
// getPorts converts the ports used to a string of the from "port1, port2"
func getPorts(ports map[string]struct{}) string {
var arr []string
if len(ports) == 0 {
return ""
}
for key := range ports {
arr = append(arr, key)
}
return strings.Join(arr, ",")
}
// FilterParamsPS contains the filter options for ps
type FilterParamsPS struct {
id string
label string
name string
exited int32
status string
ancestor string
before time.Time
since time.Time
volume string
}
// parseFilter takes a filter string and a list of containers and filters it
func parseFilter(filter string, containers []*oci.Container) (*FilterParamsPS, error) {
params := new(FilterParamsPS)
allFilters := strings.Split(filter, ",")
for _, param := range allFilters {
pair := strings.SplitN(param, "=", 2)
switch strings.TrimSpace(pair[0]) {
case "id":
params.id = pair[1]
case "label":
params.label = pair[1]
case "name":
params.name = pair[1]
case "exited":
exitedCode, err := strconv.ParseInt(pair[1], 10, 32)
if err != nil {
return nil, errors.Errorf("exited code out of range %q", pair[1])
}
params.exited = int32(exitedCode)
case "status":
params.status = pair[1]
case "ancestor":
params.ancestor = pair[1]
case "before":
if ctr, err := findContainer(containers, pair[1]); err == nil {
params.before = ctr.CreatedAt()
} else {
return nil, errors.Wrapf(err, "no such container %q", pair[1])
}
case "since":
if ctr, err := findContainer(containers, pair[1]); err == nil {
params.before = ctr.CreatedAt()
} else {
return nil, errors.Wrapf(err, "no such container %q", pair[1])
}
case "volume":
params.volume = pair[1]
default:
return nil, errors.Errorf("invalid filter %q", pair[0])
}
}
return params, nil
}
// findContainer finds a container with a specific name or id from a list of containers
func findContainer(containers []*oci.Container, ref string) (*oci.Container, error) {
for _, ctr := range containers {
if strings.HasPrefix(ctr.ID(), ref) || ctr.Name() == ref {
return ctr, nil
}
}
return nil, errors.Errorf("could not find container")
}
// matchesFilter checks if a container matches all the filter parameters
func matchesFilter(ctrData *libkpod.ContainerData, params *FilterParamsPS) bool {
if params == nil {
return true
}
if params.id != "" && !matchesID(ctrData, params.id) {
return false
}
if params.name != "" && !matchesName(ctrData, params.name) {
return false
}
if !params.before.IsZero() && !matchesBeforeContainer(ctrData, params.before) {
return false
}
if !params.since.IsZero() && !matchesSinceContainer(ctrData, params.since) {
return false
}
if params.exited > 0 && !matchesExited(ctrData, params.exited) {
return false
}
if params.status != "" && !matchesStatus(ctrData, params.status) {
return false
}
if params.ancestor != "" && !matchesAncestor(ctrData, params.ancestor) {
return false
}
if params.label != "" && !matchesLabel(ctrData, params.label) {
return false
}
if params.volume != "" && !matchesVolume(ctrData, params.volume) {
return false
}
return true
}
// GetContainersMatchingFilter returns a slice of all the containers that match the provided filter parameters
func getContainersMatchingFilter(containers []*oci.Container, filter *FilterParamsPS, server *libkpod.ContainerServer) []*libkpod.ContainerData {
var filteredCtrs []*libkpod.ContainerData
for _, ctr := range containers {
ctrData, err := server.GetContainerData(ctr.ID(), true)
if err != nil {
logrus.Warn("unable to get container data for matched container")
}
if filter == nil || matchesFilter(ctrData, filter) {
filteredCtrs = append(filteredCtrs, ctrData)
}
}
return filteredCtrs
}
// matchesID returns true if the id's match
func matchesID(ctrData *libkpod.ContainerData, id string) bool {
return strings.HasPrefix(ctrData.ID, id)
}
// matchesBeforeContainer returns true if the container was created before the filter image
func matchesBeforeContainer(ctrData *libkpod.ContainerData, beforeTime time.Time) bool {
return ctrData.State.Created.Before(beforeTime)
}
// matchesSincecontainer returns true if the container was created since the filter image
func matchesSinceContainer(ctrData *libkpod.ContainerData, sinceTime time.Time) bool {
return ctrData.State.Created.After(sinceTime)
}
// matchesLabel returns true if the container label matches that of the filter label
func matchesLabel(ctrData *libkpod.ContainerData, label string) bool {
pair := strings.SplitN(label, "=", 2)
if val, ok := ctrData.Labels[pair[0]]; ok {
if len(pair) == 2 && val == pair[1] {
return true
}
if len(pair) == 1 {
return true
}
return false
}
return false
}
// matchesName returns true if the names are identical
func matchesName(ctrData *libkpod.ContainerData, name string) bool {
return ctrData.Name == name
}
// matchesExited returns true if the exit codes are identical
func matchesExited(ctrData *libkpod.ContainerData, exited int32) bool {
return ctrData.State.ExitCode == exited
}
// matchesStatus returns true if the container status matches that of filter status
func matchesStatus(ctrData *libkpod.ContainerData, status string) bool {
return ctrData.State.Status == status
}
// matchesAncestor returns true if filter ancestor is in container image name
func matchesAncestor(ctrData *libkpod.ContainerData, ancestor string) bool {
return strings.Contains(ctrData.FromImage, ancestor)
}
// matchesVolue returns true if the volume mounted or path to volue of the container matches that of filter volume
func matchesVolume(ctrData *libkpod.ContainerData, volume string) bool {
for _, vol := range ctrData.Mounts {
if strings.Contains(vol.Source, volume) {
return true
}
}
return false
}

View File

@ -1,118 +0,0 @@
package main
import (
"fmt"
"io"
"os"
"golang.org/x/crypto/ssh/terminal"
"github.com/containers/image/types"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/kubernetes-incubator/cri-o/libpod/common"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var (
pullFlags = []cli.Flag{
cli.StringFlag{
Name: "authfile",
Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
},
cli.StringFlag{
Name: "cert-dir",
Usage: "`pathname` of a directory containing TLS certificates and keys",
},
cli.StringFlag{
Name: "creds",
Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "Suppress output information when pulling images",
},
cli.StringFlag{
Name: "signature-policy",
Usage: "`pathname` of signature policy file (not usually used)",
},
cli.BoolTFlag{
Name: "tls-verify",
Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
},
}
pullDescription = "Pulls an image from a registry and stores it locally.\n" +
"An image can be pulled using its tag or digest. If a tag is not\n" +
"specified, the image with the 'latest' tag (if it exists) is pulled."
pullCommand = cli.Command{
Name: "pull",
Usage: "pull an image from a registry",
Description: pullDescription,
Flags: pullFlags,
Action: pullCmd,
ArgsUsage: "",
}
)
// pullCmd gets the data from the command line and calls pullImage
// to copy an image from a registry to a local machine
func pullCmd(c *cli.Context) error {
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
args := c.Args()
if len(args) == 0 {
logrus.Errorf("an image name must be specified")
return nil
}
if len(args) > 1 {
logrus.Errorf("too many arguments. Requires exactly 1")
return nil
}
if err := validateFlags(c, pullFlags); err != nil {
return err
}
image := args[0]
var registryCreds *types.DockerAuthConfig
if c.String("creds") != "" {
creds, err := common.ParseRegistryCreds(c.String("creds"))
if err != nil {
if err == common.ErrNoPassword {
fmt.Print("Password: ")
password, err := terminal.ReadPassword(0)
if err != nil {
return errors.Wrapf(err, "could not read password from terminal")
}
creds.Password = string(password)
} else {
return err
}
}
registryCreds = creds
}
var writer io.Writer
if !c.Bool("quiet") {
writer = os.Stdout
}
options := libpod.CopyOptions{
SignaturePolicyPath: c.String("signature-policy"),
AuthFile: c.String("authfile"),
DockerRegistryOptions: common.DockerRegistryOptions{
DockerRegistryCreds: registryCreds,
DockerCertPath: c.String("cert-dir"),
DockerInsecureSkipTLSVerify: !c.BoolT("tls-verify"),
},
Writer: writer,
}
return runtime.PullImage(image, options)
}

View File

@ -1,132 +0,0 @@
package main
import (
"fmt"
"io"
"os"
"github.com/containers/image/types"
"github.com/containers/storage/pkg/archive"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/kubernetes-incubator/cri-o/libpod/common"
"github.com/pkg/errors"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
)
var (
pushFlags = []cli.Flag{
cli.StringFlag{
Name: "signature-policy",
Usage: "`pathname` of signature policy file (not usually used)",
Hidden: true,
},
cli.StringFlag{
Name: "creds",
Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry",
},
cli.StringFlag{
Name: "cert-dir",
Usage: "`pathname` of a directory containing TLS certificates and keys",
},
cli.BoolTFlag{
Name: "tls-verify",
Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
},
cli.BoolFlag{
Name: "remove-signatures",
Usage: "discard any pre-existing signatures in the image",
},
cli.StringFlag{
Name: "sign-by",
Usage: "add a signature at the destination using the specified key",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "don't output progress information when pushing images",
},
cli.StringFlag{
Name: "authfile",
Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
},
}
pushDescription = fmt.Sprintf(`
Pushes an image to a specified location.
The Image "DESTINATION" uses a "transport":"details" format.
See kpod-push(1) section "DESTINATION" for the expected format`)
pushCommand = cli.Command{
Name: "push",
Usage: "push an image to a specified destination",
Description: pushDescription,
Flags: pushFlags,
Action: pushCmd,
ArgsUsage: "IMAGE DESTINATION",
}
)
func pushCmd(c *cli.Context) error {
var registryCreds *types.DockerAuthConfig
args := c.Args()
if len(args) < 2 {
return errors.New("kpod push requires exactly 2 arguments")
}
if err := validateFlags(c, pushFlags); err != nil {
return err
}
srcName := c.Args().Get(0)
destName := c.Args().Get(1)
registryCredsString := c.String("creds")
certPath := c.String("cert-dir")
skipVerify := !c.BoolT("tls-verify")
removeSignatures := c.Bool("remove-signatures")
signBy := c.String("sign-by")
if registryCredsString != "" {
creds, err := common.ParseRegistryCreds(registryCredsString)
if err != nil {
if err == common.ErrNoPassword {
fmt.Print("Password: ")
password, err := terminal.ReadPassword(0)
if err != nil {
return errors.Wrapf(err, "could not read password from terminal")
}
creds.Password = string(password)
} else {
return err
}
}
registryCreds = creds
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not create runtime")
}
defer runtime.Shutdown(false)
var writer io.Writer
if !c.Bool("quiet") {
writer = os.Stdout
}
options := libpod.CopyOptions{
Compression: archive.Uncompressed,
SignaturePolicyPath: c.String("signature-policy"),
DockerRegistryOptions: common.DockerRegistryOptions{
DockerRegistryCreds: registryCreds,
DockerCertPath: certPath,
DockerInsecureSkipTLSVerify: skipVerify,
},
SigningOptions: common.SigningOptions{
RemoveSignatures: removeSignatures,
SignBy: signBy,
},
AuthFile: c.String("authfile"),
Writer: writer,
}
return runtime.PushImage(srcName, destName, options)
}

View File

@ -1,49 +0,0 @@
package main
import (
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
renameDescription = "Rename a container. Container may be created, running, paused, or stopped"
renameFlags = []cli.Flag{}
renameCommand = cli.Command{
Name: "rename",
Usage: "rename a container",
Description: renameDescription,
Action: renameCmd,
ArgsUsage: "CONTAINER NEW-NAME",
Flags: renameFlags,
}
)
func renameCmd(c *cli.Context) error {
if len(c.Args()) != 2 {
return errors.Errorf("Rename requires a src container name/ID and a dest container name")
}
if err := validateFlags(c, renameFlags); err != nil {
return err
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "Could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
defer server.Shutdown()
err = server.Update()
if err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
err = server.ContainerRename(c.Args().Get(0), c.Args().Get(1))
if err != nil {
return errors.Wrapf(err, "could not rename container")
}
return nil
}

View File

@ -1,69 +0,0 @@
package main
import (
"fmt"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
"golang.org/x/net/context"
)
var (
rmFlags = []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "Force removal of a running container. The default is false",
},
}
rmDescription = "Remove one or more containers"
rmCommand = cli.Command{
Name: "rm",
Usage: fmt.Sprintf(`kpod rm will remove one or more containers from the host. The container name or ID can be used.
This does not remove images. Running containers will not be removed without the -f option.`),
Description: rmDescription,
Flags: rmFlags,
Action: rmCmd,
ArgsUsage: "",
}
)
// saveCmd saves the image to either docker-archive or oci
func rmCmd(c *cli.Context) error {
args := c.Args()
if len(args) == 0 {
return errors.Errorf("specify one or more containers to remove")
}
if err := validateFlags(c, rmFlags); err != nil {
return err
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
defer server.Shutdown()
err = server.Update()
if err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
force := c.Bool("force")
for _, container := range c.Args() {
id, err2 := server.Remove(context.Background(), container, force)
if err2 != nil {
if err == nil {
err = err2
} else {
err = errors.Wrapf(err, "%v. Stop the container before attempting removal or use -f\n", err2)
}
} else {
fmt.Println(id)
}
}
return err
}

View File

@ -1,56 +0,0 @@
package main
import (
"fmt"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
rmiDescription = "removes one or more locally stored images."
rmiFlags = []cli.Flag{
cli.BoolFlag{
Name: "force, f",
Usage: "force removal of the image",
},
}
rmiCommand = cli.Command{
Name: "rmi",
Usage: "removes one or more images from local storage",
Description: rmiDescription,
Action: rmiCmd,
ArgsUsage: "IMAGE-NAME-OR-ID [...]",
Flags: rmiFlags,
}
)
func rmiCmd(c *cli.Context) error {
if err := validateFlags(c, rmiFlags); err != nil {
return err
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
args := c.Args()
if len(args) == 0 {
return errors.Errorf("image name or ID must be specified")
}
for _, arg := range args {
image, err := runtime.GetImage(arg)
if err != nil {
return errors.Wrapf(err, "could not get image %q", arg)
}
id, err := runtime.RemoveImage(image, c.Bool("force"))
if err != nil {
return errors.Wrapf(err, "error removing image %q", id)
}
fmt.Printf("%s\n", id)
}
return nil
}

View File

@ -1,104 +0,0 @@
package main
import (
"fmt"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var runDescription = "Runs a command in a new container from the given image"
var runCommand = cli.Command{
Name: "run",
Usage: "run a command in a new container",
Description: runDescription,
Flags: createFlags,
Action: runCmd,
ArgsUsage: "IMAGE [COMMAND [ARG...]]",
}
func runCmd(c *cli.Context) error {
if err := validateFlags(c, createFlags); err != nil {
return err
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
createConfig, err := parseCreateOpts(c, runtime)
if err != nil {
return err
}
createImage := runtime.NewImage(createConfig.image)
if !createImage.HasImageLocal() {
// 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
}
defer runtime.Shutdown(false)
logrus.Debug("spec is ", runtimeSpec)
imageName, err := createImage.GetFQName()
if err != nil {
return err
}
logrus.Debug("imageName is ", imageName)
imageID, err := createImage.GetImageID()
if err != nil {
return err
}
logrus.Debug("imageID is ", imageID)
options, err := createConfig.GetContainerCreateOptions(c)
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))
ctr, err := runtime.NewContainer(runtimeSpec, options...)
if err != nil {
return err
}
logrus.Debug("new container created ", ctr.ID())
if err := ctr.Create(); err != nil {
return err
}
logrus.Debug("container storage created for %q", ctr.ID())
if c.String("cidfile") != "" {
libpod.WriteFile(ctr.ID(), c.String("cidfile"))
return nil
}
// Start the container
if err := ctr.Start(); err != nil {
return errors.Wrapf(err, "unable to start container %q", ctr.ID())
}
logrus.Debug("started container ", ctr.ID())
if createConfig.tty {
// Attach to the running container
logrus.Debug("trying to attach to the container %s", ctr.ID())
if err := ctr.Attach(false, c.String("detach-keys")); err != nil {
return errors.Wrapf(err, "unable to attach to container %s", ctr.ID())
}
} else {
fmt.Printf("%s\n", ctr.ID())
}
return nil
}

View File

@ -1,98 +0,0 @@
package main
import (
"io"
"os"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
var (
saveFlags = []cli.Flag{
cli.StringFlag{
Name: "output, o",
Usage: "Write to a file, default is STDOUT",
Value: "/dev/stdout",
},
cli.BoolFlag{
Name: "quiet, q",
Usage: "Suppress the output",
},
cli.StringFlag{
Name: "format",
Usage: "Save image to oci-archive",
},
}
saveDescription = `
Save an image to docker-archive or oci-archive on the local machine.
Default is docker-archive`
saveCommand = cli.Command{
Name: "save",
Usage: "Save image to an archive",
Description: saveDescription,
Flags: saveFlags,
Action: saveCmd,
ArgsUsage: "",
}
)
// saveCmd saves the image to either docker-archive or oci
func saveCmd(c *cli.Context) error {
args := c.Args()
if len(args) == 0 {
return errors.Errorf("need at least 1 argument")
}
if err := validateFlags(c, saveFlags); err != nil {
return err
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not create runtime")
}
defer runtime.Shutdown(false)
var writer io.Writer
if !c.Bool("quiet") {
writer = os.Stdout
}
output := c.String("output")
if output == "/dev/stdout" {
fi := os.Stdout
if logrus.IsTerminal(fi) {
return errors.Errorf("refusing to save to terminal. Use -o flag or redirect")
}
}
var dst string
switch c.String("format") {
case libpod.OCIArchive:
dst = libpod.OCIArchive + ":" + output
case libpod.DockerArchive:
fallthrough
case "":
dst = libpod.DockerArchive + ":" + output
default:
return errors.Errorf("unknown format option %q", c.String("format"))
}
saveOpts := libpod.CopyOptions{
SignaturePolicyPath: "",
Writer: writer,
}
// only one image is supported for now
// future pull requests will fix this
for _, image := range args {
dest := dst + ":" + image
if err := runtime.PushImage(image, dest, saveOpts); err != nil {
return errors.Wrapf(err, "unable to save %q", image)
}
}
return nil
}

View File

@ -1,490 +0,0 @@
package main
import (
"fmt"
"strings"
"github.com/kubernetes-incubator/cri-o/libpod"
ann "github.com/kubernetes-incubator/cri-o/pkg/annotations"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
"golang.org/x/sys/unix"
)
// Parses information needed to create a container into an OCI runtime spec
func createConfigToOCISpec(config *createConfig) (*spec.Spec, error) {
spec := config.GetDefaultLinuxSpec()
spec.Process.Cwd = config.workDir
spec.Process.Args = config.command
spec.Process.Terminal = config.tty
// User and Group must go together
spec.Process.User.UID = config.user
spec.Process.User.GID = config.group
spec.Process.User.AdditionalGids = config.groupAdd
spec.Process.Env = config.env
//TODO
// Need examples of capacity additions so I can load that properly
spec.Root.Readonly = config.readOnlyRootfs
spec.Hostname = config.hostname
// BIND MOUNTS
spec.Mounts = append(spec.Mounts, config.GetVolumeMounts()...)
// TMPFS MOUNTS
spec.Mounts = append(spec.Mounts, config.GetTmpfsMounts()...)
// RESOURCES - MEMORY
spec.Linux.Sysctl = config.sysctl
if config.resources.memory != 0 {
spec.Linux.Resources.Memory.Limit = &config.resources.memory
}
if config.resources.memoryReservation != 0 {
spec.Linux.Resources.Memory.Reservation = &config.resources.memoryReservation
}
if config.resources.memorySwap != 0 {
spec.Linux.Resources.Memory.Swap = &config.resources.memorySwap
}
if config.resources.kernelMemory != 0 {
spec.Linux.Resources.Memory.Kernel = &config.resources.kernelMemory
}
if config.resources.memorySwapiness != 0 {
spec.Linux.Resources.Memory.Swappiness = &config.resources.memorySwapiness
}
if config.resources.disableOomKiller {
spec.Linux.Resources.Memory.DisableOOMKiller = &config.resources.disableOomKiller
}
// RESOURCES - CPU
if config.resources.cpuShares != 0 {
spec.Linux.Resources.CPU.Shares = &config.resources.cpuShares
}
if config.resources.cpuQuota != 0 {
spec.Linux.Resources.CPU.Quota = &config.resources.cpuQuota
}
if config.resources.cpuPeriod != 0 {
spec.Linux.Resources.CPU.Period = &config.resources.cpuPeriod
}
if config.resources.cpuRtRuntime != 0 {
spec.Linux.Resources.CPU.RealtimeRuntime = &config.resources.cpuRtRuntime
}
if config.resources.cpuRtPeriod != 0 {
spec.Linux.Resources.CPU.RealtimePeriod = &config.resources.cpuRtPeriod
}
if config.resources.cpus != "" {
spec.Linux.Resources.CPU.Cpus = config.resources.cpus
}
if config.resources.cpusetMems != "" {
spec.Linux.Resources.CPU.Mems = config.resources.cpusetMems
}
// RESOURCES - PIDS
if config.resources.pidsLimit != 0 {
spec.Linux.Resources.Pids.Limit = config.resources.pidsLimit
}
/*
Capabilities: &spec.LinuxCapabilities{
// Rlimits []PosixRlimit // Where does this come from
// Type string
// Hard uint64
// Limit uint64
// NoNewPrivileges bool // No user input for this
// ApparmorProfile string // No user input for this
OOMScoreAdj: &config.resources.oomScoreAdj,
// Selinuxlabel
},
Hooks: &spec.Hooks{},
//Annotations
Resources: &spec.LinuxResources{
Devices: config.GetDefaultDevices(),
BlockIO: &blkio,
//HugepageLimits:
Network: &spec.LinuxNetwork{
// ClassID *uint32
// Priorites []LinuxInterfacePriority
},
},
//CgroupsPath:
//Namespaces: []LinuxNamespace
//Devices
Seccomp: &spec.LinuxSeccomp{
// DefaultAction:
// Architectures
// Syscalls:
},
// RootfsPropagation
// MaskedPaths
// ReadonlyPaths:
// MountLabel
// IntelRdt
},
}
*/
return &spec, nil
}
func (c *createConfig) CreateBlockIO() (spec.LinuxBlockIO, error) {
bio := spec.LinuxBlockIO{}
bio.Weight = &c.resources.blkioWeight
if len(c.resources.blkioDevice) > 0 {
var lwds []spec.LinuxWeightDevice
for _, i := range c.resources.blkioDevice {
wd, err := validateweightDevice(i)
if err != nil {
return bio, errors.Wrapf(err, "invalid values for blkio-weight-device")
}
wdStat := getStatFromPath(wd.path)
lwd := spec.LinuxWeightDevice{
Weight: &wd.weight,
}
lwd.Major = int64(unix.Major(wdStat.Rdev))
lwd.Minor = int64(unix.Minor(wdStat.Rdev))
lwds = append(lwds, lwd)
}
}
if len(c.resources.deviceReadBps) > 0 {
readBps, err := makeThrottleArray(c.resources.deviceReadBps)
if err != nil {
return bio, err
}
bio.ThrottleReadBpsDevice = readBps
}
if len(c.resources.deviceWriteBps) > 0 {
writeBpds, err := makeThrottleArray(c.resources.deviceWriteBps)
if err != nil {
return bio, err
}
bio.ThrottleWriteBpsDevice = writeBpds
}
if len(c.resources.deviceReadIops) > 0 {
readIops, err := makeThrottleArray(c.resources.deviceReadIops)
if err != nil {
return bio, err
}
bio.ThrottleReadIOPSDevice = readIops
}
if len(c.resources.deviceWriteIops) > 0 {
writeIops, err := makeThrottleArray(c.resources.deviceWriteIops)
if err != nil {
return bio, err
}
bio.ThrottleWriteIOPSDevice = writeIops
}
return bio, nil
}
func (c *createConfig) GetDefaultMounts() []spec.Mount {
// Default to 64K default per man page
shmSize := "65536k"
if c.resources.shmSize != "" {
shmSize = c.resources.shmSize
}
return []spec.Mount{
{
Destination: "/proc",
Type: "proc",
Source: "proc",
Options: []string{"nosuid", "noexec", "nodev"},
},
{
Destination: "/dev",
Type: "tmpfs",
Source: "tmpfs",
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
},
{
Destination: "/dev/pts",
Type: "devpts",
Source: "devpts",
Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
},
{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{"nosuid", "noexec", "nodev", "ro"},
},
{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
Options: []string{"ro", "nosuid", "noexec", "nodev"},
},
{
Destination: "/dev/mqueue",
Type: "mqueue",
Source: "mqueue",
Options: []string{"nosuid", "noexec", "nodev"},
},
{
Destination: "/dev/shm",
Type: "tmpfs",
Source: "shm",
Options: []string{"nosuid", "noexec", "nodev", "mode=1777", fmt.Sprintf("size=%s", shmSize)},
},
}
}
func iPtr(i int64) *int64 { return &i }
func (c *createConfig) GetDefaultDevices() []spec.LinuxDeviceCgroup {
return []spec.LinuxDeviceCgroup{
{
Allow: false,
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(5),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(3),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(9),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(1),
Minor: iPtr(8),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(5),
Minor: iPtr(0),
Access: "rwm",
},
{
Allow: true,
Type: "c",
Major: iPtr(5),
Minor: iPtr(1),
Access: "rwm",
},
{
Allow: false,
Type: "c",
Major: iPtr(10),
Minor: iPtr(229),
Access: "rwm",
},
}
}
func defaultCapabilities() []string {
return []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_FSETID",
"CAP_FOWNER",
"CAP_MKNOD",
"CAP_NET_RAW",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETFCAP",
"CAP_SETPCAP",
"CAP_NET_BIND_SERVICE",
"CAP_SYS_CHROOT",
"CAP_KILL",
"CAP_AUDIT_WRITE",
}
}
func (c *createConfig) GetDefaultLinuxSpec() spec.Spec {
s := spec.Spec{
Version: spec.Version,
Root: &spec.Root{},
}
s.Annotations = c.GetAnnotations()
s.Mounts = c.GetDefaultMounts()
s.Process = &spec.Process{
Capabilities: &spec.LinuxCapabilities{
Bounding: defaultCapabilities(),
Permitted: defaultCapabilities(),
Inheritable: defaultCapabilities(),
Effective: defaultCapabilities(),
},
}
s.Linux = &spec.Linux{
MaskedPaths: []string{
"/proc/kcore",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
},
ReadonlyPaths: []string{
"/proc/asound",
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger",
},
Namespaces: []spec.LinuxNamespace{
{Type: "mount"},
{Type: "network"},
{Type: "uts"},
{Type: "pid"},
{Type: "ipc"},
},
Devices: []spec.LinuxDevice{},
Resources: &spec.LinuxResources{
Devices: c.GetDefaultDevices(),
},
}
return s
}
// GetAnnotations returns the all the annotations for the container
func (c *createConfig) GetAnnotations() map[string]string {
a := getDefaultAnnotations()
// TODO
// Which annotations do we want added by default
if c.tty {
a["io.kubernetes.cri-o.TTY"] = "true"
}
return a
}
func getDefaultAnnotations() map[string]string {
var annotations map[string]string
annotations = make(map[string]string)
annotations[ann.Annotations] = ""
annotations[ann.ContainerID] = ""
annotations[ann.ContainerName] = ""
annotations[ann.ContainerType] = ""
annotations[ann.Created] = ""
annotations[ann.HostName] = ""
annotations[ann.IP] = ""
annotations[ann.Image] = ""
annotations[ann.ImageName] = ""
annotations[ann.ImageRef] = ""
annotations[ann.KubeName] = ""
annotations[ann.Labels] = ""
annotations[ann.LogPath] = ""
annotations[ann.Metadata] = ""
annotations[ann.Name] = ""
annotations[ann.PrivilegedRuntime] = ""
annotations[ann.ResolvPath] = ""
annotations[ann.HostnamePath] = ""
annotations[ann.SandboxID] = ""
annotations[ann.SandboxName] = ""
annotations[ann.ShmPath] = ""
annotations[ann.MountPoint] = ""
annotations[ann.TrustedSandbox] = ""
annotations[ann.TTY] = "false"
annotations[ann.Stdin] = ""
annotations[ann.StdinOnce] = ""
annotations[ann.Volumes] = ""
return annotations
}
//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
func (c *createConfig) GetVolumeMounts() []spec.Mount {
var m []spec.Mount
var options []string
for _, i := range c.volumes {
// We need to handle SELinux options better here, specifically :Z
spliti := strings.Split(i, ":")
if len(spliti) > 2 {
options = strings.Split(spliti[2], ",")
}
// always add rbind bc mount ignores the bind filesystem when mounting
options = append(options, "rbind")
m = append(m, spec.Mount{
Destination: spliti[1],
Type: string(TypeBind),
Source: spliti[0],
Options: options,
})
}
return m
}
//GetTmpfsMounts takes user provided input for tmpfs mounts and creates Mount structs
func (c *createConfig) GetTmpfsMounts() []spec.Mount {
var m []spec.Mount
for _, i := range c.tmpfs {
// Default options if nothing passed
options := []string{"rw", "noexec", "nosuid", "nodev", "size=65536k"}
spliti := strings.Split(i, ":")
destPath := spliti[0]
if len(spliti) > 1 {
options = strings.Split(spliti[1], ",")
}
m = append(m, spec.Mount{
Destination: destPath,
Type: string(TypeTmpfs),
Options: options,
Source: string(TypeTmpfs),
})
}
return m
}
func (c *createConfig) GetContainerCreateOptions(cli *cli.Context) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
// Uncomment after talking to mheon about unimplemented funcs
// options = append(options, libpod.WithLabels(c.labels))
if c.interactive {
options = append(options, libpod.WithStdin())
}
if c.name != "" {
logrus.Debug("appending name %s", c.name)
options = append(options, libpod.WithName(c.name))
}
return options, nil
}
func getStatFromPath(path string) unix.Stat_t {
s := unix.Stat_t{}
_ = unix.Stat(path, &s)
return s
}
func makeThrottleArray(throttleInput []string) ([]spec.LinuxThrottleDevice, error) {
var ltds []spec.LinuxThrottleDevice
for _, i := range throttleInput {
t, err := validateBpsDevice(i)
if err != nil {
return []spec.LinuxThrottleDevice{}, err
}
ltd := spec.LinuxThrottleDevice{}
ltd.Rate = t.rate
ltdStat := getStatFromPath(t.path)
ltd.Major = int64(unix.Major(ltdStat.Rdev))
ltd.Minor = int64(unix.Major(ltdStat.Rdev))
ltds = append(ltds, ltd)
}
return ltds, nil
}

View File

@ -1,245 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"text/template"
"time"
"github.com/docker/go-units"
tm "github.com/buger/goterm"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/kubernetes-incubator/cri-o/oci"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var printf func(format string, a ...interface{}) (n int, err error)
var println func(a ...interface{}) (n int, err error)
type statsOutputParams struct {
Container string
ID string
CPUPerc string
MemUsage string
MemPerc string
NetIO string
BlockIO string
PIDs uint64
}
var (
statsFlags = []cli.Flag{
cli.BoolFlag{
Name: "all, a",
Usage: "show all containers. Only running containers are shown by default. The default is false",
},
cli.BoolFlag{
Name: "no-stream",
Usage: "disable streaming stats and only pull the first result, default setting is false",
},
cli.StringFlag{
Name: "format",
Usage: "pretty-print container statistics using a Go template",
},
cli.BoolFlag{
Name: "json",
Usage: "output container statistics in json format",
},
}
statsDescription = "display a live stream of one or more containers' resource usage statistics"
statsCommand = cli.Command{
Name: "stats",
Usage: "Display percentage of CPU, memory, network I/O, block I/O and PIDs for one or more containers",
Description: statsDescription,
Flags: statsFlags,
Action: statsCmd,
ArgsUsage: "",
}
)
func statsCmd(c *cli.Context) error {
if err := validateFlags(c, statsFlags); err != nil {
return err
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not read config")
}
containerServer, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not create container server")
}
defer containerServer.Shutdown()
err = containerServer.Update()
if err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
times := -1
if c.Bool("no-stream") {
times = 1
}
statsChan := make(chan []*libkpod.ContainerStats)
// iterate over the channel until it is closed
go func() {
// print using goterm
printf = tm.Printf
println = tm.Println
for stats := range statsChan {
// Continually refresh statistics
tm.Clear()
tm.MoveCursor(1, 1)
outputStats(stats, c.String("format"), c.Bool("json"))
tm.Flush()
time.Sleep(time.Second)
}
}()
return getStats(containerServer, c.Args(), c.Bool("all"), statsChan, times)
}
func getStats(server *libkpod.ContainerServer, args []string, all bool, statsChan chan []*libkpod.ContainerStats, times int) error {
ctrs, err := server.ListContainers(isRunning, ctrInList(args))
if err != nil {
return err
}
containerStats := map[string]*libkpod.ContainerStats{}
for _, ctr := range ctrs {
initialStats, err := server.GetContainerStats(ctr, &libkpod.ContainerStats{})
if err != nil {
return err
}
containerStats[ctr.ID()] = initialStats
}
step := 1
if times == -1 {
times = 1
step = 0
}
for i := 0; i < times; i += step {
reportStats := []*libkpod.ContainerStats{}
for _, ctr := range ctrs {
id := ctr.ID()
if _, ok := containerStats[ctr.ID()]; !ok {
initialStats, err := server.GetContainerStats(ctr, &libkpod.ContainerStats{})
if err != nil {
return err
}
containerStats[id] = initialStats
}
stats, err := server.GetContainerStats(ctr, containerStats[id])
if err != nil {
return err
}
// replace the previous measurement with the current one
containerStats[id] = stats
reportStats = append(reportStats, stats)
}
statsChan <- reportStats
err := server.Update()
if err != nil {
return err
}
ctrs, err = server.ListContainers(isRunning, ctrInList(args))
if err != nil {
return err
}
}
return nil
}
func outputStats(stats []*libkpod.ContainerStats, format string, json bool) error {
if format == "" {
outputStatsHeader()
}
if json {
return outputStatsAsJSON(stats)
}
var err error
for _, s := range stats {
if format == "" {
outputStatsUsingFormatString(s)
} else {
params := getStatsOutputParams(s)
err2 := outputStatsUsingTemplate(format, params)
if err2 != nil {
err = errors.Wrapf(err, err2.Error())
}
}
}
return err
}
func outputStatsHeader() {
printf("%-64s %-16s %-32s %-16s %-24s %-24s %s\n", "CONTAINER", "CPU %", "MEM USAGE / MEM LIMIT", "MEM %", "NET I/O", "BLOCK I/O", "PIDS")
}
func outputStatsUsingFormatString(stats *libkpod.ContainerStats) {
printf("%-64s %-16s %-32s %-16s %-24s %-24s %d\n", stats.Container, floatToPercentString(stats.CPU), combineHumanValues(stats.MemUsage, stats.MemLimit), floatToPercentString(stats.MemPerc), combineHumanValues(stats.NetInput, stats.NetOutput), combineHumanValues(stats.BlockInput, stats.BlockOutput), stats.PIDs)
}
func combineHumanValues(a, b uint64) string {
return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b)))
}
func floatToPercentString(f float64) string {
return fmt.Sprintf("%.2f %s", f, "%")
}
func getStatsOutputParams(stats *libkpod.ContainerStats) statsOutputParams {
return statsOutputParams{
Container: stats.Container,
ID: stats.Container,
CPUPerc: floatToPercentString(stats.CPU),
MemUsage: combineHumanValues(stats.MemUsage, stats.MemLimit),
MemPerc: floatToPercentString(stats.MemPerc),
NetIO: combineHumanValues(stats.NetInput, stats.NetOutput),
BlockIO: combineHumanValues(stats.BlockInput, stats.BlockOutput),
PIDs: stats.PIDs,
}
}
func outputStatsUsingTemplate(format string, params statsOutputParams) error {
tmpl, err := template.New("stats").Parse(format)
if err != nil {
return errors.Wrapf(err, "template parsing error")
}
err = tmpl.Execute(os.Stdout, params)
if err != nil {
return err
}
println()
return nil
}
func outputStatsAsJSON(stats []*libkpod.ContainerStats) error {
s, err := json.Marshal(stats)
if err != nil {
return err
}
println(s)
return nil
}
func isRunning(ctr *oci.Container) bool {
return ctr.State().Status == "running"
}
func ctrInList(idsOrNames []string) func(ctr *oci.Container) bool {
if len(idsOrNames) == 0 {
return func(*oci.Container) bool { return true }
}
return func(ctr *oci.Container) bool {
for _, idOrName := range idsOrNames {
if strings.HasPrefix(ctr.ID(), idOrName) || strings.HasSuffix(ctr.Name(), idOrName) {
return true
}
}
return false
}
}

View File

@ -1,77 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
"golang.org/x/net/context"
)
var (
defaultTimeout int64 = 10
stopFlags = []cli.Flag{
cli.Int64Flag{
Name: "timeout, t",
Usage: "Seconds to wait for stop before killing the container",
Value: defaultTimeout,
},
}
stopDescription = `
kpod stop
Stops one or more running containers. The container name or ID can be used.
A timeout to forcibly stop the container can also be set but defaults to 10
seconds otherwise.
`
stopCommand = cli.Command{
Name: "stop",
Usage: "Stop one or more containers",
Description: stopDescription,
Flags: stopFlags,
Action: stopCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
}
)
func stopCmd(c *cli.Context) error {
args := c.Args()
stopTimeout := c.Int64("timeout")
if len(args) < 1 {
return errors.Errorf("you must provide at least one container name or id")
}
if err := validateFlags(c, stopFlags); err != nil {
return err
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
defer server.Shutdown()
err = server.Update()
if err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
var lastError error
for _, container := range c.Args() {
cid, err := server.ContainerStop(context.Background(), container, stopTimeout)
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "failed to stop container %v", container)
} else {
fmt.Println(cid)
}
}
return lastError
}

View File

@ -1,77 +0,0 @@
package main
import (
"github.com/containers/image/docker/reference"
"github.com/containers/storage"
"github.com/kubernetes-incubator/cri-o/libpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
tagDescription = "Adds one or more additional names to locally-stored image"
tagCommand = cli.Command{
Name: "tag",
Usage: "Add an additional name to a local image",
Description: tagDescription,
Action: tagCmd,
ArgsUsage: "IMAGE-NAME [IMAGE-NAME ...]",
}
)
func tagCmd(c *cli.Context) error {
args := c.Args()
if len(args) < 2 {
return errors.Errorf("image name and at least one new name must be specified")
}
runtime, err := getRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not create runtime")
}
defer runtime.Shutdown(false)
img, err := runtime.GetImage(args[0])
if err != nil {
return err
}
if img == nil {
return errors.New("null image")
}
err = addImageNames(runtime, img, args[1:])
if err != nil {
return errors.Wrapf(err, "error adding names %v to image %q", args[1:], args[0])
}
return nil
}
func addImageNames(runtime *libpod.Runtime, image *storage.Image, addNames []string) error {
// Add tags to the names if applicable
names, err := expandedTags(addNames)
if err != nil {
return err
}
for _, name := range names {
if err := runtime.TagImage(image, name); err != nil {
return errors.Wrapf(err, "error adding name (%v) to image %q", name, image.ID)
}
}
return nil
}
func expandedTags(tags []string) ([]string, error) {
expandedNames := []string{}
for _, tag := range tags {
var labelName string
name, err := reference.Parse(tag)
if err != nil {
return nil, errors.Wrapf(err, "error parsing tag %q", name)
}
if _, ok := name.(reference.NamedTagged); ok {
labelName = name.String()
} else {
labelName = name.String() + ":latest"
}
expandedNames = append(expandedNames, labelName)
}
return expandedNames, nil
}

View File

@ -1,41 +0,0 @@
package main
import (
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
umountCommand = cli.Command{
Name: "umount",
Aliases: []string{"unmount"},
Usage: "Unmount a working container's root filesystem",
Description: "Unmounts a working container's root filesystem",
Action: umountCmd,
ArgsUsage: "CONTAINER-NAME-OR-ID",
}
)
func umountCmd(c *cli.Context) error {
args := c.Args()
if len(args) == 0 {
return errors.Errorf("container ID must be specified")
}
if len(args) > 1 {
return errors.Errorf("too many arguments specified")
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "Could not get config")
}
store, err := getStore(config)
if err != nil {
return err
}
err = store.Unmount(args[0])
if err != nil {
return errors.Wrapf(err, "error unmounting container %q", args[0])
}
return nil
}

View File

@ -1,58 +0,0 @@
package main
import (
"fmt"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
"os"
)
var (
unpauseDescription = `
kpod unpause
Unpauses one or more running containers. The container name or ID can be used.
`
unpauseCommand = cli.Command{
Name: "unpause",
Usage: "Unpause the processes in one or more containers",
Description: unpauseDescription,
Action: unpauseCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
}
)
func unpauseCmd(c *cli.Context) error {
args := c.Args()
if len(args) < 1 {
return errors.Errorf("you must provide at least one container name or id")
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
defer server.Shutdown()
if err := server.Update(); err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
var lastError error
for _, container := range c.Args() {
cid, err := server.ContainerUnpause(container)
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "failed to unpause container %v", container)
} else {
fmt.Println(cid)
}
}
return lastError
}

View File

@ -1,121 +0,0 @@
package main
// #include <sys/types.h>
// #include <grp.h>
// #include <pwd.h>
// #include <stdlib.h>
// #include <stdio.h>
// #include <string.h>
// typedef FILE * pFILE;
import "C"
import (
"fmt"
"os/user"
"path/filepath"
"sync"
"syscall"
"unsafe"
"github.com/pkg/errors"
)
func fopenContainerFile(rootdir, filename string) (C.pFILE, error) {
var st, lst syscall.Stat_t
ctrfile := filepath.Join(rootdir, filename)
cctrfile := C.CString(ctrfile)
defer C.free(unsafe.Pointer(cctrfile))
mode := C.CString("r")
defer C.free(unsafe.Pointer(mode))
f, err := C.fopen(cctrfile, mode)
if f == nil || err != nil {
return nil, errors.Wrapf(err, "error opening %q", ctrfile)
}
if err = syscall.Fstat(int(C.fileno(f)), &st); err != nil {
return nil, errors.Wrapf(err, "fstat(%q)", ctrfile)
}
if err = syscall.Lstat(ctrfile, &lst); err != nil {
return nil, errors.Wrapf(err, "lstat(%q)", ctrfile)
}
if st.Dev != lst.Dev || st.Ino != lst.Ino {
return nil, errors.Errorf("%q is not a regular file", ctrfile)
}
return f, nil
}
var (
lookupUser, lookupGroup sync.Mutex
)
func lookupUserInContainer(rootdir, username string) (uint64, uint64, error) {
name := C.CString(username)
defer C.free(unsafe.Pointer(name))
f, err := fopenContainerFile(rootdir, "/etc/passwd")
if err != nil {
return 0, 0, err
}
defer C.fclose(f)
lookupUser.Lock()
defer lookupUser.Unlock()
pwd := C.fgetpwent(f)
for pwd != nil {
if C.strcmp(pwd.pw_name, name) != 0 {
pwd = C.fgetpwent(f)
continue
}
return uint64(pwd.pw_uid), uint64(pwd.pw_gid), nil
}
return 0, 0, user.UnknownUserError(fmt.Sprintf("error looking up user %q", username))
}
func lookupGroupForUIDInContainer(rootdir string, userid uint64) (string, uint64, error) {
f, err := fopenContainerFile(rootdir, "/etc/passwd")
if err != nil {
return "", 0, err
}
defer C.fclose(f)
lookupUser.Lock()
defer lookupUser.Unlock()
pwd := C.fgetpwent(f)
for pwd != nil {
if uint64(pwd.pw_uid) != userid {
pwd = C.fgetpwent(f)
continue
}
return C.GoString(pwd.pw_name), uint64(pwd.pw_gid), nil
}
return "", 0, user.UnknownUserError(fmt.Sprintf("error looking up user with UID %d", userid))
}
func lookupGroupInContainer(rootdir, groupname string) (uint64, error) {
name := C.CString(groupname)
defer C.free(unsafe.Pointer(name))
f, err := fopenContainerFile(rootdir, "/etc/group")
if err != nil {
return 0, err
}
defer C.fclose(f)
lookupGroup.Lock()
defer lookupGroup.Unlock()
grp := C.fgetgrent(f)
for grp != nil {
if C.strcmp(grp.gr_name, name) != 0 {
grp = C.fgetgrent(f)
continue
}
return uint64(grp.gr_gid), nil
}
return 0, user.UnknownGroupError(fmt.Sprintf("error looking up group %q", groupname))
}

View File

@ -1,48 +0,0 @@
package main
import (
"fmt"
"runtime"
"strconv"
"time"
"github.com/urfave/cli"
)
// Overwritten at build time
var (
// gitCommit is the commit that the binary is being built from.
// It will be populated by the Makefile.
gitCommit string
// buildInfo is the time at which the binary was built
// It will be populated by the Makefile.
buildInfo string
)
// versionCmd gets and prints version info for version command
func versionCmd(c *cli.Context) error {
fmt.Println("Version: ", c.App.Version)
fmt.Println("Go Version: ", runtime.Version())
if gitCommit != "" {
fmt.Println("Git Commit: ", gitCommit)
}
if buildInfo != "" {
// Converts unix time from string to int64
buildTime, err := strconv.ParseInt(buildInfo, 10, 64)
if err != nil {
return err
}
// Prints out the build time in readable format
fmt.Println("Built: ", time.Unix(buildTime, 0).Format(time.ANSIC))
}
fmt.Println("OS/Arch: ", runtime.GOOS+"/"+runtime.GOARCH)
return nil
}
// Cli command to print out the full version of kpod
var versionCommand = cli.Command{
Name: "version",
Usage: "Display the KPOD Version Information",
Action: versionCmd,
}

View File

@ -1,62 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/kubernetes-incubator/cri-o/libkpod"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
var (
waitDescription = `
kpod wait
Block until one or more containers stop and then print their exit codes
`
waitCommand = cli.Command{
Name: "wait",
Usage: "Block on one or more containers",
Description: waitDescription,
Action: waitCmd,
ArgsUsage: "CONTAINER-NAME [CONTAINER-NAME ...]",
}
)
func waitCmd(c *cli.Context) error {
args := c.Args()
if len(args) < 1 {
return errors.Errorf("you must provide at least one container name or id")
}
config, err := getConfig(c)
if err != nil {
return errors.Wrapf(err, "could not get config")
}
server, err := libkpod.New(config)
if err != nil {
return errors.Wrapf(err, "could not get container server")
}
defer server.Shutdown()
err = server.Update()
if err != nil {
return errors.Wrapf(err, "could not update list of containers")
}
var lastError error
for _, container := range c.Args() {
returnCode, err := server.ContainerWait(container)
if err != nil {
if lastError != nil {
fmt.Fprintln(os.Stderr, lastError)
}
lastError = errors.Wrapf(err, "failed to wait for the container %v", container)
} else {
fmt.Println(returnCode)
}
}
return lastError
}

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +0,0 @@
% kpod(1) kpod-attach - See the output of pid 1 of a container or enter the container
% Dan Walsh
# kpod-attach "1" "September 2017" "kpod"
## NAME
kpod-attach - Attach to a running container
## Description
We chose not to implement the `attach` feature in `kpod` even though the upstream Docker
project has it. The upstream project has had lots of issues with attaching to running
processes that we did not want to replicate. The `kpod exec` and `kpod log` commands
offer you the same functionality far more dependably.
**Reasons to attach to the primary PID of a container:**
1) Executing commands inside of the container
We recommend that you use `kpod exec` to execute a command within a container
`kpod exec CONTAINERID /bin/sh`
2) Viewing the output stream of the primary process in the container
We recommend that you use `kpod logs` to see the output from the container
`kpod logs CONTAINERID`
## SEE ALSO
kpod(1), kpod-exec(1), kpod-logs(1)

View File

@ -1,46 +0,0 @@
% kpod(1) kpod-cp - Copy content between container's file system and the host
% Dan Walsh
# kpod-cp "1" "August 2017" "kpod"
## NAME
kpod-cp - Copy files/folders between a container and the local filesystem.
## Description
We chose not to implement the `cp` feature in `kpod` even though the upstream Docker
project has it. We have a much stronger capability. Using standard kpod-mount
and kpod-umount, we can take advantage of the entire linux tool chain, rather
then just cp.
If a user wants to copy contents out of a container or into a container, they
can execute a few simple commands.
You can copy from the container's file system to the local machine or the
reverse, from the local filesystem to the container.
If you want to copy the /etc/foobar directory out of a container and onto /tmp
on the host, you could execute the following commands:
mnt=$(kpod mount CONTAINERID)
cp -R ${mnt}/etc/foobar /tmp
kpod umount CONTAINERID
If you want to untar a tar ball into a container, you can execute these commands:
mnt=$(kpod mount CONTAINERID)
tar xf content.tgz -C ${mnt}
kpod umount CONTAINERID
One last example, if you want to install a package into a container that
does not have dnf installed, you could execute something like:
mnt=$(kpod mount CONTAINERID)
dnf install --installroot=${mnt} httpd
chroot ${mnt} rm -rf /var/log/dnf /var/cache/dnf
kpod umount CONTAINERID
This shows that using `kpod mount` and `kpod umount` you can use all of the
standard linux tools for moving files into and out of containers, not just
the cp command.
## SEE ALSO
kpod(1), kpod-mount(1), kpod-umount(1)

View File

@ -1,594 +0,0 @@
% kpod(1) kpod-create - Create a new container
% Dan Walsh
kpod-create - Create a new container
# SYNOPSIS
**kpod create** [*options* [...]] IMAGE [COMMAND] [ARG...]
# DESCRIPTION
Creates a writeable container layer over the specified image and prepares it for
running the specified command. The container ID is then printed to STDOUT. This
is similar to **kpod run -d** except the container is never started. You can
then use the **kpod start <container_id>** command to start the container at
any point.
The initial status of the container created with **kpod create** is 'created'.
# OPTIONS
**--add-host**=[]
Add a custom host-to-IP mapping (host:ip)
Add a line to /etc/hosts. The format is hostname:ip. The **--add-host**
option can be set multiple times.
**-a**, **--attach**=[]
Attach to STDIN, STDOUT or STDERR.
In foreground mode (the default when **-d**
is not specified), **kpod run** can start the process in the container
and attach the console to the process's standard input, output, and standard
error. It can even pretend to be a TTY (this is what most commandline
executables expect) and pass along signals. The **-a** option can be set for
each of stdin, stdout, and stderr.
**--blkio-weight**=*0*
Block IO weight (relative weight) accepts a weight value between 10 and 1000.
**--blkio-weight-device**=[]
Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`).
**--cap-add**=[]
Add Linux capabilities
**--cap-drop**=[]
Drop Linux capabilities
**--cgroup-parent**=""
Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
**--cidfile**=""
Write the container ID to the file
**--cpu-count**=*0*
Limit the number of CPUs available for execution by the container.
On Windows Server containers, this is approximated as a percentage of total CPU usage.
On Windows Server containers, the processor resource controls are mutually exclusive, the order of precedence is CPUCount first, then CPUShares, and CPUPercent last.
**--cpu-period**=*0*
Limit the CPU CFS (Completely Fair Scheduler) period
Limit the container's CPU usage. This flag tell the kernel to restrict the container's CPU usage to the period you specify.
**--cpu-quota**=*0*
Limit the CPU CFS (Completely Fair Scheduler) quota
Limit the container's CPU usage. By default, containers run with the full
CPU resource. This flag tell the kernel to restrict the container's CPU usage
to the quota you specify.
**--cpu-rt-period**=0
Limit the CPU real-time period in microseconds
Limit the container's Real Time CPU usage. This flag tell the kernel to restrict the container's Real Time CPU usage to the period you specify.
**--cpu-rt-runtime**=0
Limit the CPU real-time runtime in microseconds
Limit the containers Real Time CPU usage. This flag tells the kernel to limit the amount of time in a given CPU period Real Time tasks may consume. Ex:
Period of 1,000,000us and Runtime of 950,000us means that this container could consume 95% of available CPU and leave the remaining 5% to normal priority tasks.
The sum of all runtimes across containers cannot exceed the amount allotted to the parent cgroup.
**--cpu-shares**=*0*
CPU shares (relative weight)
By default, all containers get the same proportion of CPU cycles. This proportion
can be modified by changing the container's CPU share weighting relative
to the weighting of all other running containers.
To modify the proportion from the default of 1024, use the **--cpu-shares**
flag to set the weighting to 2 or higher.
The proportion will only apply when CPU-intensive processes are running.
When tasks in one container are idle, other containers can use the
left-over CPU time. The actual amount of CPU time will vary depending on
the number of containers running on the system.
For example, consider three containers, one has a cpu-share of 1024 and
two others have a cpu-share setting of 512. When processes in all three
containers attempt to use 100% of CPU, the first container would receive
50% of the total CPU time. If you add a fourth container with a cpu-share
of 1024, the first container only gets 33% of the CPU. The remaining containers
receive 16.5%, 16.5% and 33% of the CPU.
On a multi-core system, the shares of CPU time are distributed over all CPU
cores. Even if a container is limited to less than 100% of CPU time, it can
use 100% of each individual CPU core.
For example, consider a system with more than three cores. If you start one
container **{C0}** with **-c=512** running one process, and another container
**{C1}** with **-c=1024** running two processes, this can result in the following
division of CPU shares:
PID container CPU CPU share
100 {C0} 0 100% of CPU0
101 {C1} 1 100% of CPU1
102 {C1} 2 100% of CPU2
**--cpus**=0.0
Number of CPUs. The default is *0.0* which means no limit.
**--cpuset-cpus**=""
CPUs in which to allow execution (0-3, 0,1)
**--cpuset-mems**=""
Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
If you have four memory nodes on your system (0-3), use `--cpuset-mems=0,1`
then processes in your container will only use memory from the first
two memory nodes.
**-d**, **--detach**=*true*|*false*
Detached mode: run the container in the background and print the new container ID. The default is *false*.
At any time you can run **kpod ps** in
the other shell to view a list of the running containers. You can reattach to a
detached container with **kpod attach**. If you choose to run a container in
the detached mode, then you cannot use the **-rm** option.
When attached in the tty mode, you can detach from the container (and leave it
running) using a configurable key sequence. The default sequence is `CTRL-p CTRL-q`.
You configure the key sequence using the **--detach-keys** option or a configuration file.
See **config-json(5)** for documentation on using a configuration file.
**--detach-keys**=""
Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
**--device**=[]
Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
**--device-read-bps**=[]
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
**--device-read-iops**=[]
Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)
**--device-write-bps**=[]
Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)
**--device-write-iops**=[]
Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)
**--dns**=[]
Set custom DNS servers
This option can be used to override the DNS
configuration passed to the container. Typically this is necessary when the
host DNS configuration is invalid for the container (e.g., 127.0.0.1). When this
is the case the **--dns** flags is necessary for every run.
**--dns-option**=[]
Set custom DNS options
**--dns-search**=[]
Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)
**--entrypoint**=""
Overwrite the default ENTRYPOINT of the image
This option allows you to overwrite the default entrypoint of the image.
The ENTRYPOINT of an image is similar to a COMMAND
because it specifies what executable to run when the container starts, but it is
(purposely) more difficult to override. The ENTRYPOINT gives a container its
default nature or behavior, so that when you set an ENTRYPOINT you can run the
container as if it were that binary, complete with default options, and you can
pass in more options via the COMMAND. But, sometimes an operator may want to run
something else inside the container, so you can override the default ENTRYPOINT
at runtime by using a **--entrypoint** and a string to specify the new
ENTRYPOINT.
**-e**, **--env**=[]
Set environment variables
This option allows you to specify arbitrary
environment variables that are available for the process that will be launched
inside of the container.
**--env-file**=[]
Read in a line delimited file of environment variables
**--expose**=[]
Expose a port, or a range of ports (e.g. --expose=3300-3310) to set up port redirection
on the host system.
**--group-add**=[]
Add additional groups to run as
**--hostname**=""
Container host name
Sets the container host name that is available inside the container.
**--help**
Print usage statement
**-i**, **--interactive**=*true*|*false*
Keep STDIN open even if not attached. The default is *false*.
**--ip**=""
Sets the container's interface IPv4 address (e.g. 172.23.0.9)
It can only be used in conjunction with **--network** for user-defined networks
**--ip6**=""
Sets the container's interface IPv6 address (e.g. 2001:db8::1b99)
It can only be used in conjunction with **--network** for user-defined networks
**--ipc**=""
Default is to create a private IPC namespace (POSIX SysV IPC) for the container
'container:<name|id>': reuses another container shared memory, semaphores and message queues
'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure.
**--kernel-memory**=""
Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)
Constrains the kernel memory available to a container. If a limit of 0
is specified (not using `--kernel-memory`), the container's kernel memory
is not limited. If you specify a limit, it may be rounded up to a multiple
of the operating system's page size and the value can be very large,
millions of trillions.
**-l**, **--label**=[]
Add metadata to a container (e.g., --label com.example.key=value)
**--label-file**=[]
Read in a line delimited file of labels
**--link-local-ip**=[]
Add one or more link-local IPv4/IPv6 addresses to the container's interface
**--log-driver**="*json-file*|*syslog*|*journald*|*gelf*|*fluentd*|*awslogs*|*splunk*|*etwlogs*|*gcplogs*|*none*"
Logging driver for the container. Default is defined by daemon `--log-driver` flag.
**Warning**: the `kpod logs` command works only for the `json-file` and
`journald` logging drivers.
**--log-opt**=[]
Logging driver specific options.
**--mac-address**=""
Container MAC address (e.g. 92:d0:c6:0a:29:33)
Remember that the MAC address in an Ethernet network must be unique.
The IPv6 link-local address will be based on the device's MAC address
according to RFC4862.
**-m**, **--memory**=""
Memory limit (format: <number>[<unit>], where unit = b, k, m or g)
Allows you to constrain the memory available to a container. If the host
supports swap memory, then the **-m** memory setting can be larger than physical
RAM. If a limit of 0 is specified (not using **-m**), the container's memory is
not limited. The actual limit may be rounded up to a multiple of the operating
system's page size (the value would be very large, that's millions of trillions).
**--memory-reservation**=""
Memory soft limit (format: <number>[<unit>], where unit = b, k, m or g)
After setting memory reservation, when the system detects memory contention
or low memory, containers are forced to restrict their consumption to their
reservation. So you should always set the value below **--memory**, otherwise the
hard limit will take precedence. By default, memory reservation will be the same
as memory limit.
**--memory-swap**="LIMIT"
A limit value equal to memory plus swap. Must be used with the **-m**
(**--memory**) flag. The swap `LIMIT` should always be larger than **-m**
(**--memory**) value. By default, the swap `LIMIT` will be set to double
the value of --memory.
The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes),
`k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you don't specify a
unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
**--memory-swappiness**=""
Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
**--name**=""
Assign a name to the container
The operator can identify a container in three ways:
UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
UUID short identifier (“f78375b1c487”)
Name (“jonah”)
kpod generates a UUID for each container, and if a name is not assigned
to the container with **--name** then the daemon will also generate a random
string name. The name is useful when defining links (see **--link**) (or any
other place you need to identify a container). This works for both background
and foreground containers.
**--network**="*bridge*"
Set the Network mode for the container
'bridge': create a network stack on the default bridge
'none': no networking
'container:<name|id>': reuse another container's network stack
'host': use the kpod host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
'<network-name>|<network-id>': connect to a user-defined network
**--network-alias**=[]
Add network-scoped alias for the container
**--oom-kill-disable**=*true*|*false*
Whether to disable OOM Killer for the container or not.
**--oom-score-adj**=""
Tune the host's OOM preferences for containers (accepts -1000 to 1000)
**--pid**=""
Set the PID mode for the container
Default is to create a private PID namespace for the container
'container:<name|id>': join another container's PID namespace
'host': use the host's PID namespace for the container. Note: the host mode gives the container full access to local PID and is therefore considered insecure.
**--pids-limit**=""
Tune the container's pids limit. Set `-1` to have unlimited pids for the container.
**--pod**=""
Run container in an existing pod
**--privileged**=*true*|*false*
Give extended privileges to this container. The default is *false*.
By default, kpod containers are
“unprivileged” (=false) and cannot, for example, modify parts of the kernel.
This is because by default a container is not allowed to access any devices.
A “privileged” container is given access to all devices.
When the operator executes **kpod run --privileged**, kpod enables access
to all devices on the host as well as set turn off most of the security messurs
protecting the host from the container.
**-p**, **--publish**=[]
Publish a container's port, or range of ports, to the host
Format: `ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort`
Both hostPort and containerPort can be specified as a range of ports.
When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range.
(e.g., `kpod run -p 1234-1236:1222-1224 --name thisWorks -t busybox`
but not `kpod run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanRangeHostPorts -t busybox`)
With ip: `kpod run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage`
Use `kpod port` to see the actual mapping: `kpod port CONTAINER $CONTAINERPORT`
**-P**, **--publish-all**=*true*|*false*
Publish all exposed ports to random ports on the host interfaces. The default is *false*.
When set to true publish all exposed ports to the host interfaces. The
default is false. If the operator uses -P (or -p) then kpod will make the
exposed port accessible on the host and the ports will be available to any
client that can reach the host. When using -P, kpod will bind any exposed
port to a random port on the host within an *ephemeral port range* defined by
`/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host
ports and the exposed ports, use `kpod port`.
**--read-only**=*true*|*false*
Mount the container's root filesystem as read only.
By default a container will have its root filesystem writable allowing processes
to write files anywhere. By specifying the `--read-only` flag the container will have
its root filesystem mounted as read only prohibiting any writes.
**--rm**=*true*|*false*
Automatically remove the container when it exits. The default is *false*.
`--rm` flag can work together with `-d`, and auto-removal will be done on daemon side. Note that it's
incompatible with any restart policy other than `none`.
**--security-opt**=[]
Security Options
"label=user:USER" : Set the label user for the container
"label=role:ROLE" : Set the label role for the container
"label=type:TYPE" : Set the label type for the container
"label=level:LEVEL" : Set the label level for the container
"label=disable" : Turn off label confinement for the container
"no-new-privileges" : Disable container processes from gaining additional privileges
"seccomp=unconfined" : Turn off seccomp confinement for the container
"seccomp=profile.json : White listed syscalls seccomp Json file to be used as a seccomp filter
"apparmor=unconfined" : Turn off apparmor confinement for the container
"apparmor=your-profile" : Set the apparmor confinement profile for the container
**--shm-size**=""
Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`.
Unit is optional and can be `b` (bytes), `k` (kilobytes), `m`(megabytes), or `g` (gigabytes).
If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`.
**--sig-proxy**=*true*|*false*
Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied. The default is *true*.
**--stop-signal**=*SIGTERM*
Signal to stop a container. Default is SIGTERM.
**--stop-timeout**=*10*
Timeout (in seconds) to stop a container. Default is 10.
**--storage-opt**=[]
Storage driver options per container
$ kpod create -it --storage-opt size=120G fedora /bin/bash
This (size) will allow to set the container rootfs size to 120G at creation time.
This option is only available for the `devicemapper`, `btrfs`, `overlay2` and `zfs` graph drivers.
For the `devicemapper`, `btrfs` and `zfs` storage drivers, user cannot pass a size less than the Default BaseFS Size.
For the `overlay2` storage driver, the size option is only available if the backing fs is `xfs` and mounted with the `pquota` mount option.
Under these conditions, user can pass any size less then the backing fs size.
**--sysctl**=SYSCTL
Configure namespaced kernel parameters at runtime
IPC Namespace - current sysctls allowed:
kernel.msgmax, kernel.msgmnb, kernel.msgmni, kernel.sem, kernel.shmall, kernel.shmmax, kernel.shmmni, kernel.shm_rmid_forced
Sysctls beginning with fs.mqueue.*
Note: if you use the --ipc=host option these sysctls will not be allowed.
Network Namespace - current sysctls allowed:
Sysctls beginning with net.*
Note: if you use the --network=host option these sysctls will not be allowed.
**--tmpfs**=[] Create a tmpfs mount
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:
$ kpod run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image
This command mounts a `tmpfs` at `/tmp` within the container. The supported mount
options are the same as the Linux default `mount` flags. If you do not specify
any options, the systems uses the following options:
`rw,noexec,nosuid,nodev,size=65536k`.
**-t**, **--tty**=*true*|*false*
Allocate a pseudo-TTY. The default is *false*.
When set to true kpod will allocate a pseudo-tty and attach to the standard
input of the container. This can be used, for example, to run a throwaway
interactive shell. The default is false.
Note: The **-t** option is incompatible with a redirection of the kpod client
standard input.
**--ulimit**=[]
Ulimit options
**-u**, **--user**=""
Sets the username or UID used and optionally the groupname or GID for the specified command.
The followings examples are all valid:
--user [user | user:group | uid | uid:gid | user:gid | uid:group ]
Without this argument the command will be run as root in the container.
**--userns**=""
Set the usernamespace mode for the container when `userns-remap` option is enabled.
**host**: use the host usernamespace and enable all privileged options (e.g., `pid=host` or `--privileged`).
**--uts**=*host*
Set the UTS mode for the container
**host**: use the host's UTS namespace inside the container.
Note: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
**-v**|**--volume**[=*[[HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, kpod
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the kpod
container. If 'HOST-DIR' is omitted, kpod automatically creates the new
volume on the host. The `OPTIONS` are a comma delimited list and can be:
* [rw|ro]
* [z|Z]
* [`[r]shared`|`[r]slave`|`[r]private`]
The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The `HOST-DIR`
can be an absolute path or a `name` value. A `name` value must start with an
alphanumeric character, followed by `a-z0-9`, `_` (underscore), `.` (period) or
`-` (hyphen). An absolute path starts with a `/` (forward slash).
If you supply a `HOST-DIR` that is an absolute path, kpod bind-mounts to the
path you specify. If you supply a `name`, kpod creates a named volume by that
`name`. For example, you can specify either `/foo` or `foo` for a `HOST-DIR`
value. If you supply the `/foo` value, kpod creates a bind-mount. If you
supply the `foo` specification, kpod creates a named volume.
You can specify multiple **-v** options to mount one or more mounts to a
container. To use these same mounts in other containers, specify the
**--volumes-from** option also.
You can add `:ro` or `:rw` suffix to a volume to mount it read-only or
read-write mode, respectively. By default, the volumes are mounted read-write.
See examples.
Labeling systems like SELinux require that proper labels are placed on volume
content mounted into a container. Without a label, the security system might
prevent the processes running inside the container from using the content. By
default, kpod does not change the labels set by the OS.
To change a label in the container context, you can add either of two suffixes
`:z` or `:Z` to the volume mount. These suffixes tell kpod to relabel file
objects on the shared volumes. The `z` option tells kpod that two containers
share the volume content. As a result, kpod labels the content with a shared
content label. Shared volume labels allow all containers to read/write content.
The `Z` option tells kpod to label the content with a private unshared label.
Only the current container can use a private volume.
By default bind mounted volumes are `private`. That means any mounts done
inside container will not be visible on host and vice-a-versa. One can change
this behavior by specifying a volume mount propagation property. Making a
volume `shared` mounts done under that volume inside container will be
visible on host and vice-a-versa. Making a volume `slave` enables only one
way mount propagation and that is mounts done on host under that volume
will be visible inside container but not the other way around.
To control mount propagation property of volume one can use `:[r]shared`,
`:[r]slave` or `:[r]private` propagation flag. Propagation property can
be specified only for bind mounted volumes and not for internal volumes or
named volumes. For mount propagation to work source mount point (mount point
where source dir is mounted on) has to have right propagation properties. For
shared volumes, source mount point has to be shared. And for slave volumes,
source mount has to be either shared or slave.
Use `df <source-dir>` to figure out the source mount and then use
`findmnt -o TARGET,PROPAGATION <source-mount-dir>` to figure out propagation
properties of source mount. If `findmnt` utility is not available, then one
can look at mount entry for source mount point in `/proc/self/mountinfo`. Look
at `optional fields` and see if any propagaion properties are specified.
`shared:X` means mount is `shared`, `master:X` means mount is `slave` and if
nothing is there that means mount is `private`.
To change propagation properties of a mount point use `mount` command. For
example, if one wants to bind mount source directory `/foo` one can do
`mount --bind /foo /foo` and `mount --make-private --make-shared /foo`. This
will convert /foo into a `shared` mount point. Alternatively one can directly
change propagation properties of source mount. Say `/` is source mount for
`/foo`, then use `mount --make-shared /` to convert `/` into a `shared` mount.
To disable automatic copying of data from the container path to the volume, use
the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
**--volumes-from**=[]
Mount volumes from the specified container(s)
Mounts already mounted volumes from a source container onto another
container. You must supply the source's container-id. To share
a volume, use the **--volumes-from** option when running
the target container. You can share volumes even if the source container
is not running.
By default, kpod mounts the volumes in the same mode (read-write or
read-only) as it is mounted in the source container. Optionally, you
can change this by suffixing the container-id with either the `:ro` or
`:rw ` keyword.
If the location of the volume from the source container overlaps with
data residing on a target container, then the volume hides
that data on the target.
**-w**, **--workdir**=""
Working directory inside the container
The default working directory for running binaries within a container is the root directory (/).
The image developer can set a different default with the WORKDIR instruction. The operator
can override the working directory by using the **-w** option.
# EXAMPLES
# HISTORY
August 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
September 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
November 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
October 2017, converted from Docker documentation to kpod by Dan Walsh for kpod <dwalsh@redhat.com>

View File

@ -1,45 +0,0 @@
% kpod(1) kpod-diff - Inspect changes on a container or image's filesystem
% Dan Walsh
# kpod-diff "1" "August 2017" "kpod"
## NAME
kpod diff - Inspect changes on a container or image's filesystem
## SYNOPSIS
**kpod** **diff** [*options* [...]] NAME
## DESCRIPTION
Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer
## OPTIONS
**--format**
Alter the output into a different format. The only valid format for diff is `json`.
## EXAMPLE
kpod diff redis:alpine
C /usr
C /usr/local
C /usr/local/bin
A /usr/local/bin/docker-entrypoint.sh
kpod diff --format json redis:alpine
{
"changed": [
"/usr",
"/usr/local",
"/usr/local/bin"
],
"added": [
"/usr/local/bin/docker-entrypoint.sh"
]
}
## SEE ALSO
kpod(1)
## HISTORY
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>

View File

@ -1,44 +0,0 @@
% kpod(1) kpod-export - Simple tool to export a container's filesystem as a tarball
% Urvashi Mohnani
# kpod-export "1" "July 2017" "kpod"
## NAME
kpod-export - Export container's filesystem contents as a tar archive
## SYNOPSIS
**kpod export**
**CONTAINER**
[**--output**|**-o**]
[**--help**|**-h**]
## DESCRIPTION
**kpod export** exports the filesystem of a container and saves it as a tarball
on the local machine. **kpod export** writes to STDOUT by default and can be
redirected to a file using the **output flag**.
**kpod [GLOBAL OPTIONS]**
**kpod export [GLOBAL OPTIONS]**
**kpod export [OPTIONS] CONTAINER**
## OPTIONS
**--output, -o**
Write to a file, default is STDOUT
## EXAMPLES
```
# kpod export -o redis-container.tar 883504668ec465463bc0fe7e63d53154ac3b696ea8d7b233748918664ea90e57
```
```
# kpod export > redis-container.tar 883504668ec465463bc0fe7e63d53154ac3b696ea8d7b233748918664ea90e57
```
## SEE ALSO
kpod(1), kpod-import(1), crio(8), crio.conf(5)
## HISTORY
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,106 +0,0 @@
% kpod(1) kpod-history - Simple tool to view the history of an image
% Urvashi Mohnani
% kpod-history "1" "JULY 2017" "kpod"
## NAME
kpod-history - Shows the history of an image
## SYNOPSIS
**kpod history**
**IMAGE[:TAG|DIGEST]**
[**--human**|**-H**]
[**--no-trunc**]
[**--quiet**|**-q**]
[**--format**]
[**--help**|**-h**]
## DESCRIPTION
**kpod history** displays the history of an image by printing out information
about each layer used in the image. The information printed out for each layer
include Created (time and date), Created By, Size, and Comment. The output can
be truncated or not using the **--no-trunc** flag. If the **--human** flag is
set, the time of creation and size are printed out in a human readable format.
The **--quiet** flag displays the ID of the image only when set and the **--format**
flag is used to print the information using the Go template provided by the user.
Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** |
| --------------- | ----------------------------------------------------------------------------- |
| .ID | Image ID |
| .Created | if **--human**, time elapsed since creation, otherwise time stamp of creation |
| .CreatedBy | Command used to create the layer |
| .Size | Size of layer on disk |
| .Comment | Comment for the layer |
**kpod [GLOBAL OPTIONS]**
**kpod history [GLOBAL OPTIONS]**
**kpod history [OPTIONS] IMAGE[:TAG|DIGEST]**
## OPTIONS
**--human, -H**
Display sizes and dates in human readable format
**--no-trunc**
Do not truncate the output
**--quiet, -q**
Print the numeric IDs only
**--format**
Alter the output for a format like 'json' or a Go template.
## EXAMPLES
```
# kpod history debian
ID CREATED CREATED BY SIZE COMMENT
b676ca55e4f2c 9 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0 B
<missing> 9 weeks ago /bin/sh -c #(nop) ADD file:ebba725fb97cea4... 45.14 MB
```
```
# kpod history --no-trunc=true --human=false debian
ID CREATED CREATED BY SIZE COMMENT
b676ca55e4f2c 2017-07-24T16:52:55Z /bin/sh -c #(nop) CMD ["bash"] 0
<missing> 2017-07-24T16:52:54Z /bin/sh -c #(nop) ADD file:ebba725fb97cea4... 45142935
```
```
# kpod history --format "{{.ID}} {{.Created}}" debian
b676ca55e4f2c 9 weeks ago
<missing> 9 weeks ago
```
```
# kpod history --format json debian
[
{
"id": "b676ca55e4f2c0ce53d0636438c5372d3efeb5ae99b676fa5a5d1581bad46060",
"created": "2017-07-24T16:52:55.195062314Z",
"createdBy": "/bin/sh -c #(nop) CMD [\"bash\"]",
"size": 0,
"comment": ""
},
{
"id": "b676ca55e4f2c0ce53d0636438c5372d3efeb5ae99b676fa5a5d1581bad46060",
"created": "2017-07-24T16:52:54.898893387Z",
"createdBy": "/bin/sh -c #(nop) ADD file:ebba725fb97cea45d0b1b35ccc8144e766fcfc9a78530465c23b0c4674b14042 in / ",
"size": 45142935,
"comment": ""
}
]
```
## history
Show the history of an image
## SEE ALSO
kpod(1), crio(8), crio.conf(5)
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,60 +0,0 @@
% kpod(1) kpod-images - List images in local storage
% Dan Walsh
# kpod-images "1" "March 2017" "kpod"
## NAME
kpod images - List images in local storage
## SYNOPSIS
**kpod** **images** [*options* [...]]
## DESCRIPTION
Displays locally stored images, their names, and their IDs.
## OPTIONS
**--digests**
Show image digests
**--filter, -f=[]**
Filter output based on conditions provided (default [])
**--format**
Change the default output format. This can be of a supported type like 'json'
or a Go template.
**--noheading, -n**
Omit the table headings from the listing of images.
**--no-trunc, --notruncate**
Do not truncate output.
**--quiet, -q**
Lists only the image IDs.
## EXAMPLE
kpod images
kpod images --quiet
kpod images -q --noheading --notruncate
kpod images --format json
kpod images --format "{{.ID}}"
kpod images --filter dangling=true
## SEE ALSO
kpod(1)
## HISTORY
March 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>

View File

@ -1,36 +0,0 @@
% kpod(1) kpod-version - Simple tool to view version information
% Vincent Batts
% kpod-version "1" "JULY 2017" "kpod"
## NAME
kpod-info - Display system information
## SYNOPSIS
**kpod** **info** [*options* [...]]
## DESCRIPTION
Information display here pertain to the host, current storage stats, and build of kpod. Useful for the user and when reporting issues.
## OPTIONS
**--debug, -D**
Show additional information
**--format**
Change output format to "json" or a Go template.
## EXAMPLE
`kpod info`
`kpod info --debug --format json| jq .host.kernel`
## SEE ALSO
crio(8), crio.conf(5)

View File

@ -1,171 +0,0 @@
% kpod(1) kpod-inspect - Display a container or image's configuration
% Dan Walsh
# kpod-inspect "1" "July 2017" "kpod"
## NAME
kpod inspect - Display a container or image's configuration
## SYNOPSIS
**kpod** **inspect** [*options* [...]] name
## DESCRIPTION
This displays the low-level information on containers and images identified by name or ID. By default, this will render all results in a JSON array. If the container and image have the same name, this will return container JSON for unspecified type. If a format is specified, the given template will be executed for each result.
## OPTIONS
**--type, t="TYPE"**
Return data on items of the specified type. Type can be 'container', 'image' or 'all' (default: all)
**--format, -f="FORMAT"**
Format the output using the given Go template
**--size**
Display the total file size if the type is a container
## EXAMPLE
kpod inspect redis:alpine
{
"ArgsEscaped": true,
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"redis-server\"]"
],
"Domainname": "",
"Entrypoint": [
"entrypoint.sh"
],
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"REDIS_VERSION=3.2.9",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.9.tar.gz",
"REDIS_DOWNLOAD_SHA=6eaacfa983b287e440d0839ead20c2231749d5d6b78bbe0e0ffa3a890c59ff26"
],
"ExposedPorts": {
"6379/tcp": {}
},
"Hostname": "e1ede117fb1e",
"Image": "sha256:75e877aa15b534396de82d385386cc4dda7819d5cbb018b9f97b77aeb8f4b55a",
"Labels": {},
"OnBuild": [],
"OpenStdin": false,
"StdinOnce": false,
"Tty": false,
"User": "",
"Volumes": {
"/data": {}
},
"WorkingDir": "/data"
}
{
"ID": "b3f2436bdb978c1d33b1387afb5d7ba7e3243ed2ce908db431ac0069da86cb45",
"Names": [
"docker.io/library/redis:alpine"
],
"Digests": [
"sha256:88286f41530e93dffd4b964e1db22ce4939fffa4a4c665dab8591fbab03d4926",
"sha256:07b1ac6c7a5068201d8b63a09bb15358ec1616b813ef3942eb8cc12ae191227f",
"sha256:91e2e140ea27b3e89f359cd9fab4ec45647dda2a8e5fb0c78633217d9dca87b5",
"sha256:08957ceaa2b3be874cde8d7fa15c274300f47185acd62bca812a2ffb6228482d",
"sha256:acd3d12a6a79f772961a771f678c1a39e1f370e7baeb9e606ad8f1b92572f4ab",
"sha256:4ad88df090801e8faa8cf0be1f403b77613d13e11dad73f561461d482f79256c",
"sha256:159ac12c79e1a8d85dfe61afff8c64b96881719139730012a9697f432d6b739a"
],
"Parent": "",
"Comment": "",
"Created": "2017-06-28T22:14:36.35280993Z",
"Container": "ba8d6c6b0d7fdd201fce404236136b44f3bfdda883466531a3d1a1f87906770b",
"ContainerConfig": {
"Hostname": "e1ede117fb1e",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"REDIS_VERSION=3.2.9",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.9.tar.gz",
"REDIS_DOWNLOAD_SHA=6eaacfa983b287e440d0839ead20c2231749d5d6b78bbe0e0ffa3a890c59ff26"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"redis-server\"]"
],
"ArgsEscaped": true,
"Image": "sha256:75e877aa15b534396de82d385386cc4dda7819d5cbb018b9f97b77aeb8f4b55a",
"Volumes": {
"/data": {}
},
"WorkingDir": "/data",
"Entrypoint": [
"entrypoint.sh"
],
"Labels": {},
"OnBuild": []
},
"Author": "",
"Config": {
"ExposedPorts": {
"6379/tcp": {}
},
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"REDIS_VERSION=3.2.9",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.9.tar.gz",
"REDIS_DOWNLOAD_SHA=6eaacfa983b287e440d0839ead20c2231749d5d6b78bbe0e0ffa3a890c59ff26"
],
"Entrypoint": [
"entrypoint.sh"
],
"Cmd": [
"redis-server"
],
"Volumes": {
"/data": {}
},
"WorkingDir": "/data"
},
"Architecture": "amd64",
"OS": "linux",
"Size": 3965955,
"VirtualSize": 19808086,
"GraphDriver": {
"Name": "overlay",
"Data": {
"MergedDir": "/var/lib/containers/storage/overlay/2059d805c90e034cb773d9722232ef018a72143dd31113b470fb876baeccd700/merged",
"UpperDir": "/var/lib/containers/storage/overlay/2059d805c90e034cb773d9722232ef018a72143dd31113b470fb876baeccd700/diff",
"WorkDir": "/var/lib/containers/storage/overlay/2059d805c90e034cb773d9722232ef018a72143dd31113b470fb876baeccd700/work"
}
},
"RootFS": {
"type": "layers",
"diff_ids": [
"sha256:5bef08742407efd622d243692b79ba0055383bbce12900324f75e56f589aedb0",
"sha256:c92a8fc997217611d0bfc9ff14d7ec00350ca564aef0ecbf726624561d7872d7",
"sha256:d4c406dea37a107b0cccb845611266a146725598be3e82ba31c55c08d1583b5a",
"sha256:8b4fa064e2b6c03a6c37089b0203f167375a8b49259c0ad7cb47c8c1e58b3fa0",
"sha256:c393e3d0b00ddf6b4166f1e2ad68245e08e9e3be0a0567a36d0a43854f03bfd6",
"sha256:38047b4117cb8bb3bba82991daf9a4e14ba01f9f66c1434d4895a7e96f67d8ba"
]
}
}
## SEE ALSO
kpod(1)

View File

@ -1,33 +0,0 @@
% kpod(1) kpod-kill- Kill one or more containers with a signal
% Brent Baude
# kpod-kill"1" "September 2017" "kpod"
## NAME
kpod kill - Kills one or more containers with a signal
## SYNOPSIS
**kpod kill [OPTIONS] CONTAINER [...]**
## DESCRIPTION
The main process inside each container specified will be sent SIGKILL, or any signal specified with option --signal.
## OPTIONS
**--signal, s**
Signal to send to the container. For more information on Linux signals, refer to *man signal(7)*.
## EXAMPLE
kpod kill mywebserver
kpod kill 860a4b23
kpod kill --signal TERM 860a4b23
## SEE ALSO
kpod(1), kpod-stop(1)
## HISTORY
September 2017, Originally compiled by Brent Baude <bbaude@redhat.com>

View File

@ -1,76 +0,0 @@
% kpod(1) kpod-load - Simple tool to load an image from an archive to containers-storage
% Urvashi Mohnani
# kpod-load "1" "July 2017" "kpod"
## NAME
kpod-load - Load an image from docker archive
## SYNOPSIS
**kpod load**
**NAME[:TAG|@DIGEST]**
[**--input**|**-i**]
[**--quiet**|**-q**]
[**--help**|**-h**]
## DESCRIPTION
**kpod load** copies an image from either **docker-archive** or **oci-archive** stored
on the local machine. **kpod load** reads from stdin by default or a file if the **input** flag is set.
The **quiet** flag suppresses the output when set.
**kpod [GLOBAL OPTIONS]**
**kpod load [GLOBAL OPTIONS]**
**kpod load [OPTIONS] NAME[:TAG|@DIGEST]**
## OPTIONS
**--input, -i**
Read from archive file, default is STDIN
**--quiet, -q**
Suppress the output
**--signature-policy="PATHNAME"**
Pathname of a signature policy file to use. It is not recommended that this
option be used, as the default behavior of using the system-wide default policy
(frequently */etc/containers/policy.json*) is most often preferred
## EXAMPLES
```
# kpod load --quiet -i fedora.tar
```
```
# kpod load -q --signature-policy /etc/containers/policy.json -i fedora.tar
```
```
# kpod load < fedora.tar
Getting image source signatures
Copying blob sha256:5bef08742407efd622d243692b79ba0055383bbce12900324f75e56f589aedb0
0 B / 4.03 MB [---------------------------------------------------------------]
Copying config sha256:7328f6f8b41890597575cbaadc884e7386ae0acc53b747401ebce5cf0d624560
0 B / 1.48 KB [---------------------------------------------------------------]
Writing manifest to image destination
Storing signatures
```
```
# cat fedora.tar | kpod load
Getting image source signatures
Copying blob sha256:5bef08742407efd622d243692b79ba0055383bbce12900324f75e56f589aedb0
0 B / 4.03 MB [---------------------------------------------------------------]
Copying config sha256:7328f6f8b41890597575cbaadc884e7386ae0acc53b747401ebce5cf0d624560
0 B / 1.48 KB [---------------------------------------------------------------]
Writing manifest to image destination
Storing signatures
```
## SEE ALSO
kpod(1), kpod-save(1), crio(8), crio.conf(5)
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,65 +0,0 @@
% kpod(1) kpod-login - Simple tool to login to a registry server
% Urvashi Mohnani
# kpod-login "1" "August 2017" "kpod"
## NAME
kpod-login - Login to a container registry
## SYNOPSIS
**kpod login**
[**--help**|**-h**]
[**--authfile**]
[**--user**|**-u**]
[**--password**|**-p**]
**REGISTRY**
## DESCRIPTION
**kpod login** logs into a specified registry server with the correct username
and password. **kpod login** reads in the username and password from STDIN.
The username and password can also be set using the **username** and **password** flags.
The path of the authentication file can be specified by the user by setting the **authfile**
flag. The default path used is **${XDG\_RUNTIME_DIR}/containers/auth.json**.
**kpod [GLOBAL OPTIONS]**
**kpod login [GLOBAL OPTIONS]**
**kpod login [OPTIONS] REGISTRY [GLOBAL OPTIONS]**
## OPTIONS
**--password, -p**
Password for registry
**--username, -u**
Username for registry
**--authfile**
Path of the authentication file. Default is ${XDG_\RUNTIME\_DIR}/containers/auth.json
## EXAMPLES
```
# kpod login docker.io
Username: umohnani
Password:
Login Succeeded!
```
```
# kpod login -u testuser -p testpassword localhost:5000
Login Succeeded!
```
```
# kpod login --authfile authdir/myauths.json docker.io
Username: umohnani
Password:
Login Succeeded!
```
## SEE ALSO
kpod(1), kpod-logout(1), crio(8), crio.conf(5)
## HISTORY
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,56 +0,0 @@
% kpod(1) kpod-logout - Simple tool to logout of a registry server
% Urvashi Mohnani
# kpod-logout "1" "August 2017" "kpod"
## NAME
kpod-logout - Logout of a container registry
## SYNOPSIS
**kpod logout**
[**--help**|**-h**]
[**--authfile**]
[**--all**|**-a**]
**REGISTRY**
## DESCRIPTION
**kpod logout** logs out of a specified registry server by deleting the cached credentials
stored in the **auth.json** file. The path of the authentication file can be overrriden by the user by setting the **authfile** flag.
The default path used is **${XDG\_RUNTIME_DIR}/containers/auth.json**.
All the cached credentials can be removed by setting the **all** flag.
**kpod [GLOBAL OPTIONS]**
**kpod logout [GLOBAL OPTIONS]**
**kpod logout [OPTIONS] REGISTRY [GLOBAL OPTIONS]**
## OPTIONS
**--authfile**
Path of the authentication file. Default is ${XDG_\RUNTIME\_DIR}/containers/auth.json
**--all, -a**
Remove the cached credentials for all registries in the auth file
## EXAMPLES
```
# kpod logout docker.io
Remove login credentials for https://registry-1.docker.io/v2/
```
```
# kpod logout --authfile authdir/myauths.json docker.io
Remove login credentials for https://registry-1.docker.io/v2/
```
```
# kpod logout --all
Remove login credentials for all registries
```
## SEE ALSO
kpod(1), kpod-login(1), crio(8), crio.conf(5)
## HISTORY
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,61 +0,0 @@
% kpod(1) kpod-logs - Fetch the logs of a container
% Ryan Cole
# kpod-logs "1" "March 2017" "kpod"
## NAME
kpod logs - Fetch the logs of a container
## SYNOPSIS
**kpod** **logs** [*options* [...]] container
## DESCRIPTION
The kpod logs command batch-retrieves whatever logs are present for a container at the time of execution. This does not guarantee execution order when combined with kpod run (i.e. your run may not have generated any logs at the time you execute kpod logs
## OPTIONS
**--follow, -f**
Follow log output. Default is false
**--since=TIMESTAMP**
Show logs since TIMESTAMP
**--tail=LINES**
Ouput the specified number of LINES at the end of the logs. LINES must be a positive integer. Defaults to 0, which prints all lines
## EXAMPLE
kpod logs b3f2436bdb978c1d33b1387afb5d7ba7e3243ed2ce908db431ac0069da86cb45
2017/08/07 10:16:21 Seeked /var/log/crio/pods/eb296bd56fab164d4d3cc46e5776b54414af3bf543d138746b25832c816b933b/c49f49788da14f776b7aa93fb97a2a71f9912f4e5a3e30397fca7dfe0ee0367b.log - &{Offset:0 Whence:0}
1:C 07 Aug 14:10:09.055 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 07 Aug 14:10:09.055 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 07 Aug 14:10:09.055 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 07 Aug 14:10:09.055 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
1:M 07 Aug 14:10:09.055 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
1:M 07 Aug 14:10:09.055 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
1:M 07 Aug 14:10:09.056 * Running mode=standalone, port=6379.
1:M 07 Aug 14:10:09.056 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 07 Aug 14:10:09.056 # Server initialized
kpod logs --tail 2 b3f2436bdb97
1:M 07 Aug 14:10:09.056 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 07 Aug 14:10:09.056 # Server initialized
kpod logs 224c375f27cd --since 2017-08-07T10:10:09.055837383-04:00 myserver
1:M 07 Aug 14:10:09.055 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
1:M 07 Aug 14:10:09.055 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
1:M 07 Aug 14:10:09.056 * Running mode=standalone, port=6379.
1:M 07 Aug 14:10:09.056 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 07 Aug 14:10:09.056 # Server initialized
## SEE ALSO
kpod(1)
## HISTORY
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>

View File

@ -1,50 +0,0 @@
% kpod(1) kpod-mount - Mount a working container's root filesystem.
% Dan Walsh
# kpod-mount "1" "July 2017" "kpod"
## NAME
kpod mount - Mount a working container's root filesystem
## SYNOPSIS
**kpod** **mount**
**kpod** **mount** **containerID**
## DESCRIPTION
Mounts the specified container's root file system in a location which can be
accessed from the host, and returns its location.
If you execute the command without any arguments, the tool will list all of the
currently mounted containers.
## RETURN VALUE
The location of the mounted file system. On error an empty string and errno is
returned.
## OPTIONS
**--format**
Print the mounted containers in specified format (json)
**--notruncate**
Do not truncate IDs in output.
**--label**
SELinux label for the mount point
## EXAMPLE
kpod mount c831414b10a3
/var/lib/containers/storage/overlay/f3ac502d97b5681989dff84dfedc8354239bcecbdc2692f9a639f4e080a02364/merged
kpod mount
c831414b10a3 /var/lib/containers/storage/overlay/f3ac502d97b5681989dff84dfedc8354239bcecbdc2692f9a639f4e080a02364/merged
a7060253093b /var/lib/containers/storage/overlay/0ff7d7ca68bed1ace424f9df154d2dd7b5a125c19d887f17653cbcd5b6e30ba1/merged
## SEE ALSO
kpod(1), kpod-umount(1), mount(8)

View File

@ -1,24 +0,0 @@
% kpod(1) kpod-pause - Pause one or more containers
% Dan Walsh
# kpod-pause "1" "September 2017" "kpod"
## NAME
kpod pause - Pause one or more containers
## SYNOPSIS
**kpod pause [OPTIONS] CONTAINER [...]**
## DESCRIPTION
Pauses all the processes in one or more containers. You may use container IDs or names as input.
## EXAMPLE
kpod pause mywebserver
kpod pause 860a4b23
## SEE ALSO
kpod(1), kpod-unpause(1)
## HISTORY
September 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>

View File

@ -1,131 +0,0 @@
% kpod(1) kpod-ps - Simple tool to list containers
% Urvashi Mohnani
% kpod-ps "1" "AUGUST 2017" "kpod"
## NAME
kpod-ps - Prints out information about containers
## SYNOPSIS
**kpod ps**
[**--all**|**-a**]
[**--no-trunc**]
[**--quiet**|**-q**]
[**--fromat**]
[**--help**|**-h**]
## DESCRIPTION
**kpod ps** lists the running containers on the system. Use the **--all** flag to view
all the containers information. By default it lists:
* container id
* the name of the image the container is using
* the COMMAND the container is executing
* the time the container was created
* the status of the container
* port mappings the container is using
* alternative names for the container
**kpod [GLOBAL OPTIONS]**
**kpod ps [GLOBAL OPTIONS]**
**kpod ps [OPTIONS]**
## OPTIONS
**--all, -a**
Show all the containers, default is only running containers
**--no-trunc**
Display the extended information
**--quiet, -q**
Print the numeric IDs of the containers only
**--format**
Pretty-print containers to JSON or using a Go template
Valid placeholders for the Go template are listed below:
| **Placeholder** | **Description** |
| --------------- | ------------------------------------------------ |
| .ID | Container ID |
| .Image | Image ID/Name |
| .Command | Quoted command used |
| .CreatedAt | Creation time for container |
| .RunningFor | Time elapsed since container was started |
| .Status | Status of container |
| .Ports | Exposed ports |
| .Size | Size of container |
| .Names | Name of container |
| .Labels | All the labels assigned to the container |
| .Mounts | Volumes mounted in the container |
**--size, -s**
Display the total file size
**--last, -n**
Print the n last created containers (all states)
**--latest, -l**
show the latest container created (all states)
**--namespace, --ns**
Display namespace information
**--filter, -f**
Filter output based on conditions given
Valid filters are listed below:
| **Filter** | **Description** |
| --------------- | ------------------------------------------------------------------- |
| id | [ID] Container's ID |
| name | [Name] Container's name |
| label | [Key] or [Key=Value] Label assigned to a container |
| exited | [Int] Container's exit code |
| status | [Status] Container's status, e.g *running*, *stopped* |
| ancestor | [ImageName] Image or descendant used to create container |
| before | [ID] or [Name] Containers created before this container |
| since | [ID] or [Name] Containers created since this container |
| volume | [VolumeName] or [MountpointDestination] Volume mounted in container |
## EXAMPLES
```
sudo kpod ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
02f65160e14ca redis:alpine "redis-server" 19 hours ago Exited (-1) 19 hours ago 6379/tcp k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0
69ed779d8ef9f redis:alpine "redis-server" 25 hours ago Created 6379/tcp k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1
```
```
sudo kpod ps -a -s
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE
02f65160e14ca redis:alpine "redis-server" 20 hours ago Exited (-1) 20 hours ago 6379/tcp k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0 27.49 MB
69ed779d8ef9f redis:alpine "redis-server" 25 hours ago Created 6379/tcp k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1 27.49 MB
```
```
sudo kpod ps -a --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}}"
02f65160e14ca redis:alpine tier=backend proc,tmpfs,devpts,shm,mqueue,sysfs,cgroup,/var/run/,/var/run/
69ed779d8ef9f redis:alpine batch=no,type=small proc,tmpfs,devpts,shm,mqueue,sysfs,cgroup,/var/run/,/var/run/
```
```
sudo kpod ps --ns -a
CONTAINER ID NAMES PID CGROUP IPC MNT NET PIDNS USER UTS
3557d882a82e3 k8s_container2_podsandbox1_redhat.test.crio_redhat-test-crio_1 29910 4026531835 4026532585 4026532593 4026532508 4026532595 4026531837 4026532594
09564cdae0bec k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1 29851 4026531835 4026532585 4026532590 4026532508 4026532592 4026531837 4026532591
a31ebbee9cee7 k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0 29717 4026531835 4026532585 4026532587 4026532508 4026532589 4026531837 4026532588
```
## ps
Print a list of containers
## SEE ALSO
kpod(1), crio(8), crio.conf(5)
## HISTORY
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,135 +0,0 @@
% kpod(1) kpod-pull - Simple tool to pull an image from a registry
% Urvashi Mohnani
# kpod-pull "1" "July 2017" "kpod"
## NAME
kpod-pull - Pull an image from a registry
## SYNOPSIS
**kpod pull**
**NAME[:TAG|@DIGEST]**
[**--help**|**-h**]
## DESCRIPTION
Copies an image from a registry onto the local machine. **kpod pull** pulls an
image from Docker Hub if a registry is not specified in the command line argument.
If an image tag is not specified, **kpod pull** defaults to the image with the
**latest** tag (if it exists) and pulls it. **kpod pull** can also pull an image
using its digest **kpod pull [image]@[digest]**. **kpod pull** can be used to pull
images from archives and local storage using different transports.
## imageID
Image stored in local container/storage
## SOURCE
The SOURCE is a location to get container images
The Image "SOURCE" uses a "transport":"details" format.
Multiple transports are supported:
**dir:**_path_
An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
**docker://**_docker-reference_
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set e.g. using `(kpod login)`.
**docker-archive:**_path_[**:**_docker-reference_]
An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
**docker-daemon:**_docker-reference_
An image _docker-reference_ stored in the docker daemon internal storage. _docker-reference_ must contain either a tag or a digest. Alternatively, when reading images, the format can also be docker-daemon:algo:digest (an image ID).
**oci-archive:**_path_**:**_tag_
An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_.
**ostree:**_image_[**@**_/absolute/repo/path_]
An image in local OSTree repository. _/absolute/repo/path_ defaults to _/ostree/repo_.
**kpod [GLOBAL OPTIONS]**
**kpod pull [GLOBAL OPTIONS]**
**kpod pull NAME[:TAG|@DIGEST]**
## OPTIONS
**--authfile**
Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json
**--cert-dir**
Pathname of a directory containing TLS certificates and keys
**--creds**
Credentials (USERNAME:PASSWORD) to use for authenticating to a registry
**--quiet, -q**
Suppress output information when pulling images
**--signature-policy="PATHNAME"**
Pathname of a signature policy file to use. It is not recommended that this
option be used, as the default behavior of using the system-wide default policy
(frequently */etc/containers/policy.json*) is most often preferred
**--tls-verify**
Require HTTPS and verify certificates when contacting registries (default: true)
## EXAMPLES
```
# kpod pull --signature-policy /etc/containers/policy.json alpine:latest
Trying to pull registry.access.redhat.com/alpine:latest... Failed
Trying to pull registry.fedoraproject.org/alpine:latest... Failed
Trying to pull docker.io/library/alpine:latest...Getting image source signatures
Copying blob sha256:88286f41530e93dffd4b964e1db22ce4939fffa4a4c665dab8591fbab03d4926
1.90 MB / 1.90 MB [========================================================] 0s
Copying config sha256:76da55c8019d7a47c347c0dceb7a6591144d232a7dd616242a367b8bed18ecbc
1.48 KB / 1.48 KB [========================================================] 0s
Writing manifest to image destination
Storing signatures
```
```
# kpod pull --authfile temp-auths/myauths.json docker://docker.io/umohnani/finaltest
Trying to pull docker.io/umohnani/finaltest:latest...Getting image source signatures
Copying blob sha256:6d987f6f42797d81a318c40d442369ba3dc124883a0964d40b0c8f4f7561d913
1.90 MB / 1.90 MB [========================================================] 0s
Copying config sha256:ad4686094d8f0186ec8249fc4917b71faa2c1030d7b5a025c29f26e19d95c156
1.41 KB / 1.41 KB [========================================================] 0s
Writing manifest to image destination
Storing signatures
```
```
# kpod pull --creds testuser:testpassword docker.io/umohnani/finaltest
Trying to pull docker.io/umohnani/finaltest:latest...Getting image source signatures
Copying blob sha256:6d987f6f42797d81a318c40d442369ba3dc124883a0964d40b0c8f4f7561d913
1.90 MB / 1.90 MB [========================================================] 0s
Copying config sha256:ad4686094d8f0186ec8249fc4917b71faa2c1030d7b5a025c29f26e19d95c156
1.41 KB / 1.41 KB [========================================================] 0s
Writing manifest to image destination
Storing signatures
```
```
# kpod pull --tls-verify=false --cert-dir image/certs docker.io/umohnani/finaltest
Trying to pull docker.io/umohnani/finaltest:latest...Getting image source signatures
Copying blob sha256:6d987f6f42797d81a318c40d442369ba3dc124883a0964d40b0c8f4f7561d913
1.90 MB / 1.90 MB [========================================================] 0s
Copying config sha256:ad4686094d8f0186ec8249fc4917b71faa2c1030d7b5a025c29f26e19d95c156
1.41 KB / 1.41 KB [========================================================] 0s
Writing manifest to image destination
Storing signatures
```
## SEE ALSO
kpod(1), kpod-push(1), crio(8), crio.conf(5)
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,116 +0,0 @@
% kpod(1) kpod-push - Push an image from local storage to elsewhere
% Dan Walsh
# kpod-push "1" "June 2017" "kpod"
## NAME
kpod push - Push an image from local storage to elsewhere
## SYNOPSIS
**kpod** **push** [*options* [...]] **imageID** [**destination**]
## DESCRIPTION
Pushes an image from local storage to a specified destination, decompressing
and recompressing layers as needed.
## imageID
Image stored in local container/storage
## DESTINATION
The DESTINATION is a location to store container images
The Image "DESTINATION" uses a "transport":"details" format.
Multiple transports are supported:
**dir:**_path_
An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
**docker://**_docker-reference_
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set e.g. using `(kpod login)`.
**docker-archive:**_path_[**:**_docker-reference_]
An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
**docker-daemon:**_docker-reference_
An image _docker-reference_ stored in the docker daemon internal storage. _docker-reference_ must contain either a tag or a digest. Alternatively, when reading images, the format can also be docker-daemon:algo:digest (an image ID).
**oci-archive:**_path_**:**_tag_
An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_.
**ostree:**_image_[**@**_/absolute/repo/path_]
An image in local OSTree repository. _/absolute/repo/path_ defaults to _/ostree/repo_.
## OPTIONS
**--authfile**
Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json
**--creds="CREDENTIALS"**
Credentials (USERNAME:PASSWORD) to use for authenticating to a registry
**cert-dir="PATHNAME"**
Pathname of a directory containing TLS certificates and keys
**--disable-compression, -D**
Don't compress copies of filesystem layers which will be pushed
**--quiet, -q**
When writing the output image, suppress progress output
**--remove-signatures**
Discard any pre-existing signatures in the image
**--signature-policy="PATHNAME"**
Pathname of a signature policy file to use. It is not recommended that this
option be used, as the default behavior of using the system-wide default policy
(frequently */etc/containers/policy.json*) is most often preferred
**--sign-by="KEY"**
Add a signature at the destination using the specified key
**--tls-verify**
Require HTTPS and verify certificates when contacting registries (default: true)
## EXAMPLE
This example extracts the imageID image to a local directory in docker format.
`# kpod push imageID dir:/path/to/image`
This example extracts the imageID image to a local directory in oci format.
`# kpod push imageID oci-archive:/path/to/layout:image:tag`
This example extracts the imageID image to a container registry named registry.example.com
`# kpod push imageID docker://registry.example.com/repository:tag`
This example extracts the imageID image and puts into the local docker container store
`# kpod push imageID docker-daemon:image:tag`
This example pushes the alpine image to umohnani/alpine on dockerhub and reads the creds from
the path given to --authfile
```
# kpod push --authfile temp-auths/myauths.json alpine docker://docker.io/umohnani/alpine
Getting image source signatures
Copying blob sha256:5bef08742407efd622d243692b79ba0055383bbce12900324f75e56f589aedb0
4.03 MB / 4.03 MB [========================================================] 1s
Copying config sha256:ad4686094d8f0186ec8249fc4917b71faa2c1030d7b5a025c29f26e19d95c156
1.41 KB / 1.41 KB [========================================================] 1s
Writing manifest to image destination
Storing signatures
```
## SEE ALSO
kpod(1), kpod-pull(1), crio(8), crio.conf(5)

View File

@ -1,24 +0,0 @@
% kpod(1) kpod-rename - Rename a container
% Ryan Cole
# kpod-images "1" "March 2017" "kpod"
## NAME
kpod rename - Rename a container
## SYNOPSIS
**kpod** **rename** CONTAINER NEW-NAME
## DESCRIPTION
Rename a container. Container may be created, running, paused, or stopped
## EXAMPLE
kpod rename redis-container webserver
kpod rename a236b9a4 mycontainer
## SEE ALSO
kpod(1)
## HISTORY
March 2017, Originally compiled by Ryan Cole <rycole@redhat.com>

View File

@ -1,31 +0,0 @@
% kpod(1) kpod-rm - Remove one or more containers
% Ryan Cole
# kpod-rm "1" "August 2017" "kpod"
## NAME
kpod rm - Remove one or more containers
## SYNOPSIS
**kpod** **rm** [*options* [...]] container
## DESCRIPTION
Kpod rm will remove one or more containers from the host. The container name or ID can be used. This does not remove images. Running containers will not be removed without the -f option
## OPTIONS
**--force, f**
Force the removal of a running container
## EXAMPLE
kpod rm mywebserver
kpod rm -f 860a4b23
## SEE ALSO
kpod(1), kpod-rmi(1)
## HISTORY
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>

View File

@ -1,32 +0,0 @@
% kpod(1) kpod-rmi - Removes one or more images
% Dan Walsh
# kpod-rmi "1" "March 2017" "kpod"
## NAME
kpod rmi - Removes one or more images
## SYNOPSIS
**kpod** **rmi** **imageID [...]**
## DESCRIPTION
Removes one or more locally stored images.
## OPTIONS
**--force, -f**
Executing this command will stop all containers that are using the image and remove them from the system
## EXAMPLE
kpod rmi imageID
kpod rmi --force imageID
kpod rmi imageID1 imageID2 imageID3
## SEE ALSO
kpod(1)
## HISTORY
March 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>

View File

@ -1,847 +0,0 @@
% kpod(1) kpod-run - Run a command in a container
% Dan Walsh
kpod-run - Run a command in a new container
# SYNOPSIS
**kpod run** [*options* [...]] IMAGE [COMMAND] [ARG...]
# DESCRIPTION
Run a process in a new container. **kpod run** starts a process with its own
file system, its own networking, and its own isolated process tree. The IMAGE
which starts the process may define defaults related to the process that will be
run in the container, the networking to expose, and more, but **kpod run**
gives final control to the operator or administrator who starts the container
from the image. For that reason **kpod run** has more options than any other
kpod command.
If the IMAGE is not already loaded then **kpod run** will pull the IMAGE, and
all image dependencies, from the repository in the same way running **kpod
pull** IMAGE, before it starts the container from that image.
# OPTIONS
**--add-host**=[]
Add a custom host-to-IP mapping (host:ip)
Add a line to /etc/hosts. The format is hostname:ip. The **--add-host**
option can be set multiple times.
**-a**, **--attach**=[]
Attach to STDIN, STDOUT or STDERR.
In foreground mode (the default when **-d**
is not specified), **kpod run** can start the process in the container
and attach the console to the process's standard input, output, and standard
error. It can even pretend to be a TTY (this is what most commandline
executables expect) and pass along signals. The **-a** option can be set for
each of stdin, stdout, and stderr.
**--blkio-weight**=*0*
Block IO weight (relative weight) accepts a weight value between 10 and 1000.
**--blkio-weight-device**=[]
Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`).
**--cap-add**=[]
Add Linux capabilities
**--cap-drop**=[]
Drop Linux capabilities
**--cgroup-parent**=""
Path to cgroups under which the cgroup for the container will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist.
**--cidfile**=""
Write the container ID to the file
**--cpu-count**=*0*
Limit the number of CPUs available for execution by the container.
On Windows Server containers, this is approximated as a percentage of total CPU usage.
On Windows Server containers, the processor resource controls are mutually exclusive, the order of precedence is CPUCount first, then CPUShares, and CPUPercent last.
**--cpu-period**=*0*
Limit the CPU CFS (Completely Fair Scheduler) period
Limit the container's CPU usage. This flag tell the kernel to restrict the container's CPU usage to the period you specify.
**--cpu-quota**=*0*
Limit the CPU CFS (Completely Fair Scheduler) quota
Limit the container's CPU usage. By default, containers run with the full
CPU resource. This flag tell the kernel to restrict the container's CPU usage
to the quota you specify.
**--cpu-rt-period**=0
Limit the CPU real-time period in microseconds
Limit the container's Real Time CPU usage. This flag tell the kernel to restrict the container's Real Time CPU usage to the period you specify.
**--cpu-rt-runtime**=0
Limit the CPU real-time runtime in microseconds
Limit the containers Real Time CPU usage. This flag tells the kernel to limit the amount of time in a given CPU period Real Time tasks may consume. Ex:
Period of 1,000,000us and Runtime of 950,000us means that this container could consume 95% of available CPU and leave the remaining 5% to normal priority tasks.
The sum of all runtimes across containers cannot exceed the amount allotted to the parent cgroup.
**--cpu-shares**=*0*
CPU shares (relative weight)
By default, all containers get the same proportion of CPU cycles. This proportion
can be modified by changing the container's CPU share weighting relative
to the weighting of all other running containers.
To modify the proportion from the default of 1024, use the **--cpu-shares**
flag to set the weighting to 2 or higher.
The proportion will only apply when CPU-intensive processes are running.
When tasks in one container are idle, other containers can use the
left-over CPU time. The actual amount of CPU time will vary depending on
the number of containers running on the system.
For example, consider three containers, one has a cpu-share of 1024 and
two others have a cpu-share setting of 512. When processes in all three
containers attempt to use 100% of CPU, the first container would receive
50% of the total CPU time. If you add a fourth container with a cpu-share
of 1024, the first container only gets 33% of the CPU. The remaining containers
receive 16.5%, 16.5% and 33% of the CPU.
On a multi-core system, the shares of CPU time are distributed over all CPU
cores. Even if a container is limited to less than 100% of CPU time, it can
use 100% of each individual CPU core.
For example, consider a system with more than three cores. If you start one
container **{C0}** with **-c=512** running one process, and another container
**{C1}** with **-c=1024** running two processes, this can result in the following
division of CPU shares:
PID container CPU CPU share
100 {C0} 0 100% of CPU0
101 {C1} 1 100% of CPU1
102 {C1} 2 100% of CPU2
**--cpus**=0.0
Number of CPUs. The default is *0.0* which means no limit.
**--cpuset-cpus**=""
CPUs in which to allow execution (0-3, 0,1)
**--cpuset-mems**=""
Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.
If you have four memory nodes on your system (0-3), use `--cpuset-mems=0,1`
then processes in your container will only use memory from the first
two memory nodes.
**-d**, **--detach**=*true*|*false*
Detached mode: run the container in the background and print the new container ID. The default is *false*.
At any time you can run **kpod ps** in
the other shell to view a list of the running containers. You can reattach to a
detached container with **kpod attach**. If you choose to run a container in
the detached mode, then you cannot use the **-rm** option.
When attached in the tty mode, you can detach from the container (and leave it
running) using a configurable key sequence. The default sequence is `CTRL-p CTRL-q`.
You configure the key sequence using the **--detach-keys** option or a configuration file.
See **config-json(5)** for documentation on using a configuration file.
**--detach-keys**=""
Override the key sequence for detaching a container. Format is a single character `[a-Z]` or `ctrl-<value>` where `<value>` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`.
**--device**=[]
Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc:rwm)
**--device-read-bps**=[]
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
**--device-read-iops**=[]
Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)
**--device-write-bps**=[]
Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)
**--device-write-iops**=[]
Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)
**--dns**=[]
Set custom DNS servers
This option can be used to override the DNS
configuration passed to the container. Typically this is necessary when the
host DNS configuration is invalid for the container (e.g., 127.0.0.1). When this
is the case the **--dns** flags is necessary for every run.
**--dns-option**=[]
Set custom DNS options
**--dns-search**=[]
Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)
**--entrypoint**=""
Overwrite the default ENTRYPOINT of the image
This option allows you to overwrite the default entrypoint of the image.
The ENTRYPOINT of an image is similar to a COMMAND
because it specifies what executable to run when the container starts, but it is
(purposely) more difficult to override. The ENTRYPOINT gives a container its
default nature or behavior, so that when you set an ENTRYPOINT you can run the
container as if it were that binary, complete with default options, and you can
pass in more options via the COMMAND. But, sometimes an operator may want to run
something else inside the container, so you can override the default ENTRYPOINT
at runtime by using a **--entrypoint** and a string to specify the new
ENTRYPOINT.
**-e**, **--env**=[]
Set environment variables
This option allows you to specify arbitrary
environment variables that are available for the process that will be launched
inside of the container.
**--env-file**=[]
Read in a line delimited file of environment variables
**--expose**=[]
Expose a port, or a range of ports (e.g. --expose=3300-3310) to set up port redirection
on the host system.
**--group-add**=[]
Add additional groups to run as
**--hostname**=""
Container host name
Sets the container host name that is available inside the container.
**--help**
Print usage statement
**-i**, **--interactive**=*true*|*false*
Keep STDIN open even if not attached. The default is *false*.
When set to true, keep stdin open even if not attached. The default is false.
**--ip**=""
Sets the container's interface IPv4 address (e.g. 172.23.0.9)
It can only be used in conjunction with **--network** for user-defined networks
**--ip6**=""
Sets the container's interface IPv6 address (e.g. 2001:db8::1b99)
It can only be used in conjunction with **--network** for user-defined networks
**--ipc**=""
Default is to create a private IPC namespace (POSIX SysV IPC) for the container
'container:<name|id>': reuses another container shared memory, semaphores and message queues
'host': use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure.
**--kernel-memory**=""
Kernel memory limit (format: `<number>[<unit>]`, where unit = b, k, m or g)
Constrains the kernel memory available to a container. If a limit of 0
is specified (not using `--kernel-memory`), the container's kernel memory
is not limited. If you specify a limit, it may be rounded up to a multiple
of the operating system's page size and the value can be very large,
millions of trillions.
**-l**, **--label**=[]
Add metadata to a container (e.g., --label com.example.key=value)
**--label-file**=[]
Read in a line delimited file of labels
**--link-local-ip**=[]
Add one or more link-local IPv4/IPv6 addresses to the container's interface
**--log-driver**="*json-file*|*syslog*|*journald*|*gelf*|*fluentd*|*awslogs*|*splunk*|*etwlogs*|*gcplogs*|*none*"
Logging driver for the container. Default is defined by daemon `--log-driver` flag.
**Warning**: the `kpod logs` command works only for the `json-file` and
`journald` logging drivers.
**--log-opt**=[]
Logging driver specific options.
**--mac-address**=""
Container MAC address (e.g. 92:d0:c6:0a:29:33)
Remember that the MAC address in an Ethernet network must be unique.
The IPv6 link-local address will be based on the device's MAC address
according to RFC4862.
**-m**, **--memory**=""
Memory limit (format: <number>[<unit>], where unit = b, k, m or g)
Allows you to constrain the memory available to a container. If the host
supports swap memory, then the **-m** memory setting can be larger than physical
RAM. If a limit of 0 is specified (not using **-m**), the container's memory is
not limited. The actual limit may be rounded up to a multiple of the operating
system's page size (the value would be very large, that's millions of trillions).
**--memory-reservation**=""
Memory soft limit (format: <number>[<unit>], where unit = b, k, m or g)
After setting memory reservation, when the system detects memory contention
or low memory, containers are forced to restrict their consumption to their
reservation. So you should always set the value below **--memory**, otherwise the
hard limit will take precedence. By default, memory reservation will be the same
as memory limit.
**--memory-swap**="LIMIT"
A limit value equal to memory plus swap. Must be used with the **-m**
(**--memory**) flag. The swap `LIMIT` should always be larger than **-m**
(**--memory**) value. By default, the swap `LIMIT` will be set to double
the value of --memory.
The format of `LIMIT` is `<number>[<unit>]`. Unit can be `b` (bytes),
`k` (kilobytes), `m` (megabytes), or `g` (gigabytes). If you don't specify a
unit, `b` is used. Set LIMIT to `-1` to enable unlimited swap.
**--memory-swappiness**=""
Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100.
**--name**=""
Assign a name to the container
The operator can identify a container in three ways:
UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
UUID short identifier (“f78375b1c487”)
Name (“jonah”)
kpod generates a UUID for each container, and if a name is not assigned
to the container with **--name** then the daemon will also generate a random
string name. The name is useful when defining links (see **--link**) (or any
other place you need to identify a container). This works for both background
and foreground containers.
**--network**="*bridge*"
Set the Network mode for the container
'bridge': create a network stack on the default bridge
'none': no networking
'container:<name|id>': reuse another container's network stack
'host': use the kpod host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
'<network-name>|<network-id>': connect to a user-defined network
**--network-alias**=[]
Add network-scoped alias for the container
**--oom-kill-disable**=*true*|*false*
Whether to disable OOM Killer for the container or not.
**--oom-score-adj**=""
Tune the host's OOM preferences for containers (accepts -1000 to 1000)
**--pid**=""
Set the PID mode for the container
Default is to create a private PID namespace for the container
'container:<name|id>': join another container's PID namespace
'host': use the host's PID namespace for the container. Note: the host mode gives the container full access to local PID and is therefore considered insecure.
**--pids-limit**=""
Tune the container's pids limit. Set `-1` to have unlimited pids for the container.
**--pod**=""
Run container in an existing pod
**--privileged**=*true*|*false*
Give extended privileges to this container. The default is *false*.
By default, kpod containers are
“unprivileged” (=false) and cannot, for example, modify parts of the kernel.
This is because by default a container is not allowed to access any devices.
A “privileged” container is given access to all devices.
When the operator executes **kpod run --privileged**, kpod enables access
to all devices on the host as well as set turn off most of the security messurs
protecting the host from the container.
**-p**, **--publish**=[]
Publish a container's port, or range of ports, to the host
Format: `ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort`
Both hostPort and containerPort can be specified as a range of ports.
When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range.
(e.g., `kpod run -p 1234-1236:1222-1224 --name thisWorks -t busybox`
but not `kpod run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanRangeHostPorts -t busybox`)
With ip: `kpod run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage`
Use `kpod port` to see the actual mapping: `kpod port CONTAINER $CONTAINERPORT`
**-P**, **--publish-all**=*true*|*false*
Publish all exposed ports to random ports on the host interfaces. The default is *false*.
When set to true publish all exposed ports to the host interfaces. The
default is false. If the operator uses -P (or -p) then kpod will make the
exposed port accessible on the host and the ports will be available to any
client that can reach the host. When using -P, kpod will bind any exposed
port to a random port on the host within an *ephemeral port range* defined by
`/proc/sys/net/ipv4/ip_local_port_range`. To find the mapping between the host
ports and the exposed ports, use `kpod port`.
**--read-only**=*true*|*false*
Mount the container's root filesystem as read only.
By default a container will have its root filesystem writable allowing processes
to write files anywhere. By specifying the `--read-only` flag the container will have
its root filesystem mounted as read only prohibiting any writes.
**--rm**=*true*|*false*
Automatically remove the container when it exits. The default is *false*.
`--rm` flag can work together with `-d`, and auto-removal will be done on daemon side. Note that it's
incompatible with any restart policy other than `none`.
**--security-opt**=[]
Security Options
"label=user:USER" : Set the label user for the container
"label=role:ROLE" : Set the label role for the container
"label=type:TYPE" : Set the label type for the container
"label=level:LEVEL" : Set the label level for the container
"label=disable" : Turn off label confinement for the container
"no-new-privileges" : Disable container processes from gaining additional privileges
"seccomp=unconfined" : Turn off seccomp confinement for the container
"seccomp=profile.json : White listed syscalls seccomp Json file to be used as a seccomp filter
"apparmor=unconfined" : Turn off apparmor confinement for the container
"apparmor=your-profile" : Set the apparmor confinement profile for the container
**--shm-size**=""
Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater than `0`.
Unit is optional and can be `b` (bytes), `k` (kilobytes), `m`(megabytes), or `g` (gigabytes).
If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`.
**--sig-proxy**=*true*|*false*
Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied. The default is *true*.
**--stop-signal**=*SIGTERM*
Signal to stop a container. Default is SIGTERM.
**--stop-timeout**=*10*
Timeout (in seconds) to stop a container. Default is 10.
**--storage-opt**=[]
Storage driver options per container
$ kpod run -it --storage-opt size=120G fedora /bin/bash
This (size) will allow to set the container rootfs size to 120G at creation time.
This option is only available for the `devicemapper`, `btrfs`, `overlay2` and `zfs` graph drivers.
For the `devicemapper`, `btrfs` and `zfs` storage drivers, user cannot pass a size less than the Default BaseFS Size.
For the `overlay2` storage driver, the size option is only available if the backing fs is `xfs` and mounted with the `pquota` mount option.
Under these conditions, user can pass any size less then the backing fs size.
**--sysctl**=SYSCTL
Configure namespaced kernel parameters at runtime
IPC Namespace - current sysctls allowed:
kernel.msgmax, kernel.msgmnb, kernel.msgmni, kernel.sem, kernel.shmall, kernel.shmmax, kernel.shmmni, kernel.shm_rmid_forced
Sysctls beginning with fs.mqueue.*
Note: if you use the `--ipc=host` option these sysctls will not be allowed.
Network Namespace - current sysctls allowed:
Sysctls beginning with net.*
Note: if you use the `--network=host` option these sysctls will not be allowed.
**--tmpfs**=[] Create a tmpfs mount
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:
$ kpod run -d --tmpfs /tmp:rw,size=787448k,mode=1777 my_image
This command mounts a `tmpfs` at `/tmp` within the container. The supported mount
options are the same as the Linux default `mount` flags. If you do not specify
any options, the systems uses the following options:
`rw,noexec,nosuid,nodev,size=65536k`.
**-t**, **--tty**=*true*|*false*
Allocate a pseudo-TTY. The default is *false*.
When set to true kpod will allocate a pseudo-tty and attach to the standard
input of the container. This can be used, for example, to run a throwaway
interactive shell. The default is false.
Note: The **-t** option is incompatible with a redirection of the kpod client
standard input.
**--ulimit**=[]
Ulimit options
**-u**, **--user**=""
Sets the username or UID used and optionally the groupname or GID for the specified command.
The followings examples are all valid:
--user [user | user:group | uid | uid:gid | user:gid | uid:group ]
Without this argument the command will be run as root in the container.
**--userns**=""
Set the usernamespace mode for the container when `userns-remap` option is enabled.
**host**: use the host usernamespace and enable all privileged options (e.g., `pid=host` or `--privileged`).
**--uts**=*host*
Set the UTS mode for the container
**host**: use the host's UTS namespace inside the container.
Note: the host mode gives the container access to changing the host's hostname and is therefore considered insecure.
**-v**|**--volume**[=*[[HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, kpod
bind mounts `/HOST-DIR` in the host to `/CONTAINER-DIR` in the kpod
container. If 'HOST-DIR' is omitted, kpod automatically creates the new
volume on the host. The `OPTIONS` are a comma delimited list and can be:
* [rw|ro]
* [z|Z]
* [`[r]shared`|`[r]slave`|`[r]private`]
* [nocopy]
The `CONTAINER-DIR` must be an absolute path such as `/src/docs`. The `HOST-DIR`
can be an absolute path or a `name` value. A `name` value must start with an
alphanumeric character, followed by `a-z0-9`, `_` (underscore), `.` (period) or
`-` (hyphen). An absolute path starts with a `/` (forward slash).
If you supply a `HOST-DIR` that is an absolute path, kpod bind-mounts to the
path you specify. If you supply a `name`, kpod creates a named volume by that
`name`. For example, you can specify either `/foo` or `foo` for a `HOST-DIR`
value. If you supply the `/foo` value, kpod creates a bind-mount. If you
supply the `foo` specification, kpod creates a named volume.
You can specify multiple **-v** options to mount one or more mounts to a
container. To use these same mounts in other containers, specify the
**--volumes-from** option also.
You can add `:ro` or `:rw` suffix to a volume to mount it read-only or
read-write mode, respectively. By default, the volumes are mounted read-write.
See examples.
Labeling systems like SELinux require that proper labels are placed on volume
content mounted into a container. Without a label, the security system might
prevent the processes running inside the container from using the content. By
default, kpod does not change the labels set by the OS.
To change a label in the container context, you can add either of two suffixes
`:z` or `:Z` to the volume mount. These suffixes tell kpod to relabel file
objects on the shared volumes. The `z` option tells kpod that two containers
share the volume content. As a result, kpod labels the content with a shared
content label. Shared volume labels allow all containers to read/write content.
The `Z` option tells kpod to label the content with a private unshared label.
Only the current container can use a private volume.
By default bind mounted volumes are `private`. That means any mounts done
inside container will not be visible on host and vice-a-versa. One can change
this behavior by specifying a volume mount propagation property. Making a
volume `shared` mounts done under that volume inside container will be
visible on host and vice-a-versa. Making a volume `slave` enables only one
way mount propagation and that is mounts done on host under that volume
will be visible inside container but not the other way around.
To control mount propagation property of volume one can use `:[r]shared`,
`:[r]slave` or `:[r]private` propagation flag. Propagation property can
be specified only for bind mounted volumes and not for internal volumes or
named volumes. For mount propagation to work source mount point (mount point
where source dir is mounted on) has to have right propagation properties. For
shared volumes, source mount point has to be shared. And for slave volumes,
source mount has to be either shared or slave.
Use `df <source-dir>` to figure out the source mount and then use
`findmnt -o TARGET,PROPAGATION <source-mount-dir>` to figure out propagation
properties of source mount. If `findmnt` utility is not available, then one
can look at mount entry for source mount point in `/proc/self/mountinfo`. Look
at `optional fields` and see if any propagaion properties are specified.
`shared:X` means mount is `shared`, `master:X` means mount is `slave` and if
nothing is there that means mount is `private`.
To change propagation properties of a mount point use `mount` command. For
example, if one wants to bind mount source directory `/foo` one can do
`mount --bind /foo /foo` and `mount --make-private --make-shared /foo`. This
will convert /foo into a `shared` mount point. Alternatively one can directly
change propagation properties of source mount. Say `/` is source mount for
`/foo`, then use `mount --make-shared /` to convert `/` into a `shared` mount.
To disable automatic copying of data from the container path to the volume, use
the `nocopy` flag. The `nocopy` flag can be set on bind mounts and named volumes.
**--volumes-from**=[]
Mount volumes from the specified container(s)
Mounts already mounted volumes from a source container onto another
container. You must supply the source's container-id. To share
a volume, use the **--volumes-from** option when running
the target container. You can share volumes even if the source container
is not running.
By default, kpod mounts the volumes in the same mode (read-write or
read-only) as it is mounted in the source container. Optionally, you
can change this by suffixing the container-id with either the `:ro` or
`:rw ` keyword.
If the location of the volume from the source container overlaps with
data residing on a target container, then the volume hides
that data on the target.
**-w**, **--workdir**=""
Working directory inside the container
The default working directory for running binaries within a container is the root directory (/).
The image developer can set a different default with the WORKDIR instruction. The operator
can override the working directory by using the **-w** option.
# Exit Status
The exit code from `kpod run` gives information about why the container
failed to run or why it exited. When `kpod run` exits with a non-zero code,
the exit codes follow the `chroot` standard, see below:
**_125_** if the error is with kpod **_itself_**
$ kpod run --foo busybox; echo $?
# flag provided but not defined: --foo
See 'kpod run --help'.
125
**_126_** if the **_contained command_** cannot be invoked
$ kpod run busybox /etc; echo $?
# exec: "/etc": permission denied
kpod: Error response from daemon: Contained command could not be invoked
126
**_127_** if the **_contained command_** cannot be found
$ kpod run busybox foo; echo $?
# exec: "foo": executable file not found in $PATH
kpod: Error response from daemon: Contained command not found or does not exist
127
**_Exit code_** of **_contained command_** otherwise
$ kpod run busybox /bin/sh -c 'exit 3'
# 3
# EXAMPLES
## Running container in read-only mode
During container image development, containers often need to write to the image
content. Installing packages into /usr, for example. In production,
applications seldom need to write to the image. Container applications write
to volumes if they need to write to file systems at all. Applications can be
made more secure by running them in read-only mode using the - -read-only switch.
This protects the containers image from modification. Read only containers may
still need to write temporary data. The best way to handle this is to mount
tmpfs directories on /run and /tmp.
# kpod run --read-only --tmpfs /run --tmpfs /tmp -i -t fedora /bin/bash
## Exposing log messages from the container to the host's log
If you want messages that are logged in your container to show up in the host's
syslog/journal then you should bind mount the /dev/log directory as follows.
# kpod run -v /dev/log:/dev/log -i -t fedora /bin/bash
From inside the container you can test this by sending a message to the log.
(bash)# logger "Hello from my container"
Then exit and check the journal.
# exit
# journalctl -b | grep Hello
This should list the message sent to logger.
## Attaching to one or more from STDIN, STDOUT, STDERR
If you do not specify -a then kpod will attach everything (stdin,stdout,stderr)
. You can specify to which of the three standard streams (stdin, stdout, stderr)
you'd like to connect instead, as in:
# kpod run -a stdin -a stdout -i -t fedora /bin/bash
## Sharing IPC between containers
Using shm_server.c available here: https://www.cs.cf.ac.uk/Dave/C/node27.html
Testing `--ipc=host` mode:
Host shows a shared memory segment with 7 pids attached, happens to be from httpd:
```
$ sudo ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x01128e25 0 root 600 1000 7
```
Now run a regular container, and it correctly does NOT see the shared memory segment from the host:
```
$ kpod run -it shm ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
```
Run a container with the new `--ipc=host` option, and it now sees the shared memory segment from the host httpd:
```
$ kpod run -it --ipc=host shm ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x01128e25 0 root 600 1000 7
```
Testing `--ipc=container:CONTAINERID` mode:
Start a container with a program to create a shared memory segment:
```
$ kpod run -it shm bash
$ sudo shm/shm_server &
$ sudo ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x0000162e 0 root 666 27 1
```
Create a 2nd container correctly shows no shared memory segment from 1st container:
```
$ kpod run shm ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
```
Create a 3rd container using the new --ipc=container:CONTAINERID option, now it shows the shared memory segment from the first:
```
$ kpod run -it --ipc=container:ed735b2264ac shm ipcs -m
$ sudo ipcs -m
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x0000162e 0 root 666 27 1
```
## Mapping Ports for External Usage
The exposed port of an application can be mapped to a host port using the **-p**
flag. For example, an httpd port 80 can be mapped to the host port 8080 using the
following:
# kpod run -p 8080:80 -d -i -t fedora/httpd
## Creating and Mounting a Data Volume Container
Many applications require the sharing of persistent data across several
containers. kpod allows you to create a Data Volume Container that other
containers can mount from. For example, create a named container that contains
directories /var/volume1 and /tmp/volume2. The image will need to contain these
directories so a couple of RUN mkdir instructions might be required for you
fedora-data image:
# kpod run --name=data -v /var/volume1 -v /tmp/volume2 -i -t fedora-data true
# kpod run --volumes-from=data --name=fedora-container1 -i -t fedora bash
Multiple --volumes-from parameters will bring together multiple data volumes from
multiple containers. And it's possible to mount the volumes that came from the
DATA container in yet another container via the fedora-container1 intermediary
container, allowing to abstract the actual data source from users of that data:
# kpod run --volumes-from=fedora-container1 --name=fedora-container2 -i -t fedora bash
## Mounting External Volumes
To mount a host directory as a container volume, specify the absolute path to
the directory and the absolute path for the container directory separated by a
colon:
# kpod run -v /var/db:/data1 -i -t fedora bash
When using SELinux, be aware that the host has no knowledge of container SELinux
policy. Therefore, in the above example, if SELinux policy is enforced, the
`/var/db` directory is not writable to the container. A "Permission Denied"
message will occur and an avc: message in the host's syslog.
To work around this, at time of writing this man page, the following command
needs to be run in order for the proper SELinux policy type label to be attached
to the host directory:
# chcon -Rt svirt_sandbox_file_t /var/db
Now, writing to the /data1 volume in the container will be allowed and the
changes will also be reflected on the host in /var/db.
## Using alternative security labeling
You can override the default labeling scheme for each container by specifying
the `--security-opt` flag. For example, you can specify the MCS/MLS level, a
requirement for MLS systems. Specifying the level in the following command
allows you to share the same content between containers.
# kpod run --security-opt label=level:s0:c100,c200 -i -t fedora bash
An MLS example might be:
# kpod run --security-opt label=level:TopSecret -i -t rhel7 bash
To disable the security labeling for this container versus running with the
`--permissive` flag, use the following command:
# kpod run --security-opt label=disable -i -t fedora bash
If you want a tighter security policy on the processes within a container,
you can specify an alternate type for the container. You could run a container
that is only allowed to listen on Apache ports by executing the following
command:
# kpod run --security-opt label=type:svirt_apache_t -i -t centos bash
Note:
You would have to write policy defining a `svirt_apache_t` type.
## Setting device weight
If you want to set `/dev/sda` device weight to `200`, you can specify the device
weight by `--blkio-weight-device` flag. Use the following command:
# kpod run -it --blkio-weight-device "/dev/sda:200" ubuntu
```
$ kpod run -d busybox top
```
## Setting Namespaced Kernel Parameters (Sysctls)
The `--sysctl` sets namespaced kernel parameters (sysctls) in the
container. For example, to turn on IP forwarding in the containers
network namespace, run this command:
$ kpod run --sysctl net.ipv4.ip_forward=1 someimage
Note:
Not all sysctls are namespaced. kpod does not support changing sysctls
inside of a container that also modify the host system. As the kernel
evolves we expect to see more sysctls become namespaced.
See the definition of the `--sysctl` option above for the current list of
supported sysctls.
# HISTORY
April 2014, Originally compiled by William Henry (whenry at redhat dot com)
based on docker.com source material and internal work.
June 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
July 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
November 2015, updated by Sally O'Malley <somalley@redhat.com>
October 2017, converted from Docker documentation to kpod by Dan Walsh for kpod <dwalsh@redhat.com>

View File

@ -1,60 +0,0 @@
% kpod(1) kpod-save - Simple tool to save an image to an archive
% Urvashi Mohnani
# kpod-save "1" "July 2017" "kpod"
## NAME
kpod-save - Save an image to docker-archive or oci-archive
## SYNOPSIS
**kpod save**
**NAME[:TAG]**
[**--quiet**|**-q**]
[**--format**]
[**--output**|**-o**]
[**--help**|**-h**]
## DESCRIPTION
**kpod save** saves an image to either **docker-archive** or **oci-archive**
on the local machine, default is **docker-archive**.
**kpod save** writes to STDOUT by default and can be redirected to a file using the **output** flag.
The **quiet** flag suppresses the output when set.
**kpod [GLOBAL OPTIONS]**
**kpod save [GLOBAL OPTIONS]**
**kpod save [OPTIONS] NAME[:TAG]**
## OPTIONS
**--output, -o**
Write to a file, default is STDOUT
**--format**
Save image to **oci-archive**
```
--format oci-archive
```
**--quiet, -q**
Suppress the output
## EXAMPLES
```
# kpod save --quiet -o alpine.tar alpine:2.6
```
```
# kpod save > alpine-all.tar alpine
```
```
# kpod save -o oci-alpine.tar --format oci-archive alpine
```
## SEE ALSO
kpod(1), kpod-load(1), crio(8), crio.conf(5)
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,37 +0,0 @@
% kpod(1) kpod-stats - Display a live stream of 1 or more containers' resource usage statistics
% Ryan Cole
# kpod-stats "1" "July 2017" "kpod"
## NAME
kpod-stats - Display a live stream of 1 or more containers' resource usage statistics
## SYNOPSIS
**kpod** **stats** [*options* [...]] [container]
## DESCRIPTION
Display a live stream of one or more containers' resource usage statistics
## OPTIONS
**--all, -a**
Show all containers. Only running containers are shown by default
**--no-stream**
Disable streaming stats and only pull the first result, default setting is false
**--format="TEMPLATE"**
Pretty-print images using a Go template
## EXAMPLE
TODO
## SEE ALSO
kpod(1)
## HISTORY
July 2017, Originally compiled by Ryan Cole <rycole@redhat.com>

View File

@ -1,35 +0,0 @@
% kpod(1) kpod-stop - Stop one or more containers
% Brent Baude
# kpod-stop "1" "September 2017" "kpod"
## NAME
kpod stop - Stop one or more containers
## SYNOPSIS
**kpod stop [OPTIONS] CONTAINER [...]**
## DESCRIPTION
Stops one or more containers. You may use container IDs or names as input. The **--timeout** switch
allows you to specify the number of seconds to wait before forcibly stopping the container after the stop command
is issued to the container. The default is 10 seconds.
## OPTIONS
**--timeout, t**
Timeout to wait before forcibly stopping the container
## EXAMPLE
kpod stop mywebserver
kpod stop 860a4b23
kpod stop --timeout 2 860a4b23
## SEE ALSO
kpod(1), kpod-rm(1)
## HISTORY
September 2018, Originally compiled by Brent Baude <bbaude@redhat.com>

View File

@ -1,34 +0,0 @@
% kpod(1) kpod-tag - Add tags to an image
% Ryan Cole
# kpod-tag "1" "July 2017" "kpod"
## NAME
kpod tag - Add an additional name to a local image
## SYNOPSIS
**kpod tag**
[**--help**|**-h**]
## DESCRIPTION
Assigns a new alias to an image in a registry. An alias refers to the entire image name, including the optional **TAG** after the ':'
**kpod [GLOBAL OPTIONS]**
**kpod [GLOBAL OPTIONS] tag [OPTIONS]**
## GLOBAL OPTIONS
**--help, -h**
Print usage statement
## EXAMPLES
kpod tag 0e3bbc2 fedora:latest
kpod tag httpd myregistryhost:5000/fedora/httpd:v2
## SEE ALSO
kpod(1), crio(8), crio.conf(5)
## HISTORY
July 2017, Originally compiled by Ryan Cole <rycole@redhat.com>

View File

@ -1,19 +0,0 @@
% kpod(1) kpod-umount - Unmount a working container's root filesystem.
% Dan Walsh
# kpod-umount "1" "July 2017" "kpod"
## NAME
kpod umount - Unmount a working container's root file system
## SYNOPSIS
**kpod** **umount** **containerID**
## DESCRIPTION
Unmounts the specified container's root file system.
## EXAMPLE
kpod umount containerID
## SEE ALSO
kpod(1), kpod-mount(1)

View File

@ -1,24 +0,0 @@
% kpod(1) kpod-unpause - Unpause one or more containers
% Dan Walsh
# kpod-unpause "1" "September 2017" "kpod"
## NAME
kpod unpause - Unpause one or more containers
## SYNOPSIS
**kpod unpause [OPTIONS] CONTAINER [...]**
## DESCRIPTION
Unpauses the processes in one or more containers. You may use container IDs or names as input.
## EXAMPLE
kpod unpause mywebserver
kpod unpause 860a4b23
## SEE ALSO
kpod(1), kpod-pause(1)
## HISTORY
September 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>

View File

@ -1,24 +0,0 @@
% kpod(1) kpod-version - Simple tool to view version information
% Urvashi Mohnani
# kpod-version "1" "July 2017" "kpod"
## NAME
kpod-version - Display the KPOD Version Information
## SYNOPSIS
**kpod version**
[**--help**|**-h**]
## DESCRIPTION
Shows the the following information: Version, Go Version, Git Commit, Build Time,
OS, and Architecture.
**kpod [GLOBAL OPTIONS]**
**kpod version**
## SEE ALSO
kpod(1), crio(8), crio.conf(5)
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>

View File

@ -1,36 +0,0 @@
% kpod(1) kpod-wait - Waits on a container
% Brent Baude
# kpod-wait "1" "September 2017" "kpod"
## NAME
kpod wait - Waits on one or more containers to stop and prints exit code
## SYNOPSIS
**kpod wait**
[**--help**|**-h**]
## DESCRIPTION
Waits on one or more containers to stop. The container can be referred to by its
name or ID. In the case of multiple containers, kpod will wait on each consecutively.
After the container stops, the container's return code is printed.
**kpod [GLOBAL OPTIONS] wait **
## GLOBAL OPTIONS
**--help, -h**
Print usage statement
## EXAMPLES
kpod wait mywebserver
kpod wait 860a4b23
kpod wait mywebserver myftpserver
## SEE ALSO
kpod(1), crio(8), crio.conf(5)
## HISTORY
September 2017, Originally compiled by Brent Baude<bbaude@redhat.com>

View File

@ -1,143 +0,0 @@
% kpod(1) kpod - Simple management tool for pods and images
% Dan Walsh
# kpod "1" "September 2016" "kpod"
## NAME
kpod - Simple management tool for containers and images
## SYNOPSIS
**kpod** [*options*] COMMAND
# DESCRIPTION
kpod is a simple client only tool to help with debugging issues when daemons
such as CRI runtime and the kubelet are not responding or failing. A shared API
layer could be created to share code between the daemon and kpod. kpod does not
require any daemon running. kpod utilizes the same underlying components that
crio uses i.e. containers/image, container/storage, oci-runtime-tool/generate,
runc or any other OCI compatible runtime. kpod shares state with crio and so
has the capability to debug pods/images created by crio.
**kpod [GLOBAL OPTIONS]**
## GLOBAL OPTIONS
**--help, -h**
Print usage statement
**--config value, -c**=**"config.file"**
Path of a config file detailing container server configuration options
**--log-level**
log messages above specified level: debug, info, warn, error (default), fatal or panic
**--root**=**value**
Path to the root directory in which data, including images, is stored
**--runroot**=**value**
Path to the 'run directory' where all state information is stored
**--runtime**=**value**
Path to the OCI compatible binary used to run containers
**--storage-driver, -s**=**value**
Select which storage driver is used to manage storage of images and containers (default is overlay)
**--storage-opt**=**value**
Used to pass an option to the storage driver
**--version, -v**
Print the version
## COMMANDS
### create
create a new container
### diff
Inspect changes on a container or image's filesystem
### export
Export container's filesystem contents as a tar archive
### history
Shows the history of an image
### images
List images in local storage
### info
Displays system information
### inspect
Display a container or image's configuration
### kill
Kill the main process in one or more containers
### load
Load an image from docker archive
### login
Login to a container registry
### logout
Logout of a container registry
### logs
Display the logs of a container
### mount
Mount a working container's root filesystem
### pause
Pause one or more containers
### ps
Prints out information about containers
### pull
Pull an image from a registry
### push
Push an image from local storage to elsewhere
### rename
Rename a container
### rm
Remove one or more containers
### rmi
Removes one or more locally stored images
### run
Run a command in a new container
### save
Save an image to docker-archive or oci
### stats
Display a live stream of one or more containers' resource usage statistics
### stop
Stops one or more running containers.
### tag
Add an additional name to a local image
### umount
Unmount a working container's root file system
### unpause
Unpause one or more containers
### version
Display the version information
### wait
Wait on one or more containers to stop and print their exit codes
## SEE ALSO
crio(8), crio.conf(5)
## HISTORY
Dec 2016, Originally compiled by Dan Walsh <dwalsh@redhat.com>

File diff suppressed because it is too large Load Diff

View File

@ -1,210 +0,0 @@
package libkpod
import (
"encoding/json"
"os"
"time"
"k8s.io/apimachinery/pkg/fields"
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
"github.com/kubernetes-incubator/cri-o/libpod/driver"
"github.com/kubernetes-incubator/cri-o/libpod/images"
"github.com/kubernetes-incubator/cri-o/oci"
"github.com/opencontainers/image-spec/specs-go/v1"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
// ContainerData handles the data used when inspecting a container
type ContainerData struct {
ID string
Name string
LogPath string
Labels fields.Set
Annotations fields.Set
State *ContainerState
Metadata *pb.ContainerMetadata
BundlePath string
StopSignal string
FromImage string `json:"Image,omitempty"`
FromImageID string `json:"ImageID"`
MountPoint string `json:"Mountpoint,omitempty"`
MountLabel string
Mounts []specs.Mount
AppArmorProfile string
ImageAnnotations map[string]string `json:"Annotations,omitempty"`
ImageCreatedBy string `json:"CreatedBy,omitempty"`
Config v1.ImageConfig `json:"Config,omitempty"`
SizeRw uint `json:"SizeRw,omitempty"`
SizeRootFs uint `json:"SizeRootFs,omitempty"`
Args []string
ResolvConfPath string
HostnamePath string
HostsPath string
GraphDriver driverData
}
type driverData struct {
Name string
Data map[string]string
}
// ContainerState represents the status of a container.
type ContainerState struct {
specs.State
Created time.Time `json:"created"`
Started time.Time `json:"started,omitempty"`
Finished time.Time `json:"finished,omitempty"`
ExitCode int32 `json:"exitCode"`
OOMKilled bool `json:"oomKilled,omitempty"`
Error string `json:"error,omitempty"`
}
// GetContainerData gets the ContainerData for a container with the given name in the given store.
// If size is set to true, it will also determine the size of the container
func (c *ContainerServer) GetContainerData(name string, size bool) (*ContainerData, error) {
ctr, err := c.inspectContainer(name)
if err != nil {
return nil, errors.Wrapf(err, "error reading build container %q", name)
}
container, err := c.store.Container(name)
if err != nil {
return nil, errors.Wrapf(err, "error reading container data")
}
// The runtime configuration won't exist if the container has never been started by cri-o or kpod,
// so treat a not-exist error as non-fatal.
m := getBlankSpec()
config, err := c.store.FromContainerDirectory(ctr.ID(), "config.json")
if err != nil && !os.IsNotExist(errors.Cause(err)) {
return nil, err
}
if len(config) > 0 {
if err = json.Unmarshal(config, &m); err != nil {
return nil, err
}
}
if container.ImageID == "" {
return nil, errors.Errorf("error reading container image data: container is not based on an image")
}
imageData, err := images.GetData(c.store, container.ImageID)
if err != nil {
return nil, errors.Wrapf(err, "error reading container image data")
}
driverName, err := driver.GetDriverName(c.store)
if err != nil {
return nil, err
}
topLayer, err := c.GetContainerTopLayerID(ctr.ID())
if err != nil {
return nil, err
}
layer, err := c.store.Layer(topLayer)
if err != nil {
return nil, err
}
driverMetadata, err := driver.GetDriverMetadata(c.store, topLayer)
if err != nil {
return nil, err
}
imageName := ""
if len(imageData.Tags) > 0 {
imageName = imageData.Tags[0]
} else if len(imageData.Digests) > 0 {
imageName = imageData.Digests[0]
}
data := &ContainerData{
ID: ctr.ID(),
Name: ctr.Name(),
LogPath: ctr.LogPath(),
Labels: ctr.Labels(),
Annotations: ctr.Annotations(),
State: c.State(ctr),
Metadata: ctr.Metadata(),
BundlePath: ctr.BundlePath(),
StopSignal: ctr.GetStopSignal(),
Args: m.Process.Args,
FromImage: imageName,
FromImageID: container.ImageID,
MountPoint: layer.MountPoint,
ImageAnnotations: imageData.Annotations,
ImageCreatedBy: imageData.CreatedBy,
Config: imageData.Config,
GraphDriver: driverData{
Name: driverName,
Data: driverMetadata,
},
MountLabel: m.Linux.MountLabel,
Mounts: m.Mounts,
AppArmorProfile: m.Process.ApparmorProfile,
ResolvConfPath: "",
HostnamePath: "",
HostsPath: "",
}
if size {
sizeRootFs, err := c.GetContainerRootFsSize(data.ID)
if err != nil {
return nil, errors.Wrapf(err, "error reading size for container %q", name)
}
data.SizeRootFs = uint(sizeRootFs)
sizeRw, err := c.GetContainerRwSize(data.ID)
if err != nil {
return nil, errors.Wrapf(err, "error reading RWSize for container %q", name)
}
data.SizeRw = uint(sizeRw)
}
return data, nil
}
// Get an oci.Container and update its status
func (c *ContainerServer) inspectContainer(container string) (*oci.Container, error) {
ociCtr, err := c.LookupContainer(container)
if err != nil {
return nil, err
}
// call runtime.UpdateStatus()
err = c.Runtime().UpdateStatus(ociCtr)
if err != nil {
return nil, err
}
return ociCtr, nil
}
func getBlankSpec() specs.Spec {
return specs.Spec{
Process: &specs.Process{},
Root: &specs.Root{},
Mounts: []specs.Mount{},
Hooks: &specs.Hooks{},
Annotations: make(map[string]string),
Linux: &specs.Linux{},
Solaris: &specs.Solaris{},
Windows: &specs.Windows{},
}
}
// State copies the crio container state to ContainerState type for kpod
func (c *ContainerServer) State(ctr *oci.Container) *ContainerState {
crioState := ctr.State()
specState := specs.State{
Version: crioState.Version,
ID: crioState.ID,
Status: crioState.Status,
Pid: crioState.Pid,
Bundle: crioState.Bundle,
Annotations: crioState.Annotations,
}
cState := &ContainerState{
Started: crioState.Started,
Created: crioState.Created,
Finished: crioState.Finished,
}
cState.State = specState
return cState
}

View File

@ -1,99 +0,0 @@
package common
import (
"io"
"strings"
"syscall"
cp "github.com/containers/image/copy"
"github.com/containers/image/signature"
"github.com/containers/image/types"
"github.com/pkg/errors"
)
var (
// ErrNoPassword is returned if the user did not supply a password
ErrNoPassword = errors.Wrapf(syscall.EINVAL, "password was not supplied")
)
// GetCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters
func GetCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, authFile string) *cp.Options {
if srcDockerRegistry == nil {
srcDockerRegistry = &DockerRegistryOptions{}
}
if destDockerRegistry == nil {
destDockerRegistry = &DockerRegistryOptions{}
}
srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath, authFile)
destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath, authFile)
return &cp.Options{
RemoveSignatures: signing.RemoveSignatures,
SignBy: signing.SignBy,
ReportWriter: reportWriter,
SourceCtx: srcContext,
DestinationCtx: destContext,
}
}
// GetSystemContext Constructs a new containers/image/types.SystemContext{} struct from the given signaturePolicy path
func GetSystemContext(signaturePolicyPath, authFilePath string) *types.SystemContext {
sc := &types.SystemContext{}
if signaturePolicyPath != "" {
sc.SignaturePolicyPath = signaturePolicyPath
}
sc.AuthFilePath = authFilePath
return sc
}
// CopyStringStringMap deep copies a map[string]string and returns the result
func CopyStringStringMap(m map[string]string) map[string]string {
n := map[string]string{}
for k, v := range m {
n[k] = v
}
return n
}
// IsTrue determines whether the given string equals "true"
func IsTrue(str string) bool {
return str == "true"
}
// IsFalse determines whether the given string equals "false"
func IsFalse(str string) bool {
return str == "false"
}
// IsValidBool determines whether the given string equals "true" or "false"
func IsValidBool(str string) bool {
return IsTrue(str) || IsFalse(str)
}
// GetPolicyContext creates a signature policy context for the given signature policy path
func GetPolicyContext(path string) (*signature.PolicyContext, error) {
policy, err := signature.DefaultPolicy(&types.SystemContext{SignaturePolicyPath: path})
if err != nil {
return nil, err
}
return signature.NewPolicyContext(policy)
}
// ParseRegistryCreds takes a credentials string in the form USERNAME:PASSWORD
// and returns a DockerAuthConfig
func ParseRegistryCreds(creds string) (*types.DockerAuthConfig, error) {
if creds == "" {
return nil, errors.New("no credentials supplied")
}
if !strings.Contains(creds, ":") {
return &types.DockerAuthConfig{
Username: creds,
Password: "",
}, ErrNoPassword
}
v := strings.SplitN(creds, ":", 2)
cfg := &types.DockerAuthConfig{
Username: v[0],
Password: v[1],
}
return cfg, nil
}

View File

@ -1,34 +0,0 @@
package common
import "github.com/containers/image/types"
// DockerRegistryOptions encapsulates settings that affect how we connect or
// authenticate to a remote registry.
type DockerRegistryOptions struct {
// DockerRegistryCreds is the user name and password to supply in case
// we need to pull an image from a registry, and it requires us to
// authenticate.
DockerRegistryCreds *types.DockerAuthConfig
// DockerCertPath is the location of a directory containing CA
// certificates which will be used to verify the registry's certificate
// (all files with names ending in ".crt"), and possibly client
// certificates and private keys (pairs of files with the same name,
// except for ".cert" and ".key" suffixes).
DockerCertPath string
// DockerInsecureSkipTLSVerify turns off verification of TLS
// certificates and allows connecting to registries without encryption.
DockerInsecureSkipTLSVerify bool
}
// GetSystemContext constructs a new system context from the given signaturePolicy path and the
// values in the DockerRegistryOptions
func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile string) *types.SystemContext {
sc := &types.SystemContext{
SignaturePolicyPath: signaturePolicyPath,
DockerAuthConfig: o.DockerRegistryCreds,
DockerCertPath: o.DockerCertPath,
DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify,
AuthFilePath: authFile,
}
return sc
}

View File

@ -1 +0,0 @@
package common

View File

@ -1,10 +0,0 @@
package common
// SigningOptions encapsulates settings that control whether or not we strip or
// add signatures to images when writing them.
type SigningOptions struct {
// RemoveSignatures directs us to remove any signatures which are already present.
RemoveSignatures bool
// SignBy is a key identifier of some kind, indicating that a signature should be generated using the specified private key and stored with the image.
SignBy string
}

View File

@ -1,454 +0,0 @@
package libpod
import (
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
"sync"
"time"
"github.com/containers/storage"
"github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/term"
crioAnnotations "github.com/kubernetes-incubator/cri-o/pkg/annotations"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/ulule/deepcopier"
"k8s.io/client-go/tools/remotecommand"
)
// ContainerState represents the current state of a container
type ContainerState int
const (
// ContainerStateUnknown indicates that the container is in an error
// state where information about it cannot be retrieved
ContainerStateUnknown ContainerState = iota
// ContainerStateConfigured indicates that the container has had its
// storage configured but it has not been created in the OCI runtime
ContainerStateConfigured ContainerState = iota
// ContainerStateCreated indicates the container has been created in
// the OCI runtime but not started
ContainerStateCreated ContainerState = iota
// ContainerStateRunning indicates the container is currently executing
ContainerStateRunning ContainerState = iota
// ContainerStateStopped indicates that the container was running but has
// exited
ContainerStateStopped ContainerState = iota
// ContainerStatePaused indicates that the container has been paused
ContainerStatePaused ContainerState = iota
)
// Container is a single OCI container
type Container struct {
config *containerConfig
pod *Pod
runningSpec *spec.Spec
state *containerRuntimeInfo
// TODO move to storage.Locker from sync.Mutex
valid bool
lock sync.Mutex
runtime *Runtime
}
// containerState contains the current state of the container
// It is stored on disk in a tmpfs and recreated on reboot
type containerRuntimeInfo struct {
// The current state of the running container
State ContainerState `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"`
// Mounted indicates whether the container's storage has been mounted
// for use
Mounted bool `json:"-"`
// MountPoint contains the path to the container's mounted storage
Mountpoint string `json:"mountPoint,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"`
// TODO: Save information about image used in container if one is used
}
// 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
type containerConfig struct {
Spec *spec.Spec `json:"spec"`
ID string `json:"id"`
Name string `json:"name"`
// RootfsFromImage indicates whether the container uses a root
// filesystem from an image, or from a user-provided directory
RootfsFromImage bool
// Directory used as a root filesystem if not configured with an image
RootfsDir string `json:"rootfsDir,omitempty"`
// Information on the image used for the root filesystem
RootfsImageID string `json:"rootfsImageID,omitempty"`
RootfsImageName string `json:"rootfsImageName,omitempty"`
UseImageConfig bool `json:"useImageConfig"`
// Whether to keep container STDIN open
Stdin bool
// Static directory for container content that will persist across
// reboot
StaticDir string `json:"staticDir"`
// Pod the container belongs to
Pod string `json:"pod,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"`
// Shared namespaces with container
SharedNamespaceCtr *string `json:"shareNamespacesWith,omitempty"`
SharedNamespaceMap map[string]string `json:"sharedNamespaces"`
// Time container was created
CreatedTime time.Time `json:"createdTime"`
// TODO save log location here and pass into OCI code
// TODO allow overriding of log path
}
// 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
}
// 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 {
spec := new(spec.Spec)
deepcopier.Copy(c.config.Spec).To(spec)
return spec
}
// 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
}
// State returns the current state of the container
func (c *Container) State() (ContainerState, error) {
c.lock.Lock()
defer c.lock.Unlock()
// TODO uncomment when working
// if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil {
// return ContainerStateUnknown, err
// }
return c.state.State, nil
}
// The path to the container's root filesystem - where the OCI spec will be
// placed, amongst other things
func (c *Container) bundlePath() string {
return c.state.RunDir
}
// Retrieves the path of the container's attach socket
func (c *Container) attachSocketPath() string {
return filepath.Join(c.runtime.ociRuntime.socketsDir, c.ID(), "attach")
}
// Make a new container
func newContainer(rspec *spec.Spec) (*Container, error) {
if rspec == nil {
return nil, errors.Wrapf(ErrInvalidArg, "must provide a valid runtime spec to create container")
}
ctr := new(Container)
ctr.config = new(containerConfig)
ctr.state = new(containerRuntimeInfo)
ctr.config.ID = stringid.GenerateNonCryptoID()
ctr.config.Name = ctr.config.ID // TODO generate unique human-readable names
ctr.config.Spec = new(spec.Spec)
deepcopier.Copy(rspec).To(ctr.config.Spec)
ctr.config.CreatedTime = time.Now()
return ctr, nil
}
// Create container root filesystem for use
func (c *Container) setupStorage() error {
c.lock.Lock()
defer c.lock.Unlock()
if !c.valid {
return errors.Wrapf(ErrCtrRemoved, "container %s is not valid", c.ID())
}
if c.state.State != ContainerStateConfigured {
return errors.Wrapf(ErrCtrStateInvalid, "container %s must be in Configured state to have storage set up", c.ID())
}
// If we're configured to use a directory, perform that setup
if !c.config.RootfsFromImage {
// TODO implement directory-based root filesystems
return ErrNotImplemented
}
// Not using a directory, so call into containers/storage
return c.setupImageRootfs()
}
// Set up an image as root filesystem using containers/storage
func (c *Container) setupImageRootfs() error {
// Need both an image ID and image name, plus a bool telling us whether to use the image configuration
if c.config.RootfsImageID == "" || c.config.RootfsImageName == "" {
return errors.Wrapf(ErrInvalidArg, "must provide image ID and image name to use an image")
}
// TODO SELinux mount label
containerInfo, err := c.runtime.storageService.CreateContainerStorage(c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, "")
if err != nil {
return errors.Wrapf(err, "error creating container storage")
}
c.config.StaticDir = containerInfo.Dir
c.state.RunDir = containerInfo.RunDir
return nil
}
// Tear down a container's storage prior to removal
func (c *Container) teardownStorage() error {
c.lock.Lock()
defer c.lock.Unlock()
if !c.valid {
return errors.Wrapf(ErrCtrRemoved, "container %s is not valid", c.ID())
}
if c.state.State == ContainerStateRunning || c.state.State == ContainerStatePaused {
return errors.Wrapf(ErrCtrStateInvalid, "cannot remove storage for container %s as it is running or paused", c.ID())
}
if !c.config.RootfsFromImage {
// TODO implement directory-based root filesystems
return ErrNotImplemented
}
return c.teardownImageRootfs()
}
// Completely remove image-based root filesystem for a container
func (c *Container) teardownImageRootfs() error {
if c.state.Mounted {
if err := c.runtime.storageService.StopContainer(c.ID()); err != nil {
return errors.Wrapf(err, "error unmounting container %s root filesystem", c.ID())
}
c.state.Mounted = false
}
if err := c.runtime.storageService.DeleteContainer(c.ID()); err != nil {
return errors.Wrapf(err, "error removing container %s root filesystem", c.ID())
}
return nil
}
// Create creates a container in the OCI runtime
func (c *Container) Create() (err error) {
c.lock.Lock()
defer c.lock.Unlock()
if !c.valid {
return errors.Wrapf(ErrCtrRemoved, "container %s is not valid", c.ID())
}
if c.state.State != ContainerStateConfigured {
return errors.Wrapf(ErrCtrExists, "container %s has already been created in runtime", c.ID())
}
// If using containers/storage, mount the container
if !c.config.RootfsFromImage {
// TODO implement directory-based root filesystems
if !c.state.Mounted {
return ErrNotImplemented
}
} else {
mountPoint, err := c.runtime.storageService.StartContainer(c.ID())
if err != nil {
return errors.Wrapf(err, "error mounting storage for container %s", c.ID())
}
c.state.Mounted = true
c.state.Mountpoint = mountPoint
logrus.Debugf("Created root filesystem for container %s at %s", c.ID(), c.state.Mountpoint)
defer func() {
if err != nil {
if err2 := c.runtime.storageService.StopContainer(c.ID()); err2 != nil {
logrus.Errorf("Error unmounting storage for container %s: %v", c.ID(), err2)
}
c.state.Mounted = false
c.state.Mountpoint = ""
}
}()
}
// Make the OCI runtime spec we will use
c.runningSpec = new(spec.Spec)
deepcopier.Copy(c.config.Spec).To(c.runningSpec)
c.runningSpec.Root.Path = c.state.Mountpoint
c.runningSpec.Annotations[crioAnnotations.Created] = c.config.CreatedTime.Format(time.RFC3339Nano)
c.runningSpec.Annotations["org.opencontainers.image.stopSignal"] = fmt.Sprintf("%d", c.config.StopSignal)
// Save the OCI spec to disk
jsonPath := filepath.Join(c.bundlePath(), "config.json")
fileJSON, err := json.Marshal(c.runningSpec)
if err != nil {
return errors.Wrapf(err, "error exporting runtime spec for container %s to JSON", c.ID())
}
if err := ioutil.WriteFile(jsonPath, fileJSON, 0644); err != nil {
return errors.Wrapf(err, "error writing runtime spec JSON to file for container %s", c.ID())
}
c.state.ConfigPath = jsonPath
logrus.Debugf("Created OCI spec for container %s at %s", c.ID(), jsonPath)
// With the spec complete, do an OCI create
// TODO set cgroup parent in a sane fashion
if err := c.runtime.ociRuntime.createContainer(c, "/libpod_parent"); err != nil {
return err
}
logrus.Debugf("Created container %s in runc", c.ID())
// TODO should flush this state to disk here
c.state.State = ContainerStateCreated
return nil
}
// Start starts a container
func (c *Container) Start() error {
c.lock.Lock()
defer c.lock.Unlock()
if !c.valid {
return ErrCtrRemoved
}
// Container must be created or stopped to be started
if !(c.state.State == ContainerStateCreated || c.state.State == ContainerStateStopped) {
return errors.Wrapf(ErrCtrStateInvalid, "container %s must be in Created or Stopped state to be started", c.ID())
}
if err := c.runtime.ociRuntime.startContainer(c); err != nil {
return err
}
logrus.Debugf("Started container %s", c.ID())
// TODO should flush state to disk here
c.state.StartedTime = time.Now()
c.state.State = ContainerStateRunning
return nil
}
// Stop stops a container
func (c *Container) Stop() error {
return ErrNotImplemented
}
// Kill sends a signal to a container
func (c *Container) Kill(signal uint) error {
return ErrNotImplemented
}
// Exec starts a new process inside the container
// Returns fully qualified URL of streaming server for executed process
func (c *Container) Exec(cmd []string, tty bool, stdin bool) (string, error) {
return "", ErrNotImplemented
}
// Attach attaches to a container
// Returns fully qualified URL of streaming server for the container
func (c *Container) Attach(noStdin bool, keys string) error {
// Check the validity of the provided keys first
var err error
detachKeys := []byte{}
if len(keys) > 0 {
detachKeys, err = term.ToBytes(keys)
if err != nil {
return errors.Wrapf(err, "invalid detach keys")
}
}
cStatus := c.state.State
if !(cStatus == ContainerStateRunning || cStatus == ContainerStateCreated) {
return errors.Errorf("%s is not created or running", c.Name())
}
resize := make(chan remotecommand.TerminalSize)
defer close(resize)
err = c.attachContainerSocket(resize, noStdin, detachKeys)
if err != nil {
return err
}
// TODO
// Re-enable this when mheon is done wth it
//c.ContainerStateToDisk(c)
return nil
}
// Mount mounts a container's filesystem on the host
// The path where the container has been mounted is returned
func (c *Container) Mount() (string, error) {
return "", ErrNotImplemented
}
// Pause pauses a container
func (c *Container) Pause() error {
return ErrNotImplemented
}
// Unpause unpauses a container
func (c *Container) Unpause() error {
return ErrNotImplemented
}
// Export exports a container's root filesystem as a tar archive
// The archive will be saved as a file at the given path
func (c *Container) Export(path string) error {
return ErrNotImplemented
}
// Commit commits the changes between a container and its image, creating a new
// image
// If the container was not created from an image (for example,
// WithRootFSFromPath will create a container from a directory on the system),
// a new base image will be created from the contents of the container's
// filesystem
func (c *Container) Commit() (*storage.Image, error) {
return nil, ErrNotImplemented
}

View File

@ -1,142 +0,0 @@
package libpod
import (
"fmt"
"io"
"net"
"os"
"path/filepath"
"strconv"
"github.com/docker/docker/pkg/term"
"github.com/kubernetes-incubator/cri-o/utils"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"k8s.io/client-go/tools/remotecommand"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
)
/* Sync with stdpipe_t in conmon.c */
const (
AttachPipeStdin = 1
AttachPipeStdout = 2
AttachPipeStderr = 3
)
// attachContainerSocket connects to the container's attach socket and deals with the IO
func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSize, noStdIn bool, detachKeys []byte) error {
inputStream := os.Stdin
outputStream := os.Stdout
errorStream := os.Stderr
defer inputStream.Close()
tty, err := strconv.ParseBool(c.runningSpec.Annotations["io.kubernetes.cri-o.TTY"])
if err != nil {
return errors.Wrapf(err, "unable to parse annotations in %s", c.ID)
}
if !tty {
return errors.Errorf("no tty available for %s", c.ID())
}
oldTermState, err := term.SaveState(inputStream.Fd())
if err != nil {
return errors.Wrapf(err, "unable to save terminal state")
}
defer term.RestoreTerminal(inputStream.Fd(), oldTermState)
// Put both input and output into raw
if !noStdIn {
term.SetRawTerminal(inputStream.Fd())
}
controlPath := filepath.Join(c.state.RunDir, "ctl")
controlFile, err := os.OpenFile(controlPath, unix.O_WRONLY, 0)
if err != nil {
return errors.Wrapf(err, "failed to open container ctl file: %v")
}
kubecontainer.HandleResizing(resize, func(size remotecommand.TerminalSize) {
logrus.Debugf("Received a resize event: %+v", size)
_, err := fmt.Fprintf(controlFile, "%d %d %d\n", 1, size.Height, size.Width)
if err != nil {
logrus.Warnf("Failed to write to control file to resize terminal: %v", err)
}
})
logrus.Debug("connecting to socket ", c.attachSocketPath())
conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: c.attachSocketPath(), Net: "unixpacket"})
if err != nil {
return errors.Wrapf(err, "failed to connect to container's attach socket: %v")
}
defer conn.Close()
receiveStdoutError := make(chan error)
if outputStream != nil || errorStream != nil {
go func() {
receiveStdoutError <- redirectResponseToOutputStreams(outputStream, errorStream, conn)
}()
}
stdinDone := make(chan error)
go func() {
var err error
if inputStream != nil && !noStdIn {
_, err = utils.CopyDetachable(conn, inputStream, detachKeys)
conn.CloseWrite()
}
stdinDone <- err
}()
select {
case err := <-receiveStdoutError:
return err
case err := <-stdinDone:
if _, ok := err.(utils.DetachError); ok {
return nil
}
if outputStream != nil || errorStream != nil {
return <-receiveStdoutError
}
}
return nil
}
func redirectResponseToOutputStreams(outputStream, errorStream io.Writer, conn io.Reader) error {
var err error
buf := make([]byte, 8192+1) /* Sync with conmon STDIO_BUF_SIZE */
for {
nr, er := conn.Read(buf)
if nr > 0 {
var dst io.Writer
switch buf[0] {
case AttachPipeStdout:
dst = outputStream
case AttachPipeStderr:
dst = errorStream
default:
logrus.Infof("Received unexpected attach type %+d", buf[0])
}
if dst != nil {
nw, ew := dst.Write(buf[1:nr])
if ew != nil {
err = ew
break
}
if nr != nw+1 {
err = io.ErrShortWrite
break
}
}
}
if er == io.EOF {
break
}
if er != nil {
err = er
break
}
}
return err
}

View File

@ -1,53 +0,0 @@
package libpod
import (
"github.com/containers/storage/pkg/archive"
"github.com/kubernetes-incubator/cri-o/libpod/layers"
"github.com/pkg/errors"
)
// GetDiff returns the differences between the two images, layers, or containers
func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) {
toLayer, err := r.getLayerID(to)
if err != nil {
return nil, err
}
fromLayer := ""
if from != "" {
fromLayer, err = r.getLayerID(from)
if err != nil {
return nil, err
}
}
return r.store.Changes(fromLayer, toLayer)
}
// GetLayerID gets a full layer id given a full or partial id
// If the id matches a container or image, the id of the top layer is returned
// If the id matches a layer, the top layer id is returned
func (r *Runtime) getLayerID(id string) (string, error) {
var toLayer string
toImage, err := r.GetImage(id)
if err != nil {
toCtr, err := r.store.Container(id)
if err != nil {
toLayer, err = layers.FullID(r.store, id)
if err != nil {
return "", errors.Errorf("layer, image, or container %s does not exist", id)
}
} else {
toLayer = toCtr.LayerID
}
} else {
toLayer = toImage.TopLayer
}
return toLayer, nil
}
func (r *Runtime) getLayerParent(layerID string) (string, error) {
layer, err := r.store.Layer(layerID)
if err != nil {
return "", err
}
return layer.Parent, nil
}

View File

@ -1,27 +0,0 @@
package driver
import cstorage "github.com/containers/storage"
// Data handles the data for a storage driver
type Data struct {
Name string
Data map[string]string
}
// GetDriverName returns the name of the driver for the given store
func GetDriverName(store cstorage.Store) (string, error) {
driver, err := store.GraphDriver()
if err != nil {
return "", err
}
return driver.String(), nil
}
// GetDriverMetadata returns the metadata regarding the driver for the layer in the given store
func GetDriverMetadata(store cstorage.Store, layerID string) (map[string]string, error) {
driver, err := store.GraphDriver()
if err != nil {
return nil, err
}
return driver.Metadata(layerID)
}

View File

@ -1,62 +0,0 @@
package libpod
import (
"errors"
)
var (
// ErrNoSuchCtr indicates the requested container does not exist
ErrNoSuchCtr = errors.New("no such container")
// ErrNoSuchPod indicates the requested pod does not exist
ErrNoSuchPod = errors.New("no such pod")
// ErrNoSuchImage indicates the requested image does not exist
ErrNoSuchImage = errors.New("no such image")
// ErrCtrExists indicates a container with the same name or ID already
// exists
ErrCtrExists = errors.New("container already exists")
// ErrPodExists indicates a pod with the same name or ID already exists
ErrPodExists = errors.New("pod already exists")
// ErrImageExists indicated an image with the same ID already exists
ErrImageExists = errors.New("image already exists")
// ErrCtrStateInvalid indicates a container is in an improper state for
// the requested operation
ErrCtrStateInvalid = errors.New("container state improper")
// ErrRuntimeFinalized indicates that the runtime has already been
// created and cannot be modified
ErrRuntimeFinalized = errors.New("runtime has been finalized")
// ErrCtrFinalized indicates that the container has already been created
// and cannot be modified
ErrCtrFinalized = errors.New("container has been finalized")
// ErrPodFinalized indicates that the pod has already been created and
// cannot be modified
ErrPodFinalized = errors.New("pod has been finalized")
// ErrInvalidArg indicates that an invalid argument was passed
ErrInvalidArg = errors.New("invalid argument")
// ErrEmptyID indicates that an empty ID was passed
ErrEmptyID = errors.New("name or ID cannot be empty")
// ErrInternal indicates an internal library error
ErrInternal = errors.New("internal libpod error")
// ErrRuntimeStopped indicates that the runtime has already been shut
// down and no further operations can be performed on it
ErrRuntimeStopped = errors.New("runtime has already been stopped")
// ErrCtrStopped indicates that the requested container is not running
// and the requested operation cannot be performed until it is started
ErrCtrStopped = errors.New("container is stopped")
// ErrCtrRemoved indicates that the container has already been removed
// and no further operations can be performed on it
ErrCtrRemoved = errors.New("container has already been removed")
// ErrPodRemoved indicates that the pod has already been removed and no
// further operations can be performed on it
ErrPodRemoved = errors.New("pod has already been removed")
// ErrNotImplemented indicates that the requested functionality is not
// yet present
ErrNotImplemented = errors.New("not yet implemented")
)

View File

@ -1,203 +0,0 @@
package images
import (
"encoding/json"
"time"
"github.com/containers/image/docker/reference"
is "github.com/containers/image/storage"
"github.com/containers/image/transports"
"github.com/containers/image/types"
"github.com/containers/storage"
"github.com/kubernetes-incubator/cri-o/libpod/driver"
digest "github.com/opencontainers/go-digest"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// Data handles the data used when inspecting a container
// nolint
type Data struct {
ID string
Tags []string
Digests []string
Digest digest.Digest
Comment string
Created *time.Time
Container string
Author string
Config ociv1.ImageConfig
Architecture string
OS string
Annotations map[string]string
CreatedBy string
Size uint
VirtualSize uint
GraphDriver driver.Data
RootFS ociv1.RootFS
}
// ParseImageNames parses the names we've stored with an image into a list of
// tagged references and a list of references which contain digests.
func ParseImageNames(names []string) (tags, digests []string, err error) {
for _, name := range names {
if named, err := reference.ParseNamed(name); err == nil {
if digested, ok := named.(reference.Digested); ok {
canonical, err := reference.WithDigest(named, digested.Digest())
if err == nil {
digests = append(digests, canonical.String())
}
} else {
if reference.IsNameOnly(named) {
named = reference.TagNameOnly(named)
}
if tagged, ok := named.(reference.Tagged); ok {
namedTagged, err := reference.WithTag(named, tagged.Tag())
if err == nil {
tags = append(tags, namedTagged.String())
}
}
}
}
}
return tags, digests, nil
}
func annotations(manifest []byte, manifestType string) map[string]string {
annotations := make(map[string]string)
switch manifestType {
case ociv1.MediaTypeImageManifest:
var m ociv1.Manifest
if err := json.Unmarshal(manifest, &m); err == nil {
for k, v := range m.Annotations {
annotations[k] = v
}
}
}
return annotations
}
// GetData gets the Data for a container with the given name in the given store.
func GetData(store storage.Store, name string) (*Data, error) {
img, err := FindImage(store, name)
if err != nil {
return nil, errors.Wrapf(err, "error reading image %q", name)
}
imgRef, err := FindImageRef(store, "@"+img.ID)
if err != nil {
return nil, errors.Wrapf(err, "error reading image %q", img.ID)
}
defer imgRef.Close()
tags, digests, err := ParseImageNames(img.Names)
if err != nil {
return nil, errors.Wrapf(err, "error parsing image names for %q", name)
}
driverName, err := driver.GetDriverName(store)
if err != nil {
return nil, errors.Wrapf(err, "error reading name of storage driver")
}
topLayerID := img.TopLayer
driverMetadata, err := driver.GetDriverMetadata(store, topLayerID)
if err != nil {
return nil, errors.Wrapf(err, "error asking storage driver %q for metadata", driverName)
}
layer, err := store.Layer(topLayerID)
if err != nil {
return nil, errors.Wrapf(err, "error reading information about layer %q", topLayerID)
}
size, err := store.DiffSize(layer.Parent, layer.ID)
if err != nil {
return nil, errors.Wrapf(err, "error determining size of layer %q", layer.ID)
}
imgSize, err := imgRef.Size()
if err != nil {
return nil, errors.Wrapf(err, "error determining size of image %q", transports.ImageName(imgRef.Reference()))
}
manifest, manifestType, err := imgRef.Manifest()
if err != nil {
return nil, errors.Wrapf(err, "error reading manifest for image %q", img.ID)
}
manifestDigest := digest.Digest("")
if len(manifest) > 0 {
manifestDigest = digest.Canonical.FromBytes(manifest)
}
annotations := annotations(manifest, manifestType)
config, err := imgRef.OCIConfig()
if err != nil {
return nil, errors.Wrapf(err, "error reading image configuration for %q", img.ID)
}
historyComment := ""
historyCreatedBy := ""
if len(config.History) > 0 {
historyComment = config.History[len(config.History)-1].Comment
historyCreatedBy = config.History[len(config.History)-1].CreatedBy
}
return &Data{
ID: img.ID,
Tags: tags,
Digests: digests,
Digest: manifestDigest,
Comment: historyComment,
Created: config.Created,
Author: config.Author,
Config: config.Config,
Architecture: config.Architecture,
OS: config.OS,
Annotations: annotations,
CreatedBy: historyCreatedBy,
Size: uint(size),
VirtualSize: uint(size + imgSize),
GraphDriver: driver.Data{
Name: driverName,
Data: driverMetadata,
},
RootFS: config.RootFS,
}, nil
}
// FindImage searches for a *storage.Image with a matching the given name or ID in the given store.
func FindImage(store storage.Store, image string) (*storage.Image, error) {
var img *storage.Image
ref, err := is.Transport.ParseStoreReference(store, image)
if err == nil {
img, err = is.Transport.GetStoreImage(store, ref)
}
if err != nil {
img2, err2 := store.Image(image)
if err2 != nil {
if ref == nil {
return nil, errors.Wrapf(err, "error parsing reference to image %q", image)
}
return nil, errors.Wrapf(err, "unable to locate image %q", image)
}
img = img2
}
return img, nil
}
// FindImageRef searches for and returns a new types.Image matching the given name or ID in the given store.
func FindImageRef(store storage.Store, image string) (types.Image, error) {
img, err := FindImage(store, image)
if err != nil {
return nil, errors.Wrapf(err, "unable to locate image %q", image)
}
ref, err := is.Transport.ParseStoreReference(store, "@"+img.ID)
if err != nil {
return nil, errors.Wrapf(err, "error parsing reference to image %q", img.ID)
}
imgRef, err := ref.NewImage(nil)
if err != nil {
return nil, errors.Wrapf(err, "error reading image %q", img.ID)
}
return imgRef, nil
}

View File

@ -1,273 +0,0 @@
package libpod
import (
"github.com/docker/docker/pkg/truncindex"
"github.com/kubernetes-incubator/cri-o/pkg/registrar"
"github.com/pkg/errors"
)
// An InMemoryState is a purely in-memory state store
type InMemoryState struct {
pods map[string]*Pod
containers map[string]*Container
podNameIndex *registrar.Registrar
podIDIndex *truncindex.TruncIndex
ctrNameIndex *registrar.Registrar
ctrIDIndex *truncindex.TruncIndex
}
// NewInMemoryState initializes a new, empty in-memory state
func NewInMemoryState() (State, error) {
state := new(InMemoryState)
state.pods = make(map[string]*Pod)
state.containers = make(map[string]*Container)
state.podNameIndex = registrar.NewRegistrar()
state.ctrNameIndex = registrar.NewRegistrar()
state.podIDIndex = truncindex.NewTruncIndex([]string{})
state.ctrIDIndex = truncindex.NewTruncIndex([]string{})
return state, nil
}
// Container retrieves a container from its full ID
func (s *InMemoryState) Container(id string) (*Container, error) {
if id == "" {
return nil, ErrEmptyID
}
ctr, ok := s.containers[id]
if !ok {
return nil, errors.Wrapf(ErrNoSuchCtr, "no container with ID %s found", id)
}
return ctr, nil
}
// LookupContainer retrieves a container by full ID, unique partial ID, or name
func (s *InMemoryState) LookupContainer(idOrName string) (*Container, error) {
if idOrName == "" {
return nil, ErrEmptyID
}
fullID, err := s.ctrNameIndex.Get(idOrName)
if err != nil {
if err == registrar.ErrNameNotReserved {
// What was passed is not a name, assume it's an ID
fullID, err = s.ctrIDIndex.Get(idOrName)
if err != nil {
if err == truncindex.ErrNotExist {
return nil, errors.Wrapf(ErrNoSuchCtr, "no container found with name or ID %s", idOrName)
}
return nil, errors.Wrapf(err, "error performing truncindex lookup for ID %s", idOrName)
}
} else {
return nil, errors.Wrapf(err, "error performing registry lookup for ID %s", idOrName)
}
}
ctr, ok := s.containers[fullID]
if !ok {
// This should never happen
return nil, errors.Wrapf(ErrInternal, "mismatch in container ID registry and containers map for ID %s", fullID)
}
return ctr, nil
}
// HasContainer checks if a container with the given ID is present in the state
func (s *InMemoryState) HasContainer(id string) (bool, error) {
if id == "" {
return false, ErrEmptyID
}
_, ok := s.containers[id]
return ok, nil
}
// AddContainer adds a container to the state
// If the container belongs to a pod, the pod must already be present when the
// container is added, and the container must be present in the pod
func (s *InMemoryState) AddContainer(ctr *Container) error {
if !ctr.valid {
return errors.Wrapf(ErrCtrRemoved, "container with ID %s is not valid", ctr.ID())
}
_, ok := s.containers[ctr.ID()]
if ok {
return errors.Wrapf(ErrCtrExists, "container with ID %s already exists in state", ctr.ID())
}
if ctr.pod != nil {
if _, ok := s.pods[ctr.pod.ID()]; !ok {
return errors.Wrapf(ErrNoSuchPod, "pod %s does not exist, cannot add container %s", ctr.pod.ID(), ctr.ID())
}
hasCtr, err := ctr.pod.HasContainer(ctr.ID())
if err != nil {
return errors.Wrapf(err, "error checking if container %s is present in pod %s", ctr.ID(), ctr.pod.ID())
} else if !hasCtr {
return errors.Wrapf(ErrNoSuchCtr, "container %s is not present in pod %s", ctr.ID(), ctr.pod.ID())
}
}
if err := s.ctrNameIndex.Reserve(ctr.Name(), ctr.ID()); err != nil {
return errors.Wrapf(err, "error registering container name %s", ctr.Name())
}
if err := s.ctrIDIndex.Add(ctr.ID()); err != nil {
s.ctrNameIndex.Release(ctr.Name())
return errors.Wrapf(err, "error registering container ID %s", ctr.ID())
}
s.containers[ctr.ID()] = ctr
return nil
}
// RemoveContainer removes a container from the state
// The container will only be removed from the state, not from the pod the container belongs to
func (s *InMemoryState) RemoveContainer(ctr *Container) error {
// Almost no validity checks are performed, to ensure we can kick
// misbehaving containers out of the state
if _, ok := s.containers[ctr.ID()]; !ok {
return errors.Wrapf(ErrNoSuchCtr, "no container exists in state with ID %s", ctr.ID())
}
if err := s.ctrIDIndex.Delete(ctr.ID()); err != nil {
return errors.Wrapf(err, "error removing container ID from index")
}
delete(s.containers, ctr.ID())
s.ctrNameIndex.Release(ctr.Name())
return nil
}
// AllContainers retrieves all containers from the state
func (s *InMemoryState) AllContainers() ([]*Container, error) {
ctrs := make([]*Container, 0, len(s.containers))
for _, ctr := range s.containers {
ctrs = append(ctrs, ctr)
}
return ctrs, nil
}
// Pod retrieves a pod from the state from its full ID
func (s *InMemoryState) Pod(id string) (*Pod, error) {
if id == "" {
return nil, ErrEmptyID
}
pod, ok := s.pods[id]
if !ok {
return nil, errors.Wrapf(ErrNoSuchPod, "no pod with id %s found", id)
}
return pod, nil
}
// LookupPod retrieves a pod from the state from a full or unique partial ID or
// a full name
func (s *InMemoryState) LookupPod(idOrName string) (*Pod, error) {
if idOrName == "" {
return nil, ErrEmptyID
}
fullID, err := s.podNameIndex.Get(idOrName)
if err != nil {
if err == registrar.ErrNameNotReserved {
// What was passed is not a name, assume it's an ID
fullID, err = s.podIDIndex.Get(idOrName)
if err != nil {
if err == truncindex.ErrNotExist {
return nil, errors.Wrapf(ErrNoSuchPod, "no pod found with name or ID %s", idOrName)
}
return nil, errors.Wrapf(err, "error performing truncindex lookup for ID %s", idOrName)
}
} else {
return nil, errors.Wrapf(err, "error performing registry lookup for ID %s", idOrName)
}
}
pod, ok := s.pods[fullID]
if !ok {
// This should never happen
return nil, errors.Wrapf(ErrInternal, "mismatch in pod ID registry and pod map for ID %s", fullID)
}
return pod, nil
}
// HasPod checks if a pod with the given ID is present in the state
func (s *InMemoryState) HasPod(id string) (bool, error) {
if id == "" {
return false, ErrEmptyID
}
_, ok := s.pods[id]
return ok, nil
}
// AddPod adds a given pod to the state
// Only empty pods can be added to the state
func (s *InMemoryState) AddPod(pod *Pod) error {
if !pod.valid {
return errors.Wrapf(ErrPodRemoved, "pod %s is not valid and cannot be added", pod.ID())
}
if _, ok := s.pods[pod.ID()]; ok {
return errors.Wrapf(ErrPodExists, "pod with ID %s already exists in state", pod.ID())
}
if len(pod.containers) != 0 {
return errors.Wrapf(ErrInternal, "only empty pods can be added to the state")
}
if err := s.podNameIndex.Reserve(pod.Name(), pod.ID()); err != nil {
return errors.Wrapf(err, "error registering pod name %s", pod.Name())
}
if err := s.podIDIndex.Add(pod.ID()); err != nil {
s.podNameIndex.Release(pod.Name())
return errors.Wrapf(err, "error registering pod ID %s", pod.ID())
}
s.pods[pod.ID()] = pod
return nil
}
// RemovePod removes a given pod from the state
// Containers within the pod will not be removed or changed
func (s *InMemoryState) RemovePod(pod *Pod) error {
// Don't make many validity checks to ensure we can kick badly formed
// pods out of the state
if _, ok := s.pods[pod.ID()]; !ok {
return errors.Wrapf(ErrNoSuchPod, "no pod exists in state with ID %s", pod.ID())
}
if err := s.podIDIndex.Delete(pod.ID()); err != nil {
return errors.Wrapf(err, "error removing pod ID %s from index", pod.ID())
}
delete(s.pods, pod.ID())
s.podNameIndex.Release(pod.Name())
return nil
}
// AllPods retrieves all pods currently in the state
func (s *InMemoryState) AllPods() ([]*Pod, error) {
pods := make([]*Pod, 0, len(s.pods))
for _, pod := range s.pods {
pods = append(pods, pod)
}
return pods, nil
}

View File

@ -1,12 +0,0 @@
package layers
import cstorage "github.com/containers/storage"
// FullID gets the full id of a layer given a partial id or name
func FullID(store cstorage.Store, id string) (string, error) {
layer, err := store.Layer(id)
if err != nil {
return "", err
}
return layer.ID, nil
}

View File

@ -1,273 +0,0 @@
package libpod
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"syscall"
"time"
"github.com/containerd/cgroups"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
// TODO import these functions into libpod and remove the import
// Trying to keep libpod from depending on CRI-O code
"github.com/kubernetes-incubator/cri-o/utils"
)
// OCI code is undergoing heavy rewrite
const (
// CgroupfsCgroupsManager represents cgroupfs native cgroup manager
CgroupfsCgroupsManager = "cgroupfs"
// SystemdCgroupsManager represents systemd native cgroup manager
SystemdCgroupsManager = "systemd"
// ContainerCreateTimeout represents the value of container creating timeout
ContainerCreateTimeout = 240 * time.Second
)
// OCIRuntime represents an OCI-compatible runtime that libpod can call into
// to perform container operations
type OCIRuntime struct {
name string
path string
conmonPath string
conmonEnv []string
cgroupManager string
tmpDir string
exitsDir string
socketsDir string
logSizeMax int64
noPivot bool
}
// syncInfo is used to return data from monitor process to daemon
type syncInfo struct {
Pid int `json:"pid"`
Message string `json:"message,omitempty"`
}
// Make a new OCI runtime with provided options
func newOCIRuntime(name string, path string, conmonPath string, conmonEnv []string, cgroupManager string, tmpDir string, logSizeMax int64, noPivotRoot bool) (*OCIRuntime, error) {
runtime := new(OCIRuntime)
runtime.name = name
runtime.path = path
runtime.conmonPath = conmonPath
runtime.conmonEnv = conmonEnv
runtime.cgroupManager = cgroupManager
runtime.tmpDir = tmpDir
runtime.logSizeMax = logSizeMax
runtime.noPivot = noPivotRoot
runtime.exitsDir = filepath.Join(runtime.tmpDir, "exits")
runtime.socketsDir = filepath.Join(runtime.tmpDir, "socket")
if cgroupManager != CgroupfsCgroupsManager && cgroupManager != SystemdCgroupsManager {
return nil, errors.Wrapf(ErrInvalidArg, "invalid cgroup manager specified: %s", cgroupManager)
}
// Create the exit files and attach sockets directories
if err := os.MkdirAll(runtime.exitsDir, 0750); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return nil, errors.Wrapf(err, "error creating OCI runtime exit files directory %s",
runtime.exitsDir)
}
}
if err := os.MkdirAll(runtime.socketsDir, 0750); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return nil, errors.Wrapf(err, "error creating OCI runtime attach sockets directory %s",
runtime.socketsDir)
}
}
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)
}
// 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) error {
var stderrBuf bytes.Buffer
parentPipe, childPipe, err := newPipe()
if err != nil {
return errors.Wrapf(err, "error creating socket pair")
}
childStartPipe, parentStartPipe, err := newPipe()
if err != nil {
return errors.Wrapf(err, "error creating socket pair for start pipe")
}
defer parentPipe.Close()
defer parentStartPipe.Close()
args := []string{}
if r.cgroupManager == SystemdCgroupsManager {
args = append(args, "-s")
}
args = append(args, "-c", ctr.ID())
args = append(args, "-u", ctr.ID())
args = append(args, "-r", r.path)
args = append(args, "-b", ctr.bundlePath())
args = append(args, "-p", filepath.Join(ctr.state.RunDir, "pidfile"))
// TODO container log location should be configurable
// The default also likely shouldn't be this
args = append(args, "-l", filepath.Join(ctr.config.StaticDir, "ctr.log"))
args = append(args, "--exit-dir", r.exitsDir)
args = append(args, "--socket-dir-path", r.socketsDir)
if ctr.config.Spec.Process.Terminal {
args = append(args, "-t")
} else if ctr.config.Stdin {
args = append(args, "-i")
}
if r.logSizeMax >= 0 {
args = append(args, "--log-size-max", fmt.Sprintf("%v", r.logSizeMax))
}
if r.noPivot {
args = append(args, "--no-pivot")
}
logrus.WithFields(logrus.Fields{
"args": args,
}).Debugf("running conmon: %s", r.conmonPath)
cmd := exec.Command(r.conmonPath, args...)
cmd.Dir = ctr.state.RunDir
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
// TODO this is probably a really bad idea for some uses
// Make this configurable
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if ctr.config.Spec.Process.Terminal {
cmd.Stderr = &stderrBuf
}
cmd.ExtraFiles = append(cmd.ExtraFiles, childPipe, childStartPipe)
// 0, 1 and 2 are stdin, stdout and stderr
cmd.Env = append(r.conmonEnv, fmt.Sprintf("_OCI_SYNCPIPE=%d", 3))
cmd.Env = append(cmd.Env, fmt.Sprintf("_OCI_STARTPIPE=%d", 4))
err = cmd.Start()
if err != nil {
childPipe.Close()
return err
}
// We don't need childPipe on the parent side
childPipe.Close()
childStartPipe.Close()
// Move conmon to specified cgroup
if r.cgroupManager == SystemdCgroupsManager {
logrus.Infof("Running conmon under slice %s and unitName %s", cgroupParent, createUnitName("libpod-conmon", ctr.ID()))
if err = utils.RunUnderSystemdScope(cmd.Process.Pid, cgroupParent, createUnitName("libpod-conmon", ctr.ID())); err != nil {
logrus.Warnf("Failed to add conmon to systemd sandbox cgroup: %v", err)
}
} else {
control, err := cgroups.New(cgroups.V1, cgroups.StaticPath(filepath.Join(cgroupParent, "/libpod-conmon-"+ctr.ID())), &spec.LinuxResources{})
if err != nil {
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
} else {
// XXX: this defer does nothing as the cgroup can't be deleted cause
// it contains the conmon pid in tasks
// we need to remove this defer and delete the cgroup once conmon exits
// maybe need a conmon monitor?
defer control.Delete()
if err := control.Add(cgroups.Process{Pid: cmd.Process.Pid}); err != nil {
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
}
}
}
/* We set the cgroup, now the child can start creating children */
someData := []byte{0}
_, err = parentStartPipe.Write(someData)
if err != nil {
return err
}
/* Wait for initial setup and fork, and reap child */
err = cmd.Wait()
if err != nil {
return err
}
// TODO should do a defer r.deleteContainer(ctr) here if err != nil
// Need deleteContainer to be working first, though...
// Wait to get container pid from conmon
type syncStruct struct {
si *syncInfo
err error
}
ch := make(chan syncStruct)
go func() {
var si *syncInfo
if err = json.NewDecoder(parentPipe).Decode(&si); err != nil {
ch <- syncStruct{err: err}
return
}
ch <- syncStruct{si: si}
}()
select {
case ss := <-ch:
if ss.err != nil {
return errors.Wrapf(ss.err, "error reading container (probably exited) json message")
}
logrus.Debugf("Received container pid: %d", ss.si.Pid)
if ss.si.Pid == -1 {
if ss.si.Message != "" {
return errors.Wrapf(ErrInternal, "container create failed: %s", ss.si.Message)
}
return errors.Wrapf(ErrInternal, "container create failed")
}
case <-time.After(ContainerCreateTimeout):
return errors.Wrapf(ErrInternal, "container creation timeout")
}
return nil
}
// updateContainerStatus retrieves the current status of the container from the
// runtime
func (r *OCIRuntime) updateContainerStatus(ctr *Container) error {
return ErrNotImplemented
}
// startContainer starts the given container
func (r *OCIRuntime) startContainer(ctr *Container) error {
// TODO: streams should probably *not* be our STDIN/OUT/ERR - redirect to buffers?
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.path, "start", ctr.ID()); err != nil {
return err
}
// TODO record start time in container struct
return nil
}

View File

@ -1,382 +0,0 @@
package libpod
import (
"fmt"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/pkg/errors"
)
var (
ctrNotImplemented = func(c *Container) error {
return fmt.Errorf("NOT IMPLEMENTED")
}
)
const (
// IPCNamespace represents the IPC namespace
IPCNamespace = "ipc"
// MountNamespace represents the mount namespace
MountNamespace = "mount"
// NetNamespace represents the network namespace
NetNamespace = "net"
// PIDNamespace represents the PID namespace
PIDNamespace = "pid"
// UserNamespace represents the user namespace
UserNamespace = "user"
// UTSNamespace represents the UTS namespace
UTSNamespace = "uts"
)
// Runtime Creation Options
// WithStorageConfig uses the given configuration to set up container storage
// If this is not specified, the system default configuration will be used
// instead
func WithStorageConfig(config storage.StoreOptions) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.StorageConfig.RunRoot = config.RunRoot
rt.config.StorageConfig.GraphRoot = config.GraphRoot
rt.config.StorageConfig.GraphDriverName = config.GraphDriverName
rt.config.StorageConfig.GraphDriverOptions = make([]string, len(config.GraphDriverOptions))
copy(rt.config.StorageConfig.GraphDriverOptions, config.GraphDriverOptions)
rt.config.StorageConfig.UIDMap = make([]idtools.IDMap, len(config.UIDMap))
copy(rt.config.StorageConfig.UIDMap, config.UIDMap)
rt.config.StorageConfig.GIDMap = make([]idtools.IDMap, len(config.GIDMap))
copy(rt.config.StorageConfig.GIDMap, config.GIDMap)
return nil
}
}
// WithImageConfig uses the given configuration to set up image handling
// If this is not specified, the system default configuration will be used
// instead
func WithImageConfig(defaultTransport string, insecureRegistries, registries []string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.ImageDefaultTransport = defaultTransport
rt.config.InsecureRegistries = make([]string, len(insecureRegistries))
copy(rt.config.InsecureRegistries, insecureRegistries)
rt.config.Registries = make([]string, len(registries))
copy(rt.config.Registries, registries)
return nil
}
}
// WithSignaturePolicy specifies the path of a file which decides how trust is
// managed for images we've pulled.
// If this is not specified, the system default configuration will be used
// instead
func WithSignaturePolicy(path string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.SignaturePolicyPath = path
return nil
}
}
// WithOCIRuntime specifies an OCI runtime to use for running containers
func WithOCIRuntime(runtimePath string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.RuntimePath = runtimePath
return nil
}
}
// WithConmonPath specifies the path to the conmon binary which manages the
// runtime
func WithConmonPath(path string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.ConmonPath = path
return nil
}
}
// WithConmonEnv specifies the environment variable list for the conmon process
func WithConmonEnv(environment []string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.ConmonEnvVars = make([]string, len(environment))
copy(rt.config.ConmonEnvVars, environment)
return nil
}
}
// WithCgroupManager specifies the manager implementation name which is used to
// handle cgroups for containers
// Current valid values are "cgroupfs" and "systemd"
func WithCgroupManager(manager string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.CgroupManager = manager
return nil
}
}
// WithStaticDir sets the directory that static runtime files which persist
// across reboots will be stored
func WithStaticDir(dir string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.StaticDir = dir
return nil
}
}
// WithTmpDir sets the directory that temporary runtime files which are not
// expected to survive across reboots will be stored
// This should be located on a tmpfs mount (/tmp or /var/run for example)
func WithTmpDir(dir string) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.TmpDir = dir
return nil
}
}
// WithSELinux enables SELinux on the container server
func WithSELinux() RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.SelinuxEnabled = true
return nil
}
}
// WithPidsLimit specifies the maximum number of processes each container is
// restricted to
func WithPidsLimit(limit int64) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.PidsLimit = limit
return nil
}
}
// WithMaxLogSize sets the maximum size of container logs
// Positive sizes are limits in bytes, -1 is unlimited
func WithMaxLogSize(limit int64) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.MaxLogSize = limit
return nil
}
}
// WithNoPivotRoot sets the runtime to use MS_MOVE instead of PIVOT_ROOT when
// starting containers
func WithNoPivotRoot(noPivot bool) RuntimeOption {
return func(rt *Runtime) error {
if rt.valid {
return ErrRuntimeFinalized
}
rt.config.NoPivotRoot = true
return nil
}
}
// Container Creation Options
// WithRootFSFromPath uses the given path as a container's root filesystem
// No further setup is performed on this path
func WithRootFSFromPath(path string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
}
if ctr.config.RootfsDir != "" || ctr.config.RootfsImageID != "" || ctr.config.RootfsImageName != "" {
return errors.Wrapf(ErrInvalidArg, "container already configured with root filesystem")
}
ctr.config.RootfsDir = path
ctr.config.RootfsFromImage = false
return nil
}
}
// WithRootFSFromImage sets up a fresh root filesystem using the given image
// If useImageConfig is specified, image volumes, environment variables, and
// other configuration from the image will be added to the config
// TODO: Replace image name and ID with a libpod.Image struct when that is finished
func WithRootFSFromImage(imageID string, imageName string, useImageConfig bool) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
}
if ctr.config.RootfsDir != "" || ctr.config.RootfsImageID != "" || ctr.config.RootfsImageName != "" {
return errors.Wrapf(ErrInvalidArg, "container already configured with root filesystem")
}
ctr.config.RootfsImageID = imageID
ctr.config.RootfsImageName = imageName
ctr.config.UseImageConfig = useImageConfig
ctr.config.RootfsFromImage = true
return nil
}
}
// WithStdin keeps stdin on the container open to allow interaction
func WithStdin() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
}
ctr.config.Stdin = true
return nil
}
}
// WithSharedNamespaces sets a container to share namespaces with another
// container. If the from container belongs to a pod, the new container will
// be added to the pod.
// By default no namespaces are shared. To share a namespace, add the Namespace
// string constant to the map as a key
func WithSharedNamespaces(from *Container, namespaces map[string]string) CtrCreateOption {
return ctrNotImplemented
}
// WithPod adds the container to a pod
func (r *Runtime) WithPod(pod *Pod) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
}
if pod == nil {
return ErrInvalidArg
}
ctr.config.Pod = pod.ID()
ctr.pod = pod
return nil
}
}
// WithLabels adds labels to the container
func WithLabels(labels map[string]string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
}
ctr.config.Labels = make(map[string]string)
for key, value := range labels {
ctr.config.Labels[key] = value
}
return nil
}
}
// WithName sets the container's name
func WithName(name string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
}
ctr.config.Name = name
return nil
}
}
// WithStopSignal sets the signal that will be sent to stop the container
func WithStopSignal(signal uint) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return ErrCtrFinalized
}
if signal == 0 {
return errors.Wrapf(ErrInvalidArg, "stop signal cannot be 0")
} else if signal > 64 {
return errors.Wrapf(ErrInvalidArg, "stop signal cannot be greater than 64 (SIGRTMAX)")
}
ctr.config.StopSignal = signal
return nil
}
}
// Pod Creation Options
// WithPodName sets the name of the pod
func WithPodName(name string) PodCreateOption {
return func(pod *Pod) error {
if pod.valid {
return ErrPodFinalized
}
pod.name = name
return nil
}
}

View File

@ -1,137 +0,0 @@
package libpod
import (
"sync"
"github.com/docker/docker/pkg/stringid"
"github.com/pkg/errors"
)
// Pod represents a group of containers that may share namespaces
type Pod struct {
id string
name string
containers map[string]*Container
valid bool
lock sync.RWMutex
}
// ID retrieves the pod's ID
func (p *Pod) ID() string {
return p.id
}
// Name retrieves the pod's name
func (p *Pod) Name() string {
return p.name
}
// Creates a new pod
func newPod() (*Pod, error) {
pod := new(Pod)
pod.id = stringid.GenerateNonCryptoID()
pod.name = pod.id // TODO generate human-readable name here
pod.containers = make(map[string]*Container)
return pod, nil
}
// Adds a container to the pod
// Does not check that container's pod ID is set correctly, or attempt to set
// pod ID after adding
func (p *Pod) addContainer(ctr *Container) error {
p.lock.Lock()
defer p.lock.Unlock()
ctr.lock.Lock()
defer ctr.lock.Unlock()
if !p.valid {
return ErrPodRemoved
}
if !ctr.valid {
return ErrCtrRemoved
}
if _, ok := p.containers[ctr.ID()]; ok {
return errors.Wrapf(ErrCtrExists, "container with ID %s already exists in pod %s", ctr.ID(), p.id)
}
p.containers[ctr.ID()] = ctr
return nil
}
// Removes a container from the pod
// Does not perform any checks on the container
func (p *Pod) removeContainer(ctr *Container) error {
p.lock.Lock()
defer p.lock.Unlock()
if !p.valid {
return ErrPodRemoved
}
if _, ok := p.containers[ctr.ID()]; !ok {
return errors.Wrapf(ErrNoSuchCtr, "no container with id %s in pod %s", ctr.ID(), p.id)
}
delete(p.containers, ctr.ID())
return nil
}
// Start starts all containers within a pod that are not already running
func (p *Pod) Start() error {
return ErrNotImplemented
}
// Stop stops all containers within a pod that are not already stopped
func (p *Pod) Stop() error {
return ErrNotImplemented
}
// Kill sends a signal to all running containers within a pod
func (p *Pod) Kill(signal uint) error {
return ErrNotImplemented
}
// HasContainer checks if a container is present in the pod
func (p *Pod) HasContainer(id string) (bool, error) {
p.lock.RLock()
defer p.lock.RUnlock()
if !p.valid {
return false, ErrPodRemoved
}
_, ok := p.containers[id]
return ok, nil
}
// GetContainers retrieves the containers in the pod
func (p *Pod) GetContainers() ([]*Container, error) {
p.lock.RLock()
defer p.lock.RUnlock()
if !p.valid {
return nil, ErrPodRemoved
}
ctrs := make([]*Container, 0, len(p.containers))
for _, ctr := range p.containers {
ctrs = append(ctrs, ctr)
}
return ctrs, nil
}
// Status gets the status of all containers in the pod
// TODO This should return a summary of the states of all containers in the pod
func (p *Pod) Status() error {
return ErrNotImplemented
}

View File

@ -1,192 +0,0 @@
package libpod
import (
"os"
"sync"
is "github.com/containers/image/storage"
"github.com/containers/image/types"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/ulule/deepcopier"
)
// A RuntimeOption is a functional option which alters the Runtime created by
// NewRuntime
type RuntimeOption func(*Runtime) error
// Runtime is the core libpod runtime
type Runtime struct {
config *RuntimeConfig
state State
store storage.Store
storageService *storageService
imageContext *types.SystemContext
ociRuntime *OCIRuntime
valid bool
lock sync.RWMutex
}
// RuntimeConfig contains configuration options used to set up the runtime
type RuntimeConfig struct {
StorageConfig storage.StoreOptions
ImageDefaultTransport string
InsecureRegistries []string
Registries []string
SignaturePolicyPath string
RuntimePath string
ConmonPath string
ConmonEnvVars []string
CgroupManager string
StaticDir string
TmpDir string
SelinuxEnabled bool
PidsLimit int64
MaxLogSize int64
NoPivotRoot bool
}
var (
defaultRuntimeConfig = RuntimeConfig{
// Leave this empty so containers/storage will use its defaults
StorageConfig: storage.StoreOptions{},
ImageDefaultTransport: "docker://",
RuntimePath: "/usr/bin/runc",
ConmonPath: "/usr/local/libexec/crio/conmon",
ConmonEnvVars: []string{
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
},
CgroupManager: "cgroupfs",
StaticDir: "/var/lib/libpod",
TmpDir: "/var/run/libpod",
SelinuxEnabled: false,
PidsLimit: 1024,
MaxLogSize: -1,
NoPivotRoot: false,
}
)
// NewRuntime creates a new container runtime
// Options can be passed to override the default configuration for the runtime
func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) {
runtime = new(Runtime)
runtime.config = new(RuntimeConfig)
// Copy the default configuration
deepcopier.Copy(defaultRuntimeConfig).To(runtime.config)
// Overwrite it with user-given configuration options
for _, opt := range options {
if err := opt(runtime); err != nil {
return nil, errors.Wrapf(err, "error configuring runtime")
}
}
// Set up containers/storage
store, err := storage.GetStore(runtime.config.StorageConfig)
if err != nil {
return nil, err
}
runtime.store = store
is.Transport.SetStore(store)
defer func() {
if err != nil {
// Don't forcibly shut down
// We could be opening a store in use by another libpod
_, err2 := runtime.store.Shutdown(false)
if err2 != nil {
logrus.Errorf("Error removing store for partially-created runtime: %s", err2)
}
}
}()
// Set up a storage service for creating container root filesystems from
// images
storageService, err := getStorageService(runtime.store)
if err != nil {
return nil, err
}
runtime.storageService = storageService
// Set up containers/image
runtime.imageContext = &types.SystemContext{
SignaturePolicyPath: runtime.config.SignaturePolicyPath,
}
// Set up the state
state, err := NewInMemoryState()
if err != nil {
return nil, err
}
runtime.state = state
// Make an OCI runtime to perform container operations
ociRuntime, err := newOCIRuntime("runc", runtime.config.RuntimePath,
runtime.config.ConmonPath, runtime.config.ConmonEnvVars,
runtime.config.CgroupManager, runtime.config.TmpDir,
runtime.config.MaxLogSize, runtime.config.NoPivotRoot)
if err != nil {
return nil, err
}
runtime.ociRuntime = ociRuntime
// Make the static files directory if it does not exist
if err := os.MkdirAll(runtime.config.StaticDir, 0755); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return nil, errors.Wrapf(err, "error creating runtime static files directory %s",
runtime.config.StaticDir)
}
}
// Make the per-boot files directory if it does not exist
if err := os.MkdirAll(runtime.config.TmpDir, 0755); err != nil {
// The directory is allowed to exist
if !os.IsExist(err) {
return nil, errors.Wrapf(err, "error creating runtime temporary files directory %s",
runtime.config.TmpDir)
}
}
// Mark the runtime as valid - ready to be used, cannot be modified
// further
runtime.valid = true
return runtime, nil
}
// GetConfig returns a copy of the configuration used by the runtime
func (r *Runtime) GetConfig() *RuntimeConfig {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return nil
}
config := new(RuntimeConfig)
// Copy so the caller won't be able to modify the actual config
deepcopier.Copy(r.config).To(config)
return config
}
// Shutdown shuts down the runtime and associated containers and storage
// If force is true, containers and mounted storage will be shut down before
// cleaning up; if force is false, an error will be returned if there are
// still containers running or mounted
func (r *Runtime) Shutdown(force bool) error {
r.lock.Lock()
defer r.lock.Unlock()
if !r.valid {
return ErrRuntimeStopped
}
r.valid = false
_, err := r.store.Shutdown(force)
return err
}

View File

@ -1,228 +0,0 @@
package libpod
import (
"github.com/containers/storage"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// Contains the public Runtime API for containers
// A CtrCreateOption is a functional option which alters the Container created
// by NewContainer
type CtrCreateOption func(*Container) error
// ContainerFilter is a function to determine whether a container is included
// in command output. Containers to be outputted are tested using the function.
// A true return will include the container, a false return will exclude it.
type ContainerFilter func(*Container) bool
// NewContainer creates a new container from a given OCI config
func (r *Runtime) NewContainer(spec *spec.Spec, options ...CtrCreateOption) (ctr *Container, err error) {
r.lock.Lock()
defer r.lock.Unlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
ctr, err = newContainer(spec)
if err != nil {
return nil, err
}
for _, option := range options {
if err := option(ctr); err != nil {
return nil, errors.Wrapf(err, "error running container create option")
}
}
ctr.valid = true
ctr.state.State = ContainerStateConfigured
ctr.runtime = r
// Set up storage for the container
if err := ctr.setupStorage(); err != nil {
return nil, errors.Wrapf(err, "error configuring storage for container")
}
defer func() {
if err != nil {
if err2 := ctr.teardownStorage(); err2 != nil {
logrus.Errorf("Error removing partially-created container root filesystem: %s", err2)
}
}
}()
// If the container is in a pod, add it to the pod
if ctr.pod != nil {
if err := ctr.pod.addContainer(ctr); err != nil {
return nil, errors.Wrapf(err, "error adding new container to pod %s", ctr.pod.ID())
}
}
defer func() {
if err != nil {
if err2 := ctr.pod.removeContainer(ctr); err2 != nil {
logrus.Errorf("Error removing partially-created container from pod %s: %s", ctr.pod.ID(), err2)
}
}
}()
if err := r.state.AddContainer(ctr); err != nil {
// TODO: Might be worth making an effort to detect duplicate IDs
// We can recover from that by generating a new ID for the
// container
return nil, errors.Wrapf(err, "error adding new container to state")
}
return ctr, nil
}
// RemoveContainer removes the given container
// If force is specified, the container will be stopped first
// Otherwise, RemoveContainer will return an error if the container is running
func (r *Runtime) RemoveContainer(c *Container, force bool) error {
r.lock.Lock()
defer r.lock.Unlock()
c.lock.Lock()
defer c.lock.Unlock()
if !r.valid {
return ErrRuntimeStopped
}
if !c.valid {
return ErrCtrRemoved
}
// TODO check container status and unmount storage
// TODO check that no other containers depend on this container's
// namespaces
status, err := c.State()
if err != nil {
return err
}
// A container cannot be removed if it is running
if status == ContainerStateRunning {
return errors.Wrapf(ErrCtrStateInvalid, "cannot remove container %s as it is running", c.ID())
}
if err := r.state.RemoveContainer(c); err != nil {
return errors.Wrapf(err, "error removing container from state")
}
// Set container as invalid so it can no longer be used
c.valid = false
// Remove container from pod, if it joined one
if c.pod != nil {
if err := c.pod.removeContainer(c); err != nil {
return errors.Wrapf(err, "error removing container from pod %s", c.pod.ID())
}
}
return nil
}
// GetContainer retrieves a container by its ID
func (r *Runtime) GetContainer(id string) (*Container, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
return r.state.Container(id)
}
// HasContainer checks if a container with the given ID is present
func (r *Runtime) HasContainer(id string) (bool, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return false, ErrRuntimeStopped
}
return r.state.HasContainer(id)
}
// LookupContainer looks up a container by its name or a partial ID
// If a partial ID is not unique, an error will be returned
func (r *Runtime) LookupContainer(idOrName string) (*Container, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
return r.state.LookupContainer(idOrName)
}
// GetContainers retrieves all containers from the state
// Filters can be provided which will determine what containers are included in
// the output. Multiple filters are handled by ANDing their output, so only
// containers matching all filters are returned
func (r *Runtime) GetContainers(filters ...ContainerFilter) ([]*Container, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
ctrs, err := r.state.AllContainers()
if err != nil {
return nil, err
}
ctrsFiltered := make([]*Container, 0, len(ctrs))
for _, ctr := range ctrs {
include := true
for _, filter := range filters {
include = include && filter(ctr)
}
if include {
ctrsFiltered = append(ctrsFiltered, ctr)
}
}
return ctrsFiltered, nil
}
// getContainersWithImage returns a list of containers referencing imageID
func (r *Runtime) getContainersWithImage(imageID string) ([]storage.Container, error) {
var matchingContainers []storage.Container
containers, err := r.store.Containers()
if err != nil {
return nil, err
}
for _, ctr := range containers {
if ctr.ImageID == imageID {
matchingContainers = append(matchingContainers, ctr)
}
}
return matchingContainers, nil
}
// removeMultipleContainers deletes a list of containers from the store
// TODO refactor this to remove libpod Containers
func (r *Runtime) removeMultipleContainers(containers []storage.Container) error {
for _, ctr := range containers {
if err := r.store.DeleteContainer(ctr.ID); err != nil {
return errors.Wrapf(err, "could not remove container %q", ctr)
}
}
return nil
}
// ContainerConfigToDisk saves a container's nonvolatile configuration to disk
func (r *Runtime) containerConfigToDisk(ctr *Container) error {
return ErrNotImplemented
}

File diff suppressed because it is too large Load Diff

View File

@ -1,122 +0,0 @@
package libpod
import (
"github.com/pkg/errors"
)
// Contains the public Runtime API for pods
// A PodCreateOption is a functional option which alters the Pod created by
// NewPod
type PodCreateOption func(*Pod) error
// PodFilter is a function to determine whether a pod is included in command
// output. Pods to be outputted are tested using the function. A true return
// will include the pod, a false return will exclude it.
type PodFilter func(*Pod) bool
// NewPod makes a new, empty pod
func (r *Runtime) NewPod(options ...PodCreateOption) (*Pod, error) {
r.lock.Lock()
defer r.lock.Unlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
pod, err := newPod()
if err != nil {
return nil, errors.Wrapf(err, "error creating pod")
}
for _, option := range options {
if err := option(pod); err != nil {
return nil, errors.Wrapf(err, "error running pod create option")
}
}
pod.valid = true
if err := r.state.AddPod(pod); err != nil {
return nil, errors.Wrapf(err, "error adding pod to state")
}
return nil, ErrNotImplemented
}
// RemovePod removes a pod and all containers in it
// If force is specified, all containers in the pod will be stopped first
// Otherwise, RemovePod will return an error if any container in the pod is running
// Remove acts atomically, removing all containers or no containers
func (r *Runtime) RemovePod(p *Pod, force bool) error {
return ErrNotImplemented
}
// GetPod retrieves a pod by its ID
func (r *Runtime) GetPod(id string) (*Pod, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
return r.state.Pod(id)
}
// HasPod checks to see if a pod with the given ID exists
func (r *Runtime) HasPod(id string) (bool, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return false, ErrRuntimeStopped
}
return r.state.HasPod(id)
}
// LookupPod retrieves a pod by its name or a partial ID
// If a partial ID is not unique, an error will be returned
func (r *Runtime) LookupPod(idOrName string) (*Pod, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
return r.state.LookupPod(idOrName)
}
// Pods retrieves all pods
// Filters can be provided which will determine which pods are included in the
// output. Multiple filters are handled by ANDing their output, so only pods
// matching all filters are returned
func (r *Runtime) Pods(filters ...PodFilter) ([]*Pod, error) {
r.lock.RLock()
defer r.lock.RUnlock()
if !r.valid {
return nil, ErrRuntimeStopped
}
pods, err := r.state.AllPods()
if err != nil {
return nil, err
}
podsFiltered := make([]*Pod, 0, len(pods))
for _, pod := range pods {
include := true
for _, filter := range filters {
include = include && filter(pod)
}
if include {
podsFiltered = append(podsFiltered, pod)
}
}
return podsFiltered, nil
}

View File

@ -1,38 +0,0 @@
package libpod
// State is a storage backend for libpod's current state
type State interface {
// Accepts full ID of container
Container(id string) (*Container, error)
// Accepts full or partial IDs (as long as they are unique) and names
LookupContainer(idOrName string) (*Container, error)
// Checks if a container with the given ID is present in the state
HasContainer(id string) (bool, error)
// Adds container to state
// If the container belongs to a pod, that pod must already be present
// in the state when the container is added, and the container must be
// present in the pod
AddContainer(ctr *Container) error
// Removes container from state
// The container will only be removed from the state, not from the pod
// which the container belongs to
RemoveContainer(ctr *Container) error
// Retrieves all containers presently in state
AllContainers() ([]*Container, error)
// Accepts full ID of pod
Pod(id string) (*Pod, error)
// Accepts full or partial IDs (as long as they are unique) and names
LookupPod(idOrName string) (*Pod, error)
// Checks if a pod with the given ID is present in the state
HasPod(id string) (bool, error)
// Adds pod to state
// Only empty pods can be added to the state
AddPod(pod *Pod) error
// Removes pod from state
// Containers within a pod will not be removed from the state, and will
// not be changed to remove them from the now-removed pod
RemovePod(pod *Pod) error
// Retrieves all pods presently in state
AllPods() ([]*Pod, error)
}

View File

@ -1,261 +0,0 @@
package libpod
import (
"encoding/json"
"time"
istorage "github.com/containers/image/storage"
"github.com/containers/image/types"
"github.com/containers/storage"
"github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
type storageService struct {
store storage.Store
}
// getStorageService returns a storageService which can create container root
// filesystems from images
func getStorageService(store storage.Store) (*storageService, error) {
return &storageService{store: store}, nil
}
// ContainerInfo wraps a subset of information about a container: the locations
// of its nonvolatile and volatile per-container directories, along with a copy
// of the configuration blob from the image that was used to create the
// container, if the image had a configuration.
type ContainerInfo struct {
Dir string
RunDir string
Config *v1.Image
}
// RuntimeContainerMetadata is the structure that we encode as JSON and store
// in the metadata field of storage.Container objects. It is used for
// specifying attributes containers when they are being created, and allows a
// container's MountLabel, and possibly other values, to be modified in one
// read/write cycle via calls to storageService.ContainerMetadata,
// RuntimeContainerMetadata.SetMountLabel, and
// storageService.SetContainerMetadata.
type RuntimeContainerMetadata struct {
// The provided name and the ID of the image that was used to
// instantiate the container.
ImageName string `json:"image-name"` // Applicable to both PodSandboxes and Containers
ImageID string `json:"image-id"` // Applicable to both PodSandboxes and Containers
// The container's name, which for an infrastructure container is usually PodName + "-infra".
ContainerName string `json:"name"` // Applicable to both PodSandboxes and Containers, mandatory
CreatedAt int64 `json:"created-at"` // Applicable to both PodSandboxes and Containers
MountLabel string `json:"mountlabel,omitempty"` // Applicable to both PodSandboxes and Containers
}
// SetMountLabel updates the mount label held by a RuntimeContainerMetadata
// object.
func (metadata *RuntimeContainerMetadata) SetMountLabel(mountLabel string) {
metadata.MountLabel = mountLabel
}
// CreateContainerStorage creates the storage end of things. We already have the container spec created
// TO-DO We should be passing in an Image object in the future.
func (r *storageService) CreateContainerStorage(systemContext *types.SystemContext, imageName, imageID, containerName, containerID, mountLabel string) (ContainerInfo, error) {
var ref types.ImageReference
if imageName == "" && imageID == "" {
return ContainerInfo{}, ErrEmptyID
}
if containerName == "" {
return ContainerInfo{}, ErrEmptyID
}
//// Check if we have the specified image.
ref, err := istorage.Transport.ParseStoreReference(r.store, imageName)
if err != nil {
return ContainerInfo{}, err
}
img, err := istorage.Transport.GetStoreImage(r.store, ref)
if err != nil {
return ContainerInfo{}, err
}
// Pull out a copy of the image's configuration.
image, err := ref.NewImage(systemContext)
if err != nil {
return ContainerInfo{}, err
}
defer image.Close()
imageConfig, err := image.OCIConfig()
if err != nil {
return ContainerInfo{}, err
}
// Update the image name and ID.
if imageName == "" && len(img.Names) > 0 {
imageName = img.Names[0]
}
imageID = img.ID
// Build metadata to store with the container.
metadata := RuntimeContainerMetadata{
ImageName: imageName,
ImageID: imageID,
ContainerName: containerName,
CreatedAt: time.Now().Unix(),
MountLabel: mountLabel,
}
mdata, err := json.Marshal(&metadata)
if err != nil {
return ContainerInfo{}, err
}
// Build the container.
names := []string{containerName}
container, err := r.store.CreateContainer(containerID, names, img.ID, "", string(mdata), nil)
if err != nil {
logrus.Debugf("failed to create container %s(%s): %v", metadata.ContainerName, containerID, err)
return ContainerInfo{}, err
}
logrus.Debugf("created container %q", container.ID)
// If anything fails after this point, we need to delete the incomplete
// container before returning.
defer func() {
if err != nil {
if err2 := r.store.DeleteContainer(container.ID); err2 != nil {
logrus.Infof("%v deleting partially-created container %q", err2, container.ID)
return
}
logrus.Infof("deleted partially-created container %q", container.ID)
}
}()
// Add a name to the container's layer so that it's easier to follow
// what's going on if we're just looking at the storage-eye view of things.
layerName := metadata.ContainerName + "-layer"
names, err = r.store.Names(container.LayerID)
if err != nil {
return ContainerInfo{}, err
}
names = append(names, layerName)
err = r.store.SetNames(container.LayerID, names)
if err != nil {
return ContainerInfo{}, err
}
// Find out where the container work directories are, so that we can return them.
containerDir, err := r.store.ContainerDirectory(container.ID)
if err != nil {
return ContainerInfo{}, err
}
logrus.Debugf("container %q has work directory %q", container.ID, containerDir)
containerRunDir, err := r.store.ContainerRunDirectory(container.ID)
if err != nil {
return ContainerInfo{}, err
}
logrus.Debugf("container %q has run directory %q", container.ID, containerRunDir)
return ContainerInfo{
Dir: containerDir,
RunDir: containerRunDir,
Config: imageConfig,
}, nil
}
func (r *storageService) DeleteContainer(idOrName string) error {
if idOrName == "" {
return ErrEmptyID
}
container, err := r.store.Container(idOrName)
if err != nil {
return err
}
err = r.store.DeleteContainer(container.ID)
if err != nil {
logrus.Debugf("failed to delete container %q: %v", container.ID, err)
return err
}
return nil
}
func (r *storageService) SetContainerMetadata(idOrName string, metadata RuntimeContainerMetadata) error {
mdata, err := json.Marshal(&metadata)
if err != nil {
logrus.Debugf("failed to encode metadata for %q: %v", idOrName, err)
return err
}
return r.store.SetMetadata(idOrName, string(mdata))
}
func (r *storageService) GetContainerMetadata(idOrName string) (RuntimeContainerMetadata, error) {
metadata := RuntimeContainerMetadata{}
mdata, err := r.store.Metadata(idOrName)
if err != nil {
return metadata, err
}
if err = json.Unmarshal([]byte(mdata), &metadata); err != nil {
return metadata, err
}
return metadata, nil
}
func (r *storageService) StartContainer(idOrName string) (string, error) {
container, err := r.store.Container(idOrName)
if err != nil {
if errors.Cause(err) == storage.ErrContainerUnknown {
return "", ErrNoSuchCtr
}
return "", err
}
metadata := RuntimeContainerMetadata{}
if err = json.Unmarshal([]byte(container.Metadata), &metadata); err != nil {
return "", err
}
mountPoint, err := r.store.Mount(container.ID, metadata.MountLabel)
if err != nil {
logrus.Debugf("failed to mount container %q: %v", container.ID, err)
return "", err
}
logrus.Debugf("mounted container %q at %q", container.ID, mountPoint)
return mountPoint, nil
}
func (r *storageService) StopContainer(idOrName string) error {
if idOrName == "" {
return ErrEmptyID
}
container, err := r.store.Container(idOrName)
if err != nil {
return err
}
err = r.store.Unmount(container.ID)
if err != nil {
logrus.Debugf("failed to unmount container %q: %v", container.ID, err)
return err
}
logrus.Debugf("unmounted container %q", container.ID)
return nil
}
func (r *storageService) GetWorkDir(id string) (string, error) {
container, err := r.store.Container(id)
if err != nil {
if errors.Cause(err) == storage.ErrContainerUnknown {
return "", ErrNoSuchCtr
}
return "", err
}
return r.store.ContainerDirectory(container.ID)
}
func (r *storageService) GetRunDir(id string) (string, error) {
container, err := r.store.Container(id)
if err != nil {
if errors.Cause(err) == storage.ErrContainerUnknown {
return "", ErrNoSuchCtr
}
return "", err
}
return r.store.ContainerRunDirectory(container.ID)
}

View File

@ -1,34 +0,0 @@
package libpod
import (
"os"
"path/filepath"
)
// WriteFile writes a provided string to a provided path
func WriteFile(content string, path string) error {
baseDir := filepath.Dir(path)
if baseDir != "" {
if _, err := os.Stat(path); err != nil {
return err
}
}
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
f.WriteString(content)
f.Sync()
return nil
}
// StringInSlice determines if a string is in a string slice, returns bool
func StringInSlice(s string, sl []string) bool {
for _, i := range sl {
if i == s {
return true
}
}
return false
}

Some files were not shown because too many files have changed in this diff Show More