Compare commits

..

537 Commits

Author SHA1 Message Date
Chris Evich
85ce427969 [release-1.11] Bump to release version 1.11.3
This branch is utilized for RHEL releases and therefore should never
ever represent a `-dev` development release.  Bump the version
number to account for the change.

Resolves: RHEL-97092 RHEL-97090

Signed-off-by: Chris Evich <cevich@redhat.com>
2025-08-11 15:38:31 -04:00
Miloslav Trmač
c8ef2dcce3 Merge pull request #2643 from cevich/release-1.11_add_release_test
[release-1.11] Add conditional release-checking system test
2025-07-04 17:13:02 +02:00
Chris Evich
b5b0a9cd81 [release-1.11] Add conditional release-checking system test
Unfortunately on a number of occasions, Skopeo has been released
officially with a `-dev` suffix in the version number.  Assist in
catching this mistake at release time by the addition of a simple
conditional test.  Note that it must be positively enabled by a
magic env. var. before executing the system tests.

Original PR: https://github.com/containers/skopeo/pull/2631

Signed-off-by: Chris Evich <cevich@redhat.com>
2025-07-02 14:30:14 -04:00
Miloslav Trmač
e0171abca9 Merge pull request #2611 from cevich/release-1.11-multiarch_registry
[release-1.11] Support CI testing on non-x86_64
2025-05-28 20:43:09 +02:00
Chris Evich
4773bf1895 Support CI testing on non-x86_64
Previously, internal CI gating tests sometimes fail because the required
registry container image only supports x86_64.  Update to the `2.8.2`
image tag with support for all primary architectures.

Signed-off-by: Chris Evich <cevich@redhat.com>
2025-05-28 14:25:03 -04:00
Miloslav Trmač
7602ac68f8 Merge pull request #2424 from TomSweeneyRedHat/dev/tsweeney/v1.11-cve-2024-3727
[release-1.11] CVE-2024-3727
2024-09-19 21:34:15 +02:00
tomsweeneyredhat
7f996f3bdb [release-1.11] CVE-2024-3727
Addresses CVE-2024-3727 by bumping c/common to v0.51.4 and c/image
to v5.24.3

Fixes: https://issues.redhat.com/browse/OCPBUGS-37020
https://issues.redhat.com/browse/OCPBUGS-37022
https://issues.redhat.com/browse/OCPBUGS-37023

Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
2024-09-19 14:11:24 -04:00
Colin Walters
78dc389125 Merge pull request #2359 from mtrmac/k8s.gcr.io-11
[release-1.11] Refer to registry.k8s.io instead of k8s.gcr.io
2024-06-19 19:41:22 -04:00
Miloslav Trmač
34ed1100de Refer to registry.k8s.io instead of k8s.gcr.io
... per https://kubernetes.io/blog/2023/02/06/k8s-gcr-io-freeze-announcement/ .

We are seeing intermittent failures (sufficient to reliably cause a test suite failure)
pulling from k8s.gcr.io, let's see if using the newer one improves things.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2024-06-19 17:50:41 +02:00
Miloslav Trmač
051898442c Merge pull request #2292 from TomSweeneyRedHat/dev/tsweeney/cve-jose-1.11
[release-1.11] Bump ocicrypt and go-jose CVE-2024-28180
2024-04-18 00:58:42 +02:00
tomsweeneyredhat
89cd9b89b6 [release-1.11] Bump ocicrypt and go-jose CVE-2024-28180
Bump github.com/go-jose/go-jose to v3.0.0 and
github.com/containers/ocicrypt to v1.1.10

Addresses: CVE-2024-28180
https://issues.redhat.com/browse/OCPBUGS-30789
https://issues.redhat.com/browse/OCPBUGS-30790
https://issues.redhat.com/browse/OCPBUGS-30791

Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
2024-04-17 18:15:23 -04:00
Miloslav Trmač
df2b9aedc8 Merge pull request #2286 from mtrmac/integration-update-1.11
[release-1.11] Backport #2280
2024-04-10 20:01:14 +02:00
Miloslav Trmač
6f884cd817 Freeze the fedora-minimal image reference at Fedora 38
... because the tests are assuming a v2s2 image, but
as of Fedora 39, the image uses the OCI format.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2024-04-08 19:55:14 +02:00
Miloslav Trmač
7e11ab4ada Merge pull request #1991 from cevich/release_1.11_add_self_destruct
[release-1.11] Cirrus: Add CI self-destruct condition on EOL date
2023-05-09 16:09:47 +02:00
Chris Evich
9b087c653c [release-1.11] Cirrus: Add CI self-destruct condition on EOL date
This branch will never receive any security-backports when the
associated RHEL release reaches EOL.  Add a condition to force CI to
break with a helpful message, after this RHEL EOL date.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-05-03 11:18:19 -04:00
Miloslav Trmač
d79588e6c1 Bump to v1.11.3-dev
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-04-03 07:51:38 -04:00
Miloslav Trmač
dc1e14f7a7 Release 1.11.2
Updates golang.org/x/net to v0.7.0 to resolve CVE-2022-41723.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-04-03 07:51:38 -04:00
Miloslav Trmač
8191ef3ea1 Merge pull request #1948 from lsm5/release-1.11-CVE-2022-41723
[release-1.11] bump golang.org/x/net to v0.7.0
2023-03-24 22:44:49 +01:00
Lokesh Mandvekar
902506dd73 bump golang.org/x/net to v0.7.0
Resolves: CVE-2022-41723
Ref: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2022-41723

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2023-03-24 09:54:45 +05:30
Miloslav Trmač
3f98753bfd Merge pull request #1912 from TomSweeneyRedHat/dev/tsweeney/1.11.1
[release-1.11] Bump to v1.11.1
2023-02-16 23:21:39 +01:00
tomsweeneyredhat
b2884205e7 [release-1.11] Bump to v1.11.2-dev
As the title says

[NO NEW TESTS NEEDEDED]

Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
2023-02-16 15:16:10 -05:00
tomsweeneyredhat
fb1ade6d9e [release-1.11] Bump to v1.11.1
As the title says.  To ready for RHEL 8.8/9.2

[NO NEW TESTS NEEDED]

Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
2023-02-15 17:27:51 -05:00
Valentin Rothberg
0d212fc3b5 Merge pull request #1902 from mtrmac/c-image-eof-1.11
[release-1.11] Update to c/image 5.24.1
2023-02-13 09:01:03 +01:00
Miloslav Trmač
40dd6507df Update to c/image 5.24.1
... to include an unexpected EOF workaround.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-02-09 21:12:48 +01:00
Daniel J Walsh
cc958d3e5d Move to v1.11.1-dev
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2023-01-26 15:34:30 -05:00
Daniel J Walsh
9d036f3053 Bump to v1.11.0
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2023-01-26 15:34:30 -05:00
Daniel J Walsh
7b886d11bb Merge pull request #1871 from TomSweeneyRedHat/dev/tsweeney/fixlang
Touch up conscious language issues
2023-01-26 15:33:46 -05:00
Valentin Rothberg
17df36a3e6 Merge pull request #1879 from sstosh/fix-docs
[CI:DOCS] Format manual page documents
2023-01-26 08:05:00 +01:00
Toshiki Sonoda
83bcd13659 [CI:DOCS] Format manual page documents
- Add a prompt to the skopeo commands.

- Add a "console" identifier to fenced code
blocks which has a prompt, not "sh".

Signed-off-by: Toshiki Sonoda <sonoda.toshiki@fujitsu.com>
2023-01-25 17:10:11 +09:00
Miloslav Trmač
b3b2c73764 Merge pull request #1877 from containers/renovate/github.com-containers-common-0.x
Update module github.com/containers/common to v0.51.0
2023-01-24 17:57:41 +01:00
renovate[bot]
afbdaf8ecb Update module github.com/containers/common to v0.51.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-01-24 17:39:17 +01:00
Miloslav Trmač
fe15a36ed9 Merge pull request #1876 from containers/renovate/github.com-containers-image-v5-5.x
Update module github.com/containers/image/v5 to v5.24.0
2023-01-23 22:56:19 +01:00
renovate[bot]
c91142485e Update module github.com/containers/image/v5 to v5.24.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-01-23 21:30:51 +00:00
Valentin Rothberg
61c519dcf2 Merge pull request #1869 from mtrmac/generate-keys
Add (skopeo generate-sigstore-key)
2023-01-23 17:54:34 +01:00
Miloslav Trmač
0fad119375 Add (skopeo generate-sigstore-key)
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-23 17:39:09 +01:00
Miloslav Trmač
48b9d94c87 Update c/image after https://github.com/containers/image/pull/1810
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-23 17:39:09 +01:00
Daniel J Walsh
47919520f5 Merge pull request #1868 from mtrmac/developer-system-tests
Fix `make test-system` when run as an unprivileged user (containerized)
2023-01-23 11:13:50 -05:00
Valentin Rothberg
e0a5df297d Merge pull request #1864 from mtrmac/storage-big-hammer
Fix storage.conf overrides in test-system in CI, update c/storage
2023-01-23 10:06:00 +01:00
tomsweeneyredhat
80e3fd1095 Touch up conscious language issues
Touch up a few issues with language in the project to
make it more inclusive.

Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
2023-01-21 17:13:25 -05:00
Miloslav Trmač
9f04dfdec9 Partially fix removal of temporary data in (make test-system)
Use (podman unshare) as already suggested, it is necessary for an unprivileged
user to remove the temporary c/storage state.  OTOH it doesn't work with Docker at all.

Don't use the - prefix, it only works at the _start_ of a rule, not in the middle of
a multi-line shell script.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-20 20:07:45 +01:00
Miloslav Trmač
36c480f643 Don't affect $XDG_RUNTIME_DIR of Podman starting the registry
Otherwise $XDG_RUNTIME_DIR/netns gets created and mounted,
breaking (rm -rf).

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-20 20:06:08 +01:00
renovate[bot]
850bc49d27 Update module github.com/containers/storage to v1.45.3
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-01-20 17:46:01 +01:00
Miloslav Trmač
a98c137243 Fix storage.conf setup in test-system
- Don't do it at all for the CI VM: We can use the
  VM's global Podman configuration, and use faster overlay
  instead of vfs, so let's do that.
- For the developer-run (make test-system):
  - Add graphroot and runroot paths to make the configuration minimally valid
  - Explicitly point CONTAINERS_STORAGE_CONF at the configutation
    to be certain it will get used.

Then drop the (podman pull ...) in runner.sh:_podman_reset that seemed to
previously workaround the invalid /etc/containers/storage.conf .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-20 17:43:21 +01:00
Miloslav Trmač
198155027d Fix (test-integration), in a container without CI
Fixes #1222 .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-20 17:38:29 +01:00
Miloslav Trmač
641efe9930 Merge pull request #1862 from cevich/fix_image_testing
Cirrus: Fix c/image CI testing
2023-01-19 19:28:23 +01:00
Chris Evich
67a8bef6ea Cirrus: Fix c/image CI testing
The containers/image CI setup reuses the runner script from this repo to
execute the skopeo tests.  However, an env. var. is being taken out of
context in that environment, leading to failure.  Fix this by
hard-coding an image-name which will always be available in both
environments.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-19 12:34:03 -05:00
Daniel J Walsh
9d65de7d61 Merge pull request #1861 from containers/dependabot/go_modules/github.com/containers/ocicrypt-1.1.7
Bump github.com/containers/ocicrypt from 1.1.6 to 1.1.7
2023-01-19 08:05:35 -05:00
dependabot[bot]
63da8390f1 Bump github.com/containers/ocicrypt from 1.1.6 to 1.1.7
Bumps [github.com/containers/ocicrypt](https://github.com/containers/ocicrypt) from 1.1.6 to 1.1.7.
- [Release notes](https://github.com/containers/ocicrypt/releases)
- [Commits](https://github.com/containers/ocicrypt/compare/v1.1.6...v1.1.7)

---
updated-dependencies:
- dependency-name: github.com/containers/ocicrypt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-19 09:03:20 +00:00
Miloslav Trmač
b51eb214c2 Merge pull request #1821 from cevich/F37_update
Cirrus: Update to F37 CI VM Images
2023-01-18 17:16:13 +01:00
Chris Evich
1fac61ef57 Cirrus: Add a common intra-test reset function
This is necessary, since running the skopeo tests modifies the host
environment.  This can result in some warning messages the first time
a container is started.  These messages can interfere with tests which
are sensitive to stdout/stderr.  Since many/most tests require a local
image registry, launch it with `/bin/true` after doing a system reset
to clear away any pesky warning messages.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-18 10:09:44 -05:00
Chris Evich
292962d34c Fix unnecessary use of podman in CI test
For whatever reasons, the podman configuration in CI results in the
inspect test throwing the following error:

```
not ok 4 inspect: image manifest list w/ diff platform
125
configuration is unset - using hardcoded default graph root
\"/var/lib/containers/storage\""
configuration is unset - using hardcoded default graph root
\"/var/lib/containers/storage\""
StoreOptions
```

Fix this by not using `podman`. It's unnecessary, since all the test
needs is the golang-flavor of the current system's architecture name.
That can easily be obtained by asking the go tool directly.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-18 10:09:44 -05:00
Chris Evich
e239f32ae0 Cirrus: Update to F37 CI VM Images
Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-18 10:09:44 -05:00
Chris Evich
ee8048583b Cirrus: Remove redundant package install attempt
These are already present in the VM images.  These instructions only
cause the DNF cache to be refreshed, wasting precious developer time.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-18 10:09:43 -05:00
Miloslav Trmač
1db6846c01 Merge pull request #1857 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.45.1
2023-01-18 15:14:35 +01:00
renovate[bot]
0698e82b30 fix(deps): update module github.com/containers/storage to v1.45.1
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-01-18 00:13:07 +00:00
Daniel J Walsh
8e09e641bf Merge pull request #1849 from mtrmac/sign-by-sigstore
Add support for Fulcio and Rekor, and --sign-by-sigstore=param-file
2023-01-16 04:22:41 -05:00
Miloslav Trmač
bb1ac89327 Add support for Fulcio and Rekor, and --sign-by-sigstore=param-file
(skopeo copy) and (skopeo sync) now support --sign-by-sigstore=param-file,
using the containers-sigstore-signing-params.yaml(5) file format.

That notably adds support for Fulcio and Rekor signing.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-14 13:33:57 +01:00
Miloslav Trmač
03b5bdec24 Update c/image after https://github.com/containers/image/pull/1787
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-14 13:33:00 +01:00
Miloslav Trmač
28995cd5d4 Merge pull request #1853 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.45.0
2023-01-13 12:57:40 +01:00
renovate[bot]
1133a2a395 fix(deps): update module github.com/containers/storage to v1.45.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-01-13 01:15:34 +00:00
Miloslav Trmač
28175104d7 Merge pull request #1850 from cevich/simple_release_ci
Cirrus: Skip OSX CI on release-branches
2023-01-12 21:47:56 +01:00
Chris Evich
d0cf39d860 Cirrus: Skip OSX CI on release-branches
This task does not make sense to maintain long-term on release
branches.  Its intent is always/only to test the latest/greatest code
and environment.  After release, it's simply too difficult to maintain
functioning CI with a constantly changing (Cirrus-managed) OSX environment.
Ensure the task only runs for PRs targeted at the default branch, or if
the current branch is the default branch.

Signed-off-by: Chris Evich <cevich@redhat.com>
2023-01-12 15:27:15 -05:00
Daniel J Walsh
71fa1f441f Merge pull request #1848 from mtrmac/stdout
Correctly use the stdout parameter in some places
2023-01-11 18:04:18 -05:00
Miloslav Trmač
f17eafe85b Correctly use the stdout parameter in some places
Should not change behavior - it would matter for unit tests
which don't exist.

Also, promptForPassphrase must continue to hard-code "real" os.Stdin and os.Stdout.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-11 22:09:35 +01:00
Miloslav Trmač
4517ea0b7b Merge pull request #1839 from containers/renovate/golang.org-x-term-0.x
fix(deps): update module golang.org/x/term to v0.4.0
2023-01-04 20:03:54 +01:00
renovate[bot]
58bccf3882 fix(deps): update module golang.org/x/term to v0.4.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-01-04 18:38:35 +00:00
Daniel J Walsh
e71305f7bb Merge pull request #1835 from containers/renovate/actions-stale-7.x
[skip-ci] Update actions/stale action to v7
2023-01-02 07:29:10 -05:00
renovate[bot]
f0c08985b3 [skip-ci] Update actions/stale action to v7
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2023-01-02 11:27:05 +00:00
Valentin Rothberg
ae44ecd570 Merge pull request #1837 from cgwalters/blob-close
proxy: Fix leak of blobs from containers-storage
2023-01-02 09:35:32 +01:00
Colin Walters
92e3146aa0 proxy: Fix leak of blobs from containers-storage
Missing `.Close()` on the blob currently leaks a temporary
file.  Noticed this when doing repeated pulls.

Signed-off-by: Colin Walters <walters@verbum.org>
2022-12-30 11:35:10 -05:00
Colin Walters
f5aaabd5cc Merge pull request #1828 from cgwalters/update-http-vendoring
vendor: Bump golang.org/x/net to 4.0
2022-12-13 16:52:28 -05:00
Colin Walters
960713da32 vendor: Bump golang.org/x/net to 4.0
I originally thought I needed this to fix a build, but that
was apparently not the case.

Signed-off-by: Colin Walters <walters@verbum.org>
2022-12-13 16:36:57 -05:00
Miloslav Trmač
60ecf7a031 Merge pull request #1825 from cgwalters/auto-close-images
proxy: Ensure images are closed when proxy is shutting down
2022-12-13 21:50:22 +01:00
Colin Walters
b51f8ea200 proxy: Ensure images are closed when proxy is shutting down
This is a complementary fix for
https://github.com/coreos/rpm-ostree/issues/4213

Basically in the case of `oci-archive` we have a temporary
directory that needs cleanup.

Signed-off-by: Colin Walters <walters@verbum.org>
Co-authored-by: Miloslav Trmač <mitr@redhat.com>
Signed-off-by: Colin Walters <walters@verbum.org>
2022-12-13 15:30:55 -05:00
Valentin Rothberg
e024c43892 Merge pull request #1817 from mtrmac/copy-archive-example
Add an example for creating a docker-archive file
2022-12-07 09:19:47 +01:00
Miloslav Trmač
9c6cbc94c7 Add an example for creating a docker-archive file
... with the image correctly tagged.

I also snuck a warning against `docker-archive:` in there.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-12-06 23:47:34 +01:00
Miloslav Trmač
fb4c49739f Merge pull request #1802 from RishabhSaini/issue/153
proxy: Add LayerInfoJSON API
2022-12-06 23:37:54 +01:00
RishabhSaini
3eb9d71d7f proxy: Add GetLayerInfo API
Extract the LayerInfos of cached image
used for exposing diffIDs of Blobs

Signed-off-by: RishabhSaini <rsaini@redhat.com>
2022-12-06 16:57:12 -05:00
Miloslav Trmač
6e6104ff8b Merge pull request #1818 from containers/renovate/golang.org-x-term-0.x
fix(deps): update module golang.org/x/term to v0.3.0
2022-12-06 21:01:49 +01:00
renovate[bot]
46d48295fb fix(deps): update module golang.org/x/term to v0.3.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2022-12-06 19:47:04 +00:00
Miloslav Trmač
c093484820 Merge pull request #1820 from cevich/fix_job_sequence
[skip-ci] GHA/Cirrus-cron: Fix execution order
2022-12-06 19:57:04 +01:00
Chris Evich
3212bbed6f [skip-ci] GHA/Cirrus-cron: Fix execution order
Fairly universally, the last Cirrus-Cron job is set to fire off at
22:22 UTC.  However, the re-run of failed jobs GHA workflow was
scheduled for 22:05, meaning it will never re-run the last cirrus-cron
job should it fail.

Re-arrange the execution order so as to give plenty of time between the
last cirrus-cron job starting, the auto-re-run attempt, and the final
failure-check e-mail.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-12-06 10:26:18 -05:00
Miloslav Trmač
b72a5c98a9 Merge pull request #1767 from Phuurl/main
Adds `--append-suffix` flag to sync command
2022-11-29 20:46:18 +01:00
Miloslav Trmač
f6d587d816 Merge pull request #1815 from kerel-fs/update_readme
README: Update example to show newly exposed LayerData
2022-11-28 20:43:49 +01:00
Fabian P. Schmidt
40ba7a27af Update skopeo-inspect man page example
Patch created by re-running the two example commands and manually
abbreviating long lists in the output.

Fixes #1766.

Signed-off-by: Fabian P. Schmidt <kerel@mailbox.org>
2022-11-28 18:12:35 +01:00
Fabian P. Schmidt
278be5a5d0 README: Update example to show newly exposed LayerData
Since d9dfc44 the 'skopeo inspect' command exposes the LayerData
which often contains the layer size. This is a very useful feature
so we mentioned it in the README now.

Signed-off-by: Fabian P. Schmidt <kerel@mailbox.org>
2022-11-25 17:35:28 +01:00
Miloslav Trmač
dc3f2b6cec Merge pull request #1813 from ashley-cui/cirrusm1
[CI:BUILD] Cirrus: Migrate OSX task to M1
2022-11-23 18:30:29 +01:00
Ashley Cui
b5ac534960 [CI:BUILD] Cirrus: Migrate OSX task to M1
Migrate our OSX build to a M1 instance, since Cirrus is sunsetting Intel-based macOS instances.

Signed-off-by: Ashley Cui <acui@redhat.com>
2022-11-22 10:49:55 -05:00
Chris Evich
661c9698ee Merge pull request #1811 from cevich/update_gha
[skip-ci] GHA: Add cirrus-cron auto-rerun job
2022-11-21 10:24:24 -05:00
Phil Corbett
35532b2404 Adds sync with tag suffix example
Signed-off-by: Phil Corbett <phil@phicorb.me.uk>
2022-11-17 17:49:27 +00:00
Chris Evich
1af1d9c261 GHA: Add cirrus-cron auto-rerun job
Also update the cirrus-cron monitoring job to reuse the podman workflow
instead of buildah.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-15 14:40:38 -05:00
Phil Corbett
bdf1930221 Adds --append-suffix flag to sync
Signed-off-by: Phil Corbett <phil@phicorb.me.uk>
2022-11-13 13:57:38 +00:00
Miloslav Trmač
b665ac4c09 Merge pull request #1808 from cevich/revdep-test-proxy
Cirrus: Add reverse-deps. test to verify proxy with ostree-rs-ext
2022-11-10 16:01:24 +01:00
Valentin Rothberg
e62fcca5ed Merge pull request #1809 from containers/renovate/github.com-containers-storage-1.x
fix(deps): update module github.com/containers/storage to v1.44.0
2022-11-09 09:58:19 +01:00
renovate[bot]
563c91a2fd fix(deps): update module github.com/containers/storage to v1.44.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2022-11-08 19:50:20 +00:00
Chris Evich
cf29c73079 Merge pull request #1805 from containers/renovate/actions-stale-6.x
[skip-ci] Update actions/stale action to v6
2022-11-08 14:48:13 -05:00
Chris Evich
e1fdb4da03 Cirrus: Add reverse-deps. test to verify proxy ext
This does reverse-dependency testing, verifying `proxy.go` using
the ostree-rs-ext Rust code's unit tests.

Based on #1781 by @cgwalters

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-08 12:45:25 -05:00
renovate[bot]
d06bf27eb8 [skip-ci] Update actions/stale action to v6
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2022-11-08 12:21:07 +00:00
Miloslav Trmač
7e6264136c Merge pull request #1804 from containers/renovate/golang.org-x-term-0.x
fix(deps): update module golang.org/x/term to v0.2.0
2022-11-08 13:20:25 +01:00
renovate[bot]
8410bfdd91 fix(deps): update module golang.org/x/term to v0.2.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2022-11-07 22:25:08 +00:00
Daniel J Walsh
6136a2b9c3 Merge pull request #1803 from cevich/renovate_rebase
Renovate: Override global no-rebase option
2022-11-07 17:23:00 -05:00
Chris Evich
16d4a81b79 Renovate: Override global no-rebase option
The `behind-base-branch` setting means:

    Renovate will rebase whenever the branch falls 1 or more
    commit behind its base branch

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-07 15:28:21 -05:00
Chris Evich
794d6b4650 Merge pull request #1788 from containers/renovate/actions-stale-digest
chore(deps): update actions/stale digest to 65b52af
2022-11-07 15:02:30 -05:00
renovate[bot]
2b55a7231a chore(deps): update actions/stale to v3
Signed-off-by: Renovate Bot <bot@renovateapp.com>
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-11-07 14:56:58 -05:00
Miloslav Trmač
62e698b567 Merge pull request #1800 from containers/renovate/github.com-spf13-cobra-1.x
fix(deps): update module github.com/spf13/cobra to v1.6.1
2022-11-01 01:31:43 +01:00
renovate[bot]
f968b2a890 fix(deps): update module github.com/spf13/cobra to v1.6.1
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2022-10-31 03:11:30 +00:00
Valentin Rothberg
2739a29aea Merge pull request #1797 from mtrmac/warnings
Close a HTTP response body
2022-10-27 13:18:09 +02:00
Miloslav Trmač
fe5c4091ee Close a HTTP response body
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-27 08:00:52 +02:00
Daniel J Walsh
5a8d72635c Merge pull request #1791 from containers/renovate/golang.org-x-term-0.x
fix(deps): update module golang.org/x/term to v0.1.0
2022-10-24 06:56:37 -04:00
Daniel J Walsh
88f6ff09f9 Merge pull request #1789 from containers/renovate/github.com-stretchr-testify-1.x
fix(deps): update module github.com/stretchr/testify to v1.8.1
2022-10-24 06:55:28 -04:00
renovate[bot]
d5327bced1 fix(deps): update module golang.org/x/term to v0.1.0
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2022-10-24 08:51:19 +00:00
renovate[bot]
6d3d9a3bb2 fix(deps): update module github.com/stretchr/testify to v1.8.1
Signed-off-by: Renovate Bot <bot@renovateapp.com>
2022-10-24 05:27:27 +00:00
Valentin Rothberg
723351cec1 Merge pull request #1786 from mtrmac/update-image
Update to c/image main branch
2022-10-21 08:04:34 +02:00
Miloslav Trmač
5c69302d75 Update to c/image main branch
> go get github.com/containers/image/v5@main
> make vendor

... to make sure that we don't regress against Skopeo 1.9.3.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-20 20:09:25 +02:00
Daniel J Walsh
bdbb46be5a Merge pull request #1783 from vrothberg/bump
bump to v1.11.0-dev
2022-10-19 10:27:21 -04:00
Valentin Rothberg
6d564d4de8 bump to v1.11.0-dev
Given there is a release-1.10 branch, we should bump main to the next
minor version.

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
2022-10-19 14:45:29 +02:00
Miloslav Trmač
01201df865 Merge pull request #1780 from containers/renovate/configure
[CI:DOCS] Configure Renovate
2022-10-18 23:09:36 +02:00
renovate[bot]
4c0e565038 chore(deps): add renovate.json
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-10-18 15:02:34 -04:00
Miloslav Trmač
03da797e42 Merge pull request #1776 from cgwalters/more-proxy-bits
proxy: Bump semver for OpenImageOptional
2022-10-17 16:24:12 +02:00
Colin Walters
757ec5dbf6 proxy: Bump semver for OpenImageOptional
I should have done this in the previous commit, it's how
clients can discover that we have the API.

Signed-off-by: Colin Walters <walters@verbum.org>
2022-10-13 16:09:19 -04:00
Miloslav Trmač
08c290170d Merge pull request #1757 from cgwalters/get-manifest-optional
proxy: Add `OpenImageOptional`
2022-10-13 03:28:06 +02:00
Colin Walters
08b27fc50e proxy: Add OpenImageOptional
In some code I'm writing I want to be able to cleanly test if an
image exists, as distinguished from other errors like authentication
problems, network flakes etc.

As best I can tell, the containers/image abstraction doesn't
offer a clean way to do this.

For now, I chose the route of adding the ugly string error matching
here for the two cases I care about (docker v2s2 registry and oci
directories), so my Rust code can operate in terms of clean
`Option<Image>`.

Signed-off-by: Colin Walters <walters@verbum.org>
2022-10-12 21:11:55 -04:00
Miloslav Trmač
7738dbb335 Merge pull request #1522 from mtrmac/collapse-errors
Update for https://github.com/containers/image/pull/1299
2022-10-12 23:27:27 +02:00
Miloslav Trmač
9b6f5b6e75 Add a workaround for public.ecr.aws not implementing tag list at all
Per https://github.com/containers/skopeo/issues/1230 , and
155d0665e8/docker/errors_test.go (L88) .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:57:29 +02:00
Miloslav Trmač
632cebd74e Update AWS workaround to use Golang types
FIXME: This is not actually tested against a representative
error; we basically assume generic "scope is not sufficient" handling.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:57:22 +02:00
Miloslav Trmač
ea9aa68b0f Reorganize the "list tags failed" logic in inspect.go a bit
... to allow adding more cases.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:57:14 +02:00
Miloslav Trmač
c476d62671 Remove a (skopeo inspect) workaround for IBM Bluemix
AFAICT, “IBM Bluemix” has become “IBM Cloud”, and the “Bluemix” registry
is now (somehow related to?) icr.io; e.g.
https://cloud.ibm.com/docs/Registry?topic=Registry-registry_overview
lists bluemix.net and icr.io host names.

Randomly looking for a public image hosted on that registry, at least
> skopeo list-tags docker://icr.io/codeengine/firstjob
now succeeds.

So I’m assuming that at least the current cloud deployment now allows
listing tags, and does not need special handling. (It's unclear if
that is true for all existing deployments.)

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:57:07 +02:00
Miloslav Trmač
fce2cf9c72 Fix an error message to refer to repo, not a single image
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:56:59 +02:00
Miloslav Trmač
9724da1ff2 Remove a special case for failing to list tags in (skopeo sync)
- It's unclear why it exists in the first place
- Looking at callers of imagesToCopyFromRepo, the only caller of this:
  either the input is a single repo, in which case the failure to
  list tags clearly results in a no-op and a "No images to sync" fatal
  failure ...
- ... or the input is YAML, and in that case the caller is already
  skipping the repo on a failure.

Either way, it's unclear why we would have a special "Registry disallows
tag retrieval" error special case instead of the generic text.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:56:52 +02:00
Miloslav Trmač
955a59c864 Update tests for changed error texts
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:56:46 +02:00
Miloslav Trmač
ae50898b8a Include c/image after https://github.com/containers/image/pull/1299
> go get github.com/containers/image/v5@main
> make vendor

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:56:18 +02:00
Miloslav Trmač
f3aee25c7c Fold a long line.
Should not change (test) behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:10:01 +02:00
Miloslav Trmač
1983173b60 Remove single-use "wanted" variables
They were useful before assertSkopeoSucceeds/assertSkopeoFails,
when they were used multiple times. Now, they don't
make the code any shorter.

Should not change (test) behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-12 22:09:48 +02:00
Miloslav Trmač
3411ebd462 Merge pull request #1775 from containers/dependabot/go_modules/github.com/spf13/cobra-1.6.0
Bump github.com/spf13/cobra from 1.5.0 to 1.6.0
2022-10-12 22:05:33 +02:00
dependabot[bot]
4ccfb033fb Bump github.com/spf13/cobra from 1.5.0 to 1.6.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.5.0 to 1.6.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.5.0...v1.6.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-12 08:11:32 +00:00
Daniel J Walsh
2133fa36da Merge pull request #1772 from containers/dependabot/go_modules/github.com/containers/ocicrypt-1.1.6
Bump github.com/containers/ocicrypt from 1.1.5 to 1.1.6
2022-10-10 09:40:05 -04:00
dependabot[bot]
a495155030 Bump github.com/containers/ocicrypt from 1.1.5 to 1.1.6
Bumps [github.com/containers/ocicrypt](https://github.com/containers/ocicrypt) from 1.1.5 to 1.1.6.
- [Release notes](https://github.com/containers/ocicrypt/releases)
- [Commits](https://github.com/containers/ocicrypt/compare/v1.1.5...v1.1.6)

---
updated-dependencies:
- dependency-name: github.com/containers/ocicrypt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-10 08:10:52 +00:00
Miloslav Trmač
032fd15c10 Merge pull request #1770 from containers/dependabot/go_modules/github.com/opencontainers/image-spec-1.1.0-rc2
Bump github.com/opencontainers/image-spec from 1.1.0-rc1 to 1.1.0-rc2
2022-10-07 19:20:18 +02:00
dependabot[bot]
e021b675e2 Bump github.com/opencontainers/image-spec from 1.1.0-rc1 to 1.1.0-rc2
Bumps [github.com/opencontainers/image-spec](https://github.com/opencontainers/image-spec) from 1.1.0-rc1 to 1.1.0-rc2.
- [Release notes](https://github.com/opencontainers/image-spec/releases)
- [Changelog](https://github.com/opencontainers/image-spec/blob/main/RELEASES.md)
- [Commits](https://github.com/opencontainers/image-spec/compare/v1.1.0-rc1...v1.1.0-rc2)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/image-spec
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-04 08:26:31 +00:00
Daniel J Walsh
7ee3396575 Merge pull request #1765 from mtrmac/v1.10.0
Release v1.10.0
2022-09-30 21:44:33 -04:00
Miloslav Trmač
5eace4078f Bump to v1.10.1-dev
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-09-30 20:43:02 +02:00
Miloslav Trmač
ee60474d5a Release v1.10.0
(skopeo inspect) now provides more information about individual layers.

The default /etc/containers/registries.d/default.yaml now has all entries
commented-out, to use built-in defaults; that can change the default for lookaside-staging
to use an unprivileged users' home directory instead of a path in /var/.

-  GHA: Re-use identical workflow from buildah repo
-  Optimize upstream skopeo container image build
-  Fix running tests on macOS
-  Reformat with Go 1.19's gofmt
-  Fix a comment
-  Fix looking for commands with GNU make 4.2.1
-  Talk about "registry repositories" in (skopeo sync) documentation
-  Point at --all in the --preserve-digests option documentation
-  Remove unused GIT_BRANCH definition
-  Don't include git commit from a parent directory in the --version output
-  Update for c/image's update of github.com/gobuffalo/pop
-  Merge pull request #1737 from mtrmac/pop-v5-override
-  Stop using docker/docker/pkg/homedir in tests
-  add inspect layersData
-  Don't abort sync if the registry returns invalid tags
-  warn users about --dest-compress and --dest-decompress misuse
-  document imageDestOptions.warnAboutIneffectiveOptions()
-  warn about ineffective destination opts in sync cmd
-  default.yaml should have all options commented
-  Fix documentation in the default registries.d content.
-  [CI:DOCS] Add quay-description update reminder
-  Revert addition of -compat=1.17 to (go mod tidy)
-  Update for https://github.com/klauspost/pgzip/pull/50

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-09-30 20:39:56 +02:00
Miloslav Trmač
ff2a361a0a Merge pull request #1764 from mtrmac/pgzip-update
Pgzip update
2022-09-30 20:36:22 +02:00
Miloslav Trmač
7ebff0f533 Update for https://github.com/klauspost/pgzip/pull/50
... to fix reads of compressed data by docker-archive:

> go get github.com/klauspost/pgzip@master
> make vendor

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-09-30 19:51:07 +02:00
Miloslav Trmač
787e10873c Revert addition of -compat=1.17 to (go mod tidy)
Typically, the compat with earlier versions causes us to use
newer versions of dependencies, which can only be a good thing.

Over time, the 1.17 version reference is just going to become obsolete.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-09-30 19:46:17 +02:00
Valentin Rothberg
a2f29acc7d Merge pull request #1763 from rhatdan/VENDOR
Update vendor containers/(common,image)
2022-09-30 15:52:54 +02:00
Daniel J Walsh
ee84302b60 Update vendor containers/(common,image)
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-09-30 06:38:07 -04:00
Miloslav Trmač
a169ccf8f3 Merge pull request #1759 from cevich/image_readme
[CI:DOCS] Add quay-description update reminder
2022-09-29 22:52:14 +02:00
Chris Evich
89ae387d7b [CI:DOCS] Add quay-description update reminder
Signed-off-by: Chris Evich <cevich@redhat.com>
2022-09-29 14:31:21 -04:00
Daniel J Walsh
66fe7af769 Merge pull request #1758 from containers/dependabot/go_modules/github.com/containers/storage-1.43.0
Bump github.com/containers/storage from 1.42.0 to 1.43.0
2022-09-29 07:15:00 -04:00
dependabot[bot]
feabfac2a7 Bump github.com/containers/storage from 1.42.0 to 1.43.0
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.42.0 to 1.43.0.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.42.0...v1.43.0)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-29 08:12:13 +00:00
Valentin Rothberg
cf354b7abd Merge pull request #1753 from mtrmac/registries.d
Fix documentation in the default registries.d content.
2022-09-26 10:35:08 +02:00
Miloslav Trmač
18a95f947e Fix documentation in the default registries.d content.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-09-21 22:06:52 +02:00
Daniel J Walsh
07da29fd37 Merge pull request #1750 from rhatdan/defaults
default.yaml should have all options commented
2022-09-13 13:18:08 -04:00
Daniel J Walsh
9b40f0be2f default.yaml should have all options commented
Rely on the hard coded defaults in libraries rather then overriding in
the yaml file.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-09-12 19:43:55 -04:00
Daniel J Walsh
869d496f18 Merge pull request #1747 from erolkskn/warn_dest_compress
warn users about --dest-compress and --dest-decompress misuse
2022-09-12 17:06:32 -04:00
Erol Keskin
166b587a77 warn about ineffective destination opts in sync cmd
Signed-off-by: Erol Keskin <erolkeskin.dev@gmail.com>
2022-09-11 00:27:18 +03:00
Erol Keskin
0a42c33af9 document imageDestOptions.warnAboutIneffectiveOptions()
Signed-off-by: Erol Keskin <erolkeskin.dev@gmail.com>
2022-09-08 02:17:24 +03:00
Erol Keskin
90c5033886 warn users about --dest-compress and --dest-decompress misuse
Signed-off-by: Erol Keskin <erolkeskin.dev@gmail.com>
2022-09-08 02:11:08 +03:00
Erol Keskin
d3ff6e2635 warn users about --dest-compress and --dest-decompress misuse
Signed-off-by: Erol Keskin <erolkeskin.dev@gmail.com>
2022-09-07 03:38:28 +03:00
Daniel J Walsh
06cf25fb53 Merge pull request #1745 from mtrmac/sync-invalid-registry-tag
Don't abort sync if the registry returns invalid tags
2022-09-03 07:08:46 -04:00
Miloslav Trmač
3a05dca94e Don't abort sync if the registry returns invalid tags
The user is not very likely to be able to do anything about that,
and we have no other way to read those images - so just skip them;
we already skip image copies in much more directly user-caused
situations, including invalid user-provided strings.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-09-02 00:15:40 +02:00
Miloslav Trmač
7bbaffc4f4 Merge pull request #1738 from ningmingxiao/enchance_inspect
add inspect layersData
2022-08-25 19:37:02 +02:00
Miloslav Trmač
2b948c177a Merge pull request #1731 from mtrmac/docker_homedir
Stop using docker/docker/pkg/homedir in tests
2022-08-24 17:57:28 +02:00
ningmingxiao
d9dfc44888 add inspect layersData
Signed-off-by: ningmingxiao <ning.mingxiao@zte.com.cn>
2022-08-24 12:05:53 +08:00
Miloslav Trmač
ba23a9162f Stop using docker/docker/pkg/homedir in tests
c/storage/pkg/homedir, which we need anyway for other purposes,
should work just as well.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-23 22:30:12 +02:00
Miloslav Trmač
1eada90813 Merge pull request #1737 from mtrmac/pop-v5-override
Update for c/image's update of github.com/gobuffalo/pop
2022-08-23 22:28:56 +02:00
Miloslav Trmač
4b9ffac0cc Update for c/image's update of github.com/gobuffalo/pop
> go get github.com/containers/image/v5@main
> go mod tidy -go=1.16 && go mod tidy -go=1.17
> make vendor

The (go mod tidy) pair is necessary to keep c/image CI working.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-23 22:00:00 +02:00
Miloslav Trmač
a81460437a Merge pull request #1736 from mtrmac/git-ceiling
Don't include git commit from a parent directory in the --version output
2022-08-23 19:55:28 +02:00
Miloslav Trmač
f36752a279 Don't include git commit from a parent directory in the --version output
This can happen when building RPMs out of tarballs (which don't contain
the .git repository).

To test:
> make -n /bin/skopeo; mv .git ../.git ; make -n bin/skopeo

Fixes #1707 .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-23 19:28:22 +02:00
Miloslav Trmač
4e2dee4362 Remove unused GIT_BRANCH definition
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-23 19:28:22 +02:00
Daniel J Walsh
d58e59a57d Merge pull request #1735 from mtrmac/preserve-digests-all
Point at --all in the --preserve-digests option documentation
2022-08-23 13:27:17 -04:00
Miloslav Trmač
3450c11a0d Point at --all in the --preserve-digests option documentation
Fixes #1720 .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-23 19:12:32 +02:00
Valentin Rothberg
b2e7139331 Merge pull request #1734 from mtrmac/sync-repositories
Talk about "registry repositories" in (skopeo sync) documentation
2022-08-23 10:23:16 +02:00
Miloslav Trmač
3a808c2ed5 Talk about "registry repositories" in (skopeo sync) documentation
- We don't sync complete registries.
- This should still refer to the remote registry servers.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-22 22:27:57 +02:00
Miloslav Trmač
04169cac6e Fix looking for commands with GNU make 4.2.1
Before https://git.savannah.gnu.org/cgit/make.git/commit/job.c?h=4.3&id=1af314465e5dfe3e8baa839a32a72e83c04f26ef ,
make was incorrectly trying to avoid running a shell for (command -v).
Use the workaround recommended in https://savannah.gnu.org/bugs/index.php?57625 .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-22 14:57:08 -04:00
Miloslav Trmač
a99bd0c9e3 Fix a comment
... to make it explicit which variable it refers to.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-22 14:57:08 -04:00
Daniel J Walsh
97c3eabacf Merge pull request #1729 from mtrmac/with-go1.19
Fix building with Go 1.19
2022-08-11 15:24:12 -04:00
Miloslav Trmač
fa2b15ff76 Reformat with Go 1.19's gofmt
This is just the minimal update; I didn't review all
existing comments for using all the new syntax.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-10 21:19:51 +02:00
Miloslav Trmač
9e79da5e33 Fix running tests on macOS
It doesn't support the 's' suffix in (sleep 5s). Seconds
is the default on Linux as well.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-10 21:19:51 +02:00
Daniel J Walsh
97cb423b52 Merge pull request #1725 from cevich/reuse_check_cirrus_cron
[CI:DOCS] GHA: Re-use identical workflow from buildah repo
2022-08-09 13:03:35 -04:00
Daniel J Walsh
32f24e8870 Merge pull request #1723 from cevich/fix_skopeoimage_upstream
[CI:BUILD] Optimize upstream skopeo container image build
2022-08-09 13:02:47 -04:00
Chris Evich
a863a0dccb Optimize upstream skopeo container image build
Running cross-platform compiles using emulation is a painfully
slow process.  Since CI-runtime is limited, improve image build time
by leveraging the automatic RPM builds occurring for the podman-next
COPR repo.  This adds build-time efficiency by offloading the
compilation task.  Note: These RPMs are built any time the 'main'
branch changes, so they'll still be very recent.

https://copr.fedorainfracloud.org/coprs/rhcontainerbot/podman-next/

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-08-04 17:20:37 -04:00
Chris Evich
67a4e04471 GHA: Re-use identical workflow from buildah repo
It's a PITA to maintain duplicate code across repos.  Relatively
github-actions added a feature that allows re-using workflows
from other repos.  Use that here to reduce duplication.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-08-02 16:17:02 -04:00
Daniel J Walsh
14b05e8064 Merge pull request #1724 from mtrmac/release-1.9.2
Release 1.9.2
2022-08-02 14:12:59 -04:00
Miloslav Trmač
e95123a2d4 Bump to v1.9.3-dev
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-02 18:59:24 +02:00
Miloslav Trmač
ca1b0f34d1 Release v1.9.2
- [CI:DOCS] Cirrus: Use the latest imgts container
- Cirrus: Update CI VM images to match podman CI
- Bump github.com/containers/common from 0.49.0 to 0.49.1

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-08-02 18:58:52 +02:00
Daniel J Walsh
28a5365945 Merge pull request #1722 from containers/dependabot/go_modules/github.com/containers/common-0.49.1
Bump github.com/containers/common from 0.49.0 to 0.49.1
2022-08-01 07:21:40 -04:00
dependabot[bot]
73a668e99d Bump github.com/containers/common from 0.49.0 to 0.49.1
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.49.0 to 0.49.1.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.49.0...v0.49.1)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-01 08:20:56 +00:00
Miloslav Trmač
61c28f5d47 Merge pull request #1711 from cevich/updated_f36
Cirrus: Update CI VM images to match podman CI
2022-07-29 00:24:59 +02:00
Chris Evich
eafd7e5518 Cirrus: Update CI VM images to match podman CI
Note: Removed disused `PRIOR_FEDORA*` and `UBUNTU_*` references since
they're not actually used in this CI.  Further, F35 VM images were not
built as part of `c6013173500215296` due to a missing golang 1.18
requirement for podman.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-07-27 14:22:05 -04:00
Chris Evich
2cfbeb2db8 Merge pull request #1714 from cevich/latest_imgts
[CI:DOCS] Cirrus: Use the latest imgts container
2022-07-26 15:59:58 -04:00
Chris Evich
b9cf626ea3 [CI:DOCS] Cirrus: Use the latest imgts container
Contains important updates re: preserving release-branch CI VM images.
Ref: https://github.com/containers/automation_images/pull/157

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-07-26 15:47:39 -04:00
Daniel J Walsh
263d3264ba Merge pull request #1713 from mtrmac/release
Release v1.9.1
2022-07-25 15:54:20 -04:00
Miloslav Trmač
63dabfcf8b Bump to v1.9.2-dev
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-25 21:15:41 +02:00
Miloslav Trmač
2eac0f463a Release v1.9.1
- Bump github.com/sirupsen/logrus from 1.8.1 to 1.9.0
- Bump github.com/containers/storage from 1.41.0 to 1.42.0
- Update to github.com/containers/image/v5 v5.22.0
- Update to github.com/containers/common v0.49.0
- Stop using deprecated names from c/common/pkg/retry

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-25 21:14:44 +02:00
Miloslav Trmač
c10b63dc71 Merge pull request #1712 from mtrmac/release
Update c/image and c/common
2022-07-25 21:13:40 +02:00
Miloslav Trmač
b7e7374e71 Stop using deprecated names from c/common/pkg/retry
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-25 18:16:13 +02:00
Miloslav Trmač
08846d18cc Update to github.com/containers/common v0.49.0
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-25 18:13:11 +02:00
Miloslav Trmač
049163fcec Update to github.com/containers/image/v5 v5.22.0
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-25 18:05:03 +02:00
Miloslav Trmač
3039cd5a77 Merge pull request #1710 from containers/dependabot/go_modules/github.com/containers/storage-1.42.0
Bump github.com/containers/storage from 1.41.0 to 1.42.0
2022-07-22 16:28:11 +02:00
dependabot[bot]
b42e664854 Bump github.com/containers/storage from 1.41.0 to 1.42.0
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.41.0 to 1.42.0.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.41.0...v1.42.0)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-22 08:17:06 +00:00
Miloslav Trmač
ad12a292a3 Merge pull request #1709 from containers/dependabot/go_modules/github.com/sirupsen/logrus-1.9.0
Bump github.com/sirupsen/logrus from 1.8.1 to 1.9.0
2022-07-19 12:56:15 +02:00
dependabot[bot]
ee477d8877 Bump github.com/sirupsen/logrus from 1.8.1 to 1.9.0
Bumps [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus) from 1.8.1 to 1.9.0.
- [Release notes](https://github.com/sirupsen/logrus/releases)
- [Changelog](https://github.com/sirupsen/logrus/blob/master/CHANGELOG.md)
- [Commits](https://github.com/sirupsen/logrus/compare/v1.8.1...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/sirupsen/logrus
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-07-19 08:11:51 +00:00
Daniel J Walsh
dbe47d765a Merge pull request #1705 from mtrmac/release-1.9.0
Release v1.9.0
2022-07-13 09:52:15 -04:00
Miloslav Trmač
f1485781be Bump to v1.9.1-dev
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-13 10:34:41 +02:00
Miloslav Trmač
a03cba7c7e Release v1.9.0
Adds support for copying non-image OCI artifacts, and for
creating and enforcing sigstore signatures.

Shell autocompletions are now auto-generated, adding support
for zsh, fish and PowerShell.

Now requires Go 1.17.

- Bump github.com/docker/docker
- Config files live in /usr/local/etc on FreeBSD
- Avoid hard-coding the location of bash
- Bump github.com/containers/storage from 1.40.2 to 1.41.0
- Bump github.com/docker/docker
- add completion command to generate shell completion scripts
- Remove cgo_pthread_ordering_workaround.go
- Update c/image
- Stop calling gpgme-config
- shell completion: add Makefile target
- shell completion: add install instructions docs
- shell completion: add completion for transports names
- [CI:DOCS] Pin actions to a full length commit SHA
- Updated skopeo logo with new artwork
- Update to gopkg.in/yaml.v3 v3.0.0
- fix make completions for all POSIX shells
- Update to github.com/opencontainers/runc >= 1.1.2
- Cirrus: use Ubuntu 22.04 LTS
- Bump github.com/containers/ocicrypt from 1.1.4 to 1.1.5
- Bump github.com/stretchr/testify from 1.7.1 to 1.7.2
- Bump github.com/docker/docker
- Update go.mod to Go 1.17
- Use testing.T.Setenv instead of os.Setenv in tests
- Change a repo used for sync tests
- Use an updated CI image
- Update for docker/distribution CLI change
- Enable schema1 support on the test registries
- CoPR: Autobuild rpm on rhcontainerbot/podman-next
- [CI:DOCS] Makefile: include cautionary note for rpm target
- [CI:DOCS] skopeo.spec.rpkg: Fix syntax highlighting
- Bump github.com/spf13/cobra from 1.4.0 to 1.5.0
- Bump github.com/stretchr/testify from 1.7.2 to 1.7.4
- Bump github.com/stretchr/testify from 1.7.4 to 1.7.5
- Cirrus: Migrate multiarch build off github actions
- Update & fix skopeo multiarch image Containerfiles
- Use bytes.ReplaceAll instead of bytes.Replace(..., -1)
- Update IRC information
- Bump github.com/stretchr/testify from 1.7.5 to 1.8.0
- Introduce noteCloseFailure, use it for reporting of cleanup errors
- Modify error messages on failures to close
- Remove uses of pkg/errors
- Use errors.As() instead of direct type checks
- Vendor unreleased c/image with OCI artifact support
- Revert "Change a repo used for sync tests"
- Vendor in c/image with sigstore support
- Add --sign-by-sigstore-private-key to (skopeo copy) and (skopeo sync)
- Update for the renames of sigstore to lookaside

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-13 10:17:32 +02:00
Daniel J Walsh
7ddc5ce06c Merge pull request #1701 from mtrmac/cosign-prototypes
Add Cosign signing/verification
2022-07-12 09:28:51 -04:00
Miloslav Trmač
b000ada3f3 Update for the renames of sigstore to lookaside
I left systemtest unmodified, to have _something_ that
exercises the compatibility path.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-12 13:47:35 +02:00
Miloslav Trmač
f2b4071b1f Add --sign-by-sigstore-private-key to (skopeo copy) and (skopeo sync)
If a passphrase is not provided, prompt for one.

Outstanding:
- Should have integration tests.
- The signing options shared between copy and sync should live in utils.go.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-12 13:47:27 +02:00
Miloslav Trmač
06be7a1559 Vendor in c/image with sigstore support
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-12 13:46:56 +02:00
Valentin Rothberg
b95e081162 Merge pull request #1680 from mtrmac/test-registry-2.8.1-with-cosign-signatures
Add OCI artifact support, test syncing Cosign signatures again
2022-07-04 09:16:23 +02:00
Miloslav Trmač
61593fccc6 Revert "Change a repo used for sync tests"
This reverts commit bbdabebd17.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-01 17:35:08 +02:00
Miloslav Trmač
62158a58bc Vendor unreleased c/image with OCI artifact support
including https://github.com/containers/image/pull/1574 .

> go get github.com/containers/image/v5@main
> make vendor

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-07-01 17:35:04 +02:00
Daniel J Walsh
5e8a236c95 Merge pull request #1688 from mtrmac/ReplaceAll
Use bytes.ReplaceAll instead of bytes.Replace(..., -1)
2022-07-01 07:03:11 -04:00
Daniel J Walsh
fffdc1f9df Merge pull request #1696 from jsoref/irc
Update IRC information
2022-07-01 07:02:58 -04:00
Daniel J Walsh
f75d570709 Merge pull request #1698 from mtrmac/pkg_errors
Error handling cleanups, and drop pkg/errors
2022-07-01 07:02:23 -04:00
Miloslav Trmač
7900440ac9 Use errors.As() instead of direct type checks
... to be a bit more robust against unexpected error wrapping.

Also be a little more idiomatic on the sync error handling path.

Should not change behavior, assuming the previous code was correct.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-30 21:40:46 +02:00
Miloslav Trmač
c654871bd9 Remove uses of pkg/errors
This is clearly safe because the changes are
mostly top-level CLI where nothing is checking
the type of the error.

Even in that case, use %w for idiomatic consistency
(and to make it easier to possibly move some code into a Go library.)

Mostly mechanical, but note the changes to error handling of .Close():
we use %w for the primary error, not for the close error.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-30 21:00:40 +02:00
Miloslav Trmač
7abcca9313 Modify error messages on failures to close
- Use a wrapping wording similar to c/image; it's slightly
  awkward at the start of the error message, but those should
  hopefully be rare.
- Notably, distinguish the three failure paths in (skopeo layers).

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-30 20:30:40 +02:00
Miloslav Trmač
f7df4a0838 Introduce noteCloseFailure, use it for reporting of cleanup errors
Note that this is a behavior change: we used to do
    retErr = errors.Wrapf(retErr, ..., closeErr)
which doesn't record closeErr if retErr was nil.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-30 20:30:11 +02:00
Miloslav Trmač
68e9f2c576 Merge pull request #1697 from containers/dependabot/go_modules/github.com/stretchr/testify-1.8.0
Bump github.com/stretchr/testify from 1.7.5 to 1.8.0
2022-06-30 17:43:30 +02:00
dependabot[bot]
331162358b Bump github.com/stretchr/testify from 1.7.5 to 1.8.0
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.5 to 1.8.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.5...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-30 08:17:49 +00:00
Josh Soref
89089f3a8d Update IRC information
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2022-06-29 20:14:56 -04:00
Miloslav Trmač
ba6af16e53 Use bytes.ReplaceAll instead of bytes.Replace(..., -1)
... for a trivial improvement in readability.

Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-29 19:40:02 +02:00
Miloslav Trmač
bc84a02bc4 Merge pull request #1661 from cevich/multiarch_build
[CI:BUILD] Cirrus: Migrate multiarch build off github actions
2022-06-29 19:16:13 +02:00
Chris Evich
2024e2e258 Update & fix skopeo multiarch image Containerfiles
These changes substantially mirror similar updates made recently to both
podman and buildah.  Besides renaming `Dockerfile` -> `Containerfile`,
there are much needed updates to docs, and the build instructions.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-06-29 11:42:15 -04:00
Chris Evich
774ff9d16f Cirrus: Migrate multiarch build off github actions
The github actions workflow for this operation is complex and difficult
to maintain.  For several months now a replacement has been running well
in the podman repository.  It's scripts/components are centralized,
versioned, unit, and integration tested.  Add cirrus tasks to run the
build, and another task to allow test builds in a PR.

This also adds support for a new magic CI string: `[CI:BUILD]`.
With this string in the PR title, automation will only do basic build
verification, and enable testing of the multi-arch build process.

Otherwise, many tasks were updated to not be created when running the
cirrus-cron multi-arch image builds, since this would simply be a waste
of time and invitation for flakes.

Lastly, since only native tooling is used in the new build process,
rename all the recipes to `Containerfile`.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-06-28 17:43:37 -04:00
Daniel J Walsh
1462a45c91 Merge pull request #1653 from mairin/patch-1
Updated skopeo logo with new artwork
2022-06-27 10:56:26 -04:00
Miloslav Trmač
7bfa5cbad8 Merge pull request #1690 from containers/dependabot/go_modules/github.com/stretchr/testify-1.7.5
Bump github.com/stretchr/testify from 1.7.4 to 1.7.5
2022-06-24 19:12:34 +02:00
dependabot[bot]
899d3686f9 Bump github.com/stretchr/testify from 1.7.4 to 1.7.5
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.4 to 1.7.5.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.4...v1.7.5)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-24 08:34:09 +00:00
Daniel J Walsh
1a98f253b4 Merge pull request #1687 from containers/dependabot/go_modules/github.com/stretchr/testify-1.7.4
Bump github.com/stretchr/testify from 1.7.2 to 1.7.4
2022-06-21 13:08:58 -04:00
Daniel J Walsh
fdd8aa2fd0 Merge pull request #1686 from containers/dependabot/go_modules/github.com/spf13/cobra-1.5.0
Bump github.com/spf13/cobra from 1.4.0 to 1.5.0
2022-06-21 13:08:33 -04:00
dependabot[bot]
2f77d21343 Bump github.com/stretchr/testify from 1.7.2 to 1.7.4
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.2 to 1.7.4.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.2...v1.7.4)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-21 08:13:03 +00:00
dependabot[bot]
2009d1c61e Bump github.com/spf13/cobra from 1.4.0 to 1.5.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.4.0...v1.5.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-21 08:12:52 +00:00
Miloslav Trmač
168f8d648a Merge pull request #1684 from lsm5/rpmspec-syntax-highlight-fix
[CI:DOCS] skopeo.spec.rpkg: Fix syntax highlighting
2022-06-17 20:35:21 +02:00
Lokesh Mandvekar
fe0228095b [CI:DOCS] skopeo.spec.rpkg: Fix syntax highlighting
For whatever reason, the comment rearrangement is
required for vim rpm synatx highlighting to work.

Also added a comment pointing out where additional comments
should go. :)

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2022-06-17 10:14:57 -04:00
Daniel J Walsh
14650880c8 Merge pull request #1679 from mtrmac/test-registry-2.8.1-from-image
Use an updated CI image with OCI-capable registry
2022-06-17 06:06:22 -04:00
Miloslav Trmač
e7363a2e30 Merge pull request #1682 from lsm5/rpkg-doc-update
[CI:DOCS] Makefile: include cautionary note for rpm target
2022-06-16 21:38:02 +02:00
Lokesh Mandvekar
71d450cb35 [CI:DOCS] Makefile: include cautionary note for rpm target
Also add same warning to skopeo.spec.rpkg

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2022-06-16 15:36:06 -04:00
Lokesh Mandvekar
3738854467 CoPR: Autobuild rpm on rhcontainerbot/podman-next
The new file `skopeo.spec.rpkg` along with a webhook will automatically
build rpms on every PR merge on the main branch.

Run `rpkg local` or `make rpm` to generate the rpm.

Known issue: Doesn't yet build for EL8 environments.

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2022-06-16 15:27:39 -04:00
Miloslav Trmač
38f4b9d032 Enable schema1 support on the test registries
We expect schema1 images to work.  Also, docker/distribution
doesn't provide useful errors for rejected schema1 images
( https://github.com/distribution/distribution/issues/2925 ),
which makes it impractical for Skopeo to automatically convert
schema1 to schema2.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-16 20:27:05 +02:00
Miloslav Trmač
1b5fb465be Update for docker/distribution CLI change
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-16 20:27:05 +02:00
Miloslav Trmač
e9ed5e04e2 Use an updated CI image
... from https://github.com/containers/automation_images/pull/137 .

This updates the docker/distribution registry to 2.8.1, allowing it
to accept OCI images.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-16 20:27:01 +02:00
Daniel J Walsh
f2c1d77c57 Merge pull request #1672 from mtrmac/non-artifact-oci-repo
Change a repo used for sync tests
2022-06-11 05:52:07 -04:00
Miloslav Trmač
bbdabebd17 Change a repo used for sync tests
The k8s.gcr.io/coredns/coredns repo now contains an OCI
artifact, which we can't copy; so, use a different
repo to test syncing.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-11 03:15:14 +02:00
Daniel J Walsh
4b5e6327cd Merge pull request #1667 from mtrmac/go1.17
Update to benefit from Go 1.17
2022-06-09 11:11:51 -04:00
Miloslav Trmač
92c0d0c09d Use testing.T.Setenv instead of os.Setenv in tests
... to benefit from Go 1.17.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-08 16:57:06 +02:00
Miloslav Trmač
a3a72342f2 Update go.mod to Go 1.17
> go mod tidy -go=1.17
> make vendor

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-06-08 16:46:38 +02:00
Miloslav Trmač
14a3b9241e Merge pull request #1666 from containers/dependabot/go_modules/github.com/docker/docker-20.10.17incompatible
Bump github.com/docker/docker from 20.10.16+incompatible to 20.10.17+incompatible
2022-06-07 15:59:14 +02:00
dependabot[bot]
e9379d15d2 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.16+incompatible to 20.10.17+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.16...v20.10.17)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-07 08:19:08 +00:00
Miloslav Trmač
eb61a79dde Merge pull request #1664 from containers/dependabot/go_modules/github.com/stretchr/testify-1.7.2
Bump github.com/stretchr/testify from 1.7.1 to 1.7.2
2022-06-06 17:31:50 +02:00
dependabot[bot]
69840fd082 Bump github.com/stretchr/testify from 1.7.1 to 1.7.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.1 to 1.7.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 14:50:42 +00:00
Daniel J Walsh
dc905cb7be Merge pull request #1663 from containers/dependabot/go_modules/github.com/containers/ocicrypt-1.1.5
Bump github.com/containers/ocicrypt from 1.1.4 to 1.1.5
2022-06-06 08:05:31 -04:00
dependabot[bot]
63622bc7c4 Bump github.com/containers/ocicrypt from 1.1.4 to 1.1.5
Bumps [github.com/containers/ocicrypt](https://github.com/containers/ocicrypt) from 1.1.4 to 1.1.5.
- [Release notes](https://github.com/containers/ocicrypt/releases)
- [Commits](https://github.com/containers/ocicrypt/compare/v1.1.4...v1.1.5)

---
updated-dependencies:
- dependency-name: github.com/containers/ocicrypt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-06-06 08:16:08 +00:00
Chris Evich
02ae5c2af5 Merge pull request #1658 from lsm5/ubuntu-2204-lts-cirrus
Cirrus: use Ubuntu 22.04 LTS
2022-05-31 13:37:11 -04:00
Lokesh Mandvekar
6b58459829 Cirrus: use Ubuntu 22.04 LTS
Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2022-05-31 13:03:56 -04:00
Valentin Rothberg
a5d4e6655d Merge pull request #1655 from mtrmac/runc-1.1.2
Update to github.com/opencontainers/runc >= 1.1.2
2022-05-31 09:09:11 +02:00
Miloslav Trmač
00a58e48b1 Update to github.com/opencontainers/runc >= 1.1.2
... to silence Dependabot alerts about CVE-2022-29162 = GHSA-f3fp-gc8g-vw66.

Note that the vulnerable code is not actually included in Skopeo at all,
this is purely to silence imprecise vulnerability checkers.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-05-31 01:23:51 +02:00
Miloslav Trmač
db663df804 Merge pull request #1659 from Luap99/make-completions
fix make completions for all POSIX shells
2022-05-30 15:00:14 +02:00
Paul Holzinger
263a5f017f fix make completions for all POSIX shells
The {a,b} syntax is not POSIX compatible. The Makefile should run with
all POSIX shells so we cannot use shell specific features like this.

Fixes #1657

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2022-05-30 14:38:56 +02:00
Valentin Rothberg
47afd101f0 Merge pull request #1656 from mtrmac/yaml-3.0.0
Update to gopkg.in/yaml.v3 v3.0.0
2022-05-30 11:38:26 +02:00
Miloslav Trmač
0a3be734a9 Update to gopkg.in/yaml.v3 v3.0.0
... to include a fix for CVE-2022-28948 = GHSA-hp87-p4gw-j4gq .

Note that the package is only used for Skopeo's tests, so
Skopeo's users can't reach the vulnerable code.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-05-26 20:30:59 +02:00
Chris Evich
e8a3064328 Merge pull request #1652 from cevich/fix_gha_security
[CI:DOCS] Pin actions to a full length commit SHA
2022-05-26 14:28:01 -04:00
Máirín Duffy
0ad7ec2402 Updated skopeo logo with new artwork
Signed-off-by: Máirín Duffy <duffy@redhat.com>
2022-05-25 13:39:05 -04:00
Chris Evich
014d47f396 [CI:DOCS] Pin actions to a full length commit SHA
+ Pin actions to a full length commit SHA is currently the only way
  to use an action as an immutable release. Pinning to a particular SHA
  helps mitigate the risk of a bad actor adding a backdoor to the action's
  repository, as they would need to generate a SHA-1 collision for a valid
  Git object payload. Ref:
  https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-third-party-actions

+ Explicitly set permissions for actions to minimum required.  The
  defaults are (unfortunately) overly permissive: Ref:
  https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-05-25 11:29:18 -04:00
Daniel J Walsh
0fa1b5038f Merge pull request #1649 from mtrmac/gpgme-native-pkg-config
Stop calling gpgme-config
2022-05-23 23:32:14 -04:00
Miloslav Trmač
1add7a81d7 Merge pull request #1647 from Luap99/completion
use spf13/cobra to generate shell completions
2022-05-23 19:22:10 +02:00
Paul Holzinger
d78bc82782 shell completion: add completion for transports names
Make sure skopeo copy/inspect/delete show the transport names when shell
completion is used to not regress compared to the old bash completion
script.

In theory I would highly recommend to set completion functions for
every flag and command. This can be ensured with a test like this:
https://github.com/containers/podman/blob/main/cmd/podman/shell_completion_test.go
But this is a lot of work to get right and I am neither a skopeo user or
maintainer so I am missing a lot of context for most options. I think
this would be better handled by a person who knows skopeo better.

Normally options should either use AutocompleteNone() or
AutocompleteDefault() from c/common/pkg/completion.
Even better would be to add custom completion functions for arguments
that only accept fixed values, see AutocompleteSupportedTransports() in
this commit.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2022-05-23 18:47:51 +02:00
Paul Holzinger
6c2a415f6c shell completion: add install instructions docs
Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2022-05-23 18:47:51 +02:00
Paul Holzinger
9bed0a9e9a shell completion: add Makefile target
Add target to generate the shell scripts and a target to install them.

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2022-05-23 18:47:46 +02:00
Miloslav Trmač
ebc5573e83 Stop calling gpgme-config
As of the just-updated github.com/proglottis/gpgme 0.1.2,
the gpgme subpackage uses CGo's native #cgo pkg-config support
to find the relevant libraries, and we no longer need to manually set
CGO_CFLAGS and CGO_LDFLAGS. So stop doing that.

Note that the proglottis/gpgme update (implied by vendoring c/image)
means the minimal supported version of GPGME is 1.13.0.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-05-19 22:03:26 +02:00
Miloslav Trmač
1ebb2520ca Update c/image
... to bring in github.com/proglottis/gpgme 0.1.2.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-05-19 21:59:36 +02:00
Miloslav Trmač
9b4c1f15f5 Remove cgo_pthread_ordering_workaround.go
Per https://bugzilla.redhat.com/show_bug.cgi?id=1326903 and
https://sourceware.org/bugzilla/show_bug.cgi?id=19861#c9 , this
was fixed in Glibc 2.24 .

Removing this will also allow us not to worry about LDFLAGS
necessary to make -lgpgme work.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-05-19 21:54:07 +02:00
Paul Holzinger
6863fe2d35 add completion command to generate shell completion scripts
Use the cobra lib to automatically generate shell completion scripts.
We can use the completion command which is automatically added, since it
is not importent for most users we hide it.

To test the new script on bash you can use `source <(bin/skopeo completion bash)`

Signed-off-by: Paul Holzinger <pholzing@redhat.com>
2022-05-19 16:56:21 +02:00
Miloslav Trmač
4b924061b8 Merge pull request #1644 from containers/dependabot/go_modules/github.com/docker/docker-20.10.16incompatible
Bump github.com/docker/docker from 20.10.15+incompatible to 20.10.16+incompatible
2022-05-13 18:09:45 +02:00
dependabot[bot]
3eca480c2b Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.15+incompatible to 20.10.16+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.15...v20.10.16)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-13 08:17:07 +00:00
Miloslav Trmač
149bb8a671 Merge pull request #1643 from containers/dependabot/go_modules/github.com/containers/storage-1.41.0
Bump github.com/containers/storage from 1.40.2 to 1.41.0
2022-05-12 18:10:19 +02:00
dependabot[bot]
149dea8dce Bump github.com/containers/storage from 1.40.2 to 1.41.0
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.40.2 to 1.41.0.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.40.2...v1.41.0)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-12 08:10:20 +00:00
Daniel J Walsh
a90efa2d60 Merge pull request #1642 from dfr/freebsd-config
Config files live in /usr/local/etc on FreeBSD (highlight in release notes!)
2022-05-10 16:13:39 -04:00
Doug Rabson
804f7c249d Avoid hard-coding the location of bash
On FreeBSD, bash lives in /usr/local/bin/bash. These scripts don't
really depend on bash so could be changed to /bin/sh.

Signed-off-by: Doug Rabson <dfr@rabson.org>
2022-05-10 11:24:45 +01:00
Doug Rabson
e47765ed9e Config files live in /usr/local/etc on FreeBSD
Signed-off-by: Doug Rabson <dfr@rabson.org>
2022-05-10 10:37:29 +01:00
Miloslav Trmač
0c6074db50 Merge pull request #1640 from containers/dependabot/go_modules/github.com/docker/docker-20.10.15incompatible
Bump github.com/docker/docker from 20.10.14+incompatible to 20.10.15+incompatible
2022-05-09 20:03:20 +02:00
dependabot[bot]
13ceb93bdf Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.14+incompatible to 20.10.15+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.14...v20.10.15)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-07 12:22:43 +00:00
Daniel J Walsh
30446fae02 Merge pull request #1641 from rhatdan/main
Bump to v1.8.0
2022-05-07 08:21:40 -04:00
Daniel J Walsh
cd4607f96b Move to v1.8.1-dev
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-05-06 10:09:43 -04:00
Daniel J Walsh
37727a45f9 Bump to v1.8.0
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-05-06 10:09:11 -04:00
Daniel J Walsh
75d94e790c Bump ocicrypt to v1.1.4
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-05-06 10:07:37 -04:00
Miloslav Trmač
1fe8da63a9 Merge pull request #1636 from rhatdan/main
Vendor in containers/storage v1.40.2
2022-05-03 20:38:28 +02:00
Daniel J Walsh
737ed9c2a4 Vendor in containers/storage v1.40.2
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-05-03 13:06:55 -04:00
Miloslav Trmač
39a4475cf3 Merge pull request #1635 from rhatdan/main
Vendor in containers/(common, storage, image)
2022-05-03 16:48:44 +02:00
Daniel J Walsh
3c286dd1d1 Vendor in containers/(common, storage, image)
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-05-03 09:59:43 -04:00
Lokesh Mandvekar
b8b0e9937b [CI:DOCS] install.md: include distro package info links
Co-authored-by: Tom Sweeney <tsweeney@redhat.com>
Co-authored-by: Miloslav Trmač <mitr@redhat.com>

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2022-05-02 10:33:57 -04:00
Miloslav Trmač
437d33ec9a Merge pull request #1632 from lsm5/remove-kubic
[CI:DOCS] install.md: remove Kubic package info for Ubuntu
2022-04-29 17:44:00 +02:00
Lokesh Mandvekar
d9035db615 [CI:DOCS] install.md: remove Kubic package info for Ubuntu
Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2022-04-29 11:38:02 -04:00
Chris Evich
198842bbec Merge pull request #1631 from cevich/f36_update
Cirrus: Update to F36 w/ netavark+aardvark-dns
2022-04-29 11:15:18 -04:00
Chris Evich
916a395d82 Cirrus: Update to F36 w/ netavark+aardvark-dns
Also includes some updates relating to improvements in the common
automation library.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-04-28 13:39:25 -04:00
Daniel J Walsh
89acf46019 Merge pull request #1626 from rhatdan/VENDOR
Update vendor of containers/(common,storage,image)
2022-04-22 08:32:24 -04:00
Daniel J Walsh
8960ab3ce7 Update vendor of containers/(common,storage,image)
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-04-22 06:45:18 -04:00
Daniel J Walsh
145304b7cf Merge pull request #1597 from mtrmac/delete-warning
Improve the (skopeo delete) man page
2022-04-14 07:15:47 -04:00
Valentin Rothberg
e534472e7d Merge pull request #1621 from mtrmac/go1.16
Update to benefit from Go 1.16
2022-04-14 09:14:09 +02:00
Miloslav Trmač
d9d3ceca45 Use filepath.WalkDir instead of filepath.Walk
... to optimize away some lstat(2) calls.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-04-13 20:14:04 +02:00
Miloslav Trmač
23a4605742 Extract four copies of the same loop into a function
Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-04-13 20:14:04 +02:00
Miloslav Trmač
4811c07d71 Update users of deprecated io/ioutil
Mostly just name changes that should not change behavior, apart
from ioutil.ReadDir -> os.ReadDir avoiding per-item lstat(2) in
one case.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-04-13 20:13:52 +02:00
Miloslav Trmač
15b38112b1 Merge pull request #1610 from Jamstah/sign-identity
Add option to specify the identity for signing
2022-04-04 13:14:49 +02:00
James Hewitt
4ef35a385a Add option to specify the identity for signing
This enables pushing to registries where the push and pull uris may be
different, for example where pushed images are mirrored to a read only
replica for distribution.

Closes #1588

Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
2022-03-30 22:02:43 +01:00
James Hewitt
38ae81fa03 Bump containers/image to include sign identity option
Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
2022-03-30 22:02:40 +01:00
Miloslav Trmač
e4297e3b30 Merge pull request #1611 from masatake/fix-man-page
delete non-existent option in the cmdline example
2022-03-30 19:47:58 +02:00
Masatake YAMATO
9b09b6eb87 delete non-existent option in the cmdline example
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
2022-03-31 02:27:42 +09:00
Valentin Rothberg
45ed92ce0c Merge pull request #1608 from rhatdan/dry-run
Add dry-run mode to skopeo-sync
2022-03-29 08:30:43 +02:00
Daniel J Walsh
c233a6dcb1 Add dry-run mode to skopeo-sync
Taking over #1459 to drive it to completion.

Signed-off-by: Ted Wexler <twexler@bloomberg.net>
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-03-28 14:18:10 -04:00
Daniel J Walsh
e0f0869151 Merge pull request #1607 from glensc/patch-2
Update skopeoimage/README.md that tags are v-prefixed
2022-03-28 08:21:47 -04:00
Elan Ruusamäe
e6802c4df4 Update skopeoimage/README.md that tags are v-prefixed
Signed-off-by: Elan Ruusamäe <glen@delfi.ee>
2022-03-28 11:36:34 +03:00
Miloslav Trmač
2b910649b9 Merge pull request #1606 from mtrmac/v1.7.0
v1.7.0
2022-03-24 20:56:58 +01:00
Miloslav Trmač
808717862b Bump to v1.7.1-dev
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-24 20:34:19 +01:00
Miloslav Trmač
f45ae950aa Release 1.7.0
skopeo list-tags docker-archive:... is now available.

- Improve a comment in the 010-inspect.bats test
- do not recommend upgrading all packages
- Bump github.com/containers/image/v5 from 5.19.1 to 5.20.0
- Update github.com/containerd/containerd
- Bump github.com/docker/docker
- Bump github.com/spf13/cobra from 1.3.0 to 1.4.0
- Add support for docker-archive: to skopeo list-tags
- Rename "self" receiver
- Remove assignments to an unused variable
- Add various missing error handling
- Simplify the proxy server a bit
- Bump github.com/stretchr/testify from 1.7.0 to 1.7.1
- Use assert.ErrorContains
- Update to Go 1.14 and revendor
- Use check.C.MkDir() instead of manual ioutil.TempDir() calls
- Formally record that we require Go 1.15
- Update the command to install golint
- Bump github.com/containers/ocicrypt from 1.1.2 to 1.1.3
- Bump github.com/docker/docker
- Bump github.com/containers/storage from 1.38.2 to 1.39.0
- Bump github.com/containers/common from 0.47.4 to 0.47.5
- Bump github.com/prometheus/client_golang to v1.11.1

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-24 20:32:24 +01:00
Lokesh Mandvekar
3bc062423e Bump github.com/prometheus/client_golang to v1.11.1
Resolves: CVE-2022-21698

Skopeo isn't actually impacted by the CVE unless a Prometheus listener
is set up, which is not a part of Skopeo's default behavior.

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2022-03-24 14:57:52 -04:00
Miloslav Trmač
d0d7d97f9c Merge pull request #1604 from containers/dependabot/go_modules/github.com/containers/common-0.47.5
Bump github.com/containers/common from 0.47.4 to 0.47.5
2022-03-24 19:32:55 +01:00
dependabot[bot]
89cd19519f Bump github.com/containers/common from 0.47.4 to 0.47.5
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.47.4 to 0.47.5.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.47.4...v0.47.5)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 17:41:02 +00:00
Miloslav Trmač
3e973e1aa2 Merge pull request #1603 from containers/dependabot/go_modules/github.com/containers/storage-1.39.0
Bump github.com/containers/storage from 1.38.2 to 1.39.0
2022-03-24 18:39:51 +01:00
dependabot[bot]
7f6b0e39d0 Bump github.com/containers/storage from 1.38.2 to 1.39.0
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.38.2 to 1.39.0.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.38.2...v1.39.0)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 17:14:41 +00:00
Miloslav Trmač
cc2445de81 Merge pull request #1602 from containers/dependabot/go_modules/github.com/docker/docker-20.10.14incompatible
Bump github.com/docker/docker from 20.10.13+incompatible to 20.10.14+incompatible
2022-03-24 18:13:38 +01:00
dependabot[bot]
f6bf57460d Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.13+incompatible to 20.10.14+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.13...v20.10.14)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-24 09:13:14 +00:00
Miloslav Trmač
a9cc9b9133 Improve the (skopeo delete) man page
Actually add a DESCRIPTION heading.

Warn about deleting by digest - it affects an unknown set of tags.

Warn about deleting by tag - it currently works by the resolved digest.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-21 15:49:14 +01:00
Miloslav Trmač
91cd3510eb Merge pull request #1600 from containers/dependabot/go_modules/github.com/containers/ocicrypt-1.1.3
Bump github.com/containers/ocicrypt from 1.1.2 to 1.1.3
2022-03-21 15:45:57 +01:00
dependabot[bot]
ac7edc7d10 Bump github.com/containers/ocicrypt from 1.1.2 to 1.1.3
Bumps [github.com/containers/ocicrypt](https://github.com/containers/ocicrypt) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/containers/ocicrypt/releases)
- [Commits](https://github.com/containers/ocicrypt/compare/v1.1.2...v1.1.3)

---
updated-dependencies:
- dependency-name: github.com/containers/ocicrypt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-21 09:10:52 +00:00
Valentin Rothberg
92b1eec64c Merge pull request #1593 from mtrmac/go-1.15
Formally require Go 1.15
2022-03-17 08:55:27 +01:00
Miloslav Trmač
c819bc1754 Update the command to install golint
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-16 16:05:08 +01:00
Miloslav Trmač
6a2f38d66c Formally record that we require Go 1.15
We already do in practice:
> vendor/golang.org/x/net/http2/transport.go:417:45: undefined: os.ErrDeadlineExceeded

so make that official.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-16 16:05:08 +01:00
Miloslav Trmač
2019b79c7f Use check.C.MkDir() instead of manual ioutil.TempDir() calls
This saves us at least 2 lines (error check, and cleanup) on every
instance, or in some cases adds cleanup that we forgot.

This is inspired by, but not directly related to, Go 1.15's addition of
Testing.T.TempDir.

NOTE: This might significantly increase the tests' disk space requirements;
AFAICS the temporary directories are only cleaned up when a whole "suite
finishes running.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-16 16:05:08 +01:00
Miloslav Trmač
f79cc8aeda Update to Go 1.14 and revendor
> go mod tidy -go=1.14
> make vendor

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-16 16:05:08 +01:00
Miloslav Trmač
0bfe297fc1 Merge pull request #1595 from mtrmac/ErrorContains
Use assert.ErrorContains
2022-03-16 16:04:38 +01:00
Miloslav Trmač
ac4c291f76 Use assert.ErrorContains
...added in github.com/stretchr/testify 1.7.1.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-16 15:13:31 +01:00
Miloslav Trmač
d2837c9e56 Merge pull request #1594 from containers/dependabot/go_modules/github.com/stretchr/testify-1.7.1
Bump github.com/stretchr/testify from 1.7.0 to 1.7.1
2022-03-16 15:09:29 +01:00
dependabot[bot]
5aaf3a9e4c Bump github.com/stretchr/testify from 1.7.0 to 1.7.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.0...v1.7.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-16 09:19:41 +00:00
Valentin Rothberg
0c4a9cc684 Merge pull request #1592 from mtrmac/lint-1.18
Various lint fixes
2022-03-16 09:08:29 +01:00
Miloslav Trmač
bd524670b1 Simplify the proxy server a bit
Move JSON parsing into the request processing handler
so that we can consolidate the two instances of the response sending code.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-15 21:48:51 +01:00
Miloslav Trmač
693de29e37 Add various missing error handling
... as found by (golangci-lint run).

Note: this does not add (golangci-lint run) to the Makefile
to ensure the coding standard.

(BTW golangci-lint currently fails on structcheck, which doesn't
handle embedded structs, and that's a years-long known unfixed
limitation.)

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-15 21:48:51 +01:00
Miloslav Trmač
f44ee2f80a Remove assignments to an unused variable
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-15 21:48:51 +01:00
Miloslav Trmač
a71900996f Rename "self" receiver
> receiver name should be a reflection of its identity; don't use generic names such as "this" or "self" (ST1006)

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-15 21:48:51 +01:00
Valentin Rothberg
a26578178b Merge pull request #1581 from zhangguanzhang/list-tags
Add support for docker-archive: to skopeo list-tags
2022-03-15 10:11:28 +01:00
zhangguanzhang
7ba56f3f7a Add support for docker-archive: to skopeo list-tags
Signed-off-by: zhangguanzhang <zhangguanzhang@qq.com>
2022-03-15 09:32:05 +08:00
Daniel J Walsh
0f701726bd Merge pull request #1589 from containers/dependabot/go_modules/github.com/docker/docker-20.10.13incompatible
Bump github.com/docker/docker from 20.10.12+incompatible to 20.10.13+incompatible
2022-03-11 05:01:09 -05:00
Daniel J Walsh
91ad8c39c6 Merge pull request #1590 from containers/dependabot/go_modules/github.com/spf13/cobra-1.4.0
Bump github.com/spf13/cobra from 1.3.0 to 1.4.0
2022-03-11 05:00:41 -05:00
dependabot[bot]
ad3e8f407d Bump github.com/spf13/cobra from 1.3.0 to 1.4.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.3.0 to 1.4.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.3.0...v1.4.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-11 09:11:45 +00:00
dependabot[bot]
0703ec6ce8 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.12+incompatible to 20.10.13+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.12...v20.10.13)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-11 09:11:37 +00:00
Valentin Rothberg
3e2defd6d3 Merge pull request #1585 from mtrmac/update-containerd
Update github.com/containerd/containerd
2022-03-07 09:47:20 +01:00
Miloslav Trmač
5200272846 Update github.com/containerd/containerd
$ go get -u github.ccom/containerd/containerd
$ make vendor

... to silence warnings about https://github.com/advisories/GHSA-crp2-qrr5-8pq7 ,
in code we don't use.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-03-04 19:26:12 +01:00
Miloslav Trmač
43eab90b36 Merge pull request #1582 from containers/dependabot/go_modules/github.com/containers/image/v5-5.20.0
Bump github.com/containers/image/v5 from 5.19.1 to 5.20.0
2022-03-04 19:15:24 +01:00
dependabot[bot]
0ad25b2d33 Bump github.com/containers/image/v5 from 5.19.1 to 5.20.0
Bumps [github.com/containers/image/v5](https://github.com/containers/image) from 5.19.1 to 5.20.0.
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.19.1...v5.20.0)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-02 09:21:25 +00:00
Daniel J Walsh
22d187181b Merge pull request #1578 from slhck/patch-1
do not recommend upgrading all packages
2022-02-28 07:45:59 -05:00
Werner Robitza
8cbfcc820a do not recommend upgrading all packages
The command to install skopeo for Ubuntu 20.04 includes a forced upgrade step for all packages.

Installing skopeo does not require the upgrade step, and it could lead to possible issues completely unrelated to the project.

Signed-off-by: Werner Robitza <werner.robitza@gmail.com>
2022-02-25 11:46:17 +01:00
Miloslav Trmač
8539d21152 Merge pull request #1576 from mtrmac/inspect-test-docs
Improve a comment in the 010-inspect.bats test
2022-02-23 22:48:17 +01:00
Miloslav Trmač
370be7e777 Improve a comment in the 010-inspect.bats test
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-22 19:44:14 +01:00
Daniel J Walsh
95e17ed1e0 Merge pull request #1573 from rhatdan/main
Bump to v1.6.1
2022-02-16 12:04:39 -05:00
Daniel J Walsh
73edfb8216 Move to v1.7.0-dev
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-02-16 12:03:15 -05:00
Daniel J Walsh
49084d2cd8 Bump to v1.6.1
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-02-16 12:02:57 -05:00
Daniel J Walsh
8b904e908e Merge pull request #1568 from mtrmac/resolved-workaround
Resolved workaround
2022-02-15 14:18:05 -05:00
Miloslav Trmač
23183072fb Work around systemd-resolved's handling of .invalid domains
... per https://github.com/containers/skopeo/pull/1558 .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-15 16:54:51 +01:00
Miloslav Trmač
3be97ce281 Beautify a few calls
Use the sort-of-convention of keeping the output matching regex,
and the command, on separatel lines.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-15 16:54:51 +01:00
Miloslav Trmač
b46506c077 Merge pull request #1572 from mtrmac/inspect-expect-config
Don't expect the config blob to be listed in (skopeo inspect)
2022-02-15 16:54:22 +01:00
Miloslav Trmač
49d9fa9faf Only look for the layer digests in the Layers field.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-15 16:08:35 +01:00
Miloslav Trmač
77363128e1 Don't expect the config blob to be listed in (skopeo inspect)
... because it currently isn't.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-15 16:08:35 +01:00
Daniel J Walsh
59a452276b Merge pull request #1558 from cevich/new_python_images
Cirrus: Use updated VM images
2022-02-10 14:23:18 -05:00
Chris Evich
0f363498c2 Cirrus: Use updated VM images
Mainly this is to confirm some changes needed for the podman-py CI
setup don't disrupt operations here. Ref:

https://github.com/containers/automation_images/pull/111

Note: Glibc resolver configuration has changed from previous images.  An
additional setup command was added to remove systemd-resolved from the
chain.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-02-10 13:38:12 -05:00
Daniel J Walsh
a2dccca2e6 Merge pull request #1565 from TomSweeneyRedHat/dev/tsweeney/commonup
Bump c/common to v0.47.4
2022-02-10 09:37:04 -05:00
tomsweeneyredhat
27b77f2bde Bump c/common to v0.47.4
As the title says

Signed-off-by: tomsweeneyredhat <tsweeney@redhat.com>
2022-02-09 19:23:20 -05:00
Miloslav Trmač
6eda759dd2 Merge pull request #1564 from edsantiago/skip_sif_on_rhel
tests: skip sif test on RHEL
2022-02-07 22:25:57 +01:00
Ed Santiago
de71408294 tests: skip sif test on RHEL
(or, more precisely, if fakeroot binary not in $PATH).

Solves RHEL gating-test failure.

Signed-off-by: Ed Santiago <santiago@redhat.com>
2022-02-07 13:04:15 -07:00
Daniel J Walsh
13cd098079 Merge pull request #1561 from mtrmac/release
Release v1.6.0
2022-02-02 17:10:11 -05:00
Miloslav Trmač
697ef59525 Bump to v1.6.1-dev
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-02 22:43:04 +01:00
Miloslav Trmač
e4b79d7741 Release v1.6.0
Highlights:
- A new sif: transport
- New options --multi-arch, --preserve-digests, --sign-passphrase-file

- Use a dynamic temp dir for test
- Add an option to allow copying image indexes alone
- proxy: Add a GetFullConfig method
- proxy: Also bump compatible semver
- Add option to preserve digests on copy
- Run codespell on code
- prompt-less signing via passphrase file
- add a SIF systemtest
- Merge pull request #1550 from vrothberg/sif-test
- Improve the documentation of the argument to (skopeo inspect)
- Document where various fields of (skopeo inspect) come from
- Improve the documentation of boolean flags

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-02 22:39:41 +01:00
Daniel J Walsh
bf24ce9ff2 Merge pull request #1560 from rhatdan/VENDOR
Bump version of containers/image and containers/common
2022-02-02 14:40:51 -05:00
Daniel J Walsh
162bbab3a6 Bump version of containers/image and containers/common
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-02-02 14:40:05 -05:00
Valentin Rothberg
cf19643e76 Merge pull request #1555 from mtrmac/inspect-docs
Improve documentation of skopeo inspect
2022-02-01 09:56:19 +01:00
Valentin Rothberg
afc18ceed3 Merge pull request #1557 from mtrmac/compress-docs
Improve the documentation of boolean flags
2022-02-01 09:55:29 +01:00
Miloslav Trmač
004519f143 Improve the documentation of boolean flags
The Go behavior of boolean flags is as follows:

Accepted values are --flag, which is the same as --flag=true, and --flag=false,
which is the default (except for OptionalBoolFlag).
--flag {false,true} is parsed as --flag=true with a non-option {false,true} argument.

So, for almost all flags, document them just as --flag, not
mentioning the [={false,true}] part, because users can just
omit =true, or the whole flag instead of =false.

OTOH, for tls-verify, document only the tls-verify={true,false}
variant, because the primary use is tls-verify=false, and because
tls-verify is not "the default", but equivalent to an explicit
tls-verify=true (overriding registries.conf).

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-01 02:16:45 +01:00
Miloslav Trmač
9db60ec007 Document where various fields of (skopeo inspect) come from
... and suggest how to deal with other-architecture images,
a fairly frequent point of confusion.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-01 02:16:35 +01:00
Miloslav Trmač
cb74933b41 Improve the documentation of the argument to (skopeo inspect)
Don't repeat ourselves, and actually point to some documentation.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-02-01 02:16:35 +01:00
Miloslav Trmač
8fb455174d Merge pull request #1556 from rhatdan/VENDOR
Update vendor of containers/storage and containers/common
2022-02-01 01:32:11 +01:00
Daniel J Walsh
7f4db3db9d Update vendor of containers/storage and containers/common
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-01-31 17:27:05 -05:00
Daniel J Walsh
96cdfac7d9 Merge pull request #1550 from vrothberg/sif-test
add a SIF systemtest
2022-01-27 08:46:27 -05:00
Valentin Rothberg
a4476c358c add a SIF systemtest
To make sure that the basic functionality is exercised in Skopeo and
c/image CI.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2022-01-27 10:02:02 +01:00
Daniel J Walsh
1391aae0a5 Merge pull request #1551 from rhatdan/VENDOR
Update vendor of containers/common
2022-01-26 12:50:22 -05:00
Daniel J Walsh
042f481629 Update vendor of containers/common
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-01-26 12:49:18 -05:00
Daniel J Walsh
3518c50688 Merge pull request #1547 from containers/dependabot/go_modules/github.com/containers/storage-1.38.1
Bump github.com/containers/storage from 1.38.0 to 1.38.1
2022-01-26 11:46:13 -05:00
Chris Evich
327f87d79b Merge pull request #1549 from cevich/fix_yaml
Github workflow: Fix yaml syntax
2022-01-26 11:26:04 -05:00
Chris Evich
bd8ed664d5 Github workflow: Fix yaml syntax
Same problem as addressed in
https://github.com/containers/podman/pull/13005 I neglected to include
in https://github.com/containers/skopeo/pull/1546 for whatever reason.
This commit makes it right.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-01-26 10:17:52 -05:00
dependabot[bot]
b51707d50d Bump github.com/containers/storage from 1.38.0 to 1.38.1
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.38.0 to 1.38.1.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.38.0...v1.38.1)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-26 12:42:07 +00:00
Miloslav Trmač
2c84bc232c Merge pull request #1540 from vrothberg/passphrase
prompt-less signing via passphrase file
2022-01-26 13:41:08 +01:00
Valentin Rothberg
bb49923af4 prompt-less signing via passphrase file
To support signing images without prompting the user, add CLI flags for
providing a passphrase file.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2022-01-26 08:30:49 +01:00
Daniel J Walsh
639aabbaf3 Merge pull request #1546 from cevich/notify_on_error
[CI:DOCS] Github-workflow: Report both failures and errors
2022-01-25 19:50:18 -05:00
Chris Evich
cd58349b25 Github-workflow: Report both failures and errors
Port of changes from https://github.com/containers/podman/pull/12997 and
https://github.com/containers/podman/pull/13005 to the workflow in this
repository.

***Note***: Impractical to automatically verify these changes until
they're merged into `main`.  Though the similar changes made in the
podman repo. have been manually confirmed to function as intended.

Signed-off-by: Chris Evich <cevich@redhat.com>
2022-01-25 14:56:15 -05:00
Daniel J Walsh
4b79ed7d7d Merge pull request #1543 from rhatdan/codespell
Run codespell on code
2022-01-21 15:29:35 -05:00
Daniel J Walsh
2858904e4b Run codespell on code
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-01-21 07:49:49 -05:00
Miloslav Trmač
15296d9876 Merge pull request #1542 from rhatdan/VENDOR
Update the vendor of containers/common
2022-01-20 20:11:19 +01:00
Daniel J Walsh
923c58a8ee Update the vendor of containers/common
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2022-01-20 13:30:07 -05:00
Miloslav Trmač
43726bbc27 Merge pull request #1541 from containers/dependabot/go_modules/github.com/containers/storage-1.38.0
Bump github.com/containers/storage from 1.37.0 to 1.38.0
2022-01-20 13:27:03 +01:00
dependabot[bot]
1bf18b7ef8 Bump github.com/containers/storage from 1.37.0 to 1.38.0
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.37.0 to 1.38.0.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.37.0...v1.38.0)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-20 09:10:51 +00:00
Daniel J Walsh
df4d82b960 Merge pull request #1536 from mtrmac/dep-updates
Update github.com/containerd/containerd to 1.5.9
2022-01-07 10:53:39 -05:00
Miloslav Trmač
d32c56b47f Update github.com/containerd/containerd to 1.5.9
> go get github.com/containerd/containerd@latest
> make vendor

... because 1.5.9 contains a vulnerability fix, and we
want to silence scanners.

NOTE: Skopeo DOES NOT use the vulnerable code that
was fixed in containerd 1.5.9, so it is NOT vulnerable to
GHSA-mvff-h3cj-wj9c .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-01-06 22:11:46 +01:00
Miloslav Trmač
6007e792e4 Fix the pseudo-version of github.com/opencontainers/image-spec
> go get github.com/opencontainers/image-spec@a5463b7f9c8451553af3adcba2cab538469df00c
> make vendor

Primarily we want to use a 1.0.3-0... version rather than 1.0.2-0..., so that
dependencies on 1.0.2 don't cause Skopeo to use 1.0.2 instead of
the later main-branch code.

Go has some logic to prevent using pseudo-version that don't follow
a released version (which is the case here, where 1.0.2 is on a branch,
and we want to use a main-branch commit instead); luckily some later
PRs on the main branch include the full contents of the 1.0.2 branch.
So, update a bit further along the main branch.

This particular commit corresponds to the choice in
https://github.com/containers/image/pull/1433 .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-01-06 22:11:14 +01:00
Daniel J Walsh
77f881e61c Merge pull request #1532 from mtrmac/bump-runc
Update github.com/opencontainers/runc to v1.0.3
2022-01-06 07:53:40 -05:00
Miloslav Trmač
5aa06a51f4 Update github.com/opencontainers/runc to v1.0.3
... to silence warnings about CVE-2021-43784
/ GHSA-v95c-p5hm-xq8f .

NOTE: The vulnerable code was not used in this package,
so Skopeo is has not been vulnerable to this issue.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-01-03 12:51:52 +01:00
Miloslav Trmač
e422e44fca Merge pull request #1527 from containers/dependabot/go_modules/github.com/spf13/cobra-1.3.0
Bump github.com/spf13/cobra from 1.2.1 to 1.3.0
2021-12-15 22:11:56 +01:00
dependabot[bot]
f6a84289eb Bump github.com/spf13/cobra from 1.2.1 to 1.3.0
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.2.1 to 1.3.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Changelog](https://github.com/spf13/cobra/blob/master/CHANGELOG.md)
- [Commits](https://github.com/spf13/cobra/compare/v1.2.1...v1.3.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-15 09:33:22 +00:00
Daniel J Walsh
2689eb367f Merge pull request #1526 from containers/dependabot/go_modules/github.com/docker/docker-20.10.12incompatible
Bump github.com/docker/docker from 20.10.11+incompatible to 20.10.12+incompatible
2021-12-14 15:07:33 -05:00
dependabot[bot]
c5b45c6c49 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.11+incompatible to 20.10.12+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.11...v20.10.12)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-14 09:33:41 +00:00
Miloslav Trmač
037f518146 Merge pull request #1520 from Jamstah/preserve-digests
Add option to preserve digests on copy and sync
2021-12-10 17:06:53 +01:00
James Hewitt
c582c4844f Add option to preserve digests on copy
When enabled, if digests can't be preserved an error will be raised.

Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
2021-12-07 13:16:10 +00:00
James Hewitt
2046bfdaaa Add option to preserve digests on copy
When enabled, if digests can't be preserved an error will be raised.

Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
2021-12-07 13:16:10 +00:00
Daniel J Walsh
25868f17c0 Merge pull request #1523 from cgwalters/proxy-config-2
proxy: Add a GetFullConfig method
2021-12-07 06:33:55 -05:00
Colin Walters
e7dc5e79f2 proxy: Also bump compatible semver
To denote we have new API.
2021-12-06 20:59:17 -05:00
Colin Walters
3606b2d1de proxy: Add a GetFullConfig method
Sadly...I swear I had tested this at one point, but it was
*definitely* not the intention that we just return the container
runtime configuration.

I need a method to return the full image configuration.  At some point
I must have accidentally added a redundant `.Config`.

This whole new method `GetFullConfig` is like `GetConfig` but
returns the whole image configuration.  A specific motivation
here is that it's only in the image configuration that we can
stick arbitrary metadata (labels) that will survive a round trip through
docker schema v2.
2021-12-06 17:15:46 -05:00
Daniel J Walsh
f03d0401c1 Merge pull request #1521 from mtrmac/image-spec
Update opencontainers/image-spec
2021-12-02 13:51:26 -05:00
Miloslav Trmač
5c82c7728f Update github.com/containerd/containerd to v1.5.8
just to keep various dependency checkers happy.

> go get github.com/containerd/containerd@v1.5.8

NOTE: This is NOT a fix for CVE-2021-41190 / GHSA-77vh-xpmg-72qh ,
that was fixed in Skopeo 1.5.2.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-12-02 19:03:33 +01:00
Miloslav Trmač
37d801c90b Update opencontainers/image-spec
... to a version past 1.0.2, just to keep various
dependency checkers happy.

> go get github.com/opencontainers/image-spec@v1.0.2-0.20211123152302-43a7dee1ec31

The commit is intended to match https://github.com/containers/image/pull/1419
to minimize churn.

NOTE: This is NOT a fix for CVE-2021-41190 / GHSA-77vh-xpmg-72qh ,
that was fixed in Skopeo 1.5.2.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-12-02 18:56:36 +01:00
Miloslav Trmač
c3f65951bc Merge pull request #1511 from Jamstah/copy-sparse-manifest
Add an option to allow copying image indexes alone
2021-12-02 14:38:27 +01:00
James Hewitt
d94015466f Add an option to allow copying image indexes alone
The new --multi-arch option allows the user to select between copying the
image associated with the system platform, all images in the index, or
just the index itself without attempting to copy the images.

Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
2021-11-27 15:38:42 +00:00
Miloslav Trmač
1d24e657fa Merge pull request #1518 from Jamstah/int-test-ignore
Stop test producing output in source directory
2021-11-27 16:13:31 +01:00
James Hewitt
4dcd28df92 Use a dynamic temp dir for test
This test was incorrectly assuming that nothing would be made on disk,
but it was putting files into the source directory.

Signed-off-by: James Hewitt <james.hewitt@uk.ibm.com>
2021-11-27 13:44:28 +00:00
Miloslav Trmač
789ee8bea9 Bump to 1.5.3-dev 2021-11-26 11:49:38 -05:00
Miloslav Trmač
8a88191c84 Release 1.5.2
Includes a fix for CVE-2021-41190 / GHSA-77vh-xpmg-72qh .

- use fedora:latest in contrib/skopeoimage/*/Dockerfile
- Fix test bug that prevented useful diagnostics on registry fail
- proxy: Add an API to fetch the config upconverted to OCI
- proxy: Add support for manifest lists
- proxy: Uncapitalize all errors
- Cirrus: Bump Fedora to release 35 & Ubuntu to 21.10
- Update to c/image v5.17.0

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-11-26 11:49:38 -05:00
Miloslav Trmač
69728fdf93 Update to c/image v5.17.0
Includes a fix for CVE-2021-41190 / GHSA-77vh-xpmg-72qh .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-11-22 14:19:37 -05:00
Daniel J Walsh
904c745bb0 Merge pull request #1499 from cevich/update_to_f35
Cirrus: Bump Fedora to release 35 & Ubuntu to 21.10
2021-11-19 11:57:13 -05:00
Chris Evich
47066f2d77 Cirrus: Bump Fedora to release 35 & Ubuntu to 21.10
The Fedora 35 cloud images have switched to UEFI boot with a GPT
partition. Formerly, all Fedora images included support for runtime
re-partitioning. However, the requirement to test alternate storage
has since been dropped/removed.  Rather than maintain a disused
feature, and supporting scripts, these Fedora VM images have reverted
to the default: Automatically resize to 100% on boot.

Signed-off-by: Chris Evich <cevich@redhat.com>
2021-11-19 10:11:16 -05:00
Daniel J Walsh
fab344c335 Merge pull request #1509 from containers/dependabot/go_modules/github.com/docker/docker-20.10.11incompatible
Bump github.com/docker/docker from 20.10.10+incompatible to 20.10.11+incompatible
2021-11-18 09:18:17 -05:00
dependabot[bot]
adfa1d4e49 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.10+incompatible to 20.10.11+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.10...v20.10.11)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-18 09:33:27 +00:00
Valentin Rothberg
002978258c Merge pull request #1495 from cgwalters/proxy-config
proxy: Add `GetConfig`, add manifest list support, add an integration test
2021-11-16 17:00:59 +01:00
Colin Walters
05a2ed4921 proxy: Uncapitalize all errors
By Go convention.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
e9535f868b tests: Add new "procutils" that exposes PDEATHSIG
To fix compilation on MacOS.

I think actually we want to use this pervasively in our tests
on Linux; it doesn't really matter when run inside a transient
container, but `PDEATHSIG` is useful for persistent containers (e.g.)
toolbox and when running outside of a pid namespace, e.g. on a host
system shell directly or in systemd.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
fa86297c36 proxy_test: Test GetConfig
Now that we have a test suite, let's use it more.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
2bb6f27d13 proxy_test: Add helper to read all from a reply
Prep for testing `GetConfig`.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
f90725d80c proxy_test: Add a helper method to call without fd
To verify in one place.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
644074cbb4 proxy: Add support for manifest lists
We need to support manifest lists. I'm not sure how I missed this
originally.  At least now we have integration tests that cover this.

The issue here is fairly subtle - the way c/image works right now,
`image.FromUnparsedImage` does pick a matching image from a list
by default.  But it also overrides `GetManifest()` to return the
original manifest list, which defeats our goal here.

Handle this by adding explicit manifest list support code.  We'll
want this anyways for future support for `GetRawManifest` or so
which exposes OCI manifest lists to the client.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
83416068d3 tests/integration/proxy_test: New test that exercises proxy.go
I debated adding "reverse dependency testing" using
https://crates.io/crates/containers-image-proxy
but I think it's easier to reuse the test infrastructure here.

This also starts fleshing out a Go client for the proxy (not
that this is going to be something most Go projects would want
versus vendoring c/image...but hey, maybe it'll be useful).

Now what I hit in trying to use the main test images is currently
the proxy fails on manifest lists, so I'll need to fix that.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
a3adf36db6 proxy: Use float → int helper for pipeid
Just noticed while scrolling past the code.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
6510f1011b proxy: Add a helper to return a byte array
Since this is shared between the manifest and config paths.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Colin Walters
e7b7be5734 proxy: Add an API to fetch the config upconverted to OCI
While the caller could fetch this today as a blob, it'd be in
either docker or oci schema.  In keeping with the model of having
this proxy only expose OCI, add an API which uses the c/image logic
to do the conversion.

This is necessary for callers to get the diffIDs, and in general
to implement something like an external `skopeo copy`.

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-15 21:02:14 -05:00
Miloslav Trmač
1e01e38459 Merge pull request #1502 from edsantiago/duh
Fix bug that prevented useful diagnostics on registry fail
2021-11-11 15:12:08 +01:00
Ed Santiago
942cd6ec58 Fix bug that prevented useful diagnostics on registry fail
Sigh. 'expr 1 - 1' yields 0 (correctly) but also exits 1. This
is even documented in the man page, but I didn't know it. And
thus, on the final iteration, when timeout reached 0, BATS
errored out on the expr instead of continuing to the 'podman logs'
or the 'die' message.

Solution is super trivial: use $(( ... )) instead of expr.

Signed-off-by: Ed Santiago <santiago@redhat.com>
2021-11-10 19:56:33 -07:00
Tom Sweeney
a902709e14 Merge pull request #1496 from lsm5/skopeoimage
use fedora:latest in contrib/skopeoimage/*/Dockerfile
2021-11-08 17:05:21 -05:00
Lokesh Mandvekar
41de7f2f66 use fedora:latest in contrib/skopeoimage/*/Dockerfile
Fixes: #1492

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2021-11-08 14:44:42 -05:00
Lokesh Mandvekar
c264cec359 Move to v1.5.2-dev
Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2021-11-04 10:15:42 -04:00
Lokesh Mandvekar
2b357d8276 Bump to v1.5.1
Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2021-11-04 10:15:42 -04:00
Colin Walters
4acc9f0d2c main: Error out if an unrecognized subcommand is provided
Surprisingly, the spf13/cobra CLI parsing logic, when presented
with an unknown subcommand outputs usage to stdout
and *exits successfully*.

This is bad for both users and scripts.  Cargo cult some code
I found in podman to handle this.

Motivated by https://github.com/containers/containers-image-proxy-rs/pull/1

Signed-off-by: Colin Walters <walters@verbum.org>
2021-11-03 15:14:49 -04:00
Daniel J Walsh
c2732cb15d Merge pull request #1480 from jaikiran/785
skopeo inspect command - introduce a way to skip querying all available tags
2021-10-26 14:57:51 -04:00
Valentin Rothberg
49f709576a Merge pull request #1487 from vrothberg/vendor-common
move optional-flag code to c/common/pkg/flag
2021-10-26 16:15:36 +02:00
Valentin Rothberg
7885162a35 move optional-flag code to c/common/pkg/flag
As the title says: it allows for code share with other tools such as
Podman and Buildah.

Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
2021-10-26 15:18:30 +02:00
Miloslav Trmač
01e58f8e25 Merge pull request #1484 from lyft/precompute-digests
Add --dest-precompute-digests option for docker
2021-10-22 16:54:56 +02:00
Paul Fisher
36d860ebce Add --dest-precompute-digests option for docker
This ensures layers are not uploaded that already exist on the
destination registry, in exchange for streaming layers to temporary
files when digests are unknown (ex. compressing "on the fly").

Signed-off-by: Paul Fisher <pfisher@lyft.com>
2021-10-21 17:29:03 -07:00
Paul Fisher
c8777f3bf7 bump containers/image to 2541165
Signed-off-by: Paul Fisher <pfisher@lyft.com>
2021-10-21 17:29:03 -07:00
Miloslav Trmač
8f64c0412f Merge pull request #1483 from jpetazzo/static-build-instructions
Add instructions to generate static binaries
2021-10-20 16:07:29 +02:00
Jerome Petazzoni
985d4c09ae Add instructions to generate static binaries
Following the discussion in #1478, we don't want to provide
(and maintain) static binaries, but giving instructions to
produce such builds (with appropriate warnings around these
instructions) was considered acceptable, so - here we go!
2021-10-19 23:10:48 +02:00
Miloslav Trmač
8182255d22 Merge pull request #1476 from cgwalters/proxy
Add new `experimental-image-proxy` hidden command
2021-10-14 20:48:58 +02:00
Colin Walters
11b5989872 Add new experimental-image-proxy hidden command
This imports the code from https://github.com/cgwalters/container-image-proxy

First, assume one is operating on a codebase that isn't Go, but wants
to interact with container images - we can't just include the Go containers/image
library.

The primary intended use case of this is for things like
[ostree-containers](https://github.com/ostreedev/ostree-rs-ext/issues/18)
where we're using container images to encapsulate host operating system
updates, but we don't want to involve the [containers/image](github.com/containers/image/)
storage layer.

Vendoring the containers/image stack in another project is a large lift; the stripped
binary for this proxy standalone weighs in at 16M (I'm sure the lack
of LTO and the overall simplicity of the Go compiler is a large factor).
Anyways, I'd like to avoid shipping another copy.

This command is marked as experimental, and hidden.  The goal is
just to use it from the ostree stack for now, ideally shipping at least
in CentOS 9 Stream relatively soon.   We can (and IMO should)
change and improve it later.

A lot more discussion in https://github.com/cgwalters/container-image-proxy/issues/1
2021-10-14 14:16:32 -04:00
Jaikiran Pai
2144a37c21 issue#785 inspect command - introduce a way to skip querying available tags for an image 2021-10-12 20:24:39 +05:30
Valentin Rothberg
9c9a9f3a1f Merge pull request #1481 from mtrmac/container-install
Document container images as an alternative to installing packages
2021-10-12 10:13:11 +02:00
Miloslav Trmač
60c98cacde Document container images as an alternative to installing packages
Also fix the location of the introductory text about building from source,
and fix the document title.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-10-11 20:26:39 +02:00
Miloslav Trmač
116e75fbfd Merge pull request #1470 from jaikiran/527
Introduce --username and --password to pass credentials
2021-10-07 18:34:51 +02:00
Jaikiran Pai
89ecd5a4c0 Introduce --username and --password to pass credentials 2021-10-07 20:31:59 +05:30
Daniel J Walsh
fc81803bfa Merge pull request #1475 from rhatdan/main
Bump to v1.5.0
2021-10-06 16:34:36 -04:00
Daniel J Walsh
119eeb83a7 Move to v1.5.1-dev
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2021-10-06 16:32:33 -04:00
Daniel J Walsh
209a993159 Bump to v1.5.0
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2021-10-06 16:31:52 -04:00
Daniel J Walsh
5e7d11cbf3 Merge pull request #1474 from containers/dependabot/go_modules/github.com/containers/image/v5-5.16.1
Bump github.com/containers/image/v5 from 5.16.0 to 5.16.1
2021-10-06 12:17:20 -04:00
Lokesh Mandvekar
fc86da2023 Merge branch 'main' into dependabot/go_modules/github.com/containers/image/v5-5.16.1 2021-10-06 15:36:01 +00:00
Miloslav Trmač
0f370eed02 Merge pull request #1471 from containers/dependabot/go_modules/github.com/docker/docker-20.10.9incompatible
Bump github.com/docker/docker from 20.10.8+incompatible to 20.10.9+incompatible
2021-10-06 17:29:21 +02:00
dependabot[bot]
3e4d4a480f Bump github.com/containers/image/v5 from 5.16.0 to 5.16.1
Bumps [github.com/containers/image/v5](https://github.com/containers/image) from 5.16.0 to 5.16.1.
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.16.0...v5.16.1)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-06 08:29:03 +00:00
dependabot[bot]
3a97a0c032 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.8+incompatible to 20.10.9+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.8...v20.10.9)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-05 20:59:22 +00:00
Miloslav Trmač
ff88d3fcc2 Remove leftover Nix packaging files
... after https://github.com/containers/skopeo/pull/1463 dropped
it from the Makefile.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-10-05 20:11:54 +00:00
Daniel J Walsh
64be259655 Merge pull request #1472 from mtrmac/containerd
Update github.com/containerd/containerd to v1.5.7
2021-10-05 15:10:17 -04:00
Miloslav Trmač
e19b57c3b9 Update github.com/containerd/containerd to v1.5.7
... to include a fix for
https://github.com/advisories/GHSA-c2h3-6mxw-7mvq .

(Note that Skopeo doesn't depend on the vulnerable code,
so this is primarily to avoid dependency checker warnings.)

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-10-05 18:45:24 +02:00
Miloslav Trmač
2d5a00e833 Merge pull request #1468 from jaikiran/1466
Introduce a --ignore option to allow "sync" command to continue syncing even after a particular image sync fails
2021-10-05 15:19:12 +02:00
Jaikiran Pai
b950f83c60 issue#1466 - Introduce a --keep-going option to allow "sync" command to continue syncing even after a particular image sync fails 2021-10-05 07:18:38 +05:30
Daniel J Walsh
a95b0cc6fa Merge pull request #1467 from containers/dependabot/go_modules/github.com/containers/storage-1.37.0
Bump github.com/containers/storage from 1.36.0 to 1.37.0
2021-10-01 10:38:40 -04:00
dependabot[bot]
12d0103730 Bump github.com/containers/storage from 1.36.0 to 1.37.0
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.36.0 to 1.37.0.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.36.0...v1.37.0)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-10-01 08:33:20 +00:00
Valentin Rothberg
53cf287e37 Merge pull request #1464 from lsm5/update-installation-steps
Update installation doc with latest steps
2021-10-01 08:42:21 +02:00
Lokesh Mandvekar
e0c53dfd9b Update installation doc with latest steps
- Remove Kubic repo suggestions where skopeo exists by default
- Include documentation about lack of Windows package
(RE: https://github.com/containers/skopeo/issues/715)

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2021-09-30 13:35:32 -04:00
Valentin Rothberg
86fa758ad8 Merge pull request #1463 from lsm5/drop-nix
drop nix support
2021-09-28 15:23:03 +02:00
Lokesh Mandvekar
aba57a8814 Makefile: drop nix support
nix build is no longer being maintained.

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2021-09-27 14:55:44 -04:00
Daniel J Walsh
4d3588e46a Merge pull request #1462 from containers/dependabot/go_modules/github.com/containers/common-0.46.0
Bump github.com/containers/common from 0.45.0 to 0.46.0
2021-09-27 13:14:33 -04:00
dependabot[bot]
93c42bcd74 Bump github.com/containers/common from 0.45.0 to 0.46.0
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.45.0 to 0.46.0.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.45.0...v0.46.0)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-27 08:31:48 +00:00
Daniel J Walsh
2c2e5b773f Merge pull request #1431 from rhatdan/tls-verify
Remove the extra (defaults to true) help msg
2021-09-25 05:25:26 -04:00
Miloslav Trmač
25d3e7b46d Merge pull request #1457 from containers/dependabot/go_modules/github.com/containers/common-0.45.0
Bump github.com/containers/common from 0.44.1 to 0.45.0
2021-09-22 18:32:37 +02:00
dependabot[bot]
c0f07d3dfd Bump github.com/containers/common from 0.44.1 to 0.45.0
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.44.1 to 0.45.0.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.44.1...v0.45.0)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-22 08:33:52 +00:00
Daniel J Walsh
c5a5199f57 Merge pull request #1456 from containers/dependabot/go_modules/github.com/containers/common-0.44.1
Bump github.com/containers/common from 0.44.0 to 0.44.1
2021-09-21 05:32:10 -04:00
dependabot[bot]
0ce7081e6d Bump github.com/containers/common from 0.44.0 to 0.44.1
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.44.0 to 0.44.1.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.44.0...v0.44.1)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-21 08:28:21 +00:00
Miloslav Trmač
db1e814e86 Merge pull request #1455 from mtrmac/mpb
Update to github.com/vbauerster/mpb v7.1.5
2021-09-20 16:14:32 +02:00
Miloslav Trmač
52dafe8f8d Update to github.com/vbauerster/mpb v7.1.5
... to fix https://github.com/vbauerster/mpb/issues/100 .

> go get github.com/vbauerster/mpb/v7@latest
> make vendor

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-09-20 15:43:07 +02:00
Daniel J Walsh
31b8981b04 Merge pull request #1444 from cevich/update_images
Update VM Images + Drop prior-ubuntu references
2021-09-16 04:06:29 -04:00
Daniel J Walsh
d8ba8b90fe Merge pull request #1443 from jaikiran/1411
Introduce DISABLE_DOCS to skip doc generation while building from source
2021-09-16 04:05:37 -04:00
Jaikiran Pai
ee8b8e77fc Explain the usage of DISABLE_DOCS in the installation doc 2021-09-15 17:21:31 +05:30
Chris Evich
1d204fb10f Update VM Images + Drop prior-ubuntu references
These images contain a workaround for:
     https://github.com/containers/podman/issues/11123

Prior-Ubuntu support is being dropped everywhere.

Ref: https://github.com/containers/podman/issues/11070
     https://github.com/containers/automation_images/pull/88

Signed-off-by: Chris Evich <cevich@redhat.com>
2021-09-14 11:32:34 -04:00
Jaikiran Pai
6131077770 issue#1411 Introduce DISABLE_DOCS to skip doc generation while building from source 2021-09-14 20:23:11 +05:30
Daniel J Walsh
177443f47d Merge pull request #1441 from containers/dependabot/go_modules/github.com/containers/common-0.44.0
Bump github.com/containers/common from 0.43.2 to 0.44.0
2021-09-14 06:19:20 -04:00
dependabot[bot]
ed96bf04a1 Bump github.com/containers/common from 0.43.2 to 0.44.0
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.43.2 to 0.44.0.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.43.2...v0.44.0)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-14 08:34:07 +00:00
Daniel J Walsh
30f208ea59 Merge pull request #1439 from containers/dependabot/go_modules/github.com/containers/storage-1.36.0
Bump github.com/containers/storage from 1.35.0 to 1.36.0
2021-09-13 14:04:29 -04:00
dependabot[bot]
a837fbe28b Bump github.com/containers/storage from 1.35.0 to 1.36.0
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.35.0 to 1.36.0.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.35.0...v1.36.0)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-13 08:31:20 +00:00
Daniel J Walsh
9edeb69f6a Remove the extra (defaults to true) help msg
By default skopeo checks to see if the user actually uses one of the
--*tls-verify flags. Their initial value is ignored.  Setting the
initial value to false causes Cobra to not display the default value on
the screen when the user runs a `skopeo --help` command.

If the user does not specify a --*tls-verify option, it falls back to
using the value specified in the registries.conf file.

Fixes: https://github.com/containers/skopeo/issues/1383

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2021-08-27 06:16:50 -04:00
Daniel J Walsh
47b808275d Merge pull request #1430 from containers/dependabot/go_modules/github.com/containers/image/v5-5.16.0
Bump github.com/containers/image/v5 from 5.15.2 to 5.16.0
2021-08-26 07:06:21 -04:00
dependabot[bot]
a2d083ca84 Bump github.com/containers/image/v5 from 5.15.2 to 5.16.0
Bumps [github.com/containers/image/v5](https://github.com/containers/image) from 5.15.2 to 5.16.0.
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.15.2...v5.16.0)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-26 08:30:41 +00:00
Miloslav Trmač
4fda005a3e Merge pull request #1427 from mtrmac/go1.17
Run (gofmt -s -w)
2021-08-23 20:50:51 +02:00
Miloslav Trmač
0e87d4d1ca Run (gofmt -s -w)
Go 1.17 introduces a much more reasonable build constraint format, and gofmt now fails without using it.

Sadly we still need the old format as well, to support <1.17 builds.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-08-23 18:04:45 +02:00
Miloslav Trmač
5739b90946 Merge pull request #1428 from mtrmac/deps
Update non-module dependencies
2021-08-23 18:04:19 +02:00
Miloslav Trmač
c399909f04 Update non-module dependencies
Dependabot was apparently not picking these up (and
several haven't had a release for a long time anyway).

Also move from github.com/go-check/check to its newly
declared (and go.mod-enforced) name gopkg.in/check.v1.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-08-23 15:52:48 +02:00
Daniel J Walsh
5da1b0f304 Merge pull request #1422 from containers/dependabot/go_modules/github.com/containers/image/v5-5.15.2
Bump github.com/containers/image/v5 from 5.15.1 to 5.15.2
2021-08-19 06:11:23 -04:00
dependabot[bot]
102e2143ac Bump github.com/containers/image/v5 from 5.15.1 to 5.15.2
Bumps [github.com/containers/image/v5](https://github.com/containers/image) from 5.15.1 to 5.15.2.
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.15.1...v5.15.2)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-19 08:32:27 +00:00
Miloslav Trmač
291bbdf66c Merge pull request #1420 from rhatdan/codespell
[CI:DOCS] Add OWNERS file
2021-08-18 23:17:03 +02:00
Miloslav Trmač
6bdadc8058 Merge pull request #1421 from containers/dependabot/go_modules/github.com/containers/common-0.43.2
Bump github.com/containers/common from 0.43.1 to 0.43.2
2021-08-18 18:49:38 +02:00
dependabot[bot]
7d5ef9d9e7 Bump github.com/containers/common from 0.43.1 to 0.43.2
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.43.1 to 0.43.2.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.43.1...v0.43.2)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-18 08:30:12 +00:00
Daniel J Walsh
70eaf171ea Add OWNERS file
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2021-08-17 16:50:30 -04:00
Miloslav Trmač
8da1c849a8 Merge pull request #1419 from containers/dependabot/go_modules/github.com/containers/image/v5-5.15.1
Bump github.com/containers/image/v5 from 5.15.0 to 5.15.1
2021-08-17 19:05:11 +02:00
dependabot[bot]
6196947297 Bump github.com/containers/image/v5 from 5.15.0 to 5.15.1
Bumps [github.com/containers/image/v5](https://github.com/containers/image) from 5.15.0 to 5.15.1.
- [Release notes](https://github.com/containers/image/releases)
- [Commits](https://github.com/containers/image/compare/v5.15.0...v5.15.1)

---
updated-dependencies:
- dependency-name: github.com/containers/image/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-17 10:51:44 +00:00
Daniel J Walsh
ecd3809bf5 Merge pull request #1418 from containers/dependabot/go_modules/github.com/containers/storage-1.34.1
Bump github.com/containers/storage from 1.34.0 to 1.34.1
2021-08-17 06:41:40 -04:00
dependabot[bot]
ec1ac5d0c8 Bump github.com/containers/storage from 1.34.0 to 1.34.1
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.34.0 to 1.34.1.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.34.0...v1.34.1)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-17 08:25:16 +00:00
Miloslav Trmač
a15fcbe63c Merge pull request #1417 from containers/dependabot/go_modules/github.com/containers/common-0.43.1
Bump github.com/containers/common from 0.43.0 to 0.43.1
2021-08-14 15:00:41 +02:00
dependabot[bot]
082db20fc0 Bump github.com/containers/common from 0.43.0 to 0.43.1
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.43.0 to 0.43.1.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.43.0...v0.43.1)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-13 08:22:25 +00:00
Miloslav Trmač
85ce748e8e Merge pull request #1414 from rhatdan/codespell
Add codespell fixes
2021-08-12 15:26:46 +02:00
Daniel J Walsh
8dce403b95 Add codespell fixes
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2021-08-11 16:47:48 -04:00
Miloslav Trmač
ab36f7f092 Merge pull request #1413 from edsantiago/flake_debug
systemtests: if registry times out, show container logs
2021-08-11 18:46:53 +02:00
Ed Santiago
f6ae786508 systemtests: if registry times out, show container logs
the 'signing' test is flaking; symptom is that we can never
connect to the port on the registry:

   https://api.cirrus-ci.com/v1/task/6208385738604544/logs/system.log

By all indications, the registry is up, i.e., the 'podman rm -f reg'
in teardown() succeeds, as shown by the 53c (CID) in the log. (It
bothers me that the FAIL message from die() does not appear in the
log, and I can't figure out why).

To try to diagnose this, run 'podman logs' on the registry upon
failure.

Signed-off-by: Ed Santiago <santiago@redhat.com>
2021-08-11 10:10:23 -06:00
Miloslav Trmač
4069abba0e Merge pull request #1412 from containers/dependabot/go_modules/github.com/containers/common-0.43.0
Bump github.com/containers/common from 0.42.1 to 0.43.0
2021-08-11 16:13:59 +02:00
dependabot[bot]
9acb8b6a15 Bump github.com/containers/common from 0.42.1 to 0.43.0
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.42.1 to 0.43.0.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.42.1...v0.43.0)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-11 08:23:50 +00:00
Daniel J Walsh
0ae0e8d23f Merge pull request #1410 from containers/dependabot/go_modules/github.com/containers/storage-1.34.0
Bump github.com/containers/storage from 1.33.2 to 1.34.0
2021-08-10 14:05:12 -04:00
dependabot[bot]
a23b9f532d Bump github.com/containers/storage from 1.33.2 to 1.34.0
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.33.2 to 1.34.0.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.33.2...v1.34.0)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2021-08-10 13:16:43 -04:00
Daniel J Walsh
252af41dba Merge pull request #1408 from containers/dependabot/go_modules/github.com/containers/storage-1.33.2
Bump github.com/containers/storage from 1.33.1 to 1.33.2
2021-08-06 11:50:47 -04:00
dependabot[bot]
be821b4f59 Bump github.com/containers/storage from 1.33.1 to 1.33.2
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.33.1 to 1.33.2.
- [Release notes](https://github.com/containers/storage/releases)
- [Changelog](https://github.com/containers/storage/blob/main/docs/containers-storage-changes.md)
- [Commits](https://github.com/containers/storage/compare/v1.33.1...v1.33.2)

---
updated-dependencies:
- dependency-name: github.com/containers/storage
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2021-08-06 11:15:28 -04:00
Miloslav Trmač
678682f128 Merge pull request #1334 from cevich/drop_podmanmake
Cirrus: Run checks directly on the host
2021-08-04 22:31:21 +02:00
Chris Evich
da294ebce1 Merge pull request #1405 from cevich/cron_fail_mail
[CI:DOCS] Github: Add workflow to monitor Cirrus-Cron builds
2021-08-04 15:55:08 -04:00
Chris Evich
ab87b15fea Cirrus: Run checks directly on the host
In order to meet achievable deadlines converting from Travis to Cirrus
CI, one significant artifact was carried forward (instead of fixing):

Depending on a `--privileged` container to execute all/most automated
checks/tests.

Prior attempts to remove this aspect resulted in several test failures.
Fixing the problems was viewed as more time-consuming than simply
preserving this runtime environment.

Time has passed, and the code has since moved on.  This commit removes
the legacy need to execute CI operations in a `--privileged`
container, instead running them directly on the host.  At the same time,
the necessary test binaries are obtained from the same container used
for development/local testing purposes.  This ensures the two
experiences are virtually always identical.

Signed-off-by: Chris Evich <cevich@redhat.com>
2021-08-04 15:37:57 -04:00
Chris Evich
1aa98baba4 Github: Add workflow to monitor Cirrus-Cron builds
The Cirrus-CI configuration for this repository is setup to execute test
builds on certain important release branches.  There is no built-in way
to monitor these for success or failure.  This commit adds a
Github-Actions Workflow to e-mail the podman-monitor list if any fail.
Otherwise it will take no action if everything is successful.

Note: This duplicates 99.999% of the same YAML used for the Buildah
repository.  The only changes were for the settings URL and
mentioning "skopeo" in a comment.  A similar workflow is also in use
on the Podman repository.

Signed-off-by: Chris Evich <cevich@redhat.com>
2021-08-04 10:09:45 -04:00
Daniel J Walsh
3e127edb9c Merge pull request #1404 from containers/dependabot/go_modules/github.com/docker/docker-20.10.8incompatible
Bump github.com/docker/docker from 20.10.7+incompatible to 20.10.8+incompatible
2021-08-04 05:50:46 -04:00
dependabot[bot]
fbf9699867 Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 20.10.7+incompatible to 20.10.8+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Changelog](https://github.com/moby/moby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/docker/docker/compare/v20.10.7...v20.10.8)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-04 08:24:01 +00:00
Valentin Rothberg
a0084eda60 Merge pull request #1402 from containers/dependabot/go_modules/github.com/containers/common-0.42.1
Bump github.com/containers/common from 0.42.0 to 0.42.1
2021-08-03 11:12:24 +02:00
dependabot[bot]
a3bb1cc5b8 Bump github.com/containers/common from 0.42.0 to 0.42.1
Bumps [github.com/containers/common](https://github.com/containers/common) from 0.42.0 to 0.42.1.
- [Release notes](https://github.com/containers/common/releases)
- [Commits](https://github.com/containers/common/compare/v0.42.0...v0.42.1)

---
updated-dependencies:
- dependency-name: github.com/containers/common
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-03 08:31:38 +00:00
Daniel J Walsh
8060e41dce Merge pull request #1400 from mtrmac/v1.4.0
v1.4.0
2021-08-02 11:52:08 -04:00
Miloslav Trmač
0667a1e037 Bump to 1.4.1-dev
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2021-08-02 17:37:31 +02:00
2874 changed files with 332168 additions and 97344 deletions

296
.cirrus.yml Normal file
View File

@@ -0,0 +1,296 @@
---
# Main collection of env. vars to set for all tasks and scripts.
env:
####
#### Global variables used for all tasks
####
# Name of the ultimate destination branch for this CI run, PR or post-merge.
DEST_BRANCH: "main"
# Overrides default location (/tmp/cirrus) for repo clone
GOPATH: &gopath "/var/tmp/go"
GOBIN: "${GOPATH}/bin"
GOCACHE: "${GOPATH}/cache"
GOSRC: &gosrc "/var/tmp/go/src/github.com/containers/skopeo"
# Required for consistency with containers/image CI
SKOPEO_PATH: *gosrc
CIRRUS_WORKING_DIR: *gosrc
# The default is 'sh' if unspecified
CIRRUS_SHELL: "/bin/bash"
# Save a little typing (path relative to $CIRRUS_WORKING_DIR)
SCRIPT_BASE: "./contrib/cirrus"
####
#### Cache-image names to test with (double-quotes around names are critical)
####
FEDORA_NAME: "fedora-37"
# Google-cloud VM Images
IMAGE_SUFFIX: "c6300530360713216"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
# Container FQIN's
FEDORA_CONTAINER_FQIN: "quay.io/libpod/fedora_podman:${IMAGE_SUFFIX}"
# Built along with the standard PR-based workflow in c/automation_images
SKOPEO_CIDEV_CONTAINER_FQIN: "quay.io/libpod/skopeo_cidev:${IMAGE_SUFFIX}"
# Default timeout for each task
timeout_in: 45m
gcp_credentials: ENCRYPTED[52d9e807b531b37ab14e958cb5a72499460663f04c8d73e22ad608c027a31118420f1c80f0be0882fbdf96f49d8f9ac0]
validate_task:
# The git-validation tool doesn't work well on branch or tag push,
# under Cirrus-CI, due to challenges obtaining the starting commit ID.
# Only do validation for PRs.
only_if: &is_pr $CIRRUS_PR != ''
container:
image: '${SKOPEO_CIDEV_CONTAINER_FQIN}'
cpu: 4
memory: 8
script: |
make validate-local
make vendor && hack/tree_status.sh
doccheck_task:
only_if: *is_pr
depends_on:
- validate
container:
image: "${FEDORA_CONTAINER_FQIN}"
cpu: 4
memory: 8
env:
BUILDTAGS: &withopengpg 'btrfs_noversion libdm_no_deferred_remove containers_image_openpgp'
script: |
# TODO: Can't use 'runner.sh setup' inside container. However,
# removing the pre-installed package is the only necessary step
# at the time of this comment.
dnf erase -y skopeo # Guarantee non-interference
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" build
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" doccheck
osx_task:
# Don't run for docs-only or multi-arch image builds.
# Also don't run on release-branches or their PRs,
# since base container-image is not version-constrained.
only_if: &not_docs_or_release_branch >-
($CIRRUS_BASE_BRANCH == $CIRRUS_DEFAULT_BRANCH ||
$CIRRUS_BRANCH == $CIRRUS_DEFAULT_BRANCH ) &&
$CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*' &&
$CIRRUS_CRON != 'multiarch'
depends_on:
- validate
macos_instance:
image: ghcr.io/cirruslabs/macos-ventura-base:latest
setup_script: |
export PATH=$GOPATH/bin:$PATH
brew update
brew install gpgme go go-md2man
go install golang.org/x/lint/golint@latest
test_script: |
export PATH=$GOPATH/bin:$PATH
go version
go env
make validate-local test-unit-local bin/skopeo
sudo make install
/usr/local/bin/skopeo -v
cross_task:
alias: cross
only_if: >-
$CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*' &&
$CIRRUS_CRON != 'multiarch'
depends_on:
- validate
gce_instance: &standardvm
image_project: libpod-218412
zone: "us-central1-f"
cpu: 2
memory: "4Gb"
# Required to be 200gig, do not modify - has i/o performance impact
# according to gcloud CLI tool warning messages.
disk: 200
image_name: ${FEDORA_CACHE_IMAGE_NAME}
env:
BUILDTAGS: *withopengpg
setup_script: >-
"${GOSRC}/${SCRIPT_BASE}/runner.sh" setup
cross_script: >-
"${GOSRC}/${SCRIPT_BASE}/runner.sh" cross
ostree-rs-ext_task:
alias: proxy_ostree_ext
only_if: *not_docs_or_release_branch
# WARNING: This task potentially performs a container image
# build (on change) with runtime package installs. Therefore,
# its behavior can be unpredictable and potentially flake-prone.
# In case of emergency, uncomment the next statement to bypass.
#
# skip: $CI == "true"
#
depends_on:
- validate
# Ref: https://cirrus-ci.org/guide/docker-builder-vm/#dockerfile-as-a-ci-environment
container:
# The runtime image will be rebuilt on change
dockerfile: contrib/cirrus/ostree_ext.dockerfile
docker_arguments: # required build-args
BASE_FQIN: quay.io/coreos-assembler/fcos-buildroot:testing-devel
CIRRUS_IMAGE_VERSION: 1
env:
EXT_REPO_NAME: ostree-rs-ext
EXT_REPO_HOME: $CIRRUS_WORKING_DIR/../$EXT_REPO_NAME
EXT_REPO: https://github.com/ostreedev/${EXT_REPO_NAME}.git
skopeo_build_script:
- dnf builddep -y skopeo
- make
- make install
proxy_ostree_ext_build_script:
- git clone --depth 1 $EXT_REPO $EXT_REPO_HOME
- cd $EXT_REPO_HOME
- cargo test --no-run
proxy_ostree_ext_test_script:
- cd $EXT_REPO_HOME
- cargo test -- --nocapture --quiet
#####
##### NOTE: This task is subtantially duplicated in the containers/image
##### repository's `.cirrus.yml`. Changes made here should be fully merged
##### prior to being manually duplicated and maintained in containers/image.
#####
test_skopeo_task:
alias: test_skopeo
# Don't test for [CI:DOCS], [CI:BUILD], or 'multiarch' cron.
only_if: >-
$CIRRUS_CHANGE_TITLE !=~ '.*CI:BUILD.*' &&
$CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*' &&
$CIRRUS_CRON != 'multiarch'
depends_on:
- validate
gce_instance:
image_project: libpod-218412
zone: "us-central1-f"
cpu: 2
memory: "4Gb"
# Required to be 200gig, do not modify - has i/o performance impact
# according to gcloud CLI tool warning messages.
disk: 200
image_name: ${FEDORA_CACHE_IMAGE_NAME}
matrix:
- name: "Skopeo Test" # N/B: Name ref. by hack/get_fqin.sh
env:
BUILDTAGS: 'btrfs_noversion libdm_no_deferred_remove'
- name: "Skopeo Test w/ opengpg"
env:
BUILDTAGS: *withopengpg
setup_script: >-
"${GOSRC}/${SCRIPT_BASE}/runner.sh" setup
vendor_script: >-
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" vendor
build_script: >-
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" build
unit_script: >-
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" unit
integration_script: >-
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" integration
system_script: >
"${SKOPEO_PATH}/${SCRIPT_BASE}/runner.sh" system
image_build_task: &image-build
name: "Build multi-arch $CTXDIR"
alias: image_build
# Some of these container images take > 1h to build, limit
# this task to a specific Cirrus-Cron entry with this name.
only_if: $CIRRUS_CRON == 'multiarch'
timeout_in: 120m # emulation is sssllllooooowwww
gce_instance:
<<: *standardvm
image_name: build-push-${IMAGE_SUFFIX}
# More muscle required for parallel multi-arch build
type: "n2-standard-4"
matrix:
- env:
CTXDIR: contrib/skopeoimage/upstream
- env:
CTXDIR: contrib/skopeoimage/testing
- env:
CTXDIR: contrib/skopeoimage/stable
env:
SKOPEO_USERNAME: ENCRYPTED[4195884d23b154553f2ddb26a63fc9fbca50ba77b3e447e4da685d8639ed9bc94b9a86a9c77272c8c80d32ead9ca48da]
SKOPEO_PASSWORD: ENCRYPTED[36e06f9befd17e5da2d60260edb9ef0d40e6312e2bba4cf881d383f1b8b5a18c8e5a553aea2fdebf39cebc6bd3b3f9de]
CONTAINERS_USERNAME: ENCRYPTED[dd722c734641f103b394a3a834d51ca5415347e378637cf98ee1f99e64aad2ec3dbd4664c0d94cb0e06b83d89e9bbe91]
CONTAINERS_PASSWORD: ENCRYPTED[d8b0fac87fe251cedd26c864ba800480f9e0570440b9eb264265b67411b253a626fb69d519e188e6c9a7f525860ddb26]
main_script:
- source /etc/automation_environment
- main.sh $CIRRUS_REPO_CLONE_URL $CTXDIR
test_image_build_task:
<<: *image-build
alias: test_image_build
# Allow this to run inside a PR w/ [CI:BUILD] only.
only_if: $CIRRUS_PR != '' && $CIRRUS_CHANGE_TITLE =~ '.*CI:BUILD.*'
# This takes a LONG time, only run when requested. N/B: Any task
# made to depend on this one will block FOREVER unless triggered.
# DO NOT ADD THIS TASK AS DEPENDENCY FOR `success_task`.
trigger_type: manual
# Overwrite all 'env', don't push anything, just do the build.
env:
DRYRUN: 1
# This task is critical. It updates the "last-used by" timestamp stored
# in metadata for all VM images. This mechanism functions in tandem with
# an out-of-band pruning operation to remove disused VM images.
meta_task:
name: "VM img. keepalive"
alias: meta
container: &smallcontainer
cpu: 2
memory: 2
image: quay.io/libpod/imgts:latest
env:
# Space-separated list of images used by this repository state
IMGNAMES: |
${FEDORA_CACHE_IMAGE_NAME}
build-push-${IMAGE_SUFFIX}
BUILDID: "${CIRRUS_BUILD_ID}"
REPOREF: "${CIRRUS_REPO_NAME}"
GCPJSON: ENCRYPTED[6867b5a83e960e7c159a98fe6c8360064567a071c6f4b5e7d532283ecd870aa65c94ccd74bdaa9bf7aadac9d42e20a67]
GCPNAME: ENCRYPTED[1cf558ae125e3c39ec401e443ad76452b25d790c45eb73d77c83eb059a0f7fd5085ef7e2f7e410b04ea6e83b0aab2eb1]
GCPPROJECT: libpod-218412
clone_script: &noop mkdir -p "$CIRRUS_WORKING_DIR"
script: /usr/local/bin/entrypoint.sh
# Status aggregator for all tests. This task simply ensures a defined
# set of tasks all passed, and allows confirming that based on the status
# of this task.
success_task:
name: "Total Success"
alias: success
# N/B: ALL tasks must be listed here, minus their '_task' suffix.
depends_on:
- validate
- doccheck
- osx
- cross
- proxy_ostree_ext
- test_skopeo
- image_build
- meta
container: *smallcontainer
env:
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
TEST_ENVIRON: container
clone_script: *noop
script: /bin/true

74
.github/renovate.json5 vendored Normal file
View File

@@ -0,0 +1,74 @@
/*
Renovate is a service similar to GitHub Dependabot, but with
(fantastically) more configuration options. So many options
in fact, if you're new I recommend glossing over this cheat-sheet
prior to the official documentation:
https://www.augmentedmind.de/2021/07/25/renovate-bot-cheat-sheet
Configuration Update/Change Procedure:
1. Make changes
2. Manually validate changes (from repo-root):
podman run -it \
-v ./.github/renovate.json5:/usr/src/app/renovate.json5:z \
docker.io/renovate/renovate:latest \
renovate-config-validator
3. Commit.
Configuration Reference:
https://docs.renovatebot.com/configuration-options/
Monitoring Dashboard:
https://app.renovatebot.com/dashboard#github/containers
Note: The Renovate bot will create/manage it's business on
branches named 'renovate/*'. Otherwise, and by
default, the only the copy of this file that matters
is the one on the `main` branch. No other branches
will be monitored or touched in any way.
*/
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
/*************************************************
****** Global/general configuration options *****
*************************************************/
// Re-use predefined sets of configuration options to DRY
"extends": [
// https://github.com/containers/automation/blob/main/renovate/defaults.json5
"github>containers/automation//renovate/defaults.json5"
],
// Permit automatic rebasing when base-branch changes by more than
// one commit.
"rebaseWhen": "behind-base-branch",
/*************************************************
*** Repository-specific configuration options ***
*************************************************/
// Don't leave dep. update. PRs "hanging", assign them to people.
"assignees": ["containers/image-maintainers"], // same for skopeo
/*************************************************
***** Golang-specific configuration options *****
*************************************************/
"golang": {
// N/B: LAST MATCHING RULE WINS
// https://docs.renovatebot.com/configuration-options/#packagerules
"packageRules": [
// Package version retraction (https://go.dev/ref/mod#go-mod-file-retract)
// is broken in Renovate
// ref: https://github.com/renovatebot/renovate/issues/13012
{
"matchPackageNames": ["github.com/containers/common"],
// Both v1.0.0 and v1.0.1 should be ignored.
"allowedVersions": "!/v((1.0.0)|(1.0.1))$/"
},
],
},
}

20
.github/workflows/check_cirrus_cron.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
---
# See also:
# https://github.com/containers/podman/blob/main/.github/workflows/check_cirrus_cron.yml
on:
# Note: This only applies to the default branch.
schedule:
# N/B: This should correspond to a period slightly after
# the last job finishes running. See job defs. at:
# https://cirrus-ci.com/settings/repository/6706677464432640
- cron: '03 03 * * 1-5'
# Debug: Allow triggering job manually in github-actions WebUI
workflow_dispatch: {}
jobs:
# Ref: https://docs.github.com/en/actions/using-workflows/reusing-workflows
call_cron_failures:
uses: containers/podman/.github/workflows/check_cirrus_cron.yml@main
secrets: inherit

View File

@@ -1,209 +0,0 @@
---
# Please see contrib/<reponame>image/README.md for details on the intentions
# of this workflow.
#
# BIG FAT WARNING: This workflow is duplicated across containers/skopeo,
# containers/buildah, and containers/podman. ANY AND
# ALL CHANGES MADE HERE MUST BE MANUALLY DUPLICATED
# TO THE OTHER REPOS.
name: build multi-arch images
on:
# Upstream tends to be very active, with many merges per day.
# Only run this daily via cron schedule, or manually, not by branch push.
schedule:
- cron: '0 8 * * *'
# allows to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
multi:
name: multi-arch image build
env:
REPONAME: skopeo # No easy way to parse this out of $GITHUB_REPOSITORY
# Server/namespace value used to format FQIN
REPONAME_QUAY_REGISTRY: quay.io/skopeo
CONTAINERS_QUAY_REGISTRY: quay.io/containers
# list of architectures for build
PLATFORMS: linux/amd64,linux/s390x,linux/ppc64le,linux/arm64
# Command to execute in container to obtain project version number
VERSION_CMD: "--version" # skopeo is the entrypoint
# build several images (upstream, testing, stable) in parallel
strategy:
# By default, failure of one matrix item cancels all others
fail-fast: false
matrix:
# Builds are located under contrib/<reponame>image/<source> directory
source:
- upstream
- testing
- stable
runs-on: ubuntu-latest
# internal registry caches build for inspection before push
services:
registry:
image: quay.io/libpod/registry:2
ports:
- 5000:5000
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
driver-opts: network=host
install: true
- name: Build and locally push image
uses: docker/build-push-action@v2
with:
context: contrib/${{ env.REPONAME }}image/${{ matrix.source }}
file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile
platforms: ${{ env.PLATFORMS }}
push: true
tags: localhost:5000/${{ env.REPONAME }}/${{ matrix.source }}
# Simple verification that stable images work, and
# also grab version number use in forming the FQIN.
- name: amd64 container sniff test
if: matrix.source == 'stable'
id: sniff_test
run: |
podman pull --tls-verify=false \
localhost:5000/$REPONAME/${{ matrix.source }}
VERSION_OUTPUT=$(podman run \
localhost:5000/$REPONAME/${{ matrix.source }} \
$VERSION_CMD)
echo "$VERSION_OUTPUT"
VERSION=$(awk -r -e "/^${REPONAME} version /"'{print $3}' <<<"$VERSION_OUTPUT")
test -n "$VERSION"
echo "::set-output name=version::$VERSION"
- name: Generate image FQIN(s) to push
id: reponame_reg
run: |
if [[ "${{ matrix.source }}" == 'stable' ]]; then
# The command version in image just built
VERSION='v${{ steps.sniff_test.outputs.version }}'
# workaround vim syntax-highlight bug: '
# Push both new|updated version-tag and latest-tag FQINs
FQIN="$REPONAME_QUAY_REGISTRY/stable:$VERSION,$REPONAME_QUAY_REGISTRY/stable:latest"
elif [[ "${{ matrix.source }}" == 'testing' ]]; then
# Assume some contents changed, always push latest testing.
FQIN="$REPONAME_QUAY_REGISTRY/testing:latest"
elif [[ "${{ matrix.source }}" == 'upstream' ]]; then
# Assume some contents changed, always push latest upstream.
FQIN="$REPONAME_QUAY_REGISTRY/upstream:latest"
else
echo "::error::Unknown matrix item '${{ matrix.source }}'"
exit 1
fi
echo "::warning::Pushing $FQIN"
echo "::set-output name=fqin::${FQIN}"
echo '::set-output name=push::true'
# This is substantially similar to the above logic,
# but only handles $CONTAINERS_QUAY_REGISTRY for
# the stable "latest" and named-version tagged images.
- name: Generate containers reg. image FQIN(s)
if: matrix.source == 'stable'
id: containers_reg
run: |
VERSION='v${{ steps.sniff_test.outputs.version }}'
# workaround vim syntax-highlight bug: '
# Push both new|updated version-tag and latest-tag FQINs
FQIN="$CONTAINERS_QUAY_REGISTRY/$REPONAME:$VERSION,$CONTAINERS_QUAY_REGISTRY/$REPONAME:latest"
echo "::warning::Pushing $FQIN"
echo "::set-output name=fqin::${FQIN}"
echo '::set-output name=push::true'
- name: Define LABELS multi-line env. var. value
run: |
# This is a really hacky/strange workflow idiom, required
# for setting multi-line $LABELS value for consumption in
# a future step. There is literally no cleaner way to do this :<
# https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#multiline-strings
function set_labels() {
echo 'LABELS<<DELIMITER' >> "$GITHUB_ENV"
for line; do
echo "$line" | tee -a "$GITHUB_ENV"
done
echo "DELIMITER" >> "$GITHUB_ENV"
}
declare -a lines
lines=(\
"org.opencontainers.image.source=https://github.com/${GITHUB_REPOSITORY}.git"
"org.opencontainers.image.revision=${GITHUB_SHA}"
"org.opencontainers.image.created=$(date -u --iso-8601=seconds)"
)
# Only the 'stable' matrix source obtains $VERSION
if [[ "${{ matrix.source }}" == "stable" ]]; then
lines+=(\
"org.opencontainers.image.version=${{ steps.sniff_test.outputs.version }}"
)
fi
set_labels "${lines[@]}"
# Separate steps to login and push for $REPONAME_QUAY_REGISTRY and
# $CONTAINERS_QUAY_REGISTRY are required, because 2 sets of credentials
# are used and namespaced within the registry. At the same time, reuse
# of non-shell steps is not supported by Github Actions nor are YAML
# anchors/aliases, nor composite actions.
# Push to $REPONAME_QUAY_REGISTRY for stable, testing. and upstream
- name: Login to ${{ env.REPONAME_QUAY_REGISTRY }}
uses: docker/login-action@v1
if: steps.reponame_reg.outputs.push == 'true'
with:
registry: ${{ env.REPONAME_QUAY_REGISTRY }}
# N/B: Secrets are not passed to workflows that are triggered
# by a pull request from a fork
username: ${{ secrets.REPONAME_QUAY_USERNAME }}
password: ${{ secrets.REPONAME_QUAY_PASSWORD }}
- name: Push images to ${{ steps.reponame_reg.outputs.fqin }}
uses: docker/build-push-action@v2
if: steps.reponame_reg.outputs.push == 'true'
with:
cache-from: type=registry,ref=localhost:5000/${{ env.REPONAME }}/${{ matrix.source }}
cache-to: type=inline
context: contrib/${{ env.REPONAME }}image/${{ matrix.source }}
file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile
platforms: ${{ env.PLATFORMS }}
push: true
tags: ${{ steps.reponame_reg.outputs.fqin }}
labels: |
${{ env.LABELS }}
# Push to $CONTAINERS_QUAY_REGISTRY only stable
- name: Login to ${{ env.CONTAINERS_QUAY_REGISTRY }}
if: steps.containers_reg.outputs.push == 'true'
uses: docker/login-action@v1
with:
registry: ${{ env.CONTAINERS_QUAY_REGISTRY}}
username: ${{ secrets.CONTAINERS_QUAY_USERNAME }}
password: ${{ secrets.CONTAINERS_QUAY_PASSWORD }}
- name: Push images to ${{ steps.containers_reg.outputs.fqin }}
if: steps.containers_reg.outputs.push == 'true'
uses: docker/build-push-action@v2
with:
cache-from: type=registry,ref=localhost:5000/${{ env.REPONAME }}/${{ matrix.source }}
cache-to: type=inline
context: contrib/${{ env.REPONAME }}image/${{ matrix.source }}
file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile
platforms: ${{ env.PLATFORMS }}
push: true
tags: ${{ steps.containers_reg.outputs.fqin }}
labels: |
${{ env.LABELS }}

19
.github/workflows/rerun_cirrus_cron.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
---
# See also: https://github.com/containers/podman/blob/main/.github/workflows/rerun_cirrus_cron.yml
on:
# Note: This only applies to the default branch.
schedule:
# N/B: This should correspond to a period slightly after
# the last job finishes running. See job defs. at:
# https://cirrus-ci.com/settings/repository/6706677464432640
- cron: '01 01 * * 1-5'
# Debug: Allow triggering job manually in github-actions WebUI
workflow_dispatch: {}
jobs:
# Ref: https://docs.github.com/en/actions/using-workflows/reusing-workflows
call_cron_rerun:
uses: containers/podman/.github/workflows/rerun_cirrus_cron.yml@main
secrets: inherit

View File

@@ -7,13 +7,17 @@ on:
schedule:
- cron: "0 0 * * *"
permissions:
contents: read
jobs:
stale:
permissions:
issues: write # for actions/stale to close stale issues
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v1
- uses: actions/stale@v7
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'A friendly reminder that this issue had no activity for 30 days.'

2
.gitignore vendored
View File

@@ -2,7 +2,7 @@
/layers-*
/skopeo
result
/completions/
# ignore JetBrains IDEs (GoLand) config folder
.idea

View File

@@ -149,7 +149,7 @@ When new PRs for [containers/image](https://github.com/containers/image) break `
## Communications
For general questions, or discussions, please use the
IRC group on `irc.freenode.net` called `container-projects`
IRC channel on `irc.libera.chat` called `#container-projects`
that has been setup.
For discussions around issues/bugs and features, you can use the github

View File

@@ -1,56 +0,0 @@
FROM registry.fedoraproject.org/fedora:latest
RUN dnf -y update && dnf install -y make git golang golang-github-cpuguy83-md2man \
# storage deps
btrfs-progs-devel \
device-mapper-devel \
# gpgme bindings deps
libassuan-devel gpgme-devel \
gnupg \
# htpasswd for system tests
httpd-tools \
# OpenShift deps
which tar wget hostname util-linux bsdtar socat ethtool device-mapper iptables tree findutils nmap-ncat e2fsprogs xfsprogs lsof docker iproute \
bats jq podman runc \
golint \
openssl \
&& dnf clean all
# Install two versions of the registry. The first is an older version that
# only supports schema1 manifests. The second is a newer version that supports
# both. This allows integration-cli tests to cover push/pull with both schema1
# and schema2 manifests.
RUN set -x \
&& export GO111MODULE=off \
&& REGISTRY_COMMIT_SCHEMA1=ec87e9b6971d831f0eff752ddb54fb64693e51cd \
&& REGISTRY_COMMIT=47a064d4195a9b56133891bbb13620c3ac83a827 \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/docker/distribution.git "$GOPATH/src/github.com/docker/distribution" \
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT") \
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
go build -o /usr/local/bin/registry-v2 github.com/docker/distribution/cmd/registry \
&& (cd "$GOPATH/src/github.com/docker/distribution" && git checkout -q "$REGISTRY_COMMIT_SCHEMA1") \
&& GOPATH="$GOPATH/src/github.com/docker/distribution/Godeps/_workspace:$GOPATH" \
go build -o /usr/local/bin/registry-v2-schema1 github.com/docker/distribution/cmd/registry \
&& rm -rf "$GOPATH"
RUN set -x \
&& export GO111MODULE=off \
&& export GOPATH=$(mktemp -d) \
&& git clone --depth 1 -b v1.5.0-alpha.3 git://github.com/openshift/origin "$GOPATH/src/github.com/openshift/origin" \
# The sed edits out a "go < 1.5" check which works incorrectly with go ≥ 1.10. \
&& sed -i -e 's/\[\[ "\${go_version\[2]}" < "go1.5" ]]/false/' "$GOPATH/src/github.com/openshift/origin/hack/common.sh" \
&& (cd "$GOPATH/src/github.com/openshift/origin" && make clean build && make all WHAT=cmd/dockerregistry) \
&& cp -a "$GOPATH/src/github.com/openshift/origin/_output/local/bin/linux"/*/* /usr/local/bin \
&& cp "$GOPATH/src/github.com/openshift/origin/images/dockerregistry/config.yml" /atomic-registry-config.yml \
&& rm -rf "$GOPATH" \
&& mkdir /registry
ENV GOPATH /usr/share/gocode:/go
ENV PATH $GOPATH/bin:/usr/share/gocode/bin:$PATH
ENV container_magic 85531765-346b-4316-bdb8-358e4cca9e5d
RUN go version
WORKDIR /go/src/github.com/containers/skopeo
COPY . /go/src/github.com/containers/skopeo
#ENTRYPOINT ["hack/dind"]

View File

@@ -1,12 +0,0 @@
FROM registry.fedoraproject.org/fedora:33
RUN dnf update -y && \
dnf install -y \
btrfs-progs-devel \
device-mapper-devel \
golang \
gpgme-devel \
make
ENV GOPATH=/
WORKDIR /src/github.com/containers/skopeo

186
Makefile
View File

@@ -1,24 +1,23 @@
.PHONY: all binary build-container docs docs-in-container build-local clean install install-binary install-completions shell test-integration .install.vndr vendor vendor-in-container
.PHONY: all binary docs docs-in-container build-local clean install install-binary install-completions shell test-integration .install.vndr vendor vendor-in-container
export GOPROXY=https://proxy.golang.org
# On some plaforms (eg. macOS, FreeBSD) gpgme is installed in /usr/local/ but /usr/local/include/ is
# not in the default search path. Rather than hard-code this directory, use gpgme-config.
# Sadly that must be done at the top-level user instead of locally in the gpgme subpackage, because cgo
# supports only pkg-config, not general shell scripts, and gpgme does not install a pkg-config file.
# If gpgme is not installed or gpgme-config cant be found for other reasons, the error is silently ignored
# (and the user will probably find out because the cgo compilation will fail).
GPGME_ENV := CGO_CFLAGS="$(shell gpgme-config --cflags 2>/dev/null)" CGO_LDFLAGS="$(shell gpgme-config --libs 2>/dev/null)"
# The following variables very roughly follow https://www.gnu.org/prep/standards/standards.html#Makefile-Conventions .
DESTDIR ?=
PREFIX ?= /usr/local
ifeq ($(shell uname -s),FreeBSD)
CONTAINERSCONFDIR ?= /usr/local/etc/containers
else
CONTAINERSCONFDIR ?= /etc/containers
endif
REGISTRIESDDIR ?= ${CONTAINERSCONFDIR}/registries.d
SIGSTOREDIR ?= /var/lib/containers/sigstore
LOOKASIDEDIR ?= /var/lib/containers/sigstore
BINDIR ?= ${PREFIX}/bin
MANDIR ?= ${PREFIX}/share/man
BASHCOMPLETIONSDIR ?= ${PREFIX}/share/bash-completion/completions
BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
ZSHINSTALLDIR=${PREFIX}/share/zsh/site-functions
FISHINSTALLDIR=${PREFIX}/share/fish/vendor_completions.d
GO ?= go
GOBIN := $(shell $(GO) env GOBIN)
@@ -29,12 +28,15 @@ ifeq ($(GOBIN),)
GOBIN := $(GOPATH)/bin
endif
# Required for integration-tests to detect they are running inside a specific
# container image. Env. var defined in image, make does not automatically
# pass to children unless explicitly exported
export container_magic
CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null || echo docker)
GOMD2MAN ?= $(shell command -v go-md2man || echo '$(GOBIN)/go-md2man')
# Scripts may also use CONTAINER_RUNTIME, so we need to export it.
# Note possibly non-obvious aspects of this:
# - We need to use 'command -v' here, not 'which', for compatibility with MacOS.
# - GNU Make 4.2.1 (included in Ubuntu 20.04) incorrectly tries to avoid invoking
# a shell, and fails because there is no /usr/bin/command. The trailing ';' in
# $(shell … ;) defeats that heuristic (recommended in
# https://savannah.gnu.org/bugs/index.php?57625 ).
export CONTAINER_RUNTIME ?= $(if $(shell command -v podman ;),podman,docker)
GOMD2MAN ?= $(if $(shell command -v go-md2man ;),go-md2man,$(GOBIN)/go-md2man)
# Go module support: set `-mod=vendor` to use the vendored sources.
# See also hack/make.sh.
@@ -53,10 +55,31 @@ ifeq ($(GOOS), linux)
endif
endif
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
IMAGE := skopeo-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
# set env like gobuildtag?
CONTAINER_CMD := ${CONTAINER_RUNTIME} run --rm -i -e TESTFLAGS="$(TESTFLAGS)" #$(CONTAINER_ENVS)
# If $TESTFLAGS is set, it is passed as extra arguments to 'go test'.
# You can increase test output verbosity with the option '-test.vv'.
# You can select certain tests to run, with `-test.run <regex>` for example:
#
# make test-unit TESTFLAGS='-test.run ^TestManifestDigest$'
#
# For integration test, we use [gocheck](https://labix.org/gocheck).
# You can increase test output verbosity with the option '-check.vv'.
# You can limit test selection with `-check.f <regex>`, for example:
#
# make test-integration TESTFLAGS='-check.f CopySuite.TestCopy.*'
export TESTFLAGS ?= -v -check.v -test.timeout=15m
# This is assumed to be set non-empty when operating inside a CI/automation environment
CI ?=
# This env. var. is interpreted by some tests as a permission to
# modify local configuration files and services.
export SKOPEO_CONTAINER_TESTS ?= $(if $(CI),1,0)
# This is a compromise, we either use a container for this or require
# the local user to have a compatible python3 development environment.
# Define it as a "resolve on use" variable to avoid calling out when possible
SKOPEO_CIDEV_CONTAINER_FQIN ?= $(shell hack/get_fqin.sh)
CONTAINER_CMD ?= ${CONTAINER_RUNTIME} run --rm -i -e TESTFLAGS="$(TESTFLAGS)" -e CI=$(CI) -e SKOPEO_CONTAINER_TESTS=1
# if this session isn't interactive, then we don't want to allocate a
# TTY, which would fail, but if it is interactive, we do want to attach
# so that the user can send e.g. ^C through.
@@ -64,9 +87,10 @@ INTERACTIVE := $(shell [ -t 0 ] && echo 1 || echo 0)
ifeq ($(INTERACTIVE), 1)
CONTAINER_CMD += -t
endif
CONTAINER_RUN := $(CONTAINER_CMD) "$(IMAGE)"
CONTAINER_GOSRC = /src/github.com/containers/skopeo
CONTAINER_RUN ?= $(CONTAINER_CMD) --security-opt label=disable -v $(CURDIR):$(CONTAINER_GOSRC) -w $(CONTAINER_GOSRC) $(SKOPEO_CIDEV_CONTAINER_FQIN)
GIT_COMMIT := $(shell git rev-parse HEAD 2> /dev/null || true)
GIT_COMMIT := $(shell GIT_CEILING_DIRECTORIES=$$(cd ..; pwd) git rev-parse HEAD 2> /dev/null || true)
EXTRA_LDFLAGS ?=
SKOPEO_LDFLAGS := -ldflags '-X main.gitCommit=${GIT_COMMIT} $(EXTRA_LDFLAGS)'
@@ -76,7 +100,8 @@ MANPAGES ?= $(MANPAGES_MD:%.md=%)
BTRFS_BUILD_TAG = $(shell hack/btrfs_tag.sh) $(shell hack/btrfs_installed_tag.sh)
LIBDM_BUILD_TAG = $(shell hack/libdm_tag.sh)
LOCAL_BUILD_TAGS = $(BTRFS_BUILD_TAG) $(LIBDM_BUILD_TAG)
LIBSUBID_BUILD_TAG = $(shell hack/libsubid_tag.sh)
LOCAL_BUILD_TAGS = $(BTRFS_BUILD_TAG) $(LIBDM_BUILD_TAG) $(LIBSUBID_BUILD_TAG)
BUILDTAGS += $(LOCAL_BUILD_TAGS)
ifeq ($(DISABLE_CGO), 1)
@@ -89,6 +114,9 @@ endif
# use source debugging tools like delve.
all: bin/skopeo docs
codespell:
codespell -S Makefile,build,buildah,buildah.spec,imgtype,copy,AUTHORS,bin,vendor,.git,go.sum,CHANGELOG.md,changelog.txt,seccomp.json,.cirrus.yml,"*.xz,*.gz,*.tar,*.tgz,*ico,*.png,*.1,*.5,*.orig,*.rej" -L fpr,uint,iff,od,ERRO -w
help:
@echo "Usage: make <target>"
@echo
@@ -96,7 +124,6 @@ help:
@echo
@echo " * 'install' - Install binaries and documents to system locations"
@echo " * 'binary' - Build skopeo with a container"
@echo " * 'static' - Build statically linked binary"
@echo " * 'bin/skopeo' - Build skopeo locally"
@echo " * 'test-unit' - Execute unit tests"
@echo " * 'test-integration' - Execute integration tests"
@@ -105,40 +132,18 @@ help:
@echo " * 'shell' - Run the built image and attach to a shell"
@echo " * 'clean' - Clean artifacts"
# Build a container image (skopeobuild) that has everything we need to build.
# Then do the build and the output (skopeo) should appear in current dir
# Do the build and the output (skopeo) should appear in current dir
binary: cmd/skopeo
${CONTAINER_RUNTIME} build ${BUILD_ARGS} -f Dockerfile.build -t skopeobuildimage .
${CONTAINER_RUNTIME} run --rm --security-opt label=disable -v $$(pwd):/src/github.com/containers/skopeo \
skopeobuildimage make bin/skopeo $(if $(DEBUG),DEBUG=$(DEBUG)) BUILDTAGS='$(BUILDTAGS)'
# Update nix/nixpkgs.json its latest stable commit
.PHONY: nixpkgs
nixpkgs:
@nix run \
-f channel:nixos-21.05 nix-prefetch-git \
-c nix-prefetch-git \
--no-deepClone \
https://github.com/nixos/nixpkgs refs/heads/nixos-21.05 > nix/nixpkgs.json
# Build statically linked binary
.PHONY: static
static:
@nix build -f nix/
mkdir -p ./bin
cp -rfp ./result/bin/* ./bin/
$(CONTAINER_RUN) make bin/skopeo $(if $(DEBUG),DEBUG=$(DEBUG)) BUILDTAGS='$(BUILDTAGS)'
# Build w/o using containers
.PHONY: bin/skopeo
bin/skopeo:
$(GPGME_ENV) $(GO) build $(MOD_VENDOR) ${GO_DYN_FLAGS} ${SKOPEO_LDFLAGS} -gcflags "$(GOGCFLAGS)" -tags "$(BUILDTAGS)" -o $@ ./cmd/skopeo
$(GO) build $(MOD_VENDOR) ${GO_DYN_FLAGS} ${SKOPEO_LDFLAGS} -gcflags "$(GOGCFLAGS)" -tags "$(BUILDTAGS)" -o $@ ./cmd/skopeo
bin/skopeo.%:
GOOS=$(word 2,$(subst ., ,$@)) GOARCH=$(word 3,$(subst ., ,$@)) $(GO) build $(MOD_VENDOR) ${SKOPEO_LDFLAGS} -tags "containers_image_openpgp $(BUILDTAGS)" -o $@ ./cmd/skopeo
local-cross: bin/skopeo.darwin.amd64 bin/skopeo.linux.arm bin/skopeo.linux.arm64 bin/skopeo.windows.386.exe bin/skopeo.windows.amd64.exe
build-container:
${CONTAINER_RUNTIME} build ${BUILD_ARGS} -t "$(IMAGE)" .
$(MANPAGES): %: %.md
ifneq ($(DISABLE_DOCS), 1)
sed -e 's/\((skopeo.*\.md)\)//' -e 's/\[\(skopeo.*\)\]/\1/' $< | $(GOMD2MAN) -in /dev/stdin -out $@
@@ -147,15 +152,21 @@ endif
docs: $(MANPAGES)
docs-in-container:
${CONTAINER_RUNTIME} build ${BUILD_ARGS} -f Dockerfile.build -t skopeobuildimage .
${CONTAINER_RUNTIME} run --rm --security-opt label=disable -v $$(pwd):/src/github.com/containers/skopeo \
skopeobuildimage make docs $(if $(DEBUG),DEBUG=$(DEBUG)) BUILDTAGS='$(BUILDTAGS)'
${CONTAINER_RUN} $(MAKE) docs $(if $(DEBUG),DEBUG=$(DEBUG))
.PHONY: completions
completions: bin/skopeo
install -d -m 755 completions/bash completions/zsh completions/fish completions/powershell
./bin/skopeo completion bash >| completions/bash/skopeo
./bin/skopeo completion zsh >| completions/zsh/_skopeo
./bin/skopeo completion fish >| completions/fish/skopeo.fish
./bin/skopeo completion powershell >| completions/powershell/skopeo.ps1
clean:
rm -rf bin docs/*.1
rm -rf bin docs/*.1 completions/
install: install-binary install-docs install-completions
install -d -m 755 ${DESTDIR}${SIGSTOREDIR}
install -d -m 755 ${DESTDIR}${LOOKASIDEDIR}
install -d -m 755 ${DESTDIR}${CONTAINERSCONFDIR}
install -m 644 default-policy.json ${DESTDIR}${CONTAINERSCONFDIR}/policy.json
install -d -m 755 ${DESTDIR}${REGISTRIESDDIR}
@@ -171,51 +182,61 @@ ifneq ($(DISABLE_DOCS), 1)
install -m 644 docs/*.1 ${DESTDIR}${MANDIR}/man1
endif
install-completions:
install -m 755 -d ${DESTDIR}${BASHCOMPLETIONSDIR}
install -m 644 completions/bash/skopeo ${DESTDIR}${BASHCOMPLETIONSDIR}/skopeo
install-completions: completions
install -d -m 755 ${DESTDIR}${BASHINSTALLDIR}
install -m 644 completions/bash/skopeo ${DESTDIR}${BASHINSTALLDIR}
install -d -m 755 ${DESTDIR}${ZSHINSTALLDIR}
install -m 644 completions/zsh/_skopeo ${DESTDIR}${ZSHINSTALLDIR}
install -d -m 755 ${DESTDIR}${FISHINSTALLDIR}
install -m 644 completions/fish/skopeo.fish ${DESTDIR}${FISHINSTALLDIR}
# There is no common location for powershell files so do not install them. Users have to source the file from their powershell profile.
shell: build-container
shell:
$(CONTAINER_RUN) bash
check: validate test-unit test-integration test-system
# The tests can run out of entropy and block in containers, so replace /dev/random.
test-integration: build-container
$(CONTAINER_RUN) bash -c 'rm -f /dev/random; ln -sf /dev/urandom /dev/random; SKOPEO_CONTAINER_TESTS=1 BUILDTAGS="$(BUILDTAGS)" $(MAKE) test-integration-local'
test-integration:
# This is intended to be equal to $(CONTAINER_RUN), but with --cap-add=cap_mknod.
# --cap-add=cap_mknod is important to allow skopeo to use containers-storage: directly as it exists in the callers environment, without
# creating a nested user namespace (which requires /etc/subuid and /etc/subgid to be set up)
$(CONTAINER_CMD) --security-opt label=disable --cap-add=cap_mknod -v $(CURDIR):$(CONTAINER_GOSRC) -w $(CONTAINER_GOSRC) $(SKOPEO_CIDEV_CONTAINER_FQIN) \
$(MAKE) test-integration-local
# Intended for CI, shortcut 'build-container' since already running inside container.
test-integration-local:
# Intended for CI, assumed to be running in quay.io/libpod/skopeo_cidev container.
test-integration-local: bin/skopeo
hack/make.sh test-integration
# complicated set of options needed to run podman-in-podman
test-system: build-container
test-system:
DTEMP=$(shell mktemp -d --tmpdir=/var/tmp podman-tmp.XXXXXX); \
$(CONTAINER_CMD) --privileged \
-v $$DTEMP:/var/lib/containers:Z -v /run/systemd/journal/socket:/run/systemd/journal/socket \
"$(IMAGE)" \
bash -c 'BUILDTAGS="$(BUILDTAGS)" $(MAKE) test-system-local'; \
-v $(CURDIR):$(CONTAINER_GOSRC) -w $(CONTAINER_GOSRC) \
-v $$DTEMP:/var/lib/containers:Z -v /run/systemd/journal/socket:/run/systemd/journal/socket \
"$(SKOPEO_CIDEV_CONTAINER_FQIN)" \
$(MAKE) test-system-local; \
rc=$$?; \
$(RM) -rf $$DTEMP; \
$(CONTAINER_RUNTIME) unshare rm -rf $$DTEMP; # This probably doesn't work with Docker, oh well, better than nothing... \
exit $$rc
# Intended for CI, shortcut 'build-container' since already running inside container.
test-system-local:
# Intended for CI, assumed to already be running in quay.io/libpod/skopeo_cidev container.
test-system-local: bin/skopeo
hack/make.sh test-system
test-unit: build-container
test-unit:
# Just call (make test unit-local) here instead of worrying about environment differences
$(CONTAINER_RUN) make test-unit-local BUILDTAGS='$(BUILDTAGS)'
$(CONTAINER_RUN) $(MAKE) test-unit-local
validate: build-container
$(CONTAINER_RUN) make validate-local
validate:
$(CONTAINER_RUN) $(MAKE) validate-local
# This target is only intended for development, e.g. executing it from an IDE. Use (make test) for CI or pre-release testing.
test-all-local: validate-local validate-docs test-unit-local
.PHONY: validate-local
validate-local:
hack/make.sh validate-git-marks validate-gofmt validate-lint validate-vet
BUILDTAGS="${BUILDTAGS}" hack/make.sh validate-git-marks validate-gofmt validate-lint validate-vet
# This invokes bin/skopeo, hence cannot be run as part of validate-local
.PHONY: validate-docs
@@ -223,13 +244,18 @@ validate-docs:
hack/man-page-checker
hack/xref-helpmsgs-manpages
test-unit-local:
$(GPGME_ENV) $(GO) test $(MOD_VENDOR) -tags "$(BUILDTAGS)" $$($(GO) list $(MOD_VENDOR) -tags "$(BUILDTAGS)" -e ./... | grep -v '^github\.com/containers/skopeo/\(integration\|vendor/.*\)$$')
test-unit-local: bin/skopeo
$(GO) test $(MOD_VENDOR) -tags "$(BUILDTAGS)" $$($(GO) list $(MOD_VENDOR) -tags "$(BUILDTAGS)" -e ./... | grep -v '^github\.com/containers/skopeo/\(integration\|vendor/.*\)$$')
vendor:
$(GO) mod tidy
$(GO) mod tidy -compat=1.17
$(GO) mod vendor
$(GO) mod verify
vendor-in-container:
podman run --privileged --rm --env HOME=/root -v `pwd`:/src -w /src docker.io/library/golang:1.16 make vendor
podman run --privileged --rm --env HOME=/root -v $(CURDIR):/src -w /src golang $(MAKE) vendor
# CAUTION: This is not a replacement for RPMs provided by your distro.
# Only intended to build and test the latest unreleased changes.
rpm:
rpkg local

17
OWNERS Normal file
View File

@@ -0,0 +1,17 @@
approvers:
- mtrmac
- lsm5
- TomSweeneyRedHat
- rhatdan
- vrothberg
reviewers:
- ashley-cui
- giuseppe
- containers/image-maintainers
- lsm5
- mtrmac
- QiWang19
- rhatdan
- runcom
- TomSweeneyRedHat
- vrothberg

View File

@@ -1,7 +1,8 @@
skopeo [![Build Status](https://travis-ci.org/containers/skopeo.svg?branch=master)](https://travis-ci.org/containers/skopeo)
<!--- skopeo [![Build Status](https://travis-ci.org/containers/skopeo.svg?branch=main)](https://travis-ci.org/containers/skopeo)
=
--->
<img src="https://cdn.rawgit.com/containers/skopeo/master/docs/skopeo.svg" width="250">
<img src="https://cdn.rawgit.com/containers/skopeo/main/docs/skopeo.svg" width="250">
----
@@ -56,29 +57,37 @@ Examples:
$ skopeo inspect docker://registry.fedoraproject.org/fedora:latest
{
"Name": "registry.fedoraproject.org/fedora",
"Digest": "sha256:655721ff613ee766a4126cb5e0d5ae81598e1b0c3bcf7017c36c4d72cb092fe9",
"Digest": "sha256:0f65bee641e821f8118acafb44c2f8fe30c2fc6b9a2b3729c0660376391aa117",
"RepoTags": [
"24",
"25",
"26-modular",
...
"34-aarch64",
"34",
"latest",
...
],
"Created": "2020-04-29T06:48:16Z",
"Created": "2022-11-24T13:54:18Z",
"DockerVersion": "1.10.1",
"Labels": {
"license": "MIT",
"name": "fedora",
"vendor": "Fedora Project",
"version": "32"
"version": "37"
},
"Architecture": "amd64",
"Os": "linux",
"Layers": [
"sha256:3088721d7dbf674fc0be64cd3cf00c25aab921cacf35fa0e7b1578500a3e1653"
"sha256:2a0fc6bf62e155737f0ace6142ee686f3c471c1aab4241dc3128904db46288f0"
],
"LayersData": [
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:2a0fc6bf62e155737f0ace6142ee686f3c471c1aab4241dc3128904db46288f0",
"Size": 71355009,
"Annotations": null
}
],
"Env": [
"DISTTAG=f32container",
"FGC=f32",
"DISTTAG=f37container",
"FGC=f37",
"container=oci"
]
}
@@ -200,6 +209,7 @@ Please read the [contribution guide](CONTRIBUTING.md) if you want to collaborate
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------|
| [skopeo-copy(1)](/docs/skopeo-copy.1.md) | Copy an image (manifest, filesystem layers, signatures) from one location to another. |
| [skopeo-delete(1)](/docs/skopeo-delete.1.md) | Mark the image-name for later deletion by the registry's garbage collector. |
| [skopeo-generate-sigstore-key(1)](/docs/skopeo-generate-sigstore-key.1.md) | Generate a sigstore public/private key pair. |
| [skopeo-inspect(1)](/docs/skopeo-inspect.1.md) | Return low-level information about image-name in a registry. |
| [skopeo-list-tags(1)](/docs/skopeo-list-tags.1.md) | Return a list of tags for the transport-specific image repository. |
| [skopeo-login(1)](/docs/skopeo-login.1.md) | Login to a container registry. |
@@ -207,7 +217,7 @@ Please read the [contribution guide](CONTRIBUTING.md) if you want to collaborate
| [skopeo-manifest-digest(1)](/docs/skopeo-manifest-digest.1.md) | Compute a manifest digest for a manifest-file and write it to standard output. |
| [skopeo-standalone-sign(1)](/docs/skopeo-standalone-sign.1.md) | Debugging tool - Publish and sign an image in one step. |
| [skopeo-standalone-verify(1)](/docs/skopeo-standalone-verify.1.md)| Verify an image signature. |
| [skopeo-sync(1)](/docs/skopeo-sync.1.md) | Synchronize images between container registries and local directories. |
| [skopeo-sync(1)](/docs/skopeo-sync.1.md) | Synchronize images between registry repositories and local directories. |
License
-

View File

@@ -1,35 +0,0 @@
//go:build !containers_image_openpgp
// +build !containers_image_openpgp
package main
/*
This is a pretty horrible workaround. Due to a glibc bug
https://bugzilla.redhat.com/show_bug.cgi?id=1326903 , we must ensure we link
with -lgpgme before -lpthread. Such arguments come from various packages
using cgo, and the ordering of these arguments is, with current (go tool link),
dependent on the order in which the cgo-using packages are found in a
breadth-first search following dependencies, starting from “main”.
Thus, if
import "net"
is processed before
import "…/skopeo/signature"
it will, in the next level of the BFS, pull in "runtime/cgo" (a dependency of
"net") before "mtrmac/gpgme" (a dependency of "…/skopeo/signature"), causing
-lpthread (used by "runtime/cgo") to be used before -lgpgme.
This might be possible to work around by careful import ordering, or by removing
a direct dependency on "net", but that would be very fragile.
So, until the above bug is fixed, add -lgpgme directly in the "main" package
to ensure the needed build order.
Unfortunately, this workaround needs to be applied at the top level of any user
of "…/skopeo/signature"; it cannot be added to "…/skopeo/signature" itself,
by that time this package is first processed by the linker, a -lpthread may
already be queued and it would be too late.
*/
// #cgo LDFLAGS: -lgpgme
import "C"

16
cmd/skopeo/completions.go Normal file
View File

@@ -0,0 +1,16 @@
package main
import (
"github.com/containers/image/v5/transports"
"github.com/spf13/cobra"
)
// autocompleteSupportedTransports list all supported transports with the colon suffix.
func autocompleteSupportedTransports(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
tps := transports.ListNames()
suggestions := make([]string, 0, len(tps))
for _, tp := range tps {
suggestions = append(suggestions, tp+":")
}
return suggestions, cobra.ShellCompDirectiveNoFileComp
}

View File

@@ -4,13 +4,17 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
commonFlag "github.com/containers/common/pkg/flag"
"github.com/containers/common/pkg/retry"
"github.com/containers/image/v5/copy"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/cli"
"github.com/containers/image/v5/pkg/cli/sigstore"
"github.com/containers/image/v5/signature/signer"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
encconfig "github.com/containers/ocicrypt/config"
@@ -19,21 +23,27 @@ import (
)
type copyOptions struct {
global *globalOptions
deprecatedTLSVerify *deprecatedTLSVerifyOption
srcImage *imageOptions
destImage *imageDestOptions
retryOpts *retry.RetryOptions
additionalTags []string // For docker-archive: destinations, in addition to the name:tag specified as destination, also add these
removeSignatures bool // Do not copy signatures from the source image
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
digestFile string // Write digest to this file
format optionalString // Force conversion of the image to a specified format
quiet bool // Suppress output information when copying images
all bool // Copy all of the images if the source is a list
encryptLayer []int // The list of layers to encrypt
encryptionKeys []string // Keys needed to encrypt the image
decryptionKeys []string // Keys needed to decrypt the image
global *globalOptions
deprecatedTLSVerify *deprecatedTLSVerifyOption
srcImage *imageOptions
destImage *imageDestOptions
retryOpts *retry.Options
additionalTags []string // For docker-archive: destinations, in addition to the name:tag specified as destination, also add these
removeSignatures bool // Do not copy signatures from the source image
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
signBySigstoreParamFile string // Sign the image using a sigstore signature per configuration in a param file
signBySigstorePrivateKey string // Sign the image using a sigstore private key
signPassphraseFile string // Path pointing to a passphrase file when signing (for either signature format, but only one of them)
signIdentity string // Identity of the signed image, must be a fully specified docker reference
digestFile string // Write digest to this file
format commonFlag.OptionalString // Force conversion of the image to a specified format
quiet bool // Suppress output information when copying images
all bool // Copy all of the images if the source is a list
multiArch commonFlag.OptionalString // How to handle multi architecture images
preserveDigests bool // Preserve digests during copy
encryptLayer []int // The list of layers to encrypt
encryptionKeys []string // Keys needed to encrypt the image
decryptionKeys []string // Keys needed to decrypt the image
}
func copyCmd(global *globalOptions) *cobra.Command {
@@ -58,8 +68,9 @@ Supported transports:
See skopeo(1) section "IMAGE NAMES" for the expected format
`, strings.Join(transports.ListNames(), ", ")),
RunE: commandAction(opts.run),
Example: `skopeo copy docker://quay.io/skopeo/stable:latest docker://registry.example.com/skopeo:latest`,
RunE: commandAction(opts.run),
Example: `skopeo copy docker://quay.io/skopeo/stable:latest docker://registry.example.com/skopeo:latest`,
ValidArgsFunction: autocompleteSupportedTransports,
}
adjustUsage(cmd)
flags := cmd.Flags()
@@ -71,17 +82,44 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
flags.StringSliceVar(&opts.additionalTags, "additional-tag", []string{}, "additional tags (supports docker-archive)")
flags.BoolVarP(&opts.quiet, "quiet", "q", false, "Suppress output information when copying images")
flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list")
flags.Var(commonFlag.NewOptionalStringValue(&opts.multiArch), "multi-arch", `How to handle multi-architecture images (system, all, or index-only)`)
flags.BoolVar(&opts.preserveDigests, "preserve-digests", false, "Preserve digests of images and lists")
flags.BoolVar(&opts.removeSignatures, "remove-signatures", false, "Do not copy signatures from SOURCE-IMAGE")
flags.StringVar(&opts.signByFingerprint, "sign-by", "", "Sign the image using a GPG key with the specified `FINGERPRINT`")
flags.StringVar(&opts.signBySigstoreParamFile, "sign-by-sigstore", "", "Sign the image using a sigstore parameter file at `PATH`")
flags.StringVar(&opts.signBySigstorePrivateKey, "sign-by-sigstore-private-key", "", "Sign the image using a sigstore private key at `PATH`")
flags.StringVar(&opts.signPassphraseFile, "sign-passphrase-file", "", "Read a passphrase for signing an image from `PATH`")
flags.StringVar(&opts.signIdentity, "sign-identity", "", "Identity of signed image, must be a fully specified docker reference. Defaults to the target docker reference.")
flags.StringVar(&opts.digestFile, "digestfile", "", "Write the digest of the pushed image to the specified file")
flags.VarP(newOptionalStringValue(&opts.format), "format", "f", `MANIFEST TYPE (oci, v2s1, or v2s2) to use in the destination (default is manifest type of source, with fallbacks)`)
flags.VarP(commonFlag.NewOptionalStringValue(&opts.format), "format", "f", `MANIFEST TYPE (oci, v2s1, or v2s2) to use in the destination (default is manifest type of source, with fallbacks)`)
flags.StringSliceVar(&opts.encryptionKeys, "encryption-key", []string{}, "*Experimental* key with the encryption protocol to use needed to encrypt the image (e.g. jwe:/path/to/key.pem)")
flags.IntSliceVar(&opts.encryptLayer, "encrypt-layer", []int{}, "*Experimental* the 0-indexed layer indices, with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer)")
flags.StringSliceVar(&opts.decryptionKeys, "decryption-key", []string{}, "*Experimental* key needed to decrypt the image")
return cmd
}
func (opts *copyOptions) run(args []string, stdout io.Writer) error {
// parseMultiArch parses the list processing selection
// It returns the copy.ImageListSelection to use with image.Copy option
func parseMultiArch(multiArch string) (copy.ImageListSelection, error) {
switch multiArch {
case "system":
return copy.CopySystemImage, nil
case "all":
return copy.CopyAllImages, nil
// There is no CopyNoImages value in copy.ImageListSelection, but because we
// don't provide an option to select a set of images to copy, we can use
// CopySpecificImages.
case "index-only":
return copy.CopySpecificImages, nil
// We don't expose CopySpecificImages other than index-only above, because
// we currently don't provide an option to choose the images to copy. That
// could be added in the future.
default:
return copy.CopySystemImage, fmt.Errorf("unknown multi-arch option %q. Choose one of the supported options: 'system', 'all', or 'index-only'", multiArch)
}
}
func (opts *copyOptions) run(args []string, stdout io.Writer) (retErr error) {
if len(args) != 2 {
return errorShouldDisplayUsage{errors.New("Exactly two arguments expected")}
}
@@ -96,7 +134,11 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
if err != nil {
return fmt.Errorf("Error loading trust policy: %v", err)
}
defer policyContext.Destroy()
defer func() {
if err := policyContext.Destroy(); err != nil {
retErr = noteCloseFailure(retErr, "tearing down policy context", err)
}
}()
srcRef, err := alltransports.ParseImageName(imageNames[0])
if err != nil {
@@ -117,8 +159,8 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
}
var manifestType string
if opts.format.present {
manifestType, err = parseManifestFormat(opts.format.value)
if opts.format.Present() {
manifestType, err = parseManifestFormat(opts.format.Value())
if err != nil {
return err
}
@@ -142,7 +184,17 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
if opts.quiet {
stdout = nil
}
imageListSelection := copy.CopySystemImage
if opts.multiArch.Present() && opts.all {
return fmt.Errorf("Cannot use --all and --multi-arch flags together")
}
if opts.multiArch.Present() {
imageListSelection, err = parseMultiArch(opts.multiArch.Value())
if err != nil {
return err
}
}
if opts.all {
imageListSelection = copy.CopyAllImages
}
@@ -183,18 +235,71 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
decConfig = cc.DecryptConfig
}
return retry.RetryIfNecessary(ctx, func() error {
// c/image/copy.Image does allow creating both simple signing and sigstore signatures simultaneously,
// with independent passphrases, but that would make the CLI probably too confusing.
// For now, use the passphrase with either, but only one of them.
if opts.signPassphraseFile != "" && opts.signByFingerprint != "" && opts.signBySigstorePrivateKey != "" {
return fmt.Errorf("Only one of --sign-by and sign-by-sigstore-private-key can be used with sign-passphrase-file")
}
var passphrase string
if opts.signPassphraseFile != "" {
p, err := cli.ReadPassphraseFile(opts.signPassphraseFile)
if err != nil {
return err
}
passphrase = p
} else if opts.signBySigstorePrivateKey != "" {
p, err := promptForPassphrase(opts.signBySigstorePrivateKey, os.Stdin, os.Stdout)
if err != nil {
return err
}
passphrase = p
} // opts.signByFingerprint triggers a GPG-agent passphrase prompt, possibly using a more secure channel, so we usually shouldnt prompt ourselves if no passphrase was explicitly provided.
var signers []*signer.Signer
if opts.signBySigstoreParamFile != "" {
signer, err := sigstore.NewSignerFromParameterFile(opts.signBySigstoreParamFile, &sigstore.Options{
PrivateKeyPassphrasePrompt: func(keyFile string) (string, error) {
return promptForPassphrase(keyFile, os.Stdin, os.Stdout)
},
Stdin: os.Stdin,
Stdout: stdout,
})
if err != nil {
return fmt.Errorf("Error using --sign-by-sigstore: %w", err)
}
defer signer.Close()
signers = append(signers, signer)
}
var signIdentity reference.Named = nil
if opts.signIdentity != "" {
signIdentity, err = reference.ParseNamed(opts.signIdentity)
if err != nil {
return fmt.Errorf("Could not parse --sign-identity: %v", err)
}
}
opts.destImage.warnAboutIneffectiveOptions(destRef.Transport())
return retry.IfNecessary(ctx, func() error {
manifestBytes, err := copy.Image(ctx, policyContext, destRef, srcRef, &copy.Options{
RemoveSignatures: opts.removeSignatures,
SignBy: opts.signByFingerprint,
ReportWriter: stdout,
SourceCtx: sourceCtx,
DestinationCtx: destinationCtx,
ForceManifestMIMEType: manifestType,
ImageListSelection: imageListSelection,
OciDecryptConfig: decConfig,
OciEncryptLayers: encLayers,
OciEncryptConfig: encConfig,
RemoveSignatures: opts.removeSignatures,
Signers: signers,
SignBy: opts.signByFingerprint,
SignPassphrase: passphrase,
SignBySigstorePrivateKeyFile: opts.signBySigstorePrivateKey,
SignSigstorePrivateKeyPassphrase: []byte(passphrase),
SignIdentity: signIdentity,
ReportWriter: stdout,
SourceCtx: sourceCtx,
DestinationCtx: destinationCtx,
ForceManifestMIMEType: manifestType,
ImageListSelection: imageListSelection,
PreserveDigests: opts.preserveDigests,
OciDecryptConfig: decConfig,
OciEncryptLayers: encLayers,
OciEncryptConfig: encConfig,
})
if err != nil {
return err
@@ -204,7 +309,7 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) error {
if err != nil {
return err
}
if err = ioutil.WriteFile(opts.digestFile, []byte(manifestDigest.String()), 0644); err != nil {
if err = os.WriteFile(opts.digestFile, []byte(manifestDigest.String()), 0644); err != nil {
return fmt.Errorf("Failed to write digest to file %q: %w", opts.digestFile, err)
}
}

View File

@@ -15,7 +15,7 @@ import (
type deleteOptions struct {
global *globalOptions
image *imageOptions
retryOpts *retry.RetryOptions
retryOpts *retry.Options
}
func deleteCmd(global *globalOptions) *cobra.Command {
@@ -35,8 +35,9 @@ Supported transports:
%s
See skopeo(1) section "IMAGE NAMES" for the expected format
`, strings.Join(transports.ListNames(), ", ")),
RunE: commandAction(opts.run),
Example: `skopeo delete docker://registry.example.com/example/pause:latest`,
RunE: commandAction(opts.run),
Example: `skopeo delete docker://registry.example.com/example/pause:latest`,
ValidArgsFunction: autocompleteSupportedTransports,
}
adjustUsage(cmd)
flags := cmd.Flags()
@@ -69,7 +70,7 @@ func (opts *deleteOptions) run(args []string, stdout io.Writer) error {
ctx, cancel := opts.global.commandTimeoutContext()
defer cancel()
return retry.RetryIfNecessary(ctx, func() error {
return retry.IfNecessary(ctx, func() error {
return ref.DeleteImage(ctx, sys)
}, opts.retryOpts)
}

View File

@@ -1,222 +0,0 @@
package main
import (
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestOptionalBoolSet(t *testing.T) {
for _, c := range []struct {
input string
accepted bool
value bool
}{
// Valid inputs documented for strconv.ParseBool == flag.BoolVar
{"1", true, true},
{"t", true, true},
{"T", true, true},
{"TRUE", true, true},
{"true", true, true},
{"True", true, true},
{"0", true, false},
{"f", true, false},
{"F", true, false},
{"FALSE", true, false},
{"false", true, false},
{"False", true, false},
// A few invalid inputs
{"", false, false},
{"yes", false, false},
{"no", false, false},
{"2", false, false},
} {
var ob optionalBool
v := internalNewOptionalBoolValue(&ob)
require.False(t, ob.present)
err := v.Set(c.input)
if c.accepted {
assert.NoError(t, err, c.input)
assert.Equal(t, c.value, ob.value)
} else {
assert.Error(t, err, c.input)
assert.False(t, ob.present) // Just to be extra paranoid.
}
}
// Nothing actually explicitly says that .Set() is never called when the flag is not present on the command line;
// so, check that it is not being called, at least in the straightforward case (it's not possible to test that it
// is not called in any possible situation).
var globalOB, commandOB optionalBool
actionRun := false
app := &cobra.Command{
Use: "app",
}
optionalBoolFlag(app.PersistentFlags(), &globalOB, "global-OB", "")
cmd := &cobra.Command{
Use: "cmd",
RunE: func(cmd *cobra.Command, args []string) error {
assert.False(t, globalOB.present)
assert.False(t, commandOB.present)
actionRun = true
return nil
},
}
optionalBoolFlag(cmd.Flags(), &commandOB, "command-OB", "")
app.AddCommand(cmd)
app.SetArgs([]string{"cmd"})
err := app.Execute()
require.NoError(t, err)
assert.True(t, actionRun)
}
func TestOptionalBoolString(t *testing.T) {
for _, c := range []struct {
input optionalBool
expected string
}{
{optionalBool{present: true, value: true}, "true"},
{optionalBool{present: true, value: false}, "false"},
{optionalBool{present: false, value: true}, ""},
{optionalBool{present: false, value: false}, ""},
} {
var ob optionalBool
v := internalNewOptionalBoolValue(&ob)
ob = c.input
res := v.String()
assert.Equal(t, c.expected, res)
}
}
func TestOptionalBoolIsBoolFlag(t *testing.T) {
// IsBoolFlag means that the argument value must either be part of the same argument, with =;
// if there is no =, the value is set to true.
// This differs form other flags, where the argument is required and may be either separated with = or supplied in the next argument.
for _, c := range []struct {
input []string
expectedOB optionalBool
expectedArgs []string
}{
{[]string{"1", "2"}, optionalBool{present: false}, []string{"1", "2"}}, // Flag not present
{[]string{"--OB=true", "1", "2"}, optionalBool{present: true, value: true}, []string{"1", "2"}}, // --OB=true
{[]string{"--OB=false", "1", "2"}, optionalBool{present: true, value: false}, []string{"1", "2"}}, // --OB=false
{[]string{"--OB", "true", "1", "2"}, optionalBool{present: true, value: true}, []string{"true", "1", "2"}}, // --OB true
{[]string{"--OB", "false", "1", "2"}, optionalBool{present: true, value: true}, []string{"false", "1", "2"}}, // --OB false
} {
var ob optionalBool
actionRun := false
app := &cobra.Command{Use: "app"}
cmd := &cobra.Command{
Use: "cmd",
RunE: func(cmd *cobra.Command, args []string) error {
assert.Equal(t, c.expectedOB, ob)
assert.Equal(t, c.expectedArgs, args)
actionRun = true
return nil
},
}
optionalBoolFlag(cmd.Flags(), &ob, "OB", "")
app.AddCommand(cmd)
app.SetArgs(append([]string{"cmd"}, c.input...))
err := app.Execute()
require.NoError(t, err)
assert.True(t, actionRun)
}
}
func TestOptionalStringSet(t *testing.T) {
// Really just a smoke test, but differentiating between not present and empty.
for _, c := range []string{"", "hello"} {
var os optionalString
v := newOptionalStringValue(&os)
require.False(t, os.present)
err := v.Set(c)
assert.NoError(t, err, c)
assert.Equal(t, c, os.value)
}
// Nothing actually explicitly says that .Set() is never called when the flag is not present on the command line;
// so, check that it is not being called, at least in the straightforward case (it's not possible to test that it
// is not called in any possible situation).
var globalOS, commandOS optionalString
actionRun := false
app := &cobra.Command{
Use: "app",
}
app.PersistentFlags().Var(newOptionalStringValue(&globalOS), "global-OS", "")
cmd := &cobra.Command{
Use: "cmd",
RunE: func(cmd *cobra.Command, args []string) error {
assert.False(t, globalOS.present)
assert.False(t, commandOS.present)
actionRun = true
return nil
},
}
cmd.Flags().Var(newOptionalStringValue(&commandOS), "command-OS", "")
app.AddCommand(cmd)
app.SetArgs([]string{"cmd"})
err := app.Execute()
require.NoError(t, err)
assert.True(t, actionRun)
}
func TestOptionalStringString(t *testing.T) {
for _, c := range []struct {
input optionalString
expected string
}{
{optionalString{present: true, value: "hello"}, "hello"},
{optionalString{present: true, value: ""}, ""},
{optionalString{present: false, value: "hello"}, ""},
{optionalString{present: false, value: ""}, ""},
} {
var os optionalString
v := newOptionalStringValue(&os)
os = c.input
res := v.String()
assert.Equal(t, c.expected, res)
}
}
func TestOptionalStringIsBoolFlag(t *testing.T) {
// NOTE: optionalStringValue does not implement IsBoolFlag!
// IsBoolFlag means that the argument value must either be part of the same argument, with =;
// if there is no =, the value is set to true.
// This differs form other flags, where the argument is required and may be either separated with = or supplied in the next argument.
for _, c := range []struct {
input []string
expectedOS optionalString
expectedArgs []string
}{
{[]string{"1", "2"}, optionalString{present: false}, []string{"1", "2"}}, // Flag not present
{[]string{"--OS=hello", "1", "2"}, optionalString{present: true, value: "hello"}, []string{"1", "2"}}, // --OS=true
{[]string{"--OS=", "1", "2"}, optionalString{present: true, value: ""}, []string{"1", "2"}}, // --OS=false
{[]string{"--OS", "hello", "1", "2"}, optionalString{present: true, value: "hello"}, []string{"1", "2"}}, // --OS true
{[]string{"--OS", "", "1", "2"}, optionalString{present: true, value: ""}, []string{"1", "2"}}, // --OS false
} {
var os optionalString
actionRun := false
app := &cobra.Command{
Use: "app",
}
cmd := &cobra.Command{
Use: "cmd",
RunE: func(cmd *cobra.Command, args []string) error {
assert.Equal(t, c.expectedOS, os)
assert.Equal(t, c.expectedArgs, args)
actionRun = true
return nil
},
}
cmd.Flags().Var(newOptionalStringValue(&os), "OS", "")
app.AddCommand(cmd)
app.SetArgs(append([]string{"cmd"}, c.input...))
err := app.Execute()
require.NoError(t, err)
assert.True(t, actionRun)
}
}

View File

@@ -0,0 +1,90 @@
package main
import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"github.com/containers/image/v5/pkg/cli"
"github.com/containers/image/v5/signature/sigstore"
"github.com/spf13/cobra"
)
type generateSigstoreKeyOptions struct {
outputPrefix string
passphraseFile string
}
func generateSigstoreKeyCmd() *cobra.Command {
var opts generateSigstoreKeyOptions
cmd := &cobra.Command{
Use: "generate-sigstore-key [command options] --output-prefix PREFIX",
Short: "Generate a sigstore public/private key pair",
RunE: commandAction(opts.run),
Example: "skopeo generate-sigstore-key --output-prefix my-key",
}
adjustUsage(cmd)
flags := cmd.Flags()
flags.StringVar(&opts.outputPrefix, "output-prefix", "", "Write the keys to `PREFIX`.pub and `PREFIX`.private")
flags.StringVar(&opts.passphraseFile, "passphrase-file", "", "Read a passphrase for the private key from `PATH`")
return cmd
}
// ensurePathDoesNotExist verifies that path does not refer to an existing file,
// and returns an error if so.
func ensurePathDoesNotExist(path string) error {
switch _, err := os.Stat(path); {
case err == nil:
return fmt.Errorf("Refusing to overwrite existing %q", path)
case errors.Is(err, fs.ErrNotExist):
return nil
default:
return fmt.Errorf("Error checking existence of %q: %w", path, err)
}
}
func (opts *generateSigstoreKeyOptions) run(args []string, stdout io.Writer) error {
if len(args) != 0 || opts.outputPrefix == "" {
return errors.New("Usage: generate-sigstore-key --output-prefix PREFIX")
}
pubKeyPath := opts.outputPrefix + ".pub"
privateKeyPath := opts.outputPrefix + ".private"
if err := ensurePathDoesNotExist(pubKeyPath); err != nil {
return err
}
if err := ensurePathDoesNotExist(privateKeyPath); err != nil {
return err
}
var passphrase string
if opts.passphraseFile != "" {
p, err := cli.ReadPassphraseFile(opts.passphraseFile)
if err != nil {
return err
}
passphrase = p
} else {
p, err := promptForPassphrase(privateKeyPath, os.Stdin, os.Stdout)
if err != nil {
return err
}
passphrase = p
}
keys, err := sigstore.GenerateKeyPair([]byte(passphrase))
if err != nil {
return fmt.Errorf("Error generating key pair: %w", err)
}
if err := os.WriteFile(privateKeyPath, keys.PrivateKey, 0600); err != nil {
return fmt.Errorf("Error writing private key to %q: %w", privateKeyPath, err)
}
if err := os.WriteFile(pubKeyPath, keys.PublicKey, 0644); err != nil {
return fmt.Errorf("Error writing private key to %q: %w", pubKeyPath, err)
}
fmt.Fprintf(stdout, "Key written to %q and %q", privateKeyPath, pubKeyPath)
return nil
}

View File

@@ -0,0 +1,79 @@
package main
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGenerateSigstoreKey(t *testing.T) {
// Invalid command-line arguments
for _, args := range [][]string{
{},
{"--output-prefix", "foo", "a1"},
} {
out, err := runSkopeo(append([]string{"generate-sigstore-key"}, args...)...)
assertTestFailed(t, out, err, "Usage")
}
// One of the destination files already exists
outputSuffixes := []string{".pub", ".private"}
for _, suffix := range outputSuffixes {
dir := t.TempDir()
prefix := filepath.Join(dir, "prefix")
err := os.WriteFile(prefix+suffix, []byte{}, 0600)
require.NoError(t, err)
out, err := runSkopeo("generate-sigstore-key",
"--output-prefix", prefix, "--passphrase-file", "/dev/null",
)
assertTestFailed(t, out, err, "Refusing to overwrite")
}
// One of the destinations is inaccessible (simulate by a symlink that tries to
// traverse a non-directory)
for _, suffix := range outputSuffixes {
dir := t.TempDir()
nonDirectory := filepath.Join(dir, "nondirectory")
err := os.WriteFile(nonDirectory, []byte{}, 0600)
require.NoError(t, err)
prefix := filepath.Join(dir, "prefix")
err = os.Symlink(filepath.Join(nonDirectory, "unaccessible"), prefix+suffix)
require.NoError(t, err)
out, err := runSkopeo("generate-sigstore-key",
"--output-prefix", prefix, "--passphrase-file", "/dev/null",
)
assertTestFailed(t, out, err, prefix+suffix) // + an OS-specific error message
}
destDir := t.TempDir()
// Error reading passphrase
out, err := runSkopeo("generate-sigstore-key",
"--output-prefix", filepath.Join(destDir, "prefix"),
"--passphrase-file", filepath.Join(destDir, "this-does-not-exist"),
)
assertTestFailed(t, out, err, "this-does-not-exist")
// (The interactive passphrase prompting is not yet tested)
// Error writing outputs is untested: when unit tests run as root, we cant use permissions on a directory to cause write failures,
// with the --output-prefix mechanism, and refusing to even start writing to pre-exisiting files, directories are the only mechanism
// we have to trigger a write failure.
// Success
// Just a smoke-test, useability of the keys is tested in the generate implementation.
dir := t.TempDir()
prefix := filepath.Join(dir, "prefix")
passphraseFile := filepath.Join(dir, "passphrase")
err = os.WriteFile(passphraseFile, []byte("some passphrase"), 0600)
require.NoError(t, err)
out, err = runSkopeo("generate-sigstore-key",
"--output-prefix", prefix, "--passphrase-file", passphraseFile,
)
assert.NoError(t, err)
for _, suffix := range outputSuffixes {
assert.Contains(t, out, prefix+suffix)
}
}

View File

@@ -2,9 +2,9 @@ package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"text/template"
@@ -17,19 +17,20 @@ import (
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/types"
"github.com/containers/skopeo/cmd/skopeo/inspect"
"github.com/docker/distribution/registry/api/errcode"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
type inspectOptions struct {
global *globalOptions
image *imageOptions
retryOpts *retry.RetryOptions
format string
raw bool // Output the raw manifest instead of parsing information about the image
config bool // Output the raw config blob instead of parsing information about the image
global *globalOptions
image *imageOptions
retryOpts *retry.Options
format string
raw bool // Output the raw manifest instead of parsing information about the image
config bool // Output the raw config blob instead of parsing information about the image
doNotListTags bool // Do not list all tags available in the same repository
}
func inspectCmd(global *globalOptions) *cobra.Command {
@@ -54,12 +55,14 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
Example: `skopeo inspect docker://registry.fedoraproject.org/fedora
skopeo inspect --config docker://docker.io/alpine
skopeo inspect --format "Name: {{.Name}} Digest: {{.Digest}}" docker://registry.access.redhat.com/ubi8`,
ValidArgsFunction: autocompleteSupportedTransports,
}
adjustUsage(cmd)
flags := cmd.Flags()
flags.BoolVar(&opts.raw, "raw", false, "output raw manifest or configuration")
flags.BoolVar(&opts.config, "config", false, "output configuration")
flags.StringVarP(&opts.format, "format", "f", "", "Format the output to a Go template")
flags.BoolVarP(&opts.doNotListTags, "no-tags", "n", false, "Do not list the available tags from the repository in the output")
flags.AddFlagSet(&sharedFlags)
flags.AddFlagSet(&imageFlags)
flags.AddFlagSet(&retryFlags)
@@ -93,30 +96,30 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
return err
}
if err := retry.RetryIfNecessary(ctx, func() error {
if err := retry.IfNecessary(ctx, func() error {
src, err = parseImageSource(ctx, opts.image, imageName)
return err
}, opts.retryOpts); err != nil {
return errors.Wrapf(err, "Error parsing image name %q", imageName)
return fmt.Errorf("Error parsing image name %q: %w", imageName, err)
}
defer func() {
if err := src.Close(); err != nil {
retErr = errors.Wrapf(retErr, fmt.Sprintf("(could not close image: %v) ", err))
retErr = noteCloseFailure(retErr, "closing image", err)
}
}()
if err := retry.RetryIfNecessary(ctx, func() error {
if err := retry.IfNecessary(ctx, func() error {
rawManifest, _, err = src.GetManifest(ctx, nil)
return err
}, opts.retryOpts); err != nil {
return errors.Wrapf(err, "Error retrieving manifest for image")
return fmt.Errorf("Error retrieving manifest for image: %w", err)
}
if opts.raw && !opts.config {
_, err := stdout.Write(rawManifest)
if err != nil {
return fmt.Errorf("Error writing manifest to standard output: %v", err)
return fmt.Errorf("Error writing manifest to standard output: %w", err)
}
return nil
@@ -124,29 +127,29 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
img, err := image.FromUnparsedImage(ctx, sys, image.UnparsedInstance(src, nil))
if err != nil {
return errors.Wrapf(err, "Error parsing manifest for image")
return fmt.Errorf("Error parsing manifest for image: %w", err)
}
if opts.config && opts.raw {
var configBlob []byte
if err := retry.RetryIfNecessary(ctx, func() error {
if err := retry.IfNecessary(ctx, func() error {
configBlob, err = img.ConfigBlob(ctx)
return err
}, opts.retryOpts); err != nil {
return errors.Wrapf(err, "Error reading configuration blob")
return fmt.Errorf("Error reading configuration blob: %w", err)
}
_, err = stdout.Write(configBlob)
if err != nil {
return errors.Wrapf(err, "Error writing configuration blob to standard output")
return fmt.Errorf("Error writing configuration blob to standard output: %w", err)
}
return nil
} else if opts.config {
var config *v1.Image
if err := retry.RetryIfNecessary(ctx, func() error {
if err := retry.IfNecessary(ctx, func() error {
config, err = img.OCIConfig(ctx)
return err
}, opts.retryOpts); err != nil {
return errors.Wrapf(err, "Error reading OCI-formatted configuration data")
return fmt.Errorf("Error reading OCI-formatted configuration data: %w", err)
}
if report.IsJSON(opts.format) || opts.format == "" {
var out []byte
@@ -157,15 +160,15 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
} else {
row := "{{range . }}" + report.NormalizeFormat(opts.format) + "{{end}}"
data = append(data, config)
err = printTmpl(row, data)
err = printTmpl(stdout, row, data)
}
if err != nil {
return errors.Wrapf(err, "Error writing OCI-formatted configuration data to standard output")
return fmt.Errorf("Error writing OCI-formatted configuration data to standard output: %w", err)
}
return nil
}
if err := retry.RetryIfNecessary(ctx, func() error {
if err := retry.IfNecessary(ctx, func() error {
imgInspect, err = img.Inspect(ctx)
return err
}, opts.retryOpts); err != nil {
@@ -183,29 +186,44 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
Architecture: imgInspect.Architecture,
Os: imgInspect.Os,
Layers: imgInspect.Layers,
LayersData: imgInspect.LayersData,
Env: imgInspect.Env,
}
outputData.Digest, err = manifest.Digest(rawManifest)
if err != nil {
return errors.Wrapf(err, "Error computing manifest digest")
return fmt.Errorf("Error computing manifest digest: %w", err)
}
if dockerRef := img.Reference().DockerReference(); dockerRef != nil {
outputData.Name = dockerRef.Name()
}
if img.Reference().Transport() == docker.Transport {
if !opts.doNotListTags && img.Reference().Transport() == docker.Transport {
sys, err := opts.image.newSystemContext()
if err != nil {
return err
}
outputData.RepoTags, err = docker.GetRepositoryTags(ctx, sys, img.Reference())
if err != nil {
// some registries may decide to block the "list all tags" endpoint
// gracefully allow the inspect to continue in this case. Currently
// the IBM Bluemix container registry has this restriction.
// In addition, AWS ECR rejects it with 403 (Forbidden) if the "ecr:ListImages"
// action is not allowed.
if !strings.Contains(err.Error(), "401") && !strings.Contains(err.Error(), "403") {
return errors.Wrapf(err, "Error determining repository tags")
// Some registries may decide to block the "list all tags" endpoint;
// gracefully allow the inspect to continue in this case:
fatalFailure := true
// - AWS ECR rejects it if the "ecr:ListImages" action is not allowed.
// https://github.com/containers/skopeo/issues/726
var ec errcode.ErrorCoder
if ok := errors.As(err, &ec); ok && ec.ErrorCode() == errcode.ErrorCodeDenied {
fatalFailure = false
}
// - public.ecr.aws does not implement the endpoint at all, and fails with 404:
// https://github.com/containers/skopeo/issues/1230
// This is actually "code":"NOT_FOUND", and the parser doesnt preserve that.
// So, also check the error text.
if ok := errors.As(err, &ec); ok && ec.ErrorCode() == errcode.ErrorCodeUnknown {
var e errcode.Error
if ok := errors.As(err, &e); ok && e.Code == errcode.ErrorCodeUnknown && e.Message == "404 page not found" {
fatalFailure = false
}
}
if fatalFailure {
return fmt.Errorf("Error determining repository tags: %w", err)
}
logrus.Warnf("Registry disallows tag list retrieval; skipping")
}
@@ -219,14 +237,14 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
}
row := "{{range . }}" + report.NormalizeFormat(opts.format) + "{{end}}"
data = append(data, outputData)
return printTmpl(row, data)
return printTmpl(stdout, row, data)
}
func printTmpl(row string, data []interface{}) error {
func printTmpl(stdout io.Writer, row string, data []interface{}) error {
t, err := template.New("skopeo inspect").Parse(row)
if err != nil {
return err
}
w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0)
w := tabwriter.NewWriter(stdout, 8, 2, 2, ' ', 0)
return t.Execute(w, data)
}

View File

@@ -3,6 +3,7 @@ package inspect
import (
"time"
"github.com/containers/image/v5/types"
digest "github.com/opencontainers/go-digest"
)
@@ -19,5 +20,6 @@ type Output struct {
Architecture string
Os string
Layers []string
LayersData []types.ImageInspectLayer
Env []string
}

View File

@@ -1,9 +1,9 @@
package main
import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
@@ -13,14 +13,13 @@ import (
"github.com/containers/image/v5/pkg/blobinfocache"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type layersOptions struct {
global *globalOptions
image *imageOptions
retryOpts *retry.RetryOptions
retryOpts *retry.Options
}
func layersCmd(global *globalOptions) *cobra.Command {
@@ -69,25 +68,25 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
rawSource types.ImageSource
src types.ImageCloser
)
if err = retry.RetryIfNecessary(ctx, func() error {
if err = retry.IfNecessary(ctx, func() error {
rawSource, err = parseImageSource(ctx, opts.image, imageName)
return err
}, opts.retryOpts); err != nil {
return err
}
if err = retry.RetryIfNecessary(ctx, func() error {
if err = retry.IfNecessary(ctx, func() error {
src, err = image.FromSource(ctx, sys, rawSource)
return err
}, opts.retryOpts); err != nil {
if closeErr := rawSource.Close(); closeErr != nil {
return errors.Wrapf(err, " (close error: %v)", closeErr)
return fmt.Errorf("%w (closing image source: %v)", err, closeErr)
}
return err
}
defer func() {
if err := src.Close(); err != nil {
retErr = errors.Wrapf(retErr, " (close error: %v)", err)
retErr = noteCloseFailure(retErr, "closing image", err)
}
}()
@@ -122,7 +121,7 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
}
}
tmpDir, err := ioutil.TempDir(".", "layers-")
tmpDir, err := os.MkdirTemp(".", "layers-")
if err != nil {
return err
}
@@ -137,7 +136,7 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
defer func() {
if err := dest.Close(); err != nil {
retErr = errors.Wrapf(retErr, " (close error: %v)", err)
retErr = noteCloseFailure(retErr, "closing destination", err)
}
}()
@@ -146,7 +145,7 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
r io.ReadCloser
blobSize int64
)
if err = retry.RetryIfNecessary(ctx, func() error {
if err = retry.IfNecessary(ctx, func() error {
r, blobSize, err = rawSource.GetBlob(ctx, types.BlobInfo{Digest: bd.digest, Size: -1}, cache)
return err
}, opts.retryOpts); err != nil {
@@ -154,14 +153,14 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
}
if _, err := dest.PutBlob(ctx, r, types.BlobInfo{Digest: bd.digest, Size: blobSize}, cache, bd.isConfig); err != nil {
if closeErr := r.Close(); closeErr != nil {
return errors.Wrapf(err, " (close error: %v)", closeErr)
return fmt.Errorf("%w (close error: %v)", err, closeErr)
}
return err
}
}
var manifest []byte
if err = retry.RetryIfNecessary(ctx, func() error {
if err = retry.IfNecessary(ctx, func() error {
manifest, _, err = src.Manifest(ctx)
return err
}, opts.retryOpts); err != nil {

View File

@@ -3,29 +3,46 @@ package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"sort"
"strings"
"github.com/containers/common/pkg/retry"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/archive"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
// tagListOutput is the output format of (skopeo list-tags), primarily so that we can format it with a simple json.MarshalIndent.
type tagListOutput struct {
Repository string
Repository string `json:",omitempty"`
Tags []string
}
type tagsOptions struct {
global *globalOptions
image *imageOptions
retryOpts *retry.RetryOptions
retryOpts *retry.Options
}
var transportHandlers = map[string]func(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, userInput string) (repositoryName string, tagListing []string, err error){
docker.Transport.Name(): listDockerRepoTags,
archive.Transport.Name(): listDockerArchiveTags,
}
// supportedTransports returns all the supported transports
func supportedTransports(joinStr string) string {
res := make([]string, 0, len(transportHandlers))
for handlerName := range transportHandlers {
res = append(res, handlerName)
}
sort.Strings(res)
return strings.Join(res, joinStr)
}
func tagsCmd(global *globalOptions) *cobra.Command {
@@ -38,13 +55,14 @@ func tagsCmd(global *globalOptions) *cobra.Command {
image: imageOpts,
retryOpts: retryOpts,
}
cmd := &cobra.Command{
Use: "list-tags [command options] REPOSITORY-NAME",
Short: "List tags in the transport/repository specified by the REPOSITORY-NAME",
Long: `Return the list of tags from the transport/repository "REPOSITORY-NAME"
Use: "list-tags [command options] SOURCE-IMAGE",
Short: "List tags in the transport/repository specified by the SOURCE-IMAGE",
Long: `Return the list of tags from the transport/repository "SOURCE-IMAGE"
Supported transports:
docker
` + supportedTransports(" ") + `
See skopeo-list-tags(1) section "REPOSITORY NAMES" for the expected format
`,
@@ -63,12 +81,12 @@ See skopeo-list-tags(1) section "REPOSITORY NAMES" for the expected format
// Would really love to not have this, but needed to enforce tag-less and digest-less names
func parseDockerRepositoryReference(refString string) (types.ImageReference, error) {
if !strings.HasPrefix(refString, docker.Transport.Name()+"://") {
return nil, errors.Errorf("docker: image reference %s does not start with %s://", refString, docker.Transport.Name())
return nil, fmt.Errorf("docker: image reference %s does not start with %s://", refString, docker.Transport.Name())
}
parts := strings.SplitN(refString, ":", 2)
if len(parts) != 2 {
return nil, errors.Errorf(`Invalid image name "%s", expected colon-separated transport:reference`, refString)
return nil, fmt.Errorf(`Invalid image name "%s", expected colon-separated transport:reference`, refString)
}
ref, err := reference.ParseNormalizedNamed(strings.TrimPrefix(parts[1], "//"))
@@ -90,11 +108,63 @@ func listDockerTags(ctx context.Context, sys *types.SystemContext, imgRef types.
tags, err := docker.GetRepositoryTags(ctx, sys, imgRef)
if err != nil {
return ``, nil, fmt.Errorf("Error listing repository tags: %v", err)
return ``, nil, fmt.Errorf("Error listing repository tags: %w", err)
}
return repositoryName, tags, nil
}
// return the tagLists from a docker repo
func listDockerRepoTags(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, userInput string) (repositoryName string, tagListing []string, err error) {
// Do transport-specific parsing and validation to get an image reference
imgRef, err := parseDockerRepositoryReference(userInput)
if err != nil {
return
}
if err = retry.IfNecessary(ctx, func() error {
repositoryName, tagListing, err = listDockerTags(ctx, sys, imgRef)
return err
}, opts.retryOpts); err != nil {
return
}
return
}
// return the tagLists from a docker archive file
func listDockerArchiveTags(ctx context.Context, sys *types.SystemContext, opts *tagsOptions, userInput string) (repositoryName string, tagListing []string, err error) {
ref, err := alltransports.ParseImageName(userInput)
if err != nil {
return
}
tarReader, _, err := archive.NewReaderForReference(sys, ref)
if err != nil {
return
}
defer tarReader.Close()
imageRefs, err := tarReader.List()
if err != nil {
return
}
var repoTags []string
for imageIndex, items := range imageRefs {
for _, ref := range items {
repoTags, err = tarReader.ManifestTagsForReference(ref)
if err != nil {
return
}
// handle for each untagged image
if len(repoTags) == 0 {
repoTags = []string{fmt.Sprintf("@%d", imageIndex)}
}
tagListing = append(tagListing, repoTags...)
}
}
return
}
func (opts *tagsOptions) run(args []string, stdout io.Writer) (retErr error) {
ctx, cancel := opts.global.commandTimeoutContext()
defer cancel()
@@ -113,23 +183,17 @@ func (opts *tagsOptions) run(args []string, stdout io.Writer) (retErr error) {
return fmt.Errorf("Invalid %q: does not specify a transport", args[0])
}
if transport.Name() != docker.Transport.Name() {
return fmt.Errorf("Unsupported transport '%v' for tag listing. Only '%v' currently supported", transport.Name(), docker.Transport.Name())
}
// Do transport-specific parsing and validation to get an image reference
imgRef, err := parseDockerRepositoryReference(args[0])
if err != nil {
return err
}
var repositoryName string
var tagListing []string
if err = retry.RetryIfNecessary(ctx, func() error {
repositoryName, tagListing, err = listDockerTags(ctx, sys, imgRef)
return err
}, opts.retryOpts); err != nil {
return err
if val, ok := transportHandlers[transport.Name()]; ok {
repositoryName, tagListing, err = val(ctx, sys, opts, args[0])
if err != nil {
return err
}
} else {
return fmt.Errorf("Unsupported transport '%s' for tag listing. Only supported: %s",
transport.Name(), supportedTransports(", "))
}
outputData := tagListOutput{

View File

@@ -5,6 +5,7 @@ import (
"os"
"github.com/containers/common/pkg/auth"
commonFlag "github.com/containers/common/pkg/flag"
"github.com/containers/image/v5/types"
"github.com/spf13/cobra"
)
@@ -12,7 +13,7 @@ import (
type loginOptions struct {
global *globalOptions
loginOpts auth.LoginOptions
tlsVerify optionalBool
tlsVerify commonFlag.OptionalBool
}
func loginCmd(global *globalOptions) *cobra.Command {
@@ -28,7 +29,7 @@ func loginCmd(global *globalOptions) *cobra.Command {
}
adjustUsage(cmd)
flags := cmd.Flags()
optionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry")
commonFlag.OptionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry")
flags.AddFlagSet(auth.GetLoginFlags(&opts.loginOpts))
return cmd
}
@@ -40,8 +41,8 @@ func (opts *loginOptions) run(args []string, stdout io.Writer) error {
opts.loginOpts.Stdin = os.Stdin
opts.loginOpts.AcceptRepositories = true
sys := opts.global.newSystemContext()
if opts.tlsVerify.present {
sys.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.value)
if opts.tlsVerify.Present() {
sys.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.Value())
}
return auth.Login(ctx, sys, &opts.loginOpts, args)
}

View File

@@ -4,6 +4,7 @@ import (
"io"
"github.com/containers/common/pkg/auth"
commonFlag "github.com/containers/common/pkg/flag"
"github.com/containers/image/v5/types"
"github.com/spf13/cobra"
)
@@ -11,7 +12,7 @@ import (
type logoutOptions struct {
global *globalOptions
logoutOpts auth.LogoutOptions
tlsVerify optionalBool
tlsVerify commonFlag.OptionalBool
}
func logoutCmd(global *globalOptions) *cobra.Command {
@@ -27,7 +28,7 @@ func logoutCmd(global *globalOptions) *cobra.Command {
}
adjustUsage(cmd)
flags := cmd.Flags()
optionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry")
commonFlag.OptionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry")
flags.AddFlagSet(auth.GetLogoutFlags(&opts.logoutOpts))
return cmd
}
@@ -36,8 +37,8 @@ func (opts *logoutOptions) run(args []string, stdout io.Writer) error {
opts.logoutOpts.Stdout = stdout
opts.logoutOpts.AcceptRepositories = true
sys := opts.global.newSystemContext()
if opts.tlsVerify.present {
sys.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.value)
if opts.tlsVerify.Present() {
sys.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.Value())
}
return auth.Logout(sys, &opts.logoutOpts, args)
}

View File

@@ -3,8 +3,10 @@ package main
import (
"context"
"fmt"
"strings"
"time"
commonFlag "github.com/containers/common/pkg/flag"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/types"
"github.com/containers/skopeo/version"
@@ -20,17 +22,32 @@ var gitCommit = ""
var defaultUserAgent = "skopeo/" + version.Version
type globalOptions struct {
debug bool // Enable debug output
tlsVerify optionalBool // Require HTTPS and verify certificates (for docker: and docker-daemon:)
policyPath string // Path to a signature verification policy file
insecurePolicy bool // Use an "allow everything" signature verification policy
registriesDirPath string // Path to a "registries.d" registry configuration directory
overrideArch string // Architecture to use for choosing images, instead of the runtime one
overrideOS string // OS to use for choosing images, instead of the runtime one
overrideVariant string // Architecture variant to use for choosing images, instead of the runtime one
commandTimeout time.Duration // Timeout for the command execution
registriesConfPath string // Path to the "registries.conf" file
tmpDir string // Path to use for big temporary files
debug bool // Enable debug output
tlsVerify commonFlag.OptionalBool // Require HTTPS and verify certificates (for docker: and docker-daemon:)
policyPath string // Path to a signature verification policy file
insecurePolicy bool // Use an "allow everything" signature verification policy
registriesDirPath string // Path to a "registries.d" registry configuration directory
overrideArch string // Architecture to use for choosing images, instead of the runtime one
overrideOS string // OS to use for choosing images, instead of the runtime one
overrideVariant string // Architecture variant to use for choosing images, instead of the runtime one
commandTimeout time.Duration // Timeout for the command execution
registriesConfPath string // Path to the "registries.conf" file
tmpDir string // Path to use for big temporary files
}
// requireSubcommand returns an error if no sub command is provided
// This was copied from podman: `github.com/containers/podman/cmd/podman/validate/args.go
// Some small style changes to match skopeo were applied, but try to apply any
// bugfixes there first.
func requireSubcommand(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
suggestions := cmd.SuggestionsFor(args[0])
if len(suggestions) == 0 {
return fmt.Errorf("Unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information", cmd.CommandPath(), args[0])
}
return fmt.Errorf("Unrecognized command `%[1]s %[2]s`\n\nDid you mean this?\n\t%[3]s\n\nTry '%[1]s --help' for more information", cmd.CommandPath(), args[0], strings.Join(suggestions, "\n\t"))
}
return fmt.Errorf("Missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information", cmd.CommandPath())
}
// createApp returns a cobra.Command, and the underlying globalOptions object, to be run or tested.
@@ -40,16 +57,14 @@ func createApp() (*cobra.Command, *globalOptions) {
rootCommand := &cobra.Command{
Use: "skopeo",
Long: "Various operations with container images and container image registries",
RunE: requireSubcommand,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return opts.before(cmd)
},
SilenceUsage: true,
SilenceErrors: true,
// Currently, skopeo uses manually written completions. Cobra allows
// for auto-generating completions for various shells. Podman is
// already making us of that. If Skopeo decides to follow, please
// remove the line below (and hide the `completion` command).
CompletionOptions: cobra.CompletionOptions{DisableDefaultCmd: true},
// Hide the completion command which is provided by cobra
CompletionOptions: cobra.CompletionOptions{HiddenDefaultCmd: true},
// This is documented to parse "local" (non-PersistentFlags) flags of parent commands before
// running subcommands and handling their options. We don't really run into such cases,
// because all of our flags on rootCommand are in PersistentFlags, except for the deprecated --tls-verify;
@@ -78,16 +93,18 @@ func createApp() (*cobra.Command, *globalOptions) {
logrus.Fatal("unable to mark registries-conf flag as hidden")
}
rootCommand.PersistentFlags().StringVar(&opts.tmpDir, "tmpdir", "", "directory used to store temporary files")
flag := optionalBoolFlag(rootCommand.Flags(), &opts.tlsVerify, "tls-verify", "Require HTTPS and verify certificates when accessing the registry")
flag := commonFlag.OptionalBoolFlag(rootCommand.Flags(), &opts.tlsVerify, "tls-verify", "Require HTTPS and verify certificates when accessing the registry")
flag.Hidden = true
rootCommand.AddCommand(
copyCmd(&opts),
deleteCmd(&opts),
generateSigstoreKeyCmd(),
inspectCmd(&opts),
layersCmd(&opts),
loginCmd(&opts),
logoutCmd(&opts),
manifestDigestCmd(),
proxyCmd(&opts),
syncCmd(&opts),
standaloneSignCmd(),
standaloneVerifyCmd(),
@@ -102,7 +119,7 @@ func (opts *globalOptions) before(cmd *cobra.Command) error {
if opts.debug {
logrus.SetLevel(logrus.DebugLevel)
}
if opts.tlsVerify.present {
if opts.tlsVerify.Present() {
logrus.Warn("'--tls-verify' is deprecated, please set this on the specific subcommand")
}
return nil
@@ -159,8 +176,8 @@ func (opts *globalOptions) newSystemContext() *types.SystemContext {
DockerRegistryUserAgent: defaultUserAgent,
}
// DEPRECATED: We support this for backward compatibility, but override it if a per-image flag is provided.
if opts.tlsVerify.present {
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.value)
if opts.tlsVerify.Present() {
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.Value())
}
return ctx
}

View File

@@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/containers/image/v5/manifest"
"github.com/spf13/cobra"
@@ -31,7 +31,7 @@ func (opts *manifestDigestOptions) run(args []string, stdout io.Writer) error {
}
manifestPath := args[0]
man, err := ioutil.ReadFile(manifestPath)
man, err := os.ReadFile(manifestPath)
if err != nil {
return fmt.Errorf("Error reading manifest from %s: %v", manifestPath, err)
}

865
cmd/skopeo/proxy.go Normal file
View File

@@ -0,0 +1,865 @@
//go:build !windows
// +build !windows
package main
/*
This code is currently only intended to be used by ostree
to fetch content via containers. The API is subject
to change. A goal however is to stabilize the API
eventually as a full out-of-process interface to the
core containers/image library functionality.
To use this command, in a parent process create a
`socketpair()` of type `SOCK_SEQPACKET`. Fork
off this command, and pass one half of the socket
pair to the child. Providing it on stdin (fd 0)
is the expected default.
The protocol is JSON for the control layer,
and a read side of a `pipe()` passed for large data.
Base JSON protocol:
request: { method: "MethodName": args: [arguments] }
reply: { success: bool, value: JSVAL, pipeid: number, error: string }
For any non-metadata i.e. payload data from `GetManifest`
and `GetBlob` the server will pass back the read half of a `pipe(2)` via FD passing,
along with a `pipeid` integer.
The expected flow looks like this:
- Initialize
And validate the returned protocol version versus
what your client supports.
- OpenImage docker://quay.io/someorg/example:latest
(returns an imageid)
- GetManifest imageid (and associated <pipeid>)
(Streaming read data from pipe)
- FinishPipe <pipeid>
- GetBlob imageid sha256:...
(Streaming read data from pipe)
- FinishPipe <pipeid>
- GetBlob imageid sha256:...
(Streaming read data from pipe)
- FinishPipe <pipeid>
- CloseImage imageid
You may interleave invocations of these methods, e.g. one
can also invoke `OpenImage` multiple times, as well as
starting multiple GetBlob requests before calling `FinishPipe`
on them. The server will stream data into the pipefd
until `FinishPipe` is invoked.
Note that the pipe will not be closed by the server until
the client has invoked `FinishPipe`. This is to ensure
that the client checks for errors. For example, `GetBlob`
performs digest (e.g. sha256) verification and this must
be checked after all data has been written.
*/
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"os"
"sync"
"syscall"
"github.com/containers/image/v5/image"
"github.com/containers/image/v5/manifest"
ocilayout "github.com/containers/image/v5/oci/layout"
"github.com/containers/image/v5/pkg/blobinfocache"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
dockerdistributionerrcode "github.com/docker/distribution/registry/api/errcode"
dockerdistributionapi "github.com/docker/distribution/registry/api/v2"
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)
// protocolVersion is semantic version of the protocol used by this proxy.
// The first version of the protocol has major version 0.2 to signify a
// departure from the original code which used HTTP.
//
// 0.2.1: Initial version
// 0.2.2: Added support for fetching image configuration as OCI
// 0.2.3: Added GetFullConfig
// 0.2.4: Added OpenImageOptional
// 0.2.5: Added LayerInfoJSON
const protocolVersion = "0.2.5"
// maxMsgSize is the current limit on a packet size.
// Note that all non-metadata (i.e. payload data) is sent over a pipe.
const maxMsgSize = 32 * 1024
// maxJSONFloat is ECMA Number.MAX_SAFE_INTEGER
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
// We hard error if the input JSON numbers we expect to be
// integers are above this.
const maxJSONFloat = float64(uint64(1)<<53 - 1)
// sentinelImageID represents "image not found" on the wire
const sentinelImageID = 0
// request is the JSON serialization of a function call
type request struct {
// Method is the name of the function
Method string `json:"method"`
// Args is the arguments (parsed inside the function)
Args []interface{} `json:"args"`
}
// reply is serialized to JSON as the return value from a function call.
type reply struct {
// Success is true if and only if the call succeeded.
Success bool `json:"success"`
// Value is an arbitrary value (or values, as array/map) returned from the call.
Value interface{} `json:"value"`
// PipeID is an index into open pipes, and should be passed to FinishPipe
PipeID uint32 `json:"pipeid"`
// Error should be non-empty if Success == false
Error string `json:"error"`
}
// replyBuf is our internal deserialization of reply plus optional fd
type replyBuf struct {
// value will be converted to a reply Value
value interface{}
// fd is the read half of a pipe, passed back to the client
fd *os.File
// pipeid will be provided to the client as PipeID, an index into our open pipes
pipeid uint32
}
// activePipe is an open pipe to the client.
// It contains an error value
type activePipe struct {
// w is the write half of the pipe
w *os.File
// wg is completed when our worker goroutine is done
wg sync.WaitGroup
// err may be set in our worker goroutine
err error
}
// openImage is an opened image reference
type openImage struct {
// id is an opaque integer handle
id uint32
src types.ImageSource
cachedimg types.Image
}
// proxyHandler is the state associated with our socket.
type proxyHandler struct {
// lock protects everything else in this structure.
lock sync.Mutex
// opts is CLI options
opts *proxyOptions
sysctx *types.SystemContext
cache types.BlobInfoCache
// imageSerial is a counter for open images
imageSerial uint32
// images holds our opened images
images map[uint32]*openImage
// activePipes maps from "pipeid" to a pipe + goroutine pair
activePipes map[uint32]*activePipe
}
// convertedLayerInfo is the reduced form of the OCI type BlobInfo
// Used in the return value of GetLayerInfo
type convertedLayerInfo struct {
Digest digest.Digest `json:"digest"`
Size int64 `json:"size"`
MediaType string `json:"media_type"`
}
// Initialize performs one-time initialization, and returns the protocol version
func (h *proxyHandler) Initialize(args []interface{}) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if len(args) != 0 {
return ret, fmt.Errorf("invalid request, expecting zero arguments")
}
if h.sysctx != nil {
return ret, fmt.Errorf("already initialized")
}
sysctx, err := h.opts.imageOpts.newSystemContext()
if err != nil {
return ret, err
}
h.sysctx = sysctx
h.cache = blobinfocache.DefaultCache(sysctx)
r := replyBuf{
value: protocolVersion,
}
return r, nil
}
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
// The return value is an opaque integer handle.
func (h *proxyHandler) OpenImage(args []interface{}) (replyBuf, error) {
return h.openImageImpl(args, false)
}
// isDockerManifestUnknownError is a copy of code from containers/image,
// please update there first.
func isDockerManifestUnknownError(err error) bool {
var ec dockerdistributionerrcode.ErrorCoder
if !errors.As(err, &ec) {
return false
}
return ec.ErrorCode() == dockerdistributionapi.ErrorCodeManifestUnknown
}
// isNotFoundImageError heuristically attempts to determine whether an error
// is saying the remote source couldn't find the image (as opposed to an
// authentication error, an I/O error etc.)
// TODO drive this into containers/image properly
func isNotFoundImageError(err error) bool {
return isDockerManifestUnknownError(err) ||
errors.Is(err, ocilayout.ImageNotFoundError{})
}
func (h *proxyHandler) openImageImpl(args []interface{}, allowNotFound bool) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if h.sysctx == nil {
return ret, fmt.Errorf("client error: must invoke Initialize")
}
if len(args) != 1 {
return ret, fmt.Errorf("invalid request, expecting one argument")
}
imageref, ok := args[0].(string)
if !ok {
return ret, fmt.Errorf("expecting string imageref, not %T", args[0])
}
imgRef, err := alltransports.ParseImageName(imageref)
if err != nil {
return ret, err
}
imgsrc, err := imgRef.NewImageSource(context.Background(), h.sysctx)
if err != nil {
if allowNotFound && isNotFoundImageError(err) {
ret.value = sentinelImageID
return ret, nil
}
return ret, err
}
// Note that we never return zero as an imageid; this code doesn't yet
// handle overflow though.
h.imageSerial++
openimg := &openImage{
id: h.imageSerial,
src: imgsrc,
}
h.images[openimg.id] = openimg
ret.value = openimg.id
return ret, nil
}
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
// The return value is an opaque integer handle. If the image does not exist, zero
// is returned.
func (h *proxyHandler) OpenImageOptional(args []interface{}) (replyBuf, error) {
return h.openImageImpl(args, true)
}
func (h *proxyHandler) CloseImage(args []interface{}) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if h.sysctx == nil {
return ret, fmt.Errorf("client error: must invoke Initialize")
}
if len(args) != 1 {
return ret, fmt.Errorf("invalid request, expecting one argument")
}
imgref, err := h.parseImageFromID(args[0])
if err != nil {
return ret, err
}
imgref.src.Close()
delete(h.images, imgref.id)
return ret, nil
}
func parseImageID(v interface{}) (uint32, error) {
imgidf, ok := v.(float64)
if !ok {
return 0, fmt.Errorf("expecting integer imageid, not %T", v)
}
return uint32(imgidf), nil
}
// parseUint64 validates that a number fits inside a JavaScript safe integer
func parseUint64(v interface{}) (uint64, error) {
f, ok := v.(float64)
if !ok {
return 0, fmt.Errorf("expecting numeric, not %T", v)
}
if f > maxJSONFloat {
return 0, fmt.Errorf("out of range integer for numeric %f", f)
}
return uint64(f), nil
}
func (h *proxyHandler) parseImageFromID(v interface{}) (*openImage, error) {
imgid, err := parseImageID(v)
if err != nil {
return nil, err
}
if imgid == sentinelImageID {
return nil, fmt.Errorf("Invalid imageid value of zero")
}
imgref, ok := h.images[imgid]
if !ok {
return nil, fmt.Errorf("no image %v", imgid)
}
return imgref, nil
}
func (h *proxyHandler) allocPipe() (*os.File, *activePipe, error) {
piper, pipew, err := os.Pipe()
if err != nil {
return nil, nil, err
}
f := activePipe{
w: pipew,
}
h.activePipes[uint32(pipew.Fd())] = &f
f.wg.Add(1)
return piper, &f, nil
}
// returnBytes generates a return pipe() from a byte array
// In the future it might be nicer to return this via memfd_create()
func (h *proxyHandler) returnBytes(retval interface{}, buf []byte) (replyBuf, error) {
var ret replyBuf
piper, f, err := h.allocPipe()
if err != nil {
return ret, err
}
go func() {
// Signal completion when we return
defer f.wg.Done()
_, err = io.Copy(f.w, bytes.NewReader(buf))
if err != nil {
f.err = err
}
}()
ret.value = retval
ret.fd = piper
ret.pipeid = uint32(f.w.Fd())
return ret, nil
}
// cacheTargetManifest is invoked when GetManifest or GetConfig is invoked
// the first time for a given image. If the requested image is a manifest
// list, this function resolves it to the image matching the calling process'
// operating system and architecture.
//
// TODO: Add GetRawManifest or so that exposes manifest lists
func (h *proxyHandler) cacheTargetManifest(img *openImage) error {
ctx := context.Background()
if img.cachedimg != nil {
return nil
}
unparsedToplevel := image.UnparsedInstance(img.src, nil)
mfest, manifestType, err := unparsedToplevel.Manifest(ctx)
if err != nil {
return err
}
var target *image.UnparsedImage
if manifest.MIMETypeIsMultiImage(manifestType) {
manifestList, err := manifest.ListFromBlob(mfest, manifestType)
if err != nil {
return err
}
instanceDigest, err := manifestList.ChooseInstance(h.sysctx)
if err != nil {
return err
}
target = image.UnparsedInstance(img.src, &instanceDigest)
} else {
target = unparsedToplevel
}
cachedimg, err := image.FromUnparsedImage(ctx, h.sysctx, target)
if err != nil {
return err
}
img.cachedimg = cachedimg
return nil
}
// GetManifest returns a copy of the manifest, converted to OCI format, along with the original digest.
// Manifest lists are resolved to the current operating system and architecture.
func (h *proxyHandler) GetManifest(args []interface{}) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if h.sysctx == nil {
return ret, fmt.Errorf("client error: must invoke Initialize")
}
if len(args) != 1 {
return ret, fmt.Errorf("invalid request, expecting one argument")
}
imgref, err := h.parseImageFromID(args[0])
if err != nil {
return ret, err
}
err = h.cacheTargetManifest(imgref)
if err != nil {
return ret, err
}
img := imgref.cachedimg
ctx := context.Background()
rawManifest, manifestType, err := img.Manifest(ctx)
if err != nil {
return ret, err
}
// We only support OCI and docker2schema2. We know docker2schema2 can be easily+cheaply
// converted into OCI, so consumers only need to see OCI.
switch manifestType {
case imgspecv1.MediaTypeImageManifest, manifest.DockerV2Schema2MediaType:
break
// Explicitly reject e.g. docker schema 1 type with a "legacy" note
case manifest.DockerV2Schema1MediaType, manifest.DockerV2Schema1SignedMediaType:
return ret, fmt.Errorf("unsupported legacy manifest MIME type: %s", manifestType)
default:
return ret, fmt.Errorf("unsupported manifest MIME type: %s", manifestType)
}
// We always return the original digest, as that's what clients need to do pull-by-digest
// and in general identify the image.
digest, err := manifest.Digest(rawManifest)
if err != nil {
return ret, err
}
var serialized []byte
// But, we convert to OCI format on the wire if it's not already. The idea here is that by reusing the containers/image
// stack, clients to this proxy can pretend the world is OCI only, and not need to care about e.g.
// docker schema and MIME types.
if manifestType != imgspecv1.MediaTypeImageManifest {
manifestUpdates := types.ManifestUpdateOptions{ManifestMIMEType: imgspecv1.MediaTypeImageManifest}
ociImage, err := img.UpdatedImage(ctx, manifestUpdates)
if err != nil {
return ret, err
}
ociSerialized, _, err := ociImage.Manifest(ctx)
if err != nil {
return ret, err
}
serialized = ociSerialized
} else {
serialized = rawManifest
}
return h.returnBytes(digest, serialized)
}
// GetFullConfig returns a copy of the image configuration, converted to OCI format.
// https://github.com/opencontainers/image-spec/blob/main/config.md
func (h *proxyHandler) GetFullConfig(args []interface{}) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if h.sysctx == nil {
return ret, fmt.Errorf("client error: must invoke Initialize")
}
if len(args) != 1 {
return ret, fmt.Errorf("invalid request, expecting: [imgid]")
}
imgref, err := h.parseImageFromID(args[0])
if err != nil {
return ret, err
}
err = h.cacheTargetManifest(imgref)
if err != nil {
return ret, err
}
img := imgref.cachedimg
ctx := context.TODO()
config, err := img.OCIConfig(ctx)
if err != nil {
return ret, err
}
serialized, err := json.Marshal(&config)
if err != nil {
return ret, err
}
return h.returnBytes(nil, serialized)
}
// GetConfig returns a copy of the container runtime configuration, converted to OCI format.
// Note that due to a historical mistake, this returns not the full image configuration,
// but just the container runtime configuration. You should use GetFullConfig instead.
func (h *proxyHandler) GetConfig(args []interface{}) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if h.sysctx == nil {
return ret, fmt.Errorf("client error: must invoke Initialize")
}
if len(args) != 1 {
return ret, fmt.Errorf("invalid request, expecting: [imgid]")
}
imgref, err := h.parseImageFromID(args[0])
if err != nil {
return ret, err
}
err = h.cacheTargetManifest(imgref)
if err != nil {
return ret, err
}
img := imgref.cachedimg
ctx := context.TODO()
config, err := img.OCIConfig(ctx)
if err != nil {
return ret, err
}
serialized, err := json.Marshal(&config.Config)
if err != nil {
return ret, err
}
return h.returnBytes(nil, serialized)
}
// GetBlob fetches a blob, performing digest verification.
func (h *proxyHandler) GetBlob(args []interface{}) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if h.sysctx == nil {
return ret, fmt.Errorf("client error: must invoke Initialize")
}
if len(args) != 3 {
return ret, fmt.Errorf("found %d args, expecting (imgid, digest, size)", len(args))
}
imgref, err := h.parseImageFromID(args[0])
if err != nil {
return ret, err
}
digestStr, ok := args[1].(string)
if !ok {
return ret, fmt.Errorf("expecting string blobid")
}
size, err := parseUint64(args[2])
if err != nil {
return ret, err
}
ctx := context.TODO()
d, err := digest.Parse(digestStr)
if err != nil {
return ret, err
}
blobr, blobSize, err := imgref.src.GetBlob(ctx, types.BlobInfo{Digest: d, Size: int64(size)}, h.cache)
if err != nil {
return ret, err
}
piper, f, err := h.allocPipe()
if err != nil {
blobr.Close()
return ret, err
}
go func() {
// Signal completion when we return
defer blobr.Close()
defer f.wg.Done()
verifier := d.Verifier()
tr := io.TeeReader(blobr, verifier)
n, err := io.Copy(f.w, tr)
if err != nil {
f.err = err
return
}
if n != int64(size) {
f.err = fmt.Errorf("expected %d bytes in blob, got %d", size, n)
}
if !verifier.Verified() {
f.err = fmt.Errorf("corrupted blob, expecting %s", d.String())
}
}()
ret.value = blobSize
ret.fd = piper
ret.pipeid = uint32(f.w.Fd())
return ret, nil
}
// GetLayerInfo returns data about the layers of an image, useful for reading the layer contents.
//
// This needs to be called since the data returned by GetManifest() does not allow to correctly
// calling GetBlob() for the containers-storage: transport (which doesnt store the original compressed
// representations referenced in the manifest).
func (h *proxyHandler) GetLayerInfo(args []interface{}) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
if h.sysctx == nil {
return ret, fmt.Errorf("client error: must invoke Initialize")
}
if len(args) != 1 {
return ret, fmt.Errorf("found %d args, expecting (imgid)", len(args))
}
imgref, err := h.parseImageFromID(args[0])
if err != nil {
return ret, err
}
ctx := context.TODO()
err = h.cacheTargetManifest(imgref)
if err != nil {
return ret, err
}
img := imgref.cachedimg
layerInfos, err := img.LayerInfosForCopy(ctx)
if err != nil {
return ret, err
}
if layerInfos == nil {
layerInfos = img.LayerInfos()
}
var layers []convertedLayerInfo
for _, layer := range layerInfos {
layers = append(layers, convertedLayerInfo{layer.Digest, layer.Size, layer.MediaType})
}
ret.value = layers
return ret, nil
}
// FinishPipe waits for the worker goroutine to finish, and closes the write side of the pipe.
func (h *proxyHandler) FinishPipe(args []interface{}) (replyBuf, error) {
h.lock.Lock()
defer h.lock.Unlock()
var ret replyBuf
pipeidv, err := parseUint64(args[0])
if err != nil {
return ret, err
}
pipeid := uint32(pipeidv)
f, ok := h.activePipes[pipeid]
if !ok {
return ret, fmt.Errorf("finishpipe: no active pipe %d", pipeid)
}
// Wait for the goroutine to complete
f.wg.Wait()
// And only now do we close the write half; this forces the client to call this API
f.w.Close()
// Propagate any errors from the goroutine worker
err = f.err
delete(h.activePipes, pipeid)
return ret, err
}
// close releases all resources associated with this proxy backend
func (h *proxyHandler) close() {
for _, image := range h.images {
err := image.src.Close()
if err != nil {
// This shouldn't be fatal
logrus.Warnf("Failed to close image %s: %v", transports.ImageName(image.cachedimg.Reference()), err)
}
}
}
// send writes a reply buffer to the socket
func (buf replyBuf) send(conn *net.UnixConn, err error) error {
replyToSerialize := reply{
Success: err == nil,
Value: buf.value,
PipeID: buf.pipeid,
}
if err != nil {
replyToSerialize.Error = err.Error()
}
serializedReply, err := json.Marshal(&replyToSerialize)
if err != nil {
return err
}
// We took ownership of the FD - close it when we're done.
defer func() {
if buf.fd != nil {
buf.fd.Close()
}
}()
// Copy the FD number to the socket ancillary buffer
fds := make([]int, 0)
if buf.fd != nil {
fds = append(fds, int(buf.fd.Fd()))
}
oob := syscall.UnixRights(fds...)
n, oobn, err := conn.WriteMsgUnix(serializedReply, oob, nil)
if err != nil {
return err
}
// Validate that we sent the full packet
if n != len(serializedReply) || oobn != len(oob) {
return io.ErrShortWrite
}
return nil
}
type proxyOptions struct {
global *globalOptions
imageOpts *imageOptions
sockFd int
}
func proxyCmd(global *globalOptions) *cobra.Command {
sharedFlags, sharedOpts := sharedImageFlags()
imageFlags, imageOpts := imageFlags(global, sharedOpts, nil, "", "")
opts := proxyOptions{global: global, imageOpts: imageOpts}
cmd := &cobra.Command{
Use: "experimental-image-proxy [command options] IMAGE",
Short: "Interactive proxy for fetching container images (EXPERIMENTAL)",
Long: `Run skopeo as a proxy, supporting HTTP requests to fetch manifests and blobs.`,
RunE: commandAction(opts.run),
Args: cobra.ExactArgs(0),
// Not stabilized yet
Hidden: true,
Example: `skopeo experimental-image-proxy --sockfd 3`,
}
adjustUsage(cmd)
flags := cmd.Flags()
flags.AddFlagSet(&sharedFlags)
flags.AddFlagSet(&imageFlags)
flags.IntVar(&opts.sockFd, "sockfd", 0, "Serve on opened socket pair (default 0/stdin)")
return cmd
}
// processRequest dispatches a remote request.
// replyBuf is the result of the invocation.
// terminate should be true if processing of requests should halt.
func (h *proxyHandler) processRequest(readBytes []byte) (rb replyBuf, terminate bool, err error) {
var req request
// Parse the request JSON
if err = json.Unmarshal(readBytes, &req); err != nil {
err = fmt.Errorf("invalid request: %v", err)
return
}
// Dispatch on the method
switch req.Method {
case "Initialize":
rb, err = h.Initialize(req.Args)
case "OpenImage":
rb, err = h.OpenImage(req.Args)
case "OpenImageOptional":
rb, err = h.OpenImageOptional(req.Args)
case "CloseImage":
rb, err = h.CloseImage(req.Args)
case "GetManifest":
rb, err = h.GetManifest(req.Args)
case "GetConfig":
rb, err = h.GetConfig(req.Args)
case "GetFullConfig":
rb, err = h.GetFullConfig(req.Args)
case "GetBlob":
rb, err = h.GetBlob(req.Args)
case "GetLayerInfo":
rb, err = h.GetLayerInfo(req.Args)
case "FinishPipe":
rb, err = h.FinishPipe(req.Args)
case "Shutdown":
terminate = true
// NOTE: If you add a method here, you should very likely be bumping the
// const protocolVersion above.
default:
err = fmt.Errorf("unknown method: %s", req.Method)
}
return
}
// Implementation of podman experimental-image-proxy
func (opts *proxyOptions) run(args []string, stdout io.Writer) error {
handler := &proxyHandler{
opts: opts,
images: make(map[uint32]*openImage),
activePipes: make(map[uint32]*activePipe),
}
defer handler.close()
// Convert the socket FD passed by client into a net.FileConn
fd := os.NewFile(uintptr(opts.sockFd), "sock")
fconn, err := net.FileConn(fd)
if err != nil {
return err
}
conn := fconn.(*net.UnixConn)
// Allocate a buffer to copy the packet into
buf := make([]byte, maxMsgSize)
for {
n, _, err := conn.ReadFrom(buf)
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return fmt.Errorf("reading socket: %v", err)
}
readbuf := buf[0:n]
rb, terminate, err := handler.processRequest(readbuf)
if terminate {
return nil
}
if err := rb.send(conn, err); err != nil {
return fmt.Errorf("writing to socket: %w", err)
}
}
}

View File

@@ -0,0 +1,30 @@
//go:build windows
// +build windows
package main
import (
"fmt"
"io"
"github.com/spf13/cobra"
)
type proxyOptions struct {
global *globalOptions
}
func proxyCmd(global *globalOptions) *cobra.Command {
opts := proxyOptions{global: global}
cmd := &cobra.Command{
RunE: commandAction(opts.run),
Args: cobra.ExactArgs(0),
// Not stabilized yet
Hidden: true,
}
return cmd
}
func (opts *proxyOptions) run(args []string, stdout io.Writer) error {
return fmt.Errorf("This command is not supported on Windows")
}

View File

@@ -5,14 +5,16 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/containers/image/v5/pkg/cli"
"github.com/containers/image/v5/signature"
"github.com/spf13/cobra"
)
type standaloneSignOptions struct {
output string // Output file path
output string // Output file path
passphraseFile string // Path pointing to a passphrase file when signing
}
func standaloneSignCmd() *cobra.Command {
@@ -25,6 +27,7 @@ func standaloneSignCmd() *cobra.Command {
adjustUsage(cmd)
flags := cmd.Flags()
flags.StringVarP(&opts.output, "output", "o", "", "output the signature to `SIGNATURE`")
flags.StringVarP(&opts.passphraseFile, "passphrase-file", "", "", "file that contains a passphrase for the --sign-by key")
return cmd
}
@@ -36,7 +39,7 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error {
dockerReference := args[1]
fingerprint := args[2]
manifest, err := ioutil.ReadFile(manifestPath)
manifest, err := os.ReadFile(manifestPath)
if err != nil {
return fmt.Errorf("Error reading %s: %v", manifestPath, err)
}
@@ -46,12 +49,18 @@ func (opts *standaloneSignOptions) run(args []string, stdout io.Writer) error {
return fmt.Errorf("Error initializing GPG: %v", err)
}
defer mech.Close()
signature, err := signature.SignDockerManifest(manifest, dockerReference, mech, fingerprint)
passphrase, err := cli.ReadPassphraseFile(opts.passphraseFile)
if err != nil {
return err
}
signature, err := signature.SignDockerManifestWithOptions(manifest, dockerReference, mech, fingerprint, &signature.SignOptions{Passphrase: passphrase})
if err != nil {
return fmt.Errorf("Error creating signature: %v", err)
}
if err := ioutil.WriteFile(opts.output, signature, 0644); err != nil {
if err := os.WriteFile(opts.output, signature, 0644); err != nil {
return fmt.Errorf("Error writing signature to %s: %v", opts.output, err)
}
return nil
@@ -80,11 +89,11 @@ func (opts *standaloneVerifyOptions) run(args []string, stdout io.Writer) error
expectedFingerprint := args[2]
signaturePath := args[3]
unverifiedManifest, err := ioutil.ReadFile(manifestPath)
unverifiedManifest, err := os.ReadFile(manifestPath)
if err != nil {
return fmt.Errorf("Error reading manifest from %s: %v", manifestPath, err)
}
unverifiedSignature, err := ioutil.ReadFile(signaturePath)
unverifiedSignature, err := os.ReadFile(signaturePath)
if err != nil {
return fmt.Errorf("Error reading signature from %s: %v", signaturePath, err)
}
@@ -130,7 +139,7 @@ func (opts *untrustedSignatureDumpOptions) run(args []string, stdout io.Writer)
}
untrustedSignaturePath := args[0]
untrustedSignature, err := ioutil.ReadFile(untrustedSignaturePath)
untrustedSignature, err := os.ReadFile(untrustedSignaturePath)
if err != nil {
return fmt.Errorf("Error reading untrusted signature from %s: %v", untrustedSignaturePath, err)
}

View File

@@ -2,7 +2,6 @@ package main
import (
"encoding/json"
"io/ioutil"
"os"
"testing"
"time"
@@ -25,9 +24,8 @@ const (
// Test that results of runSkopeo failed with nothing on stdout, and substring
// within the error message.
func assertTestFailed(t *testing.T, stdout string, err error, substring string) {
assert.Error(t, err)
assert.ErrorContains(t, err, substring)
assert.Empty(t, stdout)
assert.Contains(t, err.Error(), substring)
}
func TestStandaloneSign(t *testing.T) {
@@ -40,8 +38,7 @@ func TestStandaloneSign(t *testing.T) {
manifestPath := "fixtures/image.manifest.json"
dockerReference := "testing/manifest"
os.Setenv("GNUPGHOME", "fixtures")
defer os.Unsetenv("GNUPGHOME")
t.Setenv("GNUPGHOME", "fixtures")
// Invalid command-line arguments
for _, args := range [][]string{
@@ -78,7 +75,7 @@ func TestStandaloneSign(t *testing.T) {
assertTestFailed(t, out, err, "/dev/full")
// Success
sigOutput, err := ioutil.TempFile("", "sig")
sigOutput, err := os.CreateTemp("", "sig")
require.NoError(t, err)
defer os.Remove(sigOutput.Name())
out, err = runSkopeo("standalone-sign", "-o", sigOutput.Name(),
@@ -86,9 +83,9 @@ func TestStandaloneSign(t *testing.T) {
require.NoError(t, err)
assert.Empty(t, out)
sig, err := ioutil.ReadFile(sigOutput.Name())
sig, err := os.ReadFile(sigOutput.Name())
require.NoError(t, err)
manifest, err := ioutil.ReadFile(manifestPath)
manifest, err := os.ReadFile(manifestPath)
require.NoError(t, err)
mech, err = signature.NewGPGSigningMechanism()
require.NoError(t, err)
@@ -103,8 +100,7 @@ func TestStandaloneVerify(t *testing.T) {
manifestPath := "fixtures/image.manifest.json"
signaturePath := "fixtures/image.signature"
dockerReference := "testing/manifest"
os.Setenv("GNUPGHOME", "fixtures")
defer os.Unsetenv("GNUPGHOME")
t.Setenv("GNUPGHOME", "fixtures")
// Invalid command-line arguments
for _, args := range [][]string{

View File

@@ -2,24 +2,28 @@ package main
import (
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"io/fs"
"os"
"path"
"path/filepath"
"regexp"
"strings"
commonFlag "github.com/containers/common/pkg/flag"
"github.com/containers/common/pkg/retry"
"github.com/containers/image/v5/copy"
"github.com/containers/image/v5/directory"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/pkg/cli"
"github.com/containers/image/v5/pkg/cli/sigstore"
"github.com/containers/image/v5/signature/signer"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/types"
"github.com/opencontainers/go-digest"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
@@ -27,18 +31,25 @@ import (
// syncOptions contains information retrieved from the skopeo sync command line.
type syncOptions struct {
global *globalOptions // Global (not command dependent) skopeo options
deprecatedTLSVerify *deprecatedTLSVerifyOption
srcImage *imageOptions // Source image options
destImage *imageDestOptions // Destination image options
retryOpts *retry.RetryOptions
removeSignatures bool // Do not copy signatures from the source image
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
format optionalString // Force conversion of the image to a specified format
source string // Source repository name
destination string // Destination registry name
scoped bool // When true, namespace copied images at destination using the source repository name
all bool // Copy all of the images if an image in the source is a list
global *globalOptions // Global (not command dependent) skopeo options
deprecatedTLSVerify *deprecatedTLSVerifyOption
srcImage *imageOptions // Source image options
destImage *imageDestOptions // Destination image options
retryOpts *retry.Options
removeSignatures bool // Do not copy signatures from the source image
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
signBySigstoreParamFile string // Sign the image using a sigstore signature per configuration in a param file
signBySigstorePrivateKey string // Sign the image using a sigstore private key
signPassphraseFile string // Path pointing to a passphrase file when signing
format commonFlag.OptionalString // Force conversion of the image to a specified format
source string // Source repository name
destination string // Destination registry name
scoped bool // When true, namespace copied images at destination using the source repository name
all bool // Copy all of the images if an image in the source is a list
dryRun bool // Don't actually copy anything, just output what it would have done
preserveDigests bool // Preserve digests during sync
keepGoing bool // Whether or not to abort the sync if there are any errors during syncing the images
appendSuffix string // Suffix to append to destination image tag
}
// repoDescriptor contains information of a single repository used as a sync source.
@@ -99,11 +110,18 @@ See skopeo-sync(1) for details.
flags := cmd.Flags()
flags.BoolVar(&opts.removeSignatures, "remove-signatures", false, "Do not copy signatures from SOURCE images")
flags.StringVar(&opts.signByFingerprint, "sign-by", "", "Sign the image using a GPG key with the specified `FINGERPRINT`")
flags.VarP(newOptionalStringValue(&opts.format), "format", "f", `MANIFEST TYPE (oci, v2s1, or v2s2) to use when syncing image(s) to a destination (default is manifest type of source, with fallbacks)`)
flags.StringVar(&opts.signBySigstoreParamFile, "sign-by-sigstore", "", "Sign the image using a sigstore parameter file at `PATH`")
flags.StringVar(&opts.signBySigstorePrivateKey, "sign-by-sigstore-private-key", "", "Sign the image using a sigstore private key at `PATH`")
flags.StringVar(&opts.signPassphraseFile, "sign-passphrase-file", "", "File that contains a passphrase for the --sign-by key")
flags.VarP(commonFlag.NewOptionalStringValue(&opts.format), "format", "f", `MANIFEST TYPE (oci, v2s1, or v2s2) to use when syncing image(s) to a destination (default is manifest type of source, with fallbacks)`)
flags.StringVarP(&opts.source, "src", "s", "", "SOURCE transport type")
flags.StringVarP(&opts.destination, "dest", "d", "", "DESTINATION transport type")
flags.BoolVar(&opts.scoped, "scoped", false, "Images at DESTINATION are prefix using the full source image path as scope")
flags.StringVar(&opts.appendSuffix, "append-suffix", "", "String to append to DESTINATION tags")
flags.BoolVarP(&opts.all, "all", "a", false, "Copy all images if SOURCE-IMAGE is a list")
flags.BoolVar(&opts.dryRun, "dry-run", false, "Run without actually copying data")
flags.BoolVar(&opts.preserveDigests, "preserve-digests", false, "Preserve digests of images and lists")
flags.BoolVarP(&opts.keepGoing, "keep-going", "", false, "Do not abort the sync if any image copy fails")
flags.AddFlagSet(&sharedFlags)
flags.AddFlagSet(&deprecatedTLSVerifyFlags)
flags.AddFlagSet(&srcFlags)
@@ -130,13 +148,13 @@ func (tls *tlsVerifyConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
// It returns a new unmarshaled sourceConfig object and any error encountered.
func newSourceConfig(yamlFile string) (sourceConfig, error) {
var cfg sourceConfig
source, err := ioutil.ReadFile(yamlFile)
source, err := os.ReadFile(yamlFile)
if err != nil {
return cfg, err
}
err = yaml.Unmarshal(source, &cfg)
if err != nil {
return cfg, errors.Wrapf(err, "Failed to unmarshal %q", yamlFile)
return cfg, fmt.Errorf("Failed to unmarshal %q: %w", yamlFile, err)
}
return cfg, nil
}
@@ -148,7 +166,7 @@ func parseRepositoryReference(input string) (reference.Named, error) {
return nil, err
}
if !reference.IsNameOnly(ref) {
return nil, errors.Errorf("input names a reference, not a repository")
return nil, errors.New("input names a reference, not a repository")
}
return ref, nil
}
@@ -166,24 +184,24 @@ func destinationReference(destination string, transport string) (types.ImageRefe
case directory.Transport.Name():
_, err := os.Stat(destination)
if err == nil {
return nil, errors.Errorf("Refusing to overwrite destination directory %q", destination)
return nil, fmt.Errorf("Refusing to overwrite destination directory %q", destination)
}
if !os.IsNotExist(err) {
return nil, errors.Wrap(err, "Destination directory could not be used")
return nil, fmt.Errorf("Destination directory could not be used: %w", err)
}
// the directory holding the image must be created here
if err = os.MkdirAll(destination, 0755); err != nil {
return nil, errors.Wrapf(err, "Error creating directory for image %s", destination)
return nil, fmt.Errorf("Error creating directory for image %s: %w", destination, err)
}
imageTransport = directory.Transport
default:
return nil, errors.Errorf("%q is not a valid destination transport", transport)
return nil, fmt.Errorf("%q is not a valid destination transport", transport)
}
logrus.Debugf("Destination for transport %q: %s", transport, destination)
destRef, err := imageTransport.ParseReference(destination)
if err != nil {
return nil, errors.Wrapf(err, "Cannot obtain a valid image reference for transport %q and reference %q", imageTransport.Name(), destination)
return nil, fmt.Errorf("Cannot obtain a valid image reference for transport %q and reference %q: %w", imageTransport.Name(), destination, err)
}
return destRef, nil
@@ -203,16 +221,8 @@ func getImageTags(ctx context.Context, sysCtx *types.SystemContext, repoRef refe
return nil, err // Should never happen for a reference with tag and no digest
}
tags, err := docker.GetRepositoryTags(ctx, sysCtx, dockerRef)
switch err := err.(type) {
case nil:
break
case docker.ErrUnauthorizedForCredentials:
// Some registries may decide to block the "list all tags" endpoint.
// Gracefully allow the sync to continue in this case.
logrus.Warnf("Registry disallows tag list retrieval: %s", err)
default:
return tags, errors.Wrapf(err, "Error determining repository tags for image %s", name)
if err != nil {
return nil, fmt.Errorf("Error determining repository tags for repo %s: %w", name, err)
}
return tags, nil
@@ -232,11 +242,15 @@ func imagesToCopyFromRepo(sys *types.SystemContext, repoRef reference.Named) ([]
for _, tag := range tags {
taggedRef, err := reference.WithTag(repoRef, tag)
if err != nil {
return nil, errors.Wrapf(err, "Error creating a reference for repository %s and tag %q", repoRef.Name(), tag)
logrus.WithFields(logrus.Fields{
"repo": repoRef.Name(),
"tag": tag,
}).Errorf("Error creating a tagged reference from registry tag list: %v", err)
continue
}
ref, err := docker.NewReference(taggedRef)
if err != nil {
return nil, errors.Wrapf(err, "Cannot obtain a valid image reference for transport %q and reference %s", docker.Transport.Name(), taggedRef.String())
return nil, fmt.Errorf("Cannot obtain a valid image reference for transport %q and reference %s: %w", docker.Transport.Name(), taggedRef.String(), err)
}
sourceReferences = append(sourceReferences, ref)
}
@@ -249,15 +263,15 @@ func imagesToCopyFromRepo(sys *types.SystemContext, repoRef reference.Named) ([]
// and any error encountered.
func imagesToCopyFromDir(dirPath string) ([]types.ImageReference, error) {
var sourceReferences []types.ImageReference
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
err := filepath.WalkDir(dirPath, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !info.IsDir() && info.Name() == "manifest.json" {
if !d.IsDir() && d.Name() == "manifest.json" {
dirname := filepath.Dir(path)
ref, err := directory.Transport.ParseReference(dirname)
if err != nil {
return errors.Wrapf(err, "Cannot obtain a valid image reference for transport %q and reference %q", directory.Transport.Name(), dirname)
return fmt.Errorf("Cannot obtain a valid image reference for transport %q and reference %q: %w", directory.Transport.Name(), dirname, err)
}
sourceReferences = append(sourceReferences, ref)
return filepath.SkipDir
@@ -267,7 +281,7 @@ func imagesToCopyFromDir(dirPath string) ([]types.ImageReference, error) {
if err != nil {
return sourceReferences,
errors.Wrapf(err, "Error walking the path %q", dirPath)
fmt.Errorf("Error walking the path %q: %w", dirPath, err)
}
return sourceReferences, nil
@@ -425,7 +439,7 @@ func imagesToCopy(source string, transport string, sourceCtx *types.SystemContex
}
named, err := reference.ParseNormalizedNamed(source) // May be a repository or an image.
if err != nil {
return nil, errors.Wrapf(err, "Cannot obtain a valid image reference for transport %q and reference %q", docker.Transport.Name(), source)
return nil, fmt.Errorf("Cannot obtain a valid image reference for transport %q and reference %q: %w", docker.Transport.Name(), source, err)
}
imageTagged := !reference.IsNameOnly(named)
logrus.WithFields(logrus.Fields{
@@ -435,7 +449,7 @@ func imagesToCopy(source string, transport string, sourceCtx *types.SystemContex
if imageTagged {
srcRef, err := docker.NewReference(named)
if err != nil {
return nil, errors.Wrapf(err, "Cannot obtain a valid image reference for transport %q and reference %q", docker.Transport.Name(), named.String())
return nil, fmt.Errorf("Cannot obtain a valid image reference for transport %q and reference %q: %w", docker.Transport.Name(), named.String(), err)
}
desc.ImageRefs = []types.ImageReference{srcRef}
} else {
@@ -444,7 +458,7 @@ func imagesToCopy(source string, transport string, sourceCtx *types.SystemContex
return descriptors, err
}
if len(desc.ImageRefs) == 0 {
return descriptors, errors.Errorf("No images to sync found in %q", source)
return descriptors, fmt.Errorf("No images to sync found in %q", source)
}
}
descriptors = append(descriptors, desc)
@@ -455,7 +469,7 @@ func imagesToCopy(source string, transport string, sourceCtx *types.SystemContex
}
if _, err := os.Stat(source); err != nil {
return descriptors, errors.Wrap(err, "Invalid source directory specified")
return descriptors, fmt.Errorf("Invalid source directory specified: %w", err)
}
desc.DirBasePath = source
var err error
@@ -464,7 +478,7 @@ func imagesToCopy(source string, transport string, sourceCtx *types.SystemContex
return descriptors, err
}
if len(desc.ImageRefs) == 0 {
return descriptors, errors.Errorf("No images to sync found in %q", source)
return descriptors, fmt.Errorf("No images to sync found in %q", source)
}
descriptors = append(descriptors, desc)
@@ -483,7 +497,7 @@ func imagesToCopy(source string, transport string, sourceCtx *types.SystemContex
descs, err := imagesToCopyFromRegistry(registryName, registryConfig, *sourceCtx)
if err != nil {
return descriptors, errors.Wrapf(err, "Failed to retrieve list of images from registry %q", registryName)
return descriptors, fmt.Errorf("Failed to retrieve list of images from registry %q: %w", registryName, err)
}
descriptors = append(descriptors, descs...)
}
@@ -492,7 +506,7 @@ func imagesToCopy(source string, transport string, sourceCtx *types.SystemContex
return descriptors, nil
}
func (opts *syncOptions) run(args []string, stdout io.Writer) error {
func (opts *syncOptions) run(args []string, stdout io.Writer) (retErr error) {
if len(args) != 2 {
return errorShouldDisplayUsage{errors.New("Exactly two arguments expected")}
}
@@ -500,9 +514,13 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
policyContext, err := opts.global.getPolicyContext()
if err != nil {
return errors.Wrapf(err, "Error loading trust policy")
return fmt.Errorf("Error loading trust policy: %w", err)
}
defer policyContext.Destroy()
defer func() {
if err := policyContext.Destroy(); err != nil {
retErr = noteCloseFailure(retErr, "tearing down policy context", err)
}
}()
// validate source and destination options
contains := func(val string, list []string) (_ bool) {
@@ -518,20 +536,22 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
return errors.New("A source transport must be specified")
}
if !contains(opts.source, []string{docker.Transport.Name(), directory.Transport.Name(), "yaml"}) {
return errors.Errorf("%q is not a valid source transport", opts.source)
return fmt.Errorf("%q is not a valid source transport", opts.source)
}
if len(opts.destination) == 0 {
return errors.New("A destination transport must be specified")
}
if !contains(opts.destination, []string{docker.Transport.Name(), directory.Transport.Name()}) {
return errors.Errorf("%q is not a valid destination transport", opts.destination)
return fmt.Errorf("%q is not a valid destination transport", opts.destination)
}
if opts.source == opts.destination && opts.source == directory.Transport.Name() {
return errors.New("sync from 'dir' to 'dir' not implemented, consider using rsync instead")
}
opts.destImage.warnAboutIneffectiveOptions(transports.Get(opts.destination))
imageListSelection := copy.CopySystemImage
if opts.all {
imageListSelection = copy.CopyAllImages
@@ -543,8 +563,8 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
}
var manifestType string
if opts.format.present {
manifestType, err = parseManifestFormat(opts.format.value)
if opts.format.Present() {
manifestType, err = parseManifestFormat(opts.format.Value())
if err != nil {
return err
}
@@ -555,7 +575,7 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
sourceArg := args[0]
var srcRepoList []repoDescriptor
if err = retry.RetryIfNecessary(ctx, func() error {
if err = retry.IfNecessary(ctx, func() error {
srcRepoList, err = imagesToCopy(sourceArg, opts.source, sourceCtx)
return err
}, opts.retryOpts); err != nil {
@@ -568,16 +588,62 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
return err
}
imagesNumber := 0
// c/image/copy.Image does allow creating both simple signing and sigstore signatures simultaneously,
// with independent passphrases, but that would make the CLI probably too confusing.
// For now, use the passphrase with either, but only one of them.
if opts.signPassphraseFile != "" && opts.signByFingerprint != "" && opts.signBySigstorePrivateKey != "" {
return fmt.Errorf("Only one of --sign-by and sign-by-sigstore-private-key can be used with sign-passphrase-file")
}
var passphrase string
if opts.signPassphraseFile != "" {
p, err := cli.ReadPassphraseFile(opts.signPassphraseFile)
if err != nil {
return err
}
passphrase = p
} else if opts.signBySigstorePrivateKey != "" {
p, err := promptForPassphrase(opts.signBySigstorePrivateKey, os.Stdin, os.Stdout)
if err != nil {
return err
}
passphrase = p
}
var signers []*signer.Signer
if opts.signBySigstoreParamFile != "" {
signer, err := sigstore.NewSignerFromParameterFile(opts.signBySigstoreParamFile, &sigstore.Options{
PrivateKeyPassphrasePrompt: func(keyFile string) (string, error) {
return promptForPassphrase(keyFile, os.Stdin, os.Stdout)
},
Stdin: os.Stdin,
Stdout: stdout,
})
if err != nil {
return fmt.Errorf("Error using --sign-by-sigstore: %w", err)
}
defer signer.Close()
signers = append(signers, signer)
}
options := copy.Options{
RemoveSignatures: opts.removeSignatures,
Signers: signers,
SignBy: opts.signByFingerprint,
ReportWriter: os.Stdout,
SignPassphrase: passphrase,
SignBySigstorePrivateKeyFile: opts.signBySigstorePrivateKey,
SignSigstorePrivateKeyPassphrase: []byte(passphrase),
ReportWriter: stdout,
DestinationCtx: destinationCtx,
ImageListSelection: imageListSelection,
PreserveDigests: opts.preserveDigests,
OptimizeDestinationImageAlreadyExists: true,
ForceManifestMIMEType: manifestType,
}
errorsPresent := false
imagesNumber := 0
if opts.dryRun {
logrus.Warn("Running in dry-run mode")
}
for _, srcRepo := range srcRepoList {
options.SourceCtx = srcRepo.Context
@@ -600,26 +666,44 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) error {
destSuffix = path.Base(destSuffix)
}
destRef, err := destinationReference(path.Join(destination, destSuffix), opts.destination)
destRef, err := destinationReference(path.Join(destination, destSuffix)+opts.appendSuffix, opts.destination)
if err != nil {
return err
}
logrus.WithFields(logrus.Fields{
fromToFields := logrus.Fields{
"from": transports.ImageName(ref),
"to": transports.ImageName(destRef),
}).Infof("Copying image ref %d/%d", counter+1, len(srcRepo.ImageRefs))
if err = retry.RetryIfNecessary(ctx, func() error {
_, err = copy.Image(ctx, policyContext, destRef, ref, &options)
return err
}, opts.retryOpts); err != nil {
return errors.Wrapf(err, "Error copying ref %q", transports.ImageName(ref))
}
if opts.dryRun {
logrus.WithFields(fromToFields).Infof("Would have copied image ref %d/%d", counter+1, len(srcRepo.ImageRefs))
} else {
logrus.WithFields(fromToFields).Infof("Copying image ref %d/%d", counter+1, len(srcRepo.ImageRefs))
if err = retry.IfNecessary(ctx, func() error {
_, err = copy.Image(ctx, policyContext, destRef, ref, &options)
return err
}, opts.retryOpts); err != nil {
if !opts.keepGoing {
return fmt.Errorf("Error copying ref %q: %w", transports.ImageName(ref), err)
}
// log the error, keep a note that there was a failure and move on to the next
// image ref
errorsPresent = true
logrus.WithError(err).Errorf("Error copying ref %q", transports.ImageName(ref))
continue
}
}
imagesNumber++
}
}
logrus.Infof("Synced %d images from %d sources", imagesNumber, len(srcRepoList))
return nil
if opts.dryRun {
logrus.Infof("Would have synced %d images from %d sources", imagesNumber, len(srcRepoList))
} else {
logrus.Infof("Synced %d images from %d sources", imagesNumber, len(srcRepoList))
}
if !errorsPresent {
return nil
}
return errors.New("Sync failed due to previous reported error(s) for one or more images")
}

View File

@@ -1,9 +1,10 @@
package main
import (
"fmt"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/storage/pkg/unshare"
"github.com/pkg/errors"
"github.com/syndtr/gocapability/capability"
)
@@ -22,7 +23,7 @@ func maybeReexec() error {
// if we already have the capabilities we need.
capabilities, err := capability.NewPid(0)
if err != nil {
return errors.Wrapf(err, "error reading the current capabilities sets")
return fmt.Errorf("error reading the current capabilities sets: %w", err)
}
for _, cap := range neededCapabilities {
if !capabilities.Get(capability.EFFECTIVE, cap) {

View File

@@ -2,21 +2,24 @@ package main
import (
"context"
"errors"
"fmt"
"io"
"os"
"strings"
commonFlag "github.com/containers/common/pkg/flag"
"github.com/containers/common/pkg/retry"
"github.com/containers/image/v5/directory"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/pkg/compression"
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"golang.org/x/term"
)
// errorShouldDisplayUsage is a subtype of error used by command handlers to indicate that cli.ShowSubcommandHelp should be called.
@@ -24,6 +27,27 @@ type errorShouldDisplayUsage struct {
error
}
// noteCloseFailure returns (possibly-nil) err modified to account for (non-nil) closeErr.
// The error for closeErr is annotated with description (which is not a format string)
// Typical usage:
//
// defer func() {
// if err := something.Close(); err != nil {
// returnedErr = noteCloseFailure(returnedErr, "closing something", err)
// }
// }
func noteCloseFailure(err error, description string, closeErr error) error {
// We dont accept a Closer() and close it ourselves because signature.PolicyContext has .Destroy(), not .Close().
// This also makes it harder for a caller to do
// defer noteCloseFailure(returnedErr, …)
// which doesnt use the right value of returnedErr, and doesnt update it.
if err == nil {
return fmt.Errorf("%s: %w", description, closeErr)
}
// In this case we prioritize the primary error for use with %w; closeErr is usually less relevant, or might be a consequence of the primary erorr.
return fmt.Errorf("%w (%s: %v)", err, description, closeErr)
}
// commandAction intermediates between the RunE interface and the real handler,
// primarily to ensure that cobra.Command is not available to the handler, which in turn
// makes sure that the cmd.Flags() etc. flag access functions are not used,
@@ -32,8 +56,9 @@ type errorShouldDisplayUsage struct {
func commandAction(handler func(args []string, stdout io.Writer) error) func(cmd *cobra.Command, args []string) error {
return func(c *cobra.Command, args []string) error {
err := handler(args, c.OutOrStdout())
if _, ok := err.(errorShouldDisplayUsage); ok {
c.Help()
var shouldDisplayUsage errorShouldDisplayUsage
if errors.As(err, &shouldDisplayUsage) {
return c.Help()
}
return err
}
@@ -45,7 +70,7 @@ func commandAction(handler func(args []string, stdout io.Writer) error) func(cmd
// whether or not the value actually ends up being used.
// DO NOT ADD ANY NEW USES OF THIS; just call dockerImageFlags with an appropriate, possibly empty, flagPrefix.
type deprecatedTLSVerifyOption struct {
tlsVerify optionalBool // FIXME FIXME: Warn if this is used, or even if it is ignored.
tlsVerify commonFlag.OptionalBool // FIXME FIXME: Warn if this is used, or even if it is ignored.
}
// warnIfUsed warns if tlsVerify was set by the user, and suggests alternatives (which should
@@ -53,7 +78,7 @@ type deprecatedTLSVerifyOption struct {
// Every user should call this as part of handling the CLI, whether or not the value actually
// ends up being used.
func (opts *deprecatedTLSVerifyOption) warnIfUsed(alternatives []string) {
if opts.tlsVerify.present {
if opts.tlsVerify.Present() {
logrus.Warnf("'--tls-verify' is deprecated, instead use: %s", strings.Join(alternatives, ", "))
}
}
@@ -63,7 +88,7 @@ func (opts *deprecatedTLSVerifyOption) warnIfUsed(alternatives []string) {
func deprecatedTLSVerifyFlags() (pflag.FlagSet, *deprecatedTLSVerifyOption) {
opts := deprecatedTLSVerifyOption{}
fs := pflag.FlagSet{}
flag := optionalBoolFlag(&fs, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the container registry (defaults to true)")
flag := commonFlag.OptionalBoolFlag(&fs, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the container registry")
flag.Hidden = true
return fs, &opts
}
@@ -89,11 +114,13 @@ type dockerImageOptions struct {
global *globalOptions // May be shared across several imageOptions instances.
shared *sharedImageOptions // May be shared across several imageOptions instances.
deprecatedTLSVerify *deprecatedTLSVerifyOption // May be shared across several imageOptions instances, or nil.
authFilePath optionalString // Path to a */containers/auth.json (prefixed version to override shared image option).
credsOption optionalString // username[:password] for accessing a registry
registryToken optionalString // token to be used directly as a Bearer token when accessing the registry
authFilePath commonFlag.OptionalString // Path to a */containers/auth.json (prefixed version to override shared image option).
credsOption commonFlag.OptionalString // username[:password] for accessing a registry
userName commonFlag.OptionalString // username for accessing a registry
password commonFlag.OptionalString // password for accessing a registry
registryToken commonFlag.OptionalString // token to be used directly as a Bearer token when accessing the registry
dockerCertPath string // A directory using Docker-like *.{crt,cert,key} files for connecting to a registry or a daemon
tlsVerify optionalBool // Require HTTPS and verify certificates (for docker: and docker-daemon:)
tlsVerify commonFlag.OptionalBool // Require HTTPS and verify certificates (for docker: and docker-daemon:)
noCreds bool // Access the registry anonymously
}
@@ -119,18 +146,20 @@ func dockerImageFlags(global *globalOptions, shared *sharedImageOptions, depreca
fs := pflag.FlagSet{}
if flagPrefix != "" {
// the non-prefixed flag is handled by a shared flag.
fs.Var(newOptionalStringValue(&flags.authFilePath), flagPrefix+"authfile", "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json")
fs.Var(commonFlag.NewOptionalStringValue(&flags.authFilePath), flagPrefix+"authfile", "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json")
}
fs.Var(newOptionalStringValue(&flags.credsOption), flagPrefix+"creds", "Use `USERNAME[:PASSWORD]` for accessing the registry")
fs.Var(commonFlag.NewOptionalStringValue(&flags.credsOption), flagPrefix+"creds", "Use `USERNAME[:PASSWORD]` for accessing the registry")
fs.Var(commonFlag.NewOptionalStringValue(&flags.userName), flagPrefix+"username", "Username for accessing the registry")
fs.Var(commonFlag.NewOptionalStringValue(&flags.password), flagPrefix+"password", "Password for accessing the registry")
if credsOptionAlias != "" {
// This is horribly ugly, but we need to support the old option forms of (skopeo copy) for compatibility.
// Don't add any more cases like this.
f := fs.VarPF(newOptionalStringValue(&flags.credsOption), credsOptionAlias, "", "Use `USERNAME[:PASSWORD]` for accessing the registry")
f := fs.VarPF(commonFlag.NewOptionalStringValue(&flags.credsOption), credsOptionAlias, "", "Use `USERNAME[:PASSWORD]` for accessing the registry")
f.Hidden = true
}
fs.Var(newOptionalStringValue(&flags.registryToken), flagPrefix+"registry-token", "Provide a Bearer token for accessing the registry")
fs.Var(commonFlag.NewOptionalStringValue(&flags.registryToken), flagPrefix+"registry-token", "Provide a Bearer token for accessing the registry")
fs.StringVar(&flags.dockerCertPath, flagPrefix+"cert-dir", "", "use certificates at `PATH` (*.crt, *.cert, *.key) to connect to the registry or daemon")
optionalBoolFlag(&fs, &flags.tlsVerify, flagPrefix+"tls-verify", "require HTTPS and verify certificates when talking to the container registry or daemon (defaults to true)")
commonFlag.OptionalBoolFlag(&fs, &flags.tlsVerify, flagPrefix+"tls-verify", "require HTTPS and verify certificates when talking to the container registry or daemon")
fs.BoolVar(&flags.noCreds, flagPrefix+"no-creds", false, "Access the registry anonymously")
return fs, &flags
}
@@ -146,8 +175,8 @@ func imageFlags(global *globalOptions, shared *sharedImageOptions, deprecatedTLS
return fs, opts
}
func retryFlags() (pflag.FlagSet, *retry.RetryOptions) {
opts := retry.RetryOptions{}
func retryFlags() (pflag.FlagSet, *retry.Options) {
opts := retry.Options{}
fs := pflag.FlagSet{}
fs.IntVar(&opts.MaxRetry, "retry-times", 0, "the number of times to possibly retry")
return fs, &opts
@@ -164,31 +193,49 @@ func (opts *imageOptions) newSystemContext() (*types.SystemContext, error) {
ctx.AuthFilePath = opts.shared.authFilePath
ctx.DockerDaemonHost = opts.dockerDaemonHost
ctx.DockerDaemonCertPath = opts.dockerCertPath
if opts.dockerImageOptions.authFilePath.present {
ctx.AuthFilePath = opts.dockerImageOptions.authFilePath.value
if opts.dockerImageOptions.authFilePath.Present() {
ctx.AuthFilePath = opts.dockerImageOptions.authFilePath.Value()
}
if opts.deprecatedTLSVerify != nil && opts.deprecatedTLSVerify.tlsVerify.present {
if opts.deprecatedTLSVerify != nil && opts.deprecatedTLSVerify.tlsVerify.Present() {
// If both this deprecated option and a non-deprecated option is present, we use the latter value.
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.deprecatedTLSVerify.tlsVerify.value)
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.deprecatedTLSVerify.tlsVerify.Value())
}
if opts.tlsVerify.present {
ctx.DockerDaemonInsecureSkipTLSVerify = !opts.tlsVerify.value
if opts.tlsVerify.Present() {
ctx.DockerDaemonInsecureSkipTLSVerify = !opts.tlsVerify.Value()
}
if opts.tlsVerify.present {
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.value)
if opts.tlsVerify.Present() {
ctx.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!opts.tlsVerify.Value())
}
if opts.credsOption.present && opts.noCreds {
if opts.credsOption.Present() && opts.noCreds {
return nil, errors.New("creds and no-creds cannot be specified at the same time")
}
if opts.credsOption.present {
if opts.userName.Present() && opts.noCreds {
return nil, errors.New("username and no-creds cannot be specified at the same time")
}
if opts.credsOption.Present() && opts.userName.Present() {
return nil, errors.New("creds and username cannot be specified at the same time")
}
// if any of username or password is present, then both are expected to be present
if opts.userName.Present() != opts.password.Present() {
if opts.userName.Present() {
return nil, errors.New("password must be specified when username is specified")
}
return nil, errors.New("username must be specified when password is specified")
}
if opts.credsOption.Present() {
var err error
ctx.DockerAuthConfig, err = getDockerAuth(opts.credsOption.value)
ctx.DockerAuthConfig, err = getDockerAuth(opts.credsOption.Value())
if err != nil {
return nil, err
}
} else if opts.userName.Present() {
ctx.DockerAuthConfig = &types.DockerAuthConfig{
Username: opts.userName.Value(),
Password: opts.password.Value(),
}
}
if opts.registryToken.present {
ctx.DockerBearerRegistryToken = opts.registryToken.value
if opts.registryToken.Present() {
ctx.DockerBearerRegistryToken = opts.registryToken.Value()
}
if opts.noCreds {
ctx.DockerAuthConfig = &types.DockerAuthConfig{}
@@ -198,26 +245,30 @@ func (opts *imageOptions) newSystemContext() (*types.SystemContext, error) {
}
// imageDestOptions is a superset of imageOptions specialized for image destinations.
// Every user should call imageDestOptions.warnAboutIneffectiveOptions() as part of handling the CLI
type imageDestOptions struct {
*imageOptions
dirForceCompression bool // Compress layers when saving to the dir: transport
dirForceDecompression bool // Decompress layers when saving to the dir: transport
ociAcceptUncompressedLayers bool // Whether to accept uncompressed layers in the oci: transport
compressionFormat string // Format to use for the compression
compressionLevel optionalInt // Level to use for the compression
dirForceCompression bool // Compress layers when saving to the dir: transport
dirForceDecompression bool // Decompress layers when saving to the dir: transport
ociAcceptUncompressedLayers bool // Whether to accept uncompressed layers in the oci: transport
compressionFormat string // Format to use for the compression
compressionLevel commonFlag.OptionalInt // Level to use for the compression
precomputeDigests bool // Precompute digests to dedup layers when saving to the docker: transport
imageDestFlagPrefix string
}
// imageDestFlags prepares a collection of CLI flags writing into imageDestOptions, and the managed imageDestOptions structure.
func imageDestFlags(global *globalOptions, shared *sharedImageOptions, deprecatedTLSVerify *deprecatedTLSVerifyOption, flagPrefix, credsOptionAlias string) (pflag.FlagSet, *imageDestOptions) {
genericFlags, genericOptions := imageFlags(global, shared, deprecatedTLSVerify, flagPrefix, credsOptionAlias)
opts := imageDestOptions{imageOptions: genericOptions}
opts := imageDestOptions{imageOptions: genericOptions, imageDestFlagPrefix: flagPrefix}
fs := pflag.FlagSet{}
fs.AddFlagSet(&genericFlags)
fs.BoolVar(&opts.dirForceCompression, flagPrefix+"compress", false, "Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source)")
fs.BoolVar(&opts.dirForceDecompression, flagPrefix+"decompress", false, "Decompress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source)")
fs.BoolVar(&opts.ociAcceptUncompressedLayers, flagPrefix+"oci-accept-uncompressed-layers", false, "Allow uncompressed image layers when saving to an OCI image using the 'oci' transport. (default is to compress things that aren't compressed)")
fs.StringVar(&opts.compressionFormat, flagPrefix+"compress-format", "", "`FORMAT` to use for the compression")
fs.Var(newOptionalIntValue(&opts.compressionLevel), flagPrefix+"compress-level", "`LEVEL` to use for the compression")
fs.Var(commonFlag.NewOptionalIntValue(&opts.compressionLevel), flagPrefix+"compress-level", "`LEVEL` to use for the compression")
fs.BoolVar(&opts.precomputeDigests, flagPrefix+"precompute-digests", false, "Precompute digests to prevent uploading layers already on the registry using the 'docker' transport.")
return fs, &opts
}
@@ -239,12 +290,27 @@ func (opts *imageDestOptions) newSystemContext() (*types.SystemContext, error) {
}
ctx.CompressionFormat = &cf
}
if opts.compressionLevel.present {
ctx.CompressionLevel = &opts.compressionLevel.value
if opts.compressionLevel.Present() {
value := opts.compressionLevel.Value()
ctx.CompressionLevel = &value
}
ctx.DockerRegistryPushPrecomputeDigests = opts.precomputeDigests
return ctx, err
}
// warnAboutIneffectiveOptions warns if any ineffective option was set by the user
// Every user should call this as part of handling the CLI
func (opts *imageDestOptions) warnAboutIneffectiveOptions(destTransport types.ImageTransport) {
if destTransport.Name() != directory.Transport.Name() {
if opts.dirForceCompression {
logrus.Warnf("--%s can only be used if the destination transport is 'dir'", opts.imageDestFlagPrefix+"compress")
}
if opts.dirForceDecompression {
logrus.Warnf("--%s can only be used if the destination transport is 'dir'", opts.imageDestFlagPrefix+"decompress")
}
}
}
func parseCreds(creds string) (string, string, error) {
if creds == "" {
return "", "", errors.New("credentials can't be empty")
@@ -327,3 +393,19 @@ func adjustUsage(c *cobra.Command) {
c.SetUsageTemplate(usageTemplate)
c.DisableFlagsInUseLine = true
}
// promptForPassphrase interactively prompts for a passphrase related to privateKeyFile
func promptForPassphrase(privateKeyFile string, stdin, stdout *os.File) (string, error) {
stdinFd := int(stdin.Fd())
if !term.IsTerminal(stdinFd) {
return "", fmt.Errorf("Cannot prompt for a passphrase for key %s, standard input is not a TTY", privateKeyFile)
}
fmt.Fprintf(stdout, "Passphrase for key %s: ", privateKeyFile)
passphrase, err := term.ReadPassword(stdinFd)
if err != nil {
return "", fmt.Errorf("Error reading password: %w", err)
}
fmt.Fprintf(stdout, "\n")
return string(passphrase), nil
}

View File

@@ -1,7 +1,7 @@
package main
import (
"os"
"errors"
"testing"
"github.com/containers/image/v5/manifest"
@@ -13,6 +13,27 @@ import (
"github.com/stretchr/testify/require"
)
func TestNoteCloseFailure(t *testing.T) {
const description = "description"
mainErr := errors.New("main")
closeErr := errors.New("closing")
// Main success, closing failed
res := noteCloseFailure(nil, description, closeErr)
require.NotNil(t, res)
assert.Contains(t, res.Error(), description)
assert.Contains(t, res.Error(), closeErr.Error())
// Both main and closing failed
res = noteCloseFailure(mainErr, description, closeErr)
require.NotNil(t, res)
assert.Contains(t, res.Error(), mainErr.Error())
assert.Contains(t, res.Error(), description)
assert.Contains(t, res.Error(), closeErr.Error())
assert.ErrorIs(t, res, mainErr)
}
// fakeGlobalOptions creates globalOptions and sets it according to flags.
func fakeGlobalOptions(t *testing.T, flags []string) (*globalOptions, *cobra.Command) {
app, opts := createApp()
@@ -128,17 +149,9 @@ func TestImageDestOptionsNewSystemContext(t *testing.T) {
DockerRegistryUserAgent: defaultUserAgent,
}, res)
oldXRD, hasXRD := os.LookupEnv("REGISTRY_AUTH_FILE")
defer func() {
if hasXRD {
os.Setenv("REGISTRY_AUTH_FILE", oldXRD)
} else {
os.Unsetenv("REGISTRY_AUTH_FILE")
}
}()
authFile := "/tmp/auth.json"
// Make sure when REGISTRY_AUTH_FILE is set the auth file is used
os.Setenv("REGISTRY_AUTH_FILE", authFile)
t.Setenv("REGISTRY_AUTH_FILE", authFile)
// Explicitly set everything to default, except for when the default is “not present”
opts = fakeImageDestOptions(t, "dest-", true, []string{}, []string{
@@ -167,26 +180,28 @@ func TestImageDestOptionsNewSystemContext(t *testing.T) {
"--dest-tls-verify=false",
"--dest-creds", "creds-user:creds-password",
"--dest-registry-token", "faketoken",
"--dest-precompute-digests=true",
})
res, err = opts.newSystemContext()
require.NoError(t, err)
assert.Equal(t, &types.SystemContext{
RegistriesDirPath: "/srv/registries.d",
AuthFilePath: "/srv/authfile",
ArchitectureChoice: "overridden-arch",
OSChoice: "overridden-os",
VariantChoice: "overridden-variant",
OCISharedBlobDirPath: "/srv/shared-blob-dir",
DockerCertPath: "/srv/cert-dir",
DockerInsecureSkipTLSVerify: types.OptionalBoolTrue,
DockerAuthConfig: &types.DockerAuthConfig{Username: "creds-user", Password: "creds-password"},
DockerBearerRegistryToken: "faketoken",
DockerDaemonCertPath: "/srv/cert-dir",
DockerDaemonHost: "daemon-host.example.com",
DockerDaemonInsecureSkipTLSVerify: true,
DockerRegistryUserAgent: defaultUserAgent,
DirForceCompress: true,
BigFilesTemporaryDir: "/srv",
RegistriesDirPath: "/srv/registries.d",
AuthFilePath: "/srv/authfile",
ArchitectureChoice: "overridden-arch",
OSChoice: "overridden-os",
VariantChoice: "overridden-variant",
OCISharedBlobDirPath: "/srv/shared-blob-dir",
DockerCertPath: "/srv/cert-dir",
DockerInsecureSkipTLSVerify: types.OptionalBoolTrue,
DockerAuthConfig: &types.DockerAuthConfig{Username: "creds-user", Password: "creds-password"},
DockerBearerRegistryToken: "faketoken",
DockerDaemonCertPath: "/srv/cert-dir",
DockerDaemonHost: "daemon-host.example.com",
DockerDaemonInsecureSkipTLSVerify: true,
DockerRegistryUserAgent: defaultUserAgent,
DirForceCompress: true,
BigFilesTemporaryDir: "/srv",
DockerRegistryPushPrecomputeDigests: true,
}, res)
// Global/per-command tlsVerify behavior is tested in TestTLSVerifyFlags.
@@ -197,6 +212,54 @@ func TestImageDestOptionsNewSystemContext(t *testing.T) {
assert.Error(t, err)
}
// TestImageOptionsUsernamePassword verifies that using the username and password
// options works as expected
func TestImageOptionsUsernamePassword(t *testing.T) {
for _, command := range []struct {
commandArgs []string
expectedAuthConfig *types.DockerAuthConfig // data to expect, or nil if an error is expected
}{
// Set only username/password (without --creds), expected to pass
{
commandArgs: []string{"--dest-username", "foo", "--dest-password", "bar"},
expectedAuthConfig: &types.DockerAuthConfig{Username: "foo", Password: "bar"},
},
// no username but set password, expect error
{
commandArgs: []string{"--dest-password", "foo"},
expectedAuthConfig: nil,
},
// set username but no password. expected to fail (we currently don't allow a user without password)
{
commandArgs: []string{"--dest-username", "bar"},
expectedAuthConfig: nil,
},
// set username with --creds, expected to fail
{
commandArgs: []string{"--dest-username", "bar", "--dest-creds", "hello:world", "--dest-password", "foo"},
expectedAuthConfig: nil,
},
// set username with --no-creds, expected to fail
{
commandArgs: []string{"--dest-username", "bar", "--dest-no-creds", "--dest-password", "foo"},
expectedAuthConfig: nil,
},
} {
opts := fakeImageDestOptions(t, "dest-", true, []string{}, command.commandArgs)
// parse the command options
res, err := opts.newSystemContext()
if command.expectedAuthConfig == nil {
assert.Error(t, err)
} else {
require.NoError(t, err)
assert.Equal(t, &types.SystemContext{
DockerRegistryUserAgent: defaultUserAgent,
DockerAuthConfig: command.expectedAuthConfig,
}, res)
}
}
}
func TestTLSVerifyFlags(t *testing.T) {
type systemContextOpts interface { // Either *imageOptions or *imageDestOptions
newSystemContext() (*types.SystemContext, error)

View File

@@ -1,316 +0,0 @@
#! /bin/bash
_complete_() {
local options_with_args=$1
local boolean_options="$2 -h --help"
local transports=$3
local option_with_args
for option_with_args in $options_with_args $transports
do
if [ "$option_with_args" == "$prev" ] || [ "$option_with_args" == "$cur" ]
then
return
fi
done
case "$cur" in
-*)
while IFS='' read -r line; do COMPREPLY+=("$line"); done < <(compgen -W "$boolean_options $options_with_args" -- "$cur")
;;
*)
if [ -n "$transports" ]
then
compopt -o nospace
while IFS='' read -r line; do COMPREPLY+=("$line"); done < <(compgen -W "$transports" -- "$cur")
fi
;;
esac
}
_skopeo_supported_transports() {
local subcommand=$1
skopeo "$subcommand" --help | grep "Supported transports" -A 1 | tail -n 1 | sed -e 's/,/:/g' -e 's/$/:/'
}
_skopeo_copy() {
local options_with_args="
--authfile
--src-authfile
--dest-authfile
--format -f
--sign-by
--src-creds --screds
--src-cert-dir
--src-tls-verify
--dest-creds --dcreds
--dest-cert-dir
--dest-tls-verify
--src-daemon-host
--dest-daemon-host
--src-registry-token
--dest-registry-token
"
local boolean_options="
--all
--dest-compress
--dest-decompress
--remove-signatures
--src-no-creds
--dest-no-creds
--dest-oci-accept-uncompressed-layers
"
local transports
transports="
$(_skopeo_supported_transports "${FUNCNAME//"_skopeo_"/}")
"
_complete_ "$options_with_args" "$boolean_options" "$transports"
}
_skopeo_sync() {
local options_with_args="
--authfile
--dest
--dest-authfile
--dest-cert-
--dest-creds
--dest-registry-token string
--format
--retry-times
--sign-by
--src
--src-authfile
--src-cert-dir
--src-creds
--src-registry-token
"
local boolean_options="
--all
--dest-no-creds
--dest-tls-verify
--remove-signatures
--scoped
--src-no-creds
--src-tls-verify
"
local transports
transports="
$(_skopeo_supported_transports "${FUNCNAME//"_skopeo_"/}")
"
_complete_ "$options_with_args" "$boolean_options" "$transports"
}
_skopeo_inspect() {
local options_with_args="
--authfile
--creds
--cert-dir
--format
--retry-times
--registry-token
"
local boolean_options="
--config
--raw
--tls-verify
--no-creds
"
local transports
transports="
$(_skopeo_supported_transports "${FUNCNAME//"_skopeo_"/}")
"
_complete_ "$options_with_args" "$boolean_options" "$transports"
}
_skopeo_standalone_sign() {
local options_with_args="
-o --output
"
local boolean_options="
"
_complete_ "$options_with_args" "$boolean_options"
}
_skopeo_standalone_verify() {
local options_with_args="
"
local boolean_options="
"
_complete_ "$options_with_args" "$boolean_options"
}
_skopeo_manifest_digest() {
local options_with_args="
"
local boolean_options="
"
_complete_ "$options_with_args" "$boolean_options"
}
_skopeo_delete() {
local options_with_args="
--authfile
--creds
--cert-dir
--registry-token
"
local boolean_options="
--tls-verify
--no-creds
"
local transports
transports="
$(_skopeo_supported_transports "${FUNCNAME//"_skopeo_"/}")
"
_complete_ "$options_with_args" "$boolean_options" "$transports"
}
_skopeo_layers() {
local options_with_args="
--authfile
--creds
--cert-dir
--registry-token
"
local boolean_options="
--tls-verify
--no-creds
"
_complete_ "$options_with_args" "$boolean_options"
}
_skopeo_list_repository_tags() {
local options_with_args="
--authfile
--creds
--cert-dir
--registry-token
"
local boolean_options="
--tls-verify
--no-creds
"
_complete_ "$options_with_args" "$boolean_options"
}
_skopeo_login() {
local options_with_args="
--authfile
--cert-dir
--password -p
--username -u
"
local boolean_options="
--get-login
--tls-verify
--password-stdin
"
_complete_ "$options_with_args" "$boolean_options"
}
_skopeo_logout() {
local options_with_args="
--authfile
"
local boolean_options="
--all -a
"
_complete_ "$options_with_args" "$boolean_options"
}
_skopeo_skopeo() {
# XXX: Changes here need to be reflected in the manually expanded
# string in the `case` statement below as well.
local options_with_args="
--policy
--registries.d
--override-arch
--override-os
--override-variant
--command-timeout
--tmpdir
"
local boolean_options="
--insecure-policy
--debug
--version -v
--help -h
"
local commands=(
copy
delete
inspect
list-tags
login
logout
manifest-digest
standalone-sign
standalone-verify
sync
help
h
)
case "$prev" in
# XXX: Changes here need to be reflected in $options_with_args as well.
--policy|--registries.d|--override-arch|--override-os|--override-variant|--command-timeout)
return
;;
esac
case "$cur" in
-*)
while IFS='' read -r line; do COMPREPLY+=("$line"); done < <(compgen -W "$boolean_options $options_with_args" -- "$cur")
;;
*)
while IFS='' read -r line; do COMPREPLY+=("$line"); done < <(compgen -W "${commands[*]} help" -- "$cur")
;;
esac
}
_cli_bash_autocomplete() {
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY=()
local cur prev words cword
_get_comp_words_by_ref -n : cur prev words cword
local command="skopeo" cpos=0
local counter=1
while [ $counter -lt "$cword" ]; do
case "${words[$counter]}" in
skopeo|copy|sync|inspect|delete|manifest-digest|standalone-sign|standalone-verify|help|h|list-repository-tags)
command="${words[$counter]//-/_}"
cpos=$counter
(( cpos++ ))
break
;;
esac
(( counter++ ))
done
local completions_func=_skopeo_${command}
declare -F "$completions_func" >/dev/null && $completions_func
return 0
}
complete -F _cli_bash_autocomplete skopeo

View File

@@ -0,0 +1,15 @@
ARG BASE_FQIN=quay.io/coreos-assembler/fcos-buildroot:testing-devel
FROM $BASE_FQIN
# See 'Danger of using COPY and ADD instructions'
# at https://cirrus-ci.org/guide/docker-builder-vm/#dockerfile-as-a-ci-environment
# Provide easy way to force-invalidate image cache by .cirrus.yml change
ARG CIRRUS_IMAGE_VERSION
ENV CIRRUS_IMAGE_VERSION=$CIRRUS_IMAGE_VERSION
ADD https://sh.rustup.rs /var/tmp/rustup_installer.sh
RUN dnf erase -y rust && \
chmod +x /var/tmp/rustup_installer.sh && \
/var/tmp/rustup_installer.sh -y --default-toolchain stable --profile minimal
ENV PATH=/root/.cargo/bin:/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

View File

@@ -6,6 +6,30 @@
set -e
_EOL=20270501
if [[ $(date +%Y%m%d) -ge $_EOL ]]; then
die "As of $_EOL this branch is probably
no longer supported in RHEL 9.2/8.8, please
confirm this with RHEL Program Management. If so:
It should be removed from Cirrus-Cron,
the .cirrus.yml file removed, and
the VM images (manually) unmarked
'permanent=true'"
fi
# BEGIN Global export of all variables
set -a
# Due to differences across platforms and runtime execution environments,
# handling of the (otherwise) default shell setup is non-uniform. Rather
# than attempt to workaround differences, simply force-load/set required
# items every time this library is utilized.
USER="$(whoami)"
HOME="$(getent passwd $USER | cut -d : -f 6)"
# Some platforms set and make this read-only
[[ -n "$UID" ]] || \
UID=$(getent passwd $USER | cut -d : -f 3)
if [[ -r "/etc/automation_environment" ]]; then
source /etc/automation_environment
source $AUTOMATION_LIB_PATH/common_lib.sh
@@ -17,48 +41,78 @@ else
) > /dev/stderr
fi
OS_RELEASE_ID="$(source /etc/os-release; echo $ID)"
# GCE image-name compatible string representation of distribution _major_ version
OS_RELEASE_VER="$(source /etc/os-release; echo $VERSION_ID | tr -d '.')"
# Combined to ease some usage
OS_REL_VER="${OS_RELEASE_ID}-${OS_RELEASE_VER}"
# This is the magic interpreted by the tests to allow modifying local config/services.
SKOPEO_CONTAINER_TESTS=1
export "PATH=$PATH:$GOPATH/bin"
PATH=$PATH:$GOPATH/bin
# END Global export of all variables
set +a
podmanmake() {
req_env_vars GOPATH SKOPEO_PATH SKOPEO_CI_CONTAINER_FQIN
warn "Accumulated technical-debt requires execution inside a --privileged container. This is very likely hiding bugs!"
showrun podman run -it --rm --privileged \
-e GOPATH=$GOPATH \
-v $GOPATH:$GOPATH:Z \
-w $SKOPEO_PATH \
$SKOPEO_CI_CONTAINER_FQIN \
make "$@"
}
_run_setup() {
if [[ "$OS_RELEASE_ID" == "fedora" ]]; then
# This is required as part of the standard Fedora VM setup
growpart /dev/sda 1
resize2fs /dev/sda1
# VM's come with the distro. skopeo pre-installed
dnf erase -y skopeo
else
local mnt
local errmsg
req_env_vars SKOPEO_CIDEV_CONTAINER_FQIN
if [[ "$OS_RELEASE_ID" != "fedora" ]]; then
die "Unknown/unsupported distro. $OS_REL_VER"
fi
if [[ -r "/.ci_setup_complete" ]]; then
warn "Thwarted an attempt to execute setup more than once."
return
fi
# VM's come with the distro. skopeo package pre-installed
dnf erase -y skopeo
msg "Removing systemd-resolved from nsswitch.conf"
# /etc/resolv.conf is already set to bypass systemd-resolvd
sed -i -r -e 's/^(hosts.+)resolve.+dns/\1dns/' /etc/nsswitch.conf
# A slew of compiled binaries are pre-built and distributed
# within the CI/Dev container image, but we want to run
# things directly on the host VM. Fortunately they're all
# located in the container under /usr/local/bin
msg "Accessing contents of $SKOPEO_CIDEV_CONTAINER_FQIN"
podman pull --quiet $SKOPEO_CIDEV_CONTAINER_FQIN
mnt=$(podman mount $(podman create $SKOPEO_CIDEV_CONTAINER_FQIN))
# The container and VM images are built in tandem in the same repo.
# automation, but the sources are in different directories. It's
# possible for a mismatch to happen, but should (hopefully) be unlikely.
# Double-check to make sure.
if ! fgrep -qx "ID=$OS_RELEASE_ID" $mnt/etc/os-release || \
! fgrep -qx "VERSION_ID=$OS_RELEASE_VER" $mnt/etc/os-release; then
die "Somehow $SKOPEO_CIDEV_CONTAINER_FQIN is not based on $OS_REL_VER."
fi
msg "Copying test binaries from $SKOPEO_CIDEV_CONTAINER_FQIN /usr/local/bin/"
cp -a "$mnt/usr/local/bin/"* "/usr/local/bin/"
msg "Configuring the openshift registry"
# TODO: Put directory & yaml into more sensible place + update integration tests
mkdir -vp /registry
cp -a "$mnt/atomic-registry-config.yml" /
msg "Cleaning up"
podman umount --latest
podman rm --latest
# Ensure setup can only run once
touch "/.ci_setup_complete"
}
_run_vendor() {
podmanmake vendor BUILDTAGS="$BUILDTAGS"
make vendor BUILDTAGS="$BUILDTAGS"
}
_run_build() {
make bin/skopeo BUILDTAGS="$BUILDTAGS"
make install PREFIX=/usr/local
}
_run_cross() {
podmanmake local-cross BUILDTAGS="$BUILDTAGS"
make local-cross BUILDTAGS="$BUILDTAGS"
}
_run_doccheck() {
@@ -66,18 +120,23 @@ _run_doccheck() {
}
_run_unit() {
podmanmake test-unit-local BUILDTAGS="$BUILDTAGS"
make test-unit-local BUILDTAGS="$BUILDTAGS"
}
_podman_reset() {
# Ensure we start with a clean-slate
showrun podman system reset --force
}
_run_integration() {
podmanmake test-integration-local BUILDTAGS="$BUILDTAGS"
_podman_reset
make test-integration-local BUILDTAGS="$BUILDTAGS"
}
_run_system() {
# Ensure we start with a clean-slate
podman system reset --force
# Executes with containers required for testing.
showrun make test-system-local BUILDTAGS="$BUILDTAGS"
_podman_reset
##### Note: Test MODIFIES THE HOST SETUP #####
make test-system-local BUILDTAGS="$BUILDTAGS"
}
req_env_vars SKOPEO_PATH BUILDTAGS

View File

@@ -1,4 +1,17 @@
<img src="https://cdn.rawgit.com/containers/skopeo/master/docs/skopeo.svg" width="250">
[comment]: <> (***ATTENTION*** ***WARNING*** ***ALERT*** ***CAUTION*** ***DANGER***)
[comment]: <> ()
[comment]: <> (ANY changes made to this file, once commited/merged must)
[comment]: <> (be manually copy/pasted -in markdown- into the description)
[comment]: <> (field on Quay at the following locations:)
[comment]: <> ()
[comment]: <> (https://quay.io/repository/containers/skopeo)
[comment]: <> (https://quay.io/repository/skopeo/stable)
[comment]: <> (https://quay.io/repository/skopeo/testing)
[comment]: <> (https://quay.io/repository/skopeo/upstream)
[comment]: <> ()
[comment]: <> (***ATTENTION*** ***WARNING*** ***ALERT*** ***CAUTION*** ***DANGER***)
<img src="https://cdn.rawgit.com/containers/skopeo/main/docs/skopeo.svg" width="250">
----
@@ -6,7 +19,7 @@
## Overview
This directory contains the Dockerfiles necessary to create the skopeoimage container
This directory contains the Containerfiles necessary to create the skopeoimage container
images that are housed on quay.io under the skopeo account. All repositories where
the images live are public and can be pulled without credentials. These container images are secured and the
resulting containers can run safely with privileges within the container.
@@ -18,22 +31,23 @@ default to `/`.
The container images are:
* `quay.io/containers/skopeo:<version>` and `quay.io/skopeo/stable:<version>` -
These images are built when a new Skopeo version becomes available in
Fedora. These images are intended to be unchanging and stable, they will
never be updated by automation once they've been pushed. For build details,
please [see the configuration file](stable/Dockerfile).
* `quay.io/containers/skopeo:v<version>` and `quay.io/skopeo/stable:v<version>` -
These images are built daily. These images are intended contain an unchanging
and stable version of skopeo. For the most recent `<version>` tags (`vX`,
`vX.Y`, and `vX.Y.Z`) the image contents will be updated daily to incorporate
(especially) security updates. For build details, please[see the configuration
file](stable/Containerfile).
* `quay.io/containers/skopeo:latest` and `quay.io/skopeo/stable:latest` -
Built daily using the same Dockerfile as above. The skopeo version
will remain the "latest" available in Fedora, however the image
Built daily using the same Containerfile as above. The skopeo version
will remain the "latest" available in Fedora, however the other image
contents may vary compared to the version-tagged images.
* `quay.io/skopeo/testing:latest` - This image is built daily, using the
latest version of Skopeo that was in the Fedora `updates-testing` repository.
The image is Built with [the testing Dockerfile](testing/Dockerfile).
The image is Built with [the testing Containerfile](testing/Containerfile).
* `quay.io/skopeo/upstream:latest` - This image is built daily using the latest
code found in this GitHub repository. Due to the image changing frequently,
it's not guaranteed to be stable or even executable. The image is built with
[the upstream Dockerfile](upstream/Dockerfile).
[the upstream Containerfile](upstream/Containerfile).
## Sample Usage

View File

@@ -0,0 +1,47 @@
# stable/Containerfile
#
# Build a Skopeo container image from the latest
# stable version of Skopeo on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=skopeo
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM registry.fedoraproject.org/fedora:latest
# Don't include container-selinux and remove
# directories used by dnf that are just taking
# up space.
# TODO: rpm --setcaps... needed due to Fedora (base) image builds
# being (maybe still?) affected by
# https://bugzilla.redhat.com/show_bug.cgi?id=1995337#c3
RUN dnf -y update && \
rpm --setcaps shadow-utils 2>/dev/null && \
dnf -y install skopeo fuse-overlayfs \
--exclude container-selinux && \
dnf clean all && \
rm -rf /var/cache /var/log/dnf* /var/log/yum.*
RUN useradd skopeo && \
echo skopeo:100000:65536 > /etc/subuid && \
echo skopeo:100000:65536 > /etc/subgid
# Copy & modify the defaults to provide reference if runtime changes needed.
# Changes here are required for running with fuse-overlay storage inside container.
RUN sed -e 's|^#mount_program|mount_program|g' \
-e '/additionalimage.*/a "/var/lib/shared",' \
-e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' \
/usr/share/containers/storage.conf \
> /etc/containers/storage.conf
# Setup the ability to use additional stores
# with this container image.
RUN mkdir -p /var/lib/shared/overlay-images \
/var/lib/shared/overlay-layers && \
touch /var/lib/shared/overlay-images/images.lock && \
touch /var/lib/shared/overlay-layers/layers.lock
# Point to the Authorization file
ENV REGISTRY_AUTH_FILE=/tmp/auth.json
# Set the entrypoint
ENTRYPOINT ["/usr/bin/skopeo"]

View File

@@ -1,33 +0,0 @@
# stable/Dockerfile
#
# Build a Skopeo container image from the latest
# stable version of Skopeo on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=skopeo
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM registry.fedoraproject.org/fedora:33
# Don't include container-selinux and remove
# directories used by yum that are just taking
# up space. Also reinstall shadow-utils as without
# doing so, the setuid/setgid bits on newuidmap
# and newgidmap are lost in the Fedora images.
RUN useradd skopeo; yum -y update; yum -y reinstall shadow-utils; yum -y install skopeo fuse-overlayfs --exclude container-selinux; yum -y clean all; rm -rf /var/cache/dnf/* /var/log/dnf* /var/log/yum*
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf
# Setup the ability to use additional stores
# with this container image.
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
# Setup skopeo's uid/guid entries
RUN echo skopeo:100000:65536 > /etc/subuid
RUN echo skopeo:100000:65536 > /etc/subgid
# Point to the Authorization file
ENV REGISTRY_AUTH_FILE=/tmp/auth.json
# Set the entrypoint
ENTRYPOINT ["/usr/bin/skopeo"]

View File

@@ -0,0 +1,49 @@
# testing/Containerfile
#
# Build a Skopeo container image from the latest
# version of Skopeo that is in updates-testing
# on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=skopeo
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM registry.fedoraproject.org/fedora:latest
# Don't include container-selinux and remove
# directories used by dnf that are just taking
# up space.
# TODO: rpm --setcaps... needed due to Fedora (base) image builds
# being (maybe still?) affected by
# https://bugzilla.redhat.com/show_bug.cgi?id=1995337#c3
RUN dnf -y update && \
rpm --setcaps shadow-utils 2>/dev/null && \
dnf -y install skopeo fuse-overlayfs \
--exclude container-selinux \
--enablerepo updates-testing && \
dnf clean all && \
rm -rf /var/cache /var/log/dnf* /var/log/yum.*
RUN useradd skopeo && \
echo skopeo:100000:65536 > /etc/subuid && \
echo skopeo:100000:65536 > /etc/subgid
# Copy & modify the defaults to provide reference if runtime changes needed.
# Changes here are required for running with fuse-overlay storage inside container.
RUN sed -e 's|^#mount_program|mount_program|g' \
-e '/additionalimage.*/a "/var/lib/shared",' \
-e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' \
/usr/share/containers/storage.conf \
> /etc/containers/storage.conf
# Setup the ability to use additional stores
# with this container image.
RUN mkdir -p /var/lib/shared/overlay-images \
/var/lib/shared/overlay-layers && \
touch /var/lib/shared/overlay-images/images.lock && \
touch /var/lib/shared/overlay-layers/layers.lock
# Point to the Authorization file
ENV REGISTRY_AUTH_FILE=/tmp/auth.json
# Set the entrypoint
ENTRYPOINT ["/usr/bin/skopeo"]

View File

@@ -1,34 +0,0 @@
# testing/Dockerfile
#
# Build a Skopeo container image from the latest
# version of Skopeo that is in updates-testing
# on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=skopeo
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM registry.fedoraproject.org/fedora:33
# Don't include container-selinux and remove
# directories used by yum that are just taking
# up space. Also reinstall shadow-utils as without
# doing so, the setuid/setgid bits on newuidmap
# and newgidmap are lost in the Fedora images.
RUN useradd skopeo; yum -y update; yum -y reinstall shadow-utils; yum -y install skopeo fuse-overlayfs --enablerepo updates-testing --exclude container-selinux; yum -y clean all; rm -rf /var/cache/dnf/* /var/log/dnf* /var/log/yum*
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf
# Setup the ability to use additional stores
# with this container image.
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
# Setup skopeo's uid/guid entries
RUN echo skopeo:100000:65536 > /etc/subuid
RUN echo skopeo:100000:65536 > /etc/subgid
# Point to the Authorization file
ENV REGISTRY_AUTH_FILE=/tmp/auth.json
# Set the entrypoint
ENTRYPOINT ["/usr/bin/skopeo"]

View File

@@ -0,0 +1,50 @@
# upstream/Containerfile
#
# Build a Skopeo container image from the latest
# upstream version of Skopeo on GitHub.
# https://github.com/containers/skopeo
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM registry.fedoraproject.org/fedora:latest
# Don't include container-selinux and remove
# directories used by dnf that are just taking
# up space.
# TODO: rpm --setcaps... needed due to Fedora (base) image builds
# being (maybe still?) affected by
# https://bugzilla.redhat.com/show_bug.cgi?id=1995337#c3
RUN dnf -y update && \
rpm --setcaps shadow-utils 2>/dev/null && \
dnf -y install 'dnf-command(copr)' --enablerepo=updates-testing && \
dnf -y copr enable rhcontainerbot/podman-next && \
dnf -y install skopeo \
--exclude container-selinux \
--enablerepo=updates-testing && \
dnf clean all && \
rm -rf /var/cache /var/log/dnf* /var/log/yum.*
RUN useradd skopeo && \
echo skopeo:100000:65536 > /etc/subuid && \
echo skopeo:100000:65536 > /etc/subgid
# Copy & modify the defaults to provide reference if runtime changes needed.
# Changes here are required for running with fuse-overlay storage inside container.
RUN sed -e 's|^#mount_program|mount_program|g' \
-e '/additionalimage.*/a "/var/lib/shared",' \
-e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' \
/usr/share/containers/storage.conf \
> /etc/containers/storage.conf
# Setup the ability to use additional stores
# with this container image.
RUN mkdir -p /var/lib/shared/overlay-images \
/var/lib/shared/overlay-layers && \
touch /var/lib/shared/overlay-images/images.lock && \
touch /var/lib/shared/overlay-layers/layers.lock
# Point to the Authorization file
ENV REGISTRY_AUTH_FILE=/tmp/auth.json
# Set the entrypoint
ENTRYPOINT ["/usr/bin/skopeo"]

View File

@@ -1,54 +0,0 @@
# upstream/Dockerfile
#
# Build a Skopeo container image from the latest
# upstream version of Skopeo on GitHub.
# https://github.com/containers/skopeo
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM registry.fedoraproject.org/fedora:33
# Don't include container-selinux and remove
# directories used by yum that are just taking
# up space. Also reinstall shadow-utils as without
# doing so, the setuid/setgid bits on newuidmap
# and newgidmap are lost in the Fedora images.
RUN useradd skopeo; yum -y update; yum -y reinstall shadow-utils; \
yum -y install make \
golang \
git \
go-md2man \
fuse-overlayfs \
fuse3 \
containers-common \
gpgme-devel \
libassuan-devel \
btrfs-progs-devel \
device-mapper-devel --enablerepo updates-testing --exclude container-selinux; \
mkdir /root/skopeo; \
git clone https://github.com/containers/skopeo /root/skopeo/src/github.com/containers/skopeo; \
export GOPATH=/root/skopeo; \
cd /root/skopeo/src/github.com/containers/skopeo; \
make bin/skopeo;\
make PREFIX=/usr install;\
rm -rf /root/skopeo/*; \
yum -y remove git golang go-md2man make; \
yum -y clean all; yum -y clean all; rm -rf /var/cache/dnf/* /var/log/dnf* /var/log/yum*
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf
# Setup the ability to use additional stores
# with this container image.
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
# Setup skopeo's uid/guid entries
RUN echo skopeo:100000:65536 > /etc/subuid
RUN echo skopeo:100000:65536 > /etc/subgid
# Point to the Authorization file
ENV REGISTRY_AUTH_FILE=/tmp/auth.json
# Set the entrypoint
ENTRYPOINT ["/usr/bin/skopeo"]

View File

@@ -1,19 +1,21 @@
# This is a default registries.d configuration file. You may
# add to this file or create additional files in registries.d/.
#
# sigstore: indicates a location that is read and write
# sigstore-staging: indicates a location that is only for write
# lookaside: for reading/writing simple signing signatures
# lookaside-staging: for writing simple signing signatures, preferred over lookaside
#
# sigstore and sigstore-staging take a value of the following:
# sigstore: {schema}://location
# lookaside and lookaside-staging take a value of the following:
# lookaside: {schema}://location
#
# For reading signatures, schema may be http, https, or file.
# For writing signatures, schema may only be file.
# This is the default signature write location for docker registries.
# The default locations are built-in, for both reading and writing:
# /var/lib/containers/sigstore for root, or
# ~/.local/share/containers/sigstore for non-root users.
default-docker:
# sigstore: file:///var/lib/containers/sigstore
sigstore-staging: file:///var/lib/containers/sigstore
# lookaside: https://…
# lookaside-staging: file:///…
# The 'docker' indicator here is the start of the configuration
# for docker registries.
@@ -21,6 +23,6 @@ default-docker:
# docker:
#
# privateregistry.com:
# sigstore: http://privateregistry.com/sigstore/
# sigstore-staging: /mnt/nfs/privateregistry/sigstore
# lookaside: https://privateregistry.com/sigstore/
# lookaside-staging: /mnt/nfs/privateregistry/sigstore

View File

@@ -54,6 +54,10 @@ Directory to use to share blobs across OCI repositories.
After copying the image, write the digest of the resulting image to the file.
**--preserve-digests**
Preserve the digests during copying. Fail if the digest cannot be preserved. Consider using `--all` at the same time.
**--encrypt-layer** _ints_
*Experimental* the 0-indexed layer indices, with support for negative indexing (e.g. 0 is the first layer, -1 is the last layer)
@@ -66,6 +70,17 @@ MANIFEST TYPE (oci, v2s1, or v2s2) to use in the destination (default is manifes
Print usage statement
**--multi-arch** _option_
Control what is copied if _source-image_ refers to a multi-architecture image. Default is system.
Options:
- system: Copy only the image that matches the system architecture
- all: Copy the full multi-architecture image
- index-only: Copy only the index
The index-only option usually fails unless the referenced per-architecture images are already present in the destination, or the target registry supports sparse indexes.
**--quiet**, **-q**
Suppress output information when copying images.
@@ -74,9 +89,26 @@ Suppress output information when copying images.
Do not copy signatures, if any, from _source-image_. Necessary when copying a signed image to a destination which does not support signatures.
**--sign-by**=_key-id_
**--sign-by** _key-id_
Add a signature using that key ID for an image name corresponding to _destination-image_
Add a “simple signing” signature using that key ID for an image name corresponding to _destination-image_
**--sign-by-sigstore** _param-file_
Add a sigstore signature based on the options in the specified containers sigstore signing parameter file, _param-file_.
See containers-sigstore-signing-params.yaml(5) for details about the file format.
**--sign-by-sigstore-private-key** _path_
Add a sigstore signature using a private key at _path_ for an image name corresponding to _destination-image_
**--sign-passphrase-file** _path_
The passphare to use when signing with `--sign-by` or `--sign-by-sigstore-private-key`. Only the first line will be read. A passphrase stored in a file is of questionable security if other users can read this file. Do not use this option if at all avoidable.
**--sign-identity** _reference_
The identity to use when signing the image. The identity must be a fully specified docker reference. If the identity is not specified, the target docker reference will be used.
**--src-shared-blob-dir** _directory_
@@ -94,15 +126,15 @@ Key to be used for decryption of images. Key can point to keys and/or certificat
Credentials for accessing the source registry.
**--dest-compress** _bool-value_
**--dest-compress**
Compress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source).
**--dest-decompress** _bool-value_
**--dest-decompress**
Decompress tarball image layers when saving to directory using the 'dir' transport. (default is same compression type as source).
**--dest-oci-accept-uncompressed-layers** _bool-value_
**--dest-oci-accept-uncompressed-layers**
Allow uncompressed image layers when saving to an OCI image using the 'oci' transport. (default is to compress things that aren't compressed).
@@ -114,25 +146,25 @@ Credentials for accessing the destination registry.
Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the source registry or daemon.
**--src-no-creds** _bool-value_
**--src-no-creds**
Access the registry anonymously.
**--src-tls-verify** _bool-value_
**--src-tls-verify**=_bool_
Require HTTPS and verify certificates when talking to container source registry or daemon (defaults to true).
Require HTTPS and verify certificates when talking to container source registry or daemon. Default to source registry setting.
**--dest-cert-dir** _path_
Use certificates at _path_ (*.crt, *.cert, *.key) to connect to the destination registry or daemon.
**--dest-no-creds** _bool-value_
**--dest-no-creds**
Access the registry anonymously.
**--dest-tls-verify** _bool-value_
**--dest-tls-verify**=_bool_
Require HTTPS and verify certificates when talking to container destination registry or daemon (defaults to true).
Require HTTPS and verify certificates when talking to container destination registry or daemon. Default to destination registry setting.
**--src-daemon-host** _host_
@@ -160,19 +192,39 @@ Bearer token for accessing the source registry.
Bearer token for accessing the destination registry.
**--dest-precompute-digests**
Precompute digests to ensure layers are not uploaded that already exist on the destination registry. Layers with initially unknown digests (ex. compressing "on the fly") will be temporarily streamed to disk.
**--retry-times**
The number of times to retry. Retry wait time will be exponentially increased based on the number of failed attempts.
**--src-username**
The username to access the source registry.
**--src-password**
The password to access the source registry.
**--dest-username**
The username to access the destination registry.
**--dest-password**
The password to access the destination registry.
## EXAMPLES
To just copy an image from one registry to another:
```sh
```console
$ skopeo copy docker://quay.io/skopeo/stable:latest docker://registry.example.com/skopeo:latest
```
To copy the layers of the docker.io busybox image to a local directory:
```sh
```console
$ mkdir -p /var/lib/images/busybox
$ skopeo copy docker://busybox:latest dir:/var/lib/images/busybox
$ ls /var/lib/images/busybox/*
@@ -181,42 +233,46 @@ $ ls /var/lib/images/busybox/*
/tmp/busybox/8ddc19f16526912237dd8af81971d5e4dd0587907234be2b83e249518d5b673f.tar
```
To copy and sign an image:
To create an archive consumable by `docker load` (but note that using a registry is almost always more efficient):
```console
$ skopeo copy docker://busybox:latest docker-archive:archive-file.tar:busybox:latest
```
```sh
# skopeo copy --sign-by dev@example.com containers-storage:example/busybox:streaming docker://example/busybox:gold
To copy and sign an image:
```console
$ skopeo copy --sign-by dev@example.com containers-storage:example/busybox:streaming docker://example/busybox:gold
```
To encrypt an image:
```sh
skopeo copy docker://docker.io/library/nginx:1.17.8 oci:local_nginx:1.17.8
```console
$ skopeo copy docker://docker.io/library/nginx:1.17.8 oci:local_nginx:1.17.8
openssl genrsa -out private.key 1024
openssl rsa -in private.key -pubout > public.key
$ openssl genrsa -out private.key 1024
$ openssl rsa -in private.key -pubout > public.key
skopeo copy --encryption-key jwe:./public.key oci:local_nginx:1.17.8 oci:try-encrypt:encrypted
$ skopeo copy --encryption-key jwe:./public.key oci:local_nginx:1.17.8 oci:try-encrypt:encrypted
```
To decrypt an image:
```sh
skopeo copy --decryption-key ./private.key oci:try-encrypt:encrypted oci:try-decrypt:decrypted
```console
$ skopeo copy --decryption-key ./private.key oci:try-encrypt:encrypted oci:try-decrypt:decrypted
```
To copy encrypted image without decryption:
```sh
skopeo copy oci:try-encrypt:encrypted oci:try-encrypt-copy:encrypted
```console
$ skopeo copy oci:try-encrypt:encrypted oci:try-encrypt-copy:encrypted
```
To decrypt an image that requires more than one key:
```sh
skopeo copy --decryption-key ./private1.key --decryption-key ./private2.key --decryption-key ./private3.key oci:try-encrypt:encrypted oci:try-decrypt:decrypted
```console
$ skopeo copy --decryption-key ./private1.key --decryption-key ./private2.key --decryption-key ./private3.key oci:try-encrypt:encrypted oci:try-decrypt:decrypted
```
Container images can also be partially encrypted by specifying the index of the layer. Layers are 0-indexed indices, with support for negative indexing. i.e. 0 is the first layer, -1 is the last layer.
Let's say out of 3 layers that the image `docker.io/library/nginx:1.17.8` is made up of, we only want to encrypt the 2nd layer,
```sh
skopeo copy --encryption-key jwe:./public.key --encrypt-layer 1 oci:local_nginx:1.17.8 oci:try-encrypt:encrypted
```console
$ skopeo copy --encryption-key jwe:./public.key --encrypt-layer 1 oci:local_nginx:1.17.8 oci:try-encrypt:encrypted
```
## SEE ALSO

View File

@@ -6,17 +6,27 @@ skopeo\-delete - Mark the _image-name_ for later deletion by the registry's garb
## SYNOPSIS
**skopeo delete** [*options*] _image-name_
Mark _image-name_ for deletion. To release the allocated disk space, you must login to the container registry server and execute the container registry garbage collector. E.g.,
## DESCRIPTION
Mark _image-name_ for deletion.
The effect of this is registry-specific; many registries dont support this operation, or dont allow it in some circumstances / configurations.
**WARNING**: If _image-name_ contains a digest, this affects the referenced manifest, and may delete all tags (within the current repository?) pointing to that manifest.
**WARNING**: If _image-name_ contains a tag (but not a digest), in the current version of Skopeo this resolves the tag into a digest, and then deletes the manifest by digest, as described above (possibly deleting all tags pointing to that manifest, not just the provided tag). This behavior may change in the future.
When using the github.com/distribution/distribution registry server:
To release the allocated disk space, you must login to the container registry server and execute the container registry garbage collector. E.g.,
```
/usr/bin/registry garbage-collect /etc/docker-distribution/registry/config.yml
```
Note: sometimes the config.yml is stored in /etc/docker/registry/config.yml
If you are running the container registry inside of a container you would execute something like:
```
$ docker exec -it registry /usr/bin/registry garbage-collect /etc/docker-distribution/registry/config.yml
```
## OPTIONS
@@ -42,7 +52,7 @@ Use docker daemon host at _host_ (`docker-daemon:` transport only)
Print usage statement
**--no-creds** _bool-value_
**--no-creds**
Access the registry anonymously.
@@ -62,13 +72,21 @@ Directory to use to share blobs across OCI repositories.
**--tls-verify**=_bool_
Require HTTPS and verify certificates when talking to the container registry or daemon (defaults to true)
Require HTTPS and verify certificates when talking to the container registry or daemon. Default to registry.conf setting.
**--username**
The username to access the registry.
**--password**
The password to access the registry.
## EXAMPLES
Mark image example/pause for deletion from the registry.example.com registry:
```sh
$ skopeo delete --force docker://registry.example.com/example/pause:latest
```console
$ skopeo delete docker://registry.example.com/example/pause:latest
```
See above for additional details on using the command **delete**.

View File

@@ -0,0 +1,47 @@
% skopeo-generate-sigstore-key(1)
## NAME
skopeo\-generate-sigstore-key - Generate a sigstore public/private key pair.
## SYNOPSIS
**skopeo generate-sigstore-key** [*options*] **--output-prefix** _prefix_
## DESCRIPTION
Generates a public/private key pair suitable for creating sigstore image signatures.
The private key is encrypted with a passphrase;
if one is not provided using an option, this command prompts for it interactively.
The private key is written to _prefix_**.private** .
The private key is written to _prefix_**.pub** .
## OPTIONS
**--help**, **-h**
Print usage statement
**--output-prefix** _prefix_
Mandatory.
Path prefix for the output keys (_prefix_**.private** and _prefix_**.pub**).
**--passphrase-file** _path_
The passphare to use to encrypt the private key.
Only the first line will be read.
A passphrase stored in a file is of questionable security if other users can read this file.
Do not use this option if at all avoidable.
## EXAMPLES
```console
$ skopeo generate-sigstore-key --output-prefix mykey
```
# SEE ALSO
skopeo(1), skopeo-copy(1), containers-policy.json(5)
## AUTHORS
Miloslav Trmač <mitr@redhat.com>

View File

@@ -8,9 +8,12 @@ skopeo\-inspect - Return low-level information about _image-name_ in a registry.
## DESCRIPTION
Return low-level information about _image-name_ in a registry
Return low-level information about _image-name_ in a registry.
See [skopeo(1)](skopeo.1.md) for the format of _image-name_.
_image-name_ name of image to retrieve information about
The default output includes data from various sources: user input (**Name**), the remote repository, if any (**RepoTags**), the top-level manifest (**Digest**),
and a per-architecture/OS image matching the current run-time environment (most other values).
To see values for a different architecture/OS, use the **--override-os** / **--override-arch** options documented in [skopeo(1)](skopeo.1.md).
## OPTIONS
@@ -67,43 +70,107 @@ Directory to use to share blobs across OCI repositories.
**--tls-verify**=_bool_
Require HTTPS and verify certificates when talking to the container registry or daemon (defaults to true)
Require HTTPS and verify certificates when talking to the container registry or daemon. Default to registry.conf setting.
**--username**
The username to access the registry.
**--password**
The password to access the registry.
**--no-tags**, **-n**
Do not list the available tags from the repository in the output. When `true`, the `RepoTags` array will be empty. Defaults to `false`, which includes all available tags.
## EXAMPLES
To review information for the image fedora from the docker.io registry:
```sh
```console
$ skopeo inspect docker://docker.io/fedora
{
"Name": "docker.io/library/fedora",
"Digest": "sha256:a97914edb6ba15deb5c5acf87bd6bd5b6b0408c96f48a5cbd450b5b04509bb7d",
"Digest": "sha256:f99efcddc4dd6736d8a88cc1ab6722098ec1d77dbf7aed9a7a514fc997ca08e0",
"RepoTags": [
"20",
"21",
"22",
"23",
"24",
"heisenbug",
"latest",
"rawhide"
"20",
"21",
"..."
],
"Created": "2016-06-20T19:33:43.220526898Z",
"DockerVersion": "1.10.3",
"Labels": {},
"Created": "2022-11-16T07:26:42.618327645Z",
"DockerVersion": "20.10.12",
"Labels": {
"maintainer": "Clement Verna \u003ccverna@fedoraproject.org\u003e"
},
"Architecture": "amd64",
"Os": "linux",
"Layers": [
"sha256:7c91a140e7a1025c3bc3aace4c80c0d9933ac4ee24b8630a6b0b5d8b9ce6b9d4"
"sha256:cb8b1ed77979b894115a983f391465651aa7eb3edd036be4b508eea47271eb93"
],
"LayersData": [
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:cb8b1ed77979b894115a983f391465651aa7eb3edd036be4b508eea47271eb93",
"Size": 65990920,
"Annotations": null
}
],
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"DISTTAG=f37container",
"FGC=f37",
"FBR=f37"
]
}
```
To inspect python from the docker.io registry and not show the available tags:
```console
$ skopeo inspect --no-tags docker://docker.io/library/python
{
"Name": "docker.io/library/python",
"Digest": "sha256:10fc14aa6ae69f69e4c953cffd9b0964843d8c163950491d2138af891377bc1d",
"RepoTags": [],
"Created": "2022-11-16T06:55:28.566254104Z",
"DockerVersion": "20.10.12",
"Labels": null,
"Architecture": "amd64",
"Os": "linux",
"Layers": [
"sha256:a8ca11554fce00d9177da2d76307bdc06df7faeb84529755c648ac4886192ed1",
"sha256:e4e46864aba2e62ba7c75965e4aa33ec856ee1b1074dda6b478101c577b63abd",
"..."
],
"LayersData": [
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:a8ca11554fce00d9177da2d76307bdc06df7faeb84529755c648ac4886192ed1",
"Size": 55038615,
"Annotations": null
},
{
"MIMEType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"Digest": "sha256:e4e46864aba2e62ba7c75965e4aa33ec856ee1b1074dda6b478101c577b63abd",
"Size": 5164893,
"Annotations": null
},
"..."
],
"Env": [
"PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=C.UTF-8",
"...",
]
}
```
```console
$ /bin/skopeo inspect --config docker://registry.fedoraproject.org/fedora --format "{{ .Architecture }}"
amd64
```
```
```console
$ /bin/skopeo inspect --format '{{ .Env }}' docker://registry.access.redhat.com/ubi8
[PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin container=oci]
```

View File

@@ -1,14 +1,14 @@
% skopeo-list-tags(1)
## NAME
skopeo\-list\-tags - List tags in the transport-specific image repository.
skopeo\-list\-tags - List image names in a transport-specific collection of images.
## SYNOPSIS
**skopeo list-tags** [*options*] _repository-name_
**skopeo list-tags** [*options*] _source-image_
Return a list of tags from _repository-name_ in a registry.
Return a list of tags from _source-image_ in a registry or a local docker-archive file.
_repository-name_ name of repository to retrieve tag listing from
_source-image_ name of the repository to retrieve a tag listing from or a local docker-archive file.
## OPTIONS
@@ -27,7 +27,7 @@ Use certificates at _path_ (\*.crt, \*.cert, \*.key) to connect to the registry.
Print usage statement
**--no-creds** _bool-value_
**--no-creds**
Access the registry anonymously.
@@ -41,11 +41,19 @@ The number of times to retry. Retry wait time will be exponentially increased ba
**--tls-verify**=_bool_
Require HTTPS and verify certificates when talking to the container registry or daemon (defaults to true)
Require HTTPS and verify certificates when talking to the container registry or daemon. Default to registry.conf setting.
**--username**
The username to access the registry.
**--password**
The password to access the registry.
## REPOSITORY NAMES
Repository names are transport-specific references as each transport may have its own concept of a "repository" and "tags". Currently, only the Docker transport is supported.
Repository names are transport-specific references as each transport may have its own concept of a "repository" and "tags".
This commands refers to repositories using a _transport_`:`_details_ format. The following formats are supported:
@@ -64,12 +72,14 @@ This commands refers to repositories using a _transport_`:`_details_ format. The
"docker.io/myuser/myimage:v1.0"
"docker.io/myuser/myimage@sha256:f48c4cc192f4c3c6a069cb5cca6d0a9e34d6076ba7c214fd0cc3ca60e0af76bb"
**docker-archive:path[:docker-reference]
more than one images were stored in a docker save-formatted file.
## EXAMPLES
### Docker Transport
To get the list of tags in the "fedora" repository from the docker.io registry (the repository name expands to "library/fedora" per docker transport canonical form):
```sh
```console
$ skopeo list-tags docker://docker.io/fedora
{
"Repository": "docker.io/library/fedora",
@@ -100,7 +110,7 @@ $ skopeo list-tags docker://docker.io/fedora
To list the tags in a local host docker/distribution registry on port 5000, in this case for the "fedora" repository:
```sh
```console
$ skopeo list-tags docker://localhost:5000/fedora
{
"Repository": "localhost:5000/fedora",
@@ -113,8 +123,48 @@ $ skopeo list-tags docker://localhost:5000/fedora
```
### Docker-archive Transport
To list the tags in a local docker-archive file:
```console
$ skopeo list-tags docker-archive:/tmp/busybox.tar.gz
{
"Tags": [
"busybox:1.28.3"
]
}
```
Also supports more than one tags in an archive:
```console
$ skopeo list-tags docker-archive:/tmp/docker-two-images.tar.gz
{
"Tags": [
"example.com/empty:latest",
"example.com/empty/but:different"
]
}
```
Will include a source-index entry for each untagged image:
```console
$ skopeo list-tags docker-archive:/tmp/four-tags-with-an-untag.tar
{
"Tags": [
"image1:tag1",
"image2:tag2",
"@2",
"image4:tag4"
]
}
```
# SEE ALSO
skopeo(1), skopeo-login(1), docker-login(1), containers-auth.json(5)
skopeo(1), skopeo-login(1), docker-login(1), containers-auth.json(5), containers-transports(1)
## AUTHORS

View File

@@ -49,7 +49,7 @@ Print usage statement
**--tls-verify**=_bool_
Require HTTPS and verify certificates when talking to the container registry or daemon (defaults to true)
Require HTTPS and verify certificates when talking to the container registry or daemon. Default to registry.conf setting.
**--verbose**, **-v**
@@ -57,41 +57,41 @@ Write more detailed information to stdout
## EXAMPLES
```
```console
$ skopeo login docker.io
Username: testuser
Password:
Login Succeeded!
```
```
```console
$ skopeo login -u testuser -p testpassword localhost:5000
Login Succeeded!
```
```
```console
$ skopeo login --authfile authdir/myauths.json docker.io
Username: testuser
Password:
Login Succeeded!
```
```
```console
$ skopeo login --tls-verify=false -u test -p test localhost:5000
Login Succeeded!
```
```
```console
$ skopeo login --cert-dir /etc/containers/certs.d/ -u foo -p bar localhost:5000
Login Succeeded!
```
```
```console
$ skopeo login -u testuser --password-stdin < testpassword.txt docker.io
Login Succeeded!
```
```
```console
$ echo $testpassword | skopeo login -u testuser --password-stdin docker.io
Login Succeeded!
```

View File

@@ -31,21 +31,21 @@ Print usage statement
**--tls-verify**=_bool_
Require HTTPS and verify certificates when talking to the container registry or daemon (defaults to true)
Require HTTPS and verify certificates when talking to the container registry or daemon. Default to registry.conf setting.
## EXAMPLES
```
```console
$ skopeo logout docker.io
Remove login credentials for docker.io
```
```
```console
$ skopeo logout --authfile authdir/myauths.json docker.io
Remove login credentials for docker.io
```
```
```console
$ skopeo logout --all
Remove login credentials for all registries
```

View File

@@ -18,7 +18,7 @@ Print usage statement
## EXAMPLES
```sh
```console
$ skopeo manifest-digest manifest.json
sha256:a59906e33509d14c036c8678d687bd4eec81ed7c4b8ce907b888c607f6a1e0e6
```

View File

@@ -25,9 +25,13 @@ Print usage statement
Write signature to _output file_.
**--passphrase-file**=_path_
The passphare to use when signing with the key ID from `--sign-by`. Only the first line will be read. A passphrase stored in a file is of questionable security if other users can read this file. Do not use this option if at all avoidable.
## EXAMPLES
```sh
```console
$ skopeo standalone-sign busybox-manifest.json registry.example.com/example/busybox 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8 --output busybox.signature
$
```

View File

@@ -30,7 +30,7 @@ Print usage statement
## EXAMPLES
```sh
```console
$ skopeo standalone-verify busybox-manifest.json registry.example.com/example/busybox 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8 busybox.signature
Signature verified, digest sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55
```

View File

@@ -1,14 +1,14 @@
% skopeo-sync(1)
## NAME
skopeo\-sync - Synchronize images between container registries and local directories.
skopeo\-sync - Synchronize images between registry repositories and local directories.
## SYNOPSIS
**skopeo sync** [*options*] --src _transport_ --dest _transport_ _source_ _destination_
## DESCRIPTION
Synchronize images between container registries and local directories.
Synchronize images between registry repoositories and local directories.
The synchronization is achieved by copying all the images found at _source_ to _destination_.
Useful to synchronize a local container registry mirror, and to to populate registries running inside of air-gapped environments.
@@ -50,6 +50,10 @@ Path of the authentication file for the source registry. Uses path given by `--a
Path of the authentication file for the destination registry. Uses path given by `--authfile`, if not provided.
**--dry-run**
Run the sync without actually copying data to the destination.
**--src**, **-s** _transport_ Transport for the source repository.
**--dest**, **-d** _transport_ Destination transport.
@@ -62,9 +66,28 @@ Print usage statement.
**--scoped** Prefix images with the source image path, so that multiple images with the same name can be stored at _destination_.
**--append-suffix** _tag-suffix_ String to append to destination tags.
**--preserve-digests** Preserve the digests during copying. Fail if the digest cannot be preserved. Consider using `--all` at the same time.
**--remove-signatures** Do not copy signatures, if any, from _source-image_. This is necessary when copying a signed image to a destination which does not support signatures.
**--sign-by**=_key-id_ Add a signature using that key ID for an image name corresponding to _destination-image_.
**--sign-by** _key-id_
Add a “simple signing” signature using that key ID for an image name corresponding to _destination-image_
**--sign-by-sigstore** _param-file_
Add a sigstore signature based on the options in the specified containers sigstore signing parameter file, _param-file_.
See containers-sigstore-signing-params.yaml(5) for details about the file format.
**--sign-by-sigstore-private-key** _path_
Add a sigstore signature using a private key at _path_ for an image name corresponding to _destination-image_
**--sign-passphrase-file** _path_
The passphare to use when signing with `--sign-by` or `--sign-by-sigstore-private-key`. Only the first line will be read. A passphrase stored in a file is of questionable security if other users can read this file. Do not use this option if at all avoidable.
**--src-creds** _username[:password]_ for accessing the source registry.
@@ -72,15 +95,15 @@ Print usage statement.
**--src-cert-dir** _path_ Use certificates (*.crt, *.cert, *.key) at _path_ to connect to the source registry or daemon.
**--src-no-creds** _bool-value_ Access the registry anonymously.
**--src-no-creds** Access the registry anonymously.
**--src-tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to a container source registry or daemon (defaults to true).
**--src-tls-verify**=_bool_ Require HTTPS and verify certificates when talking to a container source registry or daemon. Default to source registry entry in registry.conf setting.
**--dest-cert-dir** _path_ Use certificates (*.crt, *.cert, *.key) at _path_ to connect to the destination registry or daemon.
**--dest-no-creds** _bool-value_ Access the registry anonymously.
**--dest-no-creds** Access the registry anonymously.
**--dest-tls-verify** _bool-value_ Require HTTPS and verify certificates when talking to a container destination registry or daemon (defaults to true).
**--dest-tls-verify**=_bool_ Require HTTPS and verify certificates when talking to a container destination registry or daemon. Default to destination registry entry in registry.conf setting.
**--src-registry-token** _Bearer token_ for accessing the source registry.
@@ -88,10 +111,29 @@ Print usage statement.
**--retry-times** the number of times to retry, retry wait time will be exponentially increased based on the number of failed attempts.
**--keep-going**
If any errors occur during copying of images, those errors are logged and the process continues syncing rest of the images and finally fails at the end.
**--src-username**
The username to access the source registry.
**--src-password**
The password to access the source registry.
**--dest-username**
The username to access the destination registry.
**--dest-password**
The password to access the destination registry.
## EXAMPLES
### Synchronizing to a local directory
```
```console
$ skopeo sync --src docker --dest dir registry.example.com/busybox /media/usb
```
Images are located at:
@@ -109,7 +151,7 @@ Images are located at:
/media/usb/busybox:1-glibc
```
Sync run
```
```console
$ skopeo sync --src dir --dest docker /media/usb/busybox:1-glibc my-registry.local.lan/test/
```
Destination registry content:
@@ -119,7 +161,7 @@ my-registry.local.lan/test/busybox 1-glibc
```
### Synchronizing to a local directory, scoped
```
```console
$ skopeo sync --src docker --dest dir --scoped registry.example.com/busybox /media/usb
```
Images are located at:
@@ -132,8 +174,8 @@ Images are located at:
```
### Synchronizing to a container registry
```
skopeo sync --src docker --dest docker registry.example.com/busybox my-registry.local.lan
```console
$ skopeo sync --src docker --dest docker registry.example.com/busybox my-registry.local.lan
```
Destination registry content:
```
@@ -142,8 +184,8 @@ registry.local.lan/busybox 1-glibc, 1-musl, 1-ubuntu, ..., latest
```
### Synchronizing to a container registry keeping the repository
```
skopeo sync --src docker --dest docker registry.example.com/repo/busybox my-registry.local.lan/repo
```console
$ skopeo sync --src docker --dest docker registry.example.com/repo/busybox my-registry.local.lan/repo
```
Destination registry content:
```
@@ -151,6 +193,16 @@ REPO TAGS
registry.local.lan/repo/busybox 1-glibc, 1-musl, 1-ubuntu, ..., latest
```
### Synchronizing to a container registry with tag suffix
```console
$ skopeo sync --src docker --dest docker --append-suffix '-mirror' registry.example.com/busybox my-registry.local.lan
```
Destination registry content:
```
REPO TAGS
registry.local.lan/busybox 1-glibc-mirror, 1-musl-mirror, 1-ubuntu-mirror, ..., latest-mirror
```
### YAML file content (used _source_ for `**--src yaml**`)
```yaml
@@ -175,8 +227,8 @@ quay.io:
- latest
```
If the yaml filename is `sync.yml`, sync run:
```
skopeo sync --src yaml --dest docker sync.yml my-registry.local.lan/repo/
```console
$ skopeo sync --src yaml --dest docker sync.yml my-registry.local.lan/repo/
```
This will copy the following images:
- Repository `registry.example.com/busybox`: all images, as no tags are specified.

View File

@@ -47,7 +47,7 @@ Most commands refer to container images, using a _transport_`:`_details_ format.
**oci-archive:**_path_**:**_tag_
An image _tag_ in a tar archive compliant with "Open Container Image Layout Specification" at _path_.
See [containers-transports(5)](https://github.com/containers/image/blob/master/docs/containers-transports.5.md) for details.
See [containers-transports(5)](https://github.com/containers/image/blob/main/docs/containers-transports.5.md) for details.
## OPTIONS
@@ -101,23 +101,24 @@ Print the version number
| ----------------------------------------- | ------------------------------------------------------------------------------ |
| [skopeo-copy(1)](skopeo-copy.1.md) | Copy an image (manifest, filesystem layers, signatures) from one location to another. |
| [skopeo-delete(1)](skopeo-delete.1.md) | Mark the _image-name_ for later deletion by the registry's garbage collector. |
| [skopeo-generate-sigstore-key(1)](skopeo-generate-sigstore-key.1.md) | Generate a sigstore public/private key pair. |
| [skopeo-inspect(1)](skopeo-inspect.1.md) | Return low-level information about _image-name_ in a registry. |
| [skopeo-list-tags(1)](skopeo-list-tags.1.md) | List tags in the transport-specific image repository. |
| [skopeo-list-tags(1)](skopeo-list-tags.1.md) | List image names in a transport-specific collection of images.|
| [skopeo-login(1)](skopeo-login.1.md) | Login to a container registry. |
| [skopeo-logout(1)](skopeo-logout.1.md) | Logout of a container registry. |
| [skopeo-manifest-digest(1)](skopeo-manifest-digest.1.md) | Compute a manifest digest for a manifest-file and write it to standard output. |
| [skopeo-standalone-sign(1)](skopeo-standalone-sign.1.md) | Debugging tool - Publish and sign an image in one step. |
| [skopeo-standalone-verify(1)](skopeo-standalone-verify.1.md)| Verify an image signature. |
| [skopeo-sync(1)](skopeo-sync.1.md)| Synchronize images between container registries and local directories. |
| [skopeo-sync(1)](skopeo-sync.1.md)| Synchronize images between registry repositories and local directories. |
## FILES
**/etc/containers/policy.json**
Default trust policy file, if **--policy** is not specified.
The policy format is documented in [containers-policy.json(5)](https://github.com/containers/image/blob/master/docs/containers-policy.json.5.md) .
The policy format is documented in [containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md) .
**/etc/containers/registries.d**
Default directory containing registry configuration, if **--registries.d** is not specified.
The contents of this directory are documented in [containers-policy.json(5)](https://github.com/containers/image/blob/master/docs/containers-policy.json.5.md).
The contents of this directory are documented in [containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md).
## SEE ALSO
skopeo-login(1), docker-login(1), containers-auth.json(5), containers-storage.conf(5), containers-policy.json(5), containers-transports(5)

View File

@@ -1,546 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="480.61456"
height="472.66098"
viewBox="0 0 127.1626 125.05822"
version="1.1"
id="svg8"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="skopeo.svg"
inkscape:export-filename="/home/duffy/Documents/Projects/Favors/skopeo-logo/skopeo.color.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs2">
<linearGradient
inkscape:collect="always"
id="linearGradient84477">
<stop
style="stop-color:#0093d9;stop-opacity:1"
offset="0"
id="stop84473" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop84475" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient84469">
<stop
style="stop-color:#f6e6c8;stop-opacity:1"
offset="0"
id="stop84465" />
<stop
style="stop-color:#dc9f2e;stop-opacity:1"
offset="1"
id="stop84467" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient84461">
<stop
style="stop-color:#bfdce8;stop-opacity:1;"
offset="0"
id="stop84457" />
<stop
style="stop-color:#2a72ac;stop-opacity:1"
offset="1"
id="stop84459" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient84420">
<stop
style="stop-color:#a7a9ac;stop-opacity:1;"
offset="0"
id="stop84416" />
<stop
style="stop-color:#e7e8e9;stop-opacity:1"
offset="1"
id="stop84418" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient84347">
<stop
style="stop-color:#2c2d2f;stop-opacity:1;"
offset="0"
id="stop84343" />
<stop
style="stop-color:#000000;stop-opacity:1"
offset="1"
id="stop84345" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient84339">
<stop
style="stop-color:#002442;stop-opacity:1;"
offset="0"
id="stop84335" />
<stop
style="stop-color:#151617;stop-opacity:1"
offset="1"
id="stop84337" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient84331">
<stop
style="stop-color:#003d6e;stop-opacity:1;"
offset="0"
id="stop84327" />
<stop
style="stop-color:#59b5ff;stop-opacity:1"
offset="1"
id="stop84329" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient84323">
<stop
style="stop-color:#dc9f2e;stop-opacity:1;"
offset="0"
id="stop84319" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop84321" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84323"
id="linearGradient84325"
x1="221.5741"
y1="250.235"
x2="219.20772"
y2="221.99771"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84331"
id="linearGradient84333"
x1="223.23239"
y1="212.83418"
x2="245.52328"
y2="129.64345"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84339"
id="linearGradient84341"
x1="190.36137"
y1="217.8925"
x2="205.20828"
y2="209.32063"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84347"
id="linearGradient84349"
x1="212.05453"
y1="215.20055"
x2="237.73705"
y2="230.02835"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84323"
id="linearGradient84363"
x1="193.61516"
y1="225.045"
x2="224.08698"
y2="223.54327"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84323"
id="linearGradient84377"
x1="182.72513"
y1="222.54439"
x2="184.01024"
y2="210.35291"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84420"
id="linearGradient84408"
x1="211.73801"
y1="225.48302"
x2="204.24324"
y2="238.46432"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84420"
id="linearGradient84422"
x1="190.931"
y1="221.83777"
x2="187.53873"
y2="229.26593"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84339"
id="linearGradient84425"
gradientUnits="userSpaceOnUse"
x1="190.36137"
y1="217.8925"
x2="205.20828"
y2="209.32063"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84420"
id="linearGradient84441"
x1="169.95944"
y1="215.77036"
x2="174.0289"
y2="207.81528"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84420"
id="linearGradient84455"
x1="234.08092"
y1="252.39755"
x2="245.88477"
y2="251.21777"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient84461"
id="radialGradient84463"
cx="213.19594"
cy="223.40646"
fx="214.12064"
fy="217.34077"
r="33.39888"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(2.6813748,0.05304973,-0.0423372,2.1399146,-349.74924,-255.6421)" />
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient84469"
id="radialGradient84471"
cx="207.18298"
cy="211.06483"
fx="207.18298"
fy="211.06483"
r="2.77954"
gradientTransform="matrix(1.4407627,0.18685239,-0.24637721,1.8997405,-38.989952,-218.98841)"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient84477"
id="linearGradient84479"
x1="241.60336"
y1="255.46982"
x2="244.45177"
y2="250.4846"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0,10.583333)" />
<svg width="168.71024mm" height="145.54036mm" viewBox="0 0 168.71024 145.54036" version="1.1" id="svg2674" inkscape:version="1.2 (dc2aedaf03, 2022-05-15)" sodipodi:docname="skopeo-badge-full-vert.svg" inkscape:export-filename="skopeo-badge-full-vert.png" inkscape:export-xdpi="51.86108" inkscape:export-ydpi="51.86108" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs id="defs2668">
<inkscape:path-effect is_visible="true" id="path-effect10334" effect="spiro" lpeversion="0"/>
<inkscape:path-effect effect="spiro" id="path-effect10336" is_visible="true" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect9986" effect="spiro" lpeversion="0"/>
<inkscape:path-effect effect="spiro" id="path-effect9984" is_visible="true" lpeversion="0"/>
<inkscape:path-effect effect="spiro" id="path-effect10300" is_visible="true" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect10304" effect="spiro" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect124972" effect="spiro" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect124976" effect="spiro" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect163593" effect="spiro" lpeversion="0"/>
<inkscape:path-effect effect="spiro" id="path-effect163605" is_visible="true" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect163611" effect="spiro" lpeversion="0"/>
<inkscape:path-effect effect="spiro" id="path-effect163615" is_visible="true" lpeversion="0"/>
<inkscape:path-effect effect="spiro" id="path-effect163619" is_visible="true" lpeversion="0"/>
<inkscape:path-effect effect="spiro" id="path-effect163629" is_visible="true" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect163633" effect="spiro" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect163651" effect="spiro" lpeversion="0"/>
<inkscape:path-effect effect="spiro" id="path-effect163655" is_visible="true" lpeversion="0"/>
<inkscape:path-effect is_visible="true" id="path-effect163597" effect="spiro" lpeversion="0"/>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="517.27113"
inkscape:cy="314.79773"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="px"
inkscape:snap-global="false"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata5">
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="0.7" inkscape:cx="399.28571" inkscape:cy="187.14286" inkscape:document-units="mm" inkscape:current-layer="g1208" showgrid="false" fit-margin-top="10" fit-margin-left="10" fit-margin-right="10" fit-margin-bottom="10" inkscape:window-width="2560" inkscape:window-height="1403" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:pagecheckerboard="0" inkscape:showpageshadow="2" inkscape:deskcolor="#d1d1d1"/>
<metadata id="metadata2671">
<rdf:RDF>
<cc:Work
rdf:about="">
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-149.15784,-175.92614)">
<g
id="g84497"
style="stroke-width:1.32291663;stroke-miterlimit:4;stroke-dasharray:none"
transform="translate(0,10.583333)">
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:1.32291663;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
id="rect84485"
width="31.605196"
height="19.16976"
x="299.48376"
y="87.963303"
transform="rotate(30)" />
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:1.32291663;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
id="rect84487"
width="16.725054"
height="9.8947001"
x="258.07639"
y="92.60083"
transform="rotate(30)" />
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:1.32291663;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
id="rect84489"
width="4.8383565"
height="11.503917"
x="253.2236"
y="91.796227"
transform="rotate(30)" />
<rect
y="86.859642"
x="331.21924"
height="21.377089"
width="4.521956"
id="rect84491"
style="fill:#ffffff;stroke:#000000;stroke-width:1.32291663;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
transform="rotate(30)" />
<g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(378.90631,201.21016)">
<g id="g1208">
<g id="g81584" transform="matrix(1.7276536,0,0,1.7276536,-401.82487,-530.26362)" inkscape:export-filename="/home/duffy/Documents/Projects/Favors/skopeo-logo/new skopeo/skopeo-logomark_medium_transparent-bg.png" inkscape:export-xdpi="51.86108" inkscape:export-ydpi="51.86108">
<g style="fill:#ffffff;fill-opacity:1;stroke:#3c6eb4;stroke-opacity:1" id="g81528" transform="translate(-734.38295,98.0028)">
<path inkscape:connector-curvature="0" style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#3c6eb4;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 796.57913,145.63255 -19.29817,-9.23285 -4.82036,-20.8616 13.2871,-16.780616 21.38926,-0.06408 13.38485,16.701146 -4.69887,20.8897 z" id="path81526"/>
</g>
<g transform="matrix(0.43729507,0,0,0.43729507,42.235192,80.461942)" id="g81554">
<rect style="fill:#b3b3b3;fill-opacity:1;stroke:#808080;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1" id="rect81530" width="16.725054" height="9.8947001" x="158.13725" y="255.21965" transform="rotate(30)"/>
<rect style="fill:#ffffff;stroke:#000000;stroke-width:1.32292;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6" id="rect81532" width="4.8383565" height="11.503917" x="153.28447" y="254.41505" transform="rotate(30)"/>
<path sodipodi:nodetypes="cczc" inkscape:connector-curvature="0" id="path81534" d="m 78.802289,335.54596 -9.111984,15.78242 c 1.40192,0.25963 4.990131,-0.63196 7.869989,-5.61868 2.879866,-4.98671 2.168498,-9.07865 1.241995,-10.16374 z" style="fill:#9dc6e7;fill-opacity:1;stroke:#2a72ac;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1"/>
<rect transform="rotate(30)" y="250.58212" x="199.54463" height="19.16976" width="31.605196" id="rect81536" style="fill:#b3b3b3;fill-opacity:1;stroke:#808080;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1"/>
<rect transform="rotate(30)" style="fill:#b3b3b3;fill-opacity:1;stroke:#808080;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1" id="rect81538" width="16.459545" height="15.252436" x="178.48766" y="252.54079"/>
<g style="stroke:#808080;stroke-opacity:1" id="g81548">
<rect style="fill:#e1ae4f;fill-opacity:1;stroke:#a1721b;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1" id="rect81540" width="4.521956" height="21.377089" x="195.04353" y="249.47847" transform="rotate(30)"/>
<rect y="251.64348" x="174.76939" height="17.047071" width="3.617183" id="rect81542" style="fill:#e1ae4f;fill-opacity:1;stroke:#a1721b;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1" transform="rotate(30)"/>
<rect style="fill:#e1ae4f;fill-opacity:1;stroke:#a1721b;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1" id="rect81544" width="4.8383565" height="11.503917" x="153.28447" y="254.41505" transform="rotate(30)"/>
<rect y="249.47847" x="231.28011" height="21.377089" width="4.521956" id="rect81546" style="fill:#e1ae4f;fill-opacity:1;stroke:#a1721b;stroke-width:1.81574;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1" transform="rotate(30)"/>
</g>
<path inkscape:connector-curvature="0" id="path81550" d="m 47.691007,322.31629 22.49734,12.98884" style="fill:#ffffff;fill-rule:evenodd;stroke:#ffffff;stroke-width:3.02523;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/>
<path style="fill:#ffffff;fill-rule:evenodd;stroke:#ffffff;stroke-width:3.02523;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 27.886021,312.45704 9.423431,5.07506" id="path81552" inkscape:connector-curvature="0"/>
</g>
<g transform="matrix(0.43729507,0,0,0.43729507,42.235192,101.28812)" id="g81568">
<path style="fill:#2a72ac;fill-opacity:1;stroke:#003e6f;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1" d="m 34.507847,231.71327 26.65552,8.43269 21.69622,19.51455 -8.68507,12.39398 -46.04559,-26.61429 z" id="path81556" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccc"/>
<path sodipodi:nodetypes="ccccc" inkscape:connector-curvature="0" id="path81558" d="m 28.119527,245.45648 46.0456,26.61429 -3.50256,6.07342 -46.0456,-26.61429 z" style="fill:#808080;fill-opacity:1;stroke:#000000;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6"/>
<path style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.81514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 24.616967,251.5299 -11.1013,8.29627 c 0,0 6.16202,4.57403 15.2798,4.67656 9.1178,0.1025 11.46925,-3.93799 11.46925,-3.93799 z" id="path81560" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"/>
<ellipse ry="3.8438656" rx="3.8395541" style="fill:#e1ae4f;fill-opacity:1;stroke:#a1721b;stroke-width:1.81514;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:6;stroke-opacity:1" id="ellipse81562" cx="39.230743" cy="255.66997"/>
<path sodipodi:nodetypes="ccc" style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#9dc6e7;stroke-width:1.81514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 71.999346,266.02935 -8.9307,-5.38071 10.81942,-5.07707" id="path81564" inkscape:connector-curvature="0"/>
<path style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#9dc6e7;stroke-width:1.81514;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 35.169799,245.57008 10.37702,-6.1817 -7.12581,-2.30459" id="path81566" inkscape:connector-curvature="0" sodipodi:nodetypes="ccc"/>
</g>
<g style="fill:none;fill-opacity:1;stroke:#9dc6e7;stroke-opacity:1" id="g81582" transform="translate(0.69195604,69.064926)">
<path inkscape:export-ydpi="96.181694" inkscape:export-xdpi="96.181694" sodipodi:nodetypes="cc" style="fill:none;fill-opacity:1;stroke:#9dc6e7;stroke-width:0.79375;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 83.087609,145.72448 -3.6551,1.27991" id="path81570" inkscape:connector-curvature="0" inkscape:export-filename="/home/duffy/Documents/Projects/Favors/Buildah logo/final/color-not-color.png"/>
<path inkscape:export-filename="/home/duffy/Documents/Projects/Favors/Buildah logo/final/color-not-color.png" sodipodi:nodetypes="cc" style="fill:none;fill-opacity:1;stroke:#9dc6e7;stroke-width:0.79375;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 51.138114,129.84674 1.971302,3.71206" id="path81572" inkscape:connector-curvature="0" inkscape:export-xdpi="96.181694" inkscape:export-ydpi="96.181694"/>
<path inkscape:export-filename="/home/duffy/Documents/Projects/Favors/Buildah logo/final/color-not-color.png" inkscape:connector-curvature="0" id="path81574" d="m 70.63337,129.84674 -2.345479,4.17978" style="fill:none;fill-opacity:1;stroke:#9dc6e7;stroke-width:0.79375;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" sodipodi:nodetypes="cc" inkscape:export-xdpi="96.181694" inkscape:export-ydpi="96.181694"/>
<path inkscape:export-ydpi="96.181694" inkscape:export-xdpi="96.181694" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path81576" d="m 61.405599,166.31541 v 5.83669" style="fill:none;fill-opacity:1;stroke:#9dc6e7;stroke-width:0.79375;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" inkscape:export-filename="/home/duffy/Documents/Projects/Favors/Buildah logo/final/color-not-color.png"/>
<path inkscape:export-ydpi="96.181694" inkscape:export-xdpi="96.181694" inkscape:connector-curvature="0" id="path81578" d="m 43.729779,164.25283 4.216366,-4.18995" style="fill:none;fill-opacity:1;stroke:#9dc6e7;stroke-width:0.79375;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" sodipodi:nodetypes="cc" inkscape:export-filename="/home/duffy/Documents/Projects/Favors/Buildah logo/final/color-not-color.png"/>
<path inkscape:export-ydpi="96.181694" inkscape:export-xdpi="96.181694" sodipodi:nodetypes="cc" style="fill:none;fill-opacity:1;stroke:#9dc6e7;stroke-width:0.79375;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 79.100039,164.25283 -1.50358,-1.57071" id="path81580" inkscape:connector-curvature="0" inkscape:export-filename="/home/duffy/Documents/Projects/Favors/Buildah logo/final/color-not-color.png"/>
</g>
</g>
<text id="text81524" y="-73.044861" x="-363.40085" style="font-style:normal;font-weight:normal;font-size:37.592px;line-height:22.5552px;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e1ae4f;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" xml:space="preserve"><tspan style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Montserrat;-inkscape-font-specification:'Montserrat Medium';fill:#e1ae4f;fill-opacity:1;stroke-width:0.264583px" y="-73.044861" x="-363.40085" id="tspan81522" sodipodi:role="line" dx="0 0 0 0 0 0"><tspan style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Montserrat;-inkscape-font-specification:'Montserrat Medium';fill:#294172;fill-opacity:1" id="tspan81514">sk</tspan><tspan style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Montserrat;-inkscape-font-specification:'Montserrat Medium';fill:#2a72ac;fill-opacity:1" id="tspan81516">o</tspan><tspan style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Montserrat;-inkscape-font-specification:'Montserrat Medium';fill:#294172;fill-opacity:1" id="tspan81518">pe</tspan><tspan style="font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Montserrat;-inkscape-font-specification:'Montserrat Medium';fill:#2a72ac;fill-opacity:1" id="tspan81520">o</tspan></tspan></text>
</g>
<path
style="fill:#ffffff;stroke:#000000;stroke-width:1.32291663;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 246.61693,255.0795 -9.11198,15.78242 a 2.6351497,9.1643514 30 0 0 6.60453,-6.7032 2.6351497,9.1643514 30 0 0 2.50745,-9.07922 z"
id="path84483"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="cccccc"
inkscape:connector-curvature="0"
id="path84481"
d="m 202.36709,199.05917 26.65552,8.43269 21.69622,19.51455 -8.68507,12.39398 -46.04559,-26.61429 z"
style="fill:#ffffff;stroke:#000000;stroke-width:1.32291663;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952" />
<circle
style="fill:#ffffff;stroke:#000000;stroke-width:1.32291663;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
id="path84224"
cx="213.64427"
cy="234.18927"
r="35.482784" />
<circle
r="33.39888"
cy="234.18927"
cx="213.64427"
id="circle84226"
style="fill:url(#radialGradient84463);fill-opacity:1;stroke:none;stroke-width:0.52916664;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952" />
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:0.79375005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
id="rect84114"
width="31.605196"
height="19.16976"
x="304.77545"
y="97.128738"
transform="rotate(30)" />
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
id="rect84116"
width="4.521956"
height="21.377089"
x="300.27435"
y="96.025078"
transform="rotate(30)" />
<rect
y="99.087395"
x="283.71848"
height="15.252436"
width="16.459545"
id="rect84118"
style="fill:#ffffff;stroke:#000000;stroke-width:0.79375005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
transform="rotate(30)" />
<rect
y="98.190086"
x="280.00021"
height="17.047071"
width="3.617183"
id="rect84120"
style="fill:#ffffff;stroke:#000000;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
transform="rotate(30)" />
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:0.79375005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
id="rect84122"
width="16.725054"
height="9.8947001"
x="263.36807"
y="101.76627"
transform="rotate(30)" />
<rect
style="fill:#ffffff;stroke:#000000;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
id="rect84124"
width="4.8383565"
height="11.503917"
x="258.51526"
y="100.96166"
transform="rotate(30)" />
<rect
y="96.025078"
x="336.51093"
height="21.377089"
width="4.521956"
id="rect84126"
style="fill:#ffffff;stroke:#000000;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
transform="rotate(30)" />
<path
style="fill:url(#linearGradient84325);fill-opacity:1;stroke:none;stroke-width:0.79375005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 207.24023,252.71811 25.53907,14.74414 8.52539,-14.76953 -25.53711,-14.74415 z"
id="rect84313"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path84128"
d="m 215.3335,241.36799 22.49734,12.98884"
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path84130"
d="m 246.61693,255.0795 -9.11198,15.78242 a 2.6351497,9.1643514 30 0 0 6.60453,-6.7032 2.6351497,9.1643514 30 0 0 2.50745,-9.07922 z"
style="fill:#ffffff;stroke:#000000;stroke-width:0.79375005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:5.99999952"
d="m 195.97877,212.80238 46.0456,26.61429 -3.50256,6.07342 -46.0456,-26.61429 z"
id="path84134"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#ffffff;stroke:#000000;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:5.99999952"
d="m 202.36709,199.05917 26.65552,8.43269 21.69622,19.51455 -8.68507,12.39398 -46.04559,-26.61429 z"
id="path84136"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:url(#linearGradient84422);fill-opacity:1;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 186.31445,239.41146 1.30078,0.75 7.46485,-12.92968 -1.30078,-0.75 z"
id="rect84410"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient84349);fill-opacity:1;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:5.99999952"
d="m 193.92188,218.48568 44.21289,25.55469 2.44335,-4.23242 -44.21289,-25.55664 z"
id="path84284"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient84363);fill-opacity:1;stroke:none;stroke-width:0.79375005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 189.98438,240.4935 12.42187,7.16992 6.56641,-11.375 -12.42188,-7.16992 z"
id="rect84351"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient84377);fill-opacity:1;stroke:none;stroke-width:0.79375005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 173.69727,227.99936 12.65234,7.30273 3.88867,-6.73633 -12.65234,-7.30273 z"
id="rect84365"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path84138"
d="m 192.47621,218.8758 -11.1013,8.29627 c 0,0 6.16202,4.57403 15.2798,4.67656 9.1178,0.1025 11.46925,-3.93799 11.46925,-3.93799 z"
style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<ellipse
cy="223.01579"
cx="207.08998"
id="circle84140"
style="fill:#ffffff;stroke:#000000;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
rx="3.8395541"
ry="3.8438656" />
<path
style="fill:url(#linearGradient84333);fill-opacity:1;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:round;stroke-dashoffset:5.99999952"
d="m 197.35938,212.35287 44.36523,25.64453 7.58984,-10.83203 -20.82617,-18.73242 -25.55078,-8.08399 z"
id="path84272"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path84142"
d="m 200.6837,212.37603 11.49279,-6.98413 -8.11935,-2.73742"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path84144"
d="m 241.31895,235.3047 -8.04514,-4.75769 10.057,-4.72299"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
sodipodi:nodetypes="ccc" />
<path
sodipodi:nodetypes="ccc"
style="fill:none;fill-rule:evenodd;stroke:#2a72ac;stroke-width:0.52899998;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 241.06868,235.79543 -8.9307,-5.38071 10.81942,-5.07707"
id="path84280"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#2a72ac;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 200.60886,211.70589 10.37702,-6.1817 -7.12581,-2.30459"
id="path84290"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:url(#radialGradient84471);fill-opacity:1;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 206.89258,220.23959 -0.29297,0.0352 -0.23633,0.0527 -0.26953,0.0898 -0.2793,0.125 -0.23437,0.13477 -0.20508,0.14648 -0.2207,0.19532 -0.18946,0.20117 -0.006,0.008 0.004,-0.008 -0.006,0.01 -0.008,0.01 -0.004,0.004 -0.006,0.006 -0.12109,0.1582 -0.002,0.004 -0.002,0.002 -0.16406,0.26758 -0.12109,0.24804 -0.0996,0.28125 -0.0645,0.24219 -0.0371,0.26367 -0.0176,0.31641 0.008,0.18164 0.0332,0.28711 0.0527,0.23437 0.004,0.0117 0.0937,0.28516 0.11133,0.24805 0.13086,0.23046 0.16992,0.23829 0.1836,0.20898 0.21093,0.19727 0.19532,0.14843 0.25586,0.15625 0.24218,0.11719 0.26172,0.0977 0.27344,0.0684 0.27344,0.043 0.29297,0.0137 0.18164,-0.008 0.29687,-0.0351 0.24024,-0.0547 0.27539,-0.0898 0.24218,-0.10938 0.25,-0.14453 0.23047,-0.16406 0.20899,-0.1836 0.20508,-0.21875 0.125,-0.16406 0.004,-0.006 0.1582,-0.25781 0.004,-0.008 0.12695,-0.26172 0.0996,-0.27344 0.002,-0.006 0.0586,-0.24023 0.0391,-0.26563 0.0176,-0.3125 -0.008,-0.17968 -0.0332,-0.28711 -0.0527,-0.23438 -0.004,-0.0117 -0.0937,-0.28515 -0.11132,-0.24805 -0.13086,-0.23047 -0.16993,-0.23828 -0.18554,-0.20899 -0.19922,-0.18945 -0.21875,-0.16406 -0.23828,-0.14844 -0.26563,-0.12695 -0.01,-0.004 -0.21875,-0.0801 -0.28516,-0.0723 -0.27344,-0.043 -0.29492,-0.0137 z"
id="ellipse84292"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient84425);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.79374999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 183.23633,227.10092 c 5.59753,3.20336 12.36881,4.51528 18.71366,3.17108 1.59516,-0.38 3.17489,-0.99021 4.44874,-2.04739 -0.73893,-0.64617 -1.68301,-0.99544 -2.49844,-1.53493 -3.78032,-2.18293 -7.56064,-4.36587 -11.34096,-6.5488 -3.10767,2.32001 -6.21533,4.64003 -9.323,6.96004 z"
id="path84298"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccc" />
<path
style="fill:url(#linearGradient84479);fill-opacity:1;stroke:none;stroke-width:0.79375005;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 238.62695,269.97787 0.006,-0.002 0.39453,-0.27735 0.41797,-0.34179 0.002,-0.002 0.45703,-0.42382 0.47851,-0.49219 0.0156,-0.0176 0.47656,-0.53711 0.002,-0.002 0.0117,-0.0137 0.48438,-0.5918 0.0117,-0.0156 0.49023,-0.64257 0.01,-0.0137 0.49609,-0.69726 0.48047,-0.71875 0.01,-0.0137 0.46485,-0.74805 0.004,-0.008 0.002,-0.002 0.30468,-0.51562 0.008,-0.0117 0.4375,-0.78711 0.40625,-0.77734 0.008,-0.0137 0.37109,-0.77149 0.008,-0.0156 0.33789,-0.75977 0.006,-0.0156 0.30078,-0.73829 0.27148,-0.74609 0.21289,-0.66602 0.17969,-0.66796 v -0.002 l 0.12305,-0.58203 0.002,-0.0137 0.0723,-0.51562 0.0176,-0.31836 z"
id="path84379"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient84408);fill-opacity:1;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 202.78906,251.42318 2.08399,1.20118 9.6289,-16.67969 -2.08203,-1.20117 z"
id="rect84396"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient84441);fill-opacity:1;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 169.0918,226.26889 2.35937,1.36133 4.69336,-8.13086 -2.35937,-1.36133 z"
id="rect84429"
inkscape:connector-curvature="0" />
<path
style="fill:url(#linearGradient84455);fill-opacity:1;stroke:none;stroke-width:0.79374999;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.99999952"
d="m 234.17188,269.53842 2.08203,1.20312 9.63086,-16.67773 -2.08399,-1.20313 z"
id="rect84443"
inkscape:connector-curvature="0" />
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:#f8ead2;stroke-width:0.52916664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 215.55025,240.82707 22.49734,12.98884"
id="path84521"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 14 KiB

144
go.mod
View File

@@ -1,26 +1,136 @@
module github.com/containers/skopeo
go 1.12
go 1.17
require (
github.com/containers/common v0.42.1
github.com/containers/image/v5 v5.15.2
github.com/containers/ocicrypt v1.1.2
github.com/containers/storage v1.34.1
github.com/docker/docker v20.10.8+incompatible
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/go-check/check v0.0.0-20180628173108-788fd7840127
github.com/onsi/gomega v1.15.0 // indirect
github.com/containers/common v0.51.4
github.com/containers/image/v5 v5.24.3
github.com/containers/ocicrypt v1.1.10
github.com/containers/storage v1.45.3
github.com/docker/distribution v2.8.1+incompatible
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6
github.com/opencontainers/image-tools v0.0.0-20170926011501-6d941547fa1d
github.com/pkg/errors v0.9.1
github.com/russross/blackfriday v2.0.0+incompatible // indirect
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.2.1
github.com/opencontainers/image-spec v1.1.0-rc2
github.com/opencontainers/image-tools v1.0.0-rc3
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.8.1
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
go4.org v0.0.0-20190218023631-ce4c26f7be8e // indirect
golang.org/x/term v0.17.0
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/Microsoft/go-winio v0.6.0 // indirect
github.com/Microsoft/hcsshim v0.9.6 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/containerd/cgroups v1.0.4 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.13.0 // indirect
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
github.com/coreos/go-oidc/v3 v3.5.0 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/docker v20.10.23+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/runtime v0.24.1 // indirect
github.com/go-openapi/spec v0.20.7 // indirect
github.com/go-openapi/strfmt v0.21.3 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-openapi/validate v0.22.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-containerregistry v0.13.0 // indirect
github.com/google/go-intervals v0.0.2 // indirect
github.com/google/trillian v1.5.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.15 // indirect
github.com/klauspost/pgzip v1.2.6-0.20220930104621-17e8dac29df8 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/letsencrypt/boulder v0.0.0-20230130200452-c091e64aa391 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-shellwords v1.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mistifyio/go-zfs/v3 v3.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/sys/mountinfo v0.6.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/runc v1.1.4 // indirect
github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb // indirect
github.com/opencontainers/selinux v1.10.2 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/proglottis/gpgme v0.1.3 // indirect
github.com/rivo/uniseg v0.4.3 // indirect
github.com/rogpeppe/go-internal v1.8.0 // indirect
github.com/russross/blackfriday v2.0.0+incompatible // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sigstore/fulcio v1.0.0 // indirect
github.com/sigstore/rekor v1.0.1 // indirect
github.com/sigstore/sigstore v1.5.2 // indirect
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
github.com/sylabs/sif/v2 v2.9.0 // indirect
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/vbauerster/mpb/v7 v7.5.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.mongodb.org/mongo-driver v1.11.1 // indirect
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/mod v0.8.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.7.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.6.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.56.3 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

757
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
cc -E - > /dev/null 2> /dev/null << EOF
#include <btrfs/ioctl.h>
EOF

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
cc -E - > /dev/null 2> /dev/null << EOF
#include <btrfs/version.h>
EOF

34
hack/get_fqin.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# This script is intended to be called from the Makefile. It's purpose
# is to automation correspondence between the environment used for local
# development and CI.
set -e
SCRIPT_FILEPATH=$(realpath "${BASH_SOURCE[0]}")
SCRIPT_DIRPATH=$(dirname "$SCRIPT_FILEPATH")
REPO_DIRPATH=$(realpath "$SCRIPT_DIRPATH/../")
# When running under CI, we already have the necessary information,
# simply provide it to the Makefile.
if [[ -n "$SKOPEO_CIDEV_CONTAINER_FQIN" ]]; then
echo "$SKOPEO_CIDEV_CONTAINER_FQIN"
exit 0
fi
if [[ -n $(command -v podman) ]]; then CONTAINER_RUNTIME=podman; fi
CONTAINER_RUNTIME=${CONTAINER_RUNTIME:-docker}
# Borrow the get_ci_vm container image since it's small, and
# by necessity contains a script that can accurately interpret
# env. var. values from any .cirrus.yml runtime context.
$CONTAINER_RUNTIME run --rm \
--security-opt label=disable \
-v $REPO_DIRPATH:/src:ro \
--entrypoint=/usr/share/automation/bin/cirrus-ci_env.py \
quay.io/libpod/get_ci_vm:latest \
--envs="Skopeo Test" /src/.cirrus.yml | \
egrep -m1 '^SKOPEO_CIDEV_CONTAINER_FQIN' | \
awk -F "=" -e '{print $2}' | \
tr -d \'\"

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
tmpdir="$PWD/tmp.$RANDOM"
mkdir -p "$tmpdir"
trap 'rm -fr "$tmpdir"' EXIT

19
hack/libsubid_tag.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
if test $(${GO:-go} env GOOS) != "linux" ; then
exit 0
fi
tmpdir="$PWD/tmp.$RANDOM"
mkdir -p "$tmpdir"
trap 'rm -fr "$tmpdir"' EXIT
cc -o "$tmpdir"/libsubid_tag -l subid -x c - > /dev/null 2> /dev/null << EOF
#include <shadow/subid.h>
int main() {
struct subid_range *ranges = NULL;
get_subuid_ranges("root", &ranges);
free(ranges);
return 0;
}
EOF
if test $? -eq 0 ; then
echo libsubid
fi

View File

@@ -2,15 +2,14 @@
set -e
# This script builds various binary from a checkout of the skopeo
# source code.
# source code. DO NOT CALL THIS SCRIPT DIRECTLY.
#
# Requirements:
# - The current directory should be a checkout of the skopeo source code
# (https://github.com/containers/skopeo). Whatever version is checked out
# will be built.
# - The script is intended to be run inside the docker container specified
# in the Dockerfile at the root of the source. In other words:
# DO NOT CALL THIS SCRIPT DIRECTLY.
# - The script is intended to be run inside the container specified
# in the output of hack/get_fqin.sh
# - The right way to call this script is to invoke "make" from
# your checkout of the skopeo repository.
# the Makefile will do a "docker build -t skopeo ." and then
@@ -23,21 +22,19 @@ export SKOPEO_PKG='github.com/containers/skopeo'
export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
export MAKEDIR="$SCRIPTDIR/make"
# We're a nice, sexy, little shell script, and people might try to run us;
# but really, they shouldn't. We want to be in a container!
# The magic value is defined inside our Dockerfile.
if [[ "$container_magic" != "85531765-346b-4316-bdb8-358e4cca9e5d" ]]; then
{
echo "# WARNING! I don't seem to be running in a Docker container."
echo "# The result of this command might be an incorrect build, and will not be"
echo "# officially supported."
echo "#"
echo "# Try this instead: make all"
echo "#"
} >&2
else
echo "# I appear to be running inside my designated container image, good!"
export SKOPEO_CONTAINER_TESTS=1
# Set this to 1 to enable installation/modification of environment/services
export SKOPEO_CONTAINER_TESTS=${SKOPEO_CONTAINER_TESTS:-0}
if [[ "$SKOPEO_CONTAINER_TESTS" == "0" ]] && [[ "$CI" != "true" ]]; then
(
echo "***************************************************************"
echo "WARNING: Executing tests directly on the local development"
echo " host is highly discouraged. Many important items"
echo " will be skipped. For manual execution, please utilize"
echo " the Makefile targets WITHOUT the '-local' suffix."
echo "***************************************************************"
) > /dev/stderr
sleep 5
fi
echo
@@ -56,8 +53,6 @@ DEFAULT_BUNDLES=(
test-integration
)
TESTFLAGS+=" -test.timeout=15m"
# Go module support: set `-mod=vendor` to use the vendored sources
# See also the top-level Makefile.
mod_vendor=
@@ -66,16 +61,6 @@ if go help mod >/dev/null 2>&1; then
mod_vendor='-mod=vendor'
fi
# If $TESTFLAGS is set in the environment, it is passed as extra arguments to 'go test'.
# You can use this to select certain tests to run, eg.
#
# TESTFLAGS='-test.run ^TestBuild$' ./hack/make.sh test-unit
#
# For integration-cli test, we use [gocheck](https://labix.org/gocheck), if you want
# to run certain tests on your local host, you should run with command:
#
# TESTFLAGS='-check.f DockerSuite.TestBuild*' ./hack/make.sh binary test-integration-cli
#
go_test_dir() {
dir=$1
(

View File

@@ -2,13 +2,11 @@
set -e
bundle_test_integration() {
TESTFLAGS="$TESTFLAGS -check.v"
go_test_dir ./integration
}
# subshell so that we can export PATH without breaking other things
(
make bin/skopeo ${BUILDTAGS:+BUILDTAGS="$BUILDTAGS"}
make PREFIX=/usr install
bundle_test_integration
) 2>&1

View File

@@ -1,17 +1,43 @@
#!/bin/bash
set -e
# Before running podman for the first time, make sure
# to set storage to vfs (not overlay): podman-in-podman
# doesn't work with overlay. And, disable mountopt,
# which causes error with vfs.
sed -i \
-e 's/^driver\s*=.*/driver = "vfs"/' \
-e 's/^mountopt/#mountopt/' \
/etc/containers/storage.conf
# These tests can run in/outside of a container. However,
# not all storage drivers are supported in a container
# environment. Detect this and setup storage when
# running in a container.
#
# Paradoxically (FIXME: clean this up), SKOPEO_CONTAINER_TESTS is set
# both inside a container and without a container (in a CI VM); it actually means
# "it is safe to desctructively modify the system for tests".
#
# On a CI VM, we can just use Podman as it is already configured; the changes below,
# to use VFS, are necessary only inside a container, because overlay-inside-overlay
# does not work. So, make these changes conditional on both
# SKOPEO_CONTAINER_TESTS (for acceptability to do destructive modification) and !CI
# (for necessity to adjust for in-container operation)
if ((SKOPEO_CONTAINER_TESTS)) && [[ "$CI" != true ]]; then
if [[ -r /etc/containers/storage.conf ]]; then
echo "MODIFYING existing storage.conf"
sed -i \
-e 's/^driver\s*=.*/driver = "vfs"/' \
-e 's/^mountopt/#mountopt/' \
/etc/containers/storage.conf
else
echo "CREATING NEW storage.conf"
cat >> /etc/containers/storage.conf << EOF
[storage]
driver = "vfs"
runroot = "/run/containers/storage"
graphroot = "/var/lib/containers/storage"
EOF
fi
# The logic of finding the relevant storage.conf file is convoluted
# and in effect differs between Skopeo and Podman, at least in some versions;
# explicitly point at the file we want to use to hopefully avoid that.
export CONTAINERS_STORAGE_CONF=/etc/containers/storage.conf
fi
# Build skopeo, install into /usr/bin
make bin/skopeo ${BUILDTAGS:+BUILDTAGS="$BUILDTAGS"}
make PREFIX=/usr install
# Run tests

View File

@@ -1,6 +1,6 @@
#!/bin/bash
errors=$(go vet $mod_vendor $(go list $mod_vendor -e ./...))
errors=$(go vet -tags="${BUILDTAGS}" $mod_vendor $(go list $mod_vendor -e ./...))
if [ -z "$errors" ]; then
echo 'Congratulations! All Go source files have been vetted.'

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -e
STATUS=$(git status --porcelain)

View File

@@ -1,7 +1,8 @@
# Installing from packages
# Installing Skopeo
## Distribution Packages
`skopeo` may already be packaged in your distribution.
`skopeo` may already be packaged in your distribution. This document lists the
installation steps for many distros, along with their information and support links.
### Fedora
@@ -9,34 +10,26 @@
sudo dnf -y install skopeo
```
### RHEL/CentOS ≥ 8 and CentOS Stream
[Package Info](https://src.fedoraproject.org/rpms/skopeo) and
[Bugzilla](https://bugzilla.redhat.com/buglist.cgi?bug_status=__open__&classification=Fedora&component=skopeo&product=Fedora)
Fedora bugs can be reported on the Skopeo GitHub [Issues](https://github.com/containers/skopeo/issues) page.
### RHEL / CentOS Stream ≥ 8
```sh
sudo dnf -y install skopeo
```
Newer Skopeo releases may be available on the repositories provided by the
Kubic project. Beware, these may not be suitable for production environments.
If you are a RHEL customer, please reach out through the official RHEL support
channels for any issues.
on CentOS 8:
CentOS Stream 9: [Package Info](https://gitlab.com/redhat/centos-stream/rpms/skopeo/-/tree/c9s) and
[Bugzilla](https://bugzilla.redhat.com/buglist.cgi?bug_status=__open__&classification=Red%20Hat&component=skopeo&product=Red%20Hat%20Enterprise%20Linux%209&version=CentOS%20Stream)
```sh
sudo dnf -y module disable container-tools
sudo dnf -y install 'dnf-command(copr)'
sudo dnf -y copr enable rhcontainerbot/container-selinux
sudo curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/CentOS_8/devel:kubic:libcontainers:stable.repo
sudo dnf -y install skopeo
```
CentOS Stream 8: [Package Info](https://git.centos.org/rpms/skopeo/tree/c8s-stream-rhel8) and
[Bugzilla](https://bugzilla.redhat.com/buglist.cgi?bug_status=__open__&classification=Red%20Hat&component=skopeo&product=Red%20Hat%20Enterprise%20Linux%208&version=CentOS%20Stream)
on CentOS 8 Stream:
```sh
sudo dnf -y module disable container-tools
sudo dnf -y install 'dnf-command(copr)'
sudo dnf -y copr enable rhcontainerbot/container-selinux
sudo curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/CentOS_8_Stream/devel:kubic:libcontainers:stable.repo
sudo dnf -y install skopeo
```
### RHEL/CentOS ≤ 7.x
@@ -44,18 +37,24 @@ sudo dnf -y install skopeo
sudo yum -y install skopeo
```
CentOS 7: [Package Repo](https://git.centos.org/rpms/skopeo/tree/c7-extras)
### openSUSE
```sh
sudo zypper install skopeo
```
[Package Info](https://software.opensuse.org/package/skopeo)
### Alpine
```sh
sudo apk add skopeo
```
[Package Info](https://pkgs.alpinelinux.org/packages?name=skopeo)
### macOS
```sh
@@ -67,18 +66,21 @@ brew install skopeo
$ nix-env -i skopeo
```
[Package Info](https://search.nixos.org/packages?&show=skopeo&query=skopeo)
### Debian
The skopeo package is available in
the [Bullseye (testing) branch](https://packages.debian.org/bullseye/skopeo), which
will be the next stable release (Debian 11) as well as Debian Unstable/Sid.
The skopeo package is available on [Bullseye](https://packages.debian.org/bullseye/skopeo),
and Debian Testing and Unstable.
```bash
# Debian Testing/Bullseye or Unstable/Sid
# Debian Bullseye, Testing or Unstable/Sid
sudo apt-get update
sudo apt-get -y install skopeo
```
[Package Info](https://packages.debian.org/stable/skopeo)
### Raspberry Pi OS arm64 (beta)
Raspberry Pi OS uses the standard Debian's repositories,
@@ -97,27 +99,27 @@ sudo apt-get -y update
sudo apt-get -y install skopeo
```
If you would prefer newer (though not as well-tested) packages,
the [Kubic project](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/skopeo)
provides packages for active Ubuntu releases 20.04 and newer (it should also work with direct derivatives like Pop!\_OS).
Checkout the [Kubic project page](https://build.opensuse.org/package/show/devel:kubic:libcontainers:stable/skopeo)
for a list of supported Ubuntu version and
architecture combinations. **NOTE:** The command `sudo apt-get -y upgrade`
maybe required in some cases if Skopeo cannot be installed without it.
The build sources for the Kubic packages can be found [here](https://gitlab.com/rhcontainerbot/skopeo/-/tree/debian/debian).
[Package Info](https://packages.ubuntu.com/jammy/skopeo)
CAUTION: On Ubuntu 20.10 and newer, we highly recommend you use Buildah, Podman and Skopeo ONLY from EITHER the Kubic repo
OR the official Ubuntu repos. Mixing and matching may lead to unpredictable situations including installation conflicts.
### Windows
Skopeo has not yet been packaged for Windows. There is an [open feature
request](https://github.com/containers/skopeo/issues/715) and contributions are
always welcome.
## Container Images
Skopeo container images are available at `quay.io/skopeo/stable:latest`.
For example,
```bash
. /etc/os-release
echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list
curl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | sudo apt-key add -
sudo apt-get update
sudo apt-get -y upgrade
sudo apt-get -y install skopeo
podman run docker://quay.io/skopeo/stable:latest copy --help
```
[Read more](./contrib/skopeoimage/README.md).
## Building from Source
Otherwise, read on for building and installing it from source:
@@ -126,8 +128,6 @@ To build the `skopeo` binary you need at least Go 1.12.
There are two ways to build skopeo: in a container, or locally without a
container. Choose the one which better matches your needs and environment.
## Building from Source
### Building without a container
Building without a container requires a bit more manual work and setup in your
@@ -212,6 +212,13 @@ Building in a container is simpler, but more restrictive:
$ make binary
```
### Shell completion scripts
Skopeo has shell completion scripts for bash, zsh, fish and powershell. They are installed as part of `make install`.
You may have to restart your shell in order for them to take effect.
For instructions to manually generate and load the scripts please see `skopeo completion --help`.
### Installation
Finally, after the binary and documentation is built:
@@ -219,3 +226,41 @@ Finally, after the binary and documentation is built:
```bash
sudo make install
```
### Building a static binary
There have been efforts in the past to produce and maintain static builds, but the maintainers prefer to run Skopeo using distro packages or within containers. This is because static builds of Skopeo tend to be unreliable and functionally restricted. Specifically:
- Some features of Skopeo depend on non-Go libraries like `libgpgme` and `libdevmapper`.
- Generating static Go binaries uses native Go libraries, which don't support e.g. `.local` or LDAP-based name resolution.
That being said, if you would like to build Skopeo statically, you might be able to do it by combining all the following steps.
- Export environment variable `CGO_ENABLED=0` (disabling CGO causes Go to prefer native libraries when possible, instead of dynamically linking against system libraries).
- Set the `BUILDTAGS=containers_image_openpgp` Make variable (this remove the dependency on `libgpgme` and its companion libraries).
- Clear the `GO_DYN_FLAGS` Make variable (which otherwise seems to force the creation of a dynamic executable).
The following command implements these steps to produce a static binary in the `bin` subdirectory of the repository:
```bash
docker run -v $PWD:/src -w /src -e CGO_ENABLED=0 golang \
make BUILDTAGS=containers_image_openpgp GO_DYN_FLAGS=
```
Keep in mind that the resulting binary is unsupported and might crash randomly. Only use if you know what you're doing!
For more information, history, and context about static builds, check the following issues:
- [#391] - Consider distributing statically built binaries as part of release
- [#669] - Static build fails with segmentation violation
- [#670] - Fixing static binary build using container
- [#755] - Remove static and in-container targets from Makefile
- [#932] - Add nix derivation for static builds
- [#1336] - Unable to run skopeo on Fedora 30 (due to dyn lib dependency)
- [#1478] - Publish binary releases to GitHub (request+discussion)
[#391]: https://github.com/containers/skopeo/issues/391
[#669]: https://github.com/containers/skopeo/issues/669
[#670]: https://github.com/containers/skopeo/issues/670
[#755]: https://github.com/containers/skopeo/issues/755
[#932]: https://github.com/containers/skopeo/issues/932
[#1336]: https://github.com/containers/skopeo/issues/1336
[#1478]: https://github.com/containers/skopeo/issues/1478

View File

@@ -1,7 +1,7 @@
package main
import (
"github.com/go-check/check"
"gopkg.in/check.v1"
)
const blockedRegistriesConf = "./fixtures/blocked-registries.conf"

View File

@@ -6,7 +6,7 @@ import (
"testing"
"github.com/containers/skopeo/version"
"github.com/go-check/check"
"gopkg.in/check.v1"
)
const (
@@ -36,12 +36,12 @@ func (s *SkopeoSuite) SetUpSuite(c *check.C) {
func (s *SkopeoSuite) TearDownSuite(c *check.C) {
if s.regV2 != nil {
s.regV2.Close()
s.regV2.tearDown(c)
}
if s.regV2WithAuth != nil {
//cmd := exec.Command("docker", "logout", s.regV2WithAuth)
//c.Assert(cmd.Run(), check.IsNil)
s.regV2WithAuth.Close()
s.regV2WithAuth.tearDown(c)
}
}
@@ -49,25 +49,25 @@ func (s *SkopeoSuite) TearDownSuite(c *check.C) {
//func skopeoCmd()
func (s *SkopeoSuite) TestVersion(c *check.C) {
wanted := fmt.Sprintf(".*%s version %s.*", skopeoBinary, version.Version)
assertSkopeoSucceeds(c, wanted, "--version")
assertSkopeoSucceeds(c, fmt.Sprintf(".*%s version %s.*", skopeoBinary, version.Version),
"--version")
}
func (s *SkopeoSuite) TestCanAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) {
wanted := ".*manifest unknown: manifest unknown.*"
assertSkopeoFails(c, wanted, "--tls-verify=false", "inspect", "--creds="+s.regV2WithAuth.username+":"+s.regV2WithAuth.password, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
assertSkopeoFails(c, ".*manifest unknown.*",
"--tls-verify=false", "inspect", "--creds="+s.regV2WithAuth.username+":"+s.regV2WithAuth.password, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
}
func (s *SkopeoSuite) TestNeedAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) {
wanted := ".*unauthorized: authentication required.*"
assertSkopeoFails(c, wanted, "--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
assertSkopeoFails(c, ".*authentication required.*",
"--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
}
func (s *SkopeoSuite) TestCertDirInsteadOfCertPath(c *check.C) {
wanted := ".*unknown flag: --cert-path.*"
assertSkopeoFails(c, wanted, "--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "--cert-path=/")
wanted = ".*unauthorized: authentication required.*"
assertSkopeoFails(c, wanted, "--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "--cert-dir=/etc/docker/certs.d/")
assertSkopeoFails(c, ".*unknown flag: --cert-path.*",
"--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "--cert-path=/")
assertSkopeoFails(c, ".*authentication required.*",
"--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "--cert-dir=/etc/docker/certs.d/")
}
// TODO(runcom): as soon as we can push to registries ensure you can inspect here
@@ -75,10 +75,8 @@ func (s *SkopeoSuite) TestCertDirInsteadOfCertPath(c *check.C) {
func (s *SkopeoSuite) TestNoNeedAuthToPrivateRegistryV2ImageNotFound(c *check.C) {
out, err := exec.Command(skopeoBinary, "--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2.url)).CombinedOutput()
c.Assert(err, check.NotNil, check.Commentf(string(out)))
wanted := ".*manifest unknown.*"
c.Assert(string(out), check.Matches, "(?s)"+wanted) // (?s) : '.' will also match newlines
wanted = ".*unauthorized: authentication required.*"
c.Assert(string(out), check.Not(check.Matches), "(?s)"+wanted) // (?s) : '.' will also match newlines
c.Assert(string(out), check.Matches, "(?s).*manifest unknown.*") // (?s) : '.' will also match newlines
c.Assert(string(out), check.Not(check.Matches), "(?s).*unauthorized: authentication required.*") // (?s) : '.' will also match newlines
}
func (s *SkopeoSuite) TestInspectFailsWhenReferenceIsInvalid(c *check.C) {
@@ -86,28 +84,28 @@ func (s *SkopeoSuite) TestInspectFailsWhenReferenceIsInvalid(c *check.C) {
}
func (s *SkopeoSuite) TestLoginLogout(c *check.C) {
wanted := "^Login Succeeded!\n$"
assertSkopeoSucceeds(c, wanted, "login", "--tls-verify=false", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, s.regV2WithAuth.url)
assertSkopeoSucceeds(c, "^Login Succeeded!\n$",
"login", "--tls-verify=false", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, s.regV2WithAuth.url)
// test --get-login returns username
wanted = fmt.Sprintf("^%s\n$", s.regV2WithAuth.username)
assertSkopeoSucceeds(c, wanted, "login", "--tls-verify=false", "--get-login", s.regV2WithAuth.url)
assertSkopeoSucceeds(c, fmt.Sprintf("^%s\n$", s.regV2WithAuth.username),
"login", "--tls-verify=false", "--get-login", s.regV2WithAuth.url)
// test logout
wanted = fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url)
assertSkopeoSucceeds(c, wanted, "logout", s.regV2WithAuth.url)
assertSkopeoSucceeds(c, fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url),
"logout", s.regV2WithAuth.url)
}
func (s *SkopeoSuite) TestCopyWithLocalAuth(c *check.C) {
wanted := "^Login Succeeded!\n$"
assertSkopeoSucceeds(c, wanted, "login", "--tls-verify=false", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, s.regV2WithAuth.url)
assertSkopeoSucceeds(c, "^Login Succeeded!\n$",
"login", "--tls-verify=false", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, s.regV2WithAuth.url)
// copy to private registry using local authentication
imageName := fmt.Sprintf("docker://%s/busybox:mine", s.regV2WithAuth.url)
assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", testFQIN+":latest", imageName)
// inspect from private registry
assertSkopeoSucceeds(c, "", "inspect", "--tls-verify=false", imageName)
// logout from the registry
wanted = fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url)
assertSkopeoSucceeds(c, wanted, "logout", s.regV2WithAuth.url)
assertSkopeoSucceeds(c, fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url),
"logout", s.regV2WithAuth.url)
// inspect from private registry should fail after logout
wanted = ".*unauthorized: authentication required.*"
assertSkopeoFails(c, wanted, "inspect", "--tls-verify=false", imageName)
assertSkopeoFails(c, ".*authentication required.*",
"inspect", "--tls-verify=false", imageName)
}

View File

@@ -6,7 +6,7 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
"io/fs"
"log"
"net/http"
"net/http/httptest"
@@ -17,10 +17,10 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/types"
"github.com/go-check/check"
digest "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/image-tools/image"
"gopkg.in/check.v1"
)
func init() {
@@ -31,7 +31,8 @@ const (
v2DockerRegistryURL = "localhost:5555" // Update also policy.json
v2s1DockerRegistryURL = "localhost:5556"
knownWindowsOnlyImage = "docker://mcr.microsoft.com/windows/nanoserver:1909"
knownListImage = "docker://registry.fedoraproject.org/fedora-minimal" // could have either ":latest" or "@sha256:..." appended
knownListImageRepo = "docker://registry.fedoraproject.org/fedora-minimal"
knownListImage = knownListImageRepo + ":38"
)
type CopySuite struct {
@@ -64,9 +65,7 @@ func (s *CopySuite) SetUpSuite(c *check.C) {
s.registry = setupRegistryV2At(c, v2DockerRegistryURL, false, false)
s.s1Registry = setupRegistryV2At(c, v2s1DockerRegistryURL, false, true)
gpgHome, err := ioutil.TempDir("", "skopeo-gpg")
c.Assert(err, check.IsNil)
s.gpgHome = gpgHome
s.gpgHome = c.MkDir()
os.Setenv("GNUPGHOME", s.gpgHome)
for _, key := range []string{"personal", "official"} {
@@ -75,21 +74,18 @@ func (s *CopySuite) SetUpSuite(c *check.C) {
runCommandWithInput(c, batchInput, gpgBinary, "--batch", "--gen-key")
out := combinedOutputOfCommand(c, gpgBinary, "--armor", "--export", fmt.Sprintf("%s@example.com", key))
err := ioutil.WriteFile(filepath.Join(s.gpgHome, fmt.Sprintf("%s-pubkey.gpg", key)),
err := os.WriteFile(filepath.Join(s.gpgHome, fmt.Sprintf("%s-pubkey.gpg", key)),
[]byte(out), 0600)
c.Assert(err, check.IsNil)
}
}
func (s *CopySuite) TearDownSuite(c *check.C) {
if s.gpgHome != "" {
os.RemoveAll(s.gpgHome)
}
if s.registry != nil {
s.registry.Close()
s.registry.tearDown(c)
}
if s.s1Registry != nil {
s.s1Registry.Close()
s.s1Registry.tearDown(c)
}
if s.cluster != nil {
s.cluster.tearDown(c)
@@ -97,104 +93,81 @@ func (s *CopySuite) TearDownSuite(c *check.C) {
}
func (s *CopySuite) TestCopyWithManifestList(c *check.C) {
dir, err := ioutil.TempDir("", "copy-manifest-list")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir)
dir := c.MkDir()
assertSkopeoSucceeds(c, "", "copy", knownListImage, "dir:"+dir)
}
func (s *CopySuite) TestCopyAllWithManifestList(c *check.C) {
dir, err := ioutil.TempDir("", "copy-all-manifest-list")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir)
dir := c.MkDir()
assertSkopeoSucceeds(c, "", "copy", "--all", knownListImage, "dir:"+dir)
}
func (s *CopySuite) TestCopyAllWithManifestListRoundTrip(c *check.C) {
oci1, err := ioutil.TempDir("", "copy-all-manifest-list-oci")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci1)
oci2, err := ioutil.TempDir("", "copy-all-manifest-list-oci")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci2)
dir1, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
assertSkopeoSucceeds(c, "", "copy", "--all", knownListImage, "oci:"+oci1)
assertSkopeoSucceeds(c, "", "copy", "--all", "oci:"+oci1, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "--all", "dir:"+dir1, "oci:"+oci2)
assertSkopeoSucceeds(c, "", "copy", "--all", "oci:"+oci2, "dir:"+dir2)
oci1 := c.MkDir()
oci2 := c.MkDir()
dir1 := c.MkDir()
dir2 := c.MkDir()
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", knownListImage, "oci:"+oci1)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "dir:"+dir1, "oci:"+oci2)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "oci:"+oci2, "dir:"+dir2)
assertDirImagesAreEqual(c, dir1, dir2)
out := combinedOutputOfCommand(c, "diff", "-urN", oci1, oci2)
c.Assert(out, check.Equals, "")
}
func (s *CopySuite) TestCopyAllWithManifestListConverge(c *check.C) {
oci1, err := ioutil.TempDir("", "copy-all-manifest-list-oci")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci1)
oci2, err := ioutil.TempDir("", "copy-all-manifest-list-oci")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci2)
dir1, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
assertSkopeoSucceeds(c, "", "copy", "--all", knownListImage, "oci:"+oci1)
assertSkopeoSucceeds(c, "", "copy", "--all", "oci:"+oci1, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "--all", "--format", "oci", knownListImage, "dir:"+dir2)
assertSkopeoSucceeds(c, "", "copy", "--all", "dir:"+dir2, "oci:"+oci2)
oci1 := c.MkDir()
oci2 := c.MkDir()
dir1 := c.MkDir()
dir2 := c.MkDir()
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", knownListImage, "oci:"+oci1)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "--format", "oci", knownListImage, "dir:"+dir2)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "dir:"+dir2, "oci:"+oci2)
assertDirImagesAreEqual(c, dir1, dir2)
out := combinedOutputOfCommand(c, "diff", "-urN", oci1, oci2)
c.Assert(out, check.Equals, "")
}
func (s *CopySuite) TestCopyNoneWithManifestList(c *check.C) {
dir1 := c.MkDir()
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=index-only", knownListImage, "dir:"+dir1)
manifestPath := filepath.Join(dir1, "manifest.json")
readManifest, err := os.ReadFile(manifestPath)
c.Assert(err, check.IsNil)
mimeType := manifest.GuessMIMEType(readManifest)
c.Assert(mimeType, check.Equals, "application/vnd.docker.distribution.manifest.list.v2+json")
out := combinedOutputOfCommand(c, "ls", "-1", dir1)
c.Assert(out, check.Equals, "manifest.json\nversion\n")
}
func (s *CopySuite) TestCopyWithManifestListConverge(c *check.C) {
oci1, err := ioutil.TempDir("", "copy-all-manifest-list-oci")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci1)
oci2, err := ioutil.TempDir("", "copy-all-manifest-list-oci")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci2)
dir1, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-all-manifest-list-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
oci1 := c.MkDir()
oci2 := c.MkDir()
dir1 := c.MkDir()
dir2 := c.MkDir()
assertSkopeoSucceeds(c, "", "copy", knownListImage, "oci:"+oci1)
assertSkopeoSucceeds(c, "", "copy", "--all", "oci:"+oci1, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "--format", "oci", knownListImage, "dir:"+dir2)
assertSkopeoSucceeds(c, "", "copy", "--all", "dir:"+dir2, "oci:"+oci2)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", "dir:"+dir2, "oci:"+oci2)
assertDirImagesAreEqual(c, dir1, dir2)
out := combinedOutputOfCommand(c, "diff", "-urN", oci1, oci2)
c.Assert(out, check.Equals, "")
}
func (s *CopySuite) TestCopyAllWithManifestListStorageFails(c *check.C) {
storage, err := ioutil.TempDir("", "copy-storage")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
assertSkopeoFails(c, `.*destination transport .* does not support copying multiple images as a group.*`, "copy", "--all", knownListImage, "containers-storage:"+storage+"test")
assertSkopeoFails(c, `.*destination transport .* does not support copying multiple images as a group.*`, "copy", "--multi-arch=all", knownListImage, "containers-storage:"+storage+"test")
}
func (s *CopySuite) TestCopyWithManifestListStorage(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
dir1, err := ioutil.TempDir("", "copy-manifest-list-storage-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-manifest-list-storage-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
dir1 := c.MkDir()
dir2 := c.MkDir()
assertSkopeoSucceeds(c, "", "copy", knownListImage, "containers-storage:"+storage+"test")
assertSkopeoSucceeds(c, "", "copy", knownListImage, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "containers-storage:"+storage+"test", "dir:"+dir2)
@@ -203,16 +176,10 @@ func (s *CopySuite) TestCopyWithManifestListStorage(c *check.C) {
}
func (s *CopySuite) TestCopyWithManifestListStorageMultiple(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage-multiple")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
dir1, err := ioutil.TempDir("", "copy-manifest-list-storage-multiple-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-manifest-list-storage-multiple-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
dir1 := c.MkDir()
dir2 := c.MkDir()
assertSkopeoSucceeds(c, "", "--override-arch", "amd64", "copy", knownListImage, "containers-storage:"+storage+"test")
assertSkopeoSucceeds(c, "", "--override-arch", "arm64", "copy", knownListImage, "containers-storage:"+storage+"test")
assertSkopeoSucceeds(c, "", "--override-arch", "arm64", "copy", knownListImage, "dir:"+dir1)
@@ -222,24 +189,16 @@ func (s *CopySuite) TestCopyWithManifestListStorageMultiple(c *check.C) {
}
func (s *CopySuite) TestCopyWithManifestListDigest(c *check.C) {
dir1, err := ioutil.TempDir("", "copy-manifest-list-digest-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-manifest-list-digest-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
oci1, err := ioutil.TempDir("", "copy-manifest-list-digest-oci")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci1)
oci2, err := ioutil.TempDir("", "copy-manifest-list-digest-oci")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci2)
dir1 := c.MkDir()
dir2 := c.MkDir()
oci1 := c.MkDir()
oci2 := c.MkDir()
m := combinedOutputOfCommand(c, skopeoBinary, "inspect", "--raw", knownListImage)
manifestDigest, err := manifest.Digest([]byte(m))
c.Assert(err, check.IsNil)
digest := manifestDigest.String()
assertSkopeoSucceeds(c, "", "copy", knownListImage+"@"+digest, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "--all", knownListImage+"@"+digest, "dir:"+dir2)
assertSkopeoSucceeds(c, "", "copy", knownListImageRepo+"@"+digest, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "--multi-arch=all", knownListImageRepo+"@"+digest, "dir:"+dir2)
assertSkopeoSucceeds(c, "", "copy", "dir:"+dir1, "oci:"+oci1)
assertSkopeoSucceeds(c, "", "copy", "dir:"+dir2, "oci:"+oci2)
out := combinedOutputOfCommand(c, "diff", "-urN", oci1, oci2)
@@ -247,68 +206,50 @@ func (s *CopySuite) TestCopyWithManifestListDigest(c *check.C) {
}
func (s *CopySuite) TestCopyWithDigestfileOutput(c *check.C) {
tempdir, err := ioutil.TempDir("", "tempdir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tempdir)
dir1, err := ioutil.TempDir("", "copy-manifest-list-digest-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
tempdir := c.MkDir()
dir1 := c.MkDir()
digestOutPath := filepath.Join(tempdir, "digest.txt")
assertSkopeoSucceeds(c, "", "copy", "--digestfile="+digestOutPath, knownListImage, "dir:"+dir1)
readDigest, err := ioutil.ReadFile(digestOutPath)
readDigest, err := os.ReadFile(digestOutPath)
c.Assert(err, check.IsNil)
_, err = digest.Parse(string(readDigest))
c.Assert(err, check.IsNil)
}
func (s *CopySuite) TestCopyWithManifestListStorageDigest(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage-digest")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
dir1, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
dir1 := c.MkDir()
dir2 := c.MkDir()
m := combinedOutputOfCommand(c, skopeoBinary, "inspect", "--raw", knownListImage)
manifestDigest, err := manifest.Digest([]byte(m))
c.Assert(err, check.IsNil)
digest := manifestDigest.String()
assertSkopeoSucceeds(c, "", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "copy", "containers-storage:"+storage+"test@"+digest, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", knownListImage+"@"+digest, "dir:"+dir2)
assertSkopeoSucceeds(c, "", "copy", knownListImageRepo+"@"+digest, "dir:"+dir2)
runDecompressDirs(c, "", dir1, dir2)
assertDirImagesAreEqual(c, dir1, dir2)
}
func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArches(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage-digest")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
dir1, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-dir")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
dir1 := c.MkDir()
dir2 := c.MkDir()
m := combinedOutputOfCommand(c, skopeoBinary, "inspect", "--raw", knownListImage)
manifestDigest, err := manifest.Digest([]byte(m))
c.Assert(err, check.IsNil)
digest := manifestDigest.String()
assertSkopeoSucceeds(c, "", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "copy", "containers-storage:"+storage+"test@"+digest, "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", knownListImage+"@"+digest, "dir:"+dir2)
assertSkopeoSucceeds(c, "", "copy", knownListImageRepo+"@"+digest, "dir:"+dir2)
runDecompressDirs(c, "", dir1, dir2)
assertDirImagesAreEqual(c, dir1, dir2)
}
func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesBothUseListDigest(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-multiple-arches-both")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
m := combinedOutputOfCommand(c, skopeoBinary, "inspect", "--raw", knownListImage)
manifestDigest, err := manifest.Digest([]byte(m))
@@ -316,8 +257,8 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesBothUseLi
digest := manifestDigest.String()
_, err = manifest.ListFromBlob([]byte(m), manifest.GuessMIMEType([]byte(m)))
c.Assert(err, check.IsNil)
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoFails(c, `.*reading manifest for image instance.*does not exist.*`, "--override-arch=amd64", "inspect", "containers-storage:"+storage+"test@"+digest)
assertSkopeoFails(c, `.*reading manifest for image instance.*does not exist.*`, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
i2 := combinedOutputOfCommand(c, skopeoBinary, "--override-arch=arm64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
@@ -328,9 +269,7 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesBothUseLi
}
func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesFirstUsesListDigest(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-multiple-arches-first")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
m := combinedOutputOfCommand(c, skopeoBinary, "inspect", "--raw", knownListImage)
manifestDigest, err := manifest.Digest([]byte(m))
@@ -342,8 +281,8 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesFirstUses
c.Assert(err, check.IsNil)
arm64Instance, err := list.ChooseInstance(&types.SystemContext{ArchitectureChoice: "arm64"})
c.Assert(err, check.IsNil)
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImage+"@"+arm64Instance.String(), "containers-storage:"+storage+"test@"+arm64Instance.String())
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+arm64Instance.String(), "containers-storage:"+storage+"test@"+arm64Instance.String())
i1 := combinedOutputOfCommand(c, skopeoBinary, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
var image1 imgspecv1.Image
err = json.Unmarshal([]byte(i1), &image1)
@@ -364,9 +303,7 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesFirstUses
}
func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesSecondUsesListDigest(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-multiple-arches-second")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
m := combinedOutputOfCommand(c, skopeoBinary, "inspect", "--raw", knownListImage)
manifestDigest, err := manifest.Digest([]byte(m))
@@ -378,8 +315,8 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesSecondUse
c.Assert(err, check.IsNil)
arm64Instance, err := list.ChooseInstance(&types.SystemContext{ArchitectureChoice: "arm64"})
c.Assert(err, check.IsNil)
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImage+"@"+amd64Instance.String(), "containers-storage:"+storage+"test@"+amd64Instance.String())
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+amd64Instance.String(), "containers-storage:"+storage+"test@"+amd64Instance.String())
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
i1 := combinedOutputOfCommand(c, skopeoBinary, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+amd64Instance.String())
var image1 imgspecv1.Image
err = json.Unmarshal([]byte(i1), &image1)
@@ -400,9 +337,7 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesSecondUse
}
func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesThirdUsesListDigest(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-multiple-arches-third")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
m := combinedOutputOfCommand(c, skopeoBinary, "inspect", "--raw", knownListImage)
manifestDigest, err := manifest.Digest([]byte(m))
@@ -414,9 +349,9 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesThirdUses
c.Assert(err, check.IsNil)
arm64Instance, err := list.ChooseInstance(&types.SystemContext{ArchitectureChoice: "arm64"})
c.Assert(err, check.IsNil)
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImage+"@"+amd64Instance.String(), "containers-storage:"+storage+"test@"+amd64Instance.String())
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+amd64Instance.String(), "containers-storage:"+storage+"test@"+amd64Instance.String())
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoFails(c, `.*reading manifest for image instance.*does not exist.*`, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
i1 := combinedOutputOfCommand(c, skopeoBinary, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+amd64Instance.String())
var image1 imgspecv1.Image
@@ -436,9 +371,7 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesThirdUses
}
func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesTagAndDigest(c *check.C) {
storage, err := ioutil.TempDir("", "copy-manifest-list-storage-digest-multiple-arches-tag-digest")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
m := combinedOutputOfCommand(c, skopeoBinary, "inspect", "--raw", knownListImage)
manifestDigest, err := manifest.Digest([]byte(m))
@@ -451,7 +384,7 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesTagAndDig
arm64Instance, err := list.ChooseInstance(&types.SystemContext{ArchitectureChoice: "arm64"})
c.Assert(err, check.IsNil)
assertSkopeoSucceeds(c, "", "--override-arch=amd64", "copy", knownListImage, "containers-storage:"+storage+"test:latest")
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImage+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoSucceeds(c, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
assertSkopeoFails(c, `.*reading manifest for image instance.*does not exist.*`, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
i1 := combinedOutputOfCommand(c, skopeoBinary, "--override-arch=arm64", "inspect", "--config", "containers-storage:"+storage+"test:latest")
var image1 imgspecv1.Image
@@ -481,28 +414,20 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesTagAndDig
}
func (s *CopySuite) TestCopyFailsWhenImageOSDoesNotMatchRuntimeOS(c *check.C) {
storage, err := ioutil.TempDir("", "copy-fails-image-does-not-match-runtime")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
assertSkopeoFails(c, `.*no image found in manifest list for architecture .*, variant .*, OS .*`, "copy", knownWindowsOnlyImage, "containers-storage:"+storage+"test")
}
func (s *CopySuite) TestCopySucceedsWhenImageDoesNotMatchRuntimeButWeOverride(c *check.C) {
storage, err := ioutil.TempDir("", "copy-succeeds-image-does-not-match-runtime-but-override")
c.Assert(err, check.IsNil)
defer os.RemoveAll(storage)
storage := c.MkDir()
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
assertSkopeoSucceeds(c, "", "--override-os=windows", "--override-arch=amd64", "copy", knownWindowsOnlyImage, "containers-storage:"+storage+"test")
}
func (s *CopySuite) TestCopySimpleAtomicRegistry(c *check.C) {
dir1, err := ioutil.TempDir("", "copy-1")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-2")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
dir1 := c.MkDir()
dir2 := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
// "pull": docker: → dir:
@@ -518,16 +443,12 @@ func (s *CopySuite) TestCopySimpleAtomicRegistry(c *check.C) {
func (s *CopySuite) TestCopySimple(c *check.C) {
const ourRegistry = "docker://" + v2DockerRegistryURL + "/"
dir1, err := ioutil.TempDir("", "copy-1")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "copy-2")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
dir1 := c.MkDir()
dir2 := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
// "pull": docker: → dir:
assertSkopeoSucceeds(c, "", "copy", "docker://k8s.gcr.io/pause", "dir:"+dir1)
assertSkopeoSucceeds(c, "", "copy", "docker://registry.k8s.io/pause", "dir:"+dir1)
// "push": dir: → docker(v2s2):
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--debug", "copy", "dir:"+dir1, ourRegistry+"pause:unsigned")
// The result of pushing and pulling is an unmodified image.
@@ -541,44 +462,27 @@ func (s *CopySuite) TestCopySimple(c *check.C) {
ociDest := "pause-latest-image"
ociImgName := "pause"
defer os.RemoveAll(ociDest)
assertSkopeoSucceeds(c, "", "copy", "docker://k8s.gcr.io/pause:latest", "oci:"+ociDest+":"+ociImgName)
_, err = os.Stat(ociDest)
assertSkopeoSucceeds(c, "", "copy", "docker://registry.k8s.io/pause:latest", "oci:"+ociDest+":"+ociImgName)
_, err := os.Stat(ociDest)
c.Assert(err, check.IsNil)
// docker v2s2 -> OCI image layout without image name
ociDest = "pause-latest-noimage"
defer os.RemoveAll(ociDest)
assertSkopeoSucceeds(c, "", "copy", "docker://k8s.gcr.io/pause:latest", "oci:"+ociDest)
assertSkopeoSucceeds(c, "", "copy", "docker://registry.k8s.io/pause:latest", "oci:"+ociDest)
_, err = os.Stat(ociDest)
c.Assert(err, check.IsNil)
}
func (s *CopySuite) TestCopyEncryption(c *check.C) {
originalImageDir, err := ioutil.TempDir("", "copy-1")
c.Assert(err, check.IsNil)
defer os.RemoveAll(originalImageDir)
encryptedImgDir, err := ioutil.TempDir("", "copy-2")
c.Assert(err, check.IsNil)
defer os.RemoveAll(encryptedImgDir)
decryptedImgDir, err := ioutil.TempDir("", "copy-3")
c.Assert(err, check.IsNil)
defer os.RemoveAll(decryptedImgDir)
keysDir, err := ioutil.TempDir("", "copy-4")
c.Assert(err, check.IsNil)
defer os.RemoveAll(keysDir)
undecryptedImgDir, err := ioutil.TempDir("", "copy-5")
c.Assert(err, check.IsNil)
defer os.RemoveAll(undecryptedImgDir)
multiLayerImageDir, err := ioutil.TempDir("", "copy-6")
c.Assert(err, check.IsNil)
defer os.RemoveAll(multiLayerImageDir)
partiallyEncryptedImgDir, err := ioutil.TempDir("", "copy-7")
c.Assert(err, check.IsNil)
defer os.RemoveAll(partiallyEncryptedImgDir)
partiallyDecryptedImgDir, err := ioutil.TempDir("", "copy-8")
c.Assert(err, check.IsNil)
defer os.RemoveAll(partiallyDecryptedImgDir)
originalImageDir := c.MkDir()
encryptedImgDir := c.MkDir()
decryptedImgDir := c.MkDir()
keysDir := c.MkDir()
undecryptedImgDir := c.MkDir()
multiLayerImageDir := c.MkDir()
partiallyEncryptedImgDir := c.MkDir()
partiallyDecryptedImgDir := c.MkDir()
// Create RSA key pair
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
@@ -587,9 +491,9 @@ func (s *CopySuite) TestCopyEncryption(c *check.C) {
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey)
c.Assert(err, check.IsNil)
err = ioutil.WriteFile(keysDir+"/private.key", privateKeyBytes, 0644)
err = os.WriteFile(keysDir+"/private.key", privateKeyBytes, 0644)
c.Assert(err, check.IsNil)
err = ioutil.WriteFile(keysDir+"/public.key", publicKeyBytes, 0644)
err = os.WriteFile(keysDir+"/public.key", publicKeyBytes, 0644)
c.Assert(err, check.IsNil)
// We can either perform encryption or decryption on the image.
@@ -613,7 +517,7 @@ func (s *CopySuite) TestCopyEncryption(c *check.C) {
invalidPrivateKey, err := rsa.GenerateKey(rand.Reader, 4096)
c.Assert(err, check.IsNil)
invalidPrivateKeyBytes := x509.MarshalPKCS1PrivateKey(invalidPrivateKey)
err = ioutil.WriteFile(keysDir+"/invalid_private.key", invalidPrivateKeyBytes, 0644)
err = os.WriteFile(keysDir+"/invalid_private.key", invalidPrivateKeyBytes, 0644)
c.Assert(err, check.IsNil)
assertSkopeoFails(c, ".*no suitable key unwrapper found or none of the private keys could be used for decryption.*",
"copy", "--decryption-key", keysDir+"/invalid_private.key",
@@ -653,7 +557,7 @@ func (s *CopySuite) TestCopyEncryption(c *check.C) {
}
func matchLayerBlobBinaryType(c *check.C, ociImageDirPath string, contentType string, matchCount int) {
files, err := ioutil.ReadDir(ociImageDirPath)
files, err := os.ReadDir(ociImageDirPath)
c.Assert(err, check.IsNil)
foundCount := 0
@@ -689,7 +593,7 @@ func assertDirImagesAreEqual(c *check.C, dir1, dir2 string) {
digests := []digest.Digest{}
for _, dir := range []string{dir1, dir2} {
manifestPath := filepath.Join(dir, "manifest.json")
m, err := ioutil.ReadFile(manifestPath)
m, err := os.ReadFile(manifestPath)
c.Assert(err, check.IsNil)
digest, err := manifest.Digest(m)
c.Assert(err, check.IsNil)
@@ -707,7 +611,7 @@ func assertSchema1DirImagesAreEqualExceptNames(c *check.C, dir1, ref1, dir2, ref
manifests := []map[string]interface{}{}
for dir, ref := range map[string]string{dir1: ref1, dir2: ref2} {
manifestPath := filepath.Join(dir, "manifest.json")
m, err := ioutil.ReadFile(manifestPath)
m, err := os.ReadFile(manifestPath)
c.Assert(err, check.IsNil)
data := map[string]interface{}{}
err = json.Unmarshal(m, &data)
@@ -730,12 +634,8 @@ func assertSchema1DirImagesAreEqualExceptNames(c *check.C, dir1, ref1, dir2, ref
// Streaming (skopeo copy)
func (s *CopySuite) TestCopyStreaming(c *check.C) {
dir1, err := ioutil.TempDir("", "streaming-1")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir2, err := ioutil.TempDir("", "streaming-2")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir2)
dir1 := c.MkDir()
dir2 := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
// streaming: docker: → atomic:
@@ -755,12 +655,8 @@ func (s *CopySuite) TestCopyStreaming(c *check.C) {
func (s *CopySuite) TestCopyOCIRoundTrip(c *check.C) {
const ourRegistry = "docker://" + v2DockerRegistryURL + "/"
oci1, err := ioutil.TempDir("", "oci-1")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci1)
oci2, err := ioutil.TempDir("", "oci-2")
c.Assert(err, check.IsNil)
defer os.RemoveAll(oci2)
oci1 := c.MkDir()
oci2 := c.MkDir()
// Docker -> OCI
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--debug", "copy", testFQIN, "oci:"+oci1+":latest")
@@ -783,7 +679,7 @@ func (s *CopySuite) TestCopyOCIRoundTrip(c *check.C) {
// Verify using the upstream OCI image validator, this should catch most
// non-compliance errors. DO NOT REMOVE THIS TEST UNLESS IT'S ABSOLUTELY
// NECESSARY.
err = image.ValidateLayout(oci1, nil, logger)
err := image.ValidateLayout(oci1, nil, logger)
c.Assert(err, check.IsNil)
err = image.ValidateLayout(oci2, nil, logger)
c.Assert(err, check.IsNil)
@@ -805,9 +701,7 @@ func (s *CopySuite) TestCopySignatures(c *check.C) {
c.Skip(fmt.Sprintf("Signing not supported: %v", err))
}
dir, err := ioutil.TempDir("", "signatures-dest")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir)
dir := c.MkDir()
dirDest := "dir:" + dir
policy := fileFromFixture(c, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome})
@@ -861,9 +755,7 @@ func (s *CopySuite) TestCopyDirSignatures(c *check.C) {
c.Skip(fmt.Sprintf("Signing not supported: %v", err))
}
topDir, err := ioutil.TempDir("", "dir-signatures-top")
c.Assert(err, check.IsNil)
defer os.RemoveAll(topDir)
topDir := c.MkDir()
topDirDest := "dir:" + topDir
for _, suffix := range []string{"/dir1", "/dir2", "/restricted/personal", "/restricted/official", "/restricted/badidentity", "/dest"} {
@@ -906,9 +798,7 @@ func (s *CopySuite) TestCopyDirSignatures(c *check.C) {
func (s *CopySuite) TestCopyCompression(c *check.C) {
const uncompresssedLayerFile = "160d823fdc48e62f97ba62df31e55424f8f5eb6b679c865eec6e59adfe304710"
topDir, err := ioutil.TempDir("", "compression-top")
c.Assert(err, check.IsNil)
defer os.RemoveAll(topDir)
topDir := c.MkDir()
for i, t := range []struct{ fixture, remote string }{
{"uncompressed-image-s1", "docker://" + v2DockerRegistryURL + "/compression/compression:s1"},
@@ -943,21 +833,21 @@ func (s *CopySuite) TestCopyCompression(c *check.C) {
func findRegularFiles(c *check.C, root string) []string {
result := []string{}
err := filepath.Walk(root, filepath.WalkFunc(func(path string, info os.FileInfo, err error) error {
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if info.Mode().IsRegular() {
if d.Type().IsRegular() {
result = append(result, path)
}
return nil
}))
})
c.Assert(err, check.IsNil)
return result
}
// --sign-by and policy use for docker: with sigstore
func (s *CopySuite) TestCopyDockerSigstore(c *check.C) {
// --sign-by and policy use for docker: with lookaside
func (s *CopySuite) TestCopyDockerLookaside(c *check.C) {
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
c.Assert(err, check.IsNil)
defer mech.Close()
@@ -967,21 +857,19 @@ func (s *CopySuite) TestCopyDockerSigstore(c *check.C) {
const ourRegistry = "docker://" + v2DockerRegistryURL + "/"
tmpDir, err := ioutil.TempDir("", "signatures-sigstore")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
copyDest := filepath.Join(tmpDir, "dest")
err = os.Mkdir(copyDest, 0755)
c.Assert(err, check.IsNil)
dirDest := "dir:" + copyDest
plainSigstore := filepath.Join(tmpDir, "sigstore")
splitSigstoreStaging := filepath.Join(tmpDir, "sigstore-staging")
plainLookaside := filepath.Join(tmpDir, "lookaside")
splitLookasideStaging := filepath.Join(tmpDir, "lookaside-staging")
splitSigstoreReadServerHandler := http.NotFoundHandler()
splitSigstoreReadServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
splitSigstoreReadServerHandler.ServeHTTP(w, r)
splitLookasideReadServerHandler := http.NotFoundHandler()
splitLookasideReadServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
splitLookasideReadServerHandler.ServeHTTP(w, r)
}))
defer splitSigstoreReadServer.Close()
defer splitLookasideReadServer.Close()
policy := fileFromFixture(c, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome})
defer os.Remove(policy)
@@ -989,20 +877,20 @@ func (s *CopySuite) TestCopyDockerSigstore(c *check.C) {
err = os.Mkdir(registriesDir, 0755)
c.Assert(err, check.IsNil)
registriesFile := fileFromFixture(c, "fixtures/registries.yaml",
map[string]string{"@sigstore@": plainSigstore, "@split-staging@": splitSigstoreStaging, "@split-read@": splitSigstoreReadServer.URL})
map[string]string{"@lookaside@": plainLookaside, "@split-staging@": splitLookasideStaging, "@split-read@": splitLookasideReadServer.URL})
err = os.Symlink(registriesFile, filepath.Join(registriesDir, "registries.yaml"))
c.Assert(err, check.IsNil)
// Get an image to work with. Also verifies that we can use Docker repositories with no sigstore configured.
// Get an image to work with. Also verifies that we can use Docker repositories with no lookaside configured.
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", testFQIN, ourRegistry+"original/busybox")
// Pulling an unsigned image fails.
assertSkopeoFails(c, ".*Source image rejected: A signature was required, but no signature exists.*",
"--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"original/busybox", dirDest)
// Signing with sigstore defined succeeds,
// Signing with lookaside defined succeeds,
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", "--sign-by", "personal@example.com", ourRegistry+"original/busybox", ourRegistry+"signed/busybox")
// a signature file has been created,
foundFiles := findRegularFiles(c, plainSigstore)
foundFiles := findRegularFiles(c, plainLookaside)
c.Assert(foundFiles, check.HasLen, 1)
// and pulling a signed image succeeds.
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"signed/busybox", dirDest)
@@ -1010,19 +898,19 @@ func (s *CopySuite) TestCopyDockerSigstore(c *check.C) {
// Deleting the image succeeds,
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir, "delete", ourRegistry+"signed/busybox")
// and the signature file has been deleted (but we leave the directories around).
foundFiles = findRegularFiles(c, plainSigstore)
foundFiles = findRegularFiles(c, plainLookaside)
c.Assert(foundFiles, check.HasLen, 0)
// Signing with a read/write sigstore split succeeds,
// Signing with a read/write lookaside split succeeds,
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", "--sign-by", "personal@example.com", ourRegistry+"original/busybox", ourRegistry+"public/busybox")
// and a signature file has been created.
foundFiles = findRegularFiles(c, splitSigstoreStaging)
foundFiles = findRegularFiles(c, splitLookasideStaging)
c.Assert(foundFiles, check.HasLen, 1)
// Pulling the image fails because the read sigstore URL has not been populated:
// Pulling the image fails because the read lookaside URL has not been populated:
assertSkopeoFails(c, ".*Source image rejected: A signature was required, but no signature exists.*",
"--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"public/busybox", dirDest)
// Pulling the image succeeds after the read sigstore URL is available:
splitSigstoreReadServerHandler = http.FileServer(http.Dir(splitSigstoreStaging))
// Pulling the image succeeds after the read lookaside URL is available:
splitLookasideReadServerHandler = http.FileServer(http.Dir(splitLookasideStaging))
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"public/busybox", dirDest)
}
@@ -1035,9 +923,7 @@ func (s *CopySuite) TestCopyAtomicExtension(c *check.C) {
c.Skip(fmt.Sprintf("Signing not supported: %v", err))
}
topDir, err := ioutil.TempDir("", "atomic-extension")
c.Assert(err, check.IsNil)
defer os.RemoveAll(topDir)
topDir := c.MkDir()
for _, subdir := range []string{"dirAA", "dirAD", "dirDA", "dirDD", "registries.d"} {
err := os.MkdirAll(filepath.Join(topDir, subdir), 0755)
c.Assert(err, check.IsNil)
@@ -1084,22 +970,6 @@ func (s *CopySuite) TestCopyAtomicExtension(c *check.C) {
assertDirImagesAreEqual(c, filepath.Join(topDir, "dirDA"), filepath.Join(topDir, "dirDD"))
}
// copyWithSignedIdentity creates a copy of an unsigned image, adding a signature for an unrelated identity
// This should be easier than using standalone-sign.
func copyWithSignedIdentity(c *check.C, src, dest, signedIdentity, signBy, registriesDir string) {
topDir, err := ioutil.TempDir("", "copyWithSignedIdentity")
c.Assert(err, check.IsNil)
defer os.RemoveAll(topDir)
signingDir := filepath.Join(topDir, "signing-temp")
assertSkopeoSucceeds(c, "", "copy", "--src-tls-verify=false", src, "dir:"+signingDir)
c.Logf("%s", combinedOutputOfCommand(c, "ls", "-laR", signingDir))
assertSkopeoSucceeds(c, "^$", "standalone-sign", "-o", filepath.Join(signingDir, "signature-1"),
filepath.Join(signingDir, "manifest.json"), signedIdentity, signBy)
c.Logf("%s", combinedOutputOfCommand(c, "ls", "-laR", signingDir))
assertSkopeoSucceeds(c, "", "--registries.d", registriesDir, "copy", "--dest-tls-verify=false", "dir:"+signingDir, dest)
}
// Both mirroring support in registries.conf, and mirrored remapIdentity support in policy.json
func (s *CopySuite) TestCopyVerifyingMirroredSignatures(c *check.C) {
const regPrefix = "docker://localhost:5006/myns/mirroring-"
@@ -1111,16 +981,14 @@ func (s *CopySuite) TestCopyVerifyingMirroredSignatures(c *check.C) {
c.Skip(fmt.Sprintf("Signing not supported: %v", err))
}
topDir, err := ioutil.TempDir("", "mirrored-signatures")
c.Assert(err, check.IsNil)
defer os.RemoveAll(topDir)
registriesDir := filepath.Join(topDir, "registries.d") // An empty directory to disable sigstore use
topDir := c.MkDir()
registriesDir := filepath.Join(topDir, "registries.d") // An empty directory to disable lookaside use
dirDest := "dir:" + filepath.Join(topDir, "unused-dest")
policy := fileFromFixture(c, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome})
defer os.Remove(policy)
// We use X-R-S-S for this testing to avoid having to deal with the sigstores.
// We use X-R-S-S for this testing to avoid having to deal with the lookasides.
// A downside is that OpenShift records signatures per image, so the error messages below
// list all signatures for other tags used for the same image as well.
// So, make sure to never create a signature that could be considered valid in a different part of the test (i.e. don't reuse tags).
@@ -1145,10 +1013,12 @@ func (s *CopySuite) TestCopyVerifyingMirroredSignatures(c *check.C) {
assertSkopeoFails(c, ".*Source image rejected: None of the signatures were accepted, reasons: Signature for identity localhost:5006/myns/mirroring-primary:direct is not accepted; Signature for identity localhost:5006/myns/mirroring-mirror:mirror-signed is not accepted.*",
"--policy", policy, "--registries.d", registriesDir, "--registries-conf", "fixtures/registries.conf", "copy", "--src-tls-verify=false", regPrefix+"primary:mirror-signed", dirDest)
// Fail if we specify an unqualified identity
assertSkopeoFails(c, ".*Could not parse --sign-identity: repository name must be canonical.*",
"--registries.d", registriesDir, "copy", "--src-tls-verify=false", "--dest-tls-verify=false", "--sign-by=personal@example.com", "--sign-identity=this-is-not-fully-specified", regPrefix+"primary:unsigned", regPrefix+"mirror:primary-signed")
// Create a signature for mirroring-primary:primary-signed without pushing there.
copyWithSignedIdentity(c, regPrefix+"primary:unsigned", regPrefix+"mirror:primary-signed",
"localhost:5006/myns/mirroring-primary:primary-signed", "personal@example.com",
registriesDir)
assertSkopeoSucceeds(c, "", "--registries.d", registriesDir, "copy", "--src-tls-verify=false", "--dest-tls-verify=false", "--sign-by=personal@example.com", "--sign-identity=localhost:5006/myns/mirroring-primary:primary-signed", regPrefix+"primary:unsigned", regPrefix+"mirror:primary-signed")
// Verify that a correctly signed image for the primary is accessible using the primary's reference
assertSkopeoSucceeds(c, "", "--policy", policy, "--registries.d", registriesDir, "--registries-conf", "fixtures/registries.conf", "copy", "--src-tls-verify=false", regPrefix+"primary:primary-signed", dirDest)
// … but verify that while it is accessible using the mirror location
@@ -1163,20 +1033,17 @@ func (s *CopySuite) TestCopyVerifyingMirroredSignatures(c *check.C) {
// … it is NOT accessible when requiring a signature …
assertSkopeoFails(c, ".*Source image rejected: None of the signatures were accepted, reasons: Signature for identity localhost:5006/myns/mirroring-primary:direct is not accepted; Signature for identity localhost:5006/myns/mirroring-mirror:mirror-signed is not accepted; Signature for identity localhost:5006/myns/mirroring-primary:primary-signed is not accepted.*", "--policy", policy, "--registries.d", registriesDir, "--registries-conf", "fixtures/registries.conf", "copy", "--src-tls-verify=false", regPrefix+"remap:remapped", dirDest)
// … until signed.
copyWithSignedIdentity(c, regPrefix+"remap:remapped", regPrefix+"remap:remapped",
"localhost:5006/myns/mirroring-primary:remapped", "personal@example.com",
registriesDir)
assertSkopeoSucceeds(c, "", "--registries.d", registriesDir, "copy", "--src-tls-verify=false", "--dest-tls-verify=false", "--sign-by=personal@example.com", "--sign-identity=localhost:5006/myns/mirroring-primary:remapped", regPrefix+"remap:remapped", regPrefix+"remap:remapped")
assertSkopeoSucceeds(c, "", "--policy", policy, "--registries.d", registriesDir, "--registries-conf", "fixtures/registries.conf", "copy", "--src-tls-verify=false", regPrefix+"remap:remapped", dirDest)
// To be extra clear about the semantics, verify that the signedPrefix (primary) location never exists
// and only the remapped prefix (mirror) is accessed.
assertSkopeoFails(c, ".*initializing source docker://localhost:5006/myns/mirroring-primary:remapped:.*manifest unknown: manifest unknown.*", "--policy", policy, "--registries.d", registriesDir, "--registries-conf", "fixtures/registries.conf", "copy", "--src-tls-verify=false", regPrefix+"primary:remapped", dirDest)
assertSkopeoFails(c, ".*initializing source docker://localhost:5006/myns/mirroring-primary:remapped:.*manifest unknown.*",
"--policy", policy, "--registries.d", registriesDir, "--registries-conf", "fixtures/registries.conf", "copy", "--src-tls-verify=false", regPrefix+"primary:remapped", dirDest)
}
func (s *SkopeoSuite) TestCopySrcWithAuth(c *check.C) {
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--dest-creds=testuser:testpassword", testFQIN, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
dir1, err := ioutil.TempDir("", "copy-1")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
dir1 := c.MkDir()
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--src-creds=testuser:testpassword", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "dir:"+dir1)
}
@@ -1190,12 +1057,12 @@ func (s *SkopeoSuite) TestCopySrcAndDestWithAuth(c *check.C) {
}
func (s *CopySuite) TestCopyNoPanicOnHTTPResponseWithoutTLSVerifyFalse(c *check.C) {
topDir := c.MkDir()
const ourRegistry = "docker://" + v2DockerRegistryURL + "/"
// dir:test isn't created beforehand just because we already know this could
// just fail when evaluating the src
assertSkopeoFails(c, ".*server gave HTTP response to HTTPS client.*",
"copy", ourRegistry+"foobar", "dir:test")
"copy", ourRegistry+"foobar", "dir:"+topDir)
}
func (s *CopySuite) TestCopySchemaConversion(c *check.C) {
@@ -1206,9 +1073,7 @@ func (s *CopySuite) TestCopySchemaConversion(c *check.C) {
}
func (s *CopySuite) TestCopyManifestConversion(c *check.C) {
topDir, err := ioutil.TempDir("", "manifest-conversion")
c.Assert(err, check.IsNil)
defer os.RemoveAll(topDir)
topDir := c.MkDir()
srcDir := filepath.Join(topDir, "source")
destDir1 := filepath.Join(topDir, "dest1")
destDir2 := filepath.Join(topDir, "dest2")
@@ -1231,10 +1096,15 @@ func (s *CopySuite) TestCopyManifestConversion(c *check.C) {
verifyManifestMIMEType(c, destDir2, manifest.DockerV2Schema2MediaType)
}
func (s *CopySuite) TestCopyPreserveDigests(c *check.C) {
topDir := c.MkDir()
assertSkopeoSucceeds(c, "", "copy", knownListImage, "--multi-arch=all", "--preserve-digests", "dir:"+topDir)
assertSkopeoFails(c, ".*Instructed to preserve digests.*", "copy", knownListImage, "--multi-arch=all", "--preserve-digests", "--format=oci", "dir:"+topDir)
}
func (s *CopySuite) testCopySchemaConversionRegistries(c *check.C, schema1Registry, schema2Registry string) {
topDir, err := ioutil.TempDir("", "schema-conversion")
c.Assert(err, check.IsNil)
defer os.RemoveAll(topDir)
topDir := c.MkDir()
for _, subdir := range []string{"input1", "input2", "dest2"} {
err := os.MkdirAll(filepath.Join(topDir, subdir), 0755)
c.Assert(err, check.IsNil)
@@ -1268,35 +1138,35 @@ func (s *CopySuite) testCopySchemaConversionRegistries(c *check.C, schema1Regist
const regConfFixture = "./fixtures/registries.conf"
func (s *SkopeoSuite) TestSuccessCopySrcWithMirror(c *check.C) {
dir, err := ioutil.TempDir("", "copy-mirror")
c.Assert(err, check.IsNil)
dir := c.MkDir()
assertSkopeoSucceeds(c, "", "--registries-conf="+regConfFixture, "copy",
"docker://mirror.invalid/busybox", "dir:"+dir)
}
func (s *SkopeoSuite) TestFailureCopySrcWithMirrorsUnavailable(c *check.C) {
dir, err := ioutil.TempDir("", "copy-mirror")
c.Assert(err, check.IsNil)
dir := c.MkDir()
assertSkopeoFails(c, ".*no such host.*", "--registries-conf="+regConfFixture, "copy",
"docker://invalid.invalid/busybox", "dir:"+dir)
// .invalid domains are, per RFC 6761, supposed to result in NXDOMAIN.
// With systemd-resolved (used only via NSS?), we instead seem to get “Temporary failure in name resolution”
assertSkopeoFails(c, ".*(no such host|Temporary failure in name resolution).*",
"--registries-conf="+regConfFixture, "copy", "docker://invalid.invalid/busybox", "dir:"+dir)
}
func (s *SkopeoSuite) TestSuccessCopySrcWithMirrorAndPrefix(c *check.C) {
dir, err := ioutil.TempDir("", "copy-mirror")
c.Assert(err, check.IsNil)
dir := c.MkDir()
assertSkopeoSucceeds(c, "", "--registries-conf="+regConfFixture, "copy",
"docker://gcr.invalid/foo/bar/busybox", "dir:"+dir)
}
func (s *SkopeoSuite) TestFailureCopySrcWithMirrorAndPrefixUnavailable(c *check.C) {
dir, err := ioutil.TempDir("", "copy-mirror")
c.Assert(err, check.IsNil)
dir := c.MkDir()
assertSkopeoFails(c, ".*no such host.*", "--registries-conf="+regConfFixture, "copy",
"docker://gcr.invalid/wrong/prefix/busybox", "dir:"+dir)
// .invalid domains are, per RFC 6761, supposed to result in NXDOMAIN.
// With systemd-resolved (used only via NSS?), we instead seem to get “Temporary failure in name resolution”
assertSkopeoFails(c, ".*(no such host|Temporary failure in name resolution).*",
"--registries-conf="+regConfFixture, "copy", "docker://gcr.invalid/wrong/prefix/busybox", "dir:"+dir)
}
func (s *CopySuite) TestCopyFailsWhenReferenceIsInvalid(c *check.C) {

View File

@@ -1,6 +1,6 @@
docker:
localhost:5555:
sigstore: file://@sigstore@
lookaside: file://@lookaside@
localhost:5555/public:
sigstore-staging: file://@split-staging@
sigstore: @split-read@
lookaside-staging: file://@split-staging@
lookaside: @split-read@

View File

@@ -5,15 +5,14 @@ import (
"context"
"encoding/base64"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/docker/docker/pkg/homedir"
"github.com/go-check/check"
"github.com/containers/storage/pkg/homedir"
"gopkg.in/check.v1"
)
var adminKUBECONFIG = map[string]string{
@@ -33,10 +32,7 @@ type openshiftCluster struct {
// in isolated test environment.
func startOpenshiftCluster(c *check.C) *openshiftCluster {
cluster := &openshiftCluster{}
dir, err := ioutil.TempDir("", "openshift-cluster")
c.Assert(err, check.IsNil)
cluster.workingDir = dir
cluster.workingDir = c.MkDir()
cluster.startMaster(c)
cluster.prepareRegistryConfig(c)
@@ -196,7 +192,7 @@ func (cluster *openshiftCluster) startRegistry(c *check.C) {
// The default configuration currently already contains acceptschema2: false
})
// Make sure the configuration contains "acceptschema2: false", because eventually it will be enabled upstream and this function will need to be updated.
configContents, err := ioutil.ReadFile(schema1Config)
configContents, err := os.ReadFile(schema1Config)
c.Assert(err, check.IsNil)
c.Assert(string(configContents), check.Matches, "(?s).*acceptschema2: false.*")
cluster.processes = append(cluster.processes, cluster.startRegistryProcess(c, 5005, schema1Config))
@@ -240,7 +236,7 @@ func (cluster *openshiftCluster) dockerLogin(c *check.C) {
}`, port, authValue))
}
configJSON := `{"auths": {` + strings.Join(auths, ",") + `}}`
err = ioutil.WriteFile(filepath.Join(cluster.dockerDir, "config.json"), []byte(configJSON), 0600)
err = os.WriteFile(filepath.Join(cluster.dockerDir, "config.json"), []byte(configJSON), 0600)
c.Assert(err, check.IsNil)
}
@@ -258,12 +254,12 @@ func (cluster *openshiftCluster) relaxImageSignerPermissions(c *check.C) {
// tearDown stops the cluster services and deletes (only some!) of the state.
func (cluster *openshiftCluster) tearDown(c *check.C) {
for i := len(cluster.processes) - 1; i >= 0; i-- {
cluster.processes[i].Process.Kill()
}
if cluster.workingDir != "" {
os.RemoveAll(cluster.workingDir)
// Its undocumented what Kill() returns if the process has terminated,
// so we couldnt check just for that. This is running in a container anyway…
_ = cluster.processes[i].Process.Kill()
}
if cluster.dockerDir != "" {
os.RemoveAll(cluster.dockerDir)
err := os.RemoveAll(cluster.dockerDir)
c.Assert(err, check.IsNil)
}
}

View File

@@ -7,7 +7,7 @@ import (
"os"
"os/exec"
"github.com/go-check/check"
"gopkg.in/check.v1"
)
/*
@@ -15,11 +15,15 @@ TestRunShell is not really a test; it is a convenient way to use the registry se
in openshift.go and CopySuite to get an interactive environment for experimentation.
To use it, run:
sudo make shell
to start a container, then within the container:
SKOPEO_CONTAINER_TESTS=1 PS1='nested> ' go test -tags openshift_shell -timeout=24h ./integration -v -check.v -check.vv -check.f='CopySuite.TestRunShell'
An example of what can be done within the container:
cd ..; make bin/skopeo PREFIX=/usr install
./skopeo --tls-verify=false copy --sign-by=personal@example.com docker://quay.io/libpod/busybox:latest atomic:localhost:5000/myns/personal:personal
oc get istag personal:personal -o json

12
integration/procutils.go Normal file
View File

@@ -0,0 +1,12 @@
//go:build !linux
// +build !linux
package main
import (
"os/exec"
)
// cmdLifecycleToParentIfPossible tries to exit if the parent process exits (only works on Linux)
func cmdLifecycleToParentIfPossible(c *exec.Cmd) {
}

View File

@@ -0,0 +1,14 @@
package main
import (
"os/exec"
"syscall"
)
// cmdLifecyleToParentIfPossible is a thin wrapper around prctl(PR_SET_PDEATHSIG)
// on Linux.
func cmdLifecycleToParentIfPossible(c *exec.Cmd) {
c.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
}
}

359
integration/proxy_test.go Normal file
View File

@@ -0,0 +1,359 @@
package main
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"os/exec"
"strings"
"syscall"
"time"
"gopkg.in/check.v1"
"github.com/containers/image/v5/manifest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
)
// This image is known to be x86_64 only right now
const knownNotManifestListedImage_x8664 = "docker://quay.io/coreos/11bot"
// knownNotExtantImage would be very surprising if it did exist
const knownNotExtantImage = "docker://quay.io/centos/centos:opensusewindowsubuntu"
const expectedProxySemverMajor = "0.2"
// request is copied from proxy.go
// We intentionally copy to ensure that we catch any unexpected "API" changes
// in the JSON.
type request struct {
// Method is the name of the function
Method string `json:"method"`
// Args is the arguments (parsed inside the function)
Args []interface{} `json:"args"`
}
// reply is copied from proxy.go
type reply struct {
// Success is true if and only if the call succeeded.
Success bool `json:"success"`
// Value is an arbitrary value (or values, as array/map) returned from the call.
Value interface{} `json:"value"`
// PipeID is an index into open pipes, and should be passed to FinishPipe
PipeID uint32 `json:"pipeid"`
// Error should be non-empty if Success == false
Error string `json:"error"`
}
// maxMsgSize is also copied from proxy.go
const maxMsgSize = 32 * 1024
type proxy struct {
c *net.UnixConn
}
type pipefd struct {
// id is the remote identifier "pipeid"
id uint
fd *os.File
}
func (p *proxy) call(method string, args []interface{}) (rval interface{}, fd *pipefd, err error) {
req := request{
Method: method,
Args: args,
}
reqbuf, err := json.Marshal(&req)
if err != nil {
return
}
n, err := p.c.Write(reqbuf)
if err != nil {
return
}
if n != len(reqbuf) {
err = fmt.Errorf("short write during call of %d bytes", n)
return
}
oob := make([]byte, syscall.CmsgSpace(1))
replybuf := make([]byte, maxMsgSize)
n, oobn, _, _, err := p.c.ReadMsgUnix(replybuf, oob)
if err != nil {
err = fmt.Errorf("reading reply: %v", err)
return
}
var reply reply
err = json.Unmarshal(replybuf[0:n], &reply)
if err != nil {
err = fmt.Errorf("Failed to parse reply: %w", err)
return
}
if !reply.Success {
err = fmt.Errorf("remote error: %s", reply.Error)
return
}
if reply.PipeID > 0 {
var scms []syscall.SocketControlMessage
scms, err = syscall.ParseSocketControlMessage(oob[:oobn])
if err != nil {
err = fmt.Errorf("failed to parse control message: %v", err)
return
}
if len(scms) != 1 {
err = fmt.Errorf("Expected 1 received fd, found %d", len(scms))
return
}
var fds []int
fds, err = syscall.ParseUnixRights(&scms[0])
if err != nil {
err = fmt.Errorf("failed to parse unix rights: %v", err)
return
}
fd = &pipefd{
fd: os.NewFile(uintptr(fds[0]), "replyfd"),
id: uint(reply.PipeID),
}
}
rval = reply.Value
return
}
func (p *proxy) callNoFd(method string, args []interface{}) (rval interface{}, err error) {
var fd *pipefd
rval, fd, err = p.call(method, args)
if err != nil {
return
}
if fd != nil {
err = fmt.Errorf("Unexpected fd from method %s", method)
return
}
return rval, nil
}
func (p *proxy) callReadAllBytes(method string, args []interface{}) (rval interface{}, buf []byte, err error) {
var fd *pipefd
rval, fd, err = p.call(method, args)
if err != nil {
return
}
if fd == nil {
err = fmt.Errorf("Expected fd from method %s", method)
return
}
fetchchan := make(chan byteFetch)
go func() {
manifestBytes, err := io.ReadAll(fd.fd)
fetchchan <- byteFetch{
content: manifestBytes,
err: err,
}
}()
_, err = p.callNoFd("FinishPipe", []interface{}{fd.id})
if err != nil {
return
}
select {
case fetchRes := <-fetchchan:
err = fetchRes.err
if err != nil {
return
}
buf = fetchRes.content
case <-time.After(5 * time.Minute):
err = fmt.Errorf("timed out during proxy fetch")
}
return
}
func newProxy() (*proxy, error) {
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET, 0)
if err != nil {
return nil, err
}
myfd := os.NewFile(uintptr(fds[0]), "myfd")
defer myfd.Close()
theirfd := os.NewFile(uintptr(fds[1]), "theirfd")
defer theirfd.Close()
mysock, err := net.FileConn(myfd)
if err != nil {
return nil, err
}
// Note ExtraFiles starts at 3
proc := exec.Command("skopeo", "experimental-image-proxy", "--sockfd", "3")
proc.Stderr = os.Stderr
cmdLifecycleToParentIfPossible(proc)
proc.ExtraFiles = append(proc.ExtraFiles, theirfd)
if err = proc.Start(); err != nil {
return nil, err
}
p := &proxy{
c: mysock.(*net.UnixConn),
}
v, err := p.callNoFd("Initialize", nil)
if err != nil {
return nil, err
}
semver, ok := v.(string)
if !ok {
return nil, fmt.Errorf("proxy Initialize: Unexpected value %T", v)
}
if !strings.HasPrefix(semver, expectedProxySemverMajor) {
return nil, fmt.Errorf("Unexpected semver %s", semver)
}
return p, nil
}
func init() {
check.Suite(&ProxySuite{})
}
type ProxySuite struct {
}
func (s *ProxySuite) SetUpSuite(c *check.C) {
}
func (s *ProxySuite) TearDownSuite(c *check.C) {
}
type byteFetch struct {
content []byte
err error
}
func runTestGetManifestAndConfig(p *proxy, img string) error {
v, err := p.callNoFd("OpenImage", []interface{}{knownNotManifestListedImage_x8664})
if err != nil {
return err
}
imgidv, ok := v.(float64)
if !ok {
return fmt.Errorf("OpenImage return value is %T", v)
}
imgid := uint32(imgidv)
if imgid == 0 {
return fmt.Errorf("got zero from expected image")
}
// Also verify the optional path
v, err = p.callNoFd("OpenImageOptional", []interface{}{knownNotManifestListedImage_x8664})
if err != nil {
return err
}
imgidv, ok = v.(float64)
if !ok {
return fmt.Errorf("OpenImageOptional return value is %T", v)
}
imgid2 := uint32(imgidv)
if imgid2 == 0 {
return fmt.Errorf("got zero from expected image")
}
_, err = p.callNoFd("CloseImage", []interface{}{imgid2})
if err != nil {
return err
}
_, manifestBytes, err := p.callReadAllBytes("GetManifest", []interface{}{imgid})
if err != nil {
return err
}
_, err = manifest.OCI1FromManifest(manifestBytes)
if err != nil {
return err
}
_, configBytes, err := p.callReadAllBytes("GetFullConfig", []interface{}{imgid})
if err != nil {
return err
}
var config imgspecv1.Image
err = json.Unmarshal(configBytes, &config)
if err != nil {
return err
}
// Validate that the image config seems sane
if config.Architecture == "" {
return fmt.Errorf("No architecture found")
}
if len(config.Config.Cmd) == 0 && len(config.Config.Entrypoint) == 0 {
return fmt.Errorf("No CMD or ENTRYPOINT set")
}
// Also test this legacy interface
_, ctrconfigBytes, err := p.callReadAllBytes("GetConfig", []interface{}{imgid})
if err != nil {
return err
}
var ctrconfig imgspecv1.ImageConfig
err = json.Unmarshal(ctrconfigBytes, &ctrconfig)
if err != nil {
return err
}
// Validate that the config seems sane
if len(ctrconfig.Cmd) == 0 && len(ctrconfig.Entrypoint) == 0 {
return fmt.Errorf("No CMD or ENTRYPOINT set")
}
_, err = p.callNoFd("CloseImage", []interface{}{imgid})
if err != nil {
return err
}
return nil
}
func runTestOpenImageOptionalNotFound(p *proxy, img string) error {
v, err := p.callNoFd("OpenImageOptional", []interface{}{img})
if err != nil {
return err
}
imgidv, ok := v.(float64)
if !ok {
return fmt.Errorf("OpenImageOptional return value is %T", v)
}
imgid := uint32(imgidv)
if imgid != 0 {
return fmt.Errorf("Unexpected optional image id %v", imgid)
}
return nil
}
func (s *ProxySuite) TestProxy(c *check.C) {
p, err := newProxy()
c.Assert(err, check.IsNil)
err = runTestGetManifestAndConfig(p, knownNotManifestListedImage_x8664)
if err != nil {
err = fmt.Errorf("Testing image %s: %v", knownNotManifestListedImage_x8664, err)
}
c.Assert(err, check.IsNil)
err = runTestGetManifestAndConfig(p, knownListImage)
if err != nil {
err = fmt.Errorf("Testing image %s: %v", knownListImage, err)
}
c.Assert(err, check.IsNil)
err = runTestOpenImageOptionalNotFound(p, knownNotExtantImage)
if err != nil {
err = fmt.Errorf("Testing optional image %s: %v", knownNotExtantImage, err)
}
c.Assert(err, check.IsNil)
}

View File

@@ -2,14 +2,13 @@ package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path/filepath"
"time"
"github.com/go-check/check"
"gopkg.in/check.v1"
)
const (
@@ -20,7 +19,6 @@ const (
type testRegistryV2 struct {
cmd *exec.Cmd
url string
dir string
username string
password string
email string
@@ -45,10 +43,7 @@ func setupRegistryV2At(c *check.C, url string, auth, schema1 bool) *testRegistry
}
func newTestRegistryV2At(c *check.C, url string, auth, schema1 bool) (*testRegistryV2, error) {
tmp, err := ioutil.TempDir("", "registry-test-")
if err != nil {
return nil, err
}
tmp := c.MkDir()
template := `version: 0.1
loglevel: debug
storage:
@@ -58,6 +53,9 @@ storage:
enabled: true
http:
addr: %s
compatibility:
schema1:
enabled: true
%s`
var (
htpasswd string
@@ -71,7 +69,7 @@ http:
username = "testuser"
password = "testpassword"
email = "test@test.org"
if err := ioutil.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil {
if err := os.WriteFile(htpasswdPath, []byte(userpasswd), os.FileMode(0644)); err != nil {
return nil, err
}
htpasswd = fmt.Sprintf(`auth:
@@ -86,19 +84,18 @@ http:
return nil, err
}
if _, err := fmt.Fprintf(config, template, tmp, url, htpasswd); err != nil {
os.RemoveAll(tmp)
return nil, err
}
binary := binaryV2
var cmd *exec.Cmd
if schema1 {
binary = binaryV2Schema1
cmd = exec.Command(binaryV2Schema1, confPath)
} else {
cmd = exec.Command(binaryV2, "serve", confPath)
}
cmd := exec.Command(binary, confPath)
consumeAndLogOutputs(c, fmt.Sprintf("registry-%s", url), cmd)
if err := cmd.Start(); err != nil {
os.RemoveAll(tmp)
if os.IsNotExist(err) {
c.Skip(err.Error())
}
@@ -107,7 +104,6 @@ http:
return &testRegistryV2{
cmd: cmd,
url: url,
dir: tmp,
username: username,
password: password,
email: email,
@@ -120,13 +116,15 @@ func (t *testRegistryV2) Ping() error {
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusUnauthorized {
return fmt.Errorf("registry ping replied with an unexpected status code %d", resp.StatusCode)
}
return nil
}
func (t *testRegistryV2) Close() {
t.cmd.Process.Kill()
os.RemoveAll(t.dir)
func (t *testRegistryV2) tearDown(c *check.C) {
// Its undocumented what Kill() returns if the process has terminated,
// so we couldnt check just for that. This is running in a container anyway…
_ = t.cmd.Process.Kill()
}

View File

@@ -3,13 +3,12 @@ package main
import (
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/containers/image/v5/signature"
"github.com/go-check/check"
"gopkg.in/check.v1"
)
const (
@@ -21,7 +20,6 @@ func init() {
}
type SigningSuite struct {
gpgHome string
fingerprint string
}
@@ -40,25 +38,18 @@ func (s *SigningSuite) SetUpSuite(c *check.C) {
_, err := exec.LookPath(skopeoBinary)
c.Assert(err, check.IsNil)
s.gpgHome, err = ioutil.TempDir("", "skopeo-gpg")
c.Assert(err, check.IsNil)
os.Setenv("GNUPGHOME", s.gpgHome)
gpgHome := c.MkDir()
os.Setenv("GNUPGHOME", gpgHome)
runCommandWithInput(c, "Key-Type: RSA\nName-Real: Testing user\n%no-protection\n%commit\n", gpgBinary, "--homedir", s.gpgHome, "--batch", "--gen-key")
runCommandWithInput(c, "Key-Type: RSA\nName-Real: Testing user\n%no-protection\n%commit\n", gpgBinary, "--homedir", gpgHome, "--batch", "--gen-key")
lines, err := exec.Command(gpgBinary, "--homedir", s.gpgHome, "--with-colons", "--no-permission-warning", "--fingerprint").Output()
lines, err := exec.Command(gpgBinary, "--homedir", gpgHome, "--with-colons", "--no-permission-warning", "--fingerprint").Output()
c.Assert(err, check.IsNil)
s.fingerprint, err = findFingerprint(lines)
c.Assert(err, check.IsNil)
}
func (s *SigningSuite) TearDownSuite(c *check.C) {
if s.gpgHome != "" {
err := os.RemoveAll(s.gpgHome)
c.Assert(err, check.IsNil)
}
s.gpgHome = ""
os.Unsetenv("GNUPGHOME")
}
@@ -73,7 +64,7 @@ func (s *SigningSuite) TestSignVerifySmoke(c *check.C) {
manifestPath := "fixtures/image.manifest.json"
dockerReference := "testing/smoketest"
sigOutput, err := ioutil.TempFile("", "sig")
sigOutput, err := os.CreateTemp("", "sig")
c.Assert(err, check.IsNil)
defer os.Remove(sigOutput.Name())
assertSkopeoSucceeds(c, "^$", "standalone-sign", "-o", sigOutput.Name(),

View File

@@ -3,7 +3,7 @@ package main
import (
"context"
"fmt"
"io/ioutil"
"io/fs"
"os"
"path"
"path/filepath"
@@ -14,23 +14,23 @@ import (
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/manifest"
"github.com/containers/image/v5/types"
"github.com/go-check/check"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"gopkg.in/check.v1"
)
const (
// A repository with a path with multiple components in it which
// contains multiple tags, preferably with some tags pointing to
// manifest lists, and with some tags that don't.
pullableRepo = "quay.io/libpod/testimage"
pullableRepo = "registry.k8s.io/coredns/coredns"
// A tagged image in the repository that we can inspect and copy.
pullableTaggedImage = "k8s.gcr.io/coredns/coredns:v1.6.6"
pullableTaggedImage = "registry.k8s.io/coredns/coredns:v1.6.6"
// A tagged manifest list in the repository that we can inspect and copy.
pullableTaggedManifestList = "k8s.gcr.io/coredns/coredns:v1.8.0"
pullableTaggedManifestList = "registry.k8s.io/coredns/coredns:v1.8.0"
// A repository containing multiple tags, some of which are for
// manifest lists, and which includes a "latest" tag. We specify the
// name here without a tag.
pullableRepoWithLatestTag = "k8s.gcr.io/pause"
pullableRepoWithLatestTag = "registry.k8s.io/pause"
)
func init() {
@@ -40,7 +40,6 @@ func init() {
type SyncSuite struct {
cluster *openshiftCluster
registry *testRegistryV2
gpgHome string
}
func (s *SyncSuite) SetUpSuite(c *check.C) {
@@ -74,10 +73,8 @@ func (s *SyncSuite) SetUpSuite(c *check.C) {
// FIXME: Set up TLS for the docker registry port instead of using "--tls-verify=false" all over the place.
s.registry = setupRegistryV2At(c, v2DockerRegistryURL, registryAuth, registrySchema1)
gpgHome, err := ioutil.TempDir("", "skopeo-gpg")
c.Assert(err, check.IsNil)
s.gpgHome = gpgHome
os.Setenv("GNUPGHOME", s.gpgHome)
gpgHome := c.MkDir()
os.Setenv("GNUPGHOME", gpgHome)
for _, key := range []string{"personal", "official"} {
batchInput := fmt.Sprintf("Key-Type: RSA\nName-Real: Test key - %s\nName-email: %s@example.com\n%%no-protection\n%%commit\n",
@@ -85,7 +82,7 @@ func (s *SyncSuite) SetUpSuite(c *check.C) {
runCommandWithInput(c, batchInput, gpgBinary, "--batch", "--gen-key")
out := combinedOutputOfCommand(c, gpgBinary, "--armor", "--export", fmt.Sprintf("%s@example.com", key))
err := ioutil.WriteFile(filepath.Join(s.gpgHome, fmt.Sprintf("%s-pubkey.gpg", key)),
err := os.WriteFile(filepath.Join(gpgHome, fmt.Sprintf("%s-pubkey.gpg", key)),
[]byte(out), 0600)
c.Assert(err, check.IsNil)
}
@@ -96,21 +93,32 @@ func (s *SyncSuite) TearDownSuite(c *check.C) {
return
}
if s.gpgHome != "" {
os.RemoveAll(s.gpgHome)
}
if s.registry != nil {
s.registry.Close()
s.registry.tearDown(c)
}
if s.cluster != nil {
s.cluster.tearDown(c)
}
}
func (s *SyncSuite) TestDocker2DirTagged(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
func assertNumberOfManifestsInSubdirs(c *check.C, dir string, expectedCount int) {
nManifests := 0
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() && d.Name() == "manifest.json" {
nManifests++
return filepath.SkipDir
}
return nil
})
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
c.Assert(nManifests, check.Equals, expectedCount)
}
func (s *SyncSuite) TestDocker2DirTagged(c *check.C) {
tmpDir := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
image := pullableTaggedImage
@@ -136,9 +144,7 @@ func (s *SyncSuite) TestDocker2DirTagged(c *check.C) {
}
func (s *SyncSuite) TestDocker2DirTaggedAll(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
image := pullableTaggedManifestList
@@ -163,6 +169,20 @@ func (s *SyncSuite) TestDocker2DirTaggedAll(c *check.C) {
c.Assert(out, check.Equals, "")
}
func (s *SyncSuite) TestPreserveDigests(c *check.C) {
tmpDir := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
image := pullableTaggedManifestList
// copy docker => dir
assertSkopeoSucceeds(c, "", "copy", "--all", "--preserve-digests", "docker://"+image, "dir:"+tmpDir)
_, err := os.Stat(path.Join(tmpDir, "manifest.json"))
c.Assert(err, check.IsNil)
assertSkopeoFails(c, ".*Instructed to preserve digests.*", "copy", "--all", "--preserve-digests", "--format=oci", "docker://"+image, "dir:"+tmpDir)
}
func (s *SyncSuite) TestScoped(c *check.C) {
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
image := pullableTaggedImage
@@ -170,8 +190,7 @@ func (s *SyncSuite) TestScoped(c *check.C) {
c.Assert(err, check.IsNil)
imagePath := imageRef.DockerReference().String()
dir1, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
dir1 := c.MkDir()
assertSkopeoSucceeds(c, "", "sync", "--src", "docker", "--dest", "dir", image, dir1)
_, err = os.Stat(path.Join(dir1, path.Base(imagePath), "manifest.json"))
c.Assert(err, check.IsNil)
@@ -179,8 +198,6 @@ func (s *SyncSuite) TestScoped(c *check.C) {
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "docker", "--dest", "dir", image, dir1)
_, err = os.Stat(path.Join(dir1, imagePath, "manifest.json"))
c.Assert(err, check.IsNil)
os.RemoveAll(dir1)
}
func (s *SyncSuite) TestDirIsNotOverwritten(c *check.C) {
@@ -194,8 +211,7 @@ func (s *SyncSuite) TestDirIsNotOverwritten(c *check.C) {
assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "docker://"+image, "docker://"+path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())))
//sync upstream image to dir, not scoped
dir1, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
dir1 := c.MkDir()
assertSkopeoSucceeds(c, "", "sync", "--src", "docker", "--dest", "dir", image, dir1)
_, err = os.Stat(path.Join(dir1, path.Base(imagePath), "manifest.json"))
c.Assert(err, check.IsNil)
@@ -210,14 +226,10 @@ func (s *SyncSuite) TestDirIsNotOverwritten(c *check.C) {
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src-tls-verify=false", "--src", "docker", "--dest", "dir", path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())), dir1)
_, err = os.Stat(path.Join(dir1, imagePath, "manifest.json"))
c.Assert(err, check.IsNil)
os.RemoveAll(dir1)
}
func (s *SyncSuite) TestDocker2DirUntagged(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
image := pullableRepo
@@ -239,9 +251,7 @@ func (s *SyncSuite) TestDocker2DirUntagged(c *check.C) {
}
func (s *SyncSuite) TestYamlUntagged(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
dir1 := path.Join(tmpDir, "dir1")
image := pullableRepo
@@ -262,7 +272,8 @@ func (s *SyncSuite) TestYamlUntagged(c *check.C) {
// sync to the local registry
yamlFile := path.Join(tmpDir, "registries.yaml")
ioutil.WriteFile(yamlFile, []byte(yamlConfig), 0644)
err = os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
c.Assert(err, check.IsNil)
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "docker", "--dest-tls-verify=false", yamlFile, v2DockerRegistryURL)
// sync back from local registry to a folder
os.Remove(yamlFile)
@@ -273,7 +284,8 @@ func (s *SyncSuite) TestYamlUntagged(c *check.C) {
%s: []
`, v2DockerRegistryURL, imagePath)
ioutil.WriteFile(yamlFile, []byte(yamlConfig), 0644)
err = os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
c.Assert(err, check.IsNil)
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
sysCtx = types.SystemContext{
@@ -285,31 +297,15 @@ func (s *SyncSuite) TestYamlUntagged(c *check.C) {
c.Assert(err, check.IsNil)
c.Check(len(localTags), check.Not(check.Equals), 0)
c.Assert(len(localTags), check.Equals, len(tags))
nManifests := 0
//count the number of manifest.json in dir1
err = filepath.Walk(dir1, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && info.Name() == "manifest.json" {
nManifests++
return filepath.SkipDir
}
return nil
})
c.Assert(err, check.IsNil)
c.Assert(nManifests, check.Equals, len(tags))
assertNumberOfManifestsInSubdirs(c, dir1, len(tags))
}
func (s *SyncSuite) TestYamlRegex2Dir(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
dir1 := path.Join(tmpDir, "dir1")
yamlConfig := `
k8s.gcr.io:
registry.k8s.io:
images-by-tag-regex:
pause: ^[12]\.0$ # regex string test
`
@@ -318,63 +314,35 @@ k8s.gcr.io:
c.Assert(nTags, check.Not(check.Equals), 0)
yamlFile := path.Join(tmpDir, "registries.yaml")
ioutil.WriteFile(yamlFile, []byte(yamlConfig), 0644)
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
nManifests := 0
err = filepath.Walk(dir1, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && info.Name() == "manifest.json" {
nManifests++
return filepath.SkipDir
}
return nil
})
err := os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
c.Assert(err, check.IsNil)
c.Assert(nManifests, check.Equals, nTags)
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
assertNumberOfManifestsInSubdirs(c, dir1, nTags)
}
func (s *SyncSuite) TestYamlDigest2Dir(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
dir1 := path.Join(tmpDir, "dir1")
yamlConfig := `
k8s.gcr.io:
registry.k8s.io:
images:
pause:
- sha256:59eec8837a4d942cc19a52b8c09ea75121acc38114a2c68b98983ce9356b8610
`
yamlFile := path.Join(tmpDir, "registries.yaml")
ioutil.WriteFile(yamlFile, []byte(yamlConfig), 0644)
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
nManifests := 0
err = filepath.Walk(dir1, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && info.Name() == "manifest.json" {
nManifests++
return filepath.SkipDir
}
return nil
})
err := os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
c.Assert(err, check.IsNil)
c.Assert(nManifests, check.Equals, 1)
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
assertNumberOfManifestsInSubdirs(c, dir1, 1)
}
func (s *SyncSuite) TestYaml2Dir(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
dir1 := path.Join(tmpDir, "dir1")
yamlConfig := `
k8s.gcr.io:
registry.k8s.io:
images:
coredns/coredns:
- v1.8.0
@@ -401,29 +369,15 @@ quay.io:
c.Assert(nTags, check.Not(check.Equals), 0)
yamlFile := path.Join(tmpDir, "registries.yaml")
ioutil.WriteFile(yamlFile, []byte(yamlConfig), 0644)
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
nManifests := 0
err = filepath.Walk(dir1, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && info.Name() == "manifest.json" {
nManifests++
return filepath.SkipDir
}
return nil
})
err := os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
c.Assert(err, check.IsNil)
c.Assert(nManifests, check.Equals, nTags)
assertSkopeoSucceeds(c, "", "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
assertNumberOfManifestsInSubdirs(c, dir1, nTags)
}
func (s *SyncSuite) TestYamlTLSVerify(c *check.C) {
const localRegURL = "docker://" + v2DockerRegistryURL + "/"
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
dir1 := path.Join(tmpDir, "dir1")
image := pullableRepoWithLatestTag
tag := "latest"
@@ -465,7 +419,8 @@ func (s *SyncSuite) TestYamlTLSVerify(c *check.C) {
for _, cfg := range testCfg {
yamlConfig := fmt.Sprintf(yamlTemplate, v2DockerRegistryURL, cfg.tlsVerify, image, tag)
yamlFile := path.Join(tmpDir, "registries.yaml")
ioutil.WriteFile(yamlFile, []byte(yamlConfig), 0644)
err := os.WriteFile(yamlFile, []byte(yamlConfig), 0644)
c.Assert(err, check.IsNil)
cfg.checker(c, cfg.msg, "sync", "--scoped", "--src", "yaml", "--dest", "dir", yamlFile, dir1)
os.Remove(yamlFile)
@@ -475,9 +430,7 @@ func (s *SyncSuite) TestYamlTLSVerify(c *check.C) {
}
func (s *SyncSuite) TestSyncManifestOutput(c *check.C) {
tmpDir, err := ioutil.TempDir("", "sync-manifest-output")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
destDir1 := filepath.Join(tmpDir, "dest1")
destDir2 := filepath.Join(tmpDir, "dest2")
@@ -497,9 +450,7 @@ func (s *SyncSuite) TestSyncManifestOutput(c *check.C) {
func (s *SyncSuite) TestDocker2DockerTagged(c *check.C) {
const localRegURL = "docker://" + v2DockerRegistryURL + "/"
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
image := pullableTaggedImage
@@ -530,15 +481,13 @@ func (s *SyncSuite) TestDocker2DockerTagged(c *check.C) {
func (s *SyncSuite) TestDir2DockerTagged(c *check.C) {
const localRegURL = "docker://" + v2DockerRegistryURL + "/"
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
image := pullableRepoWithLatestTag
dir1 := path.Join(tmpDir, "dir1")
err = os.Mkdir(dir1, 0755)
err := os.Mkdir(dir1, 0755)
c.Assert(err, check.IsNil)
dir2 := path.Join(tmpDir, "dir2")
err = os.Mkdir(dir2, 0755)
@@ -570,9 +519,7 @@ func (s *SyncSuite) TestDir2DockerTagged(c *check.C) {
}
func (s *SyncSuite) TestFailsWithDir2Dir(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
dir1 := path.Join(tmpDir, "dir1")
dir2 := path.Join(tmpDir, "dir2")
@@ -582,56 +529,48 @@ func (s *SyncSuite) TestFailsWithDir2Dir(c *check.C) {
}
func (s *SyncSuite) TestFailsNoSourceImages(c *check.C) {
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
assertSkopeoFails(c, ".*No images to sync found in .*",
"sync", "--scoped", "--dest-tls-verify=false", "--src", "dir", "--dest", "docker", tmpDir, v2DockerRegistryURL)
assertSkopeoFails(c, ".*No images to sync found in .*",
assertSkopeoFails(c, ".*Error determining repository tags for repo docker.io/library/hopefully_no_images_will_ever_be_called_like_this: fetching tags list: requested access to the resource is denied.*",
"sync", "--scoped", "--dest-tls-verify=false", "--src", "docker", "--dest", "docker", "hopefully_no_images_will_ever_be_called_like_this", v2DockerRegistryURL)
}
func (s *SyncSuite) TestFailsWithDockerSourceNoRegistry(c *check.C) {
const regURL = "google.com/namespace/imagename"
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
//untagged
assertSkopeoFails(c, ".*invalid status code from registry 404.*",
assertSkopeoFails(c, ".*StatusCode: 404.*",
"sync", "--scoped", "--src", "docker", "--dest", "dir", regURL, tmpDir)
//tagged
assertSkopeoFails(c, ".*invalid status code from registry 404.*",
assertSkopeoFails(c, ".*StatusCode: 404.*",
"sync", "--scoped", "--src", "docker", "--dest", "dir", regURL+":thetag", tmpDir)
}
func (s *SyncSuite) TestFailsWithDockerSourceUnauthorized(c *check.C) {
const repo = "privateimagenamethatshouldnotbepublic"
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
//untagged
assertSkopeoFails(c, ".*Registry disallows tag list retrieval.*",
assertSkopeoFails(c, ".*requested access to the resource is denied.*",
"sync", "--scoped", "--src", "docker", "--dest", "dir", repo, tmpDir)
//tagged
assertSkopeoFails(c, ".*unauthorized: authentication required.*",
assertSkopeoFails(c, ".*requested access to the resource is denied.*",
"sync", "--scoped", "--src", "docker", "--dest", "dir", repo+":thetag", tmpDir)
}
func (s *SyncSuite) TestFailsWithDockerSourceNotExisting(c *check.C) {
repo := path.Join(v2DockerRegistryURL, "imagedoesnotexist")
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
defer os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
//untagged
assertSkopeoFails(c, ".*invalid status code from registry 404.*",
assertSkopeoFails(c, ".*repository name not known to registry.*",
"sync", "--scoped", "--src-tls-verify=false", "--src", "docker", "--dest", "dir", repo, tmpDir)
//tagged
@@ -641,9 +580,9 @@ func (s *SyncSuite) TestFailsWithDockerSourceNotExisting(c *check.C) {
func (s *SyncSuite) TestFailsWithDirSourceNotExisting(c *check.C) {
// Make sure the dir does not exist!
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
c.Assert(err, check.IsNil)
err = os.RemoveAll(tmpDir)
tmpDir := c.MkDir()
tmpDir = filepath.Join(tmpDir, "this-does-not-exist")
err := os.RemoveAll(tmpDir)
c.Assert(err, check.IsNil)
_, err = os.Stat(path.Join(tmpDir))
c.Check(os.IsNotExist(err), check.Equals, true)

View File

@@ -3,15 +3,15 @@ package main
import (
"bytes"
"io"
"io/ioutil"
"net"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/containers/image/v5/manifest"
"github.com/go-check/check"
"gopkg.in/check.v1"
)
const skopeoBinary = "skopeo"
@@ -19,7 +19,7 @@ const decompressDirsBinary = "./decompress-dirs.sh"
const testFQIN = "docker://quay.io/libpod/busybox" // tag left off on purpose, some tests need to add a special one
const testFQIN64 = "docker://quay.io/libpod/busybox:amd64"
const testFQINMultiLayer = "docker://quay.io/libpod/alpine_nginx:master" // multi-layer
const testFQINMultiLayer = "docker://quay.io/libpod/alpine_nginx:latest" // multi-layer
// consumeAndLogOutputStream takes (f, err) from an exec.*Pipe(), and causes all output to it to be logged to c.
func consumeAndLogOutputStream(c *check.C, id string, f io.ReadCloser, err error) {
@@ -163,15 +163,15 @@ func modifyEnviron(env []string, name, value string) []string {
// fileFromFixtureFixture applies edits to inputPath and returns a path to the temporary file.
// Callers should defer os.Remove(the_returned_path)
func fileFromFixture(c *check.C, inputPath string, edits map[string]string) string {
contents, err := ioutil.ReadFile(inputPath)
contents, err := os.ReadFile(inputPath)
c.Assert(err, check.IsNil)
for template, value := range edits {
updated := bytes.Replace(contents, []byte(template), []byte(value), -1)
updated := bytes.ReplaceAll(contents, []byte(template), []byte(value))
c.Assert(bytes.Equal(updated, contents), check.Equals, false, check.Commentf("Replacing %s in %#v failed", template, string(contents))) // Verify that the template has matched something and we are not silently ignoring it.
contents = updated
}
file, err := ioutil.TempFile("", "policy.json")
file, err := os.CreateTemp("", "policy.json")
c.Assert(err, check.IsNil)
path := file.Name()
@@ -187,7 +187,7 @@ func fileFromFixture(c *check.C, inputPath string, edits map[string]string) stri
func runDecompressDirs(c *check.C, regexp string, args ...string) {
c.Logf("Running %s %s", decompressDirsBinary, strings.Join(args, " "))
for i, dir := range args {
m, err := ioutil.ReadFile(filepath.Join(dir, "manifest.json"))
m, err := os.ReadFile(filepath.Join(dir, "manifest.json"))
c.Assert(err, check.IsNil)
c.Logf("manifest %d before: %s", i+1, string(m))
}
@@ -197,7 +197,7 @@ func runDecompressDirs(c *check.C, regexp string, args ...string) {
if len(out) > 0 {
c.Logf("output: %s", out)
}
m, err := ioutil.ReadFile(filepath.Join(dir, "manifest.json"))
m, err := os.ReadFile(filepath.Join(dir, "manifest.json"))
c.Assert(err, check.IsNil)
c.Logf("manifest %d after: %s", i+1, string(m))
}
@@ -208,7 +208,7 @@ func runDecompressDirs(c *check.C, regexp string, args ...string) {
// Verify manifest in a dir: image at dir is expectedMIMEType.
func verifyManifestMIMEType(c *check.C, dir string, expectedMIMEType string) {
manifestBlob, err := ioutil.ReadFile(filepath.Join(dir, "manifest.json"))
manifestBlob, err := os.ReadFile(filepath.Join(dir, "manifest.json"))
c.Assert(err, check.IsNil)
mimeType := manifest.GuessMIMEType(manifestBlob)
c.Assert(mimeType, check.Equals, expectedMIMEType)

View File

@@ -1,85 +0,0 @@
let
pkgs = (import ./nixpkgs.nix {
crossSystem = {
config = "aarch64-unknown-linux-gnu";
};
config = {
packageOverrides = pkg: {
gpgme = (static pkg.gpgme);
libassuan = (static pkg.libassuan);
libgpgerror = (static pkg.libgpgerror);
libseccomp = (static pkg.libseccomp);
glib = (static pkg.glib).overrideAttrs (x: {
outputs = [ "bin" "out" "dev" ];
mesonFlags = [
"-Ddefault_library=static"
"-Ddevbindir=${placeholder ''dev''}/bin"
"-Dgtk_doc=false"
"-Dnls=disabled"
];
postInstall = ''
moveToOutput "share/glib-2.0" "$dev"
substituteInPlace "$dev/bin/gdbus-codegen" --replace "$out" "$dev"
sed -i "$dev/bin/glib-gettextize" -e "s|^gettext_dir=.*|gettext_dir=$dev/share/glib-2.0/gettext|"
sed '1i#line 1 "${x.pname}-${x.version}/include/glib-2.0/gobject/gobjectnotifyqueue.c"' \
-i "$dev"/include/glib-2.0/gobject/gobjectnotifyqueue.c
'';
});
pcsclite = (static pkg.pcsclite).overrideAttrs (x: {
configureFlags = [
"--enable-confdir=/etc"
"--enable-usbdropdir=/var/lib/pcsc/drivers"
"--disable-libsystemd"
"--disable-libudev"
"--disable-libusb"
];
buildInputs = [ pkgs.python3 pkgs.dbus ];
});
systemd = (static pkg.systemd).overrideAttrs (x: {
outputs = [ "out" "dev" ];
mesonFlags = x.mesonFlags ++ [
"-Dglib=false"
"-Dstatic-libsystemd=true"
];
});
};
};
});
static = pkg: pkg.overrideAttrs (x: {
doCheck = false;
configureFlags = (x.configureFlags or [ ]) ++ [
"--without-shared"
"--disable-shared"
];
dontDisableStatic = true;
enableSharedExecutables = false;
enableStatic = true;
});
self = with pkgs; buildGoModule rec {
name = "skopeo";
src = ./..;
vendorSha256 = null;
doCheck = false;
enableParallelBuilding = true;
outputs = [ "out" ];
nativeBuildInputs = [ bash gitMinimal go-md2man pkg-config which ];
buildInputs = [ glibc glibc.static glib gpgme libassuan libgpgerror libseccomp ];
prePatch = ''
export CFLAGS='-static -pthread'
export LDFLAGS='-s -w -static-libgcc -static'
export EXTRA_LDFLAGS='-s -w -linkmode external -extldflags "-static -lm"'
export BUILDTAGS='static netgo osusergo exclude_graphdriver_btrfs exclude_graphdriver_devicemapper'
export CGO_ENABLED=1
'';
buildPhase = ''
patchShebangs .
make bin/skopeo
'';
installPhase = ''
install -Dm755 bin/skopeo $out/bin/skopeo
'';
};
in
self

View File

@@ -1,83 +0,0 @@
{ system ? builtins.currentSystem }:
let
pkgs = (import ./nixpkgs.nix {
config = {
packageOverrides = pkg: {
gpgme = (static pkg.gpgme);
libassuan = (static pkg.libassuan);
libgpgerror = (static pkg.libgpgerror);
libseccomp = (static pkg.libseccomp);
glib = (static pkg.glib).overrideAttrs (x: {
outputs = [ "bin" "out" "dev" ];
mesonFlags = [
"-Ddefault_library=static"
"-Ddevbindir=${placeholder ''dev''}/bin"
"-Dgtk_doc=false"
"-Dnls=disabled"
];
postInstall = ''
moveToOutput "share/glib-2.0" "$dev"
substituteInPlace "$dev/bin/gdbus-codegen" --replace "$out" "$dev"
sed -i "$dev/bin/glib-gettextize" -e "s|^gettext_dir=.*|gettext_dir=$dev/share/glib-2.0/gettext|"
sed '1i#line 1 "${x.pname}-${x.version}/include/glib-2.0/gobject/gobjectnotifyqueue.c"' \
-i "$dev"/include/glib-2.0/gobject/gobjectnotifyqueue.c
'';
});
pcsclite = (static pkg.pcsclite).overrideAttrs (x: {
configureFlags = [
"--enable-confdir=/etc"
"--enable-usbdropdir=/var/lib/pcsc/drivers"
"--disable-libsystemd"
"--disable-libudev"
"--disable-libusb"
];
buildInputs = [ pkgs.python3 pkgs.dbus ];
});
systemd = (static pkg.systemd).overrideAttrs (x: {
outputs = [ "out" "dev" ];
mesonFlags = x.mesonFlags ++ [
"-Dglib=false"
"-Dstatic-libsystemd=true"
];
});
};
};
});
static = pkg: pkg.overrideAttrs (x: {
doCheck = false;
configureFlags = (x.configureFlags or [ ]) ++ [
"--without-shared"
"--disable-shared"
];
dontDisableStatic = true;
enableSharedExecutables = false;
enableStatic = true;
});
self = with pkgs; buildGoModule rec {
name = "skopeo";
src = ./..;
vendorSha256 = null;
doCheck = false;
enableParallelBuilding = true;
outputs = [ "out" ];
nativeBuildInputs = [ bash gitMinimal go-md2man pkg-config which ];
buildInputs = [ glibc glibc.static glib gpgme libassuan libgpgerror libseccomp ];
prePatch = ''
export CFLAGS='-static -pthread'
export LDFLAGS='-s -w -static-libgcc -static'
export EXTRA_LDFLAGS='-s -w -linkmode external -extldflags "-static -lm"'
export BUILDTAGS='static netgo osusergo exclude_graphdriver_btrfs exclude_graphdriver_devicemapper'
export CGO_ENABLED=1
'';
buildPhase = ''
patchShebangs .
make bin/skopeo
'';
installPhase = ''
install -Dm755 bin/skopeo $out/bin/skopeo
'';
};
in
self

View File

@@ -1,10 +0,0 @@
{
"url": "https://github.com/nixos/nixpkgs",
"rev": "2a96414d7e350160a33ed0978449c9ff5b5a6eb3",
"date": "2021-07-13T18:21:47+02:00",
"path": "/nix/store/2ai9q8ac6vxb2rrngdz82y8jxnk15cvm-nixpkgs",
"sha256": "1dzrfqdjq3yq5jjskiqflzy58l2xx6059gay9p1k07zrlm1wigy5",
"fetchSubmodules": false,
"deepClone": false,
"leaveDotGit": false
}

View File

@@ -1,9 +0,0 @@
let
json = builtins.fromJSON (builtins.readFile ./nixpkgs.json);
nixpkgs = import (builtins.fetchTarball {
name = "nixos-unstable";
url = "${json.url}/archive/${json.rev}.tar.gz";
inherit (json) sha256;
});
in
nixpkgs

132
skopeo.spec.rpkg Normal file
View File

@@ -0,0 +1,132 @@
# For automatic rebuilds in COPR
# The following tag is to get correct syntax highlighting for this file in vim text editor
# vim: syntax=spec
# Any additinoal comments should go below this line or else syntax highlighting
# may not work.
# CAUTION: This is not a replacement for RPMs provided by your distro.
# Only intended to build and test the latest unreleased changes.
%global gomodulesmode GO111MODULE=on
%global with_debug 1
%if 0%{?with_debug}
%global _find_debuginfo_dwz_opts %{nil}
%global _dwz_low_mem_die_limit 0
%else
%global debug_package %{nil}
%endif
%if ! 0%{?gobuild:1}
%define gobuild(o:) go build -buildmode pie -compiler gc -tags="rpm_crashtraceback ${BUILDTAGS:-}" -ldflags "${LDFLAGS:-} -B 0x$(head -c20 /dev/urandom|od -An -tx1|tr -d ' \\n') -extldflags '-Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld '" -a -v -x %{?**};
%endif
Name: {{{ git_dir_name }}}
Epoch: 101
Version: {{{ git_dir_version }}}
Release: 1%{?dist}
Summary: Inspect container images and repositories on registries
License: ASL 2.0
URL: https://github.com/containers/skopeo
VCS: {{{ git_dir_vcs }}}
Source: {{{ git_dir_pack }}}
%if 0%{?fedora} && ! 0%{?rhel}
BuildRequires: btrfs-progs-devel
%endif
BuildRequires: golang >= 1.16.6
BuildRequires: glib2-devel
BuildRequires: git-core
BuildRequires: go-md2man
%if 0%{?fedora} || 0%{?rhel} >= 9
BuildRequires: go-rpm-macros
%endif
BuildRequires: pkgconfig(devmapper)
BuildRequires: gpgme-devel
BuildRequires: libassuan-devel
BuildRequires: pkgconfig
BuildRequires: make
BuildRequires: ostree-devel
%if 0%{?fedora} <= 35
Requires: containers-common >= 4:1-39
%else
Requires: containers-common >= 4:1-46
%endif
%description
Command line utility to inspect images and repositories directly on Docker
registries without the need to pull them.
%package tests
Summary: Tests for %{name}
Requires: %{name} = %{epoch}:%{version}-%{release}
Requires: bats
Requires: gnupg
Requires: jq
Requires: podman
Requires: httpd-tools
Requires: openssl
Requires: fakeroot
Requires: squashfs-tools
%description tests
%{summary}
This package contains system tests for %{name}
%prep
{{{ git_dir_setup_macro }}}
sed -i 's/install-binary: bin\/skopeo/install-binary:/' Makefile
# This will invoke `make` command in the directory with the extracted sources.
%build
%set_build_flags
export CGO_CFLAGS=$CFLAGS
# These extra flags present in $CFLAGS have been skipped for now as they break the build
CGO_CFLAGS=$(echo $CGO_CFLAGS | sed 's/-flto=auto//g')
CGO_CFLAGS=$(echo $CGO_CFLAGS | sed 's/-Wp,D_GLIBCXX_ASSERTIONS//g')
CGO_CFLAGS=$(echo $CGO_CFLAGS | sed 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-annobin-cc1//g')
%ifarch x86_64
export CGO_CFLAGS+=" -m64 -mtune=generic -fcf-protection=full"
%endif
LDFLAGS=""
export BUILDTAGS="$(hack/libdm_tag.sh)"
%if 0%{?rhel}
export BUILDTAGS="$BUILDTAGS exclude_graphdriver_btrfs btrfs_noversion"
%endif
%gobuild -o bin/%{name} ./cmd/%{name}
%install
%{__make} PREFIX=%{buildroot}%{_prefix} install-binary install-docs install-completions
# system tests
install -d -p %{buildroot}/%{_datadir}/%{name}/test/system
cp -pav systemtest/* %{buildroot}/%{_datadir}/%{name}/test/system/
%files
%license LICENSE
%doc README.md
%{_bindir}/%{name}
%{_mandir}/man1/%%{name}*
%dir %{_datadir}/bash-completion
%dir %{_datadir}/bash-completion/completions
%{_datadir}/bash-completion/completions/%{name}
%dir %{_datadir}/fish
%dir %{_datadir}/fish/vendor_completions.d
%{_datadir}/fish/vendor_completions.d/%{name}.fish
%dir %{_datadir}/zsh
%dir %{_datadir}/zsh/site-functions
%{_datadir}/zsh/site-functions/_%{name}
%files tests
%license LICENSE
%{_datadir}/%{name}/test
%changelog
{{{ git_dir_changelog }}}

View File

@@ -16,4 +16,29 @@ function setup() {
expect_output --substring "skopeo version [0-9.]+"
}
@test "skopeo release isn't a development version" {
[[ "${RELEASE_TESTING:-false}" == "true" ]] || \
skip "Release testing may be enabled by setting \$RELEASE_TESTING = 'true'."
run_skopeo --version
# expect_output() doesn't support negative matching
if [[ "$output" =~ "dev" ]]; then
# This is a multi-line message, which may in turn contain multi-line
# output, so let's format it ourselves, readably
local -a output_split
readarray -t output_split <<<"$output"
printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2
printf "#| FAIL: $BATS_TEST_NAME\n" >&2
printf "#| unexpected: 'dev'\n" >&2
printf "#| actual: '%s'\n" "${output_split[0]}" >&2
local line
for line in "${output_split[@]:1}"; do
printf "#| > '%s'\n" "$line" >&2
done
printf "#\\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" >&2
false
fi
}
# vim: filetype=sh

View File

@@ -95,10 +95,11 @@ END_EXPECT
# is created by the make-noarch-manifest script in this directory.
img=docker://quay.io/libpod/notmyarch:20210121
# Get our host arch (what we're running on). This assumes that skopeo
# arch matches podman; it also assumes running podman >= April 2020
# (prior to that, the format keys were lower-case).
arch=$(podman info --format '{{.Host.Arch}}')
# Get our host golang arch (what we're running on, according to golang).
# This assumes that skopeo arch matches host arch (which it always should).
# Buildah is used here because it depends less on the exact system config
# than podman - and all we're really after is the golang-flavored arch name.
arch=$(go env GOARCH)
# By default, 'inspect' tries to match our host os+arch. This should fail.
run_skopeo 1 inspect $img
@@ -117,4 +118,15 @@ END_EXPECT
"os - variant - architecture of $img"
}
@test "inspect: don't list tags" {
remote_image=docker://quay.io/fedora/fedora
# use --no-tags to not list any tags
run_skopeo inspect --no-tags $remote_image
inspect_output=$output
# extract the content of "RepoTags" property from the JSON output
repo_tags=$(jq '.RepoTags[]' <<<"$inspect_output")
# verify that the RepoTags was empty
expect_output --from="$repo_tags" "" "inspect --no-tags was expected to return empty RepoTags[]"
}
# vim: filetype=sh

View File

@@ -50,7 +50,7 @@ function setup() {
local dir=$TESTDIR/dir
run_skopeo copy --dest-compress --dest-compress-format=zstd $remote_image oci:$dir:latest
run_skopeo copy --dest-compress-format=zstd $remote_image oci:$dir:latest
# zstd magic number
local magic=$(printf "\x28\xb5\x2f\xfd")
@@ -125,6 +125,10 @@ function setup() {
run podman --root $TESTDIR/podmanroot images
expect_output --substring "mine"
# rootless cleanup needs to be done with unshare due to subuids
if [[ "$(id -u)" != "0" ]]; then
run podman unshare rm -rf $TESTDIR/podmanroot
fi
}
# shared blob directory
@@ -144,6 +148,16 @@ function setup() {
diff -urN $shareddir $dir2/blobs
}
@test "copy: sif image" {
type -path fakeroot || skip "'fakeroot' tool not available"
local localimg=dir:$TESTDIR/dir
run_skopeo copy sif:${TEST_SOURCE_DIR}/testdata/busybox_latest.sif $localimg
run_skopeo inspect $localimg --format "{{.Architecture}}"
expect_output "amd64"
}
teardown() {
podman rm -f reg

View File

@@ -8,38 +8,40 @@ load helpers
function setup() {
standard_setup
# Remove old/stale cred file
_cred_dir=$TESTDIR/credentials
export XDG_RUNTIME_DIR=$_cred_dir
mkdir -p $_cred_dir/containers
rm -f $_cred_dir/containers/auth.json
# Start authenticated registry with random password
testuser=testuser
testpassword=$(random_string 15)
start_registry --testuser=$testuser --testpassword=$testpassword --enable-delete=true reg
_cred_dir=$TESTDIR/credentials
# It is important to change XDG_RUNTIME_DIR only after we start the registry, otherwise it affects the path of $XDG_RUNTIME_DIR/netns maintained by Podman,
# making it imposible to clean up after ourselves.
export XDG_RUNTIME_DIR=$_cred_dir
mkdir -p $_cred_dir/containers
# Remove old/stale cred file
rm -f $_cred_dir/containers/auth.json
}
@test "auth: credentials on command line" {
# No creds
run_skopeo 1 inspect --tls-verify=false docker://localhost:5000/nonesuch
expect_output --substring "unauthorized: authentication required"
expect_output --substring "authentication required"
# Wrong user
run_skopeo 1 inspect --tls-verify=false --creds=baduser:badpassword \
docker://localhost:5000/nonesuch
expect_output --substring "unauthorized: authentication required"
expect_output --substring "authentication required"
# Wrong password
run_skopeo 1 inspect --tls-verify=false --creds=$testuser:badpassword \
docker://localhost:5000/nonesuch
expect_output --substring "unauthorized: authentication required"
expect_output --substring "authentication required"
# Correct creds, but no such image
run_skopeo 1 inspect --tls-verify=false --creds=$testuser:$testpassword \
docker://localhost:5000/nonesuch
expect_output --substring "manifest unknown: manifest unknown"
expect_output --substring "manifest unknown"
# These should pass
run_skopeo copy --dest-tls-verify=false --dcreds=$testuser:$testpassword \
@@ -64,7 +66,7 @@ function setup() {
podman logout localhost:5000
run_skopeo 1 inspect --tls-verify=false docker://localhost:5000/busybox:mine
expect_output --substring "unauthorized: authentication required"
expect_output --substring "authentication required"
}
@test "auth: copy with --src-creds and --dest-creds" {
@@ -94,7 +96,7 @@ function setup() {
# inspect without authfile: should fail
run_skopeo 1 inspect --tls-verify=false docker://localhost:5000/busybox:mine
expect_output --substring "unauthorized: authentication required"
expect_output --substring "authentication required"
# inspect with authfile: should work
run_skopeo inspect --tls-verify=false --authfile $TESTDIR/test.auth docker://localhost:5000/busybox:mine

View File

@@ -12,6 +12,13 @@ function setup() {
export GNUPGHOME=$TESTDIR/skopeo-gpg
mkdir --mode=0700 $GNUPGHOME
PASSPHRASE_FILE=$TESTDIR/passphrase-file
passphrase=$(random_string 20)
echo $passphrase > $PASSPHRASE_FILE
PASSPHRASE_FILE_WRONG=$TESTDIR/passphrase-file-wrong
echo $(random_string 10) > $PASSPHRASE_FILE_WRONG
# gpg on f30 needs this, otherwise:
# gpg: agent_genkey failed: Inappropriate ioctl for device
# ...but gpg on f29 (and, probably, Ubuntu) doesn't grok this
@@ -21,7 +28,7 @@ function setup() {
fi
for k in alice bob;do
gpg --batch $GPGOPTS --gen-key --passphrase '' <<END_GPG
gpg --batch $GPGOPTS --gen-key --passphrase $passphrase <<END_GPG
Key-Type: RSA
Name-Real: Test key - $k
Name-email: $k@test.redhat.com
@@ -81,8 +88,18 @@ END_POLICY_JSON
start_registry reg
}
function kill_gpg_agent {
# Kill the running gpg-agent to drop unlocked keys. This allows for testing
# handling of invalid passphrases.
run gpgconf --kill gpg-agent
if [ "$status" -ne 0 ]; then
die "could not restart gpg-agent: $output"
fi
}
@test "signing" {
run_skopeo '?' standalone-sign /dev/null busybox alice@test.redhat.com -o /dev/null
kill_gpg_agent
run_skopeo '?' standalone-sign /dev/null busybox alice@test.redhat.com -o /dev/null --passphrase-file $PASSPHRASE_FILE
if [[ "$output" =~ 'signing is not supported' ]]; then
skip "skopeo built without support for creating signatures"
return 1
@@ -100,7 +117,8 @@ END_POLICY_JSON
while read path sig comments; do
local sign_opt=
if [[ $sig != '-' ]]; then
sign_opt="--sign-by=${sig}@test.redhat.com"
kill_gpg_agent
sign_opt=" --sign-passphrase-file=$PASSPHRASE_FILE --sign-by=${sig}@test.redhat.com"
fi
run_skopeo --registries.d $REGISTRIES_D \
copy --dest-tls-verify=false \
@@ -144,7 +162,8 @@ END_TESTS
}
@test "signing: remove signature" {
run_skopeo '?' standalone-sign /dev/null busybox alice@test.redhat.com -o /dev/null
kill_gpg_agent
run_skopeo '?' standalone-sign /dev/null busybox alice@test.redhat.com -o /dev/null --passphrase-file $PASSPHRASE_FILE
if [[ "$output" =~ 'signing is not supported' ]]; then
skip "skopeo built without support for creating signatures"
return 1
@@ -157,11 +176,24 @@ END_TESTS
run_skopeo copy docker://quay.io/libpod/busybox:latest \
dir:$TESTDIR/busybox
# Push a signed image
kill_gpg_agent
run_skopeo --registries.d $REGISTRIES_D \
copy --dest-tls-verify=false \
--sign-by=alice@test.redhat.com \
--sign-passphrase-file $PASSPHRASE_FILE \
dir:$TESTDIR/busybox \
docker://localhost:5000/myns/alice:signed
# Wrong passphrase file
kill_gpg_agent
run_skopeo 1 --registries.d $REGISTRIES_D \
copy --dest-tls-verify=false \
--sign-by=alice@test.redhat.com \
--sign-passphrase-file $PASSPHRASE_FILE_WRONG \
dir:$TESTDIR/busybox \
docker://localhost:5000/myns/alice:signed
expect_output --substring "Bad passphrase"
# Fetch the image with signature
run_skopeo --registries.d $REGISTRIES_D \
--policy $POLICY_JSON \
@@ -180,7 +212,8 @@ END_TESTS
}
@test "signing: standalone" {
run_skopeo '?' standalone-sign /dev/null busybox alice@test.redhat.com -o /dev/null
kill_gpg_agent
run_skopeo '?' standalone-sign /dev/null busybox alice@test.redhat.com -o /dev/null --passphrase-file $PASSPHRASE_FILE
if [[ "$output" =~ 'signing is not supported' ]]; then
skip "skopeo built without support for creating signatures"
return 1
@@ -196,7 +229,9 @@ END_TESTS
docker://localhost:5000/busybox:latest \
dir:$TESTDIR/busybox
# Standalone sign
kill_gpg_agent
run_skopeo standalone-sign -o $TESTDIR/busybox.signature \
--passphrase-file $PASSPHRASE_FILE \
$TESTDIR/busybox/manifest.json \
localhost:5000/busybox:latest \
alice@test.redhat.com

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env bats
#
# list-tags tests
#
load helpers
# list from registry
@test "list-tags: remote repository on a registry" {
local remote_image=quay.io/libpod/alpine_labels
run_skopeo list-tags "docker://${remote_image}"
expect_output --substring "quay.io/libpod/alpine_labels"
expect_output --substring "latest"
}
# list from a local docker-archive file
@test "list-tags: from a docker-archive file" {
local file_name=${TEST_SOURCE_DIR}/testdata/docker-two-images.tar.xz
run_skopeo list-tags docker-archive:$file_name
expect_output --substring "example.com/empty:latest"
expect_output --substring "example.com/empty/but:different"
}
# vim: filetype=sh

26
systemtest/080-sync.bats Normal file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bats
#
# Sync tests
#
load helpers
function setup() {
standard_setup
}
@test "sync: --dry-run" {
local remote_image=quay.io/libpod/busybox:latest
local dir=$TESTDIR/dir
run_skopeo sync --dry-run --src docker --dest dir --scoped $remote_image $dir
expect_output --substring "Would have copied image"
expect_output --substring "from=\"docker://${remote_image}\" to=\"dir:${dir}/${remote_image}\""
expect_output --substring "Would have synced 1 images from 1 sources"
}
teardown() {
standard_teardown
}
# vim: filetype=sh

View File

@@ -1,12 +1,16 @@
#!/bin/bash
SKOPEO_BINARY=${SKOPEO_BINARY:-$(dirname ${BASH_SOURCE})/../skopeo}
# Directory containing system test sources
TEST_SOURCE_DIR=${TEST_SOURCE_DIR:-$(dirname ${BASH_SOURCE})}
# Skopeo executable
SKOPEO_BINARY=${SKOPEO_BINARY:-${TEST_SOURCE_DIR}/../bin/skopeo}
# Default timeout for a skopeo command.
SKOPEO_TIMEOUT=${SKOPEO_TIMEOUT:-300}
# Default image to run as a local registry
REGISTRY_FQIN=${SKOPEO_TEST_REGISTRY_FQIN:-quay.io/libpod/registry:2}
REGISTRY_FQIN=${SKOPEO_TEST_REGISTRY_FQIN:-quay.io/libpod/registry:2.8.2}
###############################################################################
# BEGIN setup/teardown
@@ -359,6 +363,7 @@ start_registry() {
timeout=$(( timeout - 1 ))
sleep 1
done
log_and_run $PODMAN logs $name
die "Timed out waiting for registry container to respond on :$port"
}

BIN
systemtest/testdata/busybox_latest.sif vendored Executable file

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More