mirror of
https://github.com/containers/skopeo.git
synced 2026-01-30 05:49:52 +00:00
Compare commits
318 Commits
release-1.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48a05d71f0 | ||
|
|
dec587b480 | ||
|
|
8bd9c541f0 | ||
|
|
7c934acec9 | ||
|
|
399adc0ef8 | ||
|
|
75f2311b4c | ||
|
|
b7856d6be6 | ||
|
|
db81e690a7 | ||
|
|
9e00374ce2 | ||
|
|
fe086daee7 | ||
|
|
00de3af75f | ||
|
|
8181f14eaa | ||
|
|
85a14032cc | ||
|
|
7ae3e0bc63 | ||
|
|
13f1418f74 | ||
|
|
f3e66b8fa0 | ||
|
|
bcfc9ce669 | ||
|
|
f399e1d0f0 | ||
|
|
85598438ce | ||
|
|
f23b6fb8f1 | ||
|
|
53f9612136 | ||
|
|
6b2c20caef | ||
|
|
f9d4a40261 | ||
|
|
77325403b2 | ||
|
|
ff5394430c | ||
|
|
83ca23a899 | ||
|
|
7a9cea1bf1 | ||
|
|
d2a1740f0d | ||
|
|
144e9151bd | ||
|
|
01f5061a82 | ||
|
|
a45ceb03d3 | ||
|
|
3d18794ee8 | ||
|
|
d287ec074a | ||
|
|
286c599025 | ||
|
|
5699f10b40 | ||
|
|
6d2144e685 | ||
|
|
1e6817a12c | ||
|
|
fcf9dd6911 | ||
|
|
d80c776b87 | ||
|
|
d79dd05773 | ||
|
|
c7a3a90716 | ||
|
|
b810e9eb46 | ||
|
|
dbd18b9728 | ||
|
|
f0f0c2c639 | ||
|
|
a489ea39a7 | ||
|
|
96eabba0b4 | ||
|
|
f59e8938db | ||
|
|
9753a1a11a | ||
|
|
cba2164671 | ||
|
|
b258da23c2 | ||
|
|
99e3d917f9 | ||
|
|
86853ba4a5 | ||
|
|
933f797dec | ||
|
|
0114569b17 | ||
|
|
24c2a374d6 | ||
|
|
afc0c0f021 | ||
|
|
509ed8e1a4 | ||
|
|
ac66ca51ec | ||
|
|
53bba51c2f | ||
|
|
85e4414c43 | ||
|
|
af27d1d877 | ||
|
|
ee194d4c44 | ||
|
|
193b51364b | ||
|
|
d8be59c1ec | ||
|
|
cf5b488bef | ||
|
|
b0c3f56cf8 | ||
|
|
a3cb7bf314 | ||
|
|
8675decea0 | ||
|
|
749370dd99 | ||
|
|
e5cf4ec75e | ||
|
|
e54634f56a | ||
|
|
5489763ce3 | ||
|
|
4e452f974c | ||
|
|
ef442e3696 | ||
|
|
31ca77cea6 | ||
|
|
9558c9c013 | ||
|
|
cd978903c8 | ||
|
|
d5881b947e | ||
|
|
16897af59a | ||
|
|
dea3a8f16f | ||
|
|
e4cb491edb | ||
|
|
11dedf8b4a | ||
|
|
6d210dd074 | ||
|
|
75eb26e6d1 | ||
|
|
1666b135d4 | ||
|
|
19f1202cd8 | ||
|
|
02055fb052 | ||
|
|
7e235ea335 | ||
|
|
bb1f057541 | ||
|
|
a958e86289 | ||
|
|
885a4fbacd | ||
|
|
0f2339c09c | ||
|
|
d64631991b | ||
|
|
fdcd7c3ebc | ||
|
|
2451f10dd8 | ||
|
|
8602d2e525 | ||
|
|
eecc6c8590 | ||
|
|
0a70844b33 | ||
|
|
32f5d3dc9a | ||
|
|
01252ab813 | ||
|
|
c45394e511 | ||
|
|
056d83e60f | ||
|
|
60b95b1be1 | ||
|
|
2d34849c79 | ||
|
|
d983e39b88 | ||
|
|
5538ce8c5f | ||
|
|
5db9ff4ad8 | ||
|
|
12340b3e7e | ||
|
|
ddf1ea2844 | ||
|
|
94ef22e9e7 | ||
|
|
a7a0fc47b6 | ||
|
|
f9d10b3c9c | ||
|
|
47aa5f526b | ||
|
|
af9f6d73e0 | ||
|
|
e2c1eecd40 | ||
|
|
5650a2cd4d | ||
|
|
33d3ae416a | ||
|
|
bec43c5784 | ||
|
|
36176ac499 | ||
|
|
92ee8a550d | ||
|
|
d1bbcb4626 | ||
|
|
5973d144f2 | ||
|
|
cf76c899c7 | ||
|
|
a4f3fb313a | ||
|
|
b59c8598cd | ||
|
|
203a4cf741 | ||
|
|
319d18c0f9 | ||
|
|
94e949984f | ||
|
|
5d2cb67fb1 | ||
|
|
4dafc99bd6 | ||
|
|
d64b59917d | ||
|
|
5a1e5749c8 | ||
|
|
0aab0745e9 | ||
|
|
7b68a033bc | ||
|
|
a3b8e175c5 | ||
|
|
729a053464 | ||
|
|
f17b4c9672 | ||
|
|
cb17dedf54 | ||
|
|
f87d569628 | ||
|
|
b395b2b923 | ||
|
|
f9bdc2d8fc | ||
|
|
1f18641d34 | ||
|
|
8651716fa7 | ||
|
|
4ac60afc85 | ||
|
|
73f3b44cdb | ||
|
|
862c2331b7 | ||
|
|
872ab48154 | ||
|
|
ef2375bc80 | ||
|
|
a4ab531365 | ||
|
|
3de83e9f73 | ||
|
|
e5104ad291 | ||
|
|
3c93577be1 | ||
|
|
b6c726f854 | ||
|
|
ac8b652729 | ||
|
|
c49bbbe2ee | ||
|
|
99c6fb09e9 | ||
|
|
634c0d99f3 | ||
|
|
40353ad7eb | ||
|
|
855cfb2f23 | ||
|
|
bdc6d70f0e | ||
|
|
654c3fcc9c | ||
|
|
54077f5238 | ||
|
|
3ddcdee045 | ||
|
|
25d4df6ee8 | ||
|
|
abf7bbbab3 | ||
|
|
0f7515e099 | ||
|
|
889225db22 | ||
|
|
eb0e5cf578 | ||
|
|
f87cd5ec62 | ||
|
|
a50b2d910b | ||
|
|
753f8be5c6 | ||
|
|
763606cdde | ||
|
|
f825fdcb15 | ||
|
|
ea37e10d4b | ||
|
|
69af7bfe4c | ||
|
|
3618a76e92 | ||
|
|
8abee34d4b | ||
|
|
08dc3c94de | ||
|
|
65cee5abeb | ||
|
|
c9d5569a84 | ||
|
|
cfae4fd800 | ||
|
|
3ad4bfcfee | ||
|
|
5c119083fe | ||
|
|
50f414a7cf | ||
|
|
dc88f3211b | ||
|
|
a5cbf05259 | ||
|
|
c3e66b5876 | ||
|
|
aaba5370de | ||
|
|
7aa78df96b | ||
|
|
37b0289f3b | ||
|
|
64e5ebe20e | ||
|
|
8335d5aa6e | ||
|
|
ec3516ec89 | ||
|
|
d0d0b7099e | ||
|
|
802f85645c | ||
|
|
9ddfd54926 | ||
|
|
4e4bb0e9d7 | ||
|
|
ffad97c2ff | ||
|
|
39445ea592 | ||
|
|
6ecc6c334f | ||
|
|
983e77d85f | ||
|
|
a477063650 | ||
|
|
faa6f8a008 | ||
|
|
1942cd2ba7 | ||
|
|
c228b1dba4 | ||
|
|
6c3096231c | ||
|
|
f8432950fa | ||
|
|
7ef7c49749 | ||
|
|
2bd609a0dd | ||
|
|
0f95b2bff9 | ||
|
|
159095c102 | ||
|
|
4d32babb91 | ||
|
|
eff578f537 | ||
|
|
bb26ea90f9 | ||
|
|
4c55fce106 | ||
|
|
ee84e9ae0a | ||
|
|
5cf5a11e04 | ||
|
|
47bf2b4ef7 | ||
|
|
320a3e0775 | ||
|
|
9bf0c3bf7c | ||
|
|
ed34be71c6 | ||
|
|
32b8827d78 | ||
|
|
3755a3db63 | ||
|
|
44877b3af2 | ||
|
|
f4d30802b2 | ||
|
|
d66183b129 | ||
|
|
b74989dfbc | ||
|
|
25481e1a88 | ||
|
|
990ac07ff5 | ||
|
|
53a1b69591 | ||
|
|
c5ccf21893 | ||
|
|
f9e03e6c04 | ||
|
|
b39bf85a1a | ||
|
|
a6ff545f30 | ||
|
|
4920d7f172 | ||
|
|
c7dca2e3ac | ||
|
|
5b135b92a8 | ||
|
|
119816b17c | ||
|
|
f5c34db79d | ||
|
|
ddc2848b66 | ||
|
|
b6b6415286 | ||
|
|
610f30db60 | ||
|
|
f7ab0ed03a | ||
|
|
ecdf380b0c | ||
|
|
0e68f7bebd | ||
|
|
c6d7c5916a | ||
|
|
edfeb73504 | ||
|
|
f37a82cddf | ||
|
|
ba2f8b7ace | ||
|
|
11fc49b491 | ||
|
|
b78fa41e42 | ||
|
|
e8d9f916e0 | ||
|
|
da1bf9f7e3 | ||
|
|
5b0b0d3954 | ||
|
|
5b30cfe29c | ||
|
|
d62cbd6178 | ||
|
|
7983f20adb | ||
|
|
8d000f4522 | ||
|
|
1f49b2c0c0 | ||
|
|
f745bb46bc | ||
|
|
a31470d7a4 | ||
|
|
ec21960402 | ||
|
|
1a38d97653 | ||
|
|
115f3727e8 | ||
|
|
49569bcf69 | ||
|
|
e7e09255b4 | ||
|
|
dd71592115 | ||
|
|
9c0f31dcce | ||
|
|
9fda7e7304 | ||
|
|
a0799484c8 | ||
|
|
603d37c588 | ||
|
|
a51828764c | ||
|
|
cb7a78c860 | ||
|
|
b8637922e2 | ||
|
|
48abc39c54 | ||
|
|
56ccf09c68 | ||
|
|
ceabd93f4a | ||
|
|
03fa889da5 | ||
|
|
c87f3aeaac | ||
|
|
15132e6c1e | ||
|
|
e7fe80b5df | ||
|
|
4080a631b1 | ||
|
|
b88f8eccaa | ||
|
|
cc743c3c0f | ||
|
|
a88629df99 | ||
|
|
91c6aa613d | ||
|
|
9d3331be7b | ||
|
|
d00ea33dfa | ||
|
|
0f5d87a9c0 | ||
|
|
7bd0dc216f | ||
|
|
5d0807795d | ||
|
|
547141ce57 | ||
|
|
1de2d3bad9 | ||
|
|
a185498c73 | ||
|
|
070f2bcfaf | ||
|
|
fad5a31a42 | ||
|
|
41e4b1b7ac | ||
|
|
5744b9b49d | ||
|
|
e9340fbc69 | ||
|
|
934ea727a4 | ||
|
|
22ab1a3717 | ||
|
|
8a44fe6c8b | ||
|
|
2d79fec20c | ||
|
|
c7aaed7397 | ||
|
|
9d73060a2e | ||
|
|
0283441cf1 | ||
|
|
5c968d67b8 | ||
|
|
690b1ef3e3 | ||
|
|
841a1b61ae | ||
|
|
8c78c03347 | ||
|
|
8c2eff1dae | ||
|
|
a5916e63cc | ||
|
|
f88186e688 | ||
|
|
ff462b3dad | ||
|
|
a3ffb772e1 | ||
|
|
8c20592d78 | ||
|
|
44d0f7103a | ||
|
|
20746ae273 |
22
.cirrus.yml
22
.cirrus.yml
@@ -21,8 +21,14 @@ env:
|
||||
SCRIPT_BASE: "./contrib/cirrus"
|
||||
|
||||
# Google-cloud VM Images
|
||||
IMAGE_SUFFIX: "c20250131t121915z-f41f40d13"
|
||||
# If you are updating IMAGE_SUFFIX: We are currently using rawhide for
|
||||
# the containers_image_sequoia tests because the rust-podman-sequoia
|
||||
# package is not available in earlier releases; once we update to a future
|
||||
# Fedora release (or if the package is backported), switch back from Rawhide
|
||||
# to the latest Fedora release.
|
||||
IMAGE_SUFFIX: "c20250910t092246z-f42f41d13"
|
||||
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
|
||||
RAWHIDE_CACHE_IMAGE_NAME: "rawhide-${IMAGE_SUFFIX}"
|
||||
|
||||
# Container FQIN's
|
||||
FEDORA_CONTAINER_FQIN: "quay.io/libpod/fedora_podman:${IMAGE_SUFFIX}"
|
||||
@@ -62,7 +68,7 @@ doccheck_task:
|
||||
cpu: 4
|
||||
memory: 8
|
||||
env:
|
||||
BUILDTAGS: &withopengpg 'btrfs_noversion libdm_no_deferred_remove containers_image_openpgp'
|
||||
BUILDTAGS: &withopengpg 'containers_image_openpgp'
|
||||
script: |
|
||||
# TODO: Can't use 'runner.sh setup' inside container. However,
|
||||
# removing the pre-installed package is the only necessary step
|
||||
@@ -190,14 +196,21 @@ test_skopeo_task:
|
||||
# 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}
|
||||
image_name: ${VM_IMAGE_NAME}
|
||||
matrix:
|
||||
- name: "Skopeo Test" # N/B: Name ref. by hack/get_fqin.sh
|
||||
env:
|
||||
BUILDTAGS: 'btrfs_noversion libdm_no_deferred_remove'
|
||||
BUILDTAGS: ''
|
||||
VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME}
|
||||
- name: "Skopeo Test w/ opengpg"
|
||||
env:
|
||||
BUILDTAGS: *withopengpg
|
||||
VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME}
|
||||
- name: "Skopeo test w/ Sequoia (currently Rawhide)"
|
||||
env:
|
||||
BUILDTAGS: 'containers_image_sequoia'
|
||||
# If you are removing the use of rawhide, also remove the VM_IMAGE_NAME condition from runner.sh .
|
||||
VM_IMAGE_NAME: ${RAWHIDE_CACHE_IMAGE_NAME}
|
||||
setup_script: >-
|
||||
"${GOSRC}/${SCRIPT_BASE}/runner.sh" setup
|
||||
vendor_script: >-
|
||||
@@ -226,6 +239,7 @@ meta_task:
|
||||
# Space-separated list of images used by this repository state
|
||||
IMGNAMES: |
|
||||
${FEDORA_CACHE_IMAGE_NAME}
|
||||
${RAWHIDE_CACHE_IMAGE_NAME}
|
||||
build-push-${IMAGE_SUFFIX}
|
||||
BUILDID: "${CIRRUS_BUILD_ID}"
|
||||
REPOREF: "${CIRRUS_REPO_NAME}"
|
||||
|
||||
1
.fmf/version
Normal file
1
.fmf/version
Normal file
@@ -0,0 +1 @@
|
||||
1
|
||||
7
.github/workflows/check_cirrus_cron.yml
vendored
7
.github/workflows/check_cirrus_cron.yml
vendored
@@ -17,4 +17,9 @@ 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
|
||||
secrets:
|
||||
SECRET_CIRRUS_API_KEY: ${{secrets.SECRET_CIRRUS_API_KEY}}
|
||||
ACTION_MAIL_SERVER: ${{secrets.ACTION_MAIL_SERVER}}
|
||||
ACTION_MAIL_USERNAME: ${{secrets.ACTION_MAIL_USERNAME}}
|
||||
ACTION_MAIL_PASSWORD: ${{secrets.ACTION_MAIL_PASSWORD}}
|
||||
ACTION_MAIL_SENDER: ${{secrets.ACTION_MAIL_SENDER}}
|
||||
|
||||
19
.github/workflows/rerun_cirrus_cron.yml
vendored
19
.github/workflows/rerun_cirrus_cron.yml
vendored
@@ -1,19 +0,0 @@
|
||||
---
|
||||
|
||||
# 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
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'A friendly reminder that this issue had no activity for 30 days.'
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
---
|
||||
run:
|
||||
timeout: 5m
|
||||
version: "2"
|
||||
linters:
|
||||
settings:
|
||||
staticcheck:
|
||||
checks:
|
||||
# Compared to golangci-lint v2.0.2 defaults, we don’t exclude
|
||||
# ST1003, ST1016, ST1020, ST1021, ST1022 as we don't hit those.
|
||||
- all
|
||||
- -ST1000 # Incorrect or missing package comment.
|
||||
- -ST1005 # Incorrectly formatted error string.
|
||||
exclusions:
|
||||
presets:
|
||||
- std-error-handling
|
||||
|
||||
61
.packit.yaml
61
.packit.yaml
@@ -19,6 +19,10 @@ packages:
|
||||
skopeo-eln:
|
||||
specfile_path: rpm/skopeo.spec
|
||||
|
||||
# https://packit.dev/docs/configuration/actions
|
||||
actions:
|
||||
get-current-version: bash -c 'grep "^const Version" version/version.go | cut -f2 -d\" | tr \- \~'
|
||||
|
||||
srpm_build_deps:
|
||||
- make
|
||||
|
||||
@@ -29,16 +33,19 @@ jobs:
|
||||
notifications: &copr_build_failure_notification
|
||||
failure_comment:
|
||||
message: "Ephemeral COPR build failed. @containers/packit-build please check."
|
||||
targets:
|
||||
targets: &fedora_copr_targets
|
||||
- fedora-all-x86_64
|
||||
- fedora-all-aarch64
|
||||
enable_net: true
|
||||
# Re-enable these scans if OpenScanHub starts scanning go packages
|
||||
# https://packit.dev/posts/openscanhub-prototype
|
||||
osh_diff_scan_after_copr_build: false
|
||||
|
||||
- job: copr_build
|
||||
trigger: pull_request
|
||||
packages: [skopeo-eln]
|
||||
notifications: *copr_build_failure_notification
|
||||
targets:
|
||||
targets: &eln_copr_targets
|
||||
fedora-eln-x86_64:
|
||||
additional_repos:
|
||||
- "https://kojipkgs.fedoraproject.org/repos/eln-build/latest/x86_64/"
|
||||
@@ -51,7 +58,7 @@ jobs:
|
||||
trigger: pull_request
|
||||
packages: [skopeo-centos]
|
||||
notifications: *copr_build_failure_notification
|
||||
targets:
|
||||
targets: ¢os_copr_targets
|
||||
- centos-stream-9-x86_64
|
||||
- centos-stream-9-aarch64
|
||||
- centos-stream-10-x86_64
|
||||
@@ -62,14 +69,49 @@ jobs:
|
||||
- job: copr_build
|
||||
trigger: commit
|
||||
packages: [skopeo-fedora]
|
||||
notifications:
|
||||
failure_comment:
|
||||
message: "podman-next COPR build failed. @containers/packit-build please check."
|
||||
branch: main
|
||||
owner: rhcontainerbot
|
||||
project: podman-next
|
||||
enable_net: true
|
||||
|
||||
# Tests on Fedora for main branch
|
||||
- job: tests
|
||||
trigger: pull_request
|
||||
packages: [skopeo-fedora]
|
||||
notifications: &test_failure_notification
|
||||
failure_comment:
|
||||
message: "Tests failed. @containers/packit-build please check."
|
||||
targets: *fedora_copr_targets
|
||||
tf_extra_params:
|
||||
environments:
|
||||
- artifacts:
|
||||
- type: repository-file
|
||||
id: https://copr.fedorainfracloud.org/coprs/rhcontainerbot/podman-next/repo/fedora-$releasever/rhcontainerbot-podman-next-fedora-$releasever.repo
|
||||
|
||||
# Tests on ELN for main branch
|
||||
- job: tests
|
||||
trigger: pull_request
|
||||
packages: [skopeo-eln]
|
||||
notifications: *test_failure_notification
|
||||
targets: *eln_copr_targets
|
||||
tf_extra_params:
|
||||
environments:
|
||||
- artifacts:
|
||||
- type: repository-file
|
||||
id: https://copr.fedorainfracloud.org/coprs/rhcontainerbot/podman-next/repo/fedora-eln/rhcontainerbot-podman-next-fedora-eln.repo
|
||||
|
||||
# Tests on CentOS Stream for main branch
|
||||
- job: tests
|
||||
trigger: pull_request
|
||||
packages: [skopeo-centos]
|
||||
notifications: *test_failure_notification
|
||||
targets: *centos_copr_targets
|
||||
tf_extra_params:
|
||||
environments:
|
||||
- artifacts:
|
||||
- type: repository-file
|
||||
id: https://copr.fedorainfracloud.org/coprs/rhcontainerbot/podman-next/repo/centos-stream-$releasever/rhcontainerbot-podman-next-centos-stream-$releasever.repo
|
||||
|
||||
# Sync to Fedora
|
||||
- job: propose_downstream
|
||||
trigger: release
|
||||
@@ -77,10 +119,15 @@ jobs:
|
||||
update_release: false
|
||||
dist_git_branches: &fedora_targets
|
||||
- fedora-all
|
||||
actions:
|
||||
post-modifications: >-
|
||||
bash -c "sed -i 's/^\(\s*\)ref: .*/\1ref: \"v${PACKIT_PROJECT_VERSION}\"/' ${PACKIT_DOWNSTREAM_REPO}/plans/main.fmf"
|
||||
|
||||
# Sync to CentOS Stream
|
||||
# FIXME: Switch trigger whenever we're ready to update CentOS Stream via
|
||||
# Packit
|
||||
- job: propose_downstream
|
||||
trigger: release
|
||||
trigger: ignore
|
||||
packages: [skopeo-centos]
|
||||
update_release: false
|
||||
dist_git_branches:
|
||||
|
||||
@@ -129,28 +129,32 @@ In order to update an existing dependency:
|
||||
- use `go get -d -u path/to/dep@version` to update the relevant dependency line in `go.mod`
|
||||
- run `make vendor`
|
||||
|
||||
When new PRs for [containers/image](https://github.com/containers/image) break `skopeo` (i.e. `containers/image` tests fail in `make test-skopeo`):
|
||||
When new PRs for [containers/container-libs](https://github.com/containers/container-libs) break `skopeo` (i.e. `containers/container-libs` tests fail in `make test-skopeo`):
|
||||
|
||||
- create out a new branch in your `skopeo` checkout and switch to it
|
||||
- find out the version of `containers/image` you want to use and note its commit ID. You might also want to use a fork of `containers/image`, in that case note its repo
|
||||
- use `go get -d github.com/$REPO/image/v5@$COMMIT_ID` to download the right version. The command will fetch the dependency and then fail because of a conflict in `go.mod`, this is expected. Note the pseudo-version (eg. `v5.13.1-0.20210707123201-50afbf0a326`)
|
||||
- use `go mod edit -replace=github.com/containers/image/v5=github.com/$REPO/image/v5@$PSEUDO_VERSION` to add a replacement line to `go.mod` (e.g. `replace github.com/containers/image/v5 => github.com/moio/image/v5 v5.13.1-0.20210707123201-50afbf0a3262`)
|
||||
- find out the version of `containers/container-libs` you want to use and note its commit ID. You might also want to use a fork of `containers/container-libs`, in that case note its repo
|
||||
- use `go get -d github.com/$REPO/container-libs/image/v5@$COMMIT_ID` to download the right version. The command will fetch the dependency and then fail because of a conflict in `go.mod`, this is expected. Note the pseudo-version (eg. `v5.13.1-0.20210707123201-50afbf0a326`)
|
||||
- use `go mod edit -replace=go.podman.io/image/v5=github.com/$REPO/container-libs/image/v5@$PSEUDO_VERSION` to add a replacement line to `go.mod` (e.g. `replace go.podman.io/image/v5 => github.com/moio/container-libs/image/v5 v5.13.1-0.20210707123201-50afbf0a3262`)
|
||||
- run `make vendor`
|
||||
- make any other necessary changes in the skopeo repo (e.g. add other dependencies now required by `containers/image`, or update skopeo for changed `containers/image` API)
|
||||
- make any other necessary changes in the skopeo repo (e.g. add other dependencies now required by `containers/container-libs`, or update skopeo for changed `containers/container-libs` API)
|
||||
- optionally add new integration tests to the skopeo repo
|
||||
- submit the resulting branch as a skopeo PR, marked “DO NOT MERGE”
|
||||
- iterate until tests pass and the PR is reviewed
|
||||
- then the original `containers/image` PR can be merged, disregarding its `make test-skopeo` failure
|
||||
- as soon as possible after that, in the skopeo PR, use `go mod edit -dropreplace=github.com/containers/image` to remove the `replace` line in `go.mod`
|
||||
- then the original `containers/container-libs` PR can be merged, disregarding its `make test-skopeo` failure
|
||||
- as soon as possible after that, in the skopeo PR, use `go mod edit -dropreplace=go.podman.io/image/v5` to remove the `replace` line in `go.mod`
|
||||
- run `make vendor`
|
||||
- update the skopeo PR with the result, drop the “DO NOT MERGE” marking
|
||||
- after tests complete successfully again, merge the skopeo PR
|
||||
|
||||
## Communications
|
||||
|
||||
For general questions, or discussions, please use the
|
||||
IRC channel on `irc.libera.chat` called `#container-projects`
|
||||
that has been setup.
|
||||
For general questions, or discussions, please use the
|
||||
[#podman](https://app.slack.com/client/T08PSQ7BQ/C08MXJLCFCN) channel on the [CNCF
|
||||
Slack](https://cloud-native.slack.com).
|
||||
|
||||
For development related discussions, please use the
|
||||
[#podman-dev](https://app.slack.com/client/T08PSQ7BQ/C08NTKCDC1W) channel on the CNCF
|
||||
Slack.
|
||||
|
||||
For discussions around issues/bugs and features, you can use the github
|
||||
[issues](https://github.com/containers/skopeo/issues)
|
||||
|
||||
12
GOVERNANCE.md
Normal file
12
GOVERNANCE.md
Normal file
@@ -0,0 +1,12 @@
|
||||
## The Skopeo Project Community Governance
|
||||
|
||||
The Skopeo project, as part of Podman Container Tools, follows the [Podman Project Governance](https://github.com/containers/podman/blob/main/GOVERNANCE.md)
|
||||
except sections found in this document, which override those found in Podman's Governance.
|
||||
|
||||
---
|
||||
|
||||
# Maintainers File
|
||||
|
||||
The definitive source of truth for maintainers of this repository is the local [MAINTAINERS.md](./MAINTAINERS.md) file. The [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file in the main Podman repository is used for project-spanning roles, including Core Maintainer and Community Manager. Some repositories in the project will also have a local [OWNERS](./OWNERS) file, which the CI system uses to map users to roles. Any changes to the [OWNERS](./OWNERS) file must make a corresponding change to the [MAINTAINERS.md](./MAINTAINERS.md) file to ensure that the file remains up to date. Most changes to [MAINTAINERS.md](./MAINTAINERS.md) will require a change to the repository’s [OWNERS](.OWNERS) file (e.g., adding a Reviewer), but some will not (e.g., promoting a Maintainer to a Core Maintainer, which comes with no additional CI-related privileges).
|
||||
|
||||
Any Core Maintainers listed in Podman’s [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file should also be added to the list of “approvers” in the local [OWNERS](./OWNERS) file and as a Core Maintainer in the list of “Maintainers” in the local [MAINTAINERS.md](./MAINTAINERS.md) file.
|
||||
35
MAINTAINERS.md
Normal file
35
MAINTAINERS.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Skopeo Maintainers
|
||||
|
||||
[GOVERNANCE.md](https://github.com/containers/podman/blob/main/GOVERNANCE.md)
|
||||
describes the project's governance and the Project Roles used below.
|
||||
|
||||
## Maintainers
|
||||
|
||||
| Maintainer | GitHub ID | Project Roles | Affiliation |
|
||||
|-------------------|----------------------------------------------------------|----------------------------------|----------------------------------------------|
|
||||
| Brent Baude | [baude](https://github.com/baude) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Nalin Dahyabhai | [nalind](https://github.com/nalind) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Matthew Heon | [mheon](https://github.com/mheon) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Paul Holzinger | [Luap99](https://github.com/Luap99) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Giuseppe Scrivano | [giuseppe](https://github.com/giuseppe) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Miloslav Trmač | [mtrmac](https://github.com/mtrmac) | Core Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Mohan Boddu | [mohanboddu](https://github.com/mohanboddu) | Community Manager | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Neil Smith | [actionmancan](https://github.com/actionmancan) | Community Manager | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Tom Sweeney | [TomSweeneyRedHat](https://github.com/TomSweeneyRedHat/) | Maintainer and Community Manager | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Lokesh Mandvekar | [lsm5](https://github.com/lsm5) | Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Dan Walsh | [rhatdan](https://github.com/rhatdan) | Maintainer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Ashley Cui | [ashley-cui](https://github.com/ashley-cui) | Reviewer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Valentin Rothberg | [vrothberg](https://github.com/vrothberg) | Reviewer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
| Colin Walters | [cgwalters](https://github.com/cgwalters) | Reviewer | [Red Hat](https://github.com/RedHatOfficial) |
|
||||
|
||||
## Alumni
|
||||
|
||||
None at present
|
||||
|
||||
## Credits
|
||||
|
||||
The structure of this document was based off of the equivalent one in the [CRI-O Project](https://github.com/cri-o/cri-o/blob/main/MAINTAINERS.md).
|
||||
|
||||
## Note
|
||||
|
||||
If there is a discrepancy between the [MAINTAINERS.md](https://github.com/containers/podman/blob/main/MAINTAINERS.md) file in the main Podman repository and this file regarding Core Maintainers or Community Managers, the file in the Podman Repository is considered the source of truth.
|
||||
50
Makefile
50
Makefile
@@ -24,10 +24,12 @@ GOBIN := $(shell $(GO) env GOBIN)
|
||||
GOOS ?= $(shell go env GOOS)
|
||||
GOARCH ?= $(shell go env GOARCH)
|
||||
|
||||
SEQUOIA_SONAME_DIR =
|
||||
|
||||
# N/B: This value is managed by Renovate, manual changes are
|
||||
# possible, as long as they don't disturb the formatting
|
||||
# (i.e. DO NOT ADD A 'v' prefix!)
|
||||
GOLANGCI_LINT_VERSION := 1.63.4
|
||||
GOLANGCI_LINT_VERSION := 2.6.1
|
||||
|
||||
ifeq ($(GOBIN),)
|
||||
GOBIN := $(GOPATH)/bin
|
||||
@@ -53,10 +55,9 @@ ifeq ($(GOOS), linux)
|
||||
endif
|
||||
endif
|
||||
|
||||
# If $TESTFLAGS is set, it is passed as extra arguments to 'go test'.
|
||||
# If $TESTFLAGS is set, it is passed as extra arguments to 'go test' on integration tests.
|
||||
# You can select certain tests to run, with `-run <regex>` for example:
|
||||
#
|
||||
# make test-unit TESTFLAGS='-run ^TestManifestDigest$'
|
||||
# make test-integration TESTFLAGS='-run copySuite.TestCopy.*'
|
||||
export TESTFLAGS ?= -timeout=15m
|
||||
|
||||
@@ -82,17 +83,16 @@ endif
|
||||
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_CEILING_DIRECTORIES=$$(cd ..; pwd) git rev-parse HEAD 2> /dev/null || true)
|
||||
|
||||
EXTRA_LDFLAGS ?=
|
||||
SKOPEO_LDFLAGS := -ldflags '-X main.gitCommit=${GIT_COMMIT} $(EXTRA_LDFLAGS)'
|
||||
SKOPEO_LDFLAGS := -ldflags '-X go.podman.io/image/v5/signature/internal/sequoia.sequoiaLibraryDir=$(SEQUOIA_SONAME_DIR) $(EXTRA_LDFLAGS)'
|
||||
|
||||
MANPAGES_MD = $(wildcard docs/*.md)
|
||||
MANPAGES ?= $(MANPAGES_MD:%.md=%)
|
||||
|
||||
BTRFS_BUILD_TAG = $(shell hack/btrfs_tag.sh) $(shell hack/btrfs_installed_tag.sh)
|
||||
BTRFS_BUILD_TAG = $(shell hack/btrfs_installed_tag.sh)
|
||||
LIBSUBID_BUILD_TAG = $(shell hack/libsubid_tag.sh)
|
||||
LOCAL_BUILD_TAGS = $(BTRFS_BUILD_TAG) $(LIBSUBID_BUILD_TAG)
|
||||
SQLITE_BUILD_TAG = $(shell hack/sqlite_tag.sh)
|
||||
LOCAL_BUILD_TAGS = $(BTRFS_BUILD_TAG) $(LIBSUBID_BUILD_TAG) $(SQLITE_BUILD_TAG)
|
||||
BUILDTAGS += $(LOCAL_BUILD_TAGS)
|
||||
|
||||
ifeq ($(DISABLE_CGO), 1)
|
||||
@@ -201,10 +201,19 @@ test-integration:
|
||||
$(MAKE) test-integration-local
|
||||
|
||||
|
||||
# Intended for CI, assumed to be running in quay.io/libpod/skopeo_cidev container.
|
||||
test-integration-local: bin/skopeo
|
||||
# Helper target to set up SKOPEO_BINARY variable for local test targets
|
||||
# SKOPEO_BINARY only takes effect on `test-integration-local` and
|
||||
# `test-system-local` targets. It's not propagated into the container used for `test-integration` and
|
||||
# `test-system`. These targets will (build and) use skopeo binary at
|
||||
# ./bin/skopeo.
|
||||
.eval-skopeo-binary: $(if $(SKOPEO_BINARY),,bin/skopeo)
|
||||
$(eval SKOPEO_BINARY := $(or $(SKOPEO_BINARY),./bin/skopeo))
|
||||
@echo "Testing with $(SKOPEO_BINARY) ..."
|
||||
|
||||
# Primarily intended for CI.
|
||||
test-integration-local: .eval-skopeo-binary
|
||||
hack/warn-destructive-tests.sh
|
||||
hack/test-integration.sh
|
||||
cd ./integration && SKOPEO_BINARY="$(abspath $(SKOPEO_BINARY))" $(GO) test $(SKOPEO_LDFLAGS) $(TESTFLAGS) $(if $(BUILDTAGS),-tags "$(BUILDTAGS)")
|
||||
|
||||
# complicated set of options needed to run podman-in-podman
|
||||
test-system:
|
||||
@@ -218,8 +227,8 @@ test-system:
|
||||
$(CONTAINER_RUNTIME) unshare rm -rf $$DTEMP; # This probably doesn't work with Docker, oh well, better than nothing... \
|
||||
exit $$rc
|
||||
|
||||
# Intended for CI, assumed to already be running in quay.io/libpod/skopeo_cidev container.
|
||||
test-system-local: bin/skopeo
|
||||
# Primarily intended for CI.
|
||||
test-system-local: .eval-skopeo-binary
|
||||
hack/warn-destructive-tests.sh
|
||||
hack/test-system.sh
|
||||
|
||||
@@ -234,10 +243,14 @@ validate:
|
||||
test-all-local: validate-local validate-docs test-unit-local
|
||||
|
||||
.PHONY: validate-local
|
||||
validate-local:
|
||||
validate-local: tools
|
||||
hack/validate-git-marks.sh
|
||||
hack/validate-gofmt.sh
|
||||
GOBIN=$(GOBIN) hack/validate-lint.sh
|
||||
$(GOBIN)/golangci-lint run --build-tags "${BUILDTAGS}"
|
||||
# An extra run with --tests=false allows detecting code unused outside of tests;
|
||||
# ideally the linter should be able to find this automatically.
|
||||
# Since everything is already cached, this additional run doesn't take much time.
|
||||
$(GOBIN)/golangci-lint run --build-tags "${BUILDTAGS}" --tests=false
|
||||
BUILDTAGS="${BUILDTAGS}" hack/validate-vet.sh
|
||||
|
||||
# This invokes bin/skopeo, hence cannot be run as part of validate-local
|
||||
@@ -247,7 +260,7 @@ validate-docs: bin/skopeo
|
||||
hack/xref-helpmsgs-manpages
|
||||
|
||||
test-unit-local:
|
||||
$(GO) test -tags "$(BUILDTAGS)" $$($(GO) list -tags "$(BUILDTAGS)" -e ./... | grep -v '^github\.com/containers/skopeo/\(integration\|vendor/.*\)$$')
|
||||
$(GO) test $(SKOPEO_LDFLAGS) -tags "$(BUILDTAGS)" $$($(GO) list -tags "$(BUILDTAGS)" -e ./... | grep -v '^github\.com/containers/skopeo/\(integration\|vendor/.*\)$$')
|
||||
|
||||
vendor:
|
||||
$(GO) mod tidy
|
||||
@@ -256,8 +269,3 @@ vendor:
|
||||
|
||||
vendor-in-container:
|
||||
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
|
||||
|
||||
18
OWNERS
18
OWNERS
@@ -1,17 +1,23 @@
|
||||
approvers:
|
||||
- mtrmac
|
||||
- baude
|
||||
- giuseppe
|
||||
- lsm5
|
||||
- TomSweeneyRedHat
|
||||
- Luap99
|
||||
- mheon
|
||||
- mtrmac
|
||||
- nalind
|
||||
- rhatdan
|
||||
- vrothberg
|
||||
- TomSweeneyRedHat
|
||||
reviewers:
|
||||
- ashley-cui
|
||||
- baude
|
||||
- cgwalters
|
||||
- giuseppe
|
||||
- containers/image-maintainers
|
||||
- lsm5
|
||||
- Luap99
|
||||
- mheon
|
||||
- mtrmac
|
||||
- QiWang19
|
||||
- nalind
|
||||
- rhatdan
|
||||
- runcom
|
||||
- TomSweeneyRedHat
|
||||
- vrothberg
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
</p>
|
||||
|
||||
----
|
||||

|
||||

|
||||
[](https://goreportcard.com/report/github.com/containers/skopeo)
|
||||
[](https://www.bestpractices.dev/projects/10516)
|
||||
|
||||
`skopeo` is a command line utility that performs various operations on container images and image repositories.
|
||||
|
||||
|
||||
12
ROADMAP.md
Normal file
12
ROADMAP.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Skopeo Roadmap
|
||||
|
||||
Skopeo intends to mostly continue to be a very thin CLI wrapper over the [https://github.com/containers/image](containers/image) library, with most features being added there, not to this repo. A typical new Skopeo feature would only add a CLI for a recent containers/image feature.
|
||||
|
||||
## Future feature focus (most of the work must be done in the containers/image library)
|
||||
|
||||
* OCI artifact support.
|
||||
* Integration of composefs.
|
||||
* Partial pull support (zstd:chunked).
|
||||
* Performance and stability improvements.
|
||||
* Reductions to the size of the Skopeo binary.
|
||||
* `skopeo sync` exists, and bugs in it should be fixed, but we don’t have much of an ambition to compete with much larger projects like [https://github.com/openshift/oc-mirror](oc-mirror).
|
||||
@@ -1,15 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/containers/image/v5/tarball"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/image/v5/directory"
|
||||
"go.podman.io/image/v5/docker"
|
||||
dockerArchive "go.podman.io/image/v5/docker/archive"
|
||||
ociArchive "go.podman.io/image/v5/oci/archive"
|
||||
oci "go.podman.io/image/v5/oci/layout"
|
||||
"go.podman.io/image/v5/sif"
|
||||
"go.podman.io/image/v5/tarball"
|
||||
"go.podman.io/image/v5/transports"
|
||||
)
|
||||
|
||||
// autocompleteSupportedTransports list all supported transports with the colon suffix.
|
||||
func autocompleteSupportedTransports(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func autocompleteImageNames(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
|
||||
transport, details, haveTransport := strings.Cut(toComplete, ":")
|
||||
if !haveTransport {
|
||||
transports := supportedTransportSuggestions()
|
||||
return transports, cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
switch transport {
|
||||
case ociArchive.Transport.Name(), dockerArchive.Transport.Name():
|
||||
// Can have [:{*reference|@source-index}]
|
||||
// FIXME: `oci-archive:/path/to/a.oci:<TAB>` completes paths
|
||||
return nil, cobra.ShellCompDirectiveNoSpace
|
||||
case sif.Transport.Name():
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
|
||||
// Both directory and oci should have ShellCompDirectiveFilterDirs to complete only directories, but it doesn't currently work in bash: https://github.com/spf13/cobra/issues/2242
|
||||
case oci.Transport.Name():
|
||||
// Can have '[:{reference|@source-index}]'
|
||||
// FIXME: `oci:/path/to/dir/:<TAB>` completes paths
|
||||
return nil, cobra.ShellCompDirectiveDefault | cobra.ShellCompDirectiveNoSpace
|
||||
case directory.Transport.Name():
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
|
||||
case docker.Transport.Name():
|
||||
if details == "" {
|
||||
return []cobra.Completion{transport + "://"}, cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
// supportedTransportSuggestions list all supported transports with the colon suffix.
|
||||
func supportedTransportSuggestions() []string {
|
||||
tps := transports.ListNames()
|
||||
suggestions := make([]string, 0, len(tps))
|
||||
suggestions := make([]cobra.Completion, 0, len(tps))
|
||||
for _, tp := range tps {
|
||||
// ListNames is generally expected to filter out deprecated transports.
|
||||
// tarball: is not deprecated, but it is only usable from a Go caller (using tarball.ConfigUpdater),
|
||||
@@ -18,5 +56,5 @@ func autocompleteSupportedTransports(cmd *cobra.Command, args []string, toComple
|
||||
suggestions = append(suggestions, tp+":")
|
||||
}
|
||||
}
|
||||
return suggestions, cobra.ShellCompDirectiveNoFileComp
|
||||
return suggestions
|
||||
}
|
||||
|
||||
@@ -7,44 +7,35 @@ import (
|
||||
"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"
|
||||
enchelpers "github.com/containers/ocicrypt/helpers"
|
||||
"github.com/spf13/cobra"
|
||||
commonFlag "go.podman.io/common/pkg/flag"
|
||||
"go.podman.io/common/pkg/retry"
|
||||
"go.podman.io/image/v5/copy"
|
||||
"go.podman.io/image/v5/docker/reference"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
"go.podman.io/image/v5/transports"
|
||||
"go.podman.io/image/v5/transports/alltransports"
|
||||
)
|
||||
|
||||
type copyOptions struct {
|
||||
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
|
||||
imageParallelCopies uint // Maximum number of parallel requests when copying images
|
||||
global *globalOptions
|
||||
deprecatedTLSVerify *deprecatedTLSVerifyOption
|
||||
srcImage *imageOptions
|
||||
destImage *imageDestOptions
|
||||
retryOpts *retry.Options
|
||||
copy *sharedCopyOptions
|
||||
additionalTags []string // For docker-archive: destinations, in addition to the name:tag specified as destination, also add these
|
||||
signIdentity string // Identity of the signed image, must be a fully specified docker reference
|
||||
digestFile string // Write digest to this file
|
||||
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
|
||||
encryptLayer []int // The list of layers to encrypt
|
||||
encryptionKeys []string // Keys needed to encrypt the image
|
||||
decryptionKeys []string // Keys needed to decrypt the image
|
||||
imageParallelCopies uint // Maximum number of parallel requests when copying images
|
||||
}
|
||||
|
||||
func copyCmd(global *globalOptions) *cobra.Command {
|
||||
@@ -53,11 +44,13 @@ func copyCmd(global *globalOptions) *cobra.Command {
|
||||
srcFlags, srcOpts := imageFlags(global, sharedOpts, deprecatedTLSVerifyOpt, "src-", "screds")
|
||||
destFlags, destOpts := imageDestFlags(global, sharedOpts, deprecatedTLSVerifyOpt, "dest-", "dcreds")
|
||||
retryFlags, retryOpts := retryFlags()
|
||||
copyFlags, copyOpts := sharedCopyFlags()
|
||||
opts := copyOptions{global: global,
|
||||
deprecatedTLSVerify: deprecatedTLSVerifyOpt,
|
||||
srcImage: srcOpts,
|
||||
destImage: destOpts,
|
||||
retryOpts: retryOpts,
|
||||
copy: copyOpts,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "copy [command options] SOURCE-IMAGE DESTINATION-IMAGE",
|
||||
@@ -71,7 +64,7 @@ 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`,
|
||||
ValidArgsFunction: autocompleteSupportedTransports,
|
||||
ValidArgsFunction: autocompleteImageNames,
|
||||
}
|
||||
adjustUsage(cmd)
|
||||
flags := cmd.Flags()
|
||||
@@ -80,19 +73,13 @@ See skopeo(1) section "IMAGE NAMES" for the expected format
|
||||
flags.AddFlagSet(&srcFlags)
|
||||
flags.AddFlagSet(&destFlags)
|
||||
flags.AddFlagSet(&retryFlags)
|
||||
flags.AddFlagSet(©Flags)
|
||||
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(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")
|
||||
@@ -160,14 +147,6 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var manifestType string
|
||||
if opts.format.Present() {
|
||||
manifestType, err = parseManifestFormat(opts.format.Value())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, image := range opts.additionalTags {
|
||||
ref, err := reference.ParseNormalizedNamed(image)
|
||||
if err != nil {
|
||||
@@ -237,43 +216,6 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||
decConfig = cc.DecryptConfig
|
||||
}
|
||||
|
||||
// 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 shouldn’t 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)
|
||||
@@ -284,26 +226,23 @@ func (opts *copyOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||
|
||||
opts.destImage.warnAboutIneffectiveOptions(destRef.Transport())
|
||||
|
||||
copyOpts, cleanupOptions, err := opts.copy.copyOptions(stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanupOptions()
|
||||
copyOpts.SignIdentity = signIdentity
|
||||
copyOpts.SourceCtx = sourceCtx
|
||||
copyOpts.DestinationCtx = destinationCtx
|
||||
copyOpts.ImageListSelection = imageListSelection
|
||||
copyOpts.OciDecryptConfig = decConfig
|
||||
copyOpts.OciEncryptLayers = encLayers
|
||||
copyOpts.OciEncryptConfig = encConfig
|
||||
copyOpts.MaxParallelDownloads = opts.imageParallelCopies
|
||||
copyOpts.ForceCompressionFormat = opts.destImage.forceCompressionFormat
|
||||
|
||||
return retry.IfNecessary(ctx, func() error {
|
||||
manifestBytes, err := copy.Image(ctx, policyContext, destRef, srcRef, ©.Options{
|
||||
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,
|
||||
MaxParallelDownloads: opts.imageParallelCopies,
|
||||
})
|
||||
manifestBytes, err := copy.Image(ctx, policyContext, destRef, srcRef, copyOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/common/pkg/retry"
|
||||
"github.com/containers/image/v5/transports"
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/common/pkg/retry"
|
||||
"go.podman.io/image/v5/transports"
|
||||
"go.podman.io/image/v5/transports/alltransports"
|
||||
)
|
||||
|
||||
type deleteOptions struct {
|
||||
@@ -37,7 +37,7 @@ 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`,
|
||||
ValidArgsFunction: autocompleteSupportedTransports,
|
||||
ValidArgsFunction: autocompleteImageNames,
|
||||
}
|
||||
adjustUsage(cmd)
|
||||
flags := cmd.Flags()
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,9 +7,9 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/containers/image/v5/pkg/cli"
|
||||
"github.com/containers/image/v5/signature/sigstore"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/image/v5/pkg/cli"
|
||||
"go.podman.io/image/v5/signature/sigstore"
|
||||
)
|
||||
|
||||
type generateSigstoreKeyOptions struct {
|
||||
@@ -85,6 +85,6 @@ func (opts *generateSigstoreKeyOptions) run(args []string, stdout io.Writer) 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)
|
||||
fmt.Fprintf(stdout, "Key written to %q and %q\n", privateKeyPath, pubKeyPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,18 +7,18 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/common/pkg/report"
|
||||
"github.com/containers/common/pkg/retry"
|
||||
"github.com/containers/image/v5/docker"
|
||||
"github.com/containers/image/v5/image"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"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/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/common/pkg/report"
|
||||
"go.podman.io/common/pkg/retry"
|
||||
"go.podman.io/image/v5/docker"
|
||||
"go.podman.io/image/v5/image"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
"go.podman.io/image/v5/transports"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
type inspectOptions struct {
|
||||
@@ -53,17 +53,17 @@ 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,
|
||||
ValidArgsFunction: autocompleteImageNames,
|
||||
}
|
||||
adjustUsage(cmd)
|
||||
flags := cmd.Flags()
|
||||
flags.AddFlagSet(&sharedFlags)
|
||||
flags.AddFlagSet(&imageFlags)
|
||||
flags.AddFlagSet(&retryFlags)
|
||||
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)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -106,8 +106,9 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
|
||||
}
|
||||
}()
|
||||
|
||||
unparsedInstance := image.UnparsedInstance(src, nil)
|
||||
if err := retry.IfNecessary(ctx, func() error {
|
||||
rawManifest, _, err = src.GetManifest(ctx, nil)
|
||||
rawManifest, _, err = unparsedInstance.Manifest(ctx)
|
||||
return err
|
||||
}, opts.retryOpts); err != nil {
|
||||
return fmt.Errorf("Error retrieving manifest for image: %w", err)
|
||||
@@ -122,7 +123,7 @@ func (opts *inspectOptions) run(args []string, stdout io.Writer) (retErr error)
|
||||
return nil
|
||||
}
|
||||
|
||||
img, err := image.FromUnparsedImage(ctx, sys, image.UnparsedInstance(src, nil))
|
||||
img, err := image.FromUnparsedImage(ctx, sys, unparsedInstance)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error parsing manifest for image: %w", err)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package inspect
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
// Output is the output format of (skopeo inspect),
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/common/pkg/retry"
|
||||
"github.com/containers/image/v5/directory"
|
||||
"github.com/containers/image/v5/image"
|
||||
"github.com/containers/image/v5/pkg/blobinfocache"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/common/pkg/retry"
|
||||
"go.podman.io/image/v5/directory"
|
||||
"go.podman.io/image/v5/image"
|
||||
"go.podman.io/image/v5/pkg/blobinfocache"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
type layersOptions struct {
|
||||
@@ -151,12 +151,22 @@ func (opts *layersOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||
}, opts.retryOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
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 fmt.Errorf("%w (close error: %v)", err, closeErr)
|
||||
defer func() {
|
||||
if err := r.Close(); err != nil {
|
||||
retErr = noteCloseFailure(retErr, fmt.Sprintf("closing blob %q", bd.digest.String()), err)
|
||||
}
|
||||
}()
|
||||
verifier := bd.digest.Verifier()
|
||||
tr := io.TeeReader(r, verifier)
|
||||
if _, err := dest.PutBlob(ctx, tr, types.BlobInfo{Digest: bd.digest, Size: blobSize}, cache, bd.isConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(io.Discard, tr); err != nil { // Ensure we process all of tr, so that we can validate the digest.
|
||||
return err
|
||||
}
|
||||
if !verifier.Verified() {
|
||||
return fmt.Errorf("corrupt blob %q", bd.digest.String())
|
||||
}
|
||||
}
|
||||
|
||||
var manifest []byte
|
||||
|
||||
@@ -6,17 +6,17 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"maps"
|
||||
"slices"
|
||||
"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/spf13/cobra"
|
||||
"golang.org/x/exp/maps"
|
||||
"go.podman.io/common/pkg/retry"
|
||||
"go.podman.io/image/v5/docker"
|
||||
"go.podman.io/image/v5/docker/archive"
|
||||
"go.podman.io/image/v5/docker/reference"
|
||||
"go.podman.io/image/v5/transports/alltransports"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
// tagListOutput is the output format of (skopeo list-tags), primarily so that we can format it with a simple json.MarshalIndent.
|
||||
@@ -38,8 +38,7 @@ var transportHandlers = map[string]func(ctx context.Context, sys *types.SystemCo
|
||||
|
||||
// supportedTransports returns all the supported transports
|
||||
func supportedTransports(joinStr string) string {
|
||||
res := maps.Keys(transportHandlers)
|
||||
sort.Strings(res)
|
||||
res := slices.Sorted(maps.Keys(transportHandlers))
|
||||
return strings.Join(res, joinStr)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.podman.io/image/v5/transports/alltransports"
|
||||
)
|
||||
|
||||
// Tests the kinds of inputs allowed and expected to the command
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/containers/common/pkg/auth"
|
||||
commonFlag "github.com/containers/common/pkg/flag"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/common/pkg/auth"
|
||||
commonFlag "go.podman.io/common/pkg/flag"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
type loginOptions struct {
|
||||
@@ -29,8 +29,8 @@ func loginCmd(global *globalOptions) *cobra.Command {
|
||||
}
|
||||
adjustUsage(cmd)
|
||||
flags := cmd.Flags()
|
||||
commonFlag.OptionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry")
|
||||
flags.AddFlagSet(auth.GetLoginFlags(&opts.loginOpts))
|
||||
commonFlag.OptionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@ package main
|
||||
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"
|
||||
"go.podman.io/common/pkg/auth"
|
||||
commonFlag "go.podman.io/common/pkg/flag"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
type logoutOptions struct {
|
||||
@@ -28,8 +28,8 @@ func logoutCmd(global *globalOptions) *cobra.Command {
|
||||
}
|
||||
adjustUsage(cmd)
|
||||
flags := cmd.Flags()
|
||||
commonFlag.OptionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry")
|
||||
flags.AddFlagSet(auth.GetLogoutFlags(&opts.logoutOpts))
|
||||
commonFlag.OptionalBoolFlag(flags, &opts.tlsVerify, "tls-verify", "require HTTPS and verify certificates when accessing the registry")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,19 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"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"
|
||||
"github.com/containers/storage/pkg/reexec"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
commonFlag "go.podman.io/common/pkg/flag"
|
||||
"go.podman.io/image/v5/signature"
|
||||
"go.podman.io/image/v5/types"
|
||||
"go.podman.io/storage/pkg/reexec"
|
||||
)
|
||||
|
||||
// gitCommit will be the hash that the binary was built from
|
||||
// and will be populated by the Makefile
|
||||
var gitCommit = ""
|
||||
|
||||
var defaultUserAgent = "skopeo/" + version.Version
|
||||
|
||||
type globalOptions struct {
|
||||
@@ -33,6 +30,7 @@ type globalOptions struct {
|
||||
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
|
||||
userAgentPrefix string // Prefix to add to the user agent string
|
||||
}
|
||||
|
||||
// requireSubcommand returns an error if no sub command is provided
|
||||
@@ -70,8 +68,10 @@ func createApp() (*cobra.Command, *globalOptions) {
|
||||
// (skopeo --tls-verify inspect) (causes a warning) and (skopeo inspect --tls-verify) (no warning).
|
||||
TraverseChildren: true,
|
||||
}
|
||||
if gitCommit != "" {
|
||||
rootCommand.Version = fmt.Sprintf("%s commit: %s", version.Version, gitCommit)
|
||||
// We don’t use debug.ReadBuildInfo to automate version.Version, because that would not work well for builds from
|
||||
// a released tarball (e.g. RPM builds).
|
||||
if commit := gitCommit(); commit != "" {
|
||||
rootCommand.Version = fmt.Sprintf("%s commit: %s", version.Version, commit)
|
||||
} else {
|
||||
rootCommand.Version = version.Version
|
||||
}
|
||||
@@ -91,6 +91,7 @@ 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")
|
||||
rootCommand.PersistentFlags().StringVar(&opts.userAgentPrefix, "user-agent-prefix", "", "prefix to add to the user agent string")
|
||||
flag := commonFlag.OptionalBoolFlag(rootCommand.Flags(), &opts.tlsVerify, "tls-verify", "Require HTTPS and verify certificates when accessing the registry")
|
||||
flag.Hidden = true
|
||||
rootCommand.AddCommand(
|
||||
@@ -112,6 +113,20 @@ func createApp() (*cobra.Command, *globalOptions) {
|
||||
return rootCommand, &opts
|
||||
}
|
||||
|
||||
// gitCommit returns the git commit for this codebase, if we are built from a git repo; "" otherwise.
|
||||
func gitCommit() string {
|
||||
bi, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
logrus.Fatal("runtime.ReadBuildInfo failed")
|
||||
}
|
||||
for _, e := range bi.Settings {
|
||||
if e.Key == "vcs.revision" {
|
||||
return e.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// before is run by the cli package for any command, before running the command-specific handler.
|
||||
func (opts *globalOptions) before(cmd *cobra.Command, args []string) error {
|
||||
if opts.debug {
|
||||
@@ -168,6 +183,10 @@ func (opts *globalOptions) commandTimeoutContext() (context.Context, context.Can
|
||||
// newSystemContext returns a *types.SystemContext corresponding to opts.
|
||||
// It is guaranteed to return a fresh instance, so it is safe to make additional updates to it.
|
||||
func (opts *globalOptions) newSystemContext() *types.SystemContext {
|
||||
userAgent := defaultUserAgent
|
||||
if opts.userAgentPrefix != "" {
|
||||
userAgent = opts.userAgentPrefix + " " + defaultUserAgent
|
||||
}
|
||||
ctx := &types.SystemContext{
|
||||
RegistriesDirPath: opts.registriesDirPath,
|
||||
ArchitectureChoice: opts.overrideArch,
|
||||
@@ -175,7 +194,7 @@ func (opts *globalOptions) newSystemContext() *types.SystemContext {
|
||||
VariantChoice: opts.overrideVariant,
|
||||
SystemRegistriesConfPath: opts.registriesConfPath,
|
||||
BigFilesTemporaryDir: opts.tmpDir,
|
||||
DockerRegistryUserAgent: defaultUserAgent,
|
||||
DockerRegistryUserAgent: userAgent,
|
||||
}
|
||||
// DEPRECATED: We support this for backward compatibility, but override it if a per-image flag is provided.
|
||||
if opts.tlsVerify.Present() {
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
// runSkopeo creates an app object and runs it with args, with an implied first "skopeo".
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
)
|
||||
|
||||
type manifestDigestOptions struct {
|
||||
|
||||
@@ -1,62 +1,11 @@
|
||||
//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.
|
||||
This command is still experimental. Documentation
|
||||
is available in
|
||||
docs-experimental/skopeo-experimental-image-proxy.1.md
|
||||
*/
|
||||
|
||||
import (
|
||||
@@ -71,29 +20,25 @@ import (
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/containers/image/v5/image"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"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"
|
||||
"github.com/opencontainers/go-digest"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/common/pkg/retry"
|
||||
"go.podman.io/image/v5/image"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
"go.podman.io/image/v5/pkg/blobinfocache"
|
||||
"go.podman.io/image/v5/transports"
|
||||
"go.podman.io/image/v5/transports/alltransports"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
// 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
|
||||
// 0.2.6: Policy Verification before pulling OCI
|
||||
const protocolVersion = "0.2.6"
|
||||
// When bumping this, please also update the man page.
|
||||
const protocolVersion = "0.2.8"
|
||||
|
||||
// maxMsgSize is the current limit on a packet size.
|
||||
// Note that all non-metadata (i.e. payload data) is sent over a pipe.
|
||||
@@ -116,6 +61,23 @@ type request struct {
|
||||
Args []any `json:"args"`
|
||||
}
|
||||
|
||||
type proxyErrorCode string
|
||||
|
||||
const (
|
||||
// proxyErrPipe means we got EPIPE writing to a pipe owned by the client
|
||||
proxyErrPipe proxyErrorCode = "EPIPE"
|
||||
// proxyErrRetryable can be used by clients to automatically retry operations
|
||||
proxyErrRetryable proxyErrorCode = "retryable"
|
||||
// All other errors
|
||||
proxyErrOther proxyErrorCode = "other"
|
||||
)
|
||||
|
||||
// proxyError is serialized over the errfd channel for GetRawBlob
|
||||
type proxyError struct {
|
||||
Code proxyErrorCode `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -124,6 +86,8 @@ type reply struct {
|
||||
Value any `json:"value"`
|
||||
// PipeID is an index into open pipes, and should be passed to FinishPipe
|
||||
PipeID uint32 `json:"pipeid"`
|
||||
// ErrorCode will be non-empty if error is set (new in 0.2.8)
|
||||
ErrorCode proxyErrorCode `json:"error_code"`
|
||||
// Error should be non-empty if Success == false
|
||||
Error string `json:"error"`
|
||||
}
|
||||
@@ -132,8 +96,11 @@ type reply struct {
|
||||
type replyBuf struct {
|
||||
// value will be converted to a reply Value
|
||||
value any
|
||||
// fd is the read half of a pipe, passed back to the client
|
||||
// fd is the read half of a pipe, passed back to the client for additional data
|
||||
fd *os.File
|
||||
// errfd will be a serialization of error state. This is optional and is currently
|
||||
// only used by GetRawBlob.
|
||||
errfd *os.File
|
||||
// pipeid will be provided to the client as PipeID, an index into our open pipes
|
||||
pipeid uint32
|
||||
}
|
||||
@@ -182,6 +149,30 @@ type convertedLayerInfo struct {
|
||||
MediaType string `json:"media_type"`
|
||||
}
|
||||
|
||||
// mapProxyErrorCode turns an error into a known string value.
|
||||
func mapProxyErrorCode(err error) proxyErrorCode {
|
||||
switch {
|
||||
case err == nil:
|
||||
return ""
|
||||
case errors.Is(err, syscall.EPIPE):
|
||||
return proxyErrPipe
|
||||
case retry.IsErrorRetryable(err):
|
||||
return proxyErrRetryable
|
||||
default:
|
||||
return proxyErrOther
|
||||
}
|
||||
}
|
||||
|
||||
// newProxyError creates a serializable structure for
|
||||
// the client containing a mapped error code based
|
||||
// on the error type, plus its value as a string.
|
||||
func newProxyError(err error) proxyError {
|
||||
return proxyError{
|
||||
Code: mapProxyErrorCode(err),
|
||||
Message: fmt.Sprintf("%v", err),
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize performs one-time initialization, and returns the protocol version
|
||||
func (h *proxyHandler) Initialize(args []any) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
@@ -277,7 +268,7 @@ func (h *proxyHandler) openImageImpl(args []any, allowNotFound bool) (retReplyBu
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// OpenImage accepts a string image reference i.e. TRANSPORT:REF - like `skopeo copy`.
|
||||
// OpenImageOptional 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 []any) (replyBuf, error) {
|
||||
@@ -617,11 +608,97 @@ func (h *proxyHandler) GetBlob(args []any) (replyBuf, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetRawBlob can be viewed as a more general purpose successor
|
||||
// to GetBlob. First, it does not verify the digest, which in
|
||||
// some cases is unnecessary as the client would prefer to do it.
|
||||
//
|
||||
// It also does not use the "FinishPipe" API call, but instead
|
||||
// returns *two* file descriptors, one for errors and one for data.
|
||||
//
|
||||
// On (initial) success, the return value provided to the client is the size of the blob.
|
||||
func (h *proxyHandler) GetRawBlob(args []any) (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) != 2 {
|
||||
return ret, fmt.Errorf("found %d args, expecting (imgid, digest)", 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")
|
||||
}
|
||||
|
||||
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(-1)}, h.cache)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Note this doesn't call allocPipe; we're not using the FinishPipe infrastructure.
|
||||
piper, pipew, err := os.Pipe()
|
||||
if err != nil {
|
||||
blobr.Close()
|
||||
return ret, err
|
||||
}
|
||||
errpipeR, errpipeW, err := os.Pipe()
|
||||
if err != nil {
|
||||
piper.Close()
|
||||
pipew.Close()
|
||||
blobr.Close()
|
||||
return ret, err
|
||||
}
|
||||
// Asynchronous worker doing a copy
|
||||
go func() {
|
||||
// We own the read from registry, and write pipe objects
|
||||
defer blobr.Close()
|
||||
defer pipew.Close()
|
||||
defer errpipeW.Close()
|
||||
logrus.Debugf("Copying blob to client: %d bytes", blobSize)
|
||||
_, err := io.Copy(pipew, blobr)
|
||||
// Handle errors here by serializing a JSON error back over
|
||||
// the error channel. In either case, both file descriptors
|
||||
// will be closed, signaling the completion of the operation.
|
||||
if err != nil {
|
||||
logrus.Debugf("Sending error to client: %v", err)
|
||||
serializedErr := newProxyError(err)
|
||||
buf, err := json.Marshal(serializedErr)
|
||||
if err != nil {
|
||||
// Should never happen
|
||||
panic(err)
|
||||
}
|
||||
_, writeErr := errpipeW.Write(buf)
|
||||
if writeErr != nil && !errors.Is(err, syscall.EPIPE) {
|
||||
logrus.Debugf("Writing to client: %v", err)
|
||||
}
|
||||
}
|
||||
logrus.Debugf("Completed GetRawBlob operation")
|
||||
}()
|
||||
|
||||
ret.value = blobSize
|
||||
ret.fd = piper
|
||||
ret.errfd = errpipeR
|
||||
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 doesn’t store the original compressed
|
||||
// representations referenced in the manifest).
|
||||
// This is the same as GetLayerInfoPiped, but returns its contents inline. This is subject to
|
||||
// failure for large images (because we use SOCK_SEQPACKET which has a maximum buffer size)
|
||||
// and is hence only retained for backwards compatibility. Callers are expected to use
|
||||
// the semver to know whether they can call the new API.
|
||||
func (h *proxyHandler) GetLayerInfo(args []any) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
@@ -667,6 +744,59 @@ func (h *proxyHandler) GetLayerInfo(args []any) (replyBuf, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// GetLayerInfoPiped 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 doesn’t store the original compressed
|
||||
// representations referenced in the manifest).
|
||||
func (h *proxyHandler) GetLayerInfoPiped(args []any) (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()
|
||||
}
|
||||
|
||||
layers := make([]convertedLayerInfo, 0, len(layerInfos))
|
||||
for _, layer := range layerInfos {
|
||||
layers = append(layers, convertedLayerInfo{layer.Digest, layer.Size, layer.MediaType})
|
||||
}
|
||||
|
||||
serialized, err := json.Marshal(&layers)
|
||||
if err != nil {
|
||||
return ret, err
|
||||
}
|
||||
return h.returnBytes(nil, serialized)
|
||||
}
|
||||
|
||||
// FinishPipe waits for the worker goroutine to finish, and closes the write side of the pipe.
|
||||
func (h *proxyHandler) FinishPipe(args []any) (replyBuf, error) {
|
||||
h.lock.Lock()
|
||||
@@ -709,30 +839,37 @@ func (h *proxyHandler) close() {
|
||||
|
||||
// send writes a reply buffer to the socket
|
||||
func (buf replyBuf) send(conn *net.UnixConn, err error) error {
|
||||
logrus.Debugf("Sending reply: err=%v value=%v pipeid=%v", err, buf.value, buf.pipeid)
|
||||
logrus.Debugf("Sending reply: err=%v value=%v pipeid=%v datafd=%v errfd=%v", err, buf.value, buf.pipeid, buf.fd, buf.errfd)
|
||||
// We took ownership of these FDs, so close when we're done sending them or on error
|
||||
defer func() {
|
||||
if buf.fd != nil {
|
||||
buf.fd.Close()
|
||||
}
|
||||
if buf.errfd != nil {
|
||||
buf.errfd.Close()
|
||||
}
|
||||
}()
|
||||
replyToSerialize := reply{
|
||||
Success: err == nil,
|
||||
Value: buf.value,
|
||||
PipeID: buf.pipeid,
|
||||
}
|
||||
if err != nil {
|
||||
replyToSerialize.ErrorCode = mapProxyErrorCode(err)
|
||||
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
|
||||
// Copy the FD number(s) to the socket ancillary buffer
|
||||
fds := make([]int, 0)
|
||||
if buf.fd != nil {
|
||||
fds = append(fds, int(buf.fd.Fd()))
|
||||
}
|
||||
if buf.errfd != nil {
|
||||
fds = append(fds, int(buf.errfd.Fd()))
|
||||
}
|
||||
oob := syscall.UnixRights(fds...)
|
||||
n, oobn, err := conn.WriteMsgUnix(serializedReply, oob, nil)
|
||||
if err != nil {
|
||||
@@ -804,8 +941,12 @@ func (h *proxyHandler) processRequest(readBytes []byte) (rb replyBuf, terminate
|
||||
rb, err = h.GetFullConfig(req.Args)
|
||||
case "GetBlob":
|
||||
rb, err = h.GetBlob(req.Args)
|
||||
case "GetRawBlob":
|
||||
rb, err = h.GetRawBlob(req.Args)
|
||||
case "GetLayerInfo":
|
||||
rb, err = h.GetLayerInfo(req.Args)
|
||||
case "GetLayerInfoPiped":
|
||||
rb, err = h.GetLayerInfoPiped(req.Args)
|
||||
case "FinishPipe":
|
||||
rb, err = h.FinishPipe(req.Args)
|
||||
case "Shutdown":
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/v5/pkg/cli"
|
||||
"github.com/containers/image/v5/signature"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/image/v5/pkg/cli"
|
||||
"go.podman.io/image/v5/signature"
|
||||
)
|
||||
|
||||
type standaloneSignOptions struct {
|
||||
|
||||
@@ -6,19 +6,19 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/v5/signature"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.podman.io/image/v5/signature"
|
||||
)
|
||||
|
||||
const (
|
||||
// fixturesTestImageManifestDigest is the Docker manifest digest of "image.manifest.json"
|
||||
fixturesTestImageManifestDigest = digest.Digest("sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55")
|
||||
// fixturesTestKeyFingerprint is the fingerprint of the private key.
|
||||
fixturesTestKeyFingerprint = "1D8230F6CDB6A06716E414C1DB72F2188BB46CC8"
|
||||
fixturesTestKeyFingerprint = "08CD26E446E2E95249B7A405E932F44B23E8DD43"
|
||||
// fixturesTestKeyFingerprint is the key ID of the private key.
|
||||
fixturesTestKeyShortID = "DB72F2188BB46CC8"
|
||||
fixturesTestKeyShortID = "E932F44B23E8DD43"
|
||||
)
|
||||
|
||||
// Test that results of runSkopeo failed with nothing on stdout, and substring
|
||||
@@ -29,7 +29,8 @@ func assertTestFailed(t *testing.T, stdout string, err error, substring string)
|
||||
}
|
||||
|
||||
func TestStandaloneSign(t *testing.T) {
|
||||
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
|
||||
t.Setenv("GNUPGHOME", "fixtures")
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
if err := mech.SupportsSigning(); err != nil {
|
||||
@@ -38,7 +39,6 @@ func TestStandaloneSign(t *testing.T) {
|
||||
|
||||
manifestPath := "fixtures/image.manifest.json"
|
||||
dockerReference := "testing/manifest"
|
||||
t.Setenv("GNUPGHOME", "fixtures")
|
||||
|
||||
// Invalid command-line arguments
|
||||
for _, args := range [][]string{
|
||||
@@ -87,9 +87,6 @@ func TestStandaloneSign(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
manifest, err := os.ReadFile(manifestPath)
|
||||
require.NoError(t, err)
|
||||
mech, err = signature.NewGPGSigningMechanism()
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
verified, err := signature.VerifyDockerManifestSignature(sig, manifest, dockerReference, mech, fixturesTestKeyFingerprint)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, dockerReference, verified.DockerReference)
|
||||
|
||||
@@ -14,46 +14,36 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
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/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/types"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"go.podman.io/common/pkg/retry"
|
||||
"go.podman.io/image/v5/copy"
|
||||
"go.podman.io/image/v5/directory"
|
||||
"go.podman.io/image/v5/docker"
|
||||
"go.podman.io/image/v5/docker/reference"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
"go.podman.io/image/v5/transports"
|
||||
"go.podman.io/image/v5/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// 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.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
|
||||
digestFile string // Write digest to this file
|
||||
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
|
||||
global *globalOptions // Global (not command dependent) skopeo options
|
||||
deprecatedTLSVerify *deprecatedTLSVerifyOption
|
||||
srcImage *imageOptions // Source image options
|
||||
destImage *imageDestOptions // Destination image options
|
||||
retryOpts *retry.Options
|
||||
copy *sharedCopyOptions
|
||||
source string // Source repository name
|
||||
destination string // Destination registry name
|
||||
digestFile string // Write digest to this file
|
||||
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
|
||||
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.
|
||||
@@ -89,6 +79,7 @@ func syncCmd(global *globalOptions) *cobra.Command {
|
||||
srcFlags, srcOpts := dockerImageFlags(global, sharedOpts, deprecatedTLSVerifyOpt, "src-", "screds")
|
||||
destFlags, destOpts := dockerImageFlags(global, sharedOpts, deprecatedTLSVerifyOpt, "dest-", "dcreds")
|
||||
retryFlags, retryOpts := retryFlags()
|
||||
copyFlags, copyOpts := sharedCopyFlags()
|
||||
|
||||
opts := syncOptions{
|
||||
global: global,
|
||||
@@ -96,6 +87,7 @@ func syncCmd(global *globalOptions) *cobra.Command {
|
||||
srcImage: srcOpts,
|
||||
destImage: &imageDestOptions{imageOptions: destOpts},
|
||||
retryOpts: retryOpts,
|
||||
copy: copyOpts,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -113,12 +105,12 @@ See skopeo-sync(1) for details.
|
||||
}
|
||||
adjustUsage(cmd)
|
||||
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.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.AddFlagSet(&sharedFlags)
|
||||
flags.AddFlagSet(&deprecatedTLSVerifyFlags)
|
||||
flags.AddFlagSet(&srcFlags)
|
||||
flags.AddFlagSet(&destFlags)
|
||||
flags.AddFlagSet(&retryFlags)
|
||||
flags.AddFlagSet(©Flags)
|
||||
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")
|
||||
@@ -126,13 +118,7 @@ See skopeo-sync(1) for details.
|
||||
flags.StringVar(&opts.digestFile, "digestfile", "", "Write the digests and Image References of the resulting images to the specified file, separated by newlines")
|
||||
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)
|
||||
flags.AddFlagSet(&destFlags)
|
||||
flags.AddFlagSet(&retryFlags)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -303,8 +289,11 @@ func imagesToCopyFromRegistry(registryName string, cfg registrySyncConfig, sourc
|
||||
// override ctx with per-registryName options
|
||||
serverCtx.DockerCertPath = cfg.CertDir
|
||||
serverCtx.DockerDaemonCertPath = cfg.CertDir
|
||||
serverCtx.DockerDaemonInsecureSkipTLSVerify = (cfg.TLSVerify.skip == types.OptionalBoolTrue)
|
||||
serverCtx.DockerInsecureSkipTLSVerify = cfg.TLSVerify.skip
|
||||
// Only override TLS verification if explicitly specified in YAML; otherwise, keep CLI/global settings.
|
||||
if cfg.TLSVerify.skip != types.OptionalBoolUndefined {
|
||||
serverCtx.DockerDaemonInsecureSkipTLSVerify = (cfg.TLSVerify.skip == types.OptionalBoolTrue)
|
||||
serverCtx.DockerInsecureSkipTLSVerify = cfg.TLSVerify.skip
|
||||
}
|
||||
if cfg.Credentials != (types.DockerAuthConfig{}) {
|
||||
serverCtx.DockerAuthConfig = &cfg.Credentials
|
||||
}
|
||||
@@ -643,14 +632,6 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||
return err
|
||||
}
|
||||
|
||||
var manifestType string
|
||||
if opts.format.Present() {
|
||||
manifestType, err = parseManifestFormat(opts.format.Value())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := opts.global.commandTimeoutContext()
|
||||
defer cancel()
|
||||
|
||||
@@ -669,57 +650,15 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
options, cleanupOptions, err := opts.copy.copyOptions(stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanupOptions()
|
||||
options.DestinationCtx = destinationCtx
|
||||
options.ImageListSelection = imageListSelection
|
||||
options.OptimizeDestinationImageAlreadyExists = true
|
||||
|
||||
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,
|
||||
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 {
|
||||
@@ -775,7 +714,7 @@ func (opts *syncOptions) run(args []string, stdout io.Writer) (retErr error) {
|
||||
} else {
|
||||
logrus.WithFields(fromToFields).Infof("Copying image ref %d/%d", counter+1, len(srcRepo.ImageRefs))
|
||||
if err = retry.IfNecessary(ctx, func() error {
|
||||
manifestBytes, err = copy.Image(ctx, policyContext, destRef, ref, &options)
|
||||
manifestBytes, err = copy.Image(ctx, policyContext, destRef, ref, options)
|
||||
return err
|
||||
}, opts.retryOpts); err != nil {
|
||||
if !opts.keepGoing {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.podman.io/image/v5/types"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -59,3 +60,42 @@ func TestSync(t *testing.T) {
|
||||
// FIXME: Much more test coverage
|
||||
// Actual feature tests exist in integration and systemtest
|
||||
}
|
||||
|
||||
// TestSyncTLSPrecedence validates the interactions of tls-verify in YAML and --src-tls-verify in the CLI.
|
||||
func TestSyncTLSPrecedence(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
cli string
|
||||
yaml string
|
||||
wantSkip types.OptionalBool
|
||||
wantDaemonSkip bool
|
||||
}{
|
||||
{"--src-tls-verify=false", `# nothing`, types.OptionalBoolTrue, true},
|
||||
{"--src-tls-verify=true", `# nothing`, types.OptionalBoolFalse, false},
|
||||
{"", `# nothing`, types.OptionalBoolUndefined, false},
|
||||
{"--src-tls-verify=false", "tls-verify: true", types.OptionalBoolFalse, false},
|
||||
{"--src-tls-verify=true", "tls-verify: false", types.OptionalBoolTrue, true},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("%#v + %q", tt.cli, tt.yaml), func(t *testing.T) {
|
||||
opts := fakeImageOptions(t, "src-", true, []string{}, []string{tt.cli})
|
||||
sourceCtx, err := opts.newSystemContext()
|
||||
require.NoError(t, err)
|
||||
var cfg registrySyncConfig
|
||||
err = yaml.Unmarshal(fmt.Appendf(nil, `
|
||||
%s
|
||||
images:
|
||||
repo: # Specifying an explicit repo+tag avoids imagesToCopyFromRegistry trying to contact the registry.
|
||||
- latest
|
||||
`, tt.yaml,
|
||||
), &cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
descs, err := imagesToCopyFromRegistry("example.com", cfg, *sourceCtx)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, descs)
|
||||
ctx := descs[0].Context
|
||||
require.NotNil(t, ctx)
|
||||
assert.Equal(t, tt.wantSkip, ctx.DockerInsecureSkipTLSVerify)
|
||||
assert.Equal(t, tt.wantDaemonSkip, ctx.DockerDaemonInsecureSkipTLSVerify)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/moby/sys/capability"
|
||||
"go.podman.io/image/v5/transports/alltransports"
|
||||
"go.podman.io/storage/pkg/unshare"
|
||||
)
|
||||
|
||||
var neededCapabilities = []capability.Cap{
|
||||
@@ -16,6 +16,7 @@ var neededCapabilities = []capability.Cap{
|
||||
capability.CAP_FSETID,
|
||||
capability.CAP_MKNOD,
|
||||
capability.CAP_SETFCAP,
|
||||
capability.CAP_SYS_ADMIN,
|
||||
}
|
||||
|
||||
func maybeReexec() error {
|
||||
|
||||
@@ -9,21 +9,27 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
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"
|
||||
ocilayout "github.com/containers/image/v5/oci/layout"
|
||||
"github.com/containers/image/v5/pkg/compression"
|
||||
"github.com/containers/image/v5/storage"
|
||||
"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"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
commonFlag "go.podman.io/common/pkg/flag"
|
||||
"go.podman.io/common/pkg/retry"
|
||||
"go.podman.io/image/v5/copy"
|
||||
"go.podman.io/image/v5/directory"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
ociarchive "go.podman.io/image/v5/oci/archive"
|
||||
ocilayout "go.podman.io/image/v5/oci/layout"
|
||||
"go.podman.io/image/v5/pkg/cli"
|
||||
"go.podman.io/image/v5/pkg/cli/sigstore"
|
||||
"go.podman.io/image/v5/pkg/compression"
|
||||
"go.podman.io/image/v5/signature/signer"
|
||||
"go.podman.io/image/v5/signature/simplesequoia"
|
||||
"go.podman.io/image/v5/storage"
|
||||
"go.podman.io/image/v5/transports/alltransports"
|
||||
"go.podman.io/image/v5/types"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
@@ -109,7 +115,7 @@ type sharedImageOptions struct {
|
||||
func sharedImageFlags() (pflag.FlagSet, *sharedImageOptions) {
|
||||
opts := sharedImageOptions{}
|
||||
fs := pflag.FlagSet{}
|
||||
fs.StringVar(&opts.authFilePath, "authfile", os.Getenv("REGISTRY_AUTH_FILE"), "path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json")
|
||||
fs.StringVar(&opts.authFilePath, "authfile", os.Getenv("REGISTRY_AUTH_FILE"), "path of the registry credentials file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json")
|
||||
return fs, &opts
|
||||
}
|
||||
|
||||
@@ -152,7 +158,7 @@ func dockerImageFlags(global *globalOptions, shared *sharedImageOptions, depreca
|
||||
fs := pflag.FlagSet{}
|
||||
if flagPrefix != "" {
|
||||
// the non-prefixed flag is handled by a shared flag.
|
||||
fs.Var(commonFlag.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 registry credentials file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json")
|
||||
}
|
||||
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")
|
||||
@@ -175,9 +181,9 @@ func imageFlags(global *globalOptions, shared *sharedImageOptions, deprecatedTLS
|
||||
dockerFlags, opts := dockerImageFlags(global, shared, deprecatedTLSVerify, flagPrefix, credsOptionAlias)
|
||||
|
||||
fs := pflag.FlagSet{}
|
||||
fs.AddFlagSet(&dockerFlags)
|
||||
fs.StringVar(&opts.sharedBlobDir, flagPrefix+"shared-blob-dir", "", "`DIRECTORY` to use to share blobs across OCI repositories")
|
||||
fs.StringVar(&opts.dockerDaemonHost, flagPrefix+"daemon-host", "", "use docker daemon host at `HOST` (docker-daemon: only)")
|
||||
fs.AddFlagSet(&dockerFlags)
|
||||
return fs, opts
|
||||
}
|
||||
|
||||
@@ -200,8 +206,8 @@ 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.authFilePath.Present() {
|
||||
ctx.AuthFilePath = opts.authFilePath.Value()
|
||||
}
|
||||
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.
|
||||
@@ -261,6 +267,7 @@ type imageDestOptions struct {
|
||||
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
|
||||
forceCompressionFormat bool // Ensures that the compression algorithm set in compressionFormat is used exclusively
|
||||
imageDestFlagPrefix string
|
||||
}
|
||||
|
||||
@@ -276,6 +283,7 @@ func imageDestFlags(global *globalOptions, shared *sharedImageOptions, deprecate
|
||||
fs.StringVar(&opts.compressionFormat, flagPrefix+"compress-format", "", "`FORMAT` 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.")
|
||||
fs.BoolVar(&opts.forceCompressionFormat, flagPrefix+"force-compress-format", false, "Force exclusive use of the compression algorithm set in --dest-compress-format")
|
||||
return fs, &opts
|
||||
}
|
||||
|
||||
@@ -318,6 +326,143 @@ func (opts *imageDestOptions) warnAboutIneffectiveOptions(destTransport types.Im
|
||||
}
|
||||
}
|
||||
|
||||
// sharedCopyOptions collects CLI flags that affect copying images, currently shared between the copy and sync commands.
|
||||
type sharedCopyOptions struct {
|
||||
removeSignatures bool // Do not copy signatures from the source image
|
||||
signByFingerprint string // Sign the image using a GPG key with the specified fingerprint
|
||||
signBySequoiaFingerprint string // Sign the image using a Sequoia-PGP 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
|
||||
preserveDigests bool // Preserve digests during copy
|
||||
format commonFlag.OptionalString // Force conversion of the image to a specified format
|
||||
}
|
||||
|
||||
// sharedCopyFlags prepares a collection of CLI flags writing into sharedCopyoptions.
|
||||
func sharedCopyFlags() (pflag.FlagSet, *sharedCopyOptions) {
|
||||
opts := sharedCopyOptions{}
|
||||
fs := pflag.FlagSet{}
|
||||
fs.BoolVar(&opts.removeSignatures, "remove-signatures", false, "Do not copy signatures from source")
|
||||
fs.StringVar(&opts.signByFingerprint, "sign-by", "", "Sign the image using a GPG key with the specified `FINGERPRINT`")
|
||||
fs.StringVar(&opts.signBySequoiaFingerprint, "sign-by-sq-fingerprint", "", "Sign the image using a Sequoia-PGP key with the specified `FINGERPRINT`")
|
||||
fs.StringVar(&opts.signBySigstoreParamFile, "sign-by-sigstore", "", "Sign the image using a sigstore parameter file at `PATH`")
|
||||
fs.StringVar(&opts.signBySigstorePrivateKey, "sign-by-sigstore-private-key", "", "Sign the image using a sigstore private key at `PATH`")
|
||||
fs.StringVar(&opts.signPassphraseFile, "sign-passphrase-file", "", "Read a passphrase for signing an image from `PATH`")
|
||||
fs.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)`)
|
||||
fs.BoolVar(&opts.preserveDigests, "preserve-digests", false, "Preserve digests of images and lists")
|
||||
return fs, &opts
|
||||
}
|
||||
|
||||
// copyOptions interprets opts, returns a partially-filled *copy.Options,
|
||||
// and a function that should be called to clean up.
|
||||
func (opts *sharedCopyOptions) copyOptions(stdout io.Writer) (*copy.Options, func(), error) {
|
||||
var manifestType string
|
||||
if opts.format.Present() {
|
||||
mt, err := parseManifestFormat(opts.format.Value())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
manifestType = mt
|
||||
}
|
||||
|
||||
// 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 != "" {
|
||||
count := 0
|
||||
if opts.signByFingerprint != "" {
|
||||
count++
|
||||
}
|
||||
if opts.signBySequoiaFingerprint != "" {
|
||||
count++
|
||||
}
|
||||
if opts.signBySigstorePrivateKey != "" {
|
||||
count++
|
||||
}
|
||||
if count > 1 {
|
||||
return nil, nil, fmt.Errorf("Only one of --sign-by, --sign-by-sq-fingerprint and --sign-by-sigstore-private-key can be used with --sign-passphrase-file")
|
||||
}
|
||||
}
|
||||
// Simple signing does not really allow empty but present passphrases — but for sigstore, cosign does support creating keys encrypted with an empty passphrase;
|
||||
// so, at least for that case, we must track the distinction between an empty and a missing passphrase precisely.
|
||||
var passphrase string
|
||||
passphraseSet := false
|
||||
if opts.signPassphraseFile != "" {
|
||||
p, err := cli.ReadPassphraseFile(opts.signPassphraseFile)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
passphrase = p
|
||||
passphraseSet = true
|
||||
} else if opts.signBySigstorePrivateKey != "" {
|
||||
p, err := promptForPassphrase(opts.signBySigstorePrivateKey, os.Stdin, os.Stdout)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
passphrase = p
|
||||
passphraseSet = true
|
||||
} // opts.signByFingerprint triggers a GPG-agent passphrase prompt, possibly using a more secure channel, so we usually shouldn’t prompt ourselves if no passphrase was explicitly provided.
|
||||
// With opts.signBySequoiaFingerprint, we don’t prompt for a passphrase (for now??): We don’t know whether the key requires a passphrase.
|
||||
var passphraseBytes []byte
|
||||
if passphraseSet {
|
||||
passphraseBytes = []byte(passphrase)
|
||||
}
|
||||
|
||||
var signers []*signer.Signer
|
||||
closeSigners := func() {
|
||||
for _, signer := range signers {
|
||||
signer.Close()
|
||||
}
|
||||
}
|
||||
succeeded := false
|
||||
defer func() {
|
||||
if !succeeded {
|
||||
closeSigners()
|
||||
}
|
||||
}()
|
||||
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 nil, nil, fmt.Errorf("Error using --sign-by-sigstore: %w", err)
|
||||
}
|
||||
signers = append(signers, signer)
|
||||
}
|
||||
if opts.signBySequoiaFingerprint != "" {
|
||||
sqOpts := []simplesequoia.Option{
|
||||
simplesequoia.WithKeyFingerprint(opts.signBySequoiaFingerprint),
|
||||
}
|
||||
if passphraseSet {
|
||||
sqOpts = append(sqOpts, simplesequoia.WithPassphrase(passphrase))
|
||||
}
|
||||
signer, err := simplesequoia.NewSigner(sqOpts...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Error using --sign-by-sq-fingerprint: %w", err)
|
||||
}
|
||||
signers = append(signers, signer)
|
||||
}
|
||||
|
||||
succeeded = true
|
||||
return ©.Options{
|
||||
RemoveSignatures: opts.removeSignatures,
|
||||
Signers: signers,
|
||||
SignBy: opts.signByFingerprint,
|
||||
SignPassphrase: passphrase,
|
||||
SignBySigstorePrivateKeyFile: opts.signBySigstorePrivateKey,
|
||||
SignSigstorePrivateKeyPassphrase: passphraseBytes,
|
||||
|
||||
ReportWriter: stdout,
|
||||
|
||||
PreserveDigests: opts.preserveDigests,
|
||||
ForceManifestMIMEType: manifestType,
|
||||
}, closeSigners, nil
|
||||
}
|
||||
|
||||
func parseCreds(creds string) (string, string, error) {
|
||||
if creds == "" {
|
||||
return "", "", errors.New("credentials can't be empty")
|
||||
@@ -419,9 +564,12 @@ func promptForPassphrase(privateKeyFile string, stdin, stdout *os.File) (string,
|
||||
// authentication error, an I/O error etc.)
|
||||
// TODO drive this into containers/image properly
|
||||
func isNotFoundImageError(err error) bool {
|
||||
var layoutImageNotFoundError ocilayout.ImageNotFoundError
|
||||
var archiveImageNotFoundError ociarchive.ImageNotFoundError
|
||||
return isDockerManifestUnknownError(err) ||
|
||||
errors.Is(err, storage.ErrNoSuchImage) ||
|
||||
errors.Is(err, ocilayout.ImageNotFoundError{})
|
||||
errors.As(err, &layoutImageNotFoundError) ||
|
||||
errors.As(err, &archiveImageNotFoundError)
|
||||
}
|
||||
|
||||
// isDockerManifestUnknownError is a copy of code from containers/image,
|
||||
|
||||
5
cmd/skopeo/utils_nosequoia_test.go
Normal file
5
cmd/skopeo/utils_nosequoia_test.go
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build !containers_image_sequoia
|
||||
|
||||
package main
|
||||
|
||||
const buildWithSequoia = false
|
||||
5
cmd/skopeo/utils_sequoia_test.go
Normal file
5
cmd/skopeo/utils_sequoia_test.go
Normal file
@@ -0,0 +1,5 @@
|
||||
//go:build containers_image_sequoia
|
||||
|
||||
package main
|
||||
|
||||
const buildWithSequoia = true
|
||||
@@ -1,16 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/types"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.podman.io/image/v5/copy"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
func TestNoteCloseFailure(t *testing.T) {
|
||||
@@ -350,6 +353,135 @@ func TestTLSVerifyFlags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// fakeSharedCopyOptions creates sharedCopyOptions and sets it according to cmdFlags.
|
||||
func fakeSharedCopyOptions(t *testing.T, cmdFlags []string) *sharedCopyOptions {
|
||||
_, cmd := fakeGlobalOptions(t, []string{})
|
||||
sharedCopyFlags, sharedCopyOpts := sharedCopyFlags()
|
||||
cmd.Flags().AddFlagSet(&sharedCopyFlags)
|
||||
err := cmd.ParseFlags(cmdFlags)
|
||||
require.NoError(t, err)
|
||||
return sharedCopyOpts
|
||||
}
|
||||
|
||||
func TestSharedCopyOptionsCopyOptions(t *testing.T) {
|
||||
someStdout := bytes.Buffer{}
|
||||
|
||||
passphraseFile, err := os.CreateTemp("", "passphrase") // Eventually we could refer to a passphrase fixture instead
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(passphraseFile.Name())
|
||||
_, err = passphraseFile.WriteString("test-passphrase")
|
||||
require.NoError(t, err)
|
||||
|
||||
type tc struct {
|
||||
options []string
|
||||
expected copy.Options
|
||||
expectedSigner bool
|
||||
}
|
||||
c := []tc{
|
||||
{ // Default state
|
||||
options: []string{},
|
||||
expected: copy.Options{
|
||||
ReportWriter: &someStdout,
|
||||
},
|
||||
},
|
||||
// Set most flags to non-default values
|
||||
// This should also test --sign-by-sigstore and --sign-by-sigstore-private-key; we would have
|
||||
// to create test keys for that.
|
||||
// This does not test --sign-by-sq-fingerprint, because that needs to be conditional based on buildWithSequoia.
|
||||
{
|
||||
options: []string{"--remove-signatures",
|
||||
"--sign-by", "gpgFingerprint",
|
||||
"--format", "oci",
|
||||
"--preserve-digests",
|
||||
},
|
||||
expected: copy.Options{
|
||||
RemoveSignatures: true,
|
||||
SignBy: "gpgFingerprint",
|
||||
ReportWriter: &someStdout,
|
||||
PreserveDigests: true,
|
||||
ForceManifestMIMEType: imgspecv1.MediaTypeImageManifest,
|
||||
},
|
||||
},
|
||||
{ // --sign-passphrase-file + --sign-by work
|
||||
options: []string{
|
||||
"--sign-by", "gpgFingerprint",
|
||||
"--sign-passphrase-file", passphraseFile.Name(),
|
||||
},
|
||||
expected: copy.Options{
|
||||
SignBy: "gpgFingerprint",
|
||||
SignPassphrase: "test-passphrase",
|
||||
SignSigstorePrivateKeyPassphrase: []byte("test-passphrase"),
|
||||
ReportWriter: &someStdout,
|
||||
},
|
||||
},
|
||||
{ // --sign-passphrase-file + --sign-by-sigstore-private-key work
|
||||
options: []string{
|
||||
"--sign-by-sigstore-private-key", "/some/key/path.private",
|
||||
"--sign-passphrase-file", passphraseFile.Name(),
|
||||
},
|
||||
expected: copy.Options{
|
||||
SignPassphrase: "test-passphrase",
|
||||
SignBySigstorePrivateKeyFile: "/some/key/path.private",
|
||||
SignSigstorePrivateKeyPassphrase: []byte("test-passphrase"),
|
||||
ReportWriter: &someStdout,
|
||||
},
|
||||
},
|
||||
{ // --sign-passphrase-file + --sign-by-sigstore-private-key work with an empty passphrase
|
||||
options: []string{
|
||||
"--sign-by-sigstore-private-key", "/some/key/path.private",
|
||||
"--sign-passphrase-file", "./fixtures/empty.passphrase",
|
||||
},
|
||||
expected: copy.Options{
|
||||
SignPassphrase: "",
|
||||
SignBySigstorePrivateKeyFile: "/some/key/path.private",
|
||||
SignSigstorePrivateKeyPassphrase: []byte(""),
|
||||
ReportWriter: &someStdout,
|
||||
},
|
||||
},
|
||||
}
|
||||
// If Sequoia is supported, --sign-passphrase-file + --sign-by-sq-fingerprint work
|
||||
if buildWithSequoia {
|
||||
c = append(c, tc{
|
||||
options: []string{
|
||||
"--sign-by-sq-fingerprint", "sqFingerprint",
|
||||
"--sign-passphrase-file", passphraseFile.Name(),
|
||||
},
|
||||
expected: copy.Options{
|
||||
SignPassphrase: "test-passphrase",
|
||||
SignSigstorePrivateKeyPassphrase: []byte("test-passphrase"),
|
||||
ReportWriter: &someStdout,
|
||||
},
|
||||
expectedSigner: true,
|
||||
})
|
||||
}
|
||||
for _, c := range c {
|
||||
opts := fakeSharedCopyOptions(t, c.options)
|
||||
res, cleanup, err := opts.copyOptions(&someStdout)
|
||||
require.NoError(t, err)
|
||||
defer cleanup()
|
||||
if c.expectedSigner {
|
||||
assert.NotNil(t, res.Signers)
|
||||
res.Signers = nil // To allow the comparison below
|
||||
}
|
||||
assert.Equal(t, &c.expected, res)
|
||||
}
|
||||
|
||||
for _, opts := range [][]string{
|
||||
{"--format", "invalid"}, // Invalid --format
|
||||
// More --sign-by-sigstore-private-key, --sign-by-sigstore failure cases should be tested here.
|
||||
// --sign-passphrase-file + more than one key option
|
||||
{"--sign-by", "gpgFingerprint", "--sign-by-sq-fingerprint", "sqFingerprint", "--sign-passphrase-file", passphraseFile.Name()},
|
||||
{"--sign-by", "gpgFingerprint", "--sign-by-sigstore-private-key", "sigstorePrivateKey", "--sign-passphrase-file", passphraseFile.Name()},
|
||||
{"--sign-by-sq-fingerprint", "sqFingerprint", "--sign-by-sigstore-private-key", "sigstorePrivateKey", "--sign-passphrase-file", passphraseFile.Name()},
|
||||
{"--sign-by", "gpgFingerprint", "--sign-passphrase-file", "/dev/null/this/does/not/exist"}, // --sign-passphrase-file not found
|
||||
{"--sign-by-sigstore", "/dev/null/this/does/not/exist"}, // --sign-by-sigstore file not found
|
||||
} {
|
||||
opts := fakeSharedCopyOptions(t, opts)
|
||||
_, _, err = opts.copyOptions(&someStdout)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseManifestFormat(t *testing.T) {
|
||||
for _, testCase := range []struct {
|
||||
formatParam string
|
||||
|
||||
@@ -64,15 +64,17 @@ _run_setup() {
|
||||
# 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
|
||||
podman pull --retry 3 --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
|
||||
# Temporarily, allow running on Rawhide VMs and consuming older binaries:
|
||||
# that should be compatible enough. Eventually, we’ll stop using Rawhide again.
|
||||
if ! grep -Fqx "ID=$OS_RELEASE_ID" $mnt/etc/os-release || \
|
||||
{ ! [[ "$VM_IMAGE_NAME" =~ "rawhide" ]] && ! grep -Fqx "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/"
|
||||
@@ -97,7 +99,7 @@ _run_vendor() {
|
||||
|
||||
_run_build() {
|
||||
make bin/skopeo BUILDTAGS="$BUILDTAGS"
|
||||
make install PREFIX=/usr/local
|
||||
make install PREFIX=/usr/local BUILDTAGS="$BUILDTAGS"
|
||||
}
|
||||
|
||||
_run_cross() {
|
||||
@@ -128,7 +130,7 @@ _run_system() {
|
||||
make test-system-local BUILDTAGS="$BUILDTAGS"
|
||||
}
|
||||
|
||||
req_env_vars SKOPEO_PATH BUILDTAGS
|
||||
req_env_vars SKOPEO_PATH
|
||||
|
||||
handler="_run_${1}"
|
||||
if [ "$(type -t $handler)" != "function" ]; then
|
||||
|
||||
162
docs-experimental/skopeo-experimental-image-proxy.1.md
Normal file
162
docs-experimental/skopeo-experimental-image-proxy.1.md
Normal file
@@ -0,0 +1,162 @@
|
||||
% skopeo-experimental-image-proxy(1)
|
||||
|
||||
# NAME
|
||||
skopeo-experimental-image-proxy - API server for fetching container images (EXPERIMENTAL)
|
||||
|
||||
# SYNOPSIS
|
||||
**skopeo experimental-image-proxy** [*options*]
|
||||
|
||||
# DESCRIPTION
|
||||
**EXPERIMENTAL COMMAND**: This command is experimental, and its API is subject to change. It is currently hidden from the main help output and not supported on Windows.
|
||||
|
||||
`skopeo experimental-image-proxy` exposes core container image fetching APIs via custom JSON+fd-passing protocol. This provides a lightweight way to fetch container image content (manifests and blobs). This command is primarily intended for programs that want to operate on a storage type that skopeo doesn't natively handle. For example, the bootc project currently has a custom ostree-based container storage backend.
|
||||
|
||||
The client process that invokes `skopeo experimental-image-proxy` is responsible for creating a socket pair and passing one of the file descriptors to the proxy. By default, the proxy expects this file descriptor to be its standard input (fd 0), but a different fd can be specified using the **--sockfd** option.
|
||||
|
||||
**Protocol Overview**
|
||||
|
||||
The protocol requires a `socketpair(2)` of type `SOCK_SEQPACKET`, over which a single JSON message is sent per packet. Large data payloads, such as image manifests and blobs, are transferred over separate pipes (`pipe(2)`), with the read-ends of these pipes passed to the client via file descriptor (FD) passing.
|
||||
|
||||
* **Request Format**: A JSON object: `{ "method": "MethodName", "args": [arguments] }`
|
||||
* **Reply Format**: A JSON object: `{ "success": boolean, "value": JSONValue, "pipeid": number, "error_code": string, "error": string }`
|
||||
* `success`: `true` if the call succeeded, `false` otherwise.
|
||||
* `value`: The return value of the method, if any.
|
||||
* `pipeid`: An integer identifying a pipe for data transfer. This ID is used with the `FinishPipe` method.
|
||||
* `error_code`: A string indicating the type of error if `success` is `false` (e.g., "EPIPE", "retryable", "other"). (Introduced in protocol version 0.2.8)
|
||||
* `error`: A string describing the error if `success` is `false`.
|
||||
|
||||
The current protocol version is `0.2.8`.
|
||||
|
||||
**Supported Protocol Methods**
|
||||
|
||||
The server supports the following methods:
|
||||
|
||||
* **Initialize**: Initializes the proxy. This method must be called before any other method.
|
||||
* Args: `[]` (empty array)
|
||||
* Returns: `string` (the protocol version, e.g., "0.2.8")
|
||||
* **OpenImage**: Opens an image reference (e.g., `docker://quay.io/example/image:latest`).
|
||||
* Args: `[string imageName]`
|
||||
* Returns: `uint64` (an opaque image ID to be used in subsequent calls)
|
||||
* **OpenImageOptional**: Similar to `OpenImage`, but if the image is not found, it returns `0` (a sentinel image ID) instead of an error.
|
||||
* Args: `[string imageName]`
|
||||
* Returns: `uint64` (opaque image ID, or `0` if the image is not found)
|
||||
* **CloseImage**: Closes a previously opened image, releasing associated resources.
|
||||
* Args: `[uint64 imageID]`
|
||||
* Returns: `null`
|
||||
* **GetManifest**: Retrieves the image manifest. If the image is a manifest list, it is resolved to an image matching the proxy's current OS and architecture. The manifest is converted to OCI format if it isn't already. The `value` field in the reply contains the original digest of the manifest (if the image is a manifest list, this is the digest of the list, not the per-platform instance). The manifest content is streamed over a pipe.
|
||||
* Args: `[uint64 imageID]`
|
||||
* Returns: `string` (manifest digest in `value`), manifest data via pipe.
|
||||
* **GetFullConfig**: Retrieves the full image configuration, conforming to the OCI Image Format Specification. Configuration data is streamed over a pipe.
|
||||
* Args: `[uint64 imageID]`
|
||||
* Returns: `null`, configuration data via pipe.
|
||||
* **GetBlob**: Fetches an image blob (e.g., a layer) by its digest and expected size. The proxy performs digest verification on the blob data. The `value` field in the reply contains the blob size. Blob data is streamed over a pipe.
|
||||
* Args: `[uint64 imageID, string digest, uint64 size]`
|
||||
* Returns: `int64` (blob size in `value`, `-1` if unknown), blob data via pipe.
|
||||
* **GetRawBlob**: Fetches an image blob by its digest. Unlike `GetBlob`, this method does not perform server-side digest verification. It returns two file descriptors to the client: one for the blob data and another for reporting errors that occur during the streaming. This method does not use the `FinishPipe` mechanism. The `value` field in the reply contains the blob size. (Introduced in protocol version 0.2.8)
|
||||
* Args: `[uint64 imageID, string digest]`
|
||||
* Returns: `int64` (blob size in `value`, `-1` if unknown), and *two* file descriptors: one for the blob data, one for errors. The error is a `ProxyError` type, see below.
|
||||
* **GetLayerInfoPiped**: Retrieves information about image layers. This replaces `GetLayerInfo`. Layer information data is streamed over a pipe, which makes it more reliable for images with many layers that would exceed message size limits with `GetLayerInfo`. The returned data is a JSON array of `{digest: string, size: int64, media_type: string}`. (Introduced in protocol version 0.2.7)
|
||||
* Args: `[uint64 imageID]`
|
||||
* Returns: `null`, layer information data via pipe.
|
||||
* **FinishPipe**: Signals that the client has finished reading all data from a pipe associated with a `pipeid` (obtained from methods like `GetManifest` or `GetBlob`). This allows the server to close its end of the pipe and report any pending errors (e.g., digest verification failure for `GetBlob`). This method **must** be called by the client after consuming data from a pipe, except for pipes from `GetRawBlob`.
|
||||
* Args: `[uint32 pipeID]`
|
||||
* Returns: `null`
|
||||
* **Shutdown**: Instructs the proxy server to terminate gracefully.
|
||||
* Args: `[]` (empty array)
|
||||
* Returns: `null`
|
||||
|
||||
The following methods are deprecated:
|
||||
|
||||
* **GetConfig**: (deprecated) Retrieves the container runtime configuration part of the image (the OCI `config` field). **Note**: This method returns only a part of the full image configuration due to a historical oversight. Use `GetFullConfig` for the complete image configuration. Configuration data is streamed over a pipe.
|
||||
* Args: `[uint64 imageID]`
|
||||
* Returns: `null`, configuration data via pipe.
|
||||
* **GetLayerInfo**: (deprecated) Retrieves an array of objects, each describing an image layer (digest, size, mediaType). **Note**: This method returns data inline and may fail for images with many layers due to message size limits. Use `GetLayerInfoPiped` for a more robust solution.
|
||||
* Args: `[uint64 imageID]`
|
||||
* Returns: `array` of `{digest: string, size: int64, media_type: string}`.
|
||||
|
||||
**Data Transfer for Pipes**
|
||||
|
||||
When a method returns a `pipeid`, the server also passes the read-end of a pipe via file descriptor (FD) passing. The client reads the data (e.g., manifest content, blob content) from this FD. After successfully reading all data, the client **must** call `FinishPipe` with the corresponding `pipeid`. This signals to the server that the transfer is complete, allows the server to clean up resources, and enables the client to check for any errors that might have occurred during the data streaming process (e.g., a digest mismatch during `GetBlob`). The `GetRawBlob` method is an exception; it uses a dedicated error pipe instead of the `FinishPipe` mechanism.
|
||||
|
||||
**ProxyError**
|
||||
|
||||
`GetBlobRaw` returns a JSON object of the following form in the error pipe where:
|
||||
```
|
||||
{
|
||||
"code": "EPIPE" | "retryable" | "other",
|
||||
"message": "error message"
|
||||
}
|
||||
```
|
||||
|
||||
- EPIPE: The client closed the pipe before reading all data.
|
||||
- retryable: The operation failed but might succeed if retried.
|
||||
- other: A generic error occurred.
|
||||
|
||||
# OPTIONS
|
||||
**--sockfd**=*fd*
|
||||
Serve on the opened socket passed as file descriptor *fd*. Defaults to 0 (standard input).
|
||||
|
||||
The command also supports common skopeo options for interacting with image registries and local storage. These include:
|
||||
|
||||
**--authfile**=*path*
|
||||
|
||||
Path of the primary registry credentials file. On Linux, the default is ${XDG\_RUNTIME\_DIR}/containers/auth.json.
|
||||
See **containers-auth.json**(5) for more details about the credential search mechanism and defaults on other platforms.
|
||||
|
||||
Use `skopeo login` to manage the credentials.
|
||||
|
||||
The default value of this option is read from the `REGISTRY\_AUTH\_FILE` environment variable.
|
||||
|
||||
**--cert-dir**=*path*
|
||||
|
||||
Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry.
|
||||
|
||||
**--creds** _username[:password]_
|
||||
|
||||
Username and password for accessing the registry.
|
||||
|
||||
**--daemon-host** _host_
|
||||
|
||||
Use docker daemon host at _host_ (`docker-daemon:` transport only)
|
||||
|
||||
**--no-creds**
|
||||
|
||||
Access the registry anonymously.
|
||||
|
||||
**--password**=*password*
|
||||
|
||||
Password for accessing the registry. Use with **--username**.
|
||||
|
||||
**--registry-token**=*token*
|
||||
|
||||
Provide a Bearer *token* for accessing the registry.
|
||||
|
||||
**--shared-blob-dir** _directory_
|
||||
|
||||
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. Default to registry.conf setting.
|
||||
|
||||
**--username**=*username*
|
||||
Username for accessing the registry. Use with **--password**.
|
||||
|
||||
# REFERENCE CLIENT LIBRARIES
|
||||
|
||||
- Rust: The [containers-image-proxy-rs project](https://github.com/containers/containers-image-proxy-rs) serves
|
||||
as the reference Rust client.
|
||||
|
||||
# PROTOCOL HISTORY
|
||||
|
||||
- 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
|
||||
- 0.2.6: Policy Verification before pulling OCI
|
||||
- 0.2.7: Added GetLayerInfoPiped
|
||||
- 0.2.8: Added GetRawBlob and error_code to replies
|
||||
|
||||
## SEE ALSO
|
||||
skopeo(1), containers-auth.json(5)
|
||||
@@ -34,19 +34,20 @@ the images in the list, and the list itself.
|
||||
|
||||
**--authfile** _path_
|
||||
|
||||
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
|
||||
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
|
||||
Path of the primary registry credentials file. On Linux, the default is ${XDG\_RUNTIME\_DIR}/containers/auth.json.
|
||||
See **containers-auth.json**(5) for more details about the credential search mechanism and defaults on other platforms.
|
||||
|
||||
Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE
|
||||
environment variable. `export REGISTRY_AUTH_FILE=path`
|
||||
Use `skopeo login` to manage the credentials.
|
||||
|
||||
The default value of this option is read from the `REGISTRY\_AUTH\_FILE` environment variable.
|
||||
|
||||
**--src-authfile** _path_
|
||||
|
||||
Path of the authentication file for the source registry. Uses path given by `--authfile`, if not provided.
|
||||
Path of the primary registry credentials file for the source registry. Uses path given by `--authfile`, if not provided.
|
||||
|
||||
**--dest-authfile** _path_
|
||||
|
||||
Path of the authentication file for the destination registry. Uses path given by `--authfile`, if not provided.
|
||||
Path of the primary registry credentials file for the destination registry. Uses path given by `--authfile`, if not provided.
|
||||
|
||||
**--dest-shared-blob-dir** _directory_
|
||||
|
||||
@@ -106,9 +107,14 @@ See containers-sigstore-signing-params.yaml(5) for details about the file format
|
||||
|
||||
Add a sigstore signature using a private key at _path_ for an image name corresponding to _destination-image_
|
||||
|
||||
**--sign-by-sq-fingerprint** _fingerprint_
|
||||
|
||||
Add a “simple signing” signature using a Sequoia-PGP key with the specified _fingerprint_.
|
||||
|
||||
**--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.
|
||||
The passphrase to use when signing with `--sign-by`, `--sign-by-sigstore-private-key` or `--sign-by-sq-fingerprint`.
|
||||
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_
|
||||
|
||||
@@ -190,6 +196,10 @@ and will be treated as `zstd` with a warning in that case.
|
||||
|
||||
Specifies the compression level to use. The value is specific to the compression algorithm used, e.g. for zstd the accepted values are in the range 1-20 (inclusive), while for gzip it is 1-9 (inclusive).
|
||||
|
||||
**--dest-force-compress-format**
|
||||
|
||||
Ensures that the compression algorithm set in --dest-compress-format is used exclusively.
|
||||
|
||||
**--src-registry-token** _token_
|
||||
|
||||
Bearer token for accessing the source registry.
|
||||
|
||||
@@ -35,8 +35,12 @@ See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--authfile** _path_
|
||||
|
||||
Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
|
||||
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
|
||||
Path of the primary registry credentials file. On Linux, the default is ${XDG\_RUNTIME\_DIR}/containers/auth.json.
|
||||
See **containers-auth.json**(5) for more details about the credential search mechanism and defaults on other platforms.
|
||||
|
||||
Use `skopeo login` to manage the credentials.
|
||||
|
||||
The default value of this option is read from the `REGISTRY\_AUTH\_FILE` environment variable.
|
||||
|
||||
**--creds** _username[:password]_
|
||||
|
||||
|
||||
@@ -21,8 +21,12 @@ See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--authfile** _path_
|
||||
|
||||
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
|
||||
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
|
||||
Path of the primary registry credentials file. On Linux, the default is ${XDG\_RUNTIME\_DIR}/containers/auth.json.
|
||||
See **containers-auth.json**(5) for more details about the credential search mechanism and defaults on other platforms.
|
||||
|
||||
Use `skopeo login` to manage the credentials.
|
||||
|
||||
The default value of this option is read from the `REGISTRY\_AUTH\_FILE` environment variable.
|
||||
|
||||
**--cert-dir** _path_
|
||||
|
||||
|
||||
@@ -16,8 +16,12 @@ See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--authfile** _path_
|
||||
|
||||
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
|
||||
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
|
||||
Path of the updated registry credentials file. On Linux, the default is ${XDG\_RUNTIME\_DIR}/containers/auth.json.
|
||||
See **containers-auth.json**(5) for more details about the credential search mechanism and defaults on other platforms.
|
||||
|
||||
Use `skopeo login` to manage the credentials.
|
||||
|
||||
The default value of this option is read from the `REGISTRY\_AUTH\_FILE` environment variable.
|
||||
|
||||
**--creds** _username[:password]_ for accessing the registry.
|
||||
|
||||
@@ -64,7 +68,7 @@ Repository names are transport-specific references as each transport may have it
|
||||
This commands refers to repositories using a _transport_`:`_details_ format. The following formats are supported:
|
||||
|
||||
**docker://**_docker-repository-reference_
|
||||
A repository in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in either `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(skopeo login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`.
|
||||
A repository in a registry implementing the "Docker Registry HTTP API V2".
|
||||
A _docker-repository-reference_ is of the form: **registryhost:port/repositoryname** which is similar to an _image-reference_ but with no tag or digest allowed as the last component (e.g no `:latest` or `@sha256:xyz`)
|
||||
|
||||
Examples of valid docker-repository-references:
|
||||
|
||||
@@ -10,8 +10,8 @@ skopeo\-login - Login to a container registry.
|
||||
**skopeo login** logs into a specified registry server with the correct username
|
||||
and password. **skopeo login** reads in the username and password from STDIN.
|
||||
The username and password can also be set using the **username** and **password** flags.
|
||||
The path of the authentication file can be specified by the user by setting the **authfile**
|
||||
flag. The default path used is **${XDG\_RUNTIME\_DIR}/containers/auth.json**.
|
||||
The path of the credentials file can be specified by the user by setting the **authfile**
|
||||
flag.
|
||||
|
||||
## OPTIONS
|
||||
|
||||
@@ -31,10 +31,10 @@ Username for registry
|
||||
|
||||
**--authfile**=*path*
|
||||
|
||||
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json
|
||||
Path of the managed registry credentials file. On Linux, the default is ${XDG\_RUNTIME\_DIR}/containers/auth.json.
|
||||
See **containers-auth.json**(5) for more details about the default on other platforms.
|
||||
|
||||
Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE
|
||||
environment variable. `export REGISTRY_AUTH_FILE=path`
|
||||
The default value of this option is read from the `REGISTRY\_AUTH\_FILE` environment variable.
|
||||
|
||||
**--compat-auth-file**=*path*
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ skopeo\-logout - Logout of a container registry.
|
||||
|
||||
## DESCRIPTION
|
||||
**skopeo logout** logs out of a specified registry server by deleting the cached credentials
|
||||
stored in the **auth.json** file. The path of the authentication file can be overridden by the user by setting the **authfile** flag.
|
||||
The default path used is **${XDG\_RUNTIME\_DIR}/containers/auth.json**.
|
||||
stored in the **auth.json** file. The path of the credentials file can be overridden by the user by setting the **authfile** flag.
|
||||
All the cached credentials can be removed by setting the **all** flag.
|
||||
|
||||
## OPTIONS
|
||||
@@ -18,10 +17,10 @@ See also [skopeo(1)](skopeo.1.md) for options placed before the subcommand name.
|
||||
|
||||
**--authfile**=*path*
|
||||
|
||||
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json
|
||||
Path of the managed registry credentials file. On Linux, the default is ${XDG\_RUNTIME\_DIR}/containers/auth.json.
|
||||
See **containers-auth.json**(5) for more details about the default on other platforms.
|
||||
|
||||
Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE
|
||||
environment variable. `export REGISTRY_AUTH_FILE=path`
|
||||
The default value of this option is read from the `REGISTRY\_AUTH\_FILE` environment variable.
|
||||
|
||||
**--compat-auth-file**=*path*
|
||||
|
||||
|
||||
@@ -39,16 +39,20 @@ the images in the list, and the list itself.
|
||||
|
||||
**--authfile** _path_
|
||||
|
||||
Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `skopeo login`.
|
||||
If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
|
||||
Path of the primary registry credentials file. On Linux, the default is ${XDG\_RUNTIME\_DIR}/containers/auth.json.
|
||||
See **containers-auth.json**(5) for more details about the credential search mechanism and defaults on other platforms.
|
||||
|
||||
Use `skopeo login` to manage the credentials.
|
||||
|
||||
The default value of this option is read from the `REGISTRY\_AUTH\_FILE` environment variable.
|
||||
|
||||
**--src-authfile** _path_
|
||||
|
||||
Path of the authentication file for the source registry. Uses path given by `--authfile`, if not provided.
|
||||
Path of the primary registry credentials file for the source registry. Uses path given by `--authfile`, if not provided.
|
||||
|
||||
**--dest-authfile** _path_
|
||||
|
||||
Path of the authentication file for the destination registry. Uses path given by `--authfile`, if not provided.
|
||||
Path of the primary registry credentials file for the destination registry. Uses path given by `--authfile`, if not provided.
|
||||
|
||||
**--dry-run**
|
||||
|
||||
@@ -99,9 +103,14 @@ See containers-sigstore-signing-params.yaml(5) for details about the file format
|
||||
|
||||
Add a sigstore signature using a private key at _path_ for an image name corresponding to _destination-image_
|
||||
|
||||
**--sign-by-sq-fingerprint** _fingerprint_
|
||||
|
||||
Add a “simple signing” signature using a Sequoia-PGP key with the specified _fingerprint_.
|
||||
|
||||
**--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.
|
||||
The passphrase to use when signing with `--sign-by`, `--sign-by-sigstore-private-key` or `--sign-by-sq-fingerprint`.
|
||||
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.
|
||||
|
||||
|
||||
@@ -33,7 +33,9 @@ Most commands refer to container images, using a _transport_`:`_details_ format.
|
||||
An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
|
||||
|
||||
**docker://**_docker-reference_
|
||||
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in either `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(skopeo login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`.
|
||||
An image in a registry implementing the "Docker Registry HTTP API V2".
|
||||
Credentials are typically managed using `(skopeo login)`;
|
||||
see **containers-auth.json**(5) for more details about the credential search mechanism.
|
||||
|
||||
**docker-archive:**_path_[**:**_docker-reference_]
|
||||
An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
|
||||
@@ -94,6 +96,10 @@ Use registry configuration files in _dir_ (e.g. for container signature storage)
|
||||
|
||||
Directory used to store temporary files. Defaults to /var/tmp.
|
||||
|
||||
**--user-agent-prefix** _prefix_
|
||||
|
||||
Prefix to add to the user agent string. The resulting user agent will be in the format "_prefix_ skopeo/_version_".
|
||||
|
||||
**--version**, **-v**
|
||||
|
||||
Print the version number
|
||||
|
||||
147
go.mod
147
go.mod
@@ -1,139 +1,110 @@
|
||||
module github.com/containers/skopeo
|
||||
|
||||
// Minimum required golang version
|
||||
go 1.22.8
|
||||
go 1.24.2
|
||||
|
||||
// Warning: Ensure the "go" and "toolchain" versions match exactly to prevent unwanted auto-updates
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.3.1
|
||||
github.com/containers/common v0.62.0
|
||||
github.com/containers/image/v5 v5.34.0
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/containers/ocicrypt v1.2.1
|
||||
github.com/containers/storage v1.57.1
|
||||
github.com/docker/distribution v2.8.3+incompatible
|
||||
github.com/moby/sys/capability v0.4.0
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/opencontainers/image-spec v1.1.0
|
||||
github.com/opencontainers/image-spec v1.1.2-0.20251016170850-26647a49f642
|
||||
github.com/opencontainers/image-tools v1.0.0-rc3
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329
|
||||
golang.org/x/term v0.29.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.podman.io/common v0.66.0
|
||||
go.podman.io/image/v5 v5.38.0
|
||||
go.podman.io/storage v1.61.0
|
||||
golang.org/x/term v0.36.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.12.9 // 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-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.0.3 // indirect
|
||||
github.com/containerd/errdefs v0.3.0 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.17.0 // indirect
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.12.0 // indirect
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/coreos/go-oidc/v3 v3.14.1 // indirect
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker v27.5.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/docker v28.5.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.4 // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.23.0 // indirect
|
||||
github.com/go-openapi/errors v0.22.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/loads v0.22.0 // indirect
|
||||
github.com/go-openapi/runtime v0.28.0 // indirect
|
||||
github.com/go-openapi/spec v0.21.0 // indirect
|
||||
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // 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.4 // indirect
|
||||
github.com/google/go-containerregistry v0.20.2 // indirect
|
||||
github.com/google/go-containerregistry v0.20.6 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // 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.7 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.2-0.20250313123807-1ee6e1a1957a // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.24 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.32 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mistifyio/go-zfs/v3 v3.1.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.2 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // 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/runtime-spec v1.2.0 // indirect
|
||||
github.com/opencontainers/selinux v1.11.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect
|
||||
github.com/opencontainers/image-spec/schema v0.0.0-20250717171153-ab80ff15c2dd // indirect
|
||||
github.com/opencontainers/runtime-spec v1.2.1 // indirect
|
||||
github.com/opencontainers/selinux v1.12.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/proglottis/gpgme v0.1.4 // indirect
|
||||
github.com/proglottis/gpgme v0.1.5 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday v2.0.0+incompatible // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect
|
||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/sigstore/fulcio v1.6.4 // indirect
|
||||
github.com/sigstore/rekor v1.3.8 // indirect
|
||||
github.com/sigstore/sigstore v1.8.12 // indirect
|
||||
github.com/sigstore/fulcio v1.7.1 // indirect
|
||||
github.com/sigstore/protobuf-specs v0.4.1 // indirect
|
||||
github.com/sigstore/sigstore v1.9.5 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/smallstep/pkcs7 v0.1.1 // indirect
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
|
||||
github.com/sylabs/sif/v2 v2.20.2 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.2 // indirect
|
||||
github.com/sylabs/sif/v2 v2.22.0 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/vbatts/tar-split v0.11.7 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.9.1 // 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.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect
|
||||
google.golang.org/grpc v1.69.4 // indirect
|
||||
google.golang.org/protobuf v1.36.2 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
github.com/vbatts/tar-split v0.12.1 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.10.2 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/net v0.45.0 // indirect
|
||||
golang.org/x/oauth2 v0.32.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
|
||||
google.golang.org/grpc v1.72.2 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
)
|
||||
|
||||
495
go.sum
495
go.sum
@@ -1,196 +1,121 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg=
|
||||
github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
||||
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg=
|
||||
github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y=
|
||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
|
||||
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
|
||||
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
|
||||
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/containers/common v0.62.0 h1:Sl9WE5h7Y/F3bejrMAA4teP1EcY9ygqJmW4iwSloZ10=
|
||||
github.com/containers/common v0.62.0/go.mod h1:Yec+z8mrSq4rydHofrnDCBqAcNA/BGrSg1kfFUL6F6s=
|
||||
github.com/containers/image/v5 v5.34.0 h1:HPqQaDUsox/3mC1pbOyLAIQEp0JhQqiUZ+6JiFIZLDI=
|
||||
github.com/containers/image/v5 v5.34.0/go.mod h1:/WnvUSEfdqC/ahMRd4YJDBLrpYWkGl018rB77iB3FDo=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.17.0 h1:+TyQIsR/zSFI1Rm31EQBwpAA1ovYgIKHy7kctL3sLcE=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.17.0/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM=
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
|
||||
github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM=
|
||||
github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ=
|
||||
github.com/containers/storage v1.57.1 h1:hKPoFsuBcB3qTzBxa4IFpZMRzUuL5Xhv/BE44W0XHx8=
|
||||
github.com/containers/storage v1.57.1/go.mod h1:i/Hb4lu7YgFr9G0K6BMjqW0BLJO1sFsnWQwj2UoWCUM=
|
||||
github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo=
|
||||
github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f h1:eHnXnuK47UlSTOQexbzxAZfekVz6i+LKRdj1CU5DPaM=
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/cli v27.5.1+incompatible h1:JB9cieUT9YNiMITtIsguaN55PLOHhBSz3LKVc6cqWaY=
|
||||
github.com/docker/cli v27.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY=
|
||||
github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8=
|
||||
github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
|
||||
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI=
|
||||
github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
|
||||
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
|
||||
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
||||
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
|
||||
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
|
||||
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
|
||||
github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
|
||||
github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
|
||||
github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
|
||||
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
|
||||
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
|
||||
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
|
||||
github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
|
||||
github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo=
|
||||
github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=
|
||||
github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=
|
||||
github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM=
|
||||
github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY=
|
||||
github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-multierror v1.1.2-0.20250313123807-1ee6e1a1957a h1:zTI4FFCOXw14aUC78fxMh4tS7jJI7Fm51sH4smjl+Fc=
|
||||
github.com/hashicorp/go-multierror v1.1.2-0.20250313123807-1ee6e1a1957a/go.mod h1:RYOtqYU2MvOrqUMooJlQoFFuqR6sazGdm1ubZTL++r8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs=
|
||||
github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@@ -200,32 +125,32 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ=
|
||||
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
|
||||
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU=
|
||||
github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mistifyio/go-zfs/v3 v3.1.0 h1:FZaylcg0hjUp27i23VcJJQiuBeAZjrC8lPqCGM1CopY=
|
||||
github.com/mistifyio/go-zfs/v3 v3.1.0/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
|
||||
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
|
||||
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -235,109 +160,91 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/onsi/ginkgo/v2 v2.26.0 h1:1J4Wut1IlYZNEAWIV3ALrT9NfiaGW2cDCJQSFQMs/gE=
|
||||
github.com/onsi/ginkgo/v2 v2.26.0/go.mod h1:qhEywmzWTBUY88kfO0BRvX4py7scov9yR+Az2oavUzw=
|
||||
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
|
||||
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/opencontainers/image-spec v1.1.2-0.20251016170850-26647a49f642 h1:BNZwTO1e0QJV7HVGz/Qw/tyOE/GnooRmuy6qZnhNGCE=
|
||||
github.com/opencontainers/image-spec v1.1.2-0.20251016170850-26647a49f642/go.mod h1:GRy5q9c6/vsqXmQ1I6TL1PkhA64F6eXG9fUOQ9tFvm8=
|
||||
github.com/opencontainers/image-spec/schema v0.0.0-20250717171153-ab80ff15c2dd h1:demTtfPH+DsqagnumQZv8nQrFoUqCJDNVrw+6LsGpm4=
|
||||
github.com/opencontainers/image-spec/schema v0.0.0-20250717171153-ab80ff15c2dd/go.mod h1:vPOv9cXqxB6ycHY5iVwqL4rkYbwRh46GZj13CfkZ6As=
|
||||
github.com/opencontainers/image-tools v1.0.0-rc3 h1:ZR837lBIxq6mmwEqfYrbLMuf75eBSHhccVHy6lsBeM4=
|
||||
github.com/opencontainers/image-tools v1.0.0-rc3/go.mod h1:A9btVpZLzttF4iFaKNychhPyrhfOjJ1OF5KrA8GcLj4=
|
||||
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
|
||||
github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8=
|
||||
github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f h1:/UDgs8FGMqwnHagNDPGOlts35QkhAZ8by3DR7nMih7M=
|
||||
github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
|
||||
github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww=
|
||||
github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8=
|
||||
github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/proglottis/gpgme v0.1.4 h1:3nE7YNA70o2aLjcg63tXMOhPD7bplfE5CBdV+hLAm2M=
|
||||
github.com/proglottis/gpgme v0.1.4/go.mod h1:5LoXMgpE4bttgwwdv9bLs/vwqv3qV7F4glEEZ7mRKrM=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.57.0 h1:Ro/rKjwdq9mZn1K5QPctzh+MA4Lp0BuYk5ZZEVhoNcY=
|
||||
github.com/prometheus/common v0.57.0/go.mod h1:7uRPFSUTbfZWsJ7MHY56sqt7hLQu3bxXHDnNhl8E9qI=
|
||||
github.com/proglottis/gpgme v0.1.5 h1:KCGyOw8sQ+SI96j6G8D8YkOGn+1TwbQTT9/zQXoVlz0=
|
||||
github.com/proglottis/gpgme v0.1.5/go.mod h1:5LoXMgpE4bttgwwdv9bLs/vwqv3qV7F4glEEZ7mRKrM=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
|
||||
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY=
|
||||
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/sebdah/goldie/v2 v2.7.1 h1:PkBHymaYdtvEkZV7TmyqKxdmn5/Vcj+8TpATWZjnG5E=
|
||||
github.com/sebdah/goldie/v2 v2.7.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU=
|
||||
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
|
||||
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sigstore/fulcio v1.6.4 h1:d86obfxUAG3Y6CYwOx1pdwCZwKmROB6w6927pKOVIRY=
|
||||
github.com/sigstore/fulcio v1.6.4/go.mod h1:Y6bn3i3KGhXpaHsAtYP3Z4Np0+VzCo1fLv8Ci6mbPDs=
|
||||
github.com/sigstore/rekor v1.3.8 h1:B8kJI8mpSIXova4Jxa6vXdJyysRxFGsEsLKBDl0rRjA=
|
||||
github.com/sigstore/rekor v1.3.8/go.mod h1:/dHFYKSuxEygfDRnEwyJ+ZD6qoVYNXQdi1mJrKvKWsI=
|
||||
github.com/sigstore/sigstore v1.8.12 h1:S8xMVZbE2z9ZBuQUEG737pxdLjnbOIcFi5v9UFfkJFc=
|
||||
github.com/sigstore/sigstore v1.8.12/go.mod h1:+PYQAa8rfw0QdPpBcT+Gl3egKD9c+TUgAlF12H3Nmjo=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sigstore/fulcio v1.7.1 h1:RcoW20Nz49IGeZyu3y9QYhyyV3ZKQ85T+FXPKkvE+aQ=
|
||||
github.com/sigstore/fulcio v1.7.1/go.mod h1:7lYY+hsd8Dt+IvKQRC+KEhWpCZ/GlmNvwIa5JhypMS8=
|
||||
github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc=
|
||||
github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc=
|
||||
github.com/sigstore/sigstore v1.9.5 h1:Wm1LT9yF4LhQdEMy5A2JeGRHTrAWGjT3ubE5JUSrGVU=
|
||||
github.com/sigstore/sigstore v1.9.5/go.mod h1:VtxgvGqCmEZN9X2zhFSOkfXxvKUjpy8RpUW39oCtoII=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU=
|
||||
github.com/smallstep/pkcs7 v0.1.1/go.mod h1:dL6j5AIz9GHjVEBTXtW+QliALcgM19RtXaTeyxI+AfA=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/sylabs/sif/v2 v2.20.2 h1:HGEPzauCHhIosw5o6xmT3jczuKEuaFzSfdjAsH33vYw=
|
||||
github.com/sylabs/sif/v2 v2.20.2/go.mod h1:WyYryGRaR4Wp21SAymm5pK0p45qzZCSRiZMFvUZiuhc=
|
||||
github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM=
|
||||
github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/sylabs/sif/v2 v2.22.0 h1:Y+xXufp4RdgZe02SR3nWEg7S6q4tPWN237WHYzkDSKA=
|
||||
github.com/sylabs/sif/v2 v2.22.0/go.mod h1:W1XhWTmG1KcG7j5a3KSYdMcUIFvbs240w/MMVW627hs=
|
||||
github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
|
||||
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/vbatts/tar-split v0.11.7 h1:ixZ93pO/GmvaZw4Vq9OwmfZK/kc2zKdPfu0B+gYqs3U=
|
||||
github.com/vbatts/tar-split v0.11.7/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/vbauerster/mpb/v8 v8.9.1 h1:LH5R3lXPfE2e3lIGxN7WNWv3Hl5nWO6LRi2B0L0ERHw=
|
||||
github.com/vbauerster/mpb/v8 v8.9.1/go.mod h1:4XMvznPh8nfe2NpnDo1QTPvW9MVkUhbG90mPWvmOzcQ=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo=
|
||||
github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/vbauerster/mpb/v8 v8.10.2 h1:2uBykSHAYHekE11YvJhKxYmLATKHAGorZwFlyNw4hHM=
|
||||
github.com/vbauerster/mpb/v8 v8.10.2/go.mod h1:+Ja4P92E3/CorSZgfDtK46D7AVbDqmBQRTmyTqPElo0=
|
||||
github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
|
||||
github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=
|
||||
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
|
||||
@@ -348,67 +255,53 @@ github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE=
|
||||
github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
|
||||
github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=
|
||||
github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk=
|
||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||
go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=
|
||||
go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.podman.io/common v0.66.0 h1:KElE3HKLFdMdJL+jv5ExBiX2Dh4Qcv8ovmzaBGRsyZM=
|
||||
go.podman.io/common v0.66.0/go.mod h1:aNd2a0S7pY+fx1X5kpQYuF4hbwLU8ZOccuVrhu7h1Xc=
|
||||
go.podman.io/image/v5 v5.38.0 h1:aUKrCANkPvze1bnhLJsaubcfz0d9v/bSDLnwsXJm6G4=
|
||||
go.podman.io/image/v5 v5.38.0/go.mod h1:hSIoIUzgBnmc4DjoIdzk63aloqVbD7QXDMkSE/cvG90=
|
||||
go.podman.io/storage v1.61.0 h1:5hD/oyRYt1f1gxgvect+8syZBQhGhV28dCw2+CZpx0Q=
|
||||
go.podman.io/storage v1.61.0/go.mod h1:A3UBK0XypjNZ6pghRhuxg62+2NIm5lcUGv/7XyMhMUI=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
|
||||
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
@@ -416,27 +309,20 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -448,8 +334,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -459,8 +345,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
@@ -469,65 +355,34 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
||||
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
|
||||
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e h1:UdXH7Kzbj+Vzastr5nVfccbmFsmYNygVLSPk1pEfDoY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.72.2 h1:TdbGzwb82ty4OusHWepvFWGLgIbNo1/SUynEN0ssqv8=
|
||||
google.golang.org/grpc v1.72.2/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
${CPP:-${CC:-cc} -E} ${CPPFLAGS} - > /dev/null 2> /dev/null << EOF
|
||||
#include <btrfs/version.h>
|
||||
EOF
|
||||
if test $? -ne 0 ; then
|
||||
echo btrfs_noversion
|
||||
fi
|
||||
7
hack/sqlite_tag.sh
Executable file
7
hack/sqlite_tag.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
${CPP:-${CC:-cc} -E} ${CPPFLAGS} - &> /dev/null << EOF
|
||||
#include <sqlite3.h>
|
||||
EOF
|
||||
if test $? -eq 0 ; then
|
||||
echo libsqlite3
|
||||
fi
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
make PREFIX=/usr install
|
||||
|
||||
echo "cd ./integration;" go test $TESTFLAGS ${BUILDTAGS:+-tags "$BUILDTAGS"}
|
||||
cd ./integration
|
||||
go test $TESTFLAGS ${BUILDTAGS:+-tags "$BUILDTAGS"}
|
||||
@@ -37,8 +37,4 @@ EOF
|
||||
export CONTAINERS_STORAGE_CONF=/etc/containers/storage.conf
|
||||
fi
|
||||
|
||||
# Build skopeo, install into /usr/bin
|
||||
make PREFIX=/usr install
|
||||
|
||||
# Run tests
|
||||
SKOPEO_BINARY=/usr/bin/skopeo bats --tap systemtest
|
||||
bats --tap systemtest
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
errors=$($GOBIN/golangci-lint run --build-tags "${BUILDTAGS}" 2>&1)
|
||||
|
||||
if [ -z "$errors" ]; then
|
||||
echo 'Congratulations! All Go source files have been linted.'
|
||||
else
|
||||
{
|
||||
echo "Errors from golangci-lint:"
|
||||
echo "$errors"
|
||||
echo
|
||||
echo 'Please fix the above errors. You can test via "golangci-lint" and commit the result.'
|
||||
echo
|
||||
} >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -139,7 +139,7 @@ located at [https://github.com/containers/image_build/tree/main/skopeo](https://
|
||||
|
||||
Otherwise, read on for building and installing it from source:
|
||||
|
||||
To build the `skopeo` binary you need at least Go 1.22.
|
||||
To build the `skopeo` binary you need at least Go 1.23.
|
||||
|
||||
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.
|
||||
|
||||
@@ -50,7 +50,7 @@ func (s *skopeoSuite) TearDownSuite() {
|
||||
|
||||
func (s *skopeoSuite) TestVersion() {
|
||||
t := s.T()
|
||||
assertSkopeoSucceeds(t, fmt.Sprintf(".*%s version %s.*", skopeoBinary, version.Version),
|
||||
assertSkopeoSucceeds(t, fmt.Sprintf(".*skopeo version %s.*", version.Version),
|
||||
"--version")
|
||||
}
|
||||
|
||||
@@ -107,7 +107,8 @@ func (s *skopeoSuite) TestCopyWithLocalAuth() {
|
||||
"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(t, "", "copy", "--dest-tls-verify=false", testFQIN+":latest", imageName)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--dest-tls-verify=false", "--retry-times", "3",
|
||||
testFQIN+":latest", imageName)
|
||||
// inspect from private registry
|
||||
assertSkopeoSucceeds(t, "", "inspect", "--tls-verify=false", imageName)
|
||||
// logout from the registry
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -15,15 +16,16 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/signature"
|
||||
"github.com/containers/image/v5/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/opencontainers/image-tools/image"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
"go.podman.io/image/v5/signature"
|
||||
"go.podman.io/image/v5/signature/simplesequoia"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -101,16 +103,28 @@ func (s *copySuite) TearDownSuite() {
|
||||
}
|
||||
}
|
||||
|
||||
// policyFixture applies the general edits, as well as extraSubstitutions, to the policy.json fixture,
|
||||
// and returns a path to a policy, which will be automatically removed when the test completes.
|
||||
func (s *copySuite) policyFixture(extraSubstitutions map[string]string) string {
|
||||
t := s.T()
|
||||
fixtureDir, err := filepath.Abs("fixtures")
|
||||
require.NoError(t, err)
|
||||
edits := map[string]string{"@keydir@": s.gpgHome, "@fixturedir@": fixtureDir}
|
||||
maps.Copy(edits, extraSubstitutions)
|
||||
policyPath := fileFromFixture(t, "fixtures/policy.json", edits)
|
||||
return policyPath
|
||||
}
|
||||
|
||||
func (s *copySuite) TestCopyWithManifestList() {
|
||||
t := s.T()
|
||||
dir := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImage, "dir:"+dir)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImage, "dir:"+dir)
|
||||
}
|
||||
|
||||
func (s *copySuite) TestCopyAllWithManifestList() {
|
||||
t := s.T()
|
||||
dir := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "copy", "--all", knownListImage, "dir:"+dir)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--all", knownListImage, "dir:"+dir)
|
||||
}
|
||||
|
||||
func (s *copySuite) TestCopyAllWithManifestListRoundTrip() {
|
||||
@@ -119,7 +133,7 @@ func (s *copySuite) TestCopyAllWithManifestListRoundTrip() {
|
||||
oci2 := t.TempDir()
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", knownListImage, "oci:"+oci1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--multi-arch=all", knownListImage, "oci:"+oci1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", "dir:"+dir1, "oci:"+oci2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", "oci:"+oci2, "dir:"+dir2)
|
||||
@@ -134,9 +148,9 @@ func (s *copySuite) TestCopyAllWithManifestListConverge() {
|
||||
oci2 := t.TempDir()
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", knownListImage, "oci:"+oci1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--multi-arch=all", knownListImage, "oci:"+oci1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", "--format", "oci", knownListImage, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--multi-arch=all", "--format", "oci", knownListImage, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", "dir:"+dir2, "oci:"+oci2)
|
||||
assertDirImagesAreEqual(t, dir1, dir2)
|
||||
out := combinedOutputOfCommand(t, "diff", "-urN", oci1, oci2)
|
||||
@@ -146,7 +160,7 @@ func (s *copySuite) TestCopyAllWithManifestListConverge() {
|
||||
func (s *copySuite) TestCopyNoneWithManifestList() {
|
||||
t := s.T()
|
||||
dir1 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=index-only", knownListImage, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--multi-arch=index-only", knownListImage, "dir:"+dir1)
|
||||
|
||||
manifestPath := filepath.Join(dir1, "manifest.json")
|
||||
readManifest, err := os.ReadFile(manifestPath)
|
||||
@@ -163,9 +177,9 @@ func (s *copySuite) TestCopyWithManifestListConverge() {
|
||||
oci2 := t.TempDir()
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImage, "oci:"+oci1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImage, "oci:"+oci1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", "oci:"+oci1, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--format", "oci", knownListImage, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--format", "oci", knownListImage, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", "dir:"+dir2, "oci:"+oci2)
|
||||
assertDirImagesAreEqual(t, dir1, dir2)
|
||||
out := combinedOutputOfCommand(t, "diff", "-urN", oci1, oci2)
|
||||
@@ -176,7 +190,8 @@ func (s *copySuite) TestCopyAllWithManifestListStorageFails() {
|
||||
t := s.T()
|
||||
storage := t.TempDir()
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
assertSkopeoFails(t, `.*destination transport .* does not support copying multiple images as a group.*`, "copy", "--multi-arch=all", knownListImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoFails(t, `.*destination transport .* does not support copying multiple images as a group.*`,
|
||||
"copy", "--retry-times", "3", "--multi-arch=all", knownListImage, "containers-storage:"+storage+"test")
|
||||
}
|
||||
|
||||
func (s *copySuite) TestCopyWithManifestListStorage() {
|
||||
@@ -185,8 +200,8 @@ func (s *copySuite) TestCopyWithManifestListStorage() {
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImage, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImage, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "containers-storage:"+storage+"test", "dir:"+dir2)
|
||||
decompressDirs(t, dir1, dir2)
|
||||
assertDirImagesAreEqual(t, dir1, dir2)
|
||||
@@ -198,9 +213,9 @@ func (s *copySuite) TestCopyWithManifestListStorageMultiple() {
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "--override-arch", "amd64", "copy", knownListImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoSucceeds(t, "", "--override-arch", "arm64", "copy", knownListImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoSucceeds(t, "", "--override-arch", "arm64", "copy", knownListImage, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch", "amd64", "copy", "--retry-times", "3", knownListImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoSucceeds(t, "", "--override-arch", "arm64", "copy", "--retry-times", "3", knownListImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoSucceeds(t, "", "--override-arch", "arm64", "copy", "--retry-times", "3", knownListImage, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "containers-storage:"+storage+"test", "dir:"+dir2)
|
||||
decompressDirs(t, dir1, dir2)
|
||||
assertDirImagesAreEqual(t, dir1, dir2)
|
||||
@@ -212,12 +227,12 @@ func (s *copySuite) TestCopyWithManifestListDigest() {
|
||||
dir2 := t.TempDir()
|
||||
oci1 := t.TempDir()
|
||||
oci2 := t.TempDir()
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--raw", knownListImage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--retry-times", "3", "--raw", knownListImage)
|
||||
manifestDigest, err := manifest.Digest([]byte(m))
|
||||
require.NoError(t, err)
|
||||
digest := manifestDigest.String()
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImageRepo+"@"+digest, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--multi-arch=all", knownListImageRepo+"@"+digest, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--multi-arch=all", knownListImageRepo+"@"+digest, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "dir:"+dir1, "oci:"+oci1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "dir:"+dir2, "oci:"+oci2)
|
||||
out := combinedOutputOfCommand(t, "diff", "-urN", oci1, oci2)
|
||||
@@ -229,7 +244,7 @@ func (s *copySuite) TestCopyWithDigestfileOutput() {
|
||||
tempdir := t.TempDir()
|
||||
dir1 := t.TempDir()
|
||||
digestOutPath := filepath.Join(tempdir, "digest.txt")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--digestfile="+digestOutPath, knownListImage, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--digestfile="+digestOutPath, knownListImage, "dir:"+dir1)
|
||||
readDigest, err := os.ReadFile(digestOutPath)
|
||||
require.NoError(t, err)
|
||||
_, err = digest.Parse(string(readDigest))
|
||||
@@ -242,13 +257,13 @@ func (s *copySuite) TestCopyWithManifestListStorageDigest() {
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--raw", knownListImage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--retry-times", "3", "--raw", knownListImage)
|
||||
manifestDigest, err := manifest.Digest([]byte(m))
|
||||
require.NoError(t, err)
|
||||
digest := manifestDigest.String()
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "copy", "containers-storage:"+storage+"test@"+digest, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImageRepo+"@"+digest, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "dir:"+dir2)
|
||||
decompressDirs(t, dir1, dir2)
|
||||
assertDirImagesAreEqual(t, dir1, dir2)
|
||||
}
|
||||
@@ -259,13 +274,13 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArches() {
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
dir1 := t.TempDir()
|
||||
dir2 := t.TempDir()
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--raw", knownListImage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--retry-times", "3", "--raw", knownListImage)
|
||||
manifestDigest, err := manifest.Digest([]byte(m))
|
||||
require.NoError(t, err)
|
||||
digest := manifestDigest.String()
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "copy", "containers-storage:"+storage+"test@"+digest, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImageRepo+"@"+digest, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "dir:"+dir2)
|
||||
decompressDirs(t, dir1, dir2)
|
||||
assertDirImagesAreEqual(t, dir1, dir2)
|
||||
}
|
||||
@@ -274,14 +289,14 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesBothUseLi
|
||||
t := s.T()
|
||||
storage := t.TempDir()
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--raw", knownListImage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--retry-times", "3", "--raw", knownListImage)
|
||||
manifestDigest, err := manifest.Digest([]byte(m))
|
||||
require.NoError(t, err)
|
||||
digest := manifestDigest.String()
|
||||
_, err = manifest.ListFromBlob([]byte(m), manifest.GuessMIMEType([]byte(m)))
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoFails(t, `.*reading manifest for image instance.*does not exist.*`, "--override-arch=amd64", "inspect", "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoFails(t, `.*reading manifest for image instance.*does not exist.*`, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
|
||||
i2 := combinedOutputOfCommand(t, skopeoBinary, "--override-arch=arm64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
|
||||
@@ -295,7 +310,7 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesFirstUses
|
||||
t := s.T()
|
||||
storage := t.TempDir()
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--raw", knownListImage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--retry-times", "3", "--raw", knownListImage)
|
||||
manifestDigest, err := manifest.Digest([]byte(m))
|
||||
require.NoError(t, err)
|
||||
digest := manifestDigest.String()
|
||||
@@ -305,8 +320,8 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesFirstUses
|
||||
require.NoError(t, err)
|
||||
arm64Instance, err := list.ChooseInstance(&types.SystemContext{ArchitectureChoice: "arm64"})
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+arm64Instance.String(), "containers-storage:"+storage+"test@"+arm64Instance.String())
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", "--retry-times", "3", knownListImageRepo+"@"+arm64Instance.String(), "containers-storage:"+storage+"test@"+arm64Instance.String())
|
||||
i1 := combinedOutputOfCommand(t, skopeoBinary, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
|
||||
var image1 imgspecv1.Image
|
||||
err = json.Unmarshal([]byte(i1), &image1)
|
||||
@@ -330,7 +345,7 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesSecondUse
|
||||
t := s.T()
|
||||
storage := t.TempDir()
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--raw", knownListImage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--retry-times", "3", "--raw", knownListImage)
|
||||
manifestDigest, err := manifest.Digest([]byte(m))
|
||||
require.NoError(t, err)
|
||||
digest := manifestDigest.String()
|
||||
@@ -340,8 +355,8 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesSecondUse
|
||||
require.NoError(t, err)
|
||||
arm64Instance, err := list.ChooseInstance(&types.SystemContext{ArchitectureChoice: "arm64"})
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+amd64Instance.String(), "containers-storage:"+storage+"test@"+amd64Instance.String())
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", "--retry-times", "3", knownListImageRepo+"@"+amd64Instance.String(), "containers-storage:"+storage+"test@"+amd64Instance.String())
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
i1 := combinedOutputOfCommand(t, skopeoBinary, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+amd64Instance.String())
|
||||
var image1 imgspecv1.Image
|
||||
err = json.Unmarshal([]byte(i1), &image1)
|
||||
@@ -365,7 +380,7 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesThirdUses
|
||||
t := s.T()
|
||||
storage := t.TempDir()
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--raw", knownListImage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--retry-times", "3", "--raw", knownListImage)
|
||||
manifestDigest, err := manifest.Digest([]byte(m))
|
||||
require.NoError(t, err)
|
||||
digest := manifestDigest.String()
|
||||
@@ -375,9 +390,9 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesThirdUses
|
||||
require.NoError(t, err)
|
||||
arm64Instance, err := list.ChooseInstance(&types.SystemContext{ArchitectureChoice: "arm64"})
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+amd64Instance.String(), "containers-storage:"+storage+"test@"+amd64Instance.String())
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", "--retry-times", "3", knownListImageRepo+"@"+amd64Instance.String(), "containers-storage:"+storage+"test@"+amd64Instance.String())
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoFails(t, `.*reading manifest for image instance.*does not exist.*`, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
|
||||
i1 := combinedOutputOfCommand(t, skopeoBinary, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+amd64Instance.String())
|
||||
var image1 imgspecv1.Image
|
||||
@@ -400,7 +415,7 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesTagAndDig
|
||||
t := s.T()
|
||||
storage := t.TempDir()
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--raw", knownListImage)
|
||||
m := combinedOutputOfCommand(t, skopeoBinary, "inspect", "--retry-times", "3", "--raw", knownListImage)
|
||||
manifestDigest, err := manifest.Digest([]byte(m))
|
||||
require.NoError(t, err)
|
||||
digest := manifestDigest.String()
|
||||
@@ -410,8 +425,8 @@ func (s *copySuite) TestCopyWithManifestListStorageDigestMultipleArchesTagAndDig
|
||||
require.NoError(t, err)
|
||||
arm64Instance, err := list.ChooseInstance(&types.SystemContext{ArchitectureChoice: "arm64"})
|
||||
require.NoError(t, err)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", knownListImage, "containers-storage:"+storage+"test:latest")
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=amd64", "copy", "--retry-times", "3", knownListImage, "containers-storage:"+storage+"test:latest")
|
||||
assertSkopeoSucceeds(t, "", "--override-arch=arm64", "copy", "--retry-times", "3", knownListImageRepo+"@"+digest, "containers-storage:"+storage+"test@"+digest)
|
||||
assertSkopeoFails(t, `.*reading manifest for image instance.*does not exist.*`, "--override-arch=amd64", "inspect", "--config", "containers-storage:"+storage+"test@"+digest)
|
||||
i1 := combinedOutputOfCommand(t, skopeoBinary, "--override-arch=arm64", "inspect", "--config", "containers-storage:"+storage+"test:latest")
|
||||
var image1 imgspecv1.Image
|
||||
@@ -444,14 +459,15 @@ func (s *copySuite) TestCopyFailsWhenImageOSDoesNotMatchRuntimeOS() {
|
||||
t := s.T()
|
||||
storage := t.TempDir()
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
assertSkopeoFails(t, `.*no image found in manifest list for architecture .*, variant .*, OS .*`, "copy", knownWindowsOnlyImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoFails(t, `.*no image found in manifest list for architecture .*, variant .*, OS .*`, "copy", "--retry-times", "3", knownWindowsOnlyImage, "containers-storage:"+storage+"test")
|
||||
}
|
||||
|
||||
func (s *copySuite) TestCopySucceedsWhenImageDoesNotMatchRuntimeButWeOverride() {
|
||||
t := s.T()
|
||||
storage := t.TempDir()
|
||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||
assertSkopeoSucceeds(t, "", "--override-os=windows", "--override-arch=amd64", "copy", knownWindowsOnlyImage, "containers-storage:"+storage+"test")
|
||||
assertSkopeoSucceeds(t, "", "--override-os=windows", "--override-arch=amd64", "copy", "--retry-times", "3", knownWindowsOnlyImage,
|
||||
"containers-storage:"+storage+"test")
|
||||
}
|
||||
|
||||
func (s *copySuite) TestCopySimpleAtomicRegistry() {
|
||||
@@ -461,7 +477,7 @@ func (s *copySuite) TestCopySimpleAtomicRegistry() {
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
// "pull": docker: → dir:
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQIN64, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQIN64, "--retry-times", "3", "dir:"+dir1)
|
||||
// "push": dir: → atomic:
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--debug", "copy", "dir:"+dir1, "atomic:localhost:5000/myns/unsigned:unsigned")
|
||||
// The result of pushing and pulling is an equivalent image, except for schema1 embedded names.
|
||||
@@ -479,7 +495,7 @@ func (s *copySuite) TestCopySimple() {
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
// "pull": docker: → dir:
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://registry.k8s.io/pause", "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "docker://registry.k8s.io/pause", "dir:"+dir1)
|
||||
// "push": dir: → docker(v2s2):
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--debug", "copy", "dir:"+dir1, ourRegistry+"pause:unsigned")
|
||||
// The result of pushing and pulling is an unmodified image.
|
||||
@@ -493,14 +509,16 @@ func (s *copySuite) TestCopySimple() {
|
||||
ociDest := "pause-latest-image"
|
||||
ociImgName := "pause"
|
||||
defer os.RemoveAll(ociDest)
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://registry.k8s.io/pause:latest", "oci:"+ociDest+":"+ociImgName)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "docker://registry.k8s.io/pause:latest", "oci:"+ociDest+":"+ociImgName)
|
||||
_, err := os.Stat(ociDest)
|
||||
require.NoError(t, err)
|
||||
// copy exits with status 2 if the image is not found within the container, in some transports.
|
||||
assertSkopeoFailsWithStatus(t, 2, "copy", "oci:"+ociDest+":thisdoesnotexist", "dir:"+t.TempDir())
|
||||
|
||||
// docker v2s2 -> OCI image layout without image name
|
||||
ociDest = "pause-latest-noimage"
|
||||
defer os.RemoveAll(ociDest)
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://registry.k8s.io/pause:latest", "oci:"+ociDest)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "docker://registry.k8s.io/pause:latest", "oci:"+ociDest)
|
||||
_, err = os.Stat(ociDest)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
@@ -539,7 +557,8 @@ func (s *copySuite) TestCopyEncryption() {
|
||||
"oci:"+encryptedImgDir+":encrypted", "oci:"+decryptedImgDir+":decrypted")
|
||||
|
||||
// Copy a standard busybox image locally
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQIN+":1.30.1", "oci:"+originalImageDir+":latest")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3",
|
||||
testFQIN+":1.30.1", "oci:"+originalImageDir+":latest")
|
||||
|
||||
// Encrypt the image
|
||||
assertSkopeoSucceeds(t, "", "copy", "--encryption-key",
|
||||
@@ -570,7 +589,7 @@ func (s *copySuite) TestCopyEncryption() {
|
||||
matchLayerBlobBinaryType(t, decryptedImgDir+"/blobs/sha256", "application/x-gzip", 1)
|
||||
|
||||
// Copy a standard multi layer nginx image locally
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQINMultiLayer, "oci:"+multiLayerImageDir+":latest")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", testFQINMultiLayer, "oci:"+multiLayerImageDir+":latest")
|
||||
|
||||
// Partially encrypt the image
|
||||
assertSkopeoSucceeds(t, "", "copy", "--encryption-key", "jwe:"+keysDir+"/public.key",
|
||||
@@ -671,9 +690,10 @@ func (s *copySuite) TestCopyStreaming() {
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
// streaming: docker: → atomic:
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--debug", "copy", testFQIN64, "atomic:localhost:5000/myns/unsigned:streaming")
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--debug", "copy", "--retry-times", "3",
|
||||
testFQIN64, "atomic:localhost:5000/myns/unsigned:streaming")
|
||||
// Compare (copies of) the original and the copy:
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQIN64, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", testFQIN64, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/unsigned:streaming", "dir:"+dir2)
|
||||
assertSchema1DirImagesAreEqualExceptNames(t, dir1, "libpod/busybox:amd64", dir2, "myns/unsigned:streaming")
|
||||
// FIXME: Also check pushing to docker://
|
||||
@@ -692,7 +712,8 @@ func (s *copySuite) TestCopyOCIRoundTrip() {
|
||||
oci2 := t.TempDir()
|
||||
|
||||
// Docker -> OCI
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--debug", "copy", testFQIN, "oci:"+oci1+":latest")
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--debug", "copy", "--retry-times", "3",
|
||||
testFQIN, "oci:"+oci1+":latest")
|
||||
// OCI -> Docker
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--debug", "copy", "oci:"+oci1+":latest", ourRegistry+"original/busybox:oci_copy")
|
||||
// Docker -> OCI
|
||||
@@ -728,7 +749,7 @@ func (s *copySuite) TestCopyOCIRoundTrip() {
|
||||
// --sign-by and --policy copy, primarily using atomic:
|
||||
func (s *copySuite) TestCopySignatures() {
|
||||
t := s.T()
|
||||
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
if err := mech.SupportsSigning(); err != nil { // FIXME? Test that verification and policy enforcement works, using signatures from fixtures
|
||||
@@ -738,29 +759,31 @@ func (s *copySuite) TestCopySignatures() {
|
||||
dir := t.TempDir()
|
||||
dirDest := "dir:" + dir
|
||||
|
||||
policy := fileFromFixture(t, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome})
|
||||
defer os.Remove(policy)
|
||||
policy := s.policyFixture(nil)
|
||||
|
||||
// type: reject
|
||||
assertSkopeoFails(t, fmt.Sprintf(".*Source image rejected: Running image %s:latest is rejected by policy.*", testFQIN),
|
||||
"--policy", policy, "copy", testFQIN+":latest", dirDest)
|
||||
"--policy", policy, "copy", "--retry-times", "3", testFQIN+":latest", dirDest)
|
||||
|
||||
// type: insecureAcceptAnything
|
||||
assertSkopeoSucceeds(t, "", "--policy", policy, "copy", "docker://quay.io/openshift/origin-hello-openshift", dirDest)
|
||||
assertSkopeoSucceeds(t, "", "--policy", policy, "copy", "--retry-times", "3", "docker://quay.io/openshift/origin-hello-openshift", dirDest)
|
||||
|
||||
// type: signedBy
|
||||
// Sign the images
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--sign-by", "personal@example.com", testFQIN+":1.26", "atomic:localhost:5006/myns/personal:personal")
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--sign-by", "official@example.com", testFQIN+":1.26.1", "atomic:localhost:5006/myns/official:official")
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--retry-times", "3", "--sign-by", "personal@example.com",
|
||||
testFQIN+":1.26", "atomic:localhost:5006/myns/personal:personal")
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--retry-times", "3", "--sign-by", "official@example.com",
|
||||
testFQIN+":1.26.1", "atomic:localhost:5006/myns/official:official")
|
||||
// Verify that we can pull them
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/personal:personal", dirDest)
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/official:official", dirDest)
|
||||
// Verify that mis-signed images are rejected
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "atomic:localhost:5006/myns/personal:personal", "atomic:localhost:5006/myns/official:attack")
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "atomic:localhost:5006/myns/official:official", "atomic:localhost:5006/myns/personal:attack")
|
||||
assertSkopeoFails(t, ".*Source image rejected: Invalid GPG signature.*",
|
||||
// "Invalid GPG signature" is reported by the gpgme mechanism; "Missing key: $fingerprint" by Sequoia.
|
||||
assertSkopeoFails(t, ".*Source image rejected: (Invalid GPG signature|Missing key:).*",
|
||||
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/personal:attack", dirDest)
|
||||
assertSkopeoFails(t, ".*Source image rejected: Invalid GPG signature.*",
|
||||
assertSkopeoFails(t, ".*Source image rejected: (Invalid GPG signature|Missing key:).*",
|
||||
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/official:attack", dirDest)
|
||||
|
||||
// Verify that signed identity is verified.
|
||||
@@ -773,7 +796,8 @@ func (s *copySuite) TestCopySignatures() {
|
||||
|
||||
// Verify that cosigning requirements are enforced
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "atomic:localhost:5006/myns/official:official", "atomic:localhost:5006/myns/cosigned:cosigned")
|
||||
assertSkopeoFails(t, ".*Source image rejected: Invalid GPG signature.*",
|
||||
// "Invalid GPG signature" is reported by the gpgme mechanism; "Missing key: $fingerprint" by Sequoia.
|
||||
assertSkopeoFails(t, ".*Source image rejected: (Invalid GPG signature|Missing key:).*",
|
||||
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/cosigned:cosigned", dirDest)
|
||||
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--sign-by", "personal@example.com", "atomic:localhost:5006/myns/official:official", "atomic:localhost:5006/myns/cosigned:cosigned")
|
||||
@@ -783,7 +807,7 @@ func (s *copySuite) TestCopySignatures() {
|
||||
// --policy copy for dir: sources
|
||||
func (s *copySuite) TestCopyDirSignatures() {
|
||||
t := s.T()
|
||||
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
if err := mech.SupportsSigning(); err != nil { // FIXME? Test that verification and policy enforcement works, using signatures from fixtures
|
||||
@@ -800,12 +824,11 @@ func (s *copySuite) TestCopyDirSignatures() {
|
||||
|
||||
// Note the "/@dirpath@": The value starts with a slash so that it is not rejected in other tests which do not replace it,
|
||||
// but we must ensure that the result is a canonical path, not something starting with a "//".
|
||||
policy := fileFromFixture(t, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome, "/@dirpath@": topDir + "/restricted"})
|
||||
defer os.Remove(policy)
|
||||
policy := s.policyFixture(map[string]string{"/@dirpath@": topDir + "/restricted"})
|
||||
|
||||
// Get some images.
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQIN+":armfh", topDirDest+"/dir1")
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQIN+":s390x", topDirDest+"/dir2")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", testFQIN+":armfh", topDirDest+"/dir1")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", testFQIN+":s390x", topDirDest+"/dir2")
|
||||
|
||||
// Sign the images. By coping from a topDirDest/dirN, also test that non-/restricted paths
|
||||
// use the dir:"" default of insecureAcceptAnything.
|
||||
@@ -819,7 +842,8 @@ func (s *copySuite) TestCopyDirSignatures() {
|
||||
// Verify that correct images are accepted
|
||||
assertSkopeoSucceeds(t, "", "--policy", policy, "copy", topDirDest+"/restricted/official", topDirDest+"/dest")
|
||||
// ... and that mis-signed images are rejected.
|
||||
assertSkopeoFails(t, ".*Source image rejected: Invalid GPG signature.*",
|
||||
// "Invalid GPG signature" is reported by the gpgme mechanism; "Missing key: $fingerprint" by Sequoia.
|
||||
assertSkopeoFails(t, ".*Source image rejected: (Invalid GPG signature|Missing key:).*",
|
||||
"--policy", policy, "copy", topDirDest+"/restricted/personal", topDirDest+"/dest")
|
||||
|
||||
// Verify that the signed identity is verified.
|
||||
@@ -829,6 +853,39 @@ func (s *copySuite) TestCopyDirSignatures() {
|
||||
"--policy", policy, "copy", topDirDest+"/restricted/badidentity", topDirDest+"/dest")
|
||||
}
|
||||
|
||||
func (s *copySuite) TestCopySequoiaSignatures() {
|
||||
t := s.T()
|
||||
signer, err := simplesequoia.NewSigner(simplesequoia.WithSequoiaHome(testSequoiaHome), simplesequoia.WithKeyFingerprint(testSequoiaKeyFingerprint))
|
||||
if err != nil {
|
||||
t.Skipf("Sequoia not supported: %v", err)
|
||||
}
|
||||
signer.Close()
|
||||
|
||||
const ourRegistry = "docker://" + v2DockerRegistryURL + "/"
|
||||
|
||||
dirDest := "dir:" + t.TempDir()
|
||||
|
||||
policy := s.policyFixture(nil)
|
||||
registriesDir := t.TempDir()
|
||||
registriesFile := fileFromFixture(t, "fixtures/registries.yaml",
|
||||
map[string]string{"@lookaside@": t.TempDir(), "@split-staging@": "/var/empty", "@split-read@": "file://var/empty"})
|
||||
err = os.Symlink(registriesFile, filepath.Join(registriesDir, "registries.yaml"))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Sign the images
|
||||
absSequoiaHome, err := filepath.Abs(testSequoiaHome)
|
||||
require.NoError(t, err)
|
||||
t.Setenv("SEQUOIA_HOME", absSequoiaHome)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--dest-tls-verify=false", "--sign-by-sq-fingerprint", testSequoiaKeyFingerprint,
|
||||
testFQIN+":1.26", ourRegistry+"sequoia-no-passphrase")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--dest-tls-verify=false", "--sign-by-sq-fingerprint", testSequoiaKeyFingerprintWithPassphrase,
|
||||
"--sign-passphrase-file", filepath.Join(absSequoiaHome, "with-passphrase.passphrase"),
|
||||
testFQIN+":1.26.1", ourRegistry+"sequoia-with-passphrase")
|
||||
// Verify that we can pull them
|
||||
assertSkopeoSucceeds(t, "", "--policy", policy, "copy", "--src-tls-verify=false", ourRegistry+"sequoia-no-passphrase", dirDest)
|
||||
assertSkopeoSucceeds(t, "", "--policy", policy, "copy", "--src-tls-verify=false", ourRegistry+"sequoia-with-passphrase", dirDest)
|
||||
}
|
||||
|
||||
// Compression during copy
|
||||
func (s *copySuite) TestCopyCompression() {
|
||||
t := s.T()
|
||||
@@ -885,7 +942,7 @@ func findRegularFiles(t *testing.T, root string) []string {
|
||||
// --sign-by and policy use for docker: with lookaside
|
||||
func (s *copySuite) TestCopyDockerLookaside() {
|
||||
t := s.T()
|
||||
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
if err := mech.SupportsSigning(); err != nil { // FIXME? Test that verification and policy enforcement works, using signatures from fixtures
|
||||
@@ -908,8 +965,7 @@ func (s *copySuite) TestCopyDockerLookaside() {
|
||||
}))
|
||||
defer splitLookasideReadServer.Close()
|
||||
|
||||
policy := fileFromFixture(t, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome})
|
||||
defer os.Remove(policy)
|
||||
policy := s.policyFixture(nil)
|
||||
registriesDir := filepath.Join(tmpDir, "registries.d")
|
||||
err = os.Mkdir(registriesDir, 0755)
|
||||
require.NoError(t, err)
|
||||
@@ -919,7 +975,8 @@ func (s *copySuite) TestCopyDockerLookaside() {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get an image to work with. Also verifies that we can use Docker repositories with no lookaside configured.
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", testFQIN, ourRegistry+"original/busybox")
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", "--retry-times", "3",
|
||||
testFQIN, ourRegistry+"original/busybox")
|
||||
// Pulling an unsigned image fails.
|
||||
assertSkopeoFails(t, ".*Source image rejected: A signature was required, but no signature exists.*",
|
||||
"--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"original/busybox", dirDest)
|
||||
@@ -954,7 +1011,7 @@ func (s *copySuite) TestCopyDockerLookaside() {
|
||||
// atomic: and docker: X-Registry-Supports-Signatures works and interoperates
|
||||
func (s *copySuite) TestCopyAtomicExtension() {
|
||||
t := s.T()
|
||||
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
if err := mech.SupportsSigning(); err != nil { // FIXME? Test that the reading/writing works using signatures from fixtures
|
||||
@@ -968,11 +1025,11 @@ func (s *copySuite) TestCopyAtomicExtension() {
|
||||
}
|
||||
registriesDir := filepath.Join(topDir, "registries.d")
|
||||
dirDest := "dir:" + topDir
|
||||
policy := fileFromFixture(t, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome})
|
||||
defer os.Remove(policy)
|
||||
policy := s.policyFixture(nil)
|
||||
|
||||
// Get an image to work with to an atomic: destination. Also verifies that we can use Docker repositories without X-Registry-Supports-Signatures
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", testFQIN, "atomic:localhost:5000/myns/extension:unsigned")
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--registries.d", registriesDir, "copy", "--retry-times", "3",
|
||||
testFQIN, "atomic:localhost:5000/myns/extension:unsigned")
|
||||
// Pulling an unsigned image using atomic: fails.
|
||||
assertSkopeoFails(t, ".*Source image rejected: A signature was required, but no signature exists.*",
|
||||
"--tls-verify=false", "--policy", policy,
|
||||
@@ -996,7 +1053,8 @@ func (s *copySuite) TestCopyAtomicExtension() {
|
||||
|
||||
// Get another image (different so that they don't share signatures, and sign it using docker://)
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "--registries.d", registriesDir,
|
||||
"copy", "--sign-by", "personal@example.com", testFQIN+":ppc64le", "docker://localhost:5000/myns/extension:extension")
|
||||
"copy", "--retry-times", "3", "--sign-by", "personal@example.com",
|
||||
testFQIN+":ppc64le", "docker://localhost:5000/myns/extension:extension")
|
||||
t.Logf("%s", combinedOutputOfCommand(t, "oc", "get", "istag", "extension:extension", "-o", "json"))
|
||||
// Pulling the image using atomic: succeeds.
|
||||
assertSkopeoSucceeds(t, "", "--debug", "--tls-verify=false", "--policy", policy,
|
||||
@@ -1013,7 +1071,7 @@ func (s *copySuite) TestCopyVerifyingMirroredSignatures() {
|
||||
t := s.T()
|
||||
const regPrefix = "docker://localhost:5006/myns/mirroring-"
|
||||
|
||||
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
if err := mech.SupportsSigning(); err != nil { // FIXME? Test that verification and policy enforcement works, using signatures from fixtures
|
||||
@@ -1024,8 +1082,7 @@ func (s *copySuite) TestCopyVerifyingMirroredSignatures() {
|
||||
registriesDir := filepath.Join(topDir, "registries.d") // An empty directory to disable lookaside use
|
||||
dirDest := "dir:" + filepath.Join(topDir, "unused-dest")
|
||||
|
||||
policy := fileFromFixture(t, "fixtures/policy.json", map[string]string{"@keydir@": s.gpgHome})
|
||||
defer os.Remove(policy)
|
||||
policy := s.policyFixture(nil)
|
||||
|
||||
// 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
|
||||
@@ -1033,7 +1090,8 @@ func (s *copySuite) TestCopyVerifyingMirroredSignatures() {
|
||||
// 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).
|
||||
|
||||
// Get an image to work with.
|
||||
assertSkopeoSucceeds(t, "", "copy", "--dest-tls-verify=false", testFQIN, regPrefix+"primary:unsigned")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--dest-tls-verify=false",
|
||||
testFQIN, regPrefix+"primary:unsigned")
|
||||
// Verify that unsigned images are rejected
|
||||
assertSkopeoFails(t, ".*Source image rejected: A signature was required, but no signature exists.*",
|
||||
"--policy", policy, "--registries.d", registriesDir, "--registries-conf", "fixtures/registries.conf", "copy", "--src-tls-verify=false", regPrefix+"primary:unsigned", dirDest)
|
||||
@@ -1082,19 +1140,22 @@ func (s *copySuite) TestCopyVerifyingMirroredSignatures() {
|
||||
|
||||
func (s *skopeoSuite) TestCopySrcWithAuth() {
|
||||
t := s.T()
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--dest-creds=testuser:testpassword", testFQIN, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--retry-times", "3", "--dest-creds=testuser:testpassword",
|
||||
testFQIN, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
|
||||
dir1 := t.TempDir()
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--src-creds=testuser:testpassword", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "dir:"+dir1)
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestCopyDestWithAuth() {
|
||||
t := s.T()
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--dest-creds=testuser:testpassword", testFQIN, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--retry-times", "3", "--dest-creds=testuser:testpassword",
|
||||
testFQIN, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
|
||||
}
|
||||
|
||||
func (s *skopeoSuite) TestCopySrcAndDestWithAuth() {
|
||||
t := s.T()
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--dest-creds=testuser:testpassword", testFQIN, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--retry-times", "3", "--dest-creds=testuser:testpassword",
|
||||
testFQIN, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
|
||||
assertSkopeoSucceeds(t, "", "--tls-verify=false", "copy", "--src-creds=testuser:testpassword", "--dest-creds=testuser:testpassword", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), fmt.Sprintf("docker://%s/test:auth", s.regV2WithAuth.url))
|
||||
}
|
||||
|
||||
@@ -1125,7 +1186,7 @@ func (s *copySuite) TestCopyManifestConversion() {
|
||||
|
||||
// oci to v2s1 and vice-versa not supported yet
|
||||
// get v2s2 manifest type
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQIN, "dir:"+srcDir)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", testFQIN, "dir:"+srcDir)
|
||||
verifyManifestMIMEType(t, srcDir, manifest.DockerV2Schema2MediaType)
|
||||
// convert from v2s2 to oci
|
||||
assertSkopeoSucceeds(t, "", "copy", "--format=oci", "dir:"+srcDir, "dir:"+destDir1)
|
||||
@@ -1145,8 +1206,8 @@ func (s *copySuite) TestCopyPreserveDigests() {
|
||||
t := s.T()
|
||||
topDir := t.TempDir()
|
||||
|
||||
assertSkopeoSucceeds(t, "", "copy", knownListImage, "--multi-arch=all", "--preserve-digests", "dir:"+topDir)
|
||||
assertSkopeoFails(t, ".*Instructed to preserve digests.*", "copy", knownListImage, "--multi-arch=all", "--preserve-digests", "--format=oci", "dir:"+topDir)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", knownListImage, "--multi-arch=all", "--preserve-digests", "dir:"+topDir)
|
||||
assertSkopeoFails(t, ".*Instructed to preserve digests.*", "copy", "--retry-times", "3", knownListImage, "--multi-arch=all", "--preserve-digests", "--format=oci", "dir:"+topDir)
|
||||
}
|
||||
|
||||
func (s *copySuite) testCopySchemaConversionRegistries(t *testing.T, schema1Registry, schema2Registry string) {
|
||||
@@ -1161,7 +1222,7 @@ func (s *copySuite) testCopySchemaConversionRegistries(t *testing.T, schema1Regi
|
||||
|
||||
// Ensure we are working with a schema2 image.
|
||||
// dir: accepts any manifest format, i.e. this makes …/input2 a schema2 source which cannot be asked to produce schema1 like ordinary docker: registries can.
|
||||
assertSkopeoSucceeds(t, "", "copy", testFQIN, "dir:"+input2Dir)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", testFQIN, "dir:"+input2Dir)
|
||||
verifyManifestMIMEType(t, input2Dir, manifest.DockerV2Schema2MediaType)
|
||||
// 2→2 (the "f2t2" in tag means "from 2 to 2")
|
||||
assertSkopeoSucceeds(t, "", "copy", "--dest-tls-verify=false", "dir:"+input2Dir, schema2Registry+":f2t2")
|
||||
|
||||
1
integration/fixtures/.gitignore
vendored
Normal file
1
integration/fixtures/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/data/pgp.cert.d/_sequoia*
|
||||
0
integration/fixtures/data/keystore/keystore.cookie
Normal file
0
integration/fixtures/data/keystore/keystore.cookie
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
integration/fixtures/data/pgp.cert.d/trust-root
Normal file
BIN
integration/fixtures/data/pgp.cert.d/trust-root
Normal file
Binary file not shown.
0
integration/fixtures/data/pgp.cert.d/writelock
Normal file
0
integration/fixtures/data/pgp.cert.d/writelock
Normal file
38
integration/fixtures/no-passphrase.pub
Normal file
38
integration/fixtures/no-passphrase.pub
Normal file
@@ -0,0 +1,38 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEaGwFVhYJKwYBBAHaRw8BAQdAZzfnqEAgvE3RoCtPWEOc3Xp8oMURR0qjq+Ru
|
||||
PHJrc6TCwAsEHxYKAH0FgmhsBVYDCwkHCRD2+Qi2+kiiKUcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcEjRQtILaFnIhczxeUkcfW0KMHEZ30
|
||||
wTdJ1v1iHB7NKQMVCggCmwECHgkWIQRQ3eiY305IdVyMK3r2+Qi2+kiiKQAA86gA
|
||||
/1ZkXWPHUxh3nQu/EL72ZeP9k/SLWkEuNKs6dJrmRud9AQCHbWwSUwKyt12EFVt/
|
||||
QvMFSQ95brUxsWLHgFMPpNfWAc0aU2tvcGVvIFNlcXVvaWEgdGVzdGluZyBrZXnC
|
||||
wA4EExYKAIAFgmhsBVYDCwkHCRD2+Qi2+kiiKUcUAAAAAAAeACBzYWx0QG5vdGF0
|
||||
aW9ucy5zZXF1b2lhLXBncC5vcmctF7xuY06GUyedOGjd2iNKwab85gV64zEAGKgi
|
||||
ExHRxgMVCggCmQECmwECHgkWIQRQ3eiY305IdVyMK3r2+Qi2+kiiKQAA3SEBAMe1
|
||||
y6rWaPjDpkeiDthLV1Umr6NsXVBv/IJTcP9RM4quAQCwmlsdQMddCsc+K3Y5KH88
|
||||
saIG0/MRZaPJdsd8vRGUCs4zBGhsBVYWCSsGAQQB2kcPAQEHQLN8yt/21QDMzcB4
|
||||
2bzFRg1LpkFZWECjkb2ty7Iju/aOwsC/BBgWCgExBYJobAVWCRD2+Qi2+kiiKUcU
|
||||
AAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmce9QEurrtI24ys
|
||||
vXssO/40rI5rlsNokEEFr7CVwVgWvAKbAr6gBBkWCgBvBYJobAVWCRB63Ra9Qdgp
|
||||
tkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcBWCJsdUfj
|
||||
oYpld4qcYBqjxsyScwpID2vkNlYMLmS+IhYhBKyZqvZ6WI3zgaapXHrdFr1B2Cm2
|
||||
AAAEZwEA/UhpNN1XElYx6Xq+JMKlXywoIgButkQy1+H2EcRBeHsBAM7lq8BXvRKz
|
||||
bDjRlgxiIAYl77p7ihVQ5NYcuZcAlH0CFiEEUN3omN9OSHVcjCt69vkItvpIoikA
|
||||
AJcwAP9D4spfb28k16w2cemrWAtAE1WUgV8V+OEpE7+gpV+17gEA+0Kzf7jBHgd3
|
||||
pBAWwttuRd8OHlZZzKs3f26z28I6mgLOMwRobAVWFgkrBgEEAdpHDwEBB0DPyS14
|
||||
jQk1mSWNmuYR4P9M5zOfU2mkhwaqx1l3OWTZD8LAvwQYFgoBMQWCaGwFVgkQ9vkI
|
||||
tvpIoilHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn+wfK
|
||||
FmPmtrsi0sY5zIq9KFmbrQyhXz/VZIw6K8D1zdECmyC+oAQZFgoAbwWCaGwFVgkQ
|
||||
bwujLUxU69BHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn
|
||||
xF3KXB4+dN9suOhCD2XkYlAWUJ4GVBVV2wAmdQAueyEWIQTv1sMw2eUTIMQmb7Zv
|
||||
C6MtTFTr0AAA/LYA/iBkRh6dGbp76VzuuHVNUNgTqvXgz9FjizZGJKnVZctXAPwL
|
||||
TlHxcH6XX96AuiCy9QAMUpm8ZvMu8TAgjgOrlFPKCBYhBFDd6JjfTkh1XIwrevb5
|
||||
CLb6SKIpAAA0rQD9HWbBeSoshjH6/k5ntZjOfIAha4/TLlBrMq2w+t4LWD0A/2q5
|
||||
DEbYh6PwMidDxXteyHWf4Qnr0vH8vip9d+WHbDYEzjgEaGwFVhIKKwYBBAGXVQEF
|
||||
AQEHQLxXHw9STOAhb2PLEjrl3uQDwpaXIdigg67vId0jSstVAwEIB8LAAAQYFgoA
|
||||
cgWCaGwFVgkQ9vkItvpIoilHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9p
|
||||
YS1wZ3Aub3Jn8bvuQCv3uEYJtK6h5y5e4AY9lJtVXx3brexR5bmFCwcCmwwWIQRQ
|
||||
3eiY305IdVyMK3r2+Qi2+kiiKQAAEzkA/Az97rdlp3hf97S6a5AxU8pTry4gKI63
|
||||
lwKtBAT+uF/pAP9lAziQRlNEa1sX6qCXrQqeA/aQ0nj9gRJ1Wvi1PMxWBA==
|
||||
=7jmE
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -13,6 +13,20 @@
|
||||
"keyPath": "@keydir@/personal-pubkey.gpg"
|
||||
}
|
||||
],
|
||||
"localhost:5555/sequoia-no-passphrase": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "GPGKeys",
|
||||
"keyPath": "@fixturedir@/no-passphrase.pub"
|
||||
}
|
||||
],
|
||||
"localhost:5555/sequoia-with-passphrase": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
"keyType": "GPGKeys",
|
||||
"keyPath": "@fixturedir@/with-passphrase.pub"
|
||||
}
|
||||
],
|
||||
"localhost:5000/myns/extension": [
|
||||
{
|
||||
"type": "signedBy",
|
||||
|
||||
1
integration/fixtures/with-passphrase.passphrase
Normal file
1
integration/fixtures/with-passphrase.passphrase
Normal file
@@ -0,0 +1 @@
|
||||
WithPassphrase123
|
||||
39
integration/fixtures/with-passphrase.pub
Normal file
39
integration/fixtures/with-passphrase.pub
Normal file
@@ -0,0 +1,39 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEaGwF3RYJKwYBBAHaRw8BAQdAouHF6y7foOScub78AINlTzXnEQrYrAJyH8fr
|
||||
3biwuMzCwAsEHxYKAH0FgmhsBd0DCwkHCRAtEaGaukHGrkcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdRHdDbkndmp7Q96YisL7ezwrLxSfQj
|
||||
46zFb8wob+6yvgMVCggCmwECHgkWIQQfWCUoW3heHbE7820tEaGaukHGrgAAbd8A
|
||||
/3iwAF7qTVgqqCqLVIj8oJxrZr/jWbHbjO1DzFafQQjMAQDwwOuL9dhy9Q7N5UkW
|
||||
x3kq3WLEIuogh+0meAwfMrJMAM0qU2tvcGVvIFNlcXVvaWEgdGVzdGluZyBrZXkg
|
||||
d2l0aCBwYXNzcGhyYXNlwsAOBBMWCgCABYJobAXdAwsJBwkQLRGhmrpBxq5HFAAA
|
||||
AAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnwGFwQkw9BWc963pG
|
||||
lBgz8D9CbfsqoDS58GXyd24W8g4DFQoIApkBApsBAh4JFiEEH1glKFt4Xh2xO/Nt
|
||||
LRGhmrpBxq4AAKTpAPsHMyzeL+fT/EdPbU/+fi/+RbGuRQH5QHtzaDfAu+ZGUwD+
|
||||
Oeoi7OOy8+bgvnEdj31TohAGEexTvhMIILglL9ymTgfOMwRobAXdFgkrBgEEAdpH
|
||||
DwEBB0DNeYLgt7VaYbdJ3TyTqiYp7pEuXYVYjeqRtt055Hs60cLAvwQYFgoBMQWC
|
||||
aGwF3QkQLRGhmrpBxq5HFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1w
|
||||
Z3Aub3Jnr08xd/fCttifQZ/b+oVq2huO6HT9zpTITLIzPLLBI6cCmwK+oAQZFgoA
|
||||
bwWCaGwF3QkQVNJA3Fgs7h9HFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9p
|
||||
YS1wZ3Aub3JnN1Kokqv7bIxnM7EODP0bX7yuAV8OP+kCivD84d8TrkEWIQQemi42
|
||||
PEh1us0v16FU0kDcWCzuHwAAUisBAKBMLjhkVO+KCFNKxYoak/Hj7VAHwiqnEAXB
|
||||
aMstWEE1AP9rVWwZ85IdlSejb475H9HGl+Nl0a5BOioR/Y+Kl15UBxYhBB9YJShb
|
||||
eF4dsTvzbS0RoZq6QcauAAAKnAEAvgb1r2cteb+9wd9U5vYZ7/xXKEljojjA7CQT
|
||||
QFmecoYBAO3/rNK3xYcKleni3lknNhzQap+Ed6ri2WVQCKujRgIAzjMEaGwF3RYJ
|
||||
KwYBBAHaRw8BAQdA1JYMc2I192WwvCI/qFcLrwmFPwDDkHvNDDt4Kc2ziHjCwL8E
|
||||
GBYKATEFgmhsBd0JEC0RoZq6QcauRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNl
|
||||
cXVvaWEtcGdwLm9yZxDKeHct4SrN5lJ3oAkhIfwcJpCTVv9Sux05J7Pn0U6TApsg
|
||||
vqAEGRYKAG8FgmhsBd0JEMb35fxT9XmfRxQAAAAAAB4AIHNhbHRAbm90YXRpb25z
|
||||
LnNlcXVvaWEtcGdwLm9yZ9MZe54S5aYMdKLQmZiNN7Q1tot0zCuRp0DOMrZIsWQg
|
||||
FiEEzXeihzhK/PSlCtVwxvfl/FP1eZ8AAFpUAQC1WlWjrTCL+ZiG3X9ThPO8418f
|
||||
wu+p3l9jJAF1SK15QQEA6Go0+bbWOHMpkMNckSwlXhbBKVp53y2IhQnwLAfbZwoW
|
||||
IQQfWCUoW3heHbE7820tEaGaukHGrgAAhYMA/iuXYUHqeXNpFCmoDFWmvwHDoPIs
|
||||
8ZrgBJOfSnzg+x5wAQCFIWANcwYD/rCHTN6KQY70VI/x7SmkqKJZVrIBCB7DB844
|
||||
BGhsBd0SCisGAQQBl1UBBQEBB0CYZYh5OKFAiuKOx4MIk6pocGCdfpL/XrJVoWjT
|
||||
9aDSNAMBCAfCwAAEGBYKAHIFgmhsBd0JEC0RoZq6QcauRxQAAAAAAB4AIHNhbHRA
|
||||
bm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ0oyJPZxXWc2dSxHpS1UAuvCfc80DaDy
|
||||
mr1nRs5/QO0aApsMFiEEH1glKFt4Xh2xO/NtLRGhmrpBxq4AANKZAP0T00LyderN
|
||||
Qsdk2UgpeeoZhN4wKtlUGocUs7I90P3AhgD/WuDXAlF6b9IXyTUoG9VkLrnlemCx
|
||||
Dii+5qsdk0HFcgA=
|
||||
=YS7U
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
@@ -3,4 +3,12 @@ package main
|
||||
const (
|
||||
// TestImageManifestDigest is the Docker manifest digest of "fixtures/image.manifest.json"
|
||||
TestImageManifestDigest = "sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55"
|
||||
|
||||
testSequoiaHome = "./fixtures"
|
||||
// testSequoiaKeyFingerprint is a fingerprint of a test key in testSequoiaHome, generated using
|
||||
// > sq --home $(pwd)/signature/simplesequoia/testdata key generate --name 'Skopeo Sequoia testing key' --own-key --expiration=never
|
||||
testSequoiaKeyFingerprint = "50DDE898DF4E48755C8C2B7AF6F908B6FA48A229"
|
||||
// testSequoiaKeyFingerprintWithPassphrase is a fingerprint of a test key in testSequoiaHome, generated using
|
||||
// > sq --home $(pwd)/signature/simplesequoia/testdata key generate --name 'Skopeo Sequoia testing key with passphrase' --own-key --expiration=never
|
||||
testSequoiaKeyFingerprintWithPassphrase = "1F5825285B785E1DB13BF36D2D11A19ABA41C6AE"
|
||||
)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build openshift_shell
|
||||
// +build openshift_shell
|
||||
|
||||
package main
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containers/storage/pkg/homedir"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.podman.io/storage/pkg/homedir"
|
||||
)
|
||||
|
||||
var adminKUBECONFIG = map[string]string{
|
||||
@@ -153,7 +154,7 @@ func (cluster *openshiftCluster) prepareRegistryConfig(t *testing.T) {
|
||||
require.Equal(t, "", string(out))
|
||||
}
|
||||
|
||||
// startRegistry starts the OpenShift registry with configPart on port, waits for it to be ready, and returns the process object, or terminates on failure.
|
||||
// startRegistryProcess starts the OpenShift registry with configPart on port, waits for it to be ready, and returns the process object, or terminates on failure.
|
||||
func (cluster *openshiftCluster) startRegistryProcess(t *testing.T, port uint16, configPath string) *exec.Cmd {
|
||||
cmd := cluster.clusterCmd(map[string]string{
|
||||
"KUBECONFIG": "openshift.local.registry/openshift-registry.kubeconfig",
|
||||
@@ -222,7 +223,7 @@ func (cluster *openshiftCluster) ocLoginToProject(t *testing.T) {
|
||||
// We do not run (docker login) directly, because that requires a running daemon and a docker package.
|
||||
func (cluster *openshiftCluster) dockerLogin(t *testing.T) {
|
||||
cluster.dockerDir = filepath.Join(homedir.Get(), ".docker")
|
||||
err := os.Mkdir(cluster.dockerDir, 0700)
|
||||
err := os.MkdirAll(cluster.dockerDir, 0700)
|
||||
require.NoError(t, err)
|
||||
|
||||
out := combinedOutputOfCommand(t, "oc", "config", "view", "-o", "json", "-o", "jsonpath={.users[*].user.token}")
|
||||
@@ -253,10 +254,10 @@ func (cluster *openshiftCluster) relaxImageSignerPermissions(t *testing.T) {
|
||||
|
||||
// tearDown stops the cluster services and deletes (only some!) of the state.
|
||||
func (cluster *openshiftCluster) tearDown(t *testing.T) {
|
||||
for i := len(cluster.processes) - 1; i >= 0; i-- {
|
||||
for _, process := range slices.Backward(cluster.processes) {
|
||||
// It’s undocumented what Kill() returns if the process has terminated,
|
||||
// so we couldn’t check just for that. This is running in a container anyway…
|
||||
_ = cluster.processes[i].Process.Kill()
|
||||
_ = process.Process.Kill()
|
||||
}
|
||||
if cluster.dockerDir != "" {
|
||||
err := os.RemoveAll(cluster.dockerDir)
|
||||
@@ -1,5 +1,4 @@
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
//go:build unix && !linux
|
||||
|
||||
package main
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build unix
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -8,15 +10,16 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
)
|
||||
|
||||
// This image is known to be x86_64 only right now
|
||||
@@ -58,8 +61,9 @@ type proxy struct {
|
||||
|
||||
type pipefd struct {
|
||||
// id is the remote identifier "pipeid"
|
||||
id uint
|
||||
fd *os.File
|
||||
id uint
|
||||
datafd *os.File
|
||||
errfd *os.File
|
||||
}
|
||||
|
||||
func (p *proxy) call(method string, args []any) (rval any, fd *pipefd, err error) {
|
||||
@@ -97,26 +101,41 @@ func (p *proxy) call(method string, args []any) (rval any, fd *pipefd, err error
|
||||
return
|
||||
}
|
||||
|
||||
var scms []syscall.SocketControlMessage
|
||||
scms, err = syscall.ParseSocketControlMessage(oob[:oobn])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to parse control message: %w", err)
|
||||
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: %w", err)
|
||||
return
|
||||
}
|
||||
if len(scms) != 1 {
|
||||
err = fmt.Errorf("Expected 1 received fd, found %d", len(scms))
|
||||
err = fmt.Errorf("Expected 1 socket control message, found %d", len(scms))
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(scms) > 2 {
|
||||
err = fmt.Errorf("Expected 1 or 2 socket control message, found %d", len(scms))
|
||||
return
|
||||
}
|
||||
if len(scms) != 0 {
|
||||
var fds []int
|
||||
fds, err = syscall.ParseUnixRights(&scms[0])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to parse unix rights: %w", err)
|
||||
return
|
||||
}
|
||||
if len(fds) < 1 || len(fds) > 2 {
|
||||
err = fmt.Errorf("expected 1 or 2 fds, found %d", len(fds))
|
||||
return
|
||||
}
|
||||
var errfd *os.File
|
||||
if len(fds) == 2 {
|
||||
errfd = os.NewFile(uintptr(fds[1]), "errfd")
|
||||
}
|
||||
fd = &pipefd{
|
||||
fd: os.NewFile(uintptr(fds[0]), "replyfd"),
|
||||
id: uint(reply.PipeID),
|
||||
datafd: os.NewFile(uintptr(fds[0]), "replyfd"),
|
||||
id: uint(reply.PipeID),
|
||||
errfd: errfd,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +168,7 @@ func (p *proxy) callReadAllBytes(method string, args []any) (rval any, buf []byt
|
||||
}
|
||||
fetchchan := make(chan byteFetch)
|
||||
go func() {
|
||||
manifestBytes, err := io.ReadAll(fd.fd)
|
||||
manifestBytes, err := io.ReadAll(fd.datafd)
|
||||
fetchchan <- byteFetch{
|
||||
content: manifestBytes,
|
||||
err: err,
|
||||
@@ -173,6 +192,80 @@ func (p *proxy) callReadAllBytes(method string, args []any) (rval any, buf []byt
|
||||
return
|
||||
}
|
||||
|
||||
type proxyError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (p *proxy) callGetRawBlob(args []any) (rval any, buf []byte, err error) {
|
||||
var fd *pipefd
|
||||
rval, fd, err = p.call("GetRawBlob", args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if fd == nil {
|
||||
err = fmt.Errorf("Expected fds from method GetRawBlob")
|
||||
return
|
||||
}
|
||||
if fd.errfd == nil {
|
||||
err = fmt.Errorf("Expected errfd from method GetRawBlob")
|
||||
return
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
fetchchan := make(chan byteFetch, 1)
|
||||
errchan := make(chan proxyError, 1)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
defer close(fetchchan)
|
||||
defer fd.datafd.Close()
|
||||
buf, err := io.ReadAll(fd.datafd)
|
||||
fetchchan <- byteFetch{
|
||||
content: buf,
|
||||
err: err,
|
||||
}
|
||||
|
||||
}()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
defer fd.errfd.Close()
|
||||
defer close(errchan)
|
||||
buf, err := io.ReadAll(fd.errfd)
|
||||
var proxyErr proxyError
|
||||
if err != nil {
|
||||
proxyErr.Code = "read-from-proxy"
|
||||
proxyErr.Message = err.Error()
|
||||
errchan <- proxyErr
|
||||
return
|
||||
}
|
||||
// No error, leave code+message unset
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
unmarshalErr := json.Unmarshal(buf, &proxyErr)
|
||||
// Shouldn't happen
|
||||
if unmarshalErr != nil {
|
||||
panic(unmarshalErr)
|
||||
}
|
||||
errchan <- proxyErr
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
errMsg := <-errchan
|
||||
if errMsg.Code != "" {
|
||||
return nil, nil, fmt.Errorf("(%s) %s", errMsg.Code, errMsg.Message)
|
||||
}
|
||||
fetchRes := <-fetchchan
|
||||
err = fetchRes.err
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf = fetchRes.content
|
||||
return
|
||||
}
|
||||
|
||||
func newProxy() (*proxy, error) {
|
||||
fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET, 0)
|
||||
if err != nil {
|
||||
@@ -189,7 +282,7 @@ func newProxy() (*proxy, error) {
|
||||
}
|
||||
|
||||
// Note ExtraFiles starts at 3
|
||||
proc := exec.Command("skopeo", "experimental-image-proxy", "--sockfd", "3")
|
||||
proc := exec.Command(skopeoBinary, "experimental-image-proxy", "--sockfd", "3")
|
||||
proc.Stderr = os.Stderr
|
||||
cmdLifecycleToParentIfPossible(proc)
|
||||
proc.ExtraFiles = append(proc.ExtraFiles, theirfd)
|
||||
@@ -229,7 +322,8 @@ type byteFetch struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
// This exercises all the metadata fetching APIs.
|
||||
func runTestMetadataAPIs(p *proxy, img string) error {
|
||||
v, err := p.callNoFd("OpenImage", []any{img})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -291,6 +385,19 @@ func runTestGetManifestAndConfig(p *proxy, img string) error {
|
||||
return fmt.Errorf("No CMD or ENTRYPOINT set")
|
||||
}
|
||||
|
||||
_, layerInfoBytes, err := p.callReadAllBytes("GetLayerInfoPiped", []any{imgid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var layerInfoBytesData []interface{}
|
||||
err = json.Unmarshal(layerInfoBytes, &layerInfoBytesData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(layerInfoBytesData) == 0 {
|
||||
return fmt.Errorf("expected layer info data")
|
||||
}
|
||||
|
||||
// Also test this legacy interface
|
||||
_, ctrconfigBytes, err := p.callReadAllBytes("GetConfig", []any{imgid})
|
||||
if err != nil {
|
||||
@@ -332,18 +439,57 @@ func runTestOpenImageOptionalNotFound(p *proxy, img string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *proxySuite) TestProxy() {
|
||||
func runTestGetBlob(p *proxy, img string) error {
|
||||
imgid, err := p.callNoFd("OpenImage", []any{img})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, manifestBytes, err := p.callReadAllBytes("GetManifest", []any{imgid})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mfest, err := manifest.OCI1FromManifest(manifestBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, layer := range mfest.Layers {
|
||||
_, blobBytes, err := p.callGetRawBlob([]any{imgid, layer.Digest})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(blobBytes) != int(layer.Size) {
|
||||
panic(fmt.Sprintf("Expected %d bytes, got %d", layer.Size, len(blobBytes)))
|
||||
}
|
||||
}
|
||||
|
||||
// echo "not a valid layer" | sha256sum
|
||||
invalidDigest := "sha256:21a9aab5a3494674d2b4d8e7381c236a799384dd10545531014606cf652c119f"
|
||||
|
||||
_, blobBytes, err := p.callGetRawBlob([]any{imgid, invalidDigest})
|
||||
if err == nil {
|
||||
panic("Expected error fetching invalid blob")
|
||||
}
|
||||
if blobBytes != nil {
|
||||
panic("Expected no bytes fetching invalid blob")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *proxySuite) TestProxyMetadata() {
|
||||
t := s.T()
|
||||
p, err := newProxy()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = runTestGetManifestAndConfig(p, knownNotManifestListedImageX8664)
|
||||
err = runTestMetadataAPIs(p, knownNotManifestListedImageX8664)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Testing image %s: %v", knownNotManifestListedImageX8664, err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = runTestGetManifestAndConfig(p, knownListImage)
|
||||
err = runTestMetadataAPIs(p, knownListImage)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Testing image %s: %v", knownListImage, err)
|
||||
}
|
||||
@@ -355,3 +501,15 @@ func (s *proxySuite) TestProxy() {
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func (s *proxySuite) TestProxyGetBlob() {
|
||||
t := s.T()
|
||||
p, err := newProxy()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = runTestGetBlob(p, knownListImage)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Testing GetBLob for %s: %v", knownListImage, err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
binaryV2 = "registry-v2"
|
||||
binaryV2 = "registry"
|
||||
binaryV2Schema1 = "registry-v2-schema1"
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -8,9 +9,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/signature"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.podman.io/image/v5/signature"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -29,9 +30,8 @@ type signingSuite struct {
|
||||
var _ = suite.SetupAllSuite(&signingSuite{})
|
||||
|
||||
func findFingerprint(lineBytes []byte) (string, error) {
|
||||
lines := string(lineBytes)
|
||||
for _, line := range strings.Split(lines, "\n") {
|
||||
fields := strings.Split(line, ":")
|
||||
for line := range bytes.SplitSeq(lineBytes, []byte{'\n'}) {
|
||||
fields := strings.Split(string(line), ":")
|
||||
if len(fields) >= 10 && fields[0] == "fpr" {
|
||||
return fields[9], nil
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func (s *signingSuite) SetupSuite() {
|
||||
|
||||
func (s *signingSuite) TestSignVerifySmoke() {
|
||||
t := s.T()
|
||||
mech, _, err := signature.NewEphemeralGPGSigningMechanism([]byte{})
|
||||
mech, err := signature.NewGPGSigningMechanism()
|
||||
require.NoError(t, err)
|
||||
defer mech.Close()
|
||||
if err := mech.SupportsSigning(); err != nil { // FIXME? Test that verification and policy enforcement works, using signatures from fixtures
|
||||
|
||||
@@ -11,14 +11,14 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containers/image/v5/docker"
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/containers/image/v5/types"
|
||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.podman.io/image/v5/docker"
|
||||
"go.podman.io/image/v5/docker/reference"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
"go.podman.io/image/v5/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -146,7 +146,7 @@ func (s *syncSuite) TestDocker2DirTagged() {
|
||||
require.NoError(t, err)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://"+image, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "docker://"+image, "dir:"+dir2)
|
||||
_, err = os.Stat(path.Join(dir2, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -173,7 +173,7 @@ func (s *syncSuite) TestDocker2DirTaggedAll() {
|
||||
require.NoError(t, err)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "--all", "docker://"+image, "dir:"+dir2)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--all", "docker://"+image, "dir:"+dir2)
|
||||
_, err = os.Stat(path.Join(dir2, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -189,11 +189,12 @@ func (s *syncSuite) TestPreserveDigests() {
|
||||
image := pullableTaggedManifestList
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "--all", "--preserve-digests", "docker://"+image, "dir:"+tmpDir)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--all", "--preserve-digests", "docker://"+image, "dir:"+tmpDir)
|
||||
_, err := os.Stat(path.Join(tmpDir, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
assertSkopeoFails(t, ".*Instructed to preserve digests.*", "copy", "--all", "--preserve-digests", "--format=oci", "docker://"+image, "dir:"+tmpDir)
|
||||
assertSkopeoFails(t, ".*Instructed to preserve digests.*",
|
||||
"copy", "--retry-times", "3", "--all", "--preserve-digests", "--format=oci", "docker://"+image, "dir:"+tmpDir)
|
||||
}
|
||||
|
||||
func (s *syncSuite) TestScoped() {
|
||||
@@ -223,7 +224,7 @@ func (s *syncSuite) TestDirIsNotOverwritten() {
|
||||
imagePath := imageRef.DockerReference().String()
|
||||
|
||||
// make a copy of the image in the local registry
|
||||
assertSkopeoSucceeds(t, "", "copy", "--dest-tls-verify=false", "docker://"+image, "docker://"+path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())))
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--dest-tls-verify=false", "docker://"+image, "docker://"+path.Join(v2DockerRegistryURL, reference.Path(imageRef.DockerReference())))
|
||||
|
||||
//sync upstream image to dir, not scoped
|
||||
dir1 := t.TempDir()
|
||||
@@ -381,7 +382,7 @@ quay.io:
|
||||
// get the number of tags
|
||||
re := regexp.MustCompile(`^ +- +[^:/ ]+`)
|
||||
var nTags int
|
||||
for _, l := range strings.Split(yamlConfig, "\n") {
|
||||
for l := range strings.SplitSeq(yamlConfig, "\n") {
|
||||
if re.MatchString(l) {
|
||||
nTags++
|
||||
}
|
||||
@@ -405,7 +406,7 @@ func (s *syncSuite) TestYamlTLSVerify() {
|
||||
|
||||
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||
// copy docker => docker
|
||||
assertSkopeoSucceeds(t, "", "copy", "--dest-tls-verify=false", "docker://"+image+":"+tag, localRegURL+image+":"+tag)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "--dest-tls-verify=false", "docker://"+image+":"+tag, localRegURL+image+":"+tag)
|
||||
|
||||
yamlTemplate := `
|
||||
%s:
|
||||
@@ -488,7 +489,7 @@ func (s *syncSuite) TestDocker2DockerTagged() {
|
||||
assertSkopeoSucceeds(t, "", "sync", "--scoped", "--dest-tls-verify=false", "--src", "docker", "--dest", "docker", image, v2DockerRegistryURL)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://"+image, "dir:"+dir1)
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "docker://"+image, "dir:"+dir1)
|
||||
_, err = os.Stat(path.Join(dir1, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -522,7 +523,7 @@ func (s *syncSuite) TestDir2DockerTagged() {
|
||||
require.NoError(t, err)
|
||||
|
||||
// copy docker => dir
|
||||
assertSkopeoSucceeds(t, "", "copy", "docker://"+image, "dir:"+path.Join(dir1, image))
|
||||
assertSkopeoSucceeds(t, "", "copy", "--retry-times", "3", "docker://"+image, "dir:"+path.Join(dir1, image))
|
||||
_, err = os.Stat(path.Join(dir1, image, "manifest.json"))
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
114
integration/user_agent_test.go
Normal file
114
integration/user_agent_test.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// mockRegistryHandler implements a minimal Docker Registry V2 API that captures User-Agent headers
|
||||
type mockRegistryHandler struct {
|
||||
mu sync.Mutex
|
||||
userAgents []string
|
||||
}
|
||||
|
||||
func (h *mockRegistryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// Capture the User-Agent header
|
||||
h.mu.Lock()
|
||||
h.userAgents = append(h.userAgents, r.Header.Get("User-Agent"))
|
||||
h.mu.Unlock()
|
||||
|
||||
// Implement minimal Docker Registry V2 API endpoints for inspect --raw
|
||||
switch {
|
||||
case r.URL.Path == "/v2/":
|
||||
// Registry version check endpoint
|
||||
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
case strings.HasSuffix(r.URL.Path, "/manifests/latest"):
|
||||
// Return a minimal OCI manifest as raw string
|
||||
// The digest matches this exact content
|
||||
manifest := `{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.oci.image.config.v1+json","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":0}]}`
|
||||
w.Header().Set("Content-Type", "application/vnd.oci.image.manifest.v1+json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
if _, err := w.Write([]byte(manifest)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
default:
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *mockRegistryHandler) getUserAgents() []string {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
return slices.Clone(h.userAgents)
|
||||
}
|
||||
|
||||
func TestUserAgent(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
extraArgs []string
|
||||
userAgentValidator func(string) bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "default user agent",
|
||||
extraArgs: []string{},
|
||||
userAgentValidator: func(ua string) bool {
|
||||
return strings.HasPrefix(ua, "skopeo/")
|
||||
},
|
||||
description: "Default user agent should start with 'skopeo/'",
|
||||
},
|
||||
{
|
||||
name: "custom user agent prefix",
|
||||
extraArgs: []string{"--user-agent-prefix", "bootc/1.0"},
|
||||
userAgentValidator: func(ua string) bool {
|
||||
return strings.HasPrefix(ua, "bootc/1.0 skopeo/")
|
||||
},
|
||||
description: "Custom user agent should be in format 'prefix skopeo/version'",
|
||||
},
|
||||
{
|
||||
name: "prefix with spaces",
|
||||
extraArgs: []string{"--user-agent-prefix", "my cool app"},
|
||||
userAgentValidator: func(ua string) bool {
|
||||
return strings.HasPrefix(ua, "my cool app skopeo/")
|
||||
},
|
||||
description: "User agent with spaces should work correctly",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
handler := &mockRegistryHandler{}
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
|
||||
// Extract host:port from the test server URL
|
||||
registryAddr := strings.TrimPrefix(server.URL, "http://")
|
||||
imageRef := "docker://" + registryAddr + "/test/image:latest"
|
||||
|
||||
// Build arguments: base args + test-specific args + image ref
|
||||
args := append([]string{"--tls-verify=false"}, tc.extraArgs...)
|
||||
args = append(args, "inspect", "--raw", imageRef)
|
||||
|
||||
// Run skopeo inspect --raw
|
||||
assertSkopeoSucceeds(t, "", args...)
|
||||
|
||||
// Verify that at least one request was made with the expected User-Agent
|
||||
userAgents := handler.getUserAgents()
|
||||
require.NotEmpty(t, userAgents, "Expected at least one request to be made")
|
||||
|
||||
// Check that at least one User-Agent matches the validator
|
||||
require.True(t,
|
||||
slices.ContainsFunc(userAgents, tc.userAgentValidator),
|
||||
"%s, got: %v", tc.description, userAgents)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,20 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/v5/manifest"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.podman.io/image/v5/manifest"
|
||||
)
|
||||
|
||||
const skopeoBinary = "skopeo"
|
||||
// FIXME: Move to SetupSuite
|
||||
// https://github.com/containers/skopeo/pull/2703#discussion_r2331374730
|
||||
var skopeoBinary = func() string {
|
||||
if binary := os.Getenv("SKOPEO_BINARY"); binary != "" {
|
||||
return binary
|
||||
}
|
||||
return "skopeo"
|
||||
}()
|
||||
|
||||
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"
|
||||
@@ -55,7 +62,7 @@ func consumeAndLogOutputs(t *testing.T, id string, cmd *exec.Cmd) {
|
||||
}
|
||||
|
||||
// combinedOutputOfCommand runs a command as if exec.Command().CombinedOutput(), verifies that the exit status is 0, and returns the output,
|
||||
// or terminates c on failure.
|
||||
// or terminates t on failure.
|
||||
func combinedOutputOfCommand(t *testing.T, name string, args ...string) string {
|
||||
t.Logf("Running %s %s", name, strings.Join(args, " "))
|
||||
out, err := exec.Command(name, args...).CombinedOutput()
|
||||
@@ -64,8 +71,7 @@ func combinedOutputOfCommand(t *testing.T, name string, args ...string) string {
|
||||
}
|
||||
|
||||
// assertSkopeoSucceeds runs a skopeo command as if exec.Command().CombinedOutput, verifies that the exit status is 0,
|
||||
// and optionally that the output matches a multi-line regexp if it is nonempty;
|
||||
// or terminates c on failure
|
||||
// and optionally that the output matches a multi-line regexp if it is nonempty
|
||||
func assertSkopeoSucceeds(t *testing.T, regexp string, args ...string) {
|
||||
t.Logf("Running %s %s", skopeoBinary, strings.Join(args, " "))
|
||||
out, err := exec.Command(skopeoBinary, args...).CombinedOutput()
|
||||
@@ -75,9 +81,8 @@ func assertSkopeoSucceeds(t *testing.T, regexp string, args ...string) {
|
||||
}
|
||||
}
|
||||
|
||||
// assertSkopeoFails runs a skopeo command as if exec.Command().CombinedOutput, verifies that the exit status is 0,
|
||||
// and that the output matches a multi-line regexp;
|
||||
// or terminates c on failure
|
||||
// assertSkopeoFails runs a skopeo command as if exec.Command().CombinedOutput, verifies that the exit status is not 0,
|
||||
// and that the output matches a multi-line regexp
|
||||
func assertSkopeoFails(t *testing.T, regexp string, args ...string) {
|
||||
t.Logf("Running %s %s", skopeoBinary, strings.Join(args, " "))
|
||||
out, err := exec.Command(skopeoBinary, args...).CombinedOutput()
|
||||
@@ -85,15 +90,25 @@ func assertSkopeoFails(t *testing.T, regexp string, args ...string) {
|
||||
assert.Regexp(t, "(?s)"+regexp, string(out)) // (?s) : '.' will also match newlines
|
||||
}
|
||||
|
||||
// assertSkopeoFailsWithStatus runs a skopeo command as if exec.Command().CombinedOutput,
|
||||
// and verifies that it fails with a specific exit status.
|
||||
func assertSkopeoFailsWithStatus(t *testing.T, status int, args ...string) {
|
||||
t.Logf("Running %s %s", skopeoBinary, strings.Join(args, " "))
|
||||
_, err := exec.Command(skopeoBinary, args...).CombinedOutput()
|
||||
var exitErr *exec.ExitError
|
||||
require.ErrorAs(t, err, &exitErr)
|
||||
assert.Equal(t, status, exitErr.ExitCode())
|
||||
}
|
||||
|
||||
// runCommandWithInput runs a command as if exec.Command(), sending it the input to stdin,
|
||||
// and verifies that the exit status is 0, or terminates c on failure.
|
||||
// and verifies that the exit status is 0, or terminates t on failure.
|
||||
func runCommandWithInput(t *testing.T, input string, name string, args ...string) {
|
||||
cmd := exec.Command(name, args...)
|
||||
runExecCmdWithInput(t, cmd, input)
|
||||
}
|
||||
|
||||
// runExecCmdWithInput runs an exec.Cmd, sending it the input to stdin,
|
||||
// and verifies that the exit status is 0, or terminates c on failure.
|
||||
// and verifies that the exit status is 0, or terminates t on failure.
|
||||
func runExecCmdWithInput(t *testing.T, cmd *exec.Cmd, input string) {
|
||||
t.Logf("Running %s %s", cmd.Path, strings.Join(cmd.Args, " "))
|
||||
consumeAndLogOutputs(t, cmd.Path+" "+strings.Join(cmd.Args, " "), cmd)
|
||||
@@ -166,8 +181,8 @@ func modifyEnviron(env []string, name, value string) []string {
|
||||
return append(res, prefix+value)
|
||||
}
|
||||
|
||||
// fileFromFixture applies edits to inputPath and returns a path to the temporary file.
|
||||
// Callers should defer os.Remove(the_returned_path)
|
||||
// fileFromFixture applies edits to inputPath and returns a path to the temporary file with the edits,
|
||||
// which will be automatically removed when the test completes.
|
||||
func fileFromFixture(t *testing.T, inputPath string, edits map[string]string) string {
|
||||
contents, err := os.ReadFile(inputPath)
|
||||
require.NoError(t, err)
|
||||
@@ -180,6 +195,7 @@ func fileFromFixture(t *testing.T, inputPath string, edits map[string]string) st
|
||||
file, err := os.CreateTemp("", "policy.json")
|
||||
require.NoError(t, err)
|
||||
path := file.Name()
|
||||
t.Cleanup(func() { os.Remove(path) })
|
||||
|
||||
_, err = file.Write(contents)
|
||||
require.NoError(t, err)
|
||||
34
plans/main.fmf
Normal file
34
plans/main.fmf
Normal file
@@ -0,0 +1,34 @@
|
||||
discover:
|
||||
how: fmf
|
||||
execute:
|
||||
how: tmt
|
||||
prepare:
|
||||
- when: distro == centos-stream or distro == rhel
|
||||
how: shell
|
||||
script: |
|
||||
# Install bats
|
||||
# https://bats-core.readthedocs.io/en/stable/installation.html
|
||||
BATS_VERSION=1.12.0
|
||||
curl -L https://github.com/bats-core/bats-core/archive/refs/tags/v"$BATS_VERSION".tar.gz | tar -xz
|
||||
pushd bats-core-"$BATS_VERSION"
|
||||
./install.sh /usr
|
||||
popd
|
||||
rm -rf bats-core-"$BATS_VERSION"
|
||||
order: 10
|
||||
- when: initiator == packit
|
||||
how: shell
|
||||
script: |
|
||||
COPR_REPO_FILE="/etc/yum.repos.d/*podman-next*.repo"
|
||||
if compgen -G $COPR_REPO_FILE > /dev/null; then
|
||||
sed -i -n '/^priority=/!p;$apriority=1' $COPR_REPO_FILE
|
||||
fi
|
||||
dnf -y upgrade --allowerasing
|
||||
order: 20
|
||||
- name: Disable installing everything from srpm
|
||||
how: install
|
||||
exclude:
|
||||
- ".*"
|
||||
- name: Install the main package
|
||||
how: install
|
||||
package:
|
||||
- skopeo-tests
|
||||
@@ -9,25 +9,27 @@
|
||||
|
||||
%global gomodulesmode GO111MODULE=on
|
||||
|
||||
# No btrfs on RHEL
|
||||
# Distro and environment conditionals
|
||||
%if %{defined fedora}
|
||||
# Fedora conditionals
|
||||
%define build_with_btrfs 1
|
||||
%define conditional_epoch 1
|
||||
%if %{?fedora} >= 43
|
||||
%define sequoia 1
|
||||
%endif
|
||||
|
||||
%if %{defined rhel}
|
||||
%else
|
||||
# RHEL conditionals
|
||||
%define conditional_epoch 2
|
||||
%define fips 1
|
||||
%endif
|
||||
|
||||
# Only used in official koji builds
|
||||
# Copr builds set a separate epoch for all environments
|
||||
%if %{defined fedora}
|
||||
%define conditional_epoch 1
|
||||
%else
|
||||
%define conditional_epoch 2
|
||||
# set higher Epoch only for podman-next builds
|
||||
%if %{defined copr_username} && "%{copr_username}" == "rhcontainerbot" && "%{copr_projectname}" == "podman-next"
|
||||
%define next_build 1
|
||||
%endif
|
||||
|
||||
Name: skopeo
|
||||
%if %{defined copr_username}
|
||||
%if %{defined next_build}
|
||||
Epoch: 102
|
||||
%else
|
||||
Epoch: %{conditional_epoch}
|
||||
@@ -62,24 +64,25 @@ BuildRequires: go-rpm-macros
|
||||
%endif
|
||||
BuildRequires: gpgme-devel
|
||||
BuildRequires: libassuan-devel
|
||||
BuildRequires: ostree-devel
|
||||
BuildRequires: glib2-devel
|
||||
BuildRequires: make
|
||||
BuildRequires: shadow-utils-subid-devel
|
||||
BuildRequires: sqlite-devel
|
||||
Requires: containers-common >= 4:1-21
|
||||
%if %{defined sequoia}
|
||||
Requires: podman-sequoia
|
||||
%endif
|
||||
|
||||
%description
|
||||
Command line utility to inspect images and repositories directly on Docker
|
||||
registries without the need to pull them
|
||||
registries without the need to pull them.
|
||||
|
||||
# NOTE: The tests subpackage is only intended for testing and will not be supported
|
||||
# for end-users and/or customers.
|
||||
%package tests
|
||||
Summary: Tests for %{name}
|
||||
Summary: Test dependencies for %{name}
|
||||
|
||||
Requires: %{name} = %{epoch}:%{version}-%{release}
|
||||
%if %{defined fedora}
|
||||
Requires: bats
|
||||
Requires: fakeroot
|
||||
%endif
|
||||
Requires: gnupg
|
||||
Requires: jq
|
||||
Requires: golang
|
||||
@@ -88,11 +91,12 @@ Requires: crun
|
||||
Requires: httpd-tools
|
||||
Requires: openssl
|
||||
Requires: squashfs-tools
|
||||
# bats and fakeroot are not present on RHEL and ELN so they shouldn't be strong deps
|
||||
Recommends: bats
|
||||
Recommends: fakeroot
|
||||
|
||||
%description tests
|
||||
%{summary}
|
||||
|
||||
This package contains system tests for %{name}
|
||||
This package installs system test dependencies for %{name}
|
||||
|
||||
%prep
|
||||
%autosetup -Sgit %{name}-%{version}
|
||||
@@ -115,17 +119,21 @@ CGO_CFLAGS=$(echo $CGO_CFLAGS | sed 's/-specs=\/usr\/lib\/rpm\/redhat\/redhat-an
|
||||
export CGO_CFLAGS="$CGO_CFLAGS -m64 -mtune=generic -fcf-protection=full"
|
||||
%endif
|
||||
|
||||
BASEBUILDTAGS="$(hack/libsubid_tag.sh)"
|
||||
BASEBUILDTAGS="$(hack/libsubid_tag.sh) libsqlite3"
|
||||
%if %{defined build_with_btrfs}
|
||||
export BUILDTAGS="$BASEBUILDTAGS $(hack/btrfs_tag.sh) $(hack/btrfs_installed_tag.sh)"
|
||||
export BUILDTAGS="$BASEBUILDTAGS $(hack/btrfs_installed_tag.sh)"
|
||||
%else
|
||||
export BUILDTAGS="$BASEBUILDTAGS btrfs_noversion exclude_graphdriver_btrfs"
|
||||
export BUILDTAGS="$BASEBUILDTAGS exclude_graphdriver_btrfs"
|
||||
%endif
|
||||
|
||||
%if %{defined fips}
|
||||
export BUILDTAGS="$BUILDTAGS libtrust_openssl"
|
||||
%endif
|
||||
|
||||
%if %{defined sequoia}
|
||||
export BUILDTAGS="$BUILDTAGS containers_image_sequoia"
|
||||
%endif
|
||||
|
||||
# unset LDFLAGS earlier set from set_build_flags
|
||||
LDFLAGS=''
|
||||
|
||||
@@ -138,10 +146,6 @@ make \
|
||||
PREFIX=%{_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/
|
||||
|
||||
#define license tag if not already defined
|
||||
%{!?_licensedir:%global license %doc}
|
||||
|
||||
@@ -162,9 +166,8 @@ cp -pav systemtest/* %{buildroot}/%{_datadir}/%{name}/test/system/
|
||||
%dir %{_datadir}/zsh/site-functions
|
||||
%{_datadir}/zsh/site-functions/_%{name}
|
||||
|
||||
# Only test dependencies installed, no files.
|
||||
%files tests
|
||||
%license LICENSE vendor/modules.txt
|
||||
%{_datadir}/%{name}/test
|
||||
|
||||
%changelog
|
||||
%autochangelog
|
||||
|
||||
@@ -16,4 +16,30 @@ 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
|
||||
|
||||
@@ -136,6 +136,8 @@ END_PUSH
|
||||
# Done pushing. Now try to fetch. From here on we use the --policy option.
|
||||
# The table below lists the paths to fetch, and the expected errors (or
|
||||
# none, if we expect them to pass).
|
||||
#
|
||||
# "Invalid GPG signature" is reported by the gpgme mechanism; "Missing key: $fingerprint" by Sequoia.
|
||||
while read path expected_error; do
|
||||
expected_rc=
|
||||
if [[ -n $expected_error ]]; then
|
||||
@@ -154,7 +156,7 @@ END_PUSH
|
||||
fi
|
||||
done <<END_TESTS
|
||||
/myns/alice:signed
|
||||
/myns/bob:signedbyalice Invalid GPG signature
|
||||
/myns/bob:signedbyalice (Invalid GPG signature|Missing key:)
|
||||
/myns/alice:unsigned Signature for identity \\\\\\\\"localhost:5000/myns/alice:signed\\\\\\\\" is not accepted
|
||||
/myns/carol:latest Running image docker://localhost:5000/myns/carol:latest is rejected by policy.
|
||||
/open/forall:latest
|
||||
|
||||
19
systemtest/tmt/main.fmf
Normal file
19
systemtest/tmt/main.fmf
Normal file
@@ -0,0 +1,19 @@
|
||||
environment:
|
||||
SKOPEO_BINARY: /usr/bin/skopeo
|
||||
|
||||
adjust:
|
||||
- when: initiator != "packit"
|
||||
environment+:
|
||||
RELEASE_TESTING: true
|
||||
|
||||
summary: System test
|
||||
test: |
|
||||
rpm -q containers-common skopeo-tests
|
||||
# If these tests are to be run anywhere else, TMT can fetch
|
||||
# them using the URL and git ref, so git commands should work.
|
||||
make -C $(git rev-parse --show-toplevel) test-system-local
|
||||
require:
|
||||
- git-core
|
||||
- make
|
||||
- skopeo-tests
|
||||
duration: 60m
|
||||
7
vendor/dario.cat/mergo/FUNDING.json
vendored
Normal file
7
vendor/dario.cat/mergo/FUNDING.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"drips": {
|
||||
"ethereum": {
|
||||
"ownedBy": "0x6160020e7102237aC41bdb156e94401692D76930"
|
||||
}
|
||||
}
|
||||
}
|
||||
5
vendor/dario.cat/mergo/README.md
vendored
5
vendor/dario.cat/mergo/README.md
vendored
@@ -85,7 +85,6 @@ Mergo is used by [thousands](https://deps.dev/go/dario.cat%2Fmergo/v1.0.0/depend
|
||||
* [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
|
||||
* [go-micro/go-micro](https://github.com/go-micro/go-micro)
|
||||
* [grafana/loki](https://github.com/grafana/loki)
|
||||
* [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
|
||||
* [masterminds/sprig](github.com/Masterminds/sprig)
|
||||
* [moby/moby](https://github.com/moby/moby)
|
||||
* [slackhq/nebula](https://github.com/slackhq/nebula)
|
||||
@@ -191,10 +190,6 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
Note: if test are failing due missing package, please execute:
|
||||
|
||||
go get gopkg.in/yaml.v3
|
||||
|
||||
### Transformers
|
||||
|
||||
Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`?
|
||||
|
||||
4
vendor/dario.cat/mergo/SECURITY.md
vendored
4
vendor/dario.cat/mergo/SECURITY.md
vendored
@@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 0.3.x | :white_check_mark: |
|
||||
| < 0.3 | :x: |
|
||||
| 1.x.x | :white_check_mark: |
|
||||
| < 1.0 | :x: |
|
||||
|
||||
## Security contact information
|
||||
|
||||
|
||||
2
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
2
vendor/github.com/BurntSushi/toml/README.md
generated
vendored
@@ -3,7 +3,7 @@ reflection interface similar to Go's standard library `json` and `xml` packages.
|
||||
|
||||
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
|
||||
|
||||
Documentation: https://godocs.io/github.com/BurntSushi/toml
|
||||
Documentation: https://pkg.go.dev/github.com/BurntSushi/toml
|
||||
|
||||
See the [releases page](https://github.com/BurntSushi/toml/releases) for a
|
||||
changelog; this information is also in the git tag annotations (e.g. `git show
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user