diff options
56 files changed, 554 insertions, 354 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 885564f9f..dbd0e8b3e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -27,12 +27,12 @@ env: #### #### Cache-image names to test with ### - FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-9afa57a9" - PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-28-libpod-9afa57a9" - UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-9afa57a9" - RHEL_CACHE_IMAGE_NAME: "rhel-8-notready" - PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-notready" - CENTOS_CACHE_IMAGE_NAME: "centos-7-notready" + FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-7f4cd1f7" + PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-28-libpod-7f4cd1f7" + UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-7f4cd1f7" + # RHEL_CACHE_IMAGE_NAME: "rhel-8-notready" + PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-libpod-7f4cd1f7" + # CENTOS_CACHE_IMAGE_NAME: "centos-7-notready" #### #### Variables for composing new cache-images (used in PR testing) from @@ -45,9 +45,9 @@ env: CRIU_COMMIT: "c74b83cd49c00589c0c0468ba5fe685b67fdbd0a" RUNC_COMMIT: "25f3f893c86d07426df93b7aa172f33fdf093fbd" # CSV of cache-image names to build (see $PACKER_BASE/libpod_images.json) - PACKER_BUILDS: "ubuntu-18,fedora-29,fedora-28" # TODO: fah-29,rhel-7,centos-7 + PACKER_BUILDS: "ubuntu-18,fedora-29,fedora-28,rhel-7" # TODO: rhel-8,centos-7 # Version of packer to use - PACKER_VER: "1.3.1" + PACKER_VER: "1.3.2" # Special image w/ nested-libvirt + tools for creating new cache and base images IMAGE_BUILDER_CACHE_IMAGE_NAME: "image-builder-image-1541772081" # Google-maintained base-image names @@ -58,7 +58,7 @@ env: PRIOR_FEDORA_BASE_IMAGE: "fedora-cloud-base-28-1-1-1544474897" FAH_BASE_IMAGE: "fedora-atomichost-29-20181025-1-1541787861" # RHEL image must be imported, google bills extra for their native image. - RHEL_BASE_IMAGE: "rhel-guest-image-7-6-210-x86-64-qcow2-1541783972" + RHEL_BASE_IMAGE: "rhel-guest-image-7-6-210-x86-64-qcow2-1548099756" #### #### Credentials and other secret-sauces, decrypted at runtime when authorized. @@ -154,10 +154,10 @@ testing_task: image_name: "${FEDORA_CACHE_IMAGE_NAME}" image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" image_name: "${UBUNTU_CACHE_IMAGE_NAME}" + image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME}" # TODO: tests fail - # image_name: "${RHEL_CACHE_IMAGE_NAME} - # image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME} + # image_name: "${RHEL_CACHE_IMAGE_NAME}" # image_name: "${CENTOS_CACHE_IMAGE_NAME}" timeout_in: 120m @@ -191,9 +191,9 @@ optional_testing_task: image_name: "${FEDORA_CACHE_IMAGE_NAME}" image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" image_name: "${UBUNTU_CACHE_IMAGE_NAME}" + image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME}" # TODO: Make these work (also build_images_task below) - # image_name: "${RHEL_CACHE_IMAGE_NAME} - # image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME} + # image_name: "${RHEL_CACHE_IMAGE_NAME}" # image_name: "${CENTOS_CACHE_IMAGE_NAME}" timeout_in: 60m @@ -65,7 +65,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func ImageExists(name: string) int](#ImageExists) -[func ImagesPrune() []string](#ImagesPrune) +[func ImagesPrune(all: bool) []string](#ImagesPrune) [func ImportImage(source: string, reference: string, message: string, changes: []string) string](#ImportImage) @@ -81,7 +81,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func ListContainerChanges(name: string) ContainerChanges](#ListContainerChanges) -[func ListContainerMounts() []string](#ListContainerMounts) +[func ListContainerMounts() map[string]](#ListContainerMounts) [func ListContainerPorts(name: string) NotImplemented](#ListContainerPorts) @@ -580,7 +580,7 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "im ### <a name="ImagesPrune"></a>func ImagesPrune <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> -method ImagesPrune() [[]string](#[]string)</div> +method ImagesPrune(all: [bool](https://godoc.org/builtin#bool)) [[]string](#[]string)</div> ImagesPrune removes all unused images from the local store. Upon successful pruning, the IDs of the removed images are returned. ### <a name="ImportImage"></a>func ImportImage @@ -643,19 +643,17 @@ its base image. It returns a struct of changed, deleted, and added path names. ### <a name="ListContainerMounts"></a>func ListContainerMounts <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> -method ListContainerMounts() [[]string](#[]string)</div> +method ListContainerMounts() [map[string]](#map[string])</div> ListContainerMounts gathers all the mounted container mount points and returns them as an array of strings #### Example ~~~ -$ varlink call -m unix:/run/podman/io.podman/io.podman.ListContainerMounts +$ varlink call unix:/run/podman/io.podman/io.podman.ListContainerMounts { - "mounts": [ - "/var/lib/containers/storage/overlay/b215fb622c65ba3b06c6d2341be80b76a9de7ae415ce419e65228873d4f0dcc8/merged", - "/var/lib/containers/storage/overlay/5eaf806073f79c0ed9a695180ad598e34f963f7407da1d2ccf3560bdab49b26f/merged", - "/var/lib/containers/storage/overlay/1ecb6b1dbb251737c7a24a31869096839c3719d8b250bf075f75172ddcc701e1/merged", - "/var/lib/containers/storage/overlay/7137b28a3c422165fe920cba851f2f8da271c6b5908672c451ebda03ad3919e2/merged" - ] + "mounts": { + "04e4c255269ed2545e7f8bd1395a75f7949c50c223415c00c1d54bfa20f3b3d9": "/var/lib/containers/storage/overlay/a078925828f57e20467ca31cfca8a849210d21ec7e5757332b72b6924f441c17/merged", + "1d58c319f9e881a644a5122ff84419dccf6d138f744469281446ab243ef38924": "/var/lib/containers/storage/overlay/948fcf93f8cb932f0f03fd52e3180a58627d547192ffe3b88e0013b98ddcd0d2/merged" + } } ~~~ ### <a name="ListContainerPorts"></a>func ListContainerPorts @@ -224,6 +224,7 @@ install: .gopathok install.bin install.man install.cni install.systemd install.bin: install ${SELINUXOPT} -d -m 755 $(BINDIR) install ${SELINUXOPT} -m 755 bin/podman $(BINDIR)/podman + test -z "${SELINUXOPT}" || chcon --verbose --reference=$(BINDIR)/podman bin/podman install.man: docs install ${SELINUXOPT} -d -m 755 $(MANDIR)/man1 @@ -3,8 +3,7 @@ # Library and tool for running OCI-based containers in Pods Libpod provides a library for applications looking to use the Container Pod concept, -popularized by Kubernetes. libpod also contains the `podman` tool, for managing -Pods, Containers, and Container Images. +popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes. * [Latest Version: 1.0.0](https://github.com/containers/libpod/releases/latest) * [Continuous Integration:](contrib/cirrus/README.md) [![Build Status](https://api.cirrus-ci.com/github/containers/libpod.svg)](https://cirrus-ci.com/github/containers/libpod/master) diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 065d08df4..c56efa153 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "io" "io/ioutil" "os" "path/filepath" @@ -128,7 +129,12 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container var data *inspect.ImageData = nil if rootfs == "" && !rootless.SkipStorageSetup() { - newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", os.Stderr, nil, image.SigningOptions{}, false) + var writer io.Writer + if !c.Bool("quiet") { + writer = os.Stderr + } + + newImage, err := runtime.ImageRuntime().New(ctx, c.Args()[0], rtc.SignaturePolicyPath, "", writer, nil, image.SigningOptions{}, false) if err != nil { return nil, nil, err } diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 031f06618..d4f405975 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -188,13 +188,6 @@ func imagesCmd(c *cli.Context) error { } opts.outputformat = opts.setOutputFormat() - /* - podman does not implement --all for images - - intermediate images are only generated during the build process. they are - children to the image once built. until buildah supports caching builds, - it will not generate these intermediate images. - */ images, err := runtime.GetImages() if err != nil { return errors.Wrapf(err, "unable to get images") diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go index 06879e02d..aef387732 100644 --- a/cmd/podman/images_prune.go +++ b/cmd/podman/images_prune.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/containers/libpod/cmd/podman/libpodruntime" + "github.com/containers/libpod/libpod/adapter" "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -13,33 +13,36 @@ var ( Removes all unnamed images from local storage ` - + pruneImageFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "all, a", + Usage: "remove all unused images, not just dangling ones", + }, + } pruneImagesCommand = cli.Command{ Name: "prune", Usage: "Remove unused images", Description: pruneImagesDescription, Action: pruneImagesCmd, OnUsageError: usageErrorHandler, + Flags: pruneImageFlags, } ) func pruneImagesCmd(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") } defer runtime.Shutdown(false) - 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()) + // Call prune; if any cids are returned, print them and then + // return err in case an error also came up + pruneCids, err := runtime.PruneImages(c.Bool("all")) + if len(pruneCids) > 0 { + for _, cid := range pruneCids { + fmt.Println(cid) } - fmt.Println(i.ID()) } - return nil + return err } diff --git a/cmd/podman/info.go b/cmd/podman/info.go index 3888829a3..f5f91b603 100644 --- a/cmd/podman/info.go +++ b/cmd/podman/info.go @@ -29,7 +29,7 @@ var ( Usage: "display additional debug information", }, cli.StringFlag{ - Name: "format", + Name: "format, f", Usage: "Change the output format to JSON or a Go template", }, } diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go index 3ef740463..1346da9fb 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -32,7 +32,7 @@ var ( Usage: "Change the output format to a Go template", }, cli.BoolFlag{ - Name: "size", + Name: "size, s", Usage: "Display total file size if the type is container", }, LatestFlag, @@ -40,7 +40,7 @@ var ( inspectDescription = "This displays the low-level information on containers and images identified by name or ID. By default, this will render all results in a JSON array. If the container and image have the same name, this will return container JSON for unspecified type." inspectCommand = cli.Command{ Name: "inspect", - Usage: "Displays the configuration of a container or image", + Usage: "Display the configuration of a container or image", Description: inspectDescription, Flags: sortFlags(inspectFlags), Action: inspectCmd, diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go index fcb2f3cb8..9da6abf4b 100644 --- a/cmd/podman/pause.go +++ b/cmd/podman/pause.go @@ -25,7 +25,7 @@ var ( ` pauseCommand = cli.Command{ Name: "pause", - Usage: "Pauses all the processes in one or more containers", + Usage: "Pause all the processes in one or more containers", Description: pauseDescription, Flags: pauseFlags, Action: pauseCmd, diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 0ad3f4c73..1708c671c 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -606,19 +606,50 @@ func portsToString(ports []ocicni.PortMapping) string { } func printFormat(format string, containers []shared.PsContainerOutput) error { - out := template.New("output") - out, err := out.Parse(format + "\n") + // return immediately if no containers are present + if len(containers) == 0 { + return nil + } + + // Use a tabwriter to align column format + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + + // Make a map of the field names for the headers + headerNames := make(map[string]string) + v := reflect.ValueOf(containers[0]) + t := v.Type() + for i := 0; i < t.NumField(); i++ { + headerNames[t.Field(i).Name] = t.Field(i).Name + } + + // Spit out the header if "table" is present in the format + if strings.HasPrefix(format, "table") { + hformat := strings.Replace(strings.TrimSpace(format[5:]), " ", "\t", -1) + format = hformat + headerTmpl, err := template.New("header").Parse(hformat) + if err != nil { + return err + } + if err := headerTmpl.Execute(w, headerNames); err != nil { + return err + } + fmt.Fprintln(w, "") + } + // Spit out the data rows now + dataTmpl, err := template.New("data").Parse(format) if err != nil { return err } + for _, container := range containers { - if err := out.Execute(os.Stdout, container); err != nil { + if err := dataTmpl.Execute(w, container); err != nil { return err } - + fmt.Fprintln(w, "") } - return nil + // Flush the writer + return w.Flush() } func dumpJSON(containers []shared.PsContainerOutput) error { diff --git a/cmd/podman/rmi.go b/cmd/podman/rmi.go index fbf860eb2..c904f2f92 100644 --- a/cmd/podman/rmi.go +++ b/cmd/podman/rmi.go @@ -24,7 +24,7 @@ var ( } rmiCommand = cli.Command{ Name: "rmi", - Usage: "Removes one or more images from local storage", + Usage: "Remove one or more images from local storage", Description: rmiDescription, Action: rmiCmd, ArgsUsage: "IMAGE-NAME-OR-ID [...]", diff --git a/cmd/podman/trust.go b/cmd/podman/trust.go index 863f36d09..a99be6ba2 100644 --- a/cmd/podman/trust.go +++ b/cmd/podman/trust.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "sort" + "strings" "github.com/containers/image/types" "github.com/containers/libpod/cmd/podman/formats" @@ -13,6 +14,7 @@ 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" ) @@ -103,6 +105,7 @@ func showTrustCmd(c *cli.Context) error { var ( policyPath string systemRegistriesDirPath string + outjson interface{} ) if c.IsSet("policypath") { policyPath = c.String("policypath") @@ -127,30 +130,26 @@ func showTrustCmd(c *cli.Context) error { return nil } - var policyContentStruct trust.PolicyContent - if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { - return errors.Errorf("could not read trust policies") - } - policyJSON, showOutputMap, err := trust.GetPolicy(policyContentStruct, systemRegistriesDirPath) + policyContentStruct, err := trust.GetPolicy(policyPath) if err != nil { - return errors.Wrapf(err, "error reading registry config file") + return errors.Wrapf(err, "could not read trust policies") } + if c.Bool("json") { - var outjson interface{} + policyJSON, err := getPolicyJSON(policyContentStruct, systemRegistriesDirPath) + if err != nil { + return errors.Wrapf(err, "could not show trust policies in JSON format") + } outjson = policyJSON out := formats.JSONStruct{Output: outjson} return formats.Writer(out).Out() } - sortedRepos := sortPolicyJSONKey(policyJSON) - var output []interface{} - for _, reponame := range sortedRepos { - showOutput, exists := showOutputMap[reponame] - if exists { - output = append(output, interface{}(showOutput)) - } + showOutputMap, err := getPolicyShowOutput(policyContentStruct, systemRegistriesDirPath) + if err != nil { + return errors.Wrapf(err, "could not show trust policies") } - out := formats.StdoutTemplateArray{Output: output, Template: "{{.Repo}}\t{{.Trusttype}}\t{{.GPGid}}\t{{.Sigstore}}"} + out := formats.StdoutTemplateArray{Output: showOutputMap, Template: "{{.Repo}}\t{{.Trusttype}}\t{{.GPGid}}\t{{.Sigstore}}"} return formats.Writer(out).Out() } @@ -159,7 +158,11 @@ func setTrustCmd(c *cli.Context) error { if err != nil { return errors.Wrapf(err, "could not create runtime") } - + var ( + policyPath string + policyContentStruct trust.PolicyContent + newReposContent []trust.RepoContent + ) args := c.Args() if len(args) != 1 { return errors.Errorf("default or a registry name must be specified") @@ -182,17 +185,13 @@ func setTrustCmd(c *cli.Context) error { return errors.Errorf("At least one public key must be defined for type 'signedBy'") } - var policyPath string if c.IsSet("policypath") { policyPath = c.String("policypath") } else { 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) @@ -200,11 +199,7 @@ 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 { for _, filepath := range pubkeysfile { newReposContent = append(newReposContent, trust.RepoContent{Type: trusttype, KeyType: "GPGKeys", KeyPath: filepath}) @@ -215,8 +210,8 @@ func setTrustCmd(c *cli.Context) error { if args[0] == "default" { policyContentStruct.Default = newReposContent } else { - if policyFileExists == false && len(policyContentStruct.Default) == 0 { - return errors.Errorf("Default trust policy must be set to create the policy file.") + if len(policyContentStruct.Default) == 0 { + return errors.Errorf("Default trust policy must be set.") } registryExists := false for transport, transportval := range policyContentStruct.Transports { @@ -248,7 +243,7 @@ func setTrustCmd(c *cli.Context) error { return nil } -func sortPolicyJSONKey(m map[string]map[string]interface{}) []string { +func sortShowOutputMapKey(m map[string]trust.ShowOutput) []string { keys := make([]string, len(m)) i := 0 for k := range m { @@ -269,3 +264,106 @@ func isValidTrustType(t string) bool { func getDefaultPolicyPath() string { return trust.DefaultPolicyPath(&types.SystemContext{}) } + +func getPolicyJSON(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, error) { + registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return nil, err + } + + policyJSON := make(map[string]map[string]interface{}) + if len(policyContentStruct.Default) > 0 { + policyJSON["* (default)"] = make(map[string]interface{}) + policyJSON["* (default)"]["type"] = policyContentStruct.Default[0].Type + } + for transname, transval := range policyContentStruct.Transports { + for repo, repoval := range transval { + policyJSON[repo] = make(map[string]interface{}) + policyJSON[repo]["type"] = repoval[0].Type + policyJSON[repo]["transport"] = transname + keyarr := []string{} + uids := []string{} + for _, repoele := range repoval { + if len(repoele.KeyPath) > 0 { + keyarr = append(keyarr, repoele.KeyPath) + uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) + } + if len(repoele.KeyData) > 0 { + keyarr = append(keyarr, string(repoele.KeyData)) + uids = append(uids, trust.GetGPGIdFromKeyData(string(repoele.KeyData))...) + } + } + policyJSON[repo]["keys"] = keyarr + policyJSON[repo]["sigstore"] = "" + registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs) + if registryNamespace != nil { + policyJSON[repo]["sigstore"] = registryNamespace.SigStore + } + } + } + return policyJSON, 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 getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]interface{}, error) { + var output []interface{} + + registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return nil, err + } + + trustShowOutputMap := make(map[string]trust.ShowOutput) + if len(policyContentStruct.Default) > 0 { + defaultPolicyStruct := trust.ShowOutput{ + Repo: "default", + Trusttype: trustTypeDescription(policyContentStruct.Default[0].Type), + } + trustShowOutputMap["* (default)"] = defaultPolicyStruct + } + for _, transval := range policyContentStruct.Transports { + for repo, repoval := range transval { + tempTrustShowOutput := trust.ShowOutput{ + Repo: repo, + Trusttype: repoval[0].Type, + } + keyarr := []string{} + uids := []string{} + for _, repoele := range repoval { + if len(repoele.KeyPath) > 0 { + keyarr = append(keyarr, repoele.KeyPath) + uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) + } + if len(repoele.KeyData) > 0 { + keyarr = append(keyarr, string(repoele.KeyData)) + uids = append(uids, trust.GetGPGIdFromKeyData(string(repoele.KeyData))...) + } + } + tempTrustShowOutput.GPGid = strings.Join(uids, ", ") + + registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs) + if registryNamespace != nil { + tempTrustShowOutput.Sigstore = registryNamespace.SigStore + } + trustShowOutputMap[repo] = tempTrustShowOutput + } + } + + sortedRepos := sortShowOutputMapKey(trustShowOutputMap) + for _, reponame := range sortedRepos { + showOutput, exists := trustShowOutputMap[reponame] + if exists { + output = append(output, interface{}(showOutput)) + } + } + return output, nil +} diff --git a/cmd/podman/umount.go b/cmd/podman/umount.go index 7c9b5897b..42f169228 100644 --- a/cmd/podman/umount.go +++ b/cmd/podman/umount.go @@ -34,7 +34,7 @@ An unmount can be forced with the --force flag. umountCommand = cli.Command{ Name: "umount", Aliases: []string{"unmount"}, - Usage: "Unmounts working container's root filesystem", + Usage: "Unmount working container's root filesystem", Description: description, Flags: sortFlags(umountFlags), Action: umountCmd, diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 86c3eb7ff..8b02057a1 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -987,17 +987,15 @@ method ContainerRunlabel(runlabel: Runlabel) -> () # of strings # #### Example # ~~~ -# $ varlink call -m unix:/run/podman/io.podman/io.podman.ListContainerMounts +# $ varlink call unix:/run/podman/io.podman/io.podman.ListContainerMounts # { -# "mounts": [ -# "/var/lib/containers/storage/overlay/b215fb622c65ba3b06c6d2341be80b76a9de7ae415ce419e65228873d4f0dcc8/merged", -# "/var/lib/containers/storage/overlay/5eaf806073f79c0ed9a695180ad598e34f963f7407da1d2ccf3560bdab49b26f/merged", -# "/var/lib/containers/storage/overlay/1ecb6b1dbb251737c7a24a31869096839c3719d8b250bf075f75172ddcc701e1/merged", -# "/var/lib/containers/storage/overlay/7137b28a3c422165fe920cba851f2f8da271c6b5908672c451ebda03ad3919e2/merged" -# ] +# "mounts": { +# "04e4c255269ed2545e7f8bd1395a75f7949c50c223415c00c1d54bfa20f3b3d9": "/var/lib/containers/storage/overlay/a078925828f57e20467ca31cfca8a849210d21ec7e5757332b72b6924f441c17/merged", +# "1d58c319f9e881a644a5122ff84419dccf6d138f744469281446ab243ef38924": "/var/lib/containers/storage/overlay/948fcf93f8cb932f0f03fd52e3180a58627d547192ffe3b88e0013b98ddcd0d2/merged" +# } # } # ~~~ -method ListContainerMounts() -> (mounts: []string) +method ListContainerMounts() -> (mounts: [string]string) # MountContainer mounts a container by name or full/partial ID. Upon a successful mount, the destination # mount is returned as a string. @@ -1019,7 +1017,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() -> (pruned: []string) +method ImagesPrune(all: bool) -> (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 fd7f06b7c..ce773ee2e 100644 --- a/cmd/podman/version.go +++ b/cmd/podman/version.go @@ -57,7 +57,7 @@ var ( } versionFlags = []cli.Flag{ cli.StringFlag{ - Name: "format", + Name: "format, f", Usage: "Change the output format to JSON or a Go template", }, } diff --git a/completions/bash/podman b/completions/bash/podman index 08891563c..e0de4586c 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1219,6 +1219,7 @@ _podman_info() { --debug " local options_with_args=" + -f --format " @@ -1366,6 +1367,7 @@ _podman_inspect() { --type -t --size + -s " local all_options="$options_with_args $boolean_options" @@ -2036,6 +2038,7 @@ _podman_version() { " local options_with_args=" --format + -f " local all_options="$options_with_args $boolean_options" @@ -2459,6 +2462,8 @@ _podman_images_prune() { " local boolean_options=" + -a + --all -h --help " diff --git a/contrib/cirrus/build_vm_images.sh b/contrib/cirrus/build_vm_images.sh index ee45b1ead..6b86aa4d4 100755 --- a/contrib/cirrus/build_vm_images.sh +++ b/contrib/cirrus/build_vm_images.sh @@ -42,15 +42,33 @@ then fi fi -set -x - cd "$GOSRC/$PACKER_BASE" + +# Separate PR-produced images from those produced on master. +if [[ "${CIRRUS_BRANCH:-}" == "master" ]] +then + POST_MERGE_BUCKET_SUFFIX="-master" +else + POST_MERGE_BUCKET_SUFFIX="" +fi + make libpod_images \ PACKER_BUILDS=$PACKER_BUILDS \ PACKER_VER=$PACKER_VER \ GOSRC=$GOSRC \ SCRIPT_BASE=$SCRIPT_BASE \ PACKER_BASE=$PACKER_BASE \ + POST_MERGE_BUCKET_SUFFIX=$POST_MERGE_BUCKET_SUFFIX \ BUILT_IMAGE_SUFFIX=$BUILT_IMAGE_SUFFIX record_timestamp "cache-image build end" + +# When successful, upload manifest of produced images using a filename unique +# to this build. +URI="gs://packer-import${POST_MERGE_BUCKET_SUFFIX}/manifest${BUILT_IMAGE_SUFFIX}.json" +gsutil cp packer-manifest.json "$URI" + +echo "Finished." +echo "Any tarball URI's referenced above at at $URI" +echo "may be used to create VM images suitable for use in" +echo ".cirrus.yml as values for the 'image_name' keys." diff --git a/contrib/cirrus/integration_test.sh b/contrib/cirrus/integration_test.sh index 627864f47..58c8af289 100755 --- a/contrib/cirrus/integration_test.sh +++ b/contrib/cirrus/integration_test.sh @@ -17,9 +17,9 @@ set -x cd "$GOSRC" case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in ubuntu-18) - make install PREFIX=/usr ETCDIR=/etc "BUILDTAGS=$BUILDTAGS" - make test-binaries "BUILDTAGS=$BUILDTAGS" - SKIP_USERNS=1 make localintegration "BUILDTAGS=$BUILDTAGS" + make install PREFIX=/usr ETCDIR=/etc + make test-binaries + SKIP_USERNS=1 make localintegration ;; fedora-29) ;& # Continue to the next item fedora-28) ;& diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index 32b2c91a5..39e6c7699 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -199,7 +199,7 @@ install_runc_from_git(){ cd "$DEST" ooe.sh git fetch origin --tags ooe.sh git checkout -q "$RUNC_COMMIT" - ooe.sh make static BUILDTAGS="seccomp selinux" + ooe.sh make static BUILDTAGS="seccomp apparmor selinux" sudo install -m 755 runc /usr/bin/runc cd $wd } diff --git a/contrib/cirrus/packer/Makefile b/contrib/cirrus/packer/Makefile index 9bf27373e..0a783e979 100644 --- a/contrib/cirrus/packer/Makefile +++ b/contrib/cirrus/packer/Makefile @@ -3,7 +3,7 @@ # builder name(s) from applicable YAML file, # e.g for names see libpod_images.yml -PACKER_VER ?= 1.3.1 +PACKER_VER ?= 1.3.2 PACKER_DIST_FILENAME := packer_${PACKER_VER}_linux_amd64.zip # Only needed for libpod_base_images target @@ -11,6 +11,7 @@ TIMESTAMP := $(shell date +%s) GOSRC ?= $(shell realpath "./../../../") PACKER_BASE ?= contrib/cirrus/packer SCRIPT_BASE ?= contrib/cirrus +POST_MERGE_BUCKET_SUFFIX ?= # For debugging nested-virt, use #TTYDEV := $(shell tty) @@ -50,10 +51,6 @@ endif -var PACKER_BASE=$(PACKER_BASE) \ -var SCRIPT_BASE=$(SCRIPT_BASE) \ libpod_images.json - @echo "" - @echo "Finished. The images mentioned above, and in packer-manifest.json" - @echo "can be used in .cirrus.yml as values for the 'image_name' keys" - @echo "" cidata.ssh: ssh-keygen -f $@ -P "" -q @@ -100,9 +97,6 @@ endif -var RHEL_IMAGE_FILE=$(RHEL_IMAGE_FILE) \ -var RHEL_CSUM_FILE=$(RHEL_CSUM_FILE) \ -var 'RHSM_COMMAND=$(RHSM_COMMAND)' \ + -var POST_MERGE_BUCKET_SUFFIX=$(POST_MERGE_BUCKET_SUFFIX) \ -only $(PACKER_BUILDS) \ libpod_base_images.json - @echo "" - @echo "Finished. The images mentioned above, and in packer-manifest.json" - @echo "can be used in .cirrus.yml as values for the *_BASE_IMAGE keys." - @echo "" diff --git a/contrib/cirrus/packer/centos_setup.sh b/contrib/cirrus/packer/centos_setup.sh index a13050569..923f2563b 100644 --- a/contrib/cirrus/packer/centos_setup.sh +++ b/contrib/cirrus/packer/centos_setup.sh @@ -25,6 +25,7 @@ ooe.sh sudo yum -y update ooe.sh sudo yum -y install centos-release-scl epel-release ooe.sh sudo yum -y install \ + PyYAML \ atomic-registries \ btrfs-progs-devel \ bzip2 \ diff --git a/contrib/cirrus/packer/image-builder-image_base-setup.sh b/contrib/cirrus/packer/image-builder-image_base-setup.sh index b8e2824a7..8cf9fd8ab 100644 --- a/contrib/cirrus/packer/image-builder-image_base-setup.sh +++ b/contrib/cirrus/packer/image-builder-image_base-setup.sh @@ -45,10 +45,13 @@ ooe.sh sudo yum -y install \ qemu-kvm-tools \ qemu-user \ rsync \ + rng-tools \ unzip \ util-linux \ vim +sudo systemctl enable rngd + sudo ln -s /usr/libexec/qemu-kvm /usr/bin/ sudo tee /etc/modprobe.d/kvm-nested.conf <<EOF diff --git a/contrib/cirrus/packer/libpod_base_images.yml b/contrib/cirrus/packer/libpod_base_images.yml index 109b9b8d5..bf568b40e 100644 --- a/contrib/cirrus/packer/libpod_base_images.yml +++ b/contrib/cirrus/packer/libpod_base_images.yml @@ -105,7 +105,7 @@ builders: ssh_username: 'root' - <<: *nested_virt - name: 'prior_fedora' + name: 'prior-fedora' iso_url: '{{user `PRIOR_FEDORA_IMAGE_URL`}}' iso_checksum_url: '{{user `PRIOR_FEDORA_CSUM_URL`}}' @@ -161,7 +161,7 @@ provisioners: post-processors: - - type: "compress" - only: ['fedora', 'prior_fedora', 'fah', 'rhel'] + only: ['fedora', 'prior-fedora', 'fah', 'rhel'] output: '/tmp/{{build_name}}/disk.raw.tar.gz' format: '.tar.gz' compression_level: 9 @@ -171,12 +171,12 @@ post-processors: project_id: '{{user `GCP_PROJECT_ID`}}' account_file: '{{user `GOOGLE_APPLICATION_CREDENTIALS`}}' bucket: '{{user `XFERBUCKET`}}' - gcs_object_name: '{{build_name}}-{{user `TIMESTAMP`}}-{{uuid}}.tar.gz' + gcs_object_name: '{{build_name}}-{{user `TIMESTAMP`}}.tar.gz' image_name: "{{user `FEDORA_BASE_IMAGE_NAME`}}-{{user `TIMESTAMP`}}" image_description: 'Based on {{user `FEDORA_IMAGE_URL`}}' image_family: '{{user `FEDORA_BASE_IMAGE_NAME`}}' - <<: *gcp_import - only: ['prior_fedora'] + only: ['prior-fedora'] image_name: "{{user `PRIOR_FEDORA_BASE_IMAGE_NAME`}}-{{user `TIMESTAMP`}}" image_description: 'Based on {{user `PRIOR_FEDORA_IMAGE_URL`}}' image_family: '{{user `PRIOR_FEDORA_BASE_IMAGE_NAME`}}' diff --git a/contrib/cirrus/packer/libpod_images.yml b/contrib/cirrus/packer/libpod_images.yml index d31c11a8d..30ad0723a 100644 --- a/contrib/cirrus/packer/libpod_images.yml +++ b/contrib/cirrus/packer/libpod_images.yml @@ -29,6 +29,10 @@ variables: SERVICE_ACCOUNT: '{{env `SERVICE_ACCOUNT`}}' GOOGLE_APPLICATION_CREDENTIALS: '{{env `GOOGLE_APPLICATION_CREDENTIALS`}}' + # Used to separate images produced during PR testing from those + # produced from post-merge testing. Must be empty for PR testing. + POST_MERGE_BUCKET_SUFFIX: '' + # Don't leak sensitive values in error messages / output sensitive-variables: - 'GCE_SSH_USERNAME' @@ -84,13 +88,18 @@ provisioners: - type: 'shell' script: '{{user `GOSRC`}}/{{user `PACKER_BASE`}}/{{split build_name "-" 0}}_setup.sh' environment_vars: - - 'SCRIPT_BASE={{user `SCRIPT_BASE`}}' + - 'GOSRC=/tmp/libpod' - 'CNI_COMMIT={{user `CNI_COMMIT`}}' - 'FEDORA_CNI_COMMIT={{user `FEDORA_CNI_COMMIT`}}' - 'CRIO_COMMIT={{user `CRIO_COMMIT`}}' - 'CRIU_COMMIT={{user `CRIU_COMMIT`}}' - 'RUNC_COMMIT={{user `RUNC_COMMIT`}}' + - 'SCRIPT_BASE={{user `SCRIPT_BASE`}}' - 'RHSM_COMMAND={{user `RHSM_COMMAND`}}' post-processors: - - - type: 'manifest' + # Store VM disk in GCP storage, where it will expire based on a defined + # lifecycle. This prevents GCE from filling with disused images. + - - type: 'googlecompute-export' + paths: ['gs://packer-import{{user `POST_MERGE_BUCKET_SUFFIX`}}/{{build_name}}{{user `BUILT_IMAGE_SUFFIX`}}.tar.gz'] + - type: 'manifest' # writes packer-manifest.json diff --git a/contrib/cirrus/packer/rhel_base-setup.sh b/contrib/cirrus/packer/rhel_base-setup.sh index 8b2073d4f..fbf9f61af 100644 --- a/contrib/cirrus/packer/rhel_base-setup.sh +++ b/contrib/cirrus/packer/rhel_base-setup.sh @@ -16,6 +16,8 @@ req_env_var " install_ooe +rhsm_enable + echo "Setting up repos" # Frequently needed ooe.sh sudo yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm @@ -32,12 +34,15 @@ gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg EOM -rhsm_enable +echo "Updating all packages" +ooe.sh sudo yum -y update echo "Installing/removing packages" -ooe.sh sudo yum -y install google-compute-engine google-compute-engine-oslogin -ooe.sh sudo yum -y erase "cloud-init" "rh-amazon-rhui-client*" || true +ooe.sh sudo yum -y install rng-tools google-compute-engine google-compute-engine-oslogin + +echo "Enabling critical services" ooe.sh sudo systemctl enable \ + rngd \ google-accounts-daemon \ google-clock-skew-daemon \ google-instance-setup \ @@ -47,6 +52,29 @@ ooe.sh sudo systemctl enable \ rhel_exit_handler # release subscription! +echo "Configuring boot" +cat << "EOF" | sudo tee /etc/default/grub +GRUB_TIMEOUT=0 +GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)" +GRUB_DEFAULT=saved +GRUB_DISABLE_SUBMENU=true +GRUB_TERMINAL="serial console" +GRUB_SERIAL_COMMAND="serial --speed=38400" +GRUB_CMDLINE_LINUX="crashkernel=auto console=ttyS0,38400n8" +GRUB_DISABLE_RECOVERY="true" +EOF +sudo grub2-mkconfig -o /boot/grub2/grub.cfg + +echo "Configuring networking" +ooe.sh sudo nmcli connection modify 'System eth0' 802-3-ethernet.mtu 1460 +ooe.sh sudo nmcli connection modify 'System eth0' connection.autoconnect yes +ooe.sh sudo nmcli connection modify 'System eth0' connection.autoconnect-priority +ooe.sh sudo nmcli connection modify 'System eth0' ipv4.method auto +ooe.sh sudo nmcli connection modify 'System eth0' ipv4.dhcp-send-hostname yes +ooe.sh sudo nmcli connection modify 'System eth0' ipv4.dhcp-timeout 0 +ooe.sh sudo nmcli connection modify 'System eth0' ipv4.never-default no +ooe.sh /usr/bin/google_instance_setup + rh_finalize echo "SUCCESS!" diff --git a/contrib/cirrus/packer/rhel_setup.sh b/contrib/cirrus/packer/rhel_setup.sh index 99376fd65..ac6866a57 100644 --- a/contrib/cirrus/packer/rhel_setup.sh +++ b/contrib/cirrus/packer/rhel_setup.sh @@ -31,6 +31,7 @@ ooe.sh sudo subscription-manager repos \ ooe.sh sudo yum -y update ooe.sh sudo yum -y install \ + PyYAML \ atomic-registries \ btrfs-progs-devel \ bzip2 \ @@ -64,9 +65,11 @@ ooe.sh sudo yum -y install \ protobuf-python \ python \ python2-future \ + python2-pyyaml \ python34-dateutil \ python34-psutil \ python34-pytoml \ + python34-PyYAML \ runc \ skopeo-containers \ unzip \ diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index bcfe7e396..838f3c3f3 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -57,7 +57,6 @@ then ubuntu-18) # Always install runc on Ubuntu install_runc_from_git - envstr='export BUILDTAGS="seccomp $($GOSRC/hack/btrfs_tag.sh) $($GOSRC/hack/btrfs_installed_tag.sh) $($GOSRC/hack/ostree_tag.sh) varlink exclude_graphdriver_devicemapper"' ;; fedora-29) ;& # Continue to the next item fedora-28) @@ -67,11 +66,9 @@ then ;& # Continue to the next item centos-7) ;& rhel-7) - envstr='unset BUILDTAGS' # Use default from Makefile ;; *) bad_os_id_ver ;; esac - X=$(echo "$envstr" | tee -a "$HOME/$ENVLIB") && eval "$X" && echo "$X" # Do the same for golang env. vars go env | while read envline diff --git a/contrib/cirrus/system_test.sh b/contrib/cirrus/system_test.sh index 66974f8c6..cb179407a 100755 --- a/contrib/cirrus/system_test.sh +++ b/contrib/cirrus/system_test.sh @@ -15,12 +15,9 @@ set -x cd "$GOSRC" case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in - ubuntu-18) - make install.tools "BUILDTAGS=$BUILDTAGS" - make "BUILDTAGS=$BUILDTAGS" - make test-binaries "BUILDTAGS=$BUILDTAGS" - ;; + ubuntu-18) ;& # Continue to the next item fedora-28) ;& + fedora-29) ;& centos-7) ;& rhel-7) make install.tools diff --git a/contrib/cirrus/unit_test.sh b/contrib/cirrus/unit_test.sh index 15403b7a7..fd9e82509 100755 --- a/contrib/cirrus/unit_test.sh +++ b/contrib/cirrus/unit_test.sh @@ -16,12 +16,8 @@ clean_env set -x cd "$GOSRC" case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in - ubuntu-18) - make install.tools "BUILDTAGS=$BUILDTAGS" - make localunit "BUILDTAGS=$BUILDTAGS" - make "BUILDTAGS=$BUILDTAGS" - ;; - fedora-29) ;& # Continue to the next item + ubuntu-18) ;& # Continue to the next item + fedora-29) ;& fedora-28) ;& centos-7) ;& rhel-7) diff --git a/docs/podman-image-prune.1.md b/docs/podman-image-prune.1.md index db76b26e0..df912c380 100644 --- a/docs/podman-image-prune.1.md +++ b/docs/podman-image-prune.1.md @@ -6,23 +6,38 @@ podman-image-prune - Remove all unused images # SYNOPSIS **podman image prune** +[**-a**|**--all**] [**-h**|**--help**] # DESCRIPTION -**podman image prune** removes all unused images from local storage. An unused image -is defined as an image that does not have any containers based on it. +**podman image prune** removes all dangling images from local storage. With the `all` option, +you can delete all unused images. Unused images are dangling images as well as any image that +does not have any containers based on it. + +## OPTIONS +**--all, -a** + +Remove dangling images and images that have no associated containers. ## Examples ## -Remove all unused images from local storage +Remove all dangling images from local storage ``` $ sudo podman image prune f3e20dc537fb04cb51672a5cb6fdf2292e61d411315549391a0d1f64e4e3097e 324a7a3b2e0135f4226ffdd473e4099fd9e477a74230cdc35de69e84c0f9d907 +``` + +Remove all unused images from local storage +``` +$ sudo podman image prune -a +f3e20dc537fb04cb51672a5cb6fdf2292e61d411315549391a0d1f64e4e3097e +324a7a3b2e0135f4226ffdd473e4099fd9e477a74230cdc35de69e84c0f9d907 6125002719feb1ddf3030acab1df6156da7ce0e78e571e9b6e9c250424d6220c 91e732da5657264c6f4641b8d0c4001c218ae6c1adb9dcef33ad00cafd37d8b6 e4e5109420323221f170627c138817770fb64832da7d8fe2babd863148287fca 77a57fa8285e9656dbb7b23d9efa837a106957409ddd702f995605af27a45ebe + ``` ## SEE ALSO diff --git a/docs/podman-image-trust.1.md b/docs/podman-image-trust.1.md index 3b6564315..668fee0f3 100644 --- a/docs/podman-image-trust.1.md +++ b/docs/podman-image-trust.1.md @@ -9,22 +9,24 @@ podman\-trust - Manage container registry image trust policy [**-h**|**--help**] [**-j**|**--json**] [**--raw**] -[**-f**|**--pubkeysfile** KEY1 [**f**|**--pubkeysfile** KEY2,...]] +[**-f**|**--pubkeysfile** KEY1 [**-f**|**--pubkeysfile** KEY2,...]] [**-t**|**--type** signedBy|accept|reject] REGISTRY[/REPOSITORY] # DESCRIPTION -Manages the trust policy of the host system. Trust policy describes -a registry scope (registry and/or repository) that must be signed by public keys. Trust -is defined in **/etc/containers/policy.json**. Trust is enforced when a user attempts to pull -an image from a registry. +Manages which registries you trust as a source of container images based on its location. The location is determined by the transport and the registry host of the image. Using this container image `docker://docker.io/library/busybox` as an example, `docker` is the transport and `docker.io` is the registry host. -Trust scope is evaluated by most specific to least specific. In other words, policy may -be defined for an entire registry, but refined for a particular repository in that -registry. See below for examples. +The trust policy describes a registry scope (registry and/or repository). This trust can use public keys for signed images. + +Trust is defined in **/etc/containers/policy.json** and is enforced when a user attempts to pull an image from a registry that is managed by policy.json. + +The scope of the trust is evaluated from most specific to the least specific. In other words, a policy may be defined for an entire registry. Or it could be defined for a particular repository in that registry. Or it could be defined down to a specific signed image inside of the registry. See below for examples. + +Trust **type** provides a way to: + +Whitelist ("accept") or +Blacklist ("reject") registries. -Trust **type** provides a way to whitelist ("accept") or blacklist -("reject") registries. Trust may be updated using the command **podman image trust set** for an existing trust scope. @@ -34,10 +36,10 @@ Trust may be updated using the command **podman image trust set** for an existin **-f** **--pubkeysfile** A path to an exported public key on the local system. Key paths - will be referenced in policy.json. Any path may be used but path - **/etc/pki/containers** is recommended. Option may be used multiple times to - require an image be sigend by multiple keys. One of **--pubkeys** or - **--pubkeysfile** is required for **signedBy** type. + will be referenced in policy.json. Any path may be used but the path + **/etc/pki/containers** is recommended. Options may be used multiple times to + require an image be signed by multiple keys. One of **--pubkeys** or + **--pubkeysfile** is required for the **signedBy** type. **-t** **--type** The trust type for this policy entry. Accepted values: @@ -59,23 +61,30 @@ Trust may be updated using the command **podman image trust set** for an existin Accept all unsigned images from a registry - podman image trust set --type accept docker.io + sudo podman image trust set --type accept docker.io Modify default trust policy - podman image trust set -t reject default + sudo podman image trust set -t reject default Display system trust policy - podman image trust show + sudo podman image trust show Display trust policy file - podman image trust show --raw + sudo podman image trust show --raw Display trust as JSON - podman image trust show --json + sudo podman image trust show --json + +# SEE ALSO + +policy-json(5) # HISTORY + +January 2019, updated by Tom Sweeney (tsweeney at redhat dot com) + December 2018, originally compiled by Qi Wang (qiwan at redhat dot com) diff --git a/docs/podman-info.1.md b/docs/podman-info.1.md index 836a2c420..d3a0658c9 100644 --- a/docs/podman-info.1.md +++ b/docs/podman-info.1.md @@ -19,7 +19,7 @@ Displays information pertinent to the host, current storage stats, configured co Show additional information -**--format** +**--format, -f** Change output format to "json" or a Go template. diff --git a/docs/podman-inspect.1.md b/docs/podman-inspect.1.md index 7bdbcc662..b01bc0f4e 100644 --- a/docs/podman-inspect.1.md +++ b/docs/podman-inspect.1.md @@ -27,7 +27,7 @@ The keys of the returned JSON can be used as the values for the --format flag (s Instead of providing the container name or ID, use the last created container. If you use methods other than Podman to run containers such as CRI-O, the last started container could be from either of those methods. -**--size** +**--size, -s** Display the total file size if the type is a container diff --git a/docs/podman-version.1.md b/docs/podman-version.1.md index 749a33afd..171096587 100644 --- a/docs/podman-version.1.md +++ b/docs/podman-version.1.md @@ -16,7 +16,7 @@ OS, and Architecture. Print usage statement -**--format** +**--format**, **-f** Change output format to "json" or a Go template. diff --git a/docs/podman.1.md b/docs/podman.1.md index 74e700fac..6200a07f0 100644 --- a/docs/podman.1.md +++ b/docs/podman.1.md @@ -1,19 +1,21 @@ % podman(1) ## NAME -podman - Simple management tool for containers and images +podman - Simple management tool for pods, containers and images ## SYNOPSIS **podman** [*options*] *command* ## DESCRIPTION -podman is a simple client only tool to help with debugging issues when daemons -such as CRI runtime and the kubelet are not responding or failing. A shared API -layer could be created to share code between the daemon and podman. podman does not -require any daemon running. podman utilizes the same underlying components that -crio uses i.e. containers/image, container/storage, oci-runtime-tool/generate, -runc or any other OCI compatible runtime. podman shares state with crio and so -has the capability to debug pods/images created by crio. +Podman (Pod Manager) is a fully featured container engine that is a simple daemonless tool. +Podman provides a Docker-CLI comparable command line that eases the transition from other +container engines and allows the management of pods, containers and images. Simply put: `alias docker=podman`. +Most Podman commands can be run as a regular user, without requiring additional +privileges. + +Podman uses Buildah(1) internally to create container images. Both tools share image +(not container) storage, hence each can use or manipulate images (but not containers) +created by the other. **podman [GLOBAL OPTIONS]** @@ -220,7 +222,7 @@ Images are pulled under `XDG_DATA_HOME` when specified, otherwise in the home di Currently the slirp4netns package is required to be installed to create a network device, otherwise rootless containers need to run in the network namespace of the host. ## SEE ALSO -`containers-mounts.conf(5)`, `containers-registries.conf(5)`, `containers-storage.conf(5)`, `crio(8)`, `libpod.conf(5)`, `oci-hooks(5)`, `policy.json(5)`, `subuid(5)`, `subgid(5)`, `slirp4netns(1)` +`containers-mounts.conf(5)`, `containers-registries.conf(5)`, `containers-storage.conf(5)`, `buildah(1)`, `crio(8)`, `libpod.conf(5)`, `oci-hooks(5)`, `policy.json(5)`, `subuid(5)`, `subgid(5)`, `slirp4netns(1)` ## HISTORY Dec 2016, Originally compiled by Dan Walsh <dwalsh@redhat.com> diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md index 2b938319f..5017e61cd 100644 --- a/docs/tutorials/podman_tutorial.md +++ b/docs/tutorials/podman_tutorial.md @@ -14,6 +14,17 @@ Fedora 27 and later provide Podman via the package manager. sudo dnf install -y podman ``` +*Optional*: If you've already installed podman on Fedora and you're feeling +adventerous, you can test the very latest podman in Fedora's `updates-testing` +repository before it goes out to all Fedora users. +```console +sudo yum distro-sync --enablerepo=updates-testing podman +``` + +If you use a newer podman package from Fedora's `updates-testing`, we would +appreciate your `+1` feedback in [Bodhi, Fedora's update management +system](https://bodhi.fedoraproject.org/updates/?packages=podman). + ## Install Podman on Fedora from Source Many of the basic components to run Podman are readily available from the Fedora RPM repositories. In this section, we will help you install all the runtime and build dependencies for Podman, diff --git a/install.md b/install.md index c5268c04d..4f075332d 100644 --- a/install.md +++ b/install.md @@ -4,7 +4,9 @@ #### [Arch Linux](https://www.archlinux.org) -Podman is available in the AUR through the [libpod package](https://aur.archlinux.org/packages/libpod/) +```bash +sudo pacman -S podman +``` #### [Fedora](https://www.fedoraproject.org), [CentOS](https://www.centos.org) diff --git a/libpod/adapter/runtime.go b/libpod/adapter/runtime.go index 1f3599082..f4961437e 100644 --- a/libpod/adapter/runtime.go +++ b/libpod/adapter/runtime.go @@ -99,3 +99,8 @@ func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { } return &Container{ctr}, nil } + +// PruneImages is wrapper into PruneImages within the image pkg +func (r *LocalRuntime) PruneImages(all bool) ([]string, error) { + return r.ImageRuntime().PruneImages(all) +} diff --git a/libpod/adapter/runtime_remote.go b/libpod/adapter/runtime_remote.go index 7189348bc..f184ce0a9 100644 --- a/libpod/adapter/runtime_remote.go +++ b/libpod/adapter/runtime_remote.go @@ -320,3 +320,6 @@ func (r *LocalRuntime) Config(name string) *libpod.ContainerConfig { return &data } +func (r *LocalRuntime) PruneImages(all bool) ([]string, error) { + return iopodman.ImagesPrune().Call(r.Conn, all) +} diff --git a/libpod/image/image.go b/libpod/image/image.go index ea326d820..a32336f49 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -823,9 +823,9 @@ func (i *Image) Inspect(ctx context.Context) (*inspect.ImageData, error) { return nil, err } - var repoDigests []string - for _, name := range i.Names() { - repoDigests = append(repoDigests, strings.SplitN(name, ":", 2)[0]+"@"+i.Digest().String()) + repoDigests, err := i.RepoDigests() + if err != nil { + return nil, err } driver, err := i.DriverData() diff --git a/libpod/image/prune.go b/libpod/image/prune.go index 6a1f160d5..8602c222c 100644 --- a/libpod/image/prune.go +++ b/libpod/image/prune.go @@ -1,9 +1,11 @@ package image +import "github.com/pkg/errors" + // GetPruneImages returns a slice of images that have no names/unused -func (ir *Runtime) GetPruneImages() ([]*Image, error) { +func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) { var ( - unamedImages []*Image + pruneImages []*Image ) allImages, err := ir.GetImages() if err != nil { @@ -11,16 +13,35 @@ func (ir *Runtime) GetPruneImages() ([]*Image, error) { } for _, i := range allImages { if len(i.Names()) == 0 { - unamedImages = append(unamedImages, i) + pruneImages = append(pruneImages, i) continue } - containers, err := i.Containers() - if err != nil { - return nil, err + if all { + containers, err := i.Containers() + if err != nil { + return nil, err + } + if len(containers) < 1 { + pruneImages = append(pruneImages, i) + } } - if len(containers) < 1 { - unamedImages = append(unamedImages, i) + } + return pruneImages, nil +} + +// PruneImages prunes dangling and optionally all unused images from the local +// image store +func (ir *Runtime) PruneImages(all bool) ([]string, error) { + var prunedCids []string + pruneImages, err := ir.GetPruneImages(all) + if err != nil { + return nil, errors.Wrap(err, "unable to get images to prune") + } + for _, p := range pruneImages { + if err := p.Remove(true); err != nil { + return nil, errors.Wrap(err, "failed to prune image") } + prunedCids = append(prunedCids, p.ID()) } - return unamedImages, nil + return prunedCids, nil } diff --git a/pkg/apparmor/apparmor_linux.go b/pkg/apparmor/apparmor_linux.go index 0787b3fa5..2c5022c1f 100644 --- a/pkg/apparmor/apparmor_linux.go +++ b/pkg/apparmor/apparmor_linux.go @@ -214,8 +214,15 @@ func CheckProfileAndLoadDefault(name string) (string, error) { return name, nil } - if name != "" && rootless.IsRootless() { - return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) + // AppArmor is not supported in rootless mode as it requires root + // privileges. Return an error in case a specific profile is specified. + if rootless.IsRootless() { + if name != "" { + return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name) + } else { + logrus.Debug("skipping loading default AppArmor profile (rootless mode)") + return "", nil + } } if name != "" && !runcaa.IsEnabled() { @@ -230,7 +237,7 @@ func CheckProfileAndLoadDefault(name string) (string, error) { return "", err } if !isLoaded { - return "", fmt.Errorf("AppArmor profile %q specified but not loaded") + return "", fmt.Errorf("AppArmor profile %q specified but not loaded", name) } return name, nil } diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go index 31e41903e..9a75474ae 100644 --- a/pkg/trust/trust.go +++ b/pkg/trust/trust.go @@ -175,43 +175,30 @@ func CreateTmpFile(dir, pattern string, content []byte) (string, error) { return tmpfile.Name(), nil } -func getGPGIdFromKeyPath(path []string) []string { - var uids []string - for _, k := range path { - cmd := exec.Command("gpg2", "--with-colons", k) - results, err := cmd.Output() - if err != nil { - logrus.Warnf("error get key identity: %s", err) - continue - } - uids = append(uids, parseUids(results)...) +// GetGPGIdFromKeyPath return user keyring from key path +func GetGPGIdFromKeyPath(path string) []string { + cmd := exec.Command("gpg2", "--with-colons", path) + results, err := cmd.Output() + if err != nil { + logrus.Errorf("error getting key identity: %s", err) + return nil } - return uids + return parseUids(results) } -func getGPGIdFromKeyData(keys []string) []string { - var uids []string - for _, k := range keys { - decodeKey, err := base64.StdEncoding.DecodeString(k) - if err != nil { - logrus.Warnf("error decoding key data") - continue - } - tmpfileName, err := CreateTmpFile("", "", decodeKey) - if err != nil { - logrus.Warnf("error creating key date temp file %s", err) - } - defer os.Remove(tmpfileName) - k = tmpfileName - cmd := exec.Command("gpg2", "--with-colons", k) - results, err := cmd.Output() - if err != nil { - logrus.Warnf("error get key identity: %s", err) - continue - } - uids = append(uids, parseUids(results)...) +// GetGPGIdFromKeyData return user keyring from keydata +func GetGPGIdFromKeyData(key string) []string { + decodeKey, err := base64.StdEncoding.DecodeString(key) + if err != nil { + logrus.Errorf("%s, error decoding key data", err) + return nil } - return uids + tmpfileName, err := CreateTmpFile("", "", decodeKey) + if err != nil { + logrus.Errorf("error creating key date temp file %s", err) + } + defer os.Remove(tmpfileName) + return GetGPGIdFromKeyPath(tmpfileName) } func parseUids(colonDelimitKeys []byte) []string { @@ -234,68 +221,15 @@ func parseUids(colonDelimitKeys []byte) []string { return parseduids } -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 -} - -// GetPolicy return the struct to show policy.json in json format and a map (reponame, ShowOutput) pair for image trust show command -func GetPolicy(policyContentStruct PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, map[string]ShowOutput, error) { - registryConfigs, err := LoadAndMergeConfig(systemRegistriesDirPath) +// GetPolicy parse policy.json into PolicyContent struct +func GetPolicy(policyPath string) (PolicyContent, error) { + var policyContentStruct PolicyContent + policyContent, err := ioutil.ReadFile(policyPath) if err != nil { - return nil, nil, err + return policyContentStruct, errors.Wrapf(err, "unable to read policy file %s", policyPath) } - - trustShowOutputMap := make(map[string]ShowOutput) - policyJSON := make(map[string]map[string]interface{}) - if len(policyContentStruct.Default) > 0 { - policyJSON["* (default)"] = make(map[string]interface{}) - policyJSON["* (default)"]["type"] = policyContentStruct.Default[0].Type - - var defaultPolicyStruct ShowOutput - defaultPolicyStruct.Repo = "default" - defaultPolicyStruct.Trusttype = trustTypeDescription(policyContentStruct.Default[0].Type) - trustShowOutputMap["* (default)"] = defaultPolicyStruct - } - for transname, transval := range policyContentStruct.Transports { - for repo, repoval := range transval { - tempTrustShowOutput := ShowOutput{ - Repo: repo, - Trusttype: repoval[0].Type, - } - policyJSON[repo] = make(map[string]interface{}) - policyJSON[repo]["type"] = repoval[0].Type - policyJSON[repo]["transport"] = transname - keyDataArr := []string{} - keyPathArr := []string{} - keyarr := []string{} - for _, repoele := range repoval { - if len(repoele.KeyPath) > 0 { - keyarr = append(keyarr, repoele.KeyPath) - keyPathArr = append(keyPathArr, repoele.KeyPath) - } - if len(repoele.KeyData) > 0 { - keyarr = append(keyarr, string(repoele.KeyData)) - keyDataArr = append(keyDataArr, string(repoele.KeyData)) - } - } - policyJSON[repo]["keys"] = keyarr - uids := append(getGPGIdFromKeyPath(keyPathArr), getGPGIdFromKeyData(keyDataArr)...) - tempTrustShowOutput.GPGid = strings.Join(uids, ",") - - policyJSON[repo]["sigstore"] = "" - registryNamespace := HaveMatchRegistry(repo, registryConfigs) - if registryNamespace != nil { - policyJSON[repo]["sigstore"] = registryNamespace.SigStore - tempTrustShowOutput.Sigstore = registryNamespace.SigStore - } - trustShowOutputMap[repo] = tempTrustShowOutput - } + if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { + return policyContentStruct, errors.Wrapf(err, "could not parse trust policies") } - return policyJSON, trustShowOutputMap, nil + return policyContentStruct, nil } diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 744f031c0..d6a9b7301 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -627,19 +627,10 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman. } // ImagesPrune .... -func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall) error { - var ( - pruned []string - ) - pruneImages, err := i.Runtime.ImageRuntime().GetPruneImages() +func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool) error { + prunedImages, err := i.Runtime.ImageRuntime().PruneImages(all) if err != nil { - return err - } - for _, i := range pruneImages { - if err := i.Remove(true); err != nil { - return call.ReplyErrorOccurred(err.Error()) - } - pruned = append(pruned, i.ID()) + return call.ReplyErrorOccurred(err.Error()) } - return call.ReplyImagesPrune(pruned) + return call.ReplyImagesPrune(prunedImages) } diff --git a/pkg/varlinkapi/mount.go b/pkg/varlinkapi/mount.go index 84e6b2709..3b4fe87e3 100644 --- a/pkg/varlinkapi/mount.go +++ b/pkg/varlinkapi/mount.go @@ -6,7 +6,7 @@ import ( // ListContainerMounts ... func (i *LibpodAPI) ListContainerMounts(call iopodman.VarlinkCall) error { - var mounts []string + mounts := make(map[string]string) allContainers, err := i.Runtime.GetAllContainers() if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -17,7 +17,7 @@ func (i *LibpodAPI) ListContainerMounts(call iopodman.VarlinkCall) error { return call.ReplyErrorOccurred(err.Error()) } if mounted { - mounts = append(mounts, mountPoint) + mounts[container.ID()] = mountPoint } } return call.ReplyListContainerMounts(mounts) diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 50a279232..81fb82b20 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -1,5 +1,3 @@ -// +build !remoteclient - package integration import ( @@ -41,6 +39,7 @@ var _ = Describe("Podman rm", func() { }) It("podman container prune containers", func() { + SkipIfRemote() top := podmanTest.RunTopContainer("") top.WaitWithDefaultTimeout() Expect(top.ExitCode()).To(Equal(0)) @@ -57,6 +56,7 @@ var _ = Describe("Podman rm", func() { }) It("podman image prune none images", func() { + SkipIfRemote() podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") none := podmanTest.Podman([]string{"images", "-a"}) @@ -74,10 +74,11 @@ var _ = Describe("Podman rm", func() { Expect(none.ExitCode()).To(Equal(0)) hasNoneAfter, _ := after.GrepString("<none>") Expect(hasNoneAfter).To(BeFalse()) + Expect(len(after.OutputToStringArray()) > 1).To(BeTrue()) }) It("podman image prune unused images", func() { - prune := podmanTest.Podman([]string{"image", "prune"}) + prune := podmanTest.Podman([]string{"image", "prune", "-a"}) prune.WaitWithDefaultTimeout() Expect(prune.ExitCode()).To(Equal(0)) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index bff2427d5..9b1c55bb4 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -7,6 +7,7 @@ import ( "os" "regexp" "sort" + "strings" . "github.com/containers/libpod/test/utils" "github.com/docker/go-units" @@ -148,10 +149,12 @@ var _ = Describe("Podman ps", func() { _, ec, _ := podmanTest.RunLsContainer("test1") Expect(ec).To(Equal(0)) - result := podmanTest.Podman([]string{"ps", "-a", "--format", "\"table {{.ID}} {{.Image}} {{.Labels}}\""}) + result := podmanTest.Podman([]string{"ps", "-a", "--format", "table {{.ID}} {{.Image}} {{.Labels}}"}) result.WaitWithDefaultTimeout() + Expect(strings.Contains(result.OutputToStringArray()[0], "table")).To(BeFalse()) + Expect(strings.Contains(result.OutputToStringArray()[0], "ID")).To(BeTrue()) + Expect(strings.Contains(result.OutputToStringArray()[1], "alpine:latest")).To(BeTrue()) Expect(result.ExitCode()).To(Equal(0)) - Expect(result.IsJSONOutputValid()).To(BeTrue()) }) It("podman ps ancestor filter flag", func() { diff --git a/vendor.conf b/vendor.conf index 76c2b8810..4a62c6be7 100644 --- a/vendor.conf +++ b/vendor.conf @@ -15,7 +15,7 @@ github.com/containerd/cgroups 39b18af02c4120960f517a3a4c2588fabb61d02c github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d github.com/containernetworking/cni v0.7.0-alpha1 github.com/containernetworking/plugins v0.7.4 -github.com/containers/image v1.3 +github.com/containers/image 67b1f789f2ce8a3654592a582fff26c396326236 github.com/containers/storage v1.8 github.com/containers/psgo v1.1 github.com/coreos/go-systemd v14 diff --git a/vendor/github.com/containers/image/copy/copy.go b/vendor/github.com/containers/image/copy/copy.go index 89c7e580f..e783416ad 100644 --- a/vendor/github.com/containers/image/copy/copy.go +++ b/vendor/github.com/containers/image/copy/copy.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/ioutil" + "os" "reflect" "runtime" "strings" @@ -22,6 +23,7 @@ import ( "github.com/opencontainers/go-digest" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh/terminal" "golang.org/x/sync/semaphore" pb "gopkg.in/cheggaaa/pb.v1" ) @@ -84,6 +86,7 @@ type copier struct { dest types.ImageDestination rawSource types.ImageSource reportWriter io.Writer + progressOutput io.Writer progressInterval time.Duration progress chan types.ProgressProperties blobInfoCache types.BlobInfoCache @@ -152,11 +155,19 @@ func Image(ctx context.Context, policyContext *signature.PolicyContext, destRef, } }() + // If reportWriter is not a TTY (e.g., when piping to a file), do not + // print the progress bars to avoid long and hard to parse output. + // createProgressBar() will print a single line instead. + progressOutput := reportWriter + if !isTTY(reportWriter) { + progressOutput = ioutil.Discard + } copyInParallel := dest.HasThreadSafePutBlob() && rawSource.HasThreadSafeGetBlob() c := &copier{ dest: dest, rawSource: rawSource, reportWriter: reportWriter, + progressOutput: progressOutput, progressInterval: options.ProgressInterval, progress: options.Progress, copyInParallel: copyInParallel, @@ -394,17 +405,30 @@ func shortDigest(d digest.Digest) string { return d.Encoded()[:12] } -// createProgressBar creates a pb.ProgressBar. -func createProgressBar(srcInfo types.BlobInfo, kind string, writer io.Writer) *pb.ProgressBar { +// createProgressBar creates a pb.ProgressBar. Note that if the copier's +// reportWriter is ioutil.Discard, the progress bar's output will be discarded +// and a single line will be printed instead. +func (c *copier) createProgressBar(srcInfo types.BlobInfo, kind string) *pb.ProgressBar { bar := pb.New(int(srcInfo.Size)).SetUnits(pb.U_BYTES) bar.SetMaxWidth(80) bar.ShowTimeLeft = false bar.ShowPercent = false bar.Prefix(fmt.Sprintf("Copying %s %s:", kind, shortDigest(srcInfo.Digest))) - bar.Output = writer + bar.Output = c.progressOutput + if bar.Output == ioutil.Discard { + c.Printf("Copying %s %s\n", kind, srcInfo.Digest) + } return bar } +// isTTY returns true if the io.Writer is a file and a tty. +func isTTY(w io.Writer) bool { + if f, ok := w.(*os.File); ok { + return terminal.IsTerminal(int(f.Fd())) + } + return false +} + // copyLayers copies layers from ic.src/ic.c.rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest. func (ic *imageCopier) copyLayers(ctx context.Context) error { srcInfos := ic.src.LayerInfos() @@ -456,7 +480,7 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error { bar.Finish() } else { cld.destInfo = srcLayer - logrus.Debugf("Skipping foreign layer %q copy to %s\n", cld.destInfo.Digest, ic.c.dest.Reference().Transport().Name()) + logrus.Debugf("Skipping foreign layer %q copy to %s", cld.destInfo.Digest, ic.c.dest.Reference().Transport().Name()) bar.Prefix(fmt.Sprintf("Skipping blob %s (foreign layer):", shortDigest(srcLayer.Digest))) bar.Add64(bar.Total) bar.Finish() @@ -469,12 +493,13 @@ func (ic *imageCopier) copyLayers(ctx context.Context) error { progressBars := make([]*pb.ProgressBar, numLayers) for i, srcInfo := range srcInfos { - bar := createProgressBar(srcInfo, "blob", nil) + bar := ic.c.createProgressBar(srcInfo, "blob") progressBars[i] = bar } progressPool := pb.NewPool(progressBars...) - progressPool.Output = ic.c.reportWriter + progressPool.Output = ic.c.progressOutput + if err := progressPool.Start(); err != nil { return errors.Wrapf(err, "error creating progress-bar pool") } @@ -568,7 +593,7 @@ func (c *copier) copyConfig(ctx context.Context, src types.Image) error { if err != nil { return errors.Wrapf(err, "Error reading config blob %s", srcInfo.Digest) } - bar := createProgressBar(srcInfo, "config", c.reportWriter) + bar := c.createProgressBar(srcInfo, "config") defer bar.Finish() bar.Start() destInfo, err := c.copyBlobFromStream(ctx, bytes.NewReader(configBlob), srcInfo, nil, false, true, bar) diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go index 23d2ac70f..43eb22ba2 100644 --- a/vendor/github.com/containers/image/docker/docker_client.go +++ b/vendor/github.com/containers/image/docker/docker_client.go @@ -91,7 +91,6 @@ type dockerClient struct { password string signatureBase signatureStorageBase scope authScope - extraScope *authScope // If non-nil, a temporary extra token scope (necessary for mounting from another repo) // The following members are detected registry properties: // They are set after a successful detectProperties(), and never change afterwards. scheme string // Empty value also used to indicate detectProperties() has not yet succeeded. @@ -282,7 +281,7 @@ func CheckAuth(ctx context.Context, sys *types.SystemContext, username, password client.username = username client.password = password - resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth) + resp, err := client.makeRequest(ctx, "GET", "/v2/", nil, nil, v2Auth, nil) if err != nil { return err } @@ -362,8 +361,8 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima q.Set("n", strconv.Itoa(limit)) u.RawQuery = q.Encode() - logrus.Debugf("trying to talk to v1 search endpoint\n") - resp, err := client.makeRequest(ctx, "GET", u.String(), nil, nil, noAuth) + logrus.Debugf("trying to talk to v1 search endpoint") + resp, err := client.makeRequest(ctx, "GET", u.String(), nil, nil, noAuth, nil) if err != nil { logrus.Debugf("error getting search results from v1 endpoint %q: %v", registry, err) } else { @@ -379,8 +378,8 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima } } - logrus.Debugf("trying to talk to v2 search endpoint\n") - resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil, v2Auth) + logrus.Debugf("trying to talk to v2 search endpoint") + resp, err := client.makeRequest(ctx, "GET", "/v2/_catalog", nil, nil, v2Auth, nil) if err != nil { logrus.Debugf("error getting search results from v2 endpoint %q: %v", registry, err) } else { @@ -409,20 +408,20 @@ func SearchRegistry(ctx context.Context, sys *types.SystemContext, registry, ima // makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/. -func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader, auth sendAuth) (*http.Response, error) { +func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader, auth sendAuth, extraScope *authScope) (*http.Response, error) { if err := c.detectProperties(ctx); err != nil { return nil, err } url := fmt.Sprintf("%s://%s%s", c.scheme, c.registry, path) - return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth) + return c.makeRequestToResolvedURL(ctx, method, url, headers, stream, -1, auth, extraScope) } // makeRequestToResolvedURL creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // streamLen, if not -1, specifies the length of the data expected on stream. // makeRequest should generally be preferred. // TODO(runcom): too many arguments here, use a struct -func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth) (*http.Response, error) { +func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url string, headers map[string][]string, stream io.Reader, streamLen int64, auth sendAuth, extraScope *authScope) (*http.Response, error) { req, err := http.NewRequest(method, url, stream) if err != nil { return nil, err @@ -441,7 +440,7 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url req.Header.Add("User-Agent", c.sys.DockerRegistryUserAgent) } if auth == v2Auth { - if err := c.setupRequestAuth(req); err != nil { + if err := c.setupRequestAuth(req, extraScope); err != nil { return nil, err } } @@ -460,7 +459,7 @@ func (c *dockerClient) makeRequestToResolvedURL(ctx context.Context, method, url // 2) gcr.io is sending 401 without a WWW-Authenticate header in the real request // // debugging: https://github.com/containers/image/pull/211#issuecomment-273426236 and follows up -func (c *dockerClient) setupRequestAuth(req *http.Request) error { +func (c *dockerClient) setupRequestAuth(req *http.Request, extraScope *authScope) error { if len(c.challenges) == 0 { return nil } @@ -474,10 +473,10 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error { case "bearer": cacheKey := "" scopes := []authScope{c.scope} - if c.extraScope != nil { + if extraScope != nil { // Using ':' as a separator here is unambiguous because getBearerToken below uses the same separator when formatting a remote request (and because repository names can't contain colons). - cacheKey = fmt.Sprintf("%s:%s", c.extraScope.remoteName, c.extraScope.actions) - scopes = append(scopes, *c.extraScope) + cacheKey = fmt.Sprintf("%s:%s", extraScope.remoteName, extraScope.actions) + scopes = append(scopes, *extraScope) } var token bearerToken t, inCache := c.tokenCache.Load(cacheKey) @@ -564,7 +563,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { ping := func(scheme string) error { url := fmt.Sprintf(resolvedPingV2URL, scheme, c.registry) - resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth) + resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil) if err != nil { logrus.Debugf("Ping %s err %s (%#v)", url, err.Error(), err) return err @@ -591,7 +590,7 @@ func (c *dockerClient) detectPropertiesHelper(ctx context.Context) error { // best effort to understand if we're talking to a V1 registry pingV1 := func(scheme string) bool { url := fmt.Sprintf(resolvedPingV1URL, scheme, c.registry) - resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth) + resp, err := c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil) if err != nil { logrus.Debugf("Ping %s err %s (%#v)", url, err.Error(), err) return false @@ -625,7 +624,7 @@ func (c *dockerClient) detectProperties(ctx context.Context) error { // using the original data structures. func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerReference, manifestDigest digest.Digest) (*extensionSignatureList, error) { path := fmt.Sprintf(extensionsSignaturePath, reference.Path(ref.ref), manifestDigest) - res, err := c.makeRequest(ctx, "GET", path, nil, nil, v2Auth) + res, err := c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/docker/docker_image.go b/vendor/github.com/containers/image/docker/docker_image.go index 2ab95f329..530c7513e 100644 --- a/vendor/github.com/containers/image/docker/docker_image.go +++ b/vendor/github.com/containers/image/docker/docker_image.go @@ -66,7 +66,7 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types. tags := make([]string, 0) for { - res, err := client.makeRequest(ctx, "GET", path, nil, nil, v2Auth) + res, err := client.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/docker/docker_image_dest.go b/vendor/github.com/containers/image/docker/docker_image_dest.go index 973d160d0..e9882c024 100644 --- a/vendor/github.com/containers/image/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/docker/docker_image_dest.go @@ -113,7 +113,7 @@ func (c *sizeCounter) Write(p []byte) (n int, err error) { // HasThreadSafePutBlob indicates whether PutBlob can be executed concurrently. func (d *dockerImageDestination) HasThreadSafePutBlob() bool { - return false + return true } // PutBlob writes contents of stream and returns data representing the result (with all data filled in). @@ -140,7 +140,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, // FIXME? Chunked upload, progress reporting, etc. uploadPath := fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref)) logrus.Debugf("Uploading %s", uploadPath) - res, err := d.c.makeRequest(ctx, "POST", uploadPath, nil, nil, v2Auth) + res, err := d.c.makeRequest(ctx, "POST", uploadPath, nil, nil, v2Auth, nil) if err != nil { return types.BlobInfo{}, err } @@ -157,7 +157,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, digester := digest.Canonical.Digester() sizeCounter := &sizeCounter{} tee := io.TeeReader(stream, io.MultiWriter(digester.Hash(), sizeCounter)) - res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, v2Auth) + res, err = d.c.makeRequestToResolvedURL(ctx, "PATCH", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, tee, inputInfo.Size, v2Auth, nil) if err != nil { logrus.Debugf("Error uploading layer chunked, response %#v", res) return types.BlobInfo{}, err @@ -176,7 +176,7 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, // TODO: check inputInfo.Digest == computedDigest https://github.com/containers/image/pull/70#discussion_r77646717 locationQuery.Set("digest", computedDigest.String()) uploadLocation.RawQuery = locationQuery.Encode() - res, err = d.c.makeRequestToResolvedURL(ctx, "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, v2Auth) + res, err = d.c.makeRequestToResolvedURL(ctx, "PUT", uploadLocation.String(), map[string][]string{"Content-Type": {"application/octet-stream"}}, nil, -1, v2Auth, nil) if err != nil { return types.BlobInfo{}, err } @@ -194,10 +194,10 @@ func (d *dockerImageDestination) PutBlob(ctx context.Context, stream io.Reader, // blobExists returns true iff repo contains a blob with digest, and if so, also its size. // If the destination does not contain the blob, or it is unknown, blobExists ordinarily returns (false, -1, nil); // it returns a non-nil error only on an unexpected failure. -func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.Named, digest digest.Digest) (bool, int64, error) { +func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.Named, digest digest.Digest, extraScope *authScope) (bool, int64, error) { checkPath := fmt.Sprintf(blobsPath, reference.Path(repo), digest.String()) logrus.Debugf("Checking %s", checkPath) - res, err := d.c.makeRequest(ctx, "HEAD", checkPath, nil, nil, v2Auth) + res, err := d.c.makeRequest(ctx, "HEAD", checkPath, nil, nil, v2Auth, extraScope) if err != nil { return false, -1, err } @@ -218,7 +218,7 @@ func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference. } // mountBlob tries to mount blob srcDigest from srcRepo to the current destination. -func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo reference.Named, srcDigest digest.Digest) error { +func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo reference.Named, srcDigest digest.Digest, extraScope *authScope) error { u := url.URL{ Path: fmt.Sprintf(blobUploadPath, reference.Path(d.ref.ref)), RawQuery: url.Values{ @@ -228,7 +228,7 @@ func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo referenc } mountPath := u.String() logrus.Debugf("Trying to mount %s", mountPath) - res, err := d.c.makeRequest(ctx, "POST", mountPath, nil, nil, v2Auth) + res, err := d.c.makeRequest(ctx, "POST", mountPath, nil, nil, v2Auth, extraScope) if err != nil { return err } @@ -246,7 +246,7 @@ func (d *dockerImageDestination) mountBlob(ctx context.Context, srcRepo referenc return errors.Wrap(err, "Error determining upload URL after a mount attempt") } logrus.Debugf("... started an upload instead of mounting, trying to cancel at %s", uploadLocation.String()) - res2, err := d.c.makeRequestToResolvedURL(ctx, "DELETE", uploadLocation.String(), nil, nil, -1, v2Auth) + res2, err := d.c.makeRequestToResolvedURL(ctx, "DELETE", uploadLocation.String(), nil, nil, -1, v2Auth, extraScope) if err != nil { logrus.Debugf("Error trying to cancel an inadvertent upload: %s", err) } else { @@ -276,7 +276,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. } // First, check whether the blob happens to already exist at the destination. - exists, size, err := d.blobExists(ctx, d.ref.ref, info.Digest) + exists, size, err := d.blobExists(ctx, d.ref.ref, info.Digest, nil) if err != nil { return false, types.BlobInfo{}, err } @@ -286,15 +286,6 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. } // Then try reusing blobs from other locations. - - // Checking candidateRepo, and mounting from it, requires an expanded token scope. - // We still want to reuse the ping information and other aspects of the client, so rather than make a fresh copy, there is this a bit ugly extraScope hack. - if d.c.extraScope != nil { - return false, types.BlobInfo{}, errors.New("Internal error: dockerClient.extraScope was set before TryReusingBlob") - } - defer func() { - d.c.extraScope = nil - }() for _, candidate := range cache.CandidateLocations(d.ref.Transport(), bicTransportScope(d.ref), info.Digest, canSubstitute) { candidateRepo, err := parseBICLocationReference(candidate.Location) if err != nil { @@ -314,7 +305,10 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. } // Whatever happens here, don't abort the entire operation. It's likely we just don't have permissions, and if it is a critical network error, we will find out soon enough anyway. - d.c.extraScope = &authScope{ + + // Checking candidateRepo, and mounting from it, requires an + // expanded token scope. + extraScope := &authScope{ remoteName: reference.Path(candidateRepo), actions: "pull", } @@ -325,7 +319,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. // Even worse, docker/distribution does not actually reasonably implement canceling uploads // (it would require a "delete" action in the token, and Quay does not give that to anyone, so we can't ask); // so, be a nice client and don't create unnecesary upload sessions on the server. - exists, size, err := d.blobExists(ctx, candidateRepo, candidate.Digest) + exists, size, err := d.blobExists(ctx, candidateRepo, candidate.Digest, extraScope) if err != nil { logrus.Debugf("... Failed: %v", err) continue @@ -335,7 +329,7 @@ func (d *dockerImageDestination) TryReusingBlob(ctx context.Context, info types. continue // logrus.Debug() already happened in blobExists } if candidateRepo.Name() != d.ref.ref.Name() { - if err := d.mountBlob(ctx, candidateRepo, candidate.Digest); err != nil { + if err := d.mountBlob(ctx, candidateRepo, candidate.Digest, extraScope); err != nil { logrus.Debugf("... Mount failed: %v", err) continue } @@ -369,7 +363,7 @@ func (d *dockerImageDestination) PutManifest(ctx context.Context, m []byte) erro if mimeType != "" { headers["Content-Type"] = []string{mimeType} } - res, err := d.c.makeRequest(ctx, "PUT", path, headers, bytes.NewReader(m), v2Auth) + res, err := d.c.makeRequest(ctx, "PUT", path, headers, bytes.NewReader(m), v2Auth, nil) if err != nil { return err } @@ -574,7 +568,7 @@ sigExists: } path := fmt.Sprintf(extensionsSignaturePath, reference.Path(d.ref.ref), d.manifestDigest.String()) - res, err := d.c.makeRequest(ctx, "PUT", path, nil, bytes.NewReader(body), v2Auth) + res, err := d.c.makeRequest(ctx, "PUT", path, nil, bytes.NewReader(body), v2Auth, nil) if err != nil { return err } diff --git a/vendor/github.com/containers/image/docker/docker_image_src.go b/vendor/github.com/containers/image/docker/docker_image_src.go index c88ff2f34..063511535 100644 --- a/vendor/github.com/containers/image/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/docker/docker_image_src.go @@ -89,7 +89,7 @@ func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest strin path := fmt.Sprintf(manifestPath, reference.Path(s.ref.ref), tagOrDigest) headers := make(map[string][]string) headers["Accept"] = manifest.DefaultRequestedManifestMIMETypes - res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth) + res, err := s.c.makeRequest(ctx, "GET", path, headers, nil, v2Auth, nil) if err != nil { return nil, "", err } @@ -137,7 +137,7 @@ func (s *dockerImageSource) getExternalBlob(ctx context.Context, urls []string) err error ) for _, url := range urls { - resp, err = s.c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth) + resp, err = s.c.makeRequestToResolvedURL(ctx, "GET", url, nil, nil, -1, noAuth, nil) if err == nil { if resp.StatusCode != http.StatusOK { err = errors.Errorf("error fetching external blob from %q: %d (%s)", url, resp.StatusCode, http.StatusText(resp.StatusCode)) @@ -176,7 +176,7 @@ func (s *dockerImageSource) GetBlob(ctx context.Context, info types.BlobInfo, ca path := fmt.Sprintf(blobsPath, reference.Path(s.ref.ref), info.Digest.String()) logrus.Debugf("Downloading %s", path) - res, err := s.c.makeRequest(ctx, "GET", path, nil, nil, v2Auth) + res, err := s.c.makeRequest(ctx, "GET", path, nil, nil, v2Auth, nil) if err != nil { return nil, 0, err } @@ -340,7 +340,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere return err } getPath := fmt.Sprintf(manifestPath, reference.Path(ref.ref), refTail) - get, err := c.makeRequest(ctx, "GET", getPath, headers, nil, v2Auth) + get, err := c.makeRequest(ctx, "GET", getPath, headers, nil, v2Auth, nil) if err != nil { return err } @@ -362,7 +362,7 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere // When retrieving the digest from a registry >= 2.3 use the following header: // "Accept": "application/vnd.docker.distribution.manifest.v2+json" - delete, err := c.makeRequest(ctx, "DELETE", deletePath, headers, nil, v2Auth) + delete, err := c.makeRequest(ctx, "DELETE", deletePath, headers, nil, v2Auth, nil) if err != nil { return err } diff --git a/vendor/github.com/containers/image/version/version.go b/vendor/github.com/containers/image/version/version.go index 6644bcff3..10075992d 100644 --- a/vendor/github.com/containers/image/version/version.go +++ b/vendor/github.com/containers/image/version/version.go @@ -8,7 +8,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 1 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 5 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "-dev" diff --git a/vendor/github.com/urfave/cli/help.go b/vendor/github.com/urfave/cli/help.go index 65874fa2f..a0e949f72 100644 --- a/vendor/github.com/urfave/cli/help.go +++ b/vendor/github.com/urfave/cli/help.go @@ -82,7 +82,7 @@ OPTIONS: var helpCommand = Command{ Name: "help", Aliases: []string{"h"}, - Usage: "Shows a list of commands or help for one command", + Usage: "Show a list of commands or help for one command", ArgsUsage: "[command]", Action: func(c *Context) error { args := c.Args() |