diff options
39 files changed, 502 insertions, 278 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index d3a9eea40..dbd0e8b3e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -31,7 +31,7 @@ env: 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" + PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-libpod-7f4cd1f7" # CENTOS_CACHE_IMAGE_NAME: "centos-7-notready" #### @@ -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. @@ -109,7 +109,6 @@ gating_task: - '/usr/local/bin/entrypoint.sh validate' - '/usr/local/bin/entrypoint.sh lint' - build_each_commit_task: depends_on: @@ -134,6 +133,7 @@ build_each_commit_task: - git fetch --depth $CIRRUS_CLONE_DEPTH origin $CIRRUS_BASE_BRANCH - env GOPATH=/var/tmp/go/ make build-all-new-commits GIT_BASE_BRANCH=origin/$CIRRUS_BASE_BRANCH + # This task does the unit and integration testing for every platform testing_task: @@ -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 @@ -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) @@ -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 @@ -225,6 +225,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/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/inspect.go b/cmd/podman/inspect.go index 5595a8331..1346da9fb 100644 --- a/cmd/podman/inspect.go +++ b/cmd/podman/inspect.go @@ -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 80f0bbdfe..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. 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/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/imgts/Dockerfile b/contrib/imgts/Dockerfile new file mode 100644 index 000000000..0746eca4c --- /dev/null +++ b/contrib/imgts/Dockerfile @@ -0,0 +1,20 @@ +FROM centos:7 + +# Only needed for installing build-time dependencies +COPY /contrib/imgts/google-cloud-sdk.repo /etc/yum.repos.d/google-cloud-sdk.repo +RUN yum -y update && \ + yum -y install epel-release && \ + yum -y install google-cloud-sdk && \ + yum clean all + +COPY /contrib/imgts/entrypoint.sh /usr/local/bin/entrypoint.sh +ENV GCPJSON="__unknown__" \ + GCPNAME="__unknown__" \ + GCPPROJECT="__unknown__" \ + IMGNAMES="__unknown__" \ + TIMESTAMP="__unknown__" \ + BUILDID="__unknown__" \ + REPOREF="__unknown__" +RUN chmod 755 /usr/local/bin/entrypoint.sh + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/contrib/imgts/README.md b/contrib/imgts/README.md new file mode 100644 index 000000000..ad5ed4172 --- /dev/null +++ b/contrib/imgts/README.md @@ -0,0 +1,11 @@ +![PODMAN logo](../../logo/podman-logo-source.svg) + +A container image for tracking automation metadata. +Currently this is used to update last-used timestamps on +VM images. + +Example build (from repository root): + +```bash +sudo podman build -t $IMAGE_NAME -f contrib/imgts/Dockerfile . +``` diff --git a/contrib/imgts/entrypoint.sh b/contrib/imgts/entrypoint.sh new file mode 100755 index 000000000..65a76d8e4 --- /dev/null +++ b/contrib/imgts/entrypoint.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e + +RED="\e[1;36;41m" +YEL="\e[1;33;44m" +NOR="\e[0m" + +die() { + echo -e "$2" >&2 + exit "$1" +} + +SENTINEL="__unknown__" # default set in dockerfile + +[[ "$GCPJSON" != "$SENTINEL" ]] || \ + die 1 "Must specify service account JSON in \$GCPJSON" +[[ "$GCPNAME" != "$SENTINEL" ]] || \ + die 2 "Must specify service account name in \$GCPNAME" +[[ "$GCPPROJECT" != "$SENTINEL" ]] || \ + die 4 "Must specify GCP Project ID in \$GCPPROJECT" +[[ -n "$GCPPROJECT" ]] || \ + die 5 "Must specify non-empty GCP Project ID in \$GCPPROJECT" +[[ "$IMGNAMES" != "$SENTINEL" ]] || \ + die 6 "Must specify space separated list of GCE image names in \$IMGNAMES" +[[ "$BUILDID" != "$SENTINEL" ]] || \ + die 7 "Must specify the number of current build in \$BUILDID" +[[ "$REPOREF" != "$SENTINEL" ]] || \ + die 8 "Must specify a PR number or Branch name in \$REPOREF" + +ARGS="--update-labels=last-used=$(date +%s)" +# optional +[[ -z "$BUILDID" ]] || ARGS="$ARGS --update-labels=build-id=$BUILDID" +[[ -z "$REPOREF" ]] || ARGS="$ARGS --update-labels=repo-ref=$REPOREF" + +gcloud config set account "$GCPNAME" +gcloud config set project "$GCPPROJECT" +echo "$GCPJSON" > /tmp/gcp.json +gcloud auth activate-service-account --key-file=/tmp/gcp.json || rm /tmp/gcp.json +for image in $IMGNAMES +do + gcloud compute images update "$image" $ARGS & +done +set +e # Actual update failures are only warnings +wait || die 0 "${RED}WARNING:$NOR ${YEL}Failed to update labels on one or more images:$NOR '$IMGNAMES'" diff --git a/contrib/imgts/google-cloud-sdk.repo b/contrib/imgts/google-cloud-sdk.repo new file mode 100644 index 000000000..45b1e43bb --- /dev/null +++ b/contrib/imgts/google-cloud-sdk.repo @@ -0,0 +1,8 @@ +[google-cloud-sdk] +name=Google Cloud SDK +baseurl=https://packages.cloud.google.com/yum/repos/cloud-sdk-el7-x86_64 +enabled=1 +gpgcheck=1 +repo_gpgcheck=1 +gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg + https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg 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.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/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/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/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/info_test.go b/test/e2e/info_test.go index 2022dff1b..637c04e0a 100644 --- a/test/e2e/info_test.go +++ b/test/e2e/info_test.go @@ -35,7 +35,7 @@ var _ = Describe("Podman Info", func() { It("podman info json output", func() { session := podmanTest.Podman([]string{"info", "--format=json"}) - session.Wait() + session.WaitWithDefaultTimeout() Expect(session.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() |