summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/podman/commands.go150
-rw-r--r--cmd/podman/commands_remoteclient.go21
-rw-r--r--cmd/podman/common.go18
-rw-r--r--cmd/podman/container.go43
-rw-r--r--cmd/podman/create.go132
-rw-r--r--cmd/podman/create_cli.go8
-rw-r--r--cmd/podman/exec.go14
-rw-r--r--cmd/podman/exists.go13
-rw-r--r--cmd/podman/formats/formats.go13
-rw-r--r--cmd/podman/history.go8
-rw-r--r--cmd/podman/image.go17
-rw-r--r--cmd/podman/imagefilters/filters.go84
-rw-r--r--cmd/podman/images.go52
-rw-r--r--cmd/podman/images_prune.go15
-rw-r--r--cmd/podman/info.go21
-rw-r--r--cmd/podman/inspect.go45
-rw-r--r--cmd/podman/libpodruntime/runtime.go6
-rw-r--r--cmd/podman/login.go42
-rw-r--r--cmd/podman/main.go151
-rw-r--r--cmd/podman/mount.go34
-rw-r--r--cmd/podman/platform_linux.go17
-rw-r--r--cmd/podman/platform_unsupported.go6
-rw-r--r--cmd/podman/play_kube.go2
-rw-r--r--cmd/podman/pull.go8
-rw-r--r--cmd/podman/rmi.go15
-rw-r--r--cmd/podman/shared/container.go34
-rw-r--r--cmd/podman/shared/funcs.go4
-rw-r--r--cmd/podman/shared/prune.go24
-rw-r--r--cmd/podman/sign.go198
-rw-r--r--cmd/podman/start.go16
-rw-r--r--cmd/podman/tag.go6
-rw-r--r--cmd/podman/trust.go58
-rw-r--r--cmd/podman/umount.go65
-rw-r--r--cmd/podman/varlink/io.podman.varlink30
-rw-r--r--cmd/podman/version.go18
35 files changed, 885 insertions, 503 deletions
diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go
new file mode 100644
index 000000000..718717009
--- /dev/null
+++ b/cmd/podman/commands.go
@@ -0,0 +1,150 @@
+// +build !remoteclient
+
+package main
+
+import "github.com/urfave/cli"
+
+func getAppCommands() []cli.Command {
+ return []cli.Command{
+ attachCommand,
+ commitCommand,
+ buildCommand,
+ createCommand,
+ diffCommand,
+ execCommand,
+ exportCommand,
+ importCommand,
+ killCommand,
+ kubeCommand,
+ loadCommand,
+ loginCommand,
+ logoutCommand,
+ logsCommand,
+ mountCommand,
+ pauseCommand,
+ psCommand,
+ podCommand,
+ portCommand,
+ pushCommand,
+ playCommand,
+ restartCommand,
+ rmCommand,
+ runCommand,
+ saveCommand,
+ searchCommand,
+ startCommand,
+ statsCommand,
+ stopCommand,
+ topCommand,
+ umountCommand,
+ unpauseCommand,
+ volumeCommand,
+ waitCommand,
+ }
+}
+
+func getImageSubCommands() []cli.Command {
+ return []cli.Command{
+ buildCommand,
+ importCommand,
+ loadCommand,
+ pullCommand,
+ saveCommand,
+ trustCommand,
+ signCommand,
+ }
+}
+
+func getContainerSubCommands() []cli.Command {
+ return []cli.Command{
+ attachCommand,
+ checkpointCommand,
+ cleanupCommand,
+ containerExistsCommand,
+ commitCommand,
+ createCommand,
+ diffCommand,
+ execCommand,
+ exportCommand,
+ killCommand,
+ logsCommand,
+ psCommand,
+ mountCommand,
+ pauseCommand,
+ portCommand,
+ pruneContainersCommand,
+ refreshCommand,
+ restartCommand,
+ restoreCommand,
+ rmCommand,
+ runCommand,
+ runlabelCommand,
+ startCommand,
+ statsCommand,
+ stopCommand,
+ topCommand,
+ umountCommand,
+ unpauseCommand,
+ // updateCommand,
+ waitCommand,
+ }
+}
+func getMainAppFlags() []cli.Flag {
+ return []cli.Flag{
+ cli.StringFlag{
+ Name: "cgroup-manager",
+ Usage: "cgroup manager to use (cgroupfs or systemd, default systemd)",
+ },
+ cli.StringFlag{
+ Name: "cni-config-dir",
+ Usage: "path of the configuration directory for CNI networks",
+ },
+ cli.StringFlag{
+ Name: "conmon",
+ Usage: "path of the conmon binary",
+ },
+ cli.StringFlag{
+ Name: "default-mounts-file",
+ Usage: "path to default mounts file",
+ Hidden: true,
+ },
+ cli.StringSliceFlag{
+ Name: "hooks-dir",
+ Usage: "set the OCI hooks directory path (may be set multiple times)",
+ },
+ cli.IntFlag{
+ Name: "max-workers",
+ Usage: "the maximum number of workers for parallel operations",
+ Hidden: true,
+ },
+ cli.StringFlag{
+ Name: "namespace",
+ Usage: "set the libpod namespace, used to create separate views of the containers and pods on the system",
+ Value: "",
+ },
+ 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",
+ },
+ cli.BoolFlag{
+ Name: "syslog",
+ Usage: "output logging information to syslog as well as the console",
+ },
+ }
+}
diff --git a/cmd/podman/commands_remoteclient.go b/cmd/podman/commands_remoteclient.go
new file mode 100644
index 000000000..6701e14a1
--- /dev/null
+++ b/cmd/podman/commands_remoteclient.go
@@ -0,0 +1,21 @@
+// +build remoteclient
+
+package main
+
+import "github.com/urfave/cli"
+
+func getAppCommands() []cli.Command {
+ return []cli.Command{}
+}
+
+func getImageSubCommands() []cli.Command {
+ return []cli.Command{}
+}
+
+func getContainerSubCommands() []cli.Command {
+ return []cli.Command{}
+}
+
+func getMainAppFlags() []cli.Flag {
+ return []cli.Flag{}
+}
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 8404a29b8..d934c8699 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -28,6 +28,10 @@ var (
Name: "latest, l",
Usage: "act on the latest pod podman is aware of",
}
+ WorkDirFlag = cli.StringFlag{
+ Name: "workdir, w",
+ Usage: "Working directory inside the container",
+ }
)
const (
@@ -321,6 +325,15 @@ var createFlags = []cli.Flag{
Value: "bind",
},
cli.BoolFlag{
+ Name: "init",
+ Usage: "Run an init binary inside the container that forwards signals and reaps processes",
+ },
+ cli.StringFlag{
+ Name: "init-path",
+ // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string)
+ Usage: fmt.Sprintf("Path to the container-init binary (default: %q)", libpod.DefaultInitPath),
+ },
+ cli.BoolFlag{
Name: "interactive, i",
Usage: "Keep STDIN open even if not attached",
},
@@ -513,10 +526,7 @@ var createFlags = []cli.Flag{
Name: "volumes-from",
Usage: "Mount volumes from the specified container(s) (default [])",
},
- cli.StringFlag{
- Name: "workdir, w",
- Usage: "Working `directory inside the container",
- },
+ WorkDirFlag,
}
func getFormat(c *cli.Context) (string, error) {
diff --git a/cmd/podman/container.go b/cmd/podman/container.go
index 4bb6f287a..acbcbb644 100644
--- a/cmd/podman/container.go
+++ b/cmd/podman/container.go
@@ -1,52 +1,29 @@
package main
import (
+ "sort"
+
"github.com/urfave/cli"
)
var (
- subCommands = []cli.Command{
- attachCommand,
- checkpointCommand,
- cleanupCommand,
- containerExistsCommand,
- commitCommand,
- createCommand,
- diffCommand,
- execCommand,
- exportCommand,
+ containerSubCommands = []cli.Command{
inspectCommand,
- killCommand,
- logsCommand,
- psCommand,
- mountCommand,
- pauseCommand,
- portCommand,
- pruneContainersCommand,
- refreshCommand,
- restartCommand,
- restoreCommand,
- rmCommand,
- runCommand,
- runlabelCommand,
- startCommand,
- statsCommand,
- stopCommand,
- topCommand,
- umountCommand,
- unpauseCommand,
- // updateCommand,
- waitCommand,
}
-
containerDescription = "Manage containers"
containerCommand = cli.Command{
Name: "container",
Usage: "Manage Containers",
Description: containerDescription,
ArgsUsage: "",
- Subcommands: subCommands,
+ Subcommands: getContainerSubCommandsSorted(),
UseShortOptionHandling: true,
OnUsageError: usageErrorHandler,
}
)
+
+func getContainerSubCommandsSorted() []cli.Command {
+ containerSubCommands = append(containerSubCommands, getContainerSubCommands()...)
+ sort.Sort(commandSortedAlpha{containerSubCommands})
+ return containerSubCommands
+}
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index dae429047..065d08df4 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "io/ioutil"
"os"
"path/filepath"
"strconv"
@@ -15,13 +16,11 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
ann "github.com/containers/libpod/pkg/annotations"
- "github.com/containers/libpod/pkg/apparmor"
"github.com/containers/libpod/pkg/inspect"
ns "github.com/containers/libpod/pkg/namespaces"
"github.com/containers/libpod/pkg/rootless"
cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
- libpodVersion "github.com/containers/libpod/version"
"github.com/docker/docker/pkg/signal"
"github.com/docker/go-connections/nat"
"github.com/docker/go-units"
@@ -146,7 +145,7 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container
return nil, nil, err
}
- ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx)
+ ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx, nil)
if err != nil {
return nil, nil, err
}
@@ -162,83 +161,9 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container
return ctr, createConfig, nil
}
-// Checks if a user-specified AppArmor profile is loaded, or loads the default profile if
-// AppArmor is enabled.
-// Any interaction with AppArmor requires root permissions.
-func loadAppArmor(config *cc.CreateConfig) error {
- if rootless.IsRootless() {
- noAAMsg := "AppArmor security is not available in rootless mode"
- switch config.ApparmorProfile {
- case "":
- logrus.Warn(noAAMsg)
- case "unconfined":
- default:
- return fmt.Errorf(noAAMsg)
- }
- return nil
- }
-
- if config.ApparmorProfile == "" && apparmor.IsEnabled() {
- // Unless specified otherwise, make sure that the default AppArmor
- // profile is installed. To avoid redundantly loading the profile
- // on each invocation, check if it's loaded before installing it.
- // Suffix the profile with the current libpod version to allow
- // loading the new, potentially updated profile after an update.
- profile := fmt.Sprintf("%s-%s", apparmor.DefaultLibpodProfile, libpodVersion.Version)
-
- loadProfile := func() error {
- isLoaded, err := apparmor.IsLoaded(profile)
- if err != nil {
- return err
- }
- if !isLoaded {
- err = apparmor.InstallDefault(profile)
- if err != nil {
- return err
- }
-
- }
- return nil
- }
-
- if err := loadProfile(); err != nil {
- switch err {
- case apparmor.ErrApparmorUnsupported:
- // do not set the profile when AppArmor isn't supported
- logrus.Debugf("AppArmor is not supported: setting empty profile")
- default:
- return err
- }
- } else {
- logrus.Infof("Sucessfully loaded AppAmor profile '%s'", profile)
- config.ApparmorProfile = profile
- }
- } else if config.ApparmorProfile != "" && config.ApparmorProfile != "unconfined" {
- if !apparmor.IsEnabled() {
- return fmt.Errorf("Profile specified but AppArmor is disabled on the host")
- }
-
- isLoaded, err := apparmor.IsLoaded(config.ApparmorProfile)
- if err != nil {
- switch err {
- case apparmor.ErrApparmorUnsupported:
- return fmt.Errorf("Profile specified but AppArmor is not supported")
- default:
- return fmt.Errorf("Error checking if AppArmor profile is loaded: %v", err)
- }
- }
- if !isLoaded {
- return fmt.Errorf("The specified AppArmor profile '%s' is not loaded", config.ApparmorProfile)
- }
- }
-
- return nil
-}
-
func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
var (
labelOpts []string
- err error
)
if config.PidMode.IsHost() {
@@ -248,7 +173,11 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
if err != nil {
return errors.Wrapf(err, "container %q not found", config.PidMode.Container())
}
- labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...)
+ secopts, err := label.DupSecOpt(ctr.ProcessLabel())
+ if err != nil {
+ return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
+ }
+ labelOpts = append(labelOpts, secopts...)
}
if config.IpcMode.IsHost() {
@@ -258,7 +187,11 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
if err != nil {
return errors.Wrapf(err, "container %q not found", config.IpcMode.Container())
}
- labelOpts = append(labelOpts, label.DupSecOpt(ctr.ProcessLabel())...)
+ secopts, err := label.DupSecOpt(ctr.ProcessLabel())
+ if err != nil {
+ return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
+ }
+ labelOpts = append(labelOpts, secopts...)
}
for _, opt := range securityOpts {
@@ -283,10 +216,6 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
}
}
- if err := loadAppArmor(config); err != nil {
- return err
- }
-
if config.SeccompProfilePath == "" {
if _, err := os.Stat(libpod.SeccompOverridePath); err == nil {
config.SeccompProfilePath = libpod.SeccompOverridePath
@@ -304,7 +233,7 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
}
}
config.LabelOpts = labelOpts
- return err
+ return nil
}
// isPortInPortBindings determines if an exposed host port is in user
@@ -501,6 +430,16 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
}
if c.IsSet("pod") {
if strings.HasPrefix(originalPodName, "new:") {
+ if rootless.IsRootless() {
+ // To create a new pod, we must immediately create the userns.
+ became, ret, err := rootless.BecomeRootInUserNS()
+ if err != nil {
+ return nil, err
+ }
+ if became {
+ os.Exit(ret)
+ }
+ }
// pod does not exist; lets make it
var podOptions []libpod.PodCreateOption
podOptions = append(podOptions, libpod.WithPodName(podName), libpod.WithInfraContainer(), libpod.WithPodCgroups())
@@ -809,6 +748,16 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
Syslog: c.GlobalBool("syslog"),
}
+ if c.Bool("init") {
+ initPath := c.String("init-path")
+ if initPath == "" {
+ initPath = runtime.GetConfig().InitPath
+ }
+ if err := config.AddContainerInitBinary(initPath); err != nil {
+ return nil, err
+ }
+ }
+
if config.Privileged {
config.LabelOpts = label.DisableSecOpt()
} else {
@@ -855,11 +804,15 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l
if s != libpod.ContainerStateRunning && s != libpod.ContainerStatePaused {
continue
}
- pid, err := prevCtr.PID()
+ data, err := ioutil.ReadFile(prevCtr.Config().ConmonPidFile)
if err != nil {
- return false, -1, err
+ return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", prevCtr.Config().ConmonPidFile)
}
- return rootless.JoinNS(uint(pid))
+ conmonPid, err := strconv.Atoi(string(data))
+ if err != nil {
+ return false, -1, errors.Wrapf(err, "cannot parse PID %q", data)
+ }
+ return rootless.JoinDirectUserAndMountNS(uint(conmonPid))
}
}
@@ -887,17 +840,16 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l
return rootless.BecomeRootInUserNS()
}
-func createContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateConfig, ctx context.Context) (*libpod.Container, error) {
+func createContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateConfig, ctx context.Context, pod *libpod.Pod) (*libpod.Container, error) {
runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
if err != nil {
return nil, err
}
- options, err := createConfig.GetContainerCreateOptions(r)
+ options, err := createConfig.GetContainerCreateOptions(r, pod)
if err != nil {
return nil, err
}
-
became, ret, err := joinOrCreateRootlessUserNamespace(createConfig, r)
if err != nil {
return nil, err
diff --git a/cmd/podman/create_cli.go b/cmd/podman/create_cli.go
index 2a820fa43..95b9321fd 100644
--- a/cmd/podman/create_cli.go
+++ b/cmd/podman/create_cli.go
@@ -7,7 +7,7 @@ import (
"strings"
cc "github.com/containers/libpod/pkg/spec"
- "github.com/docker/docker/pkg/sysinfo"
+ "github.com/containers/libpod/pkg/sysinfo"
"github.com/docker/go-units"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@@ -201,6 +201,9 @@ func parseVolumesFrom(volumesFrom []string) error {
}
func validateVolumeHostDir(hostDir string) error {
+ if len(hostDir) == 0 {
+ return errors.Errorf("host directory cannot be empty")
+ }
if filepath.IsAbs(hostDir) {
if _, err := os.Stat(hostDir); err != nil {
return errors.Wrapf(err, "error checking path %q", hostDir)
@@ -212,6 +215,9 @@ func validateVolumeHostDir(hostDir string) error {
}
func validateVolumeCtrDir(ctrDir string) error {
+ if len(ctrDir) == 0 {
+ return errors.Errorf("container directory cannot be empty")
+ }
if !filepath.IsAbs(ctrDir) {
return errors.Errorf("invalid container path, must be an absolute path %q", ctrDir)
}
diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go
index 1dcb88dbd..073e72e64 100644
--- a/cmd/podman/exec.go
+++ b/cmd/podman/exec.go
@@ -3,7 +3,6 @@ package main
import (
"fmt"
"os"
- "strings"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
@@ -35,6 +34,7 @@ var (
Usage: "Sets the username or UID used and optionally the groupname or GID for the specified command",
},
LatestFlag,
+ WorkDirFlag,
}
execDescription = `
podman exec
@@ -99,15 +99,7 @@ func execCmd(c *cli.Context) error {
}
// ENVIRONMENT VARIABLES
- env := defaultEnvVariables
- for _, e := range c.StringSlice("env") {
- split := strings.SplitN(e, "=", 2)
- if len(split) > 1 {
- env[split[0]] = split[1]
- } else {
- env[split[0]] = ""
- }
- }
+ env := map[string]string{}
if err := readKVStrings(env, []string{}, c.StringSlice("env")); err != nil {
return errors.Wrapf(err, "unable to process environment variables")
@@ -117,5 +109,5 @@ func execCmd(c *cli.Context) error {
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
}
- return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"))
+ return ctr.Exec(c.Bool("tty"), c.Bool("privileged"), envs, cmd, c.String("user"), c.String("workdir"))
}
diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go
index 2e2559ec7..a7601aaa2 100644
--- a/cmd/podman/exists.go
+++ b/cmd/podman/exists.go
@@ -5,6 +5,7 @@ import (
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/image"
"github.com/pkg/errors"
"github.com/urfave/cli"
@@ -66,13 +67,15 @@ func imageExistsCmd(c *cli.Context) error {
if len(args) > 1 || len(args) < 1 {
return errors.New("you may only check for the existence of one image at a time")
}
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
- if _, err := runtime.ImageRuntime().NewFromLocal(args[0]); err != nil {
- if errors.Cause(err) == image.ErrNoSuchImage {
+ if _, err := runtime.NewImageFromLocal(args[0]); err != nil {
+ //TODO we need to ask about having varlink defined errors exposed
+ //so we can reuse them
+ if errors.Cause(err) == image.ErrNoSuchImage || err.Error() == "io.podman.ImageNotFound" {
os.Exit(1)
}
return err
@@ -85,13 +88,13 @@ func containerExistsCmd(c *cli.Context) error {
if len(args) > 1 || len(args) < 1 {
return errors.New("you may only check for the existence of one container at a time")
}
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
defer runtime.Shutdown(false)
if _, err := runtime.LookupContainer(args[0]); err != nil {
- if errors.Cause(err) == libpod.ErrNoSuchCtr {
+ if errors.Cause(err) == libpod.ErrNoSuchCtr || err.Error() == "io.podman.ContainerNotFound" {
os.Exit(1)
}
return err
diff --git a/cmd/podman/formats/formats.go b/cmd/podman/formats/formats.go
index 3da0ea385..c454c39bd 100644
--- a/cmd/podman/formats/formats.go
+++ b/cmd/podman/formats/formats.go
@@ -20,6 +20,8 @@ const (
JSONString = "json"
// IDString const to save on duplicates for Go templates
IDString = "{{.ID}}"
+
+ parsingErrorStr = "Template parsing error"
)
// Writer interface for outputs
@@ -96,7 +98,7 @@ func (t StdoutTemplateArray) Out() error {
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")
+ return errors.Wrapf(err, parsingErrorStr)
}
err = headerTmpl.Execute(w, t.Fields)
if err != nil {
@@ -107,13 +109,12 @@ func (t StdoutTemplateArray) Out() error {
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")
+ return errors.Wrapf(err, parsingErrorStr)
}
- for i, img := range t.Output {
+ for i, raw := range t.Output {
basicTmpl := tmpl.Funcs(basicFunctions)
- err = basicTmpl.Execute(w, img)
- if err != nil {
- return err
+ if err := basicTmpl.Execute(w, raw); err != nil {
+ return errors.Wrapf(err, parsingErrorStr)
}
if i != len(t.Output)-1 {
fmt.Fprintln(w, "")
diff --git a/cmd/podman/history.go b/cmd/podman/history.go
index 7c8c619c8..8a9b6cd94 100644
--- a/cmd/podman/history.go
+++ b/cmd/podman/history.go
@@ -7,9 +7,9 @@ import (
"time"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/image"
- units "github.com/docker/go-units"
+ "github.com/docker/go-units"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
@@ -72,7 +72,7 @@ func historyCmd(c *cli.Context) error {
return err
}
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
@@ -88,7 +88,7 @@ func historyCmd(c *cli.Context) error {
return errors.Errorf("podman history takes at most 1 argument")
}
- image, err := runtime.ImageRuntime().NewFromLocal(args[0])
+ image, err := runtime.NewImageFromLocal(args[0])
if err != nil {
return err
}
diff --git a/cmd/podman/image.go b/cmd/podman/image.go
index e978b9cf5..a51a90b0e 100644
--- a/cmd/podman/image.go
+++ b/cmd/podman/image.go
@@ -1,35 +1,36 @@
package main
import (
+ "sort"
+
"github.com/urfave/cli"
)
var (
imageSubCommands = []cli.Command{
- buildCommand,
historyCommand,
- importCommand,
imageExistsCommand,
inspectCommand,
- loadCommand,
lsImagesCommand,
pruneImagesCommand,
pullCommand,
- pushCommand,
rmImageCommand,
- saveCommand,
tagCommand,
- trustCommand,
}
-
imageDescription = "Manage images"
imageCommand = cli.Command{
Name: "image",
Usage: "Manage images",
Description: imageDescription,
ArgsUsage: "",
- Subcommands: imageSubCommands,
+ Subcommands: getImageSubCommandsSorted(),
UseShortOptionHandling: true,
OnUsageError: usageErrorHandler,
}
)
+
+func getImageSubCommandsSorted() []cli.Command {
+ imageSubCommands = append(imageSubCommands, getImageSubCommands()...)
+ sort.Sort(commandSortedAlpha{imageSubCommands})
+ return imageSubCommands
+}
diff --git a/cmd/podman/imagefilters/filters.go b/cmd/podman/imagefilters/filters.go
new file mode 100644
index 000000000..366510202
--- /dev/null
+++ b/cmd/podman/imagefilters/filters.go
@@ -0,0 +1,84 @@
+package imagefilters
+
+import (
+ "context"
+ "strings"
+ "time"
+
+ "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/pkg/inspect"
+)
+
+// ResultFilter is a mock function for image filtering
+type ResultFilter func(*adapter.ContainerImage) bool
+
+// Filter is a function to determine whether an image is included in
+// command output. Images to be outputted are tested using the function. A true
+// return will include the image, a false return will exclude it.
+type Filter func(*adapter.ContainerImage, *inspect.ImageData) bool
+
+// CreatedBeforeFilter allows you to filter on images created before
+// the given time.Time
+func CreatedBeforeFilter(createTime time.Time) ResultFilter {
+ return func(i *adapter.ContainerImage) bool {
+ return i.Created().Before(createTime)
+ }
+}
+
+// CreatedAfterFilter allows you to filter on images created after
+// the given time.Time
+func CreatedAfterFilter(createTime time.Time) ResultFilter {
+ return func(i *adapter.ContainerImage) bool {
+ return i.Created().After(createTime)
+ }
+}
+
+// DanglingFilter allows you to filter images for dangling images
+func DanglingFilter() ResultFilter {
+ return func(i *adapter.ContainerImage) bool {
+ return i.Dangling()
+ }
+}
+
+// LabelFilter allows you to filter by images labels key and/or value
+func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
+ // We need to handle both label=key and label=key=value
+ return func(i *adapter.ContainerImage) bool {
+ var value string
+ splitFilter := strings.Split(labelfilter, "=")
+ key := splitFilter[0]
+ if len(splitFilter) > 1 {
+ value = splitFilter[1]
+ }
+ labels, err := i.Labels(ctx)
+ if err != nil {
+ return false
+ }
+ if len(strings.TrimSpace(labels[key])) > 0 && len(strings.TrimSpace(value)) == 0 {
+ return true
+ }
+ return labels[key] == value
+ }
+}
+
+// OutputImageFilter allows you to filter by an a specific image name
+func OutputImageFilter(userImage *adapter.ContainerImage) ResultFilter {
+ return func(i *adapter.ContainerImage) bool {
+ return userImage.ID() == i.ID()
+ }
+}
+
+// FilterImages filters images using a set of predefined filter funcs
+func FilterImages(images []*adapter.ContainerImage, filters []ResultFilter) []*adapter.ContainerImage {
+ var filteredImages []*adapter.ContainerImage
+ for _, image := range images {
+ include := true
+ for _, filter := range filters {
+ include = include && filter(image)
+ }
+ if include {
+ filteredImages = append(filteredImages, image)
+ }
+ }
+ return filteredImages
+}
diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index 522863b1b..031f06618 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -9,11 +9,11 @@ import (
"unicode"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/cmd/podman/imagefilters"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/libpod/image"
"github.com/docker/go-units"
- digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@@ -145,20 +145,20 @@ var (
func imagesCmd(c *cli.Context) error {
var (
- filterFuncs []image.ResultFilter
- newImage *image.Image
+ filterFuncs []imagefilters.ResultFilter
+ newImage *adapter.ContainerImage
)
if err := validateFlags(c, imagesFlags); err != nil {
return err
}
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "Could not get runtime")
}
defer runtime.Shutdown(false)
if len(c.Args()) == 1 {
- newImage, err = runtime.ImageRuntime().NewFromLocal(c.Args().Get(0))
+ newImage, err = runtime.NewImageFromLocal(c.Args().Get(0))
if err != nil {
return err
}
@@ -195,20 +195,20 @@ func imagesCmd(c *cli.Context) error {
children to the image once built. until buildah supports caching builds,
it will not generate these intermediate images.
*/
- images, err := runtime.ImageRuntime().GetImages()
+ images, err := runtime.GetImages()
if err != nil {
return errors.Wrapf(err, "unable to get images")
}
- var filteredImages []*image.Image
- // filter the images
+ var filteredImages []*adapter.ContainerImage
+ //filter the images
if len(c.StringSlice("filter")) > 0 || newImage != nil {
- filteredImages = image.FilterImages(images, filterFuncs)
+ filteredImages = imagefilters.FilterImages(images, filterFuncs)
} else {
filteredImages = images
}
- return generateImagesOutput(ctx, runtime, filteredImages, opts)
+ return generateImagesOutput(ctx, filteredImages, opts)
}
func (i imagesOptions) setOutputFormat() string {
@@ -263,7 +263,7 @@ func sortImagesOutput(sortBy string, imagesOutput imagesSorted) imagesSorted {
}
// getImagesTemplateOutput returns the images information to be printed in human readable format
-func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, images []*image.Image, opts imagesOptions) (imagesOutput imagesSorted) {
+func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) (imagesOutput imagesSorted) {
for _, img := range images {
// If all is false and the image doesn't have a name, check to see if the top layer of the image is a parent
// to another image's top layer. If it is, then it is an intermediate image so don't print out if the --all flag
@@ -319,7 +319,7 @@ func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, image
}
// getImagesJSONOutput returns the images information in its raw form
-func getImagesJSONOutput(ctx context.Context, runtime *libpod.Runtime, images []*image.Image) (imagesOutput []imagesJSONParams) {
+func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) (imagesOutput []imagesJSONParams) {
for _, img := range images {
size, err := img.Size(ctx)
if err != nil {
@@ -339,7 +339,7 @@ func getImagesJSONOutput(ctx context.Context, runtime *libpod.Runtime, images []
// generateImagesOutput generates the images based on the format provided
-func generateImagesOutput(ctx context.Context, runtime *libpod.Runtime, images []*image.Image, opts imagesOptions) error {
+func generateImagesOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) error {
if len(images) == 0 {
return nil
}
@@ -347,10 +347,10 @@ func generateImagesOutput(ctx context.Context, runtime *libpod.Runtime, images [
switch opts.format {
case formats.JSONString:
- imagesOutput := getImagesJSONOutput(ctx, runtime, images)
+ imagesOutput := getImagesJSONOutput(ctx, images)
out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)}
default:
- imagesOutput := getImagesTemplateOutput(ctx, runtime, images, opts)
+ imagesOutput := getImagesTemplateOutput(ctx, images, opts)
out = formats.StdoutTemplateArray{Output: imagesToGeneric(imagesOutput, []imagesJSONParams{}), Template: opts.outputformat, Fields: imagesOutput[0].HeaderMap()}
}
return formats.Writer(out).Out()
@@ -375,34 +375,34 @@ func (i *imagesTemplateParams) HeaderMap() map[string]string {
// CreateFilterFuncs returns an array of filter functions based on the user inputs
// and is later used to filter images for output
-func CreateFilterFuncs(ctx context.Context, r *libpod.Runtime, c *cli.Context, img *image.Image) ([]image.ResultFilter, error) {
- var filterFuncs []image.ResultFilter
+func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, c *cli.Context, img *adapter.ContainerImage) ([]imagefilters.ResultFilter, error) {
+ var filterFuncs []imagefilters.ResultFilter
for _, filter := range c.StringSlice("filter") {
splitFilter := strings.Split(filter, "=")
switch splitFilter[0] {
case "before":
- before, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
+ before, err := r.NewImageFromLocal(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
- filterFuncs = append(filterFuncs, image.CreatedBeforeFilter(before.Created()))
+ filterFuncs = append(filterFuncs, imagefilters.CreatedBeforeFilter(before.Created()))
case "after":
- after, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
+ after, err := r.NewImageFromLocal(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
- filterFuncs = append(filterFuncs, image.CreatedAfterFilter(after.Created()))
+ filterFuncs = append(filterFuncs, imagefilters.CreatedAfterFilter(after.Created()))
case "dangling":
- filterFuncs = append(filterFuncs, image.DanglingFilter())
+ filterFuncs = append(filterFuncs, imagefilters.DanglingFilter())
case "label":
labelFilter := strings.Join(splitFilter[1:], "=")
- filterFuncs = append(filterFuncs, image.LabelFilter(ctx, labelFilter))
+ filterFuncs = append(filterFuncs, imagefilters.LabelFilter(ctx, labelFilter))
default:
return nil, errors.Errorf("invalid filter %s ", splitFilter[0])
}
}
if img != nil {
- filterFuncs = append(filterFuncs, image.OutputImageFilter(img))
+ filterFuncs = append(filterFuncs, imagefilters.OutputImageFilter(img))
}
return filterFuncs, nil
}
diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go
index cb72a498f..06879e02d 100644
--- a/cmd/podman/images_prune.go
+++ b/cmd/podman/images_prune.go
@@ -1,8 +1,8 @@
package main
import (
+ "fmt"
"github.com/containers/libpod/cmd/podman/libpodruntime"
- "github.com/containers/libpod/cmd/podman/shared"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
@@ -30,5 +30,16 @@ func pruneImagesCmd(c *cli.Context) error {
}
defer runtime.Shutdown(false)
- return shared.Prune(runtime.ImageRuntime())
+ pruneImages, err := runtime.ImageRuntime().GetPruneImages()
+ if err != nil {
+ return err
+ }
+
+ for _, i := range pruneImages {
+ if err := i.Remove(true); err != nil {
+ return errors.Wrapf(err, "failed to remove %s", i.ID())
+ }
+ fmt.Println(i.ID())
+ }
+ return nil
}
diff --git a/cmd/podman/info.go b/cmd/podman/info.go
index c0639725e..3888829a3 100644
--- a/cmd/podman/info.go
+++ b/cmd/podman/info.go
@@ -1,11 +1,13 @@
package main
import (
- "runtime"
+ "fmt"
+ rt "runtime"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/adapter"
+ "github.com/containers/libpod/version"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
@@ -38,8 +40,9 @@ func infoCmd(c *cli.Context) error {
return err
}
info := map[string]interface{}{}
+ remoteClientInfo := map[string]interface{}{}
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
@@ -49,8 +52,14 @@ func infoCmd(c *cli.Context) error {
if err != nil {
return errors.Wrapf(err, "error getting info")
}
+ if runtime.Remote {
+ remoteClientInfo["RemoteAPI Version"] = version.RemoteAPIVersion
+ remoteClientInfo["Podman Version"] = version.Version
+ remoteClientInfo["OS Arch"] = fmt.Sprintf("%s/%s", rt.GOOS, rt.GOARCH)
+ infoArr = append(infoArr, libpod.InfoData{Type: "client", Data: remoteClientInfo})
+ }
- if c.Bool("debug") {
+ if !runtime.Remote && c.Bool("debug") {
debugInfo := debugInfo(c)
infoArr = append(infoArr, libpod.InfoData{Type: "debug", Data: debugInfo})
}
@@ -78,8 +87,8 @@ func infoCmd(c *cli.Context) error {
// top-level "debug" info
func debugInfo(c *cli.Context) map[string]interface{} {
info := map[string]interface{}{}
- info["compiler"] = runtime.Compiler
- info["go version"] = runtime.Version()
+ info["compiler"] = rt.Compiler
+ info["go version"] = rt.Version()
info["podman version"] = c.App.Version
version, _ := libpod.GetVersion()
info["git commit"] = version.GitCommit
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index 6ffcde55f..3ef740463 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -2,12 +2,13 @@ package main
import (
"context"
+ "encoding/json"
"strings"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/adapter"
+ cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/urfave/cli"
@@ -63,7 +64,7 @@ func inspectCmd(c *cli.Context) error {
return err
}
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
@@ -87,6 +88,9 @@ func inspectCmd(c *cli.Context) error {
}
inspectedObjects, iterateErr := iterateInput(getContext(), c, args, runtime, inspectType)
+ if iterateErr != nil {
+ return iterateErr
+ }
var out formats.Writer
if outputFormat != "" && outputFormat != formats.JSONString {
@@ -97,12 +101,11 @@ func inspectCmd(c *cli.Context) error {
out = formats.JSONStructArray{Output: inspectedObjects}
}
- formats.Writer(out).Out()
- return iterateErr
+ return formats.Writer(out).Out()
}
// func iterateInput iterates the images|containers the user has requested and returns the inspect data and error
-func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *libpod.Runtime, inspectType string) ([]interface{}, error) {
+func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *adapter.LocalRuntime, inspectType string) ([]interface{}, error) {
var (
data interface{}
inspectedItems []interface{}
@@ -122,13 +125,18 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
break
}
- data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
+ artifact, err := getArtifact(ctr)
+ if inspectError != nil {
+ inspectError = err
+ break
+ }
+ data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact)
if err != nil {
inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID())
break
}
case inspectTypeImage:
- image, err := runtime.ImageRuntime().NewFromLocal(input)
+ image, err := runtime.NewImageFromLocal(input)
if err != nil {
inspectError = errors.Wrapf(err, "error getting image %q", input)
break
@@ -141,7 +149,7 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
case inspectAll:
ctr, err := runtime.LookupContainer(input)
if err != nil {
- image, err := runtime.ImageRuntime().NewFromLocal(input)
+ image, err := runtime.NewImageFromLocal(input)
if err != nil {
inspectError = errors.Wrapf(err, "error getting image %q", input)
break
@@ -157,7 +165,12 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
break
}
- data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
+ artifact, inspectError := getArtifact(ctr)
+ if inspectError != nil {
+ inspectError = err
+ break
+ }
+ data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact)
if err != nil {
inspectError = errors.Wrapf(err, "error parsing container data %s", ctr.ID())
break
@@ -170,3 +183,15 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
}
return inspectedItems, inspectError
}
+
+func getArtifact(ctr *adapter.Container) (*cc.CreateConfig, error) {
+ var createArtifact cc.CreateConfig
+ artifact, err := ctr.GetArtifact("create-config")
+ if err != nil {
+ return nil, err
+ }
+ if err := json.Unmarshal(artifact, &createArtifact); err != nil {
+ return nil, err
+ }
+ return &createArtifact, nil
+}
diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go
index d7a0dd931..dca2f5022 100644
--- a/cmd/podman/libpodruntime/runtime.go
+++ b/cmd/podman/libpodruntime/runtime.go
@@ -4,17 +4,15 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/util"
- "github.com/containers/storage"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// GetRuntime generates a new libpod runtime configured by command line options
func GetRuntime(c *cli.Context) (*libpod.Runtime, error) {
- storageOpts := new(storage.StoreOptions)
options := []libpod.RuntimeOption{}
- _, volumePath, err := util.GetDefaultStoreOptions()
+ storageOpts, volumePath, err := util.GetDefaultStoreOptions()
if err != nil {
return nil, err
}
@@ -44,7 +42,7 @@ func GetRuntime(c *cli.Context) (*libpod.Runtime, error) {
storageOpts.GraphDriverOptions = c.GlobalStringSlice("storage-opt")
}
- options = append(options, libpod.WithStorageConfig(*storageOpts))
+ options = append(options, libpod.WithStorageConfig(storageOpts))
// TODO CLI flags for image config?
// TODO CLI flag for signature policy?
diff --git a/cmd/podman/login.go b/cmd/podman/login.go
index 4452651f8..fc7b39ed8 100644
--- a/cmd/podman/login.go
+++ b/cmd/podman/login.go
@@ -62,12 +62,18 @@ func loginCmd(c *cli.Context) error {
return errors.Errorf("too many arguments, login takes only 1 argument")
}
if len(args) == 0 {
- return errors.Errorf("registry must be given")
+ return errors.Errorf("please specify a registry to login to")
}
server := registryFromFullName(scrubServer(args[0]))
authfile := getAuthFile(c.String("authfile"))
sc := common.GetSystemContext("", authfile, false)
+ if c.IsSet("tls-verify") {
+ sc.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify"))
+ }
+ if c.String("cert-dir") != "" {
+ sc.DockerCertPath = c.String("cert-dir")
+ }
if c.IsSet("get-login") {
user, err := config.GetUserLoggedIn(sc, server)
@@ -87,39 +93,25 @@ func loginCmd(c *cli.Context) error {
// username of user logged in to server (if one exists)
userFromAuthFile, passFromAuthFile, err := config.GetAuthentication(sc, server)
if err != nil {
- return errors.Wrapf(err, "error getting logged-in user")
+ return errors.Wrapf(err, "error reading auth file")
}
ctx := getContext()
-
- var (
- username string
- password string
- )
-
- if userFromAuthFile != "" {
- username = userFromAuthFile
- password = passFromAuthFile
+ // If no username and no password is specified, try to use existing ones.
+ if c.String("username") == "" && c.String("password") == "" {
fmt.Println("Authenticating with existing credentials...")
- if err := docker.CheckAuth(ctx, sc, username, password, server); err == nil {
+ if err := docker.CheckAuth(ctx, sc, userFromAuthFile, passFromAuthFile, server); err == nil {
fmt.Println("Existing credentials are valid. Already logged in to", server)
return nil
}
fmt.Println("Existing credentials are invalid, please enter valid username and password")
}
- username, password, err = getUserAndPass(c.String("username"), c.String("password"), userFromAuthFile)
+ username, password, err := getUserAndPass(c.String("username"), c.String("password"), userFromAuthFile)
if err != nil {
return errors.Wrapf(err, "error getting username and password")
}
- if c.IsSet("tls-verify") {
- sc.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify"))
- }
- if c.String("cert-dir") != "" {
- sc.DockerCertPath = c.String("cert-dir")
- }
-
if err = docker.CheckAuth(ctx, sc, username, password, server); err == nil {
// Write the new credentials to the authfile
if err = config.SetAuthentication(sc, server, username, password); err != nil {
@@ -131,14 +123,15 @@ func loginCmd(c *cli.Context) error {
fmt.Println("Login Succeeded!")
return nil
case docker.ErrUnauthorizedForCredentials:
- return errors.Errorf("error logging into %q: invalid username/password\n", server)
+ return errors.Errorf("error logging into %q: invalid username/password", 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
+// using the -u and -p flags. If the username prompt is left empty, the
+// displayed userFromAuthFile will be used instead.
func getUserAndPass(username, password, userFromAuthFile string) (string, string, error) {
var err error
reader := bufio.NewReader(os.Stdin)
@@ -152,7 +145,10 @@ func getUserAndPass(username, password, userFromAuthFile string) (string, string
if err != nil {
return "", "", errors.Wrapf(err, "error reading username")
}
- // If no username provided, use userFromAuthFile instead.
+ // If the user just hit enter, use the displayed user from the
+ // the authentication file. This allows to do a lazy
+ // `$ podman login -p $NEW_PASSWORD` without specifying the
+ // user.
if strings.TrimSpace(username) == "" {
username = userFromAuthFile
}
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 7ef22a93b..c10590006 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"runtime/pprof"
+ "sort"
"syscall"
"github.com/containers/libpod/libpod"
@@ -47,6 +48,28 @@ var cmdsNotRequiringRootless = map[string]bool{
"top": true,
}
+type commandSorted []cli.Command
+
+func (a commandSorted) Len() int { return len(a) }
+func (a commandSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type commandSortedAlpha struct{ commandSorted }
+
+func (a commandSortedAlpha) Less(i, j int) bool {
+ return a.commandSorted[i].Name < a.commandSorted[j].Name
+}
+
+type flagSorted []cli.Flag
+
+func (a flagSorted) Len() int { return len(a) }
+func (a flagSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type flagSortedAlpha struct{ flagSorted }
+
+func (a flagSortedAlpha) Less(i, j int) bool {
+ return a.flagSorted[i].GetName() < a.flagSorted[j].GetName()
+}
+
func main() {
debug := false
cpuProfile := false
@@ -64,52 +87,21 @@ func main() {
app.Version = version.Version
app.Commands = []cli.Command{
- attachCommand,
- commitCommand,
containerCommand,
- buildCommand,
- createCommand,
- diffCommand,
- execCommand,
- exportCommand,
historyCommand,
imageCommand,
imagesCommand,
- importCommand,
infoCommand,
inspectCommand,
- killCommand,
- kubeCommand,
- loadCommand,
- loginCommand,
- logoutCommand,
- logsCommand,
- mountCommand,
- pauseCommand,
- psCommand,
- podCommand,
- portCommand,
pullCommand,
- pushCommand,
- playCommand,
- restartCommand,
- rmCommand,
rmiCommand,
- runCommand,
- saveCommand,
- searchCommand,
- startCommand,
- statsCommand,
- stopCommand,
tagCommand,
- topCommand,
- umountCommand,
- unpauseCommand,
versionCommand,
- volumeCommand,
- waitCommand,
}
+ app.Commands = append(app.Commands, getAppCommands()...)
+ sort.Sort(commandSortedAlpha{app.Commands})
+
if varlinkCommand != nil {
app.Commands = append(app.Commands, *varlinkCommand)
}
@@ -120,7 +112,7 @@ func main() {
os.Exit(1)
}
args := c.Args()
- if args.Present() {
+ if args.Present() && rootless.IsRootless() {
if _, notRequireRootless := cmdsNotRequiringRootless[args.First()]; !notRequireRootless {
became, ret, err := rootless.BecomeRootInUserNS()
if err != nil {
@@ -148,19 +140,26 @@ func main() {
logrus.SetLevel(level)
}
- // Only if not rootless, set rlimits for open files.
- // We open numerous FDs for ports opened
- if !rootless.IsRootless() {
- rlimits := new(syscall.Rlimit)
- rlimits.Cur = 1048576
- rlimits.Max = 1048576
+ rlimits := new(syscall.Rlimit)
+ rlimits.Cur = 1048576
+ rlimits.Max = 1048576
+ if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
+ if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
+ return errors.Wrapf(err, "error getting rlimits")
+ }
+ rlimits.Cur = rlimits.Max
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil {
return errors.Wrapf(err, "error setting new rlimits")
}
- } else {
+ }
+
+ if rootless.IsRootless() {
logrus.Info("running as rootless")
}
+ // Be sure we can create directories with 0755 mode.
+ syscall.Umask(0022)
+
if logLevel == "debug" {
debug = true
@@ -185,84 +184,32 @@ func main() {
}
app.Flags = []cli.Flag{
cli.StringFlag{
- Name: "cgroup-manager",
- Usage: "cgroup manager to use (cgroupfs or systemd, default systemd)",
- },
- cli.StringFlag{
- Name: "cni-config-dir",
- Usage: "path of the configuration directory for CNI networks",
- },
- cli.StringFlag{
Name: "config, c",
Usage: "path of a libpod config file detailing container server configuration options",
Hidden: true,
},
cli.StringFlag{
- Name: "conmon",
- Usage: "path of the conmon binary",
- },
- cli.StringFlag{
Name: "cpu-profile",
Usage: "path for the cpu profiling results",
},
cli.StringFlag{
- Name: "default-mounts-file",
- Usage: "path to default mounts file",
- Hidden: true,
- },
- cli.StringSliceFlag{
- Name: "hooks-dir",
- Usage: "set the OCI hooks directory path (may be set multiple times)",
- },
- cli.IntFlag{
- Name: "max-workers",
- Usage: "the maximum number of workers for parallel operations",
- Hidden: true,
- },
- cli.StringFlag{
Name: "log-level",
Usage: "log messages above specified level: debug, info, warn, error (default), fatal or panic",
Value: "error",
},
cli.StringFlag{
- Name: "namespace",
- Usage: "set the libpod namespace, used to create separate views of the containers and pods on the system",
- Value: "",
- },
- cli.StringFlag{
- Name: "root",
- Usage: "path to the root directory in which data, including images, is stored",
- },
- cli.StringFlag{
Name: "tmpdir",
Usage: "path to the tmp directory",
},
- 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",
- },
- cli.BoolFlag{
- Name: "syslog",
- Usage: "output logging information to syslog as well as the console",
- },
- }
- if _, err := os.Stat("/etc/containers/registries.conf"); err != nil {
- if os.IsNotExist(err) {
- logrus.Warn("unable to find /etc/containers/registries.conf. some podman (image shortnames) commands may be limited")
- }
}
+
+ app.Flags = append(app.Flags, getMainAppFlags()...)
+ sort.Sort(flagSortedAlpha{app.Flags})
+
+ // Check if /etc/containers/registries.conf exists when running in
+ // in a local environment.
+ CheckForRegistries()
+
if err := app.Run(os.Args); err != nil {
if debug {
logrus.Errorf(err.Error())
diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go
index c91115597..86a6b2ad1 100644
--- a/cmd/podman/mount.go
+++ b/cmd/podman/mount.go
@@ -24,13 +24,18 @@ var (
mountFlags = []cli.Flag{
cli.BoolFlag{
- Name: "notruncate",
- Usage: "do not truncate output",
+ Name: "all, a",
+ Usage: "Mount all containers",
},
cli.StringFlag{
Name: "format",
Usage: "Change the output format to Go template",
},
+ cli.BoolFlag{
+ Name: "notruncate",
+ Usage: "do not truncate output",
+ },
+ LatestFlag,
}
mountCommand = cli.Command{
Name: "mount",
@@ -80,20 +85,31 @@ func mountCmd(c *cli.Context) error {
}
}
+ if c.Bool("all") && c.Bool("latest") {
+ return errors.Errorf("--all and --latest cannot be used together")
+ }
+
+ mountContainers, err := getAllOrLatestContainers(c, runtime, -1, "all")
+ if err != nil {
+ if len(mountContainers) == 0 {
+ return err
+ }
+ fmt.Println(err.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"))
}
var lastError error
- if len(args) > 0 {
- for _, name := range args {
+ if len(mountContainers) > 0 {
+ for _, ctr := range mountContainers {
if json {
if lastError != nil {
logrus.Error(lastError)
@@ -101,14 +117,6 @@ func mountCmd(c *cli.Context) error {
lastError = errors.Wrapf(err, "json option cannot be used with a container id")
continue
}
- ctr, err := runtime.LookupContainer(name)
- if err != nil {
- if lastError != nil {
- logrus.Error(lastError)
- }
- lastError = errors.Wrapf(err, "error looking up container %q", name)
- continue
- }
mountPoint, err := ctr.Mount()
if err != nil {
if lastError != nil {
diff --git a/cmd/podman/platform_linux.go b/cmd/podman/platform_linux.go
new file mode 100644
index 000000000..2127923ae
--- /dev/null
+++ b/cmd/podman/platform_linux.go
@@ -0,0 +1,17 @@
+// +build linux
+
+package main
+
+import (
+ "os"
+
+ "github.com/sirupsen/logrus"
+)
+
+func CheckForRegistries() {
+ if _, err := os.Stat("/etc/containers/registries.conf"); err != nil {
+ if os.IsNotExist(err) {
+ logrus.Warn("unable to find /etc/containers/registries.conf. some podman (image shortnames) commands may be limited")
+ }
+ }
+}
diff --git a/cmd/podman/platform_unsupported.go b/cmd/podman/platform_unsupported.go
new file mode 100644
index 000000000..f39eeaf63
--- /dev/null
+++ b/cmd/podman/platform_unsupported.go
@@ -0,0 +1,6 @@
+// +build !linux
+
+package main
+
+func CheckForRegistries() {
+}
diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go
index f165c5f0f..2ce2e21bb 100644
--- a/cmd/podman/play_kube.go
+++ b/cmd/podman/play_kube.go
@@ -154,7 +154,7 @@ func playKubeYAMLCmd(c *cli.Context) error {
if err != nil {
return err
}
- ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx)
+ ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx, pod)
if err != nil {
return err
}
diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go
index 47130805e..2a78d0c54 100644
--- a/cmd/podman/pull.go
+++ b/cmd/podman/pull.go
@@ -9,7 +9,7 @@ import (
dockerarchive "github.com/containers/image/docker/archive"
"github.com/containers/image/transports/alltransports"
"github.com/containers/image/types"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod/adapter"
image2 "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
@@ -64,7 +64,7 @@ specified, the image with the 'latest' tag (if it exists) is pulled
// 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 := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
@@ -116,14 +116,14 @@ func pullCmd(c *cli.Context) error {
if err != nil {
return errors.Wrapf(err, "error parsing %q", image)
}
- newImage, err := runtime.ImageRuntime().LoadFromArchiveReference(getContext(), srcRef, c.String("signature-policy"), writer)
+ newImage, err := runtime.LoadFromArchiveReference(getContext(), srcRef, c.String("signature-policy"), writer)
if err != nil {
return errors.Wrapf(err, "error pulling image from %q", image)
}
imgID = newImage[0].ID()
} else {
authfile := getAuthFile(c.String("authfile"))
- newImage, err := runtime.ImageRuntime().New(getContext(), image, c.String("signature-policy"), authfile, writer, &dockerRegistryOptions, image2.SigningOptions{}, true)
+ newImage, err := runtime.New(getContext(), image, c.String("signature-policy"), authfile, writer, &dockerRegistryOptions, image2.SigningOptions{}, true)
if err != nil {
return errors.Wrapf(err, "error pulling image %q", image)
}
diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go
index 5e8ac81a2..fbf860eb2 100644
--- a/cmd/podman/rmi.go
+++ b/cmd/podman/rmi.go
@@ -4,8 +4,7 @@ import (
"fmt"
"os"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
- "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/urfave/cli"
@@ -58,7 +57,7 @@ func rmiCmd(c *cli.Context) error {
return err
}
removeAll := c.Bool("all")
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not get runtime")
}
@@ -74,7 +73,7 @@ func rmiCmd(c *cli.Context) error {
images := args[:]
- removeImage := func(img *image.Image) {
+ removeImage := func(img *adapter.ContainerImage) {
deleted = true
msg, deleteErr = runtime.RemoveImage(ctx, img, c.Bool("force"))
if deleteErr != nil {
@@ -91,8 +90,8 @@ func rmiCmd(c *cli.Context) error {
}
if removeAll {
- var imagesToDelete []*image.Image
- imagesToDelete, err = runtime.ImageRuntime().GetImages()
+ var imagesToDelete []*adapter.ContainerImage
+ imagesToDelete, err = runtime.GetImages()
if err != nil {
return errors.Wrapf(err, "unable to query local images")
}
@@ -112,7 +111,7 @@ func rmiCmd(c *cli.Context) error {
removeImage(i)
}
lastNumberofImages = len(imagesToDelete)
- imagesToDelete, err = runtime.ImageRuntime().GetImages()
+ imagesToDelete, err = runtime.GetImages()
if err != nil {
return err
}
@@ -130,7 +129,7 @@ func rmiCmd(c *cli.Context) error {
// See https://github.com/containers/libpod/issues/930 as
// an exemplary inconsistency issue.
for _, i := range images {
- newImage, err := runtime.ImageRuntime().NewFromLocal(i)
+ newImage, err := runtime.NewImageFromLocal(i)
if err != nil {
fmt.Fprintln(os.Stderr, err)
continue
diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go
index 30beb4a49..9040c4a5c 100644
--- a/cmd/podman/shared/container.go
+++ b/cmd/podman/shared/container.go
@@ -2,8 +2,8 @@ package shared
import (
"context"
- "encoding/json"
"fmt"
+ "github.com/google/shlex"
"io"
"os"
"path/filepath"
@@ -51,7 +51,7 @@ type PsOptions struct {
// BatchContainerStruct is the return obkect from BatchContainer and contains
// container related information
type BatchContainerStruct struct {
- ConConfig *libpod.Config
+ ConConfig *libpod.ContainerConfig
ConState libpod.ContainerStatus
ExitCode int32
Exited bool
@@ -328,7 +328,7 @@ func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsCon
// locks.
func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) {
var (
- conConfig *libpod.Config
+ conConfig *libpod.ContainerConfig
conState libpod.ContainerStatus
err error
exitCode int32
@@ -445,8 +445,7 @@ func getStrFromSquareBrackets(cmd string) string {
// GetCtrInspectInfo takes container inspect data and collects all its info into a ContainerData
// structure for inspection related methods
-func GetCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerInspectData) (*inspect.ContainerData, error) {
- config := ctr.Config()
+func GetCtrInspectInfo(config *libpod.ContainerConfig, ctrInspectData *inspect.ContainerInspectData, createArtifact *cc.CreateConfig) (*inspect.ContainerData, error) {
spec := config.Spec
cpus, mems, period, quota, realtimePeriod, realtimeRuntime, shares := getCPUInfo(spec)
@@ -455,16 +454,6 @@ func GetCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerI
pidsLimit := getPidsInfo(spec)
cgroup := getCgroup(spec)
- var createArtifact cc.CreateConfig
- artifact, err := ctr.GetArtifact("create-config")
- if err == nil {
- if err := json.Unmarshal(artifact, &createArtifact); err != nil {
- return nil, err
- }
- } else {
- logrus.Errorf("couldn't get some inspect information, error getting artifact %q: %v", ctr.ID(), err)
- }
-
data := &inspect.ContainerData{
ctrInspectData,
&inspect.HostConfig{
@@ -492,7 +481,7 @@ func GetCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerI
PidsLimit: pidsLimit,
Privileged: config.Privileged,
ReadonlyRootfs: spec.Root.Readonly,
- Runtime: ctr.RuntimeName(),
+ Runtime: config.OCIRuntime,
NetworkMode: string(createArtifact.NetMode),
IpcMode: string(createArtifact.IpcMode),
Cgroup: cgroup,
@@ -640,6 +629,14 @@ func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtim
// GenerateRunlabelCommand generates the command that will eventually be execucted by podman
func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]string, extraArgs []string) ([]string, []string, error) {
+ // If no name is provided, we use the image's basename instead
+ if name == "" {
+ baseName, err := image.GetImageBaseName(imageName)
+ if err != nil {
+ return nil, nil, err
+ }
+ name = baseName
+ }
// The user provided extra arguments that need to be tacked onto the label's command
if len(extraArgs) > 0 {
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(extraArgs, " "))
@@ -665,7 +662,10 @@ func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]s
return ""
}
newS := os.Expand(strings.Join(cmd, " "), envmapper)
- cmd = strings.Split(newS, " ")
+ cmd, err = shlex.Split(newS)
+ if err != nil {
+ return nil, nil, err
+ }
return cmd, env, nil
}
diff --git a/cmd/podman/shared/funcs.go b/cmd/podman/shared/funcs.go
index 8770b8ec0..70d041fd2 100644
--- a/cmd/podman/shared/funcs.go
+++ b/cmd/podman/shared/funcs.go
@@ -65,6 +65,8 @@ func GenerateCommand(command, imageName, name string) ([]string, error) {
switch arg {
case "IMAGE":
newArg = imageName
+ case "$IMAGE":
+ newArg = imageName
case "IMAGE=IMAGE":
newArg = fmt.Sprintf("IMAGE=%s", imageName)
case "IMAGE=$IMAGE":
@@ -75,6 +77,8 @@ func GenerateCommand(command, imageName, name string) ([]string, error) {
newArg = fmt.Sprintf("NAME=%s", name)
case "NAME=$NAME":
newArg = fmt.Sprintf("NAME=%s", name)
+ case "$NAME":
+ newArg = name
default:
newArg = arg
}
diff --git a/cmd/podman/shared/prune.go b/cmd/podman/shared/prune.go
deleted file mode 100644
index 90cfe4475..000000000
--- a/cmd/podman/shared/prune.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package shared
-
-import (
- "fmt"
- "github.com/pkg/errors"
-
- "github.com/containers/libpod/libpod/image"
-)
-
-// Prune removes all unnamed and unused images from the local store
-func Prune(ir *image.Runtime) error {
- pruneImages, err := ir.GetPruneImages()
- if err != nil {
- return err
- }
-
- for _, i := range pruneImages {
- if err := i.Remove(true); err != nil {
- return errors.Wrapf(err, "failed to remove %s", i.ID())
- }
- fmt.Println(i.ID())
- }
- return nil
-}
diff --git a/cmd/podman/sign.go b/cmd/podman/sign.go
new file mode 100644
index 000000000..1d9aecdc9
--- /dev/null
+++ b/cmd/podman/sign.go
@@ -0,0 +1,198 @@
+package main
+
+import (
+ "io/ioutil"
+ "net/url"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/containers/image/signature"
+ "github.com/containers/image/transports"
+ "github.com/containers/image/transports/alltransports"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/trust"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+)
+
+var (
+ signFlags = []cli.Flag{
+ cli.StringFlag{
+ Name: "sign-by",
+ Usage: "Name of the signing key",
+ },
+ cli.StringFlag{
+ Name: "directory, d",
+ Usage: "Define an alternate directory to store signatures",
+ },
+ }
+
+ signDescription = "Create a signature file that can be used later to verify the image"
+ signCommand = cli.Command{
+ Name: "sign",
+ Usage: "Sign an image",
+ Description: signDescription,
+ Flags: sortFlags(signFlags),
+ Action: signCmd,
+ ArgsUsage: "IMAGE-NAME [IMAGE-NAME ...]",
+ OnUsageError: usageErrorHandler,
+ }
+)
+
+// SignatureStoreDir defines default directory to store signatures
+const SignatureStoreDir = "/var/lib/containers/sigstore"
+
+func signCmd(c *cli.Context) error {
+ args := c.Args()
+ if len(args) < 1 {
+ return errors.Errorf("at least one image name must be specified")
+ }
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not create runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ signby := c.String("sign-by")
+ if signby == "" {
+ return errors.Errorf("please provide an identity")
+ }
+
+ var sigStoreDir string
+ if c.IsSet("directory") {
+ sigStoreDir = c.String("directory")
+ if _, err := os.Stat(sigStoreDir); err != nil {
+ return errors.Wrapf(err, "invalid directory %s", sigStoreDir)
+ }
+ }
+
+ mech, err := signature.NewGPGSigningMechanism()
+ if err != nil {
+ return errors.Wrap(err, "error initializing GPG")
+ }
+ defer mech.Close()
+ if err := mech.SupportsSigning(); err != nil {
+ return errors.Wrap(err, "signing is not supported")
+ }
+
+ systemRegistriesDirPath := trust.RegistriesDirPath(runtime.SystemContext())
+ registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath)
+ if err != nil {
+ return errors.Wrapf(err, "error reading registry configuration")
+ }
+
+ for _, signimage := range args {
+ srcRef, err := alltransports.ParseImageName(signimage)
+ if err != nil {
+ return errors.Wrapf(err, "error parsing image name")
+ }
+ rawSource, err := srcRef.NewImageSource(getContext(), runtime.SystemContext())
+ if err != nil {
+ return errors.Wrapf(err, "error getting image source")
+ }
+ manifest, _, err := rawSource.GetManifest(getContext(), nil)
+ if err != nil {
+ return errors.Wrapf(err, "error getting manifest")
+ }
+ dockerReference := rawSource.Reference().DockerReference()
+ if dockerReference == nil {
+ return errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference()))
+ }
+
+ // create the signstore file
+ newImage, err := runtime.ImageRuntime().New(getContext(), signimage, runtime.GetConfig().SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{SignBy: signby}, false)
+ if err != nil {
+ return errors.Wrapf(err, "error pulling image %s", signimage)
+ }
+
+ registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs)
+ if registryInfo != nil {
+ if sigStoreDir == "" {
+ sigStoreDir = registryInfo.SigStoreStaging
+ if sigStoreDir == "" {
+ sigStoreDir = registryInfo.SigStore
+ }
+ }
+ sigStoreDir, err = isValidSigStoreDir(sigStoreDir)
+ if err != nil {
+ return errors.Wrapf(err, "invalid signature storage %s", sigStoreDir)
+ }
+ }
+ if sigStoreDir == "" {
+ sigStoreDir = SignatureStoreDir
+ }
+
+ repos, err := newImage.RepoDigests()
+ if err != nil {
+ return errors.Wrapf(err, "error calculating repo digests for %s", signimage)
+ }
+ if len(repos) == 0 {
+ logrus.Errorf("no repodigests associated with the image %s", signimage)
+ continue
+ }
+
+ // create signature
+ newSig, err := signature.SignDockerManifest(manifest, dockerReference.String(), mech, signby)
+ if err != nil {
+ return errors.Wrapf(err, "error creating new signature")
+ }
+
+ trimmedDigest := strings.TrimPrefix(repos[0], strings.Split(repos[0], "/")[0])
+ sigStoreDir = filepath.Join(sigStoreDir, strings.Replace(trimmedDigest, ":", "=", 1))
+ if err := os.MkdirAll(sigStoreDir, 0751); err != nil {
+ // The directory is allowed to exist
+ if !os.IsExist(err) {
+ logrus.Errorf("error creating directory %s: %s", sigStoreDir, err)
+ continue
+ }
+ }
+ sigFilename, err := getSigFilename(sigStoreDir)
+ if err != nil {
+ logrus.Errorf("error creating sigstore file: %v", err)
+ continue
+ }
+ err = ioutil.WriteFile(filepath.Join(sigStoreDir, sigFilename), newSig, 0644)
+ if err != nil {
+ logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String())
+ continue
+ }
+ }
+ return nil
+}
+
+func getSigFilename(sigStoreDirPath string) (string, error) {
+ sigFileSuffix := 1
+ sigFiles, err := ioutil.ReadDir(sigStoreDirPath)
+ if err != nil {
+ return "", err
+ }
+ sigFilenames := make(map[string]bool)
+ for _, file := range sigFiles {
+ sigFilenames[file.Name()] = true
+ }
+ for {
+ sigFilename := "signature-" + strconv.Itoa(sigFileSuffix)
+ if _, exists := sigFilenames[sigFilename]; !exists {
+ return sigFilename, nil
+ }
+ sigFileSuffix++
+ }
+}
+
+func isValidSigStoreDir(sigStoreDir string) (string, error) {
+ writeURIs := map[string]bool{"file": true}
+ url, err := url.Parse(sigStoreDir)
+ if err != nil {
+ return sigStoreDir, errors.Wrapf(err, "invalid directory %s", sigStoreDir)
+ }
+ _, exists := writeURIs[url.Scheme]
+ if !exists {
+ return sigStoreDir, errors.Errorf("writing to %s is not supported. Use a supported scheme", sigStoreDir)
+ }
+ sigStoreDir = url.Path
+ return sigStoreDir, nil
+}
diff --git a/cmd/podman/start.go b/cmd/podman/start.go
index 8bb386c68..df34deec2 100644
--- a/cmd/podman/start.go
+++ b/cmd/podman/start.go
@@ -27,9 +27,9 @@ var (
Name: "interactive, i",
Usage: "Keep STDIN open even if not attached",
},
- cli.BoolFlag{
+ cli.BoolTFlag{
Name: "sig-proxy",
- Usage: "proxy received signals to the process",
+ Usage: "proxy received signals to the process (default true if attaching, false otherwise)",
},
LatestFlag,
}
@@ -67,8 +67,14 @@ func startCmd(c *cli.Context) error {
return err
}
- if c.Bool("sig-proxy") && !attach {
- return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach")
+ sigProxy := c.BoolT("sig-proxy")
+
+ if sigProxy && !attach {
+ if c.IsSet("sig-proxy") {
+ return errors.Wrapf(libpod.ErrInvalidArg, "you cannot use sig-proxy without --attach")
+ } else {
+ sigProxy = false
+ }
}
runtime, err := libpodruntime.GetRuntime(c)
@@ -111,7 +117,7 @@ func startCmd(c *cli.Context) error {
}
// attach to the container and also start it not already running
- err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.String("detach-keys"), c.Bool("sig-proxy"), !ctrRunning)
+ err = startAttachCtr(ctr, os.Stdout, os.Stderr, inputStream, c.String("detach-keys"), sigProxy, !ctrRunning)
if ctrRunning {
return err
}
diff --git a/cmd/podman/tag.go b/cmd/podman/tag.go
index 29f66d41e..d19cf69a2 100644
--- a/cmd/podman/tag.go
+++ b/cmd/podman/tag.go
@@ -1,7 +1,7 @@
package main
import (
- "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod/adapter"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
@@ -23,13 +23,13 @@ func tagCmd(c *cli.Context) error {
if len(args) < 2 {
return errors.Errorf("image name and at least one new name must be specified")
}
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "could not create runtime")
}
defer runtime.Shutdown(false)
- newImage, err := runtime.ImageRuntime().NewFromLocal(args[0])
+ newImage, err := runtime.NewImageFromLocal(args[0])
if err != nil {
return err
}
diff --git a/cmd/podman/trust.go b/cmd/podman/trust.go
index 7c404cd3f..863f36d09 100644
--- a/cmd/podman/trust.go
+++ b/cmd/podman/trust.go
@@ -13,7 +13,6 @@ import (
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/trust"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -132,7 +131,7 @@ func showTrustCmd(c *cli.Context) error {
if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil {
return errors.Errorf("could not read trust policies")
}
- policyJSON, err := trust.GetPolicyJSON(policyContentStruct, systemRegistriesDirPath)
+ policyJSON, showOutputMap, err := trust.GetPolicy(policyContentStruct, systemRegistriesDirPath)
if err != nil {
return errors.Wrapf(err, "error reading registry config file")
}
@@ -144,31 +143,12 @@ func showTrustCmd(c *cli.Context) error {
}
sortedRepos := sortPolicyJSONKey(policyJSON)
- type policydefault struct {
- Repo string
- Trusttype string
- GPGid string
- Sigstore string
- }
- var policyoutput []policydefault
- for _, repo := range sortedRepos {
- repoval := policyJSON[repo]
- var defaultstruct policydefault
- defaultstruct.Repo = repo
- if repoval["type"] != nil {
- defaultstruct.Trusttype = trustTypeDescription(repoval["type"].(string))
- }
- if repoval["keys"] != nil && len(repoval["keys"].([]string)) > 0 {
- defaultstruct.GPGid = trust.GetGPGId(repoval["keys"].([]string))
- }
- if repoval["sigstore"] != nil {
- defaultstruct.Sigstore = repoval["sigstore"].(string)
- }
- policyoutput = append(policyoutput, defaultstruct)
- }
var output []interface{}
- for _, ele := range policyoutput {
- output = append(output, interface{}(ele))
+ for _, reponame := range sortedRepos {
+ showOutput, exists := showOutputMap[reponame]
+ if exists {
+ output = append(output, interface{}(showOutput))
+ }
}
out := formats.StdoutTemplateArray{Output: output, Template: "{{.Repo}}\t{{.Trusttype}}\t{{.GPGid}}\t{{.Sigstore}}"}
return formats.Writer(out).Out()
@@ -209,8 +189,10 @@ func setTrustCmd(c *cli.Context) error {
policyPath = trust.DefaultPolicyPath(runtime.SystemContext())
}
var policyContentStruct trust.PolicyContent
+ policyFileExists := false
_, err = os.Stat(policyPath)
if !os.IsNotExist(err) {
+ policyFileExists = true
policyContent, err := ioutil.ReadFile(policyPath)
if err != nil {
return errors.Wrapf(err, "unable to read %s", policyPath)
@@ -218,6 +200,9 @@ func setTrustCmd(c *cli.Context) error {
if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil {
return errors.Errorf("could not read trust policies")
}
+ if args[0] != "default" && len(policyContentStruct.Default) == 0 {
+ return errors.Errorf("Default trust policy must be set.")
+ }
}
var newReposContent []trust.RepoContent
if len(pubkeysfile) != 0 {
@@ -230,15 +215,18 @@ func setTrustCmd(c *cli.Context) error {
if args[0] == "default" {
policyContentStruct.Default = newReposContent
} else {
- exists := false
+ if policyFileExists == false && len(policyContentStruct.Default) == 0 {
+ return errors.Errorf("Default trust policy must be set to create the policy file.")
+ }
+ registryExists := false
for transport, transportval := range policyContentStruct.Transports {
- _, exists = transportval[args[0]]
- if exists {
+ _, registryExists = transportval[args[0]]
+ if registryExists {
policyContentStruct.Transports[transport][args[0]] = newReposContent
break
}
}
- if !exists {
+ if !registryExists {
if policyContentStruct.Transports == nil {
policyContentStruct.Transports = make(map[string]trust.RepoMap)
}
@@ -260,16 +248,6 @@ func setTrustCmd(c *cli.Context) error {
return nil
}
-var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "signedBy": "signed", "reject": "reject"}
-
-func trustTypeDescription(trustType string) string {
- trustDescription, exist := typeDescription[trustType]
- if !exist {
- logrus.Warnf("invalid trust type %s", trustType)
- }
- return trustDescription
-}
-
func sortPolicyJSONKey(m map[string]map[string]interface{}) []string {
keys := make([]string, len(m))
i := 0
diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go
index 24f0f178b..7c9b5897b 100644
--- a/cmd/podman/umount.go
+++ b/cmd/podman/umount.go
@@ -21,6 +21,7 @@ var (
Name: "force, f",
Usage: "force the complete umount all of the currently mounted containers",
},
+ LatestFlag,
}
description = `
@@ -51,59 +52,37 @@ func umountCmd(c *cli.Context) error {
force := c.Bool("force")
umountAll := c.Bool("all")
- args := c.Args()
- if len(args) == 0 && !umountAll {
- return errors.Errorf("container ID must be specified")
+ if err := checkAllAndLatest(c); err != nil {
+ return err
}
- if len(args) > 0 && umountAll {
- return errors.Errorf("when using the --all switch, you may not pass any container IDs")
+
+ containers, err := getAllOrLatestContainers(c, runtime, -1, "all")
+ if err != nil {
+ if len(containers) == 0 {
+ return err
+ }
+ fmt.Println(err.Error())
}
umountContainerErrStr := "error unmounting container"
var lastError error
- if len(args) > 0 {
- for _, name := range args {
- ctr, err := runtime.LookupContainer(name)
- if err != nil {
- if lastError != nil {
- logrus.Error(lastError)
- }
- lastError = errors.Wrapf(err, "%s %s", umountContainerErrStr, name)
- continue
- }
-
- if err = ctr.Unmount(force); err != nil {
- if lastError != nil {
- logrus.Error(lastError)
- }
- lastError = errors.Wrapf(err, "%s %s", umountContainerErrStr, name)
- continue
- }
- fmt.Printf("%s\n", ctr.ID())
- }
- } else {
- containers, err := runtime.GetContainers()
- if err != nil {
- return errors.Wrapf(err, "error reading Containers")
+ for _, ctr := range containers {
+ ctrState, err := ctr.State()
+ if ctrState == libpod.ContainerStateRunning || err != nil {
+ continue
}
- for _, ctr := range containers {
- ctrState, err := ctr.State()
- if ctrState == libpod.ContainerStateRunning || err != nil {
- continue
- }
- if err = ctr.Unmount(force); err != nil {
- if umountAll && errors.Cause(err) == storage.ErrLayerNotMounted {
- continue
- }
- if lastError != nil {
- logrus.Error(lastError)
- }
- lastError = errors.Wrapf(err, "%s %s", umountContainerErrStr, ctr.ID())
+ if err = ctr.Unmount(force); err != nil {
+ if umountAll && errors.Cause(err) == storage.ErrLayerNotMounted {
continue
}
- fmt.Printf("%s\n", ctr.ID())
+ if lastError != nil {
+ logrus.Error(lastError)
+ }
+ lastError = errors.Wrapf(err, "%s %s", umountContainerErrStr, ctr.ID())
+ continue
}
+ fmt.Printf("%s\n", ctr.ID())
}
return lastError
}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index c1b7c703a..86c3eb7ff 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -9,7 +9,8 @@ type Version (
go_version: string,
git_commit: string,
built: int,
- os_arch: string
+ os_arch: string,
+ remote_api_version: int
)
type NotImplemented (
@@ -37,7 +38,8 @@ type ImageInList (
size: int,
virtualSize: int,
containers: int,
- labels: [string]string
+ labels: [string]string,
+ isParent: bool
)
# ImageHistory describes the returned structure from ImageHistory.
@@ -60,7 +62,7 @@ type ImageSearch (
star_count: int
)
-# ListContainer is the returned struct for an individual container
+# ListContainerData is the returned struct for an individual container
type ListContainerData (
id: string,
image: string,
@@ -211,6 +213,8 @@ type Create (
hostname: string,
image: string,
image_id: string,
+ init: bool,
+ init_path: string,
builtin_imgvolumes: []string,
id_mappings: IDMappingOptions,
image_volume_type: string,
@@ -1013,6 +1017,10 @@ method MountContainer(name: string) -> (path: string)
# ~~~
method UnmountContainer(name: string, force: bool) -> ()
+# ImagesPrune removes all unused images from the local store. Upon successful pruning,
+# the IDs of the removed images are returned.
+method ImagesPrune() -> (pruned: []string)
+
# This function is not implemented yet.
method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
@@ -1028,6 +1036,22 @@ method GenerateKubeService() -> (notimplemented: NotImplemented)
# like that created by GenerateKube. See also [GenerateKube](GenerateKube).
method ReplayKube() -> (notimplemented: NotImplemented)
+# ContainerConfig returns a container's config in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerConfig(name: string) -> (config: string)
+
+# ContainerArtifacts returns a container's artifacts in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerArtifacts(name: string, artifactName: string) -> (config: string)
+
+# ContainerInspectData returns a container's inspect data in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerInspectData(name: string) -> (config: string)
+
+# ContainerStateData returns a container's state config in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerStateData(name: string) -> (config: string)
+
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
error ImageNotFound (name: string)
diff --git a/cmd/podman/version.go b/cmd/podman/version.go
index d81deb696..fd7f06b7c 100644
--- a/cmd/podman/version.go
+++ b/cmd/podman/version.go
@@ -2,6 +2,8 @@ package main
import (
"fmt"
+ "os"
+ "text/tabwriter"
"time"
"github.com/containers/libpod/cmd/podman/formats"
@@ -26,20 +28,22 @@ func versionCmd(c *cli.Context) error {
default:
out = formats.StdoutTemplate{Output: output, Template: versionOutputFormat}
}
- formats.Writer(out).Out()
- return nil
+ return formats.Writer(out).Out()
}
- fmt.Println("Version: ", output.Version)
- fmt.Println("Go Version: ", output.GoVersion)
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
+ defer w.Flush()
+ fmt.Fprintf(w, "Version:\t%s\n", output.Version)
+ fmt.Fprintf(w, "RemoteAPI Version:\t%d\n", output.RemoteAPIVersion)
+ fmt.Fprintf(w, "Go Version:\t%s\n", output.GoVersion)
if output.GitCommit != "" {
- fmt.Println("Git Commit: ", output.GitCommit)
+ fmt.Fprintf(w, "Git Commit:\t%s\n", output.GitCommit)
}
// Prints out the build time in readable format
if output.Built != 0 {
- fmt.Println("Built: ", time.Unix(output.Built, 0).Format(time.ANSIC))
+ fmt.Fprintf(w, "Built:\t%s\n", time.Unix(output.Built, 0).Format(time.ANSIC))
}
- fmt.Println("OS/Arch: ", output.OsArch)
+ fmt.Fprintf(w, "OS/Arch:\t%s\n", output.OsArch)
return nil
}