diff options
Diffstat (limited to 'cmd/podman')
59 files changed, 582 insertions, 467 deletions
diff --git a/cmd/podman/attach.go b/cmd/podman/attach.go index b78633ed6..7d32c57af 100644 --- a/cmd/podman/attach.go +++ b/cmd/podman/attach.go @@ -2,6 +2,7 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -31,12 +32,15 @@ func init() { attachCommand.SetHelpTemplate(HelpTemplate()) attachCommand.SetUsageTemplate(UsageTemplate()) flags := attachCommand.Flags() - flags.StringVar(&attachCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + flags.StringVar(&attachCommand.DetachKeys, "detach-keys", define.DefaultDetachKeys, "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + // Clear the default, the value specified in the config file should have the + // priority + attachCommand.DetachKeys = "" flags.BoolVar(&attachCommand.NoStdin, "no-stdin", false, "Do not attach STDIN. The default is false") flags.BoolVar(&attachCommand.SigProxy, "sig-proxy", true, "Proxy received signals to the process") flags.BoolVarP(&attachCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") markFlagHiddenForRemoteClient("latest", flags) - // TODO allow for passing of a new deatch keys + // TODO allow for passing of a new detach keys markFlagHiddenForRemoteClient("detach-keys", flags) } diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 896d5661a..fbf85fc97 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -155,6 +155,11 @@ func buildCmd(c *cliconfig.BuildValues) error { tags = tags[1:] } } + if c.BudResults.Authfile != "" { + if _, err := os.Stat(c.BudResults.Authfile); err != nil { + return errors.Wrapf(err, "error getting authfile %s", c.BudResults.Authfile) + } + } pullPolicy := imagebuildah.PullNever if c.Pull { @@ -238,6 +243,9 @@ func buildCmd(c *cliconfig.BuildValues) error { if contextDir == "" { return errors.Errorf("no context directory specified, and no containerfile specified") } + if !fileIsDir(contextDir) { + return errors.Errorf("context must be a directory: %v", contextDir) + } if len(containerfiles) == 0 { if checkIfFileExists(filepath.Join(contextDir, "Containerfile")) { containerfiles = append(containerfiles, filepath.Join(contextDir, "Containerfile")) diff --git a/cmd/podman/checkpoint.go b/cmd/podman/checkpoint.go index 22cdb1f39..07db519f8 100644 --- a/cmd/podman/checkpoint.go +++ b/cmd/podman/checkpoint.go @@ -26,7 +26,7 @@ var ( return checkpointCmd(&checkpointCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container checkpoint --keep ctrID podman container checkpoint --all diff --git a/cmd/podman/cleanup.go b/cmd/podman/cleanup.go index c00654162..a8bc0c116 100644 --- a/cmd/podman/cleanup.go +++ b/cmd/podman/cleanup.go @@ -27,7 +27,7 @@ var ( return cleanupCmd(&cleanupCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman container cleanup --latest podman container cleanup ctrID1 ctrID2 ctrID3 diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 58d67ddc1..e81756808 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -64,6 +64,7 @@ type ImagesValues struct { NoTrunc bool Quiet bool Sort string + History bool } type EventValues struct { @@ -127,6 +128,7 @@ type ExecValues struct { PodmanCommand DetachKeys string Env []string + EnvFile []string Privileged bool Interactive bool Tty bool @@ -175,12 +177,15 @@ type HistoryValues struct { } type PruneImagesValues struct { PodmanCommand - All bool + All bool + Force bool + Filter []string } type PruneContainersValues struct { PodmanCommand - Force bool + Force bool + Filter []string } type PodPruneValues struct { @@ -274,6 +279,7 @@ type NetworkCreateValues struct { IPRange net.IPNet IPV6 bool Network net.IPNet + MacVLAN string } type NetworkListValues struct { @@ -366,6 +372,7 @@ type PodRestartValues struct { type PodRmValues struct { PodmanCommand All bool + Ignore bool Force bool Latest bool } @@ -387,6 +394,7 @@ type PodStatsValues struct { type PodStopValues struct { PodmanCommand All bool + Ignore bool Latest bool Timeout uint } @@ -467,23 +475,26 @@ type RestartValues struct { type RestoreValues struct { PodmanCommand - All bool - Keep bool - Latest bool - TcpEstablished bool - Import string - Name string - IgnoreRootfs bool - IgnoreStaticIP bool + All bool + Keep bool + Latest bool + TcpEstablished bool + Import string + Name string + IgnoreRootfs bool + IgnoreStaticIP bool + IgnoreStaticMAC bool } type RmValues struct { PodmanCommand - All bool - Force bool - Latest bool - Storage bool - Volumes bool + All bool + Force bool + Ignore bool + Latest bool + Storage bool + Volumes bool + CIDFiles []string } type RmiValues struct { @@ -556,9 +567,11 @@ type StatsValues struct { type StopValues struct { PodmanCommand - All bool - Latest bool - Timeout uint + All bool + Ignore bool + Latest bool + Timeout uint + CIDFiles []string } type TopValues struct { @@ -648,6 +661,11 @@ type SystemPruneValues struct { Volume bool } +type SystemResetValues struct { + PodmanCommand + Force bool +} + type SystemRenumberValues struct { PodmanCommand } diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 33a848553..69365201e 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/containers/buildah" + buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/sysinfo" @@ -39,24 +39,45 @@ func shortID(id string) string { return id } -// checkAllAndLatest checks that --all and --latest are used correctly -func checkAllAndLatest(c *cobra.Command, args []string, ignoreArgLen bool) error { +// checkAllLatestAndCIDFile checks that --all and --latest are used correctly. +// If cidfile is set, also check for the --cidfile flag. +func checkAllLatestAndCIDFile(c *cobra.Command, args []string, ignoreArgLen bool, cidfile bool) error { argLen := len(args) if c.Flags().Lookup("all") == nil || c.Flags().Lookup("latest") == nil { - return errors.New("unable to lookup values for 'latest' or 'all'") + if !cidfile { + return errors.New("unable to lookup values for 'latest' or 'all'") + } else if c.Flags().Lookup("cidfile") == nil { + return errors.New("unable to lookup values for 'latest', 'all' or 'cidfile'") + } + } + + specifiedAll, _ := c.Flags().GetBool("all") + specifiedLatest, _ := c.Flags().GetBool("latest") + specifiedCIDFile := false + if cid, _ := c.Flags().GetStringArray("cidfile"); len(cid) > 0 { + specifiedCIDFile = true } - all, _ := c.Flags().GetBool("all") - latest, _ := c.Flags().GetBool("latest") - if all && latest { + + if specifiedCIDFile && (specifiedAll || specifiedLatest) { + return errors.Errorf("--all, --latest and --cidfile cannot be used together") + } else if specifiedAll && specifiedLatest { return errors.Errorf("--all and --latest cannot be used together") } + if ignoreArgLen { return nil } - if (all || latest) && argLen > 0 { + if (argLen > 0) && (specifiedAll || specifiedLatest) { return errors.Errorf("no arguments are needed with --all or --latest") + } else if cidfile && (argLen > 0) && (specifiedAll || specifiedLatest || specifiedCIDFile) { + return errors.Errorf("no arguments are needed with --all, --latest or --cidfile") } - if argLen < 1 && !all && !latest { + + if specifiedCIDFile { + return nil + } + + if argLen < 1 && !specifiedAll && !specifiedLatest && !specifiedCIDFile { return errors.Errorf("you must provide at least one name or id") } return nil @@ -112,7 +133,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "Attach to STDIN, STDOUT or STDERR (default [])", ) createFlags.String( - "authfile", shared.GetAuthFile(""), + "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", ) createFlags.String( @@ -132,7 +153,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "Drop capabilities from the container", ) createFlags.String( - "cgroupns", "host", + "cgroupns", "", "cgroup namespace to use", ) createFlags.String( @@ -187,10 +208,14 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "detach", "d", false, "Run container in background and print container ID", ) - createFlags.String( - "detach-keys", "", + detachKeys := createFlags.String( + "detach-keys", define.DefaultDetachKeys, "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`", ) + // Clear the default, the value specified in the config file should have the + // priority + *detachKeys = "" + createFlags.StringSlice( "device", []string{}, "Add a host device to the container (default [])", @@ -328,7 +353,7 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { ) createFlags.String( "mac-address", "", - "Container MAC address (e.g. 92:d0:c6:0a:29:33), not currently supported", + "Container MAC address (e.g. 92:d0:c6:0a:29:33)", ) createFlags.StringP( "memory", "m", "", diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go index 3d0fef37d..78c50268c 100644 --- a/cmd/podman/containers_prune.go +++ b/cmd/podman/containers_prune.go @@ -1,6 +1,11 @@ package main import ( + "bufio" + "fmt" + "os" + "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/define" @@ -36,9 +41,23 @@ func init() { pruneContainersCommand.SetUsageTemplate(UsageTemplate()) flags := pruneContainersCommand.Flags() flags.BoolVarP(&pruneContainersCommand.Force, "force", "f", false, "Force removal of a running container. The default is false") + flags.StringArrayVar(&pruneContainersCommand.Filter, "filter", []string{}, "Provide filter values (e.g. 'until=<timestamp>')") } func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { + if !c.Force { + reader := bufio.NewReader(os.Stdin) + fmt.Printf(`WARNING! This will remove all stopped containers. +Are you sure you want to continue? [y/N] `) + ans, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(ans)[0] != 'y' { + return nil + } + } + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") @@ -49,7 +68,7 @@ func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { if c.GlobalIsSet("max-workers") { maxWorkers = c.GlobalFlags.MaxWorks } - ok, failures, err := runtime.Prune(getContext(), maxWorkers, c.Force) + ok, failures, err := runtime.Prune(getContext(), maxWorkers, c.Force, c.Filter) if err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { if len(c.InputArgs) > 1 { diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go index c53a97df3..762d70252 100644 --- a/cmd/podman/cp.go +++ b/cmd/podman/cp.go @@ -192,7 +192,7 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin } else if isBindMount, mount := isBindMountDestName(srcPath, ctr); isBindMount { path, err := pathWithBindMountSource(mount, srcPath) if err != nil { - return errors.Wrapf(err, "error getting source path from bind moutn %s", mount.Destination) + return errors.Wrapf(err, "error getting source path from bind mount %s", mount.Destination) } srcPath = path } else if filepath.IsAbs(srcPath) { diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 3c24729c5..73fba5a8c 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "os" "strings" "github.com/containers/libpod/cmd/podman/cliconfig" @@ -50,6 +51,12 @@ func createCmd(c *cliconfig.CreateValues) error { defer span.Finish() } + if c.String("authfile") != "" { + if _, err := os.Stat(c.String("authfile")); err != nil { + return errors.Wrapf(err, "error getting authfile %s", c.String("authfile")) + } + } + if err := createInit(&c.PodmanCommand); err != nil { return err } diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index 649a7b0db..6e5799396 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -2,6 +2,7 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -34,8 +35,12 @@ func init() { execCommand.SetUsageTemplate(UsageTemplate()) flags := execCommand.Flags() flags.SetInterspersed(false) - flags.StringVar(&execCommand.DetachKeys, "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 _") + flags.StringVar(&execCommand.DetachKeys, "detach-keys", define.DefaultDetachKeys, "Select 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 _") + // Clear the default, the value specified in the config file should have the + // priority + execCommand.DetachKeys = "" flags.StringArrayVarP(&execCommand.Env, "env", "e", []string{}, "Set environment variables") + flags.StringSliceVar(&execCommand.EnvFile, "env-file", []string{}, "Read in a file of environment variables") flags.BoolVarP(&execCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVarP(&execCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&execCommand.Privileged, "privileged", false, "Give the process extended Linux capabilities inside the container. The default is false") @@ -44,6 +49,7 @@ func init() { flags.IntVar(&execCommand.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container") flags.StringVarP(&execCommand.Workdir, "workdir", "w", "", "Working directory inside the container") + markFlagHiddenForRemoteClient("env-file", flags) markFlagHiddenForRemoteClient("latest", flags) markFlagHiddenForRemoteClient("preserve-fds", flags) } diff --git a/cmd/podman/imagefilters/filters.go b/cmd/podman/imagefilters/filters.go deleted file mode 100644 index 0b08314ce..000000000 --- a/cmd/podman/imagefilters/filters.go +++ /dev/null @@ -1,121 +0,0 @@ -package imagefilters - -import ( - "context" - "fmt" - "path/filepath" - "strings" - "time" - - "github.com/containers/libpod/pkg/adapter" - "github.com/containers/libpod/pkg/inspect" - "github.com/sirupsen/logrus" -) - -// 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(danglingImages bool) ResultFilter { - return func(i *adapter.ContainerImage) bool { - if danglingImages { - return i.Dangling() - } - return !i.Dangling() - } -} - -// ReadOnlyFilter allows you to filter images based on read/only and read/write -func ReadOnlyFilter(readOnly bool) ResultFilter { - return func(i *adapter.ContainerImage) bool { - if readOnly { - return i.IsReadOnly() - } - return !i.IsReadOnly() - } -} - -// 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 - } -} - -// ReferenceFilter allows you to filter by image name -// Replacing all '/' with '|' so that filepath.Match() can work -// '|' character is not valid in image name, so this is safe -func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter { - filter := fmt.Sprintf("*%s*", referenceFilter) - filter = strings.Replace(filter, "/", "|", -1) - return func(i *adapter.ContainerImage) bool { - for _, name := range i.Names() { - newName := strings.Replace(name, "/", "|", -1) - match, err := filepath.Match(filter, newName) - if err != nil { - logrus.Errorf("failed to match %s and %s, %q", name, referenceFilter, err) - } - if match { - return true - } - } - return false - } -} - -// 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 6157fda2a..e42546a55 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -5,14 +5,12 @@ import ( "fmt" "reflect" "sort" - "strconv" "strings" "time" "unicode" "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/imagefilters" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" "github.com/docker/go-units" @@ -32,6 +30,7 @@ type imagesTemplateParams struct { CreatedTime time.Time Size string ReadOnly bool + History string } type imagesJSONParams struct { @@ -42,6 +41,7 @@ type imagesJSONParams struct { Created time.Time `json:"created"` Size *uint64 `json:"size"` ReadOnly bool `json:"readonly"` + History []string `json:"history"` } type imagesOptions struct { @@ -53,6 +53,7 @@ type imagesOptions struct { outputformat string sort string all bool + history bool } // Type declaration and functions for sorting the images output @@ -124,6 +125,7 @@ func imagesInit(command *cliconfig.ImagesValues) { flags.BoolVar(&command.NoTrunc, "no-trunc", false, "Do not truncate output") flags.BoolVarP(&command.Quiet, "quiet", "q", false, "Display only image IDs") flags.StringVar(&command.Sort, "sort", "created", "Sort by created, id, repository, size, or tag") + flags.BoolVarP(&command.History, "history", "", false, "Display the image name history") } @@ -134,10 +136,10 @@ func init() { func imagesCmd(c *cliconfig.ImagesValues) error { var ( - filterFuncs []imagefilters.ResultFilter - image string + image string ) + ctx := getContext() runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "Could not get runtime") @@ -152,15 +154,9 @@ func imagesCmd(c *cliconfig.ImagesValues) error { if len(c.Filter) > 0 && image != "" { return errors.New("can not specify an image and a filter") } - ctx := getContext() - - if len(c.Filter) > 0 { - filterFuncs, err = CreateFilterFuncs(ctx, runtime, c.Filter, nil) - } else { - filterFuncs, err = CreateFilterFuncs(ctx, runtime, []string{fmt.Sprintf("reference=%s", image)}, nil) - } - if err != nil { - return err + filters := c.Filter + if len(filters) < 1 { + filters = append(filters, fmt.Sprintf("reference=%s", image)) } opts := imagesOptions{ @@ -171,29 +167,21 @@ func imagesCmd(c *cliconfig.ImagesValues) error { format: c.Format, sort: c.Sort, all: c.All, + history: c.History, } opts.outputformat = opts.setOutputFormat() - images, err := runtime.GetImages() + filteredImages, err := runtime.GetFilteredImages(filters, false) if err != nil { return errors.Wrapf(err, "unable to get images") } - for _, image := range images { + for _, image := range filteredImages { if image.IsReadOnly() { opts.outputformat += "{{.ReadOnly}}\t" break } } - - var filteredImages []*adapter.ContainerImage - //filter the images - if len(c.Filter) > 0 || len(c.InputArgs) == 1 { - filteredImages = imagefilters.FilterImages(images, filterFuncs) - } else { - filteredImages = images - } - return generateImagesOutput(ctx, filteredImages, opts) } @@ -214,6 +202,9 @@ func (i imagesOptions) setOutputFormat() string { format += "{{.Digest}}\t" } format += "{{.ID}}\t{{.Created}}\t{{.Size}}\t" + if i.history { + format += "{{if .History}}{{.History}}{{else}}<none>{{end}}\t" + } return format } @@ -291,6 +282,10 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma if len(tag) == 71 && strings.HasPrefix(tag, "sha256:") { imageDigest = digest.Digest(tag) tag = "" + } else { + if img.Digest() != "" { + imageDigest = img.Digest() + } } params := imagesTemplateParams{ Repository: repo, @@ -302,6 +297,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma Created: units.HumanDuration(time.Since(createdTime)) + " ago", Size: sizeStr, ReadOnly: img.IsReadOnly(), + History: strings.Join(img.NamesHistory(), ", "), } imagesOutput = append(imagesOutput, params) if opts.quiet { // Show only one image ID when quiet @@ -332,6 +328,7 @@ func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) Created: img.Created(), Size: size, ReadOnly: img.IsReadOnly(), + History: img.NamesHistory(), } imagesOutput = append(imagesOutput, params) } @@ -378,53 +375,3 @@ func GenImageOutputMap() map[string]string { } return values } - -// 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 *adapter.LocalRuntime, filters []string, img *adapter.ContainerImage) ([]imagefilters.ResultFilter, error) { - var filterFuncs []imagefilters.ResultFilter - for _, filter := range filters { - splitFilter := strings.Split(filter, "=") - if len(splitFilter) < 2 { - return nil, errors.Errorf("invalid filter syntax %s", filter) - } - switch splitFilter[0] { - case "before": - 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, imagefilters.CreatedBeforeFilter(before.Created())) - case "after": - 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, imagefilters.CreatedAfterFilter(after.Created())) - case "readonly": - readonly, err := strconv.ParseBool(splitFilter[1]) - if err != nil { - return nil, errors.Wrapf(err, "invalid filter readonly=%s", splitFilter[1]) - } - filterFuncs = append(filterFuncs, imagefilters.ReadOnlyFilter(readonly)) - case "dangling": - danglingImages, err := strconv.ParseBool(splitFilter[1]) - if err != nil { - return nil, errors.Wrapf(err, "invalid filter dangling=%s", splitFilter[1]) - } - filterFuncs = append(filterFuncs, imagefilters.DanglingFilter(danglingImages)) - case "label": - labelFilter := strings.Join(splitFilter[1:], "=") - filterFuncs = append(filterFuncs, imagefilters.LabelFilter(ctx, labelFilter)) - case "reference": - referenceFilter := strings.Join(splitFilter[1:], "=") - filterFuncs = append(filterFuncs, imagefilters.ReferenceFilter(ctx, referenceFilter)) - default: - return nil, errors.Errorf("invalid filter %s ", splitFilter[0]) - } - } - if img != nil { - filterFuncs = append(filterFuncs, imagefilters.OutputImageFilter(img)) - } - return filterFuncs, nil -} diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go index 5745edd6b..2b498f83d 100644 --- a/cmd/podman/images_prune.go +++ b/cmd/podman/images_prune.go @@ -1,7 +1,10 @@ package main import ( + "bufio" "fmt" + "os" + "strings" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" @@ -34,9 +37,24 @@ func init() { pruneImagesCommand.SetUsageTemplate(UsageTemplate()) flags := pruneImagesCommand.Flags() flags.BoolVarP(&pruneImagesCommand.All, "all", "a", false, "Remove all unused images, not just dangling ones") + flags.BoolVarP(&pruneImagesCommand.Force, "force", "f", false, "Do not prompt for confirmation") + flags.StringArrayVar(&pruneImagesCommand.Filter, "filter", []string{}, "Provide filter values (e.g. 'label=<key>=<value>')") } func pruneImagesCmd(c *cliconfig.PruneImagesValues) error { + if !c.Force { + reader := bufio.NewReader(os.Stdin) + fmt.Printf(` +WARNING! This will remove all dangling images. +Are you sure you want to continue? [y/N] `) + ans, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(ans)[0] != 'y' { + return nil + } + } runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") @@ -45,7 +63,7 @@ func pruneImagesCmd(c *cliconfig.PruneImagesValues) error { // Call prune; if any cids are returned, print them and then // return err in case an error also came up - pruneCids, err := runtime.PruneImages(getContext(), c.All) + pruneCids, err := runtime.PruneImages(getContext(), c.All, c.Filter) if len(pruneCids) > 0 { for _, cid := range pruneCids { fmt.Println(cid) diff --git a/cmd/podman/info.go b/cmd/podman/info.go index bf6dd4a8f..7361525ce 100644 --- a/cmd/podman/info.go +++ b/cmd/podman/info.go @@ -3,6 +3,7 @@ package main import ( "fmt" rt "runtime" + "strings" "github.com/containers/buildah/pkg/formats" "github.com/containers/libpod/cmd/podman/cliconfig" @@ -88,6 +89,9 @@ func infoCmd(c *cliconfig.InfoValues) error { var out formats.Writer infoOutputFormat := c.Format + if strings.Join(strings.Fields(infoOutputFormat), "") == "{{json.}}" { + infoOutputFormat = formats.JSONString + } switch infoOutputFormat { case formats.JSONString: out = formats.JSONStruct{Output: info} diff --git a/cmd/podman/init.go b/cmd/podman/init.go index 3f97824fc..2e0b33828 100644 --- a/cmd/podman/init.go +++ b/cmd/podman/init.go @@ -23,7 +23,7 @@ var ( return initCmd(&initCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman init --latest podman init 3c45ef19d893 diff --git a/cmd/podman/kill.go b/cmd/podman/kill.go index d5056d86d..aba2008ca 100644 --- a/cmd/podman/kill.go +++ b/cmd/podman/kill.go @@ -24,7 +24,7 @@ var ( return killCmd(&killCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman kill mywebserver podman kill 860a4b23 diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index dd8c3f173..9425cfb9c 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -157,7 +157,7 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migra // TODO flag to set CNI plugins dir? - // TODO I dont think these belong here? + // TODO I don't think these belong here? // Will follow up with a different PR to address // // Pod create options diff --git a/cmd/podman/login.go b/cmd/podman/login.go index f91366eac..369e0da16 100644 --- a/cmd/podman/login.go +++ b/cmd/podman/login.go @@ -6,11 +6,11 @@ import ( "os" "strings" + buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/image/v5/docker" "github.com/containers/image/v5/pkg/docker/config" "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/docker/docker-credential-helpers/credentials" "github.com/pkg/errors" @@ -54,7 +54,7 @@ func init() { flags.BoolVar(&loginCommand.StdinPassword, "password-stdin", false, "Take the password from stdin") // Disabled flags for the remote client if !remote { - flags.StringVar(&loginCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&loginCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&loginCommand.CertDir, "cert-dir", "", "Pathname of a directory containing TLS certificates and keys used to connect to the registry") flags.BoolVar(&loginCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } diff --git a/cmd/podman/logout.go b/cmd/podman/logout.go index ef3452afe..4a113b1d0 100644 --- a/cmd/podman/logout.go +++ b/cmd/podman/logout.go @@ -3,11 +3,11 @@ package main import ( "fmt" + buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/image/v5/docker" "github.com/containers/image/v5/pkg/docker/config" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" - "github.com/containers/libpod/libpod/image" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -40,7 +40,7 @@ func init() { logoutCommand.SetUsageTemplate(UsageTemplate()) flags := logoutCommand.Flags() flags.BoolVarP(&logoutCommand.All, "all", "a", false, "Remove the cached credentials for all registries in the auth file") - flags.StringVar(&logoutCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&logoutCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") markFlagHiddenForRemoteClient("authfile", flags) } @@ -59,7 +59,10 @@ func logoutCmd(c *cliconfig.LogoutValues) error { server = scrubServer(args[0]) } - sc := image.GetSystemContext("", c.Authfile, false) + sc, err := shared.GetSystemContext(c.Authfile) + if err != nil { + return err + } if c.All { if err := config.RemoveAllAuthentication(sc); err != nil { @@ -69,7 +72,7 @@ func logoutCmd(c *cliconfig.LogoutValues) error { return nil } - err := config.RemoveAuthentication(sc, server) + err = config.RemoveAuthentication(sc, server) switch errors.Cause(err) { case nil: fmt.Printf("Removed login credentials for %s\n", server) diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 6057eeec3..bc46e4652 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -16,7 +16,6 @@ import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" - "github.com/containers/libpod/libpod/config" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/cgroups" "github.com/containers/libpod/pkg/rootless" @@ -34,9 +33,6 @@ const remote = false func init() { cgroupManager := define.SystemdCgroupsManager - if runtimeConfig, err := config.NewConfig(""); err == nil { - cgroupManager = runtimeConfig.CgroupManager - } cgroupHelp := "Cgroup manager to use (cgroupfs or systemd)" cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() if rootless.IsRootless() && !cgroupv2 { @@ -68,12 +64,12 @@ func init() { rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Root, "root", "", "Path to the root directory in which data, including images, is stored") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored") rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.Runtime, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc") - // -s is depracated due to conflict with -s on subcommands + // -s is deprecated due to conflict with -s on subcommands rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") rootCmd.PersistentFlags().StringArrayVar(&MainGlobalOpts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver") rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Syslog, "syslog", false, "Output logging information to syslog as well as the console") - rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory") + rootCmd.PersistentFlags().StringVar(&MainGlobalOpts.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n") rootCmd.PersistentFlags().BoolVar(&MainGlobalOpts.Trace, "trace", false, "Enable opentracing output") } @@ -163,7 +159,7 @@ func setupRootless(cmd *cobra.Command, args []string) error { Remote: remoteclient, } - runtime, err := libpodruntime.GetRuntime(getContext(), &podmanCmd) + runtime, err := libpodruntime.GetRuntimeNoStore(getContext(), &podmanCmd) if err != nil { return errors.Wrapf(err, "could not get runtime") } diff --git a/cmd/podman/mount.go b/cmd/podman/mount.go index b14827592..526a236fd 100644 --- a/cmd/podman/mount.go +++ b/cmd/podman/mount.go @@ -35,7 +35,7 @@ var ( return mountCmd(&mountCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, true) + return checkAllLatestAndCIDFile(cmd, args, true, false) }, } ) diff --git a/cmd/podman/network_create.go b/cmd/podman/network_create.go index 6710883ae..886607885 100644 --- a/cmd/podman/network_create.go +++ b/cmd/podman/network_create.go @@ -41,6 +41,7 @@ func init() { flags.IPVar(&networkCreateCommand.Gateway, "gateway", nil, "IPv4 or IPv6 gateway for the subnet") flags.BoolVar(&networkCreateCommand.Internal, "internal", false, "restrict external access from this network") flags.IPNetVar(&networkCreateCommand.IPRange, "ip-range", net.IPNet{}, "allocate container IP from range") + flags.StringVar(&networkCreateCommand.MacVLAN, "macvlan", "", "create a Macvlan connection based on this device") // TODO not supported yet //flags.StringVar(&networkCreateCommand.IPamDriver, "ipam-driver", "", "IP Address Management Driver") // TODO enable when IPv6 is working @@ -50,6 +51,10 @@ func init() { } func networkcreateCmd(c *cliconfig.NetworkCreateValues) error { + var ( + fileName string + err error + ) if err := network.IsSupportedDriver(c.Driver); err != nil { return err } @@ -66,7 +71,11 @@ func networkcreateCmd(c *cliconfig.NetworkCreateValues) error { if err != nil { return err } - fileName, err := runtime.NetworkCreate(c) + if len(c.MacVLAN) > 0 { + fileName, err = runtime.NetworkCreateMacVLAN(c) + } else { + fileName, err = runtime.NetworkCreateBridge(c) + } if err == nil { fmt.Println(fileName) } diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go index 9a5cc3ec1..fc9f2d5b6 100644 --- a/cmd/podman/play_kube.go +++ b/cmd/podman/play_kube.go @@ -2,8 +2,10 @@ package main import ( "fmt" + "os" + + buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/pkg/adapter" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -40,7 +42,7 @@ func init() { flags.BoolVarP(&playKubeCommand.Quiet, "quiet", "q", false, "Suppress output information when pulling images") // Disabled flags for the remote client if !remote { - flags.StringVar(&playKubeCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&playKubeCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&playKubeCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&playKubeCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&playKubeCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") @@ -57,6 +59,12 @@ func playKubeCmd(c *cliconfig.KubePlayValues) error { return errors.New("you must supply at least one file") } + if c.Authfile != "" { + if _, err := os.Stat(c.Authfile); err != nil { + return errors.Wrapf(err, "error getting authfile %s", c.Authfile) + } + } + ctx := getContext() runtime, err := adapter.GetRuntime(ctx, &c.PodmanCommand) if err != nil { diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go index 9bda77471..064946f72 100644 --- a/cmd/podman/pod_kill.go +++ b/cmd/podman/pod_kill.go @@ -28,7 +28,7 @@ var ( return podKillCmd(&podKillCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod kill podID podman pod kill --signal TERM mywebserver diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go index 45e1319ff..320072919 100644 --- a/cmd/podman/pod_pause.go +++ b/cmd/podman/pod_pause.go @@ -25,7 +25,7 @@ var ( return podPauseCmd(&podPauseCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod pause podID1 podID2 podman pod pause --latest diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go index cc090bd6e..cb9f3770f 100644 --- a/cmd/podman/pod_restart.go +++ b/cmd/podman/pod_restart.go @@ -26,7 +26,7 @@ var ( return podRestartCmd(&podRestartCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod restart podID1 podID2 podman pod restart --latest diff --git a/cmd/podman/pod_rm.go b/cmd/podman/pod_rm.go index 82d0eb977..02daf8764 100644 --- a/cmd/podman/pod_rm.go +++ b/cmd/podman/pod_rm.go @@ -12,7 +12,7 @@ import ( var ( podRmCommand cliconfig.PodRmValues - podRmDescription = fmt.Sprintf(`podman rm will remove one or more pods from the host. + podRmDescription = fmt.Sprintf(`podman rm will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. A pod with containers will not be removed without --force. If --force is specified, all containers will be stopped, then removed.`) _podRmCommand = &cobra.Command{ @@ -26,7 +26,7 @@ var ( return podRmCmd(&podRmCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod rm mywebserverpod podman pod rm -f 860a4b23 @@ -41,7 +41,9 @@ func init() { flags := podRmCommand.Flags() flags.BoolVarP(&podRmCommand.All, "all", "a", false, "Remove all running pods") flags.BoolVarP(&podRmCommand.Force, "force", "f", false, "Force removal of a running pod by first stopping all containers, then removing all containers in the pod. The default is false") + flags.BoolVarP(&podRmCommand.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") flags.BoolVarP(&podRmCommand.Latest, "latest", "l", false, "Remove the latest pod podman is aware of") + markFlagHiddenForRemoteClient("ignore", flags) markFlagHiddenForRemoteClient("latest", flags) } diff --git a/cmd/podman/pod_start.go b/cmd/podman/pod_start.go index 64c951b43..aa2e09e98 100644 --- a/cmd/podman/pod_start.go +++ b/cmd/podman/pod_start.go @@ -26,7 +26,7 @@ var ( return podStartCmd(&podStartCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod start podID podman pod start --latest diff --git a/cmd/podman/pod_stop.go b/cmd/podman/pod_stop.go index edda99550..7d3951ec4 100644 --- a/cmd/podman/pod_stop.go +++ b/cmd/podman/pod_stop.go @@ -27,7 +27,7 @@ var ( return podStopCmd(&podStopCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod stop mywebserverpod podman pod stop --latest @@ -41,8 +41,10 @@ func init() { podStopCommand.SetUsageTemplate(UsageTemplate()) flags := podStopCommand.Flags() flags.BoolVarP(&podStopCommand.All, "all", "a", false, "Stop all running pods") + flags.BoolVarP(&podStopCommand.Ignore, "ignore", "i", false, "Ignore errors when a specified pod is missing") flags.BoolVarP(&podStopCommand.Latest, "latest", "l", false, "Stop the latest pod podman is aware of") flags.UintVarP(&podStopCommand.Timeout, "timeout", "t", 0, "Seconds to wait for pod stop before killing the container") + markFlagHiddenForRemoteClient("ignore", flags) markFlagHiddenForRemoteClient("latest", flags) } diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go index 833434c3f..1f80a7c79 100644 --- a/cmd/podman/pod_unpause.go +++ b/cmd/podman/pod_unpause.go @@ -26,7 +26,7 @@ var ( return podUnpauseCmd(&podUnpauseCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman pod unpause podID1 podID2 podman pod unpause --all diff --git a/cmd/podman/pods_prune.go b/cmd/podman/pods_prune.go index d40e37bdb..1c5c0bb58 100644 --- a/cmd/podman/pods_prune.go +++ b/cmd/podman/pods_prune.go @@ -17,7 +17,7 @@ var ( _prunePodsCommand = &cobra.Command{ Use: "prune", Args: noSubArgs, - Short: "Remove all stopped pods", + Short: "Remove all stopped pods and their containers", Long: podPruneDescription, RunE: func(cmd *cobra.Command, args []string) error { podPruneCommand.InputArgs = args @@ -32,7 +32,7 @@ func init() { podPruneCommand.SetHelpTemplate(HelpTemplate()) podPruneCommand.SetUsageTemplate(UsageTemplate()) flags := podPruneCommand.Flags() - flags.BoolVarP(&podPruneCommand.Force, "force", "f", false, "Force removal of a running pods. The default is false") + flags.BoolVarP(&podPruneCommand.Force, "force", "f", false, "Force removal of all running pods. The default is false") } func podPruneCmd(c *cliconfig.PodPruneValues) error { diff --git a/cmd/podman/port.go b/cmd/podman/port.go index 4e1f9642c..eef3d4b1d 100644 --- a/cmd/podman/port.go +++ b/cmd/podman/port.go @@ -26,7 +26,7 @@ var ( return portCmd(&portCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, true) + return checkAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman port --all podman port ctrID 80/tcp diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index d64793147..c6baf6b61 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -6,12 +6,12 @@ import ( "os" "strings" + buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/image/v5/docker" dockerarchive "github.com/containers/image/v5/docker/archive" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" @@ -60,7 +60,7 @@ func init() { markFlagHidden(flags, "override-os") // Disabled flags for the remote client if !remote { - flags.StringVar(&pullCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pullCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pullCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&pullCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&pullCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") @@ -96,6 +96,12 @@ func pullCmd(c *cliconfig.PullValues) (retError error) { return errors.Errorf("too many arguments. Requires exactly 1") } + if c.Authfile != "" { + if _, err := os.Stat(c.Authfile); err != nil { + return errors.Wrapf(err, "error getting authfile %s", c.Authfile) + } + } + arr := strings.SplitN(args[0], ":", 2) if len(arr) == 2 { if c.Bool("all-tags") { diff --git a/cmd/podman/push.go b/cmd/podman/push.go index 0fdfb6202..1be8dfe11 100644 --- a/cmd/podman/push.go +++ b/cmd/podman/push.go @@ -6,11 +6,11 @@ import ( "os" "strings" + buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/image/v5/directory" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" @@ -59,7 +59,7 @@ func init() { // Disabled flags for the remote client if !remote { - flags.StringVar(&pushCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&pushCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&pushCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.BoolVar(&pushCommand.Compress, "compress", false, "Compress tarball image layers when pushing to a directory using the 'dir' transport. (default is same compression type as source)") flags.StringVar(&pushCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") @@ -74,6 +74,12 @@ func pushCmd(c *cliconfig.PushValues) error { destName string ) + if c.Authfile != "" { + if _, err := os.Stat(c.Authfile); err != nil { + return errors.Wrapf(err, "error getting authfile %s", c.Authfile) + } + } + args := c.InputArgs if len(args) == 0 || len(args) > 2 { return errors.New("podman push requires at least one image name, and optionally a second to specify a different destination name") diff --git a/cmd/podman/remoteclientconfig/configfile_test.go b/cmd/podman/remoteclientconfig/configfile_test.go index 0bcac29a8..1710ee83f 100644 --- a/cmd/podman/remoteclientconfig/configfile_test.go +++ b/cmd/podman/remoteclientconfig/configfile_test.go @@ -88,7 +88,7 @@ func TestReadRemoteConfig(t *testing.T) { {"good", args{reader: strings.NewReader(goodConfig)}, makeGoodResult(), false}, // a connection with no destination is an error {"nodest", args{reader: strings.NewReader(noDest)}, nil, true}, - // a connnection with no user is OK + // a connection with no user is OK {"nouser", args{reader: strings.NewReader(noUser)}, makeNoUserResult(), false}, } for _, tt := range tests { diff --git a/cmd/podman/reset.go b/cmd/podman/reset.go new file mode 100644 index 000000000..9d16dc978 --- /dev/null +++ b/cmd/podman/reset.go @@ -0,0 +1,71 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/pkg/adapter" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + systemResetCommand cliconfig.SystemResetValues + systemResetDescription = `Reset podman storage back to default state" + + All containers will be stopped and removed, and all images, volumes and container content will be removed. +` + _systemResetCommand = &cobra.Command{ + Use: "reset", + Args: noSubArgs, + Short: "Reset podman storage", + Long: systemResetDescription, + RunE: func(cmd *cobra.Command, args []string) error { + systemResetCommand.InputArgs = args + systemResetCommand.GlobalFlags = MainGlobalOpts + systemResetCommand.Remote = remoteclient + return systemResetCmd(&systemResetCommand) + }, + } +) + +func init() { + systemResetCommand.Command = _systemResetCommand + flags := systemResetCommand.Flags() + flags.BoolVarP(&systemResetCommand.Force, "force", "f", false, "Do not prompt for confirmation") + + systemResetCommand.SetHelpTemplate(HelpTemplate()) + systemResetCommand.SetUsageTemplate(UsageTemplate()) +} + +func systemResetCmd(c *cliconfig.SystemResetValues) error { + // Prompt for confirmation if --force is not set + if !c.Force { + reader := bufio.NewReader(os.Stdin) + fmt.Print(` +WARNING! This will remove: + - all containers + - all pods + - all images + - all build cache +Are you sure you want to continue? [y/N] `) + ans, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(ans)[0] != 'y' { + return nil + } + } + + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "error creating libpod runtime") + } + // No shutdown, since storage will be destroyed when command completes + + return runtime.Reset() +} diff --git a/cmd/podman/restart.go b/cmd/podman/restart.go index c97fb0dc1..996a9f7ce 100644 --- a/cmd/podman/restart.go +++ b/cmd/podman/restart.go @@ -23,7 +23,7 @@ var ( return restartCmd(&restartCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman restart ctrID podman restart --latest diff --git a/cmd/podman/restore.go b/cmd/podman/restore.go index 90d0b2dc4..a8db7fa94 100644 --- a/cmd/podman/restore.go +++ b/cmd/podman/restore.go @@ -26,7 +26,7 @@ var ( return restoreCmd(&restoreCommand, cmd) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, true) + return checkAllLatestAndCIDFile(cmd, args, true, false) }, Example: `podman container restore ctrID podman container restore --latest @@ -47,6 +47,7 @@ func init() { flags.StringVarP(&restoreCommand.Name, "name", "n", "", "Specify new name for container restored from exported checkpoint (only works with --import)") flags.BoolVar(&restoreCommand.IgnoreRootfs, "ignore-rootfs", false, "Do not apply root file-system changes when importing from exported checkpoint") flags.BoolVar(&restoreCommand.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip") + flags.BoolVar(&restoreCommand.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address") markFlagHiddenForRemoteClient("latest", flags) } diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 89062f524..e69565e95 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -25,7 +25,7 @@ var ( return rmCmd(&rmCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman rm imageID podman rm mywebserver myflaskserver 860a4b23 @@ -40,12 +40,16 @@ func init() { rmCommand.SetUsageTemplate(UsageTemplate()) flags := rmCommand.Flags() flags.BoolVarP(&rmCommand.All, "all", "a", false, "Remove all containers") + flags.BoolVarP(&rmCommand.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") flags.BoolVarP(&rmCommand.Force, "force", "f", false, "Force removal of a running or unusable container. The default is false") flags.BoolVarP(&rmCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&rmCommand.Storage, "storage", false, "Remove container from storage library") - flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove the volumes associated with the container") - markFlagHiddenForRemoteClient("storage", flags) + flags.BoolVarP(&rmCommand.Volumes, "volumes", "v", false, "Remove anonymous volumes associated with the container") + flags.StringArrayVarP(&rmCommand.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") + markFlagHiddenForRemoteClient("ignore", flags) + markFlagHiddenForRemoteClient("cidfile", flags) markFlagHiddenForRemoteClient("latest", flags) + markFlagHiddenForRemoteClient("storage", flags) } // rmCmd removes one or more containers @@ -56,10 +60,10 @@ func rmCmd(c *cliconfig.RmValues) error { } defer runtime.DeferredShutdown(false) - // Storage conflicts with --all/--latest/--volumes + // Storage conflicts with --all/--latest/--volumes/--cidfile/--ignore if c.Storage { - if c.All || c.Latest || c.Volumes { - return errors.Errorf("--storage conflicts with --volumes, --all, and --latest") + if c.All || c.Ignore || c.Latest || c.Volumes || c.CIDFiles != nil { + return errors.Errorf("--storage conflicts with --volumes, --all, --latest, --ignore and --cidfile") } } diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 7aa4cb3c4..a6468f225 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -1,6 +1,8 @@ package main import ( + "os" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" "github.com/opentracing/opentracing-go" @@ -45,6 +47,11 @@ func runCmd(c *cliconfig.RunValues) error { span, _ := opentracing.StartSpanFromContext(Ctx, "runCmd") defer span.Finish() } + if c.String("authfile") != "" { + if _, err := os.Stat(c.String("authfile")); err != nil { + return errors.Wrapf(err, "error checking authfile path %s", c.String("authfile")) + } + } if err := createInit(&c.PodmanCommand); err != nil { return err } diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go index 7359bc0c7..358538155 100644 --- a/cmd/podman/runlabel.go +++ b/cmd/podman/runlabel.go @@ -6,6 +6,7 @@ import ( "os" "strings" + buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/libpodruntime" @@ -60,7 +61,7 @@ func init() { flags.BoolVarP(&runlabelCommand.Quiet, "quiet", "q", false, "Suppress output information when installing images") // Disabled flags for the remote client if !remote { - flags.StringVar(&runlabelCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&runlabelCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.StringVar(&runlabelCommand.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys") flags.StringVar(&runlabelCommand.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)") flags.BoolVar(&runlabelCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") @@ -97,6 +98,12 @@ func runlabelCmd(c *cliconfig.RunlabelValues) error { } defer runtime.DeferredShutdown(false) + if c.Authfile != "" { + if _, err := os.Stat(c.Authfile); err != nil { + return errors.Wrapf(err, "error getting authfile %s", c.Authfile) + } + } + args := c.InputArgs if len(args) < 2 { return errors.Errorf("the runlabel command requires at least 2 arguments: LABEL IMAGE") diff --git a/cmd/podman/search.go b/cmd/podman/search.go index cdcb30a59..87a26e544 100644 --- a/cmd/podman/search.go +++ b/cmd/podman/search.go @@ -1,13 +1,14 @@ package main import ( + "os" "reflect" "strings" + buildahcli "github.com/containers/buildah/pkg/cli" "github.com/containers/buildah/pkg/formats" "github.com/containers/image/v5/types" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/image" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -45,7 +46,7 @@ func init() { flags.BoolVar(&searchCommand.NoTrunc, "no-trunc", false, "Do not truncate the output") // Disabled flags for the remote client if !remote { - flags.StringVar(&searchCommand.Authfile, "authfile", shared.GetAuthFile(""), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") + flags.StringVar(&searchCommand.Authfile, "authfile", buildahcli.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") flags.BoolVar(&searchCommand.TlsVerify, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries") } } @@ -65,6 +66,12 @@ func searchCmd(c *cliconfig.SearchValues) error { return err } + if c.Authfile != "" { + if _, err := os.Stat(c.Authfile); err != nil { + return errors.Wrapf(err, "error getting authfile %s", c.Authfile) + } + } + searchOptions := image.SearchOptions{ NoTrunc: c.NoTrunc, Limit: c.Limit, diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index bc64d63a9..4f2002992 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -17,6 +17,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/timetype" "github.com/containers/libpod/pkg/util" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" @@ -195,6 +196,8 @@ func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput status = "Paused" case define.ContainerStateCreated.String(), define.ContainerStateConfigured.String(): status = "Created" + case define.ContainerStateRemoving.String(): + status = "Removing" default: status = "Error" } @@ -272,7 +275,8 @@ func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContai } } -func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { +// GenerateContainerFilterFuncs return ContainerFilter functions based of filter. +func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { switch filter { case "id": return func(c *libpod.Container) bool { @@ -394,6 +398,22 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) } return hcStatus == filterValue }, nil + case "until": + ts, err := timetype.GetTimestamp(filterValue, time.Now()) + if err != nil { + return nil, err + } + seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0) + if err != nil { + return nil, err + } + until := time.Unix(seconds, nanoseconds) + return func(c *libpod.Container) bool { + if !until.IsZero() && c.CreatedTime().After((until)) { + return true + } + return false + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } @@ -411,7 +431,7 @@ func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, m if len(filterSplit) < 2 { return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) } - generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], r) + generatedFunc, err := GenerateContainerFilterFuncs(filterSplit[0], filterSplit[1], r) if err != nil { return nil, errors.Wrapf(err, "invalid filter") } @@ -453,7 +473,7 @@ func PBatch(containers []*libpod.Container, workers int, opts PsOptions) []PsCon psResults := []PsContainerOutput{} // If the number of containers in question is less than the number of - // proposed parallel operations, we shouldnt spawn so many workers. + // proposed parallel operations, we shouldn't spawn so many workers. if workers > len(containers) { workers = len(containers) } @@ -694,7 +714,7 @@ func portsToString(ports []ocicni.PortMapping) string { portgroup, ok := portGroupMap[portMapKey] if !ok { portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} - // This list is required to travese portGroupMap. + // This list is required to traverse portGroupMap. groupKeyList = append(groupKeyList, portMapKey) continue } diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index dc343e694..bb4e9cd12 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -7,6 +7,7 @@ import ( "io" "os" "path/filepath" + goruntime "runtime" "strconv" "strings" "syscall" @@ -26,7 +27,6 @@ import ( "github.com/docker/docker/pkg/signal" "github.com/docker/go-connections/nat" "github.com/docker/go-units" - "github.com/opencontainers/selinux/go-selinux/label" "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -89,12 +89,14 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. return nil, nil, err } + overrideOS := c.String("override-os") + overrideArch := c.String("override-arch") dockerRegistryOptions := image.DockerRegistryOptions{ - OSChoice: c.String("override-os"), - ArchitectureChoice: c.String("override-arch"), + OSChoice: overrideOS, + ArchitectureChoice: overrideArch, } - newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, GetAuthFile(c.String("authfile")), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType) + newImage, err := runtime.ImageRuntime().New(ctx, name, rtc.SignaturePolicyPath, c.String("authfile"), writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullType) if err != nil { return nil, nil, err } @@ -102,6 +104,15 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. if err != nil { return nil, nil, err } + + if overrideOS == "" && data.Os != goruntime.GOOS { + return nil, nil, errors.Errorf("incompatible image OS %q on %q host", data.Os, goruntime.GOOS) + } + + if overrideArch == "" && data.Architecture != goruntime.GOARCH { + return nil, nil, errors.Errorf("incompatible image architecture %q on %q host", data.Architecture, goruntime.GOARCH) + } + names := newImage.Names() if len(names) > 0 { imageName = names[0] @@ -112,7 +123,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. // if the user disabled the healthcheck with "none", we skip adding it healthCheckCommandInput := c.String("healthcheck-command") - // the user didnt disable the healthcheck but did pass in a healthcheck command + // the user didn't disable the healthcheck but did pass in a healthcheck command // now we need to make a healthcheck from the commandline input if healthCheckCommandInput != "none" { if len(healthCheckCommandInput) > 0 { @@ -195,72 +206,6 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. return ctr, createConfig, nil } -func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string, runtime *libpod.Runtime) error { - var ( - labelOpts []string - ) - - if config.PidMode.IsHost() { - labelOpts = append(labelOpts, label.DisableSecOpt()...) - } else if config.PidMode.IsContainer() { - ctr, err := runtime.LookupContainer(config.PidMode.Container()) - if err != nil { - return errors.Wrapf(err, "container %q not found", config.PidMode.Container()) - } - 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() { - labelOpts = append(labelOpts, label.DisableSecOpt()...) - } else if config.IpcMode.IsContainer() { - ctr, err := runtime.LookupContainer(config.IpcMode.Container()) - if err != nil { - return errors.Wrapf(err, "container %q not found", config.IpcMode.Container()) - } - 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 { - if opt == "no-new-privileges" { - config.NoNewPrivs = true - } else { - con := strings.SplitN(opt, "=", 2) - if len(con) != 2 { - return fmt.Errorf("invalid --security-opt 1: %q", opt) - } - - switch con[0] { - case "label": - labelOpts = append(labelOpts, con[1]) - case "apparmor": - config.ApparmorProfile = con[1] - case "seccomp": - config.SeccompProfilePath = con[1] - default: - return fmt.Errorf("invalid --security-opt 2: %q", opt) - } - } - } - - if config.SeccompProfilePath == "" { - var err error - config.SeccompProfilePath, err = libpod.DefaultSeccompPath() - if err != nil { - return err - } - } - config.LabelOpts = labelOpts - return nil -} - func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string { entrypoint := []string{} if c.IsSet("entrypoint") { @@ -281,24 +226,24 @@ func configureEntrypoint(c *GenericCLIResults, data *inspect.ImageData) []string return entrypoint } -func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, error) { +func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[string]string, podName string) (map[string]string, string, error) { pod, err := runtime.LookupPod(podName) if err != nil { - return namespaces, err + return namespaces, "", err } podInfraID, err := pod.InfraContainerID() if err != nil { - return namespaces, err + return namespaces, "", err } hasUserns := false if podInfraID != "" { podCtr, err := runtime.GetContainer(podInfraID) if err != nil { - return namespaces, err + return namespaces, "", err } mappings, err := podCtr.IDMappings() if err != nil { - return namespaces, err + return namespaces, "", err } hasUserns = len(mappings.UIDMap) > 0 } @@ -318,7 +263,7 @@ func configurePod(c *GenericCLIResults, runtime *libpod.Runtime, namespaces map[ if (namespaces["uts"] == cc.Pod) || (!c.IsSet("uts") && pod.SharesUTS()) { namespaces["uts"] = fmt.Sprintf("container:%s", podInfraID) } - return namespaces, nil + return namespaces, podInfraID, nil } // Parses CLI options related to container creation into a config which can be @@ -336,10 +281,6 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. return nil, err } - if c.String("mac-address") != "" { - return nil, errors.Errorf("--mac-address option not currently supported") - } - imageID := "" inputCommand = c.InputArgs[1:] @@ -352,11 +293,6 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. rootfs = c.InputArgs[0] } - sysctl, err := validateSysctl(c.StringSlice("sysctl")) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for sysctl") - } - if c.String("memory") != "" { memoryLimit, err = units.RAMInBytes(c.String("memory")) if err != nil { @@ -435,6 +371,10 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. if len(podName) < 1 && c.IsSet("pod") { return nil, errors.Errorf("new pod name must be at least one character") } + + // If we are adding a container to a pod, we would like to add an annotation for the infra ID + // so kata containers can share VMs inside the pod + var podInfraID string if c.IsSet("pod") { if strings.HasPrefix(originalPodName, "new:") { // pod does not exist; lets make it @@ -463,7 +403,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. // The container now cannot have port bindings; so we reset the map portBindings = make(map[nat.Port][]nat.PortBinding) } - namespaces, err = configurePod(c, runtime, namespaces, podName) + namespaces, podInfraID, err = configurePod(c, runtime, namespaces, podName) if err != nil { return nil, err } @@ -561,12 +501,26 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. // ANNOTATIONS annotations := make(map[string]string) + // First, add our default annotations - annotations[ann.ContainerType] = "sandbox" annotations[ann.TTY] = "false" if tty { annotations[ann.TTY] = "true" } + + // in the event this container is in a pod, and the pod has an infra container + // we will want to configure it as a type "container" instead defaulting to + // the behavior of a "sandbox" container + // In Kata containers: + // - "sandbox" is the annotation that denotes the container should use its own + // VM, which is the default behavior + // - "container" denotes the container should join the VM of the SandboxID + // (the infra container) + if podInfraID != "" { + annotations[ann.SandboxID] = podInfraID + annotations[ann.ContainerType] = ann.ContainerTypeContainer + } + if data != nil { // Next, add annotations from the image for key, value := range data.Annotations { @@ -695,61 +649,96 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. pidsLimit = 0 } + pid := &cc.PidConfig{ + PidMode: pidMode, + } + ipc := &cc.IpcConfig{ + IpcMode: ipcMode, + } + + cgroup := &cc.CgroupConfig{ + Cgroups: c.String("cgroups"), + Cgroupns: c.String("cgroupns"), + CgroupParent: c.String("cgroup-parent"), + CgroupMode: cgroupMode, + } + + userns := &cc.UserConfig{ + GroupAdd: c.StringSlice("group-add"), + IDMappings: idmappings, + UsernsMode: usernsMode, + User: user, + } + + uts := &cc.UtsConfig{ + UtsMode: utsMode, + NoHosts: c.Bool("no-hosts"), + HostAdd: c.StringSlice("add-host"), + Hostname: c.String("hostname"), + } + + net := &cc.NetworkConfig{ + DNSOpt: c.StringSlice("dns-opt"), + DNSSearch: c.StringSlice("dns-search"), + DNSServers: c.StringSlice("dns"), + HTTPProxy: c.Bool("http-proxy"), + MacAddress: c.String("mac-address"), + Network: network, + NetMode: netMode, + IPAddress: c.String("ip"), + Publish: c.StringSlice("publish"), + PublishAll: c.Bool("publish-all"), + PortBindings: portBindings, + } + + sysctl, err := validateSysctl(c.StringSlice("sysctl")) + if err != nil { + return nil, errors.Wrapf(err, "invalid value for sysctl") + } + + secConfig := &cc.SecurityConfig{ + CapAdd: c.StringSlice("cap-add"), + CapDrop: c.StringSlice("cap-drop"), + Privileged: c.Bool("privileged"), + ReadOnlyRootfs: c.Bool("read-only"), + ReadOnlyTmpfs: c.Bool("read-only-tmpfs"), + Sysctl: sysctl, + } + + if err := secConfig.SetLabelOpts(runtime, pid, ipc); err != nil { + return nil, err + } + if err := secConfig.SetSecurityOpts(runtime, c.StringArray("security-opt")); err != nil { + return nil, err + } + config := &cc.CreateConfig{ Annotations: annotations, BuiltinImgVolumes: ImageVolumes, ConmonPidFile: c.String("conmon-pidfile"), ImageVolumeType: c.String("image-volume"), - CapAdd: c.StringSlice("cap-add"), - CapDrop: c.StringSlice("cap-drop"), CidFile: c.String("cidfile"), - Cgroupns: c.String("cgroupns"), - Cgroups: c.String("cgroups"), - CgroupParent: c.String("cgroup-parent"), Command: command, UserCommand: userCommand, Detach: c.Bool("detach"), Devices: c.StringSlice("device"), - DNSOpt: c.StringSlice("dns-opt"), - DNSSearch: c.StringSlice("dns-search"), - DNSServers: c.StringSlice("dns"), Entrypoint: entrypoint, Env: env, // ExposedPorts: ports, - GroupAdd: c.StringSlice("group-add"), - Hostname: c.String("hostname"), - HostAdd: c.StringSlice("add-host"), - HTTPProxy: c.Bool("http-proxy"), - NoHosts: c.Bool("no-hosts"), - IDMappings: idmappings, Init: c.Bool("init"), InitPath: c.String("init-path"), Image: imageName, ImageID: imageID, Interactive: c.Bool("interactive"), // IP6Address: c.String("ipv6"), // Not implemented yet - needs CNI support for static v6 - IPAddress: c.String("ip"), - Labels: labels, + Labels: labels, // LinkLocalIP: c.StringSlice("link-local-ip"), // Not implemented yet LogDriver: logDriver, LogDriverOpt: c.StringSlice("log-opt"), - MacAddress: c.String("mac-address"), Name: c.String("name"), - Network: network, // NetworkAlias: c.StringSlice("network-alias"), // Not implemented - does this make sense in Podman? - IpcMode: ipcMode, - NetMode: netMode, - UtsMode: utsMode, - PidMode: pidMode, - CgroupMode: cgroupMode, - Pod: podName, - Privileged: c.Bool("privileged"), - Publish: c.StringSlice("publish"), - PublishAll: c.Bool("publish-all"), - PortBindings: portBindings, - Quiet: c.Bool("quiet"), - ReadOnlyRootfs: c.Bool("read-only"), - ReadOnlyTmpfs: c.Bool("read-only-tmpfs"), + Pod: podName, + Quiet: c.Bool("quiet"), Resources: cc.CreateResourceConfig{ BlkioWeight: blkioWeight, BlkioWeightDevice: c.StringSlice("blkio-weight-device"), @@ -778,30 +767,27 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. }, RestartPolicy: c.String("restart"), Rm: c.Bool("rm"), + Security: *secConfig, StopSignal: stopSignal, StopTimeout: c.Uint("stop-timeout"), - Sysctl: sysctl, Systemd: systemd, Tmpfs: c.StringArray("tmpfs"), Tty: tty, - User: user, - UsernsMode: usernsMode, MountsFlag: c.StringArray("mount"), Volumes: c.StringArray("volume"), WorkDir: workDir, Rootfs: rootfs, VolumesFrom: c.StringSlice("volumes-from"), Syslog: c.Bool("syslog"), - } - if config.Privileged { - config.LabelOpts = label.DisableSecOpt() - } else { - if err := parseSecurityOpt(config, c.StringArray("security-opt"), runtime); err != nil { - return nil, err - } + Pid: *pid, + Ipc: *ipc, + Cgroup: *cgroup, + User: *userns, + Uts: *uts, + Network: *net, } - config.SecurityOpts = c.StringArray("security-opt") + warnings, err := verifyContainerResources(config, false) if err != nil { return nil, err diff --git a/cmd/podman/shared/funcs.go b/cmd/podman/shared/funcs.go index 9362e8e9b..404d0f288 100644 --- a/cmd/podman/shared/funcs.go +++ b/cmd/podman/shared/funcs.go @@ -6,24 +6,19 @@ import ( "path/filepath" "strings" - "github.com/containers/libpod/pkg/util" + "github.com/containers/image/v5/types" + "github.com/containers/libpod/libpod/image" "github.com/google/shlex" + "github.com/pkg/errors" ) -func GetAuthFile(authfile string) string { +func GetSystemContext(authfile string) (*types.SystemContext, error) { if authfile != "" { - return authfile - } - - authfile = os.Getenv("REGISTRY_AUTH_FILE") - if authfile != "" { - return authfile - } - - if runtimeDir, err := util.GetRuntimeDir(); err == nil { - return filepath.Join(runtimeDir, "containers/auth.json") + if _, err := os.Stat(authfile); err != nil { + return nil, errors.Wrapf(err, "error checking authfile path %s", authfile) + } } - return "" + return image.GetSystemContext("", authfile, false), nil } func substituteCommand(cmd string) (string, error) { diff --git a/cmd/podman/shared/parallel.go b/cmd/podman/shared/parallel.go index e6ce50f95..eb1d40073 100644 --- a/cmd/podman/shared/parallel.go +++ b/cmd/podman/shared/parallel.go @@ -72,7 +72,7 @@ func ParallelExecuteWorkerPool(workers int, functions []ParallelWorkerInput) (ma } // Parallelize provides the maximum number of parallel workers (int) as calculated by a basic -// heuristic. This can be overriden by the --max-workers primary switch to podman. +// heuristic. This can be overridden by the --max-workers primary switch to podman. func Parallelize(job string) int { numCpus := runtime.NumCPU() switch job { diff --git a/cmd/podman/shared/parse/parse_test.go b/cmd/podman/shared/parse/parse_test.go index 0a221c244..1359076a0 100644 --- a/cmd/podman/shared/parse/parse_test.go +++ b/cmd/podman/shared/parse/parse_test.go @@ -82,8 +82,8 @@ func TestValidateFileName(t *testing.T) { args args wantErr bool }{ - {name: "good", args: args{filename: "/som/rand/path"}, wantErr: false}, - {name: "good", args: args{filename: "som/rand/path"}, wantErr: false}, + {name: "good", args: args{filename: "/some/rand/path"}, wantErr: false}, + {name: "good", args: args{filename: "some/rand/path"}, wantErr: false}, {name: "good", args: args{filename: "/"}, wantErr: false}, {name: "bad", args: args{filename: "/:"}, wantErr: true}, {name: "bad", args: args{filename: ":/"}, wantErr: true}, diff --git a/cmd/podman/shared/workers.go b/cmd/podman/shared/workers.go index b6e3f10e7..a9d6bb77e 100644 --- a/cmd/podman/shared/workers.go +++ b/cmd/podman/shared/workers.go @@ -106,7 +106,7 @@ func (p *Pool) newWorker(slot int) { } // DefaultPoolSize provides the maximum number of parallel workers (int) as calculated by a basic -// heuristic. This can be overriden by the --max-workers primary switch to podman. +// heuristic. This can be overridden by the --max-workers primary switch to podman. func DefaultPoolSize(name string) int { numCpus := runtime.NumCPU() switch name { diff --git a/cmd/podman/start.go b/cmd/podman/start.go index 2d2cf74d2..a070cd18d 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -35,7 +35,10 @@ func init() { startCommand.SetUsageTemplate(UsageTemplate()) flags := startCommand.Flags() flags.BoolVarP(&startCommand.Attach, "attach", "a", false, "Attach container's STDOUT and STDERR") - flags.StringVar(&startCommand.DetachKeys, "detach-keys", "", "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") + // Clear the default, the value specified in the config file should have the + // priority + startCommand.DetachKeys = "" + flags.StringVar(&startCommand.DetachKeys, "detach-keys", define.DefaultDetachKeys, "Select the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-z`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") flags.BoolVarP(&startCommand.Interactive, "interactive", "i", false, "Keep STDIN open even if not attached") flags.BoolVarP(&startCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.BoolVar(&startCommand.SigProxy, "sig-proxy", false, "Proxy received signals to the process (default true if attaching, false otherwise)") diff --git a/cmd/podman/stats.go b/cmd/podman/stats.go index f8c476386..f53e09412 100644 --- a/cmd/podman/stats.go +++ b/cmd/podman/stats.go @@ -128,7 +128,7 @@ func statsCmd(c *cliconfig.StatsValues) error { for _, ctr := range ctrs { initialStats, err := ctr.GetContainerStats(&libpod.ContainerStats{}) if err != nil { - // when doing "all", dont worry about containers that are not running + // when doing "all", don't worry about containers that are not running cause := errors.Cause(err) if c.All && (cause == define.ErrCtrRemoved || cause == define.ErrNoSuchCtr || cause == define.ErrCtrStateInvalid) { continue diff --git a/cmd/podman/stop.go b/cmd/podman/stop.go index e04d8a12b..c62da80df 100644 --- a/cmd/podman/stop.go +++ b/cmd/podman/stop.go @@ -25,7 +25,7 @@ var ( return stopCmd(&stopCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, true) }, Example: `podman stop ctrID podman stop --latest @@ -39,10 +39,14 @@ func init() { stopCommand.SetUsageTemplate(UsageTemplate()) flags := stopCommand.Flags() flags.BoolVarP(&stopCommand.All, "all", "a", false, "Stop all running containers") + flags.BoolVarP(&stopCommand.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing") + flags.StringArrayVarP(&stopCommand.CIDFiles, "cidfile", "", nil, "Read the container ID from the file") flags.BoolVarP(&stopCommand.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.UintVar(&stopCommand.Timeout, "time", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") flags.UintVarP(&stopCommand.Timeout, "timeout", "t", define.CtrRemoveTimeout, "Seconds to wait for stop before killing the container") markFlagHiddenForRemoteClient("latest", flags) + markFlagHiddenForRemoteClient("cidfile", flags) + markFlagHiddenForRemoteClient("ignore", flags) } // stopCmd stops a container or containers diff --git a/cmd/podman/system.go b/cmd/podman/system.go index 80080bf44..921d0c037 100644 --- a/cmd/podman/system.go +++ b/cmd/podman/system.go @@ -19,6 +19,7 @@ var ( ) var systemCommands = []*cobra.Command{ + _systemResetCommand, _infoCommand, _pruneSystemCommand, } diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index b499d8dd2..74fdcde99 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -82,7 +82,6 @@ Are you sure you want to continue? [y/N] `, volumeString) fmt.Println("Deleted Pods") pruneValues := cliconfig.PodPruneValues{ PodmanCommand: c.PodmanCommand, - Force: c.Force, } ctx := getContext() ok, failures, lasterr := runtime.PrunePods(ctx, &pruneValues) @@ -93,7 +92,7 @@ Are you sure you want to continue? [y/N] `, volumeString) rmWorkers := shared.Parallelize("rm") fmt.Println("Deleted Containers") - ok, failures, err = runtime.Prune(ctx, rmWorkers, false) + ok, failures, err = runtime.Prune(ctx, rmWorkers, false, []string{}) if err != nil { if lasterr != nil { logrus.Errorf("%q", err) @@ -117,7 +116,8 @@ Are you sure you want to continue? [y/N] `, volumeString) // Call prune; if any cids are returned, print them and then // return err in case an error also came up - pruneCids, err := runtime.PruneImages(ctx, c.All) + // TODO: support for filters in system prune + pruneCids, err := runtime.PruneImages(ctx, c.All, []string{}) if len(pruneCids) > 0 { fmt.Println("Deleted Images") for _, cid := range pruneCids { diff --git a/cmd/podman/system_renumber.go b/cmd/podman/system_renumber.go index 81752a177..4e90a2d8c 100644 --- a/cmd/podman/system_renumber.go +++ b/cmd/podman/system_renumber.go @@ -44,9 +44,7 @@ func renumberCmd(c *cliconfig.SystemRenumberValues) error { if err != nil { return errors.Wrapf(err, "error renumbering locks") } - if err := r.Shutdown(false); err != nil { - return err - } + _ = r.Shutdown(false) return nil } diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go index 904a0d375..cb1b3fc9c 100644 --- a/cmd/podman/tree.go +++ b/cmd/podman/tree.go @@ -115,7 +115,7 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr // add continueItem i.e. '|' for next iteration prefix prefix = prefix + continueItem } else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 { - // The above condition ensure, alignment happens for node, which has more then 1 childern. + // The above condition ensure, alignment happens for node, which has more then 1 children. // If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├── intend = lastItem prefix = prefix + " " diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index c3d81d3a8..6ad485c2c 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -28,7 +28,7 @@ var ( return umountCmd(&umountCommand) }, Args: func(cmd *cobra.Command, args []string) error { - return checkAllAndLatest(cmd, args, false) + return checkAllLatestAndCIDFile(cmd, args, false, false) }, Example: `podman umount ctrID podman umount ctrID1 ctrID2 ctrID3 diff --git a/cmd/podman/utils.go b/cmd/podman/utils.go index c19e6391e..21389b43a 100644 --- a/cmd/podman/utils.go +++ b/cmd/podman/utils.go @@ -74,3 +74,13 @@ func checkIfFileExists(name string) bool { } return !file.IsDir() } + +// Check if a file is or is not a directory +func fileIsDir(name string) bool { + file, err := os.Stat(name) + // All errors return file == nil + if err != nil { + return false + } + return file.IsDir() +} diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index f9339fccb..2251050c3 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -70,7 +70,8 @@ type Image ( labels: [string]string, isParent: bool, topLayer: string, - readOnly: bool + readOnly: bool, + history: []string ) # ImageHistory describes the returned structure from ImageHistory. @@ -535,6 +536,10 @@ method GetVersion() -> ( remote_api_version: int ) +# Reset resets Podman back to its initial state. +# Removes all Pods, Containers, Images and Volumes +method Reset() -> () + # GetInfo returns a [PodmanInfo](#PodmanInfo) struct that describes podman and its host such as storage stats, # build information of Podman, and system-wide registries. method GetInfo() -> (info: PodmanInfo) @@ -779,6 +784,11 @@ method DeleteStoppedContainers() -> (containers: []string) # See also [InspectImage](#InspectImage). method ListImages() -> (images: []Image) +# ListImagesWithFilters returns information about the images that are currently in storage +# after one or more filters has been applied. +# See also [InspectImage](#InspectImage). +method ListImagesWithFilters(filters: []string) -> (images: []Image) + # GetImage returns information about a single image in storage. # If the image caGetImage returns be found, [ImageNotFound](#ImageNotFound) will be returned. method GetImage(id: string) -> (image: Image) @@ -1217,7 +1227,7 @@ 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(all: bool) -> (pruned: []string) +method ImagesPrune(all: bool, filter: []string) -> (pruned: []string) # This function is not implemented yet. # method ListContainerPorts(name: string) -> (notimplemented: NotImplemented) diff --git a/cmd/podman/version.go b/cmd/podman/version.go index 314b2e266..5907241ff 100644 --- a/cmd/podman/version.go +++ b/cmd/podman/version.go @@ -37,13 +37,40 @@ func init() { flags := versionCommand.Flags() flags.StringVarP(&versionCommand.Format, "format", "f", "", "Change the output format to JSON or a Go template") } +func getRemoteVersion(c *cliconfig.VersionValues) (version define.Version, err error) { + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + if err != nil { + return version, errors.Wrapf(err, "could not get runtime") + } + defer runtime.DeferredShutdown(false) + + return runtime.GetVersion() +} + +type versionStruct struct { + Client define.Version + Server define.Version +} // versionCmd gets and prints version info for version command func versionCmd(c *cliconfig.VersionValues) error { - clientVersion, err := define.GetVersion() + + var ( + v versionStruct + err error + ) + v.Client, err = define.GetVersion() if err != nil { return errors.Wrapf(err, "unable to determine version") } + if remote { + v.Server, err = getRemoteVersion(c) + if err != nil { + return err + } + } else { + v.Server = v.Client + } versionOutputFormat := c.Format if versionOutputFormat != "" { @@ -53,11 +80,20 @@ func versionCmd(c *cliconfig.VersionValues) error { var out formats.Writer switch versionOutputFormat { case formats.JSONString: - out = formats.JSONStruct{Output: clientVersion} + out = formats.JSONStruct{Output: v} + return out.Out() default: - out = formats.StdoutTemplate{Output: clientVersion, Template: versionOutputFormat} + out = formats.StdoutTemplate{Output: v, Template: versionOutputFormat} + err := out.Out() + if err != nil { + // On Failure, assume user is using older version of podman version --format and check client + out = formats.StdoutTemplate{Output: v.Client, Template: versionOutputFormat} + if err1 := out.Out(); err1 != nil { + return err + } + } } - return out.Out() + return nil } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) defer w.Flush() @@ -66,25 +102,13 @@ func versionCmd(c *cliconfig.VersionValues) error { if _, err := fmt.Fprintf(w, "Client:\n"); err != nil { return err } - } - formatVersion(w, clientVersion) - - if remote { - if _, err := fmt.Fprintf(w, "\nService:\n"); err != nil { - return err - } - - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) - if err != nil { - return errors.Wrapf(err, "could not get runtime") - } - defer runtime.DeferredShutdown(false) - - serviceVersion, err := runtime.GetVersion() - if err != nil { + formatVersion(w, v.Client) + if _, err := fmt.Fprintf(w, "\nServer:\n"); err != nil { return err } - formatVersion(w, serviceVersion) + formatVersion(w, v.Server) + } else { + formatVersion(w, v.Client) } return nil } |