diff options
35 files changed, 409 insertions, 280 deletions
diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md new file mode 100644 index 000000000..850e68db0 --- /dev/null +++ b/CODE-OF-CONDUCT.md @@ -0,0 +1,3 @@ +## The Libpod Project Community Code of Conduct + +The Libpod project which includes Podman, follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md). @@ -582,7 +582,7 @@ install.libseccomp.sudo: cmd/podman/varlink/iopodman.go: .gopathok cmd/podman/varlink/io.podman.varlink -ifeq ("$(shell uname -o)", "GNU/Linux") +ifneq (,$(findstring Linux,$(shell uname -o))) # Only generate the varlink code on Linux (see issue #4814). GO111MODULE=off $(GO) generate ./cmd/podman/varlink/... endif @@ -5,7 +5,7 @@ Libpod provides a library for applications looking to use the Container Pod concept, popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes. -* [Latest Version: 1.7.0](https://github.com/containers/libpod/releases/latest) +* [Latest Version: 1.8.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) * [GoDoc: ![GoDoc](https://godoc.org/github.com/containers/libpod/libpod?status.svg)](https://godoc.org/github.com/containers/libpod/libpod) * Automated continuous release downloads (including remote-client): diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index d961b713e..fc528d70f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,7 +2,7 @@ ## 1.8.0 ### Features -- The `podman service` command has been added, providing a preview of Podman's new Docker-compatible API. This API is still very new, and not yet ready for production use, but is available for early testing +- The `podman system service` command has been added, providing a preview of Podman's new Docker-compatible API. This API is still very new, and not yet ready for production use, but is available for early testing - Rootless Podman now uses Rootlesskit for port forwarding, which should greatly improve performance and capabilities - The `podman untag` command has been added to remove tags from images without deleting them - The `podman inspect` command on images now displays previous names they used @@ -29,13 +29,20 @@ - Fixed a bug where `podman history` was not computing image sizes correctly ([#4916](https://github.com/containers/libpod/issues/4916)) - Fixed a bug where Podman would not error on invalid values to the `--sort` flag to `podman images` - Fixed a bug where providing a name for the image made by `podman commit` was mandatory, not optional as it should be ([#5027](https://github.com/containers/libpod/issues/5027)) +- Fixed a bug where the remote Podman client would append an extra `"` to `%PATH` ([#4335](https://github.com/containers/libpod/issues/4335)) +- Fixed a bug where the `podman build` command would sometimes ignore the `-f` option and build the wrong Containerfile +- Fixed a bug where the `podman ps --filter` command would only filter running containers, instead of all containers, if `--all` was not passed ([#5050](https://github.com/containers/libpod/issues/5050)) +- Fixed a bug where the `podman load` command on compressed images would leave an extra copy on disk +- Fixed a bug where the `podman restart` command would not properly clean up the network, causing it to function differently from `podman stop; podman start` ([#5051](https://github.com/containers/libpod/issues/5051)) +- Fixed a bug where setting the `--memory-swap` flag to `podman create` and `podman run` to `-1` (to indicate unlimited) was not supported ([#5091](https://github.com/containers/libpod/issues/5091)) ### Misc - Initial work on version 2 of the Podman remote API has been merged, but is still in an alpha state and not ready for use. Read more [here](https://podman.io/releases/2020/01/17/podman-new-api.html) - Many formatting corrections have been made to the manpages - The changes to address ([#5009](https://github.com/containers/libpod/issues/5009)) may cause anonymous volumes created by Podman versions 1.6.3 to 1.7.0 to not be removed when their container is removed - Updated vendored Buildah to v1.13.1 -- Updated vendored containers/storage to v1.15.7 +- Updated vendored containers/storage to v1.15.8 +- Updated vendored containers/image to v5.2.0 ## 1.7.0 ### Features diff --git a/changelog.txt b/changelog.txt index 996c87441..320526596 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,35 @@ +- Changelog for v1.8.0 (2020-02-06) + * [CI:DOCS]update contrib systemd user + * [CI:DOCS]fix systemd files for apiv2 + * Update release notes for final release of v1.8.0 + * Move podman-service to podman-system-service + * Only modify conmon cgroup if we have running containers + * fix swagger docs and make sure docs validation runs + * Special case memory-swap=-1 + * vendor github.com/mtrmac/gpgme@v0.1.2 + * vendor github.com/containers/image/v5@v5.2.0 + * Add Containerfile location e2e test + * [CI:DOCS]addition of specgen package + * {CI:DOCS] run gofmt before lint + * build(deps): bump github.com/onsi/ginkgo from 1.11.0 to 1.12.0 + * Close tarSource when finished using it + * Force --all when --filter is passed to podman ps + * Initial implementation of a spec generator package + * Fix wrong Containerfile location on build + * Wrap error for failing ImageSize calls + * swagger: v2: libpod/images/{import,load,pull} + * seperate container create network options + * Cirrus: Fix gate task + make lint|validate + * Add a binding test to check image tag and list commands. + * Update /_ping support + * [CI:DOCS]add apiv2 endpoints for exec + * build(deps): bump github.com/containers/storage from 1.15.7 to 1.15.8 + * build(deps): bump github.com/onsi/gomega from 1.8.1 to 1.9.0 + * Tear down network when restarting containers + * Move install.md to podman.io, leave link page + * Update XML to not embed quote in PATH on windows + * Bump to v1.8.0-dev + - Changelog for v1.8.0-rc1 (2020-01-31) * Fix a syntax error in hack/release.sh * Minor update to release notes diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index ebd7aeb0c..d6018a6f4 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -26,9 +26,6 @@ func getMainCommands() []*cobra.Command { if len(_varlinkCommand.Use) > 0 { rootCommands = append(rootCommands, _varlinkCommand) } - if len(_serviceCommand.Use) > 0 { - rootCommands = append(rootCommands, _serviceCommand) - } return rootCommands } @@ -71,9 +68,15 @@ func getTrustSubCommands() []*cobra.Command { // Commands that the local client implements func getSystemSubCommands() []*cobra.Command { - return []*cobra.Command{ + systemCommands := []*cobra.Command{ _renumberCommand, _dfSystemCommand, _migrateCommand, } + + if len(_serviceCommand.Use) > 0 { + systemCommands = append(systemCommands, _serviceCommand) + } + + return systemCommands } diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index 9425cfb9c..e9dc87de1 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -13,32 +13,66 @@ import ( "github.com/pkg/errors" ) +type runtimeOptions struct { + name string + renumber bool + migrate bool + noStore bool + withFDS bool +} + // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers func GetRuntimeMigrate(ctx context.Context, c *cliconfig.PodmanCommand, newRuntime string) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, true, false, true, newRuntime) + return getRuntime(ctx, c, &runtimeOptions{ + name: newRuntime, + renumber: false, + migrate: true, + noStore: false, + withFDS: true, + }) } // GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify func GetRuntimeDisableFDs(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false, false, false, "") + return getRuntime(ctx, c, &runtimeOptions{ + renumber: false, + migrate: false, + noStore: false, + withFDS: false, + }) } // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber func GetRuntimeRenumber(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, true, false, false, true, "") + return getRuntime(ctx, c, &runtimeOptions{ + renumber: true, + migrate: false, + noStore: false, + withFDS: true, + }) } // GetRuntime generates a new libpod runtime configured by command line options func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false, false, true, "") + return getRuntime(ctx, c, &runtimeOptions{ + renumber: false, + migrate: false, + noStore: false, + withFDS: true, + }) } // GetRuntimeNoStore generates a new libpod runtime configured by command line options func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false, true, true, "") + return getRuntime(ctx, c, &runtimeOptions{ + renumber: false, + migrate: false, + noStore: true, + withFDS: true, + }) } -func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migrate, noStore, withFDS bool, newRuntime string) (*libpod.Runtime, error) { +func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, opts *runtimeOptions) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts := storage.StoreOptions{} storageSet := false @@ -86,14 +120,14 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migra storageSet = true storageOpts.GraphDriverOptions = c.GlobalFlags.StorageOpts } - if migrate { + if opts.migrate { options = append(options, libpod.WithMigrate()) - if newRuntime != "" { - options = append(options, libpod.WithMigrateRuntime(newRuntime)) + if opts.name != "" { + options = append(options, libpod.WithMigrateRuntime(opts.name)) } } - if renumber { + if opts.renumber { options = append(options, libpod.WithRenumber()) } @@ -102,7 +136,7 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migra options = append(options, libpod.WithStorageConfig(storageOpts)) } - if !storageSet && noStore { + if !storageSet && opts.noStore { options = append(options, libpod.WithNoStore()) } // TODO CLI flags for image config? @@ -174,7 +208,7 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migra options = append(options, libpod.WithDefaultInfraCommand(infraCommand)) } - if !withFDS { + if !opts.withFDS { options = append(options, libpod.WithEnableSDNotify()) } if c.Flags().Changed("config") { diff --git a/code-of-conduct.md b/code-of-conduct.md deleted file mode 100644 index 215ce7ac6..000000000 --- a/code-of-conduct.md +++ /dev/null @@ -1,55 +0,0 @@ -## Kubernetes Community Code of Conduct - -### Contributor Code of Conduct - -As contributors and maintainers of this project, and in the interest of fostering -an open and welcoming community, we pledge to respect all people who contribute -through reporting issues, posting feature requests, updating documentation, -submitting pull requests or patches, and other activities. - -We are committed to making participation in this project a harassment-free experience for -everyone, regardless of level of experience, gender, gender identity and expression, -sexual orientation, disability, personal appearance, body size, race, ethnicity, age, -religion, or nationality. - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery. -* Personal attacks. -* Trolling or insulting/derogatory comments. -* Public or private harassment. -* Publishing other's private information, such as physical or electronic addresses, - without explicit permission. -* Other unethical or unprofessional conduct. - -Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are not -aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers -commit themselves to fairly and consistently applying these principles to every aspect -of managing this project. Project maintainers who do not follow or enforce the Code of -Conduct may be permanently removed from the project team. - -This code of conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a Kubernetes maintainer, Sarah Novotny <sarahnovotny@google.com>, and/or Dan Kohn <dan@linuxfoundation.org>. - -This Code of Conduct is adapted from the Contributor Covenant -(http://contributor-covenant.org), version 1.2.0, available at -http://contributor-covenant.org/version/1/2/0/ - -### Kubernetes Events Code of Conduct - -Kubernetes events are working conferences intended for professional networking and collaboration in the -Kubernetes community. Attendees are expected to behave according to professional standards and in accordance -with their employer's policies on appropriate workplace behavior. - -While at Kubernetes events or related social networking opportunities, attendees should not engage in -discriminatory or offensive speech or actions regarding gender, sexuality, race, or religion. Speakers should -be especially aware of these concerns. - -The Kubernetes team does not condone any statements by speakers contrary to these standards. The Kubernetes -team reserves the right to deny entrance and/or eject from an event (without refund) any individual found to -be engaging in discriminatory or offensive speech or actions. - -Please bring any concerns to the immediate attention of the Kubernetes event staff. diff --git a/completions/bash/podman b/completions/bash/podman index 57b9547a7..56559c142 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -1200,6 +1200,19 @@ _podman_system_prune() { esac } +_podman_system_service() { + local options_with_args=" + -t + --timeout + " + local boolean_options=" + --help + -h + --varlink + " + _complete_ "$options_with_args" "$boolean_options" +} + _podman_system() { local boolean_options=" --help @@ -1755,19 +1768,6 @@ _podman_search() { _complete_ "$options_with_args" "$boolean_options" } -_podman_service() { - local options_with_args=" - -t - --timeout - " - local boolean_options=" - --help - -h - --varlink - " - _complete_ "$options_with_args" "$boolean_options" -} - _podman_unmount() { _podman_umount $@ } diff --git a/contrib/fedora-minimal/Dockerfile b/contrib/fedora-minimal/Dockerfile new file mode 100644 index 000000000..8ea4e6765 --- /dev/null +++ b/contrib/fedora-minimal/Dockerfile @@ -0,0 +1 @@ +FROM fedora-minimal:latest diff --git a/contrib/fedora-minimal/README.md b/contrib/fedora-minimal/README.md new file mode 100644 index 000000000..52bf94b53 --- /dev/null +++ b/contrib/fedora-minimal/README.md @@ -0,0 +1,4 @@ +This dockerfile exists so that the container image can be "mirrored" +onto quay.io automatically, so automated testing can be more resilient. + +https://quay.io/repository/libpod/fedora-minimal?tab=builds diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index a64f473f4..5f21571ca 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -43,7 +43,7 @@ %global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7}) Name: podman -Version: 1.8.0 +Version: 1.8.1 Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Summary: Manage Pods, Containers and Container Images License: ASL 2.0 @@ -507,7 +507,7 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath} %files %license LICENSE -%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md CODE-OF-CONDUCT.md transfer.md %{_bindir}/%{name} %{_datadir}/bash-completion/completions/* %{_datadir}/zsh/site-functions/* @@ -523,19 +523,19 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath} %if 0%{?with_devel} %files -n libpod-devel -f devel.file-list %license LICENSE -%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md CODE-OF-CONDUCT.md transfer.md %dir %{gopath}/src/%{provider}.%{provider_tld}/%{project} %endif %if 0%{?with_unit_test} && 0%{?with_devel} %files unit-test-devel -f unit-test-devel.file-list %license LICENSE -%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md CODE-OF-CONDUCT.md transfer.md %endif %files -n podman-remote %license LICENSE -%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md pkg/hooks/README-hooks.md install.md CODE-OF-CONDUCT.md transfer.md %{_bindir}/%{name}-remote %if %{with doc} diff --git a/contrib/spec/python-podman.spec.in b/contrib/spec/python-podman.spec.in index 6296586dd..b921f2645 100644 --- a/contrib/spec/python-podman.spec.in +++ b/contrib/spec/python-podman.spec.in @@ -92,7 +92,7 @@ popd %files %license LICENSE -%doc README.md CONTRIBUTING.md install.md code-of-conduct.md transfer.md +%doc README.md CONTRIBUTING.md install.md CODE-OF-CONDUCT.md transfer.md %{_bindir}/pypodman %{_mandir}/man1/pypodman.1* %dir %{python3_sitelib}/podman diff --git a/contrib/systemd/README.md b/contrib/systemd/README.md index ff266c6a5..9f1d37792 100644 --- a/contrib/systemd/README.md +++ b/contrib/systemd/README.md @@ -2,11 +2,6 @@ ## system-wide (podman service run as root) -The following unit file examples assume: - 1. copied the `service` executable into `/usr/local/bin` - 1. `chcon system_u:object_r:container_runtime_exec_t:s0 /usr/local/bin/service` - -then: 1. copy the `podman.service` and `podman.socket` files into `/etc/systemd/system` 1. `systemctl daemon-reload` 1. `systemctl enable podman.socket` @@ -16,47 +11,12 @@ then: Assuming the status messages show no errors, the libpod service is ready to respond to the APIv2 on the unix domain socket `/run/podman/podman.sock` ### podman.service -```toml -[Unit] -Description=Podman API Service -Requires=podman.socket -After=podman.socket -Documentation=man:podman-api(1) -StartLimitIntervalSec=0 - -[Service] -Type=oneshot -Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf -ExecStart=/usr/local/bin/service -TimeoutStopSec=30 -KillMode=process - -[Install] -WantedBy=multi-user.target -Also=podman.socket -``` +You can refer to [this example](https://github.com/containers/libpod/blob/master/contrib/systemd/system/podman.service) for a sample podman.service file. ### podman.socket +You can refer to [this example](https://github.com/containers/libpod/blob/master/contrib/systemd/system/podman.socket) for a sample podman.socket file. -```toml -[Unit] -Description=Podman API Socket -Documentation=man:podman-api(1) - -[Socket] -ListenStream=%t/podman/podman.sock -SocketMode=0660 - -[Install] -WantedBy=sockets.target -``` ## user (podman service run as given user aka "rootless") -The following unit file examples assume: - 1. you have a created a directory `~/bin` - 1. copied the `service` executable into `~/bin` - 1. `chcon system_u:object_r:container_runtime_exec_t:s0 ~/bin/service` - -then: 1. `mkdir -p ~/.config/systemd/user` 1. copy the `podman.service` and `podman.socket` files into `~/.config/systemd/user` 1. `systemctl --user enable podman.socket` @@ -66,37 +26,7 @@ then: Assuming the status messages show no errors, the libpod service is ready to respond to the APIv2 on the unix domain socket `/run/user/$(id -u)/podman/podman.sock` ### podman.service +You can refer to [this example](https://github.com/containers/libpod/blob/master/contrib/systemd/user/podman.service) for a rootless podman.service file. -```toml -[Unit] -Description=Podman API Service -Requires=podman.socket -After=podman.socket -Documentation=man:podman-api(1) -StartLimitIntervalSec=0 - -[Service] -Type=oneshot -Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf -ExecStart=%h/bin/service -TimeoutStopSec=30 -KillMode=process - -[Install] -WantedBy=multi-user.target -Also=podman.socket -``` ### podman.socket - -```toml -[Unit] -Description=Podman API Socket -Documentation=man:podman-api(1) - -[Socket] -ListenStream=%t/podman/podman.sock -SocketMode=0660 - -[Install] -WantedBy=sockets.target -``` +You can refer to [this example](https://github.com/containers/libpod/blob/master/contrib/systemd/user/podman.socket) for a rootless podman.socket file. diff --git a/contrib/systemd/system/podman.service b/contrib/systemd/system/podman.service index 13d858627..eaa2ec437 100644 --- a/contrib/systemd/system/podman.service +++ b/contrib/systemd/system/podman.service @@ -8,7 +8,7 @@ StartLimitIntervalSec=0 [Service] Type=oneshot Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf -ExecStart=/usr/local/bin/service +ExecStart=/usr/bin/podman system service TimeoutStopSec=30 KillMode=process diff --git a/contrib/systemd/user/podman.service b/contrib/systemd/user/podman.service index 81fa55cf8..eaa2ec437 100644 --- a/contrib/systemd/user/podman.service +++ b/contrib/systemd/user/podman.service @@ -8,7 +8,7 @@ StartLimitIntervalSec=0 [Service] Type=oneshot Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf -ExecStart=%h/bin/service +ExecStart=/usr/bin/podman system service TimeoutStopSec=30 KillMode=process diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 0f3bfa0d3..738644c16 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -178,7 +178,7 @@ Add a host device to the container. The format is `<device-on-host>[:<device-on- Note: if the user only has access rights via a group then accessing the device from inside a rootless container will fail. The `crun` runtime offers a -workaround for this by adding the option `--annotation io.crun.keep_original_groups=1`. +workaround for this by adding the option `--annotation run.oci.keep_original_groups=1`. **--disable-compression, -D** diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md index 0e641f3a3..977382e61 100644 --- a/docs/source/markdown/podman-create.1.md +++ b/docs/source/markdown/podman-create.1.md @@ -207,7 +207,7 @@ Add a host device to the container. The format is `<device-on-host>[:<device-on- Note: if the user only has access rights via a group then accessing the device from inside a rootless container will fail. The `crun` runtime offers a -workaround for this by adding the option `--annotation io.crun.keep_original_groups=1`. +workaround for this by adding the option `--annotation run.oci.keep_original_groups=1`. **--device-read-bps**=*path* diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md index bf79ea031..3befc74c8 100644 --- a/docs/source/markdown/podman-run.1.md +++ b/docs/source/markdown/podman-run.1.md @@ -213,7 +213,7 @@ Add a host device to the container. The format is `<device-on-host>[:<device-on- Note: if the user only has access rights via a group then accessing the device from inside a rootless container will fail. The `crun` runtime offers a -workaround for this by adding the option `--annotation io.crun.keep_original_groups=1`. +workaround for this by adding the option `--annotation run.oci.keep_original_groups=1`. **--device-read-bps**=*path* diff --git a/docs/source/markdown/podman-service.1.md b/docs/source/markdown/podman-system-service.1.md index 5c55e20d3..a71ce9dc0 100644 --- a/docs/source/markdown/podman-service.1.md +++ b/docs/source/markdown/podman-system-service.1.md @@ -1,13 +1,13 @@ % podman-service(1) ## NAME -podman\-service - Run an API service +podman\-system\-service - Run an API service ## SYNOPSIS -**podman service** [*options*] +**podman system service** [*options*] ## DESCRIPTION -The **podman service** command creates a listening service that will answer API calls for Podman. You may +The **podman system service** command creates a listening service that will answer API calls for Podman. You may optionally provide an endpoint for the API in URI form. For example, *unix://tmp/foobar.sock* or *tcp:localhost:8080*. If no endpoint is provided, defaults will be used. The default endpoint for a rootfull service is *unix:/run/podman/podman.sock* and rootless is *unix:/$XDG_RUNTIME_DIR/podman/podman.sock* (for diff --git a/docs/source/markdown/podman-system.1.md b/docs/source/markdown/podman-system.1.md index 1af97290d..5f163c6f0 100644 --- a/docs/source/markdown/podman-system.1.md +++ b/docs/source/markdown/podman-system.1.md @@ -19,6 +19,8 @@ The system command allows you to manage the podman systems | prune | [podman-system-prune(1)](podman-system-prune.1.md) | Remove all unused container, image and volume data. | | renumber | [podman-system-renumber(1)](podman-system-renumber.1.md)| Migrate lock numbers to handle a change in maximum number of locks. | | reset | [podman-system-reset(1)](podman-system-reset.1.md) | Reset storage back to initial state. | +| service | [podman-service(1)](podman-system-service.1.md) | Run an API service | + ## SEE ALSO podman(1) diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index af0e55925..853b5ecec 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -191,7 +191,6 @@ the exit codes follow the `chroot` standard, see below: | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. | | [podman-run(1)](podman-run.1.md) | Run a command in a new container. | | [podman-save(1)](podman-save.1.md) | Save an image to a container archive. | -| [podman-service(1)](podman-service.1.md) | Run an API service | | [podman-search(1)](podman-search.1.md) | Search a registry for an image. | | [podman-start(1)](podman-start.1.md) | Start one or more containers. | | [podman-stats(1)](podman-stats.1.md) | Display a live stream of one or more container's resource usage statistics. | @@ -12,7 +12,7 @@ require ( github.com/containernetworking/plugins v0.8.5 github.com/containers/buildah v1.13.1 github.com/containers/conmon v2.0.10+incompatible - github.com/containers/image/v5 v5.2.0 + github.com/containers/image/v5 v5.2.1 github.com/containers/psgo v1.4.0 github.com/containers/storage v1.15.8 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f @@ -45,7 +45,6 @@ require ( github.com/json-iterator/go v1.1.9 github.com/mrtazz/checkmake v0.0.0-20191009095831-03dd76b964dd // indirect github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 - github.com/mtrmac/gpgme v0.1.2 // indirect github.com/olekukonko/tablewriter v0.0.4 // indirect github.com/onsi/ginkgo v1.12.0 github.com/onsi/gomega v1.9.0 @@ -95,6 +95,8 @@ github.com/containers/image/v5 v5.1.0 h1:5FjAvPJniamuNNIQHkh4PnsL+n+xzs6Aonzaz5d github.com/containers/image/v5 v5.1.0/go.mod h1:BKlMD34WxRo1ruGHHEOrPQP0Qci7SWoPwU6fS7arsCU= github.com/containers/image/v5 v5.2.0 h1:DowY5OII5x9Pb6Pt76vnHU79BgG4/jdwhZjeAj2R+t8= github.com/containers/image/v5 v5.2.0/go.mod h1:IAub4gDGvXoxaIAdNy4e3FbVTDPVNMv9F0UfVVFbYCU= +github.com/containers/image/v5 v5.2.1 h1:rQR6QSUneWBoW1bTFpP9EJJTevQFv27YsKYQVJIzg+s= +github.com/containers/image/v5 v5.2.1/go.mod h1:TfhmLwH+v1/HBVPIWH7diLs8XwcOkP3c7t7JFgqaUEc= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v0.0.0-20190930154801-b87a4a69c741 h1:8tQkOcednLJtUcZgK7sPglscXtxvMOnFOa6wd09VWLM= diff --git a/libpod/image/image.go b/libpod/image/image.go index ba1080a71..43fd52a1a 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -99,10 +99,7 @@ func NewImageRuntimeFromOptions(options storage.StoreOptions) (*Runtime, error) if err != nil { return nil, err } - - return &Runtime{ - store: store, - }, nil + return NewImageRuntimeFromStore(store), nil } func setStore(options storage.StoreOptions) (storage.Store, error) { @@ -114,30 +111,29 @@ func setStore(options storage.StoreOptions) (storage.Store, error) { return store, nil } -// newFromStorage creates a new image object from a storage.Image -func (ir *Runtime) newFromStorage(img *storage.Image) *Image { - image := Image{ - InputName: img.ID, +// newImage creates a new image object given an "input name" and a storage.Image +func (ir *Runtime) newImage(inputName string, img *storage.Image) *Image { + return &Image{ + InputName: inputName, imageruntime: ir, image: img, } - return &image +} + +// newFromStorage creates a new image object from a storage.Image. Its "input name" will be its ID. +func (ir *Runtime) newFromStorage(img *storage.Image) *Image { + return ir.newImage(img.ID, img) } // NewFromLocal creates a new image object that is intended // to only deal with local images already in the store (or // its aliases) func (ir *Runtime) NewFromLocal(name string) (*Image, error) { - image := Image{ - InputName: name, - imageruntime: ir, - } - localImage, err := image.getLocalImage() + updatedInputName, localImage, err := ir.getLocalImage(name) if err != nil { return nil, err } - image.image = localImage - return &image, nil + return ir.newImage(updatedInputName, localImage), nil } // New creates a new image object where the image could be local @@ -148,15 +144,10 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile defer span.Finish() // We don't know if the image is local or not ... check local first - newImage := Image{ - InputName: name, - imageruntime: ir, - } if pullType != util.PullImageAlways { - localImage, err := newImage.getLocalImage() + newImage, err := ir.NewFromLocal(name) if err == nil { - newImage.image = localImage - return &newImage, nil + return newImage, nil } else if pullType == util.PullImageNever { return nil, err } @@ -171,13 +162,11 @@ func (ir *Runtime) New(ctx context.Context, name, signaturePolicyPath, authfile return nil, errors.Wrapf(err, "unable to pull %s", name) } - newImage.InputName = imageName[0] - img, err := newImage.getLocalImage() + newImage, err := ir.NewFromLocal(imageName[0]) if err != nil { return nil, errors.Wrapf(err, "error retrieving local image after pulling %s", name) } - newImage.image = img - return &newImage, nil + return newImage, nil } // LoadFromArchiveReference creates a new image object for images pulled from a tar archive and the like (podman load) @@ -194,16 +183,11 @@ func (ir *Runtime) LoadFromArchiveReference(ctx context.Context, srcRef types.Im } for _, name := range imageNames { - newImage := Image{ - InputName: name, - imageruntime: ir, - } - img, err := newImage.getLocalImage() + newImage, err := ir.NewFromLocal(name) if err != nil { return nil, errors.Wrapf(err, "error retrieving local image after pulling %s", name) } - newImage.image = img - newImages = append(newImages, &newImage) + newImages = append(newImages, newImage) } ir.newImageEvent(events.LoadFromArchive, "") return newImages, nil @@ -234,7 +218,7 @@ func (i *Image) reloadImage() error { if err != nil { return errors.Wrapf(err, "unable to reload image") } - i.image = newImage.image + i.image = newImage return nil } @@ -247,60 +231,60 @@ func stripSha256(name string) string { } // getLocalImage resolves an unknown input describing an image and -// returns a storage.Image or an error. It is used by NewFromLocal. -func (i *Image) getLocalImage() (*storage.Image, error) { - imageError := fmt.Sprintf("unable to find '%s' in local storage", i.InputName) - if i.InputName == "" { - return nil, errors.Errorf("input name is blank") +// returns an updated input name, and a storage.Image, or an error. It is used by NewFromLocal. +func (ir *Runtime) getLocalImage(inputName string) (string, *storage.Image, error) { + imageError := fmt.Sprintf("unable to find '%s' in local storage", inputName) + if inputName == "" { + return "", nil, errors.Errorf("input name is blank") } // Check if the input name has a transport and if so strip it - dest, err := alltransports.ParseImageName(i.InputName) + dest, err := alltransports.ParseImageName(inputName) if err == nil && dest.DockerReference() != nil { - i.InputName = dest.DockerReference().String() + inputName = dest.DockerReference().String() } - img, err := i.imageruntime.getImage(stripSha256(i.InputName)) + img, err := ir.getImage(stripSha256(inputName)) if err == nil { - return img.image, err + return inputName, img, err } // container-storage wasn't able to find it in its current form // check if the input name has a tag, and if not, run it through // again - decomposedImage, err := decompose(i.InputName) + decomposedImage, err := decompose(inputName) if err != nil { - return nil, err + return "", nil, err } // The image has a registry name in it and we made sure we looked for it locally // with a tag. It cannot be local. if decomposedImage.hasRegistry { - return nil, errors.Wrapf(ErrNoSuchImage, imageError) + return "", nil, errors.Wrapf(ErrNoSuchImage, imageError) } // if the image is saved with the repository localhost, searching with localhost prepended is necessary // We don't need to strip the sha because we have already determined it is not an ID ref, err := decomposedImage.referenceWithRegistry(DefaultLocalRegistry) if err != nil { - return nil, err + return "", nil, err } - img, err = i.imageruntime.getImage(ref.String()) + img, err = ir.getImage(ref.String()) if err == nil { - return img.image, err + return inputName, img, err } // grab all the local images - images, err := i.imageruntime.GetImages() + images, err := ir.GetImages() if err != nil { - return nil, err + return "", nil, err } // check the repotags of all images for a match repoImage, err := findImageInRepotags(decomposedImage, images) if err == nil { - return repoImage, nil + return inputName, repoImage, nil } - return nil, errors.Wrapf(ErrNoSuchImage, err.Error()) + return "", nil, errors.Wrapf(ErrNoSuchImage, err.Error()) } // ID returns the image ID as a string @@ -460,7 +444,7 @@ func (i *Image) Remove(ctx context.Context, force bool) error { // getImage retrieves an image matching the given name or hash from system // storage // If no matching image can be found, an error is returned -func (ir *Runtime) getImage(image string) (*Image, error) { +func (ir *Runtime) getImage(image string) (*storage.Image, error) { var img *storage.Image ref, err := is.Transport.ParseStoreReference(ir.store, image) if err == nil { @@ -476,8 +460,7 @@ func (ir *Runtime) getImage(image string) (*Image, error) { } img = img2 } - newImage := ir.newFromStorage(img) - return newImage, nil + return img, nil } // GetImages retrieves all images present in storage @@ -702,13 +685,6 @@ func (i *Image) toImageSourceRef(ctx context.Context) (types.ImageSource, error) //Size returns the size of the image func (i *Image) Size(ctx context.Context) (*uint64, error) { - if i.image == nil { - localImage, err := i.getLocalImage() - if err != nil { - return nil, err - } - i.image = localImage - } sum, err := i.imageruntime.store.ImageSize(i.ID()) if err == nil && sum >= 0 { usum := uint64(sum) diff --git a/libpod/options.go b/libpod/options.go index 923e7292c..4957f822d 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1784,3 +1784,156 @@ func WithInfraContainerPorts(bindings []ocicni.PortMapping) PodCreateOption { return nil } } + +// WithPodStaticIP sets a static IP for the pod. +func WithPodStaticIP(ip net.IP) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if len(pod.config.InfraContainer.Networks) > 1 { + return errors.Wrapf(define.ErrInvalidArg, "cannot set a static IP if joining more than 1 CNI network") + } + + pod.config.InfraContainer.StaticIP = ip + + return nil + } +} + +// WithPodStaticMAC sets a static MAC address for the pod. +func WithPodStaticMAC(mac net.HardwareAddr) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if len(pod.config.InfraContainer.Networks) > 1 { + return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if joining more than 1 CNI network") + } + + pod.config.InfraContainer.StaticMAC = mac + + return nil + } +} + +// WithPodUseImageResolvConf sets a pod to use an image's resolv.conf and not +// create its own. +func WithPodUseImageResolvConf() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if len(pod.config.InfraContainer.DNSServer) != 0 || + len(pod.config.InfraContainer.DNSSearch) != 0 || + len(pod.config.InfraContainer.DNSOption) != 0 { + return errors.Wrapf(define.ErrInvalidArg, "requested use of image resolv.conf conflicts with already-configured DNS settings") + } + + pod.config.InfraContainer.UseImageResolvConf = true + + return nil + } +} + +// WithPodDNS sets the DNS Servers for a pod. +func WithPodDNS(dnsServer []string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if pod.config.InfraContainer.UseImageResolvConf { + return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS servers if pod will not create /etc/resolv.conf") + } + + pod.config.InfraContainer.DNSServer = dnsServer + + return nil + } +} + +// WithPodDNSSearch sets the DNS Search domains for a pod. +func WithPodDNSSearch(dnsSearch []string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if pod.config.InfraContainer.UseImageResolvConf { + return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS search domains if pod will not create /etc/resolv.conf") + } + + pod.config.InfraContainer.DNSSearch = dnsSearch + + return nil + } +} + +// WithPodDNSOption sets DNS Options for a pod. +func WithPodDNSOption(dnsOption []string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if pod.config.InfraContainer.UseImageResolvConf { + return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS options if pod will not create /etc/resolv.conf") + } + + pod.config.InfraContainer.DNSOption = dnsOption + + return nil + } +} + +// WithPodUseImageHosts tells the pod not to create /etc/hosts and instead to +// use the one provided by the image. +func WithPodUseImageHosts() PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if len(pod.config.InfraContainer.HostAdd) != 0 { + return errors.Wrapf(define.ErrInvalidArg, "not creating /etc/hosts conflicts with adding to the hosts file") + } + + pod.config.InfraContainer.UseImageHosts = true + + return nil + } +} + +// WithPodHosts adds additional entries to the pod's /etc/hosts +func WithPodHosts(hosts []string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + if pod.config.InfraContainer.UseImageHosts { + return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if container is using image hosts") + } + + pod.config.InfraContainer.HostAdd = hosts + + return nil + } +} + +// WithPodNetworks sets additional CNI networks for the pod to join. +func WithPodNetworks(networks []string) PodCreateOption { + return func(pod *Pod) error { + if pod.valid { + return define.ErrPodFinalized + } + + pod.config.InfraContainer.Networks = networks + + return nil + } +} diff --git a/libpod/pod.go b/libpod/pod.go index 3b9bb9c60..4f85caf08 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -1,6 +1,7 @@ package libpod import ( + "net" "time" "github.com/containers/libpod/libpod/define" @@ -97,8 +98,17 @@ type PodContainerInfo struct { // InfraContainerConfig is the configuration for the pod's infra container type InfraContainerConfig struct { - HasInfraContainer bool `json:"makeInfraContainer"` - PortBindings []ocicni.PortMapping `json:"infraPortBindings"` + HasInfraContainer bool `json:"makeInfraContainer"` + PortBindings []ocicni.PortMapping `json:"infraPortBindings"` + StaticIP net.IP `json:"staticIP,omitempty"` + StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"` + UseImageResolvConf bool `json:"useImageResolvConf,omitempty"` + DNSServer []string `json:"dnsServer,omitempty"` + DNSSearch []string `json:"dnsSearch,omitempty"` + DNSOption []string `json:"dnsOption,omitempty"` + UseImageHosts bool `json:"useImageHosts,omitempty"` + HostAdd []string `json:"hostsAdd,omitempty"` + Networks []string `json:"networks,omitempty"` } // ID retrieves the pod's ID diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 6a27c2800..1b1421ca8 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -94,14 +94,38 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID options = append(options, withIsInfra()) // Since user namespace sharing is not implemented, we only need to check if it's rootless - networks := make([]string, 0) netmode := "bridge" if isRootless { netmode = "slirp4netns" } // PostConfigureNetNS should not be set since user namespace sharing is not implemented // and rootless networking no longer supports post configuration setup - options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, networks)) + options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, p.config.InfraContainer.Networks)) + + if p.config.InfraContainer.StaticIP != nil { + options = append(options, WithStaticIP(p.config.InfraContainer.StaticIP)) + } + if p.config.InfraContainer.StaticMAC != nil { + options = append(options, WithStaticMAC(p.config.InfraContainer.StaticMAC)) + } + if p.config.InfraContainer.UseImageResolvConf { + options = append(options, WithUseImageResolvConf()) + } + if len(p.config.InfraContainer.DNSServer) > 0 { + options = append(options, WithDNS(p.config.InfraContainer.DNSServer)) + } + if len(p.config.InfraContainer.DNSSearch) > 0 { + options = append(options, WithDNSSearch(p.config.InfraContainer.DNSSearch)) + } + if len(p.config.InfraContainer.DNSOption) > 0 { + options = append(options, WithDNSOption(p.config.InfraContainer.DNSOption)) + } + if p.config.InfraContainer.UseImageHosts { + options = append(options, WithUseImageHosts()) + } + if len(p.config.InfraContainer.HostAdd) > 0 { + options = append(options, WithHosts(p.config.InfraContainer.HostAdd)) + } return r.newContainer(ctx, g.Config, options...) } diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index 5b0111b85..4afd5760a 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -193,8 +193,6 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) } } - var removalErr error - // We're going to be removing containers. // If we are CGroupfs cgroup driver, to avoid races, we need to hit // the pod and conmon CGroups with a PID limit to prevent them from @@ -205,7 +203,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon") conmonCgroup, err := cgroups.Load(conmonCgroupPath) if err != nil && err != cgroups.ErrCgroupDeleted && err != cgroups.ErrCgroupV1Rootless { - removalErr = errors.Wrapf(err, "error retrieving pod %s conmon cgroup %s", p.ID(), conmonCgroupPath) + logrus.Errorf("Error retrieving pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err) } // New resource limits @@ -216,15 +214,13 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) // Don't try if we failed to retrieve the cgroup if err == nil { if err := conmonCgroup.Update(resLimits); err != nil { - if removalErr == nil { - removalErr = errors.Wrapf(err, "error updating pod %s conmon group", p.ID()) - } else { - logrus.Errorf("Error updating pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err) - } + logrus.Warnf("Error updating pod %s conmon cgroup %s PID limit: %v", p.ID(), conmonCgroupPath, err) } } } + var removalErr error + ctrNamedVolumes := make(map[string]*ContainerNamedVolume) // Second loop - all containers are good, so we should be clear to diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index 333595a96..b144bfa5e 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -143,6 +143,7 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error { func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildInfo) error { var ( namespace []buildah.NamespaceOption + imageID string err error ) @@ -249,7 +250,8 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI c := make(chan error) go func() { - _, _, err := i.Runtime.Build(getContext(), options, newPathDockerFiles...) + iid, _, err := i.Runtime.Build(getContext(), options, newPathDockerFiles...) + imageID = iid c <- err close(c) }() @@ -291,13 +293,9 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI } call.Continues = false - newImage, err := i.Runtime.ImageRuntime().NewFromLocal(config.Output) - if err != nil { - return call.ReplyErrorOccurred(err.Error()) - } br := iopodman.MoreResponse{ Logs: log, - Id: newImage.ID(), + Id: imageID, } return call.ReplyBuildImage(br) } diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go index 36957fc77..8432dbe32 100644 --- a/vendor/github.com/containers/image/v5/copy/copy.go +++ b/vendor/github.com/containers/image/v5/copy/copy.go @@ -380,6 +380,7 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur return nil, "", errors.Wrap(err, "Can not copy signatures") } } + canModifyManifestList := (len(sigs) == 0) // Determine if we'll need to convert the manifest list to a different format. forceListMIMEType := options.ForceManifestMIMEType @@ -394,7 +395,6 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur return nil, "", errors.Wrapf(err, "Error determining manifest list type to write to destination") } if selectedListType != list.MIMEType() { - canModifyManifestList := (len(sigs) == 0) if !canModifyManifestList { return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType) } @@ -451,12 +451,6 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur return nil, "", errors.Wrapf(err, "Error updating manifest list") } - // Check if the updates meaningfully changed the list of images. - listIsModified := false - if !reflect.DeepEqual(list.Instances(), originalList.Instances()) { - listIsModified = true - } - // Perform the list conversion. if selectedListType != list.MIMEType() { list, err = list.ConvertToMIMEType(selectedListType) @@ -465,12 +459,23 @@ func (c *copier) copyMultipleImages(ctx context.Context, policyContext *signatur } } - // If we can't use the original value, but we have to change it, flag an error. - if listIsModified { - manifestList, err = list.Serialize() - if err != nil { - return nil, "", errors.Wrapf(err, "Error encoding updated manifest list (%q: %#v)", list.MIMEType(), list.Instances()) + // Check if the updates or a type conversion meaningfully changed the list of images + // by serializing them both so that we can compare them. + updatedManifestList, err := list.Serialize() + if err != nil { + return nil, "", errors.Wrapf(err, "Error encoding updated manifest list (%q: %#v)", list.MIMEType(), list.Instances()) + } + originalManifestList, err := originalList.Serialize() + if err != nil { + return nil, "", errors.Wrapf(err, "Error encoding original manifest list for comparison (%q: %#v)", originalList.MIMEType(), originalList.Instances()) + } + + // If we can't just use the original value, but we have to change it, flag an error. + if !bytes.Equal(updatedManifestList, originalManifestList) { + if !canModifyManifestList { + return nil, "", errors.Errorf("Error: manifest list must be converted to type %q to be written to destination, but that would invalidate signatures", selectedListType) } + manifestList = updatedManifestList logrus.Debugf("Manifest list has been updated") } diff --git a/vendor/github.com/containers/image/v5/copy/manifest.go b/vendor/github.com/containers/image/v5/copy/manifest.go index bcf082df3..5a3cf06a4 100644 --- a/vendor/github.com/containers/image/v5/copy/manifest.go +++ b/vendor/github.com/containers/image/v5/copy/manifest.go @@ -127,14 +127,14 @@ func isMultiImage(ctx context.Context, img types.UnparsedImage) (bool, error) { // forced value, and returns the MIME type to which we should convert the list // of manifests, whether we are converting to it or using it unmodified. func (c *copier) determineListConversion(currentListMIMEType string, destSupportedMIMETypes []string, forcedListMIMEType string) (string, error) { - // If we're forcing it, we prefer the forced value over everything else. - if forcedListMIMEType != "" { - return forcedListMIMEType, nil - } // If there's no list of supported types, then anything we support is expected to be supported. if len(destSupportedMIMETypes) == 0 { destSupportedMIMETypes = manifest.SupportedListMIMETypes } + // If we're forcing it, replace the list of supported types with the forced value. + if forcedListMIMEType != "" { + destSupportedMIMETypes = []string{forcedListMIMEType} + } var selectedType string for i := range destSupportedMIMETypes { // The second priority is the first member of the list of acceptable types that is a list, @@ -148,9 +148,15 @@ func (c *copier) determineListConversion(currentListMIMEType string, destSupport selectedType = destSupportedMIMETypes[i] } } + logrus.Debugf("Manifest list has MIME type %s, ordered candidate list [%s]", currentListMIMEType, strings.Join(destSupportedMIMETypes, ", ")) if selectedType == "" { return "", errors.Errorf("destination does not support any supported manifest list types (%v)", manifest.SupportedListMIMETypes) } + if selectedType != currentListMIMEType { + logrus.Debugf("... will convert to %s", selectedType) + } else { + logrus.Debugf("... will use the original manifest list type") + } // Done. return selectedType, nil } diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go index 1a44baf99..0fd7a4a37 100644 --- a/vendor/github.com/containers/image/v5/version/version.go +++ b/vendor/github.com/containers/image/v5/version/version.go @@ -8,7 +8,7 @@ const ( // VersionMinor is for functionality in a backwards-compatible manner VersionMinor = 2 // VersionPatch is for backwards-compatible bug fixes - VersionPatch = 0 + VersionPatch = 1 // VersionDev indicates development branch. Releases will be empty string. VersionDev = "" diff --git a/vendor/modules.txt b/vendor/modules.txt index 771e06635..5c2485f38 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -82,7 +82,7 @@ github.com/containers/common/pkg/cgroups github.com/containers/common/pkg/unshare # github.com/containers/conmon v2.0.10+incompatible github.com/containers/conmon/runner/config -# github.com/containers/image/v5 v5.2.0 +# github.com/containers/image/v5 v5.2.1 github.com/containers/image/v5/copy github.com/containers/image/v5/directory github.com/containers/image/v5/directory/explicitfilepath diff --git a/version/version.go b/version/version.go index 4665023a4..d5926d744 100644 --- a/version/version.go +++ b/version/version.go @@ -4,7 +4,7 @@ package version // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -const Version = "1.8.0-dev" +const Version = "1.8.1-dev" // RemoteAPIVersion is the version for the remote // client API. It is used to determine compatibility |