mirror of
https://github.com/containers/skopeo.git
synced 2025-09-09 10:39:30 +00:00
@@ -142,7 +142,7 @@ When new PRs for [containers/image](https://github.com/containers/image) break `
|
|||||||
- as soon as possible after that, in the skopeo PR, restore the `containers/image` line in `vendor.conf` to use `containers/image:master`
|
- as soon as possible after that, in the skopeo PR, restore the `containers/image` line in `vendor.conf` to use `containers/image:master`
|
||||||
- run `make vendor`
|
- run `make vendor`
|
||||||
- update the skopeo PR with the result, drop the “DO NOT MERGE” marking
|
- update the skopeo PR with the result, drop the “DO NOT MERGE” marking
|
||||||
- after tests complete succcesfully again, merge the skopeo PR
|
- after tests complete successfully again, merge the skopeo PR
|
||||||
|
|
||||||
## Communications
|
## Communications
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ Skopeo works with API V2 container image registries such as [docker.io](https://
|
|||||||
For example you can copy images from one registry to another, without requiring privilege.
|
For example you can copy images from one registry to another, without requiring privilege.
|
||||||
* Inspecting a remote image showing its properties including its layers, without requiring you to pull the image to the host.
|
* Inspecting a remote image showing its properties including its layers, without requiring you to pull the image to the host.
|
||||||
* Deleting an image from an image repository.
|
* Deleting an image from an image repository.
|
||||||
* Syncing an external image repository to an internal registy for air-gapped deployments.
|
* Syncing an external image repository to an internal registry for air-gapped deployments.
|
||||||
* When required by the repository, skopeo can pass the appropriate credentials and certificates for authentication.
|
* When required by the repository, skopeo can pass the appropriate credentials and certificates for authentication.
|
||||||
|
|
||||||
Skopeo operates on the following image and repository types:
|
Skopeo operates on the following image and repository types:
|
||||||
@@ -122,7 +122,7 @@ $ skopeo inspect --config docker://registry.fedoraproject.org/fedora:latest | j
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
#### Show unverifed image's digest
|
#### Show unverified image's digest
|
||||||
```console
|
```console
|
||||||
$ skopeo inspect docker://registry.fedoraproject.org/fedora:latest | jq '.Digest'
|
$ skopeo inspect docker://registry.fedoraproject.org/fedora:latest | jq '.Digest'
|
||||||
"sha256:655721ff613ee766a4126cb5e0d5ae81598e1b0c3bcf7017c36c4d72cb092fe9"
|
"sha256:655721ff613ee766a4126cb5e0d5ae81598e1b0c3bcf7017c36c4d72cb092fe9"
|
||||||
|
@@ -17,8 +17,8 @@ func TestManifestDigest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Error reading manifest
|
// Error reading manifest
|
||||||
out, err := runSkopeo("manifest-digest", "/this/doesnt/exist")
|
out, err := runSkopeo("manifest-digest", "/this/does/not/exist")
|
||||||
assertTestFailed(t, out, err, "/this/doesnt/exist")
|
assertTestFailed(t, out, err, "/this/does/not/exist")
|
||||||
|
|
||||||
// Error computing manifest
|
// Error computing manifest
|
||||||
out, err = runSkopeo("manifest-digest", "fixtures/v2s1-invalid-signatures.manifest.json")
|
out, err = runSkopeo("manifest-digest", "fixtures/v2s1-invalid-signatures.manifest.json")
|
||||||
|
@@ -58,8 +58,8 @@ func TestStandaloneSign(t *testing.T) {
|
|||||||
|
|
||||||
// Error reading manifest
|
// Error reading manifest
|
||||||
out, err := runSkopeo("standalone-sign", "-o", "/dev/null",
|
out, err := runSkopeo("standalone-sign", "-o", "/dev/null",
|
||||||
"/this/doesnt/exist", dockerReference, fixturesTestKeyFingerprint)
|
"/this/does/not/exist", dockerReference, fixturesTestKeyFingerprint)
|
||||||
assertTestFailed(t, out, err, "/this/doesnt/exist")
|
assertTestFailed(t, out, err, "/this/does/not/exist")
|
||||||
|
|
||||||
// Invalid Docker reference
|
// Invalid Docker reference
|
||||||
out, err = runSkopeo("standalone-sign", "-o", "/dev/null",
|
out, err = runSkopeo("standalone-sign", "-o", "/dev/null",
|
||||||
@@ -117,14 +117,14 @@ func TestStandaloneVerify(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Error reading manifest
|
// Error reading manifest
|
||||||
out, err := runSkopeo("standalone-verify", "/this/doesnt/exist",
|
out, err := runSkopeo("standalone-verify", "/this/does/not/exist",
|
||||||
dockerReference, fixturesTestKeyFingerprint, signaturePath)
|
dockerReference, fixturesTestKeyFingerprint, signaturePath)
|
||||||
assertTestFailed(t, out, err, "/this/doesnt/exist")
|
assertTestFailed(t, out, err, "/this/does/not/exist")
|
||||||
|
|
||||||
// Error reading signature
|
// Error reading signature
|
||||||
out, err = runSkopeo("standalone-verify", manifestPath,
|
out, err = runSkopeo("standalone-verify", manifestPath,
|
||||||
dockerReference, fixturesTestKeyFingerprint, "/this/doesnt/exist")
|
dockerReference, fixturesTestKeyFingerprint, "/this/does/not/exist")
|
||||||
assertTestFailed(t, out, err, "/this/doesnt/exist")
|
assertTestFailed(t, out, err, "/this/does/not/exist")
|
||||||
|
|
||||||
// Error verifying signature
|
// Error verifying signature
|
||||||
out, err = runSkopeo("standalone-verify", manifestPath,
|
out, err = runSkopeo("standalone-verify", manifestPath,
|
||||||
@@ -151,8 +151,8 @@ func TestUntrustedSignatureDump(t *testing.T) {
|
|||||||
|
|
||||||
// Error reading manifest
|
// Error reading manifest
|
||||||
out, err := runSkopeo("untrusted-signature-dump-without-verification",
|
out, err := runSkopeo("untrusted-signature-dump-without-verification",
|
||||||
"/this/doesnt/exist")
|
"/this/does/not/exist")
|
||||||
assertTestFailed(t, out, err, "/this/doesnt/exist")
|
assertTestFailed(t, out, err, "/this/does/not/exist")
|
||||||
|
|
||||||
// Error reading signature (input is not a signature)
|
// Error reading signature (input is not a signature)
|
||||||
out, err = runSkopeo("untrusted-signature-dump-without-verification", "fixtures/image.manifest.json")
|
out, err = runSkopeo("untrusted-signature-dump-without-verification", "fixtures/image.manifest.json")
|
||||||
|
@@ -57,7 +57,7 @@ type dockerImageOptions struct {
|
|||||||
shared *sharedImageOptions // May be shared across several imageOptions instances.
|
shared *sharedImageOptions // May be shared across several imageOptions instances.
|
||||||
authFilePath optionalString // Path to a */containers/auth.json (prefixed version to override shared image option).
|
authFilePath optionalString // Path to a */containers/auth.json (prefixed version to override shared image option).
|
||||||
credsOption optionalString // username[:password] for accessing a registry
|
credsOption optionalString // username[:password] for accessing a registry
|
||||||
registryToken optionalString // token to be used directy as a Bearer token when accessing the registry
|
registryToken optionalString // token to be used directly as a Bearer token when accessing the registry
|
||||||
dockerCertPath string // A directory using Docker-like *.{crt,cert,key} files for connecting to a registry or a daemon
|
dockerCertPath string // A directory using Docker-like *.{crt,cert,key} files for connecting to a registry or a daemon
|
||||||
tlsVerify optionalBool // Require HTTPS and verify certificates (for docker: and docker-daemon:)
|
tlsVerify optionalBool // Require HTTPS and verify certificates (for docker: and docker-daemon:)
|
||||||
noCreds bool // Access the registry anonymously
|
noCreds bool // Access the registry anonymously
|
||||||
|
@@ -196,7 +196,7 @@ _skopeo_logout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_skopeo_skopeo() {
|
_skopeo_skopeo() {
|
||||||
# XXX: Changes here need to be refleceted in the manually expanded
|
# XXX: Changes here need to be reflected in the manually expanded
|
||||||
# string in the `case` statement below as well.
|
# string in the `case` statement below as well.
|
||||||
local options_with_args="
|
local options_with_args="
|
||||||
--policy
|
--policy
|
||||||
@@ -230,7 +230,7 @@ _skopeo_skopeo() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
# XXX: Changes here need to be refleceted in $options_with_args as well.
|
# XXX: Changes here need to be reflected in $options_with_args as well.
|
||||||
--policy|--registries.d|--override-arch|--override-os|--override-variant|--command-timeout)
|
--policy|--registries.d|--override-arch|--override-os|--override-variant|--command-timeout)
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
@@ -179,7 +179,7 @@ This will copy the following images:
|
|||||||
For the registry `registry.example.com`, the "john"/"this is a secret" credentials are used, with server TLS certificates located at `/home/john/certs`.
|
For the registry `registry.example.com`, the "john"/"this is a secret" credentials are used, with server TLS certificates located at `/home/john/certs`.
|
||||||
|
|
||||||
TLS verification is normally enabled, and it can be disabled setting `tls-verify` to `false`.
|
TLS verification is normally enabled, and it can be disabled setting `tls-verify` to `false`.
|
||||||
In the above example, TLS verification is enabled for `reigstry.example.com`, while is
|
In the above example, TLS verification is enabled for `registry.example.com`, while is
|
||||||
disabled for `quay.io`.
|
disabled for `quay.io`.
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
|
@@ -102,7 +102,7 @@ func (s *SkopeoSuite) TestCopyWithLocalAuth(c *check.C) {
|
|||||||
// copy to private registry using local authentication
|
// copy to private registry using local authentication
|
||||||
imageName := fmt.Sprintf("docker://%s/busybox:mine", s.regV2WithAuth.url)
|
imageName := fmt.Sprintf("docker://%s/busybox:mine", s.regV2WithAuth.url)
|
||||||
assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "docker://docker.io/library/busybox:latest", imageName)
|
assertSkopeoSucceeds(c, "", "copy", "--dest-tls-verify=false", "docker://docker.io/library/busybox:latest", imageName)
|
||||||
// inspec from private registry
|
// inspect from private registry
|
||||||
assertSkopeoSucceeds(c, "", "inspect", "--tls-verify=false", imageName)
|
assertSkopeoSucceeds(c, "", "inspect", "--tls-verify=false", imageName)
|
||||||
// logout from the registry
|
// logout from the registry
|
||||||
wanted = fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url)
|
wanted = fmt.Sprintf("^Removed login credentials for %s\n$", s.regV2WithAuth.url)
|
||||||
|
@@ -464,16 +464,16 @@ func (s *CopySuite) TestCopyWithManifestListStorageDigestMultipleArchesTagAndDig
|
|||||||
c.Assert(image5.Architecture, check.Equals, "arm64")
|
c.Assert(image5.Architecture, check.Equals, "arm64")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CopySuite) TestCopyFailsWhenImageOSDoesntMatchRuntimeOS(c *check.C) {
|
func (s *CopySuite) TestCopyFailsWhenImageOSDoesNotMatchRuntimeOS(c *check.C) {
|
||||||
storage, err := ioutil.TempDir("", "copy-fails-image-doesnt-match-runtime")
|
storage, err := ioutil.TempDir("", "copy-fails-image-does-not-match-runtime")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
defer os.RemoveAll(storage)
|
defer os.RemoveAll(storage)
|
||||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||||
assertSkopeoFails(c, `.*no image found in manifest list for architecture .*, variant .*, OS .*`, "copy", knownWindowsOnlyImage, "containers-storage:"+storage+"test")
|
assertSkopeoFails(c, `.*no image found in manifest list for architecture .*, variant .*, OS .*`, "copy", knownWindowsOnlyImage, "containers-storage:"+storage+"test")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CopySuite) TestCopySucceedsWhenImageDoesntMatchRuntimeButWeOverride(c *check.C) {
|
func (s *CopySuite) TestCopySucceedsWhenImageDoesNotMatchRuntimeButWeOverride(c *check.C) {
|
||||||
storage, err := ioutil.TempDir("", "copy-succeeds-image-doesnt-match-runtime-but-override")
|
storage, err := ioutil.TempDir("", "copy-succeeds-image-does-not-match-runtime-but-override")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
defer os.RemoveAll(storage)
|
defer os.RemoveAll(storage)
|
||||||
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
storage = fmt.Sprintf("[vfs@%s/root+%s/runroot]", storage, storage)
|
||||||
@@ -863,7 +863,7 @@ func (s *CopySuite) TestCopyDirSignatures(c *check.C) {
|
|||||||
assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:armfh", topDirDest+"/dir1")
|
assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:armfh", topDirDest+"/dir1")
|
||||||
assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:s390x", topDirDest+"/dir2")
|
assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:s390x", topDirDest+"/dir2")
|
||||||
|
|
||||||
// Sign the images. By coping fom a topDirDest/dirN, also test that non-/restricted paths
|
// Sign the images. By coping from a topDirDest/dirN, also test that non-/restricted paths
|
||||||
// use the dir:"" default of insecureAcceptAnything.
|
// use the dir:"" default of insecureAcceptAnything.
|
||||||
// (For signing, we must push to atomic: to get a Docker identity to use in the signature.)
|
// (For signing, we must push to atomic: to get a Docker identity to use in the signature.)
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "--sign-by", "personal@example.com", topDirDest+"/dir1", "atomic:localhost:5000/myns/personal:dirstaging")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "--sign-by", "personal@example.com", topDirDest+"/dir1", "atomic:localhost:5000/myns/personal:dirstaging")
|
||||||
@@ -1147,7 +1147,7 @@ func (s *SkopeoSuite) TestCopySrcAndDestWithAuth(c *check.C) {
|
|||||||
assertSkopeoSucceeds(c, "", "--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))
|
assertSkopeoSucceeds(c, "", "--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))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CopySuite) TestCopyNoPanicOnHTTPResponseWOTLSVerifyFalse(c *check.C) {
|
func (s *CopySuite) TestCopyNoPanicOnHTTPResponseWithoutTLSVerifyFalse(c *check.C) {
|
||||||
const ourRegistry = "docker://" + v2DockerRegistryURL + "/"
|
const ourRegistry = "docker://" + v2DockerRegistryURL + "/"
|
||||||
|
|
||||||
// dir:test isn't created beforehand just because we already know this could
|
// dir:test isn't created beforehand just because we already know this could
|
||||||
|
@@ -122,7 +122,7 @@ func (s *SyncSuite) TestDocker2DirTaggedAll(c *check.C) {
|
|||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
// FIXME: It would be nice to use one of the local Docker registries instead of neeeding an Internet connection.
|
// FIXME: It would be nice to use one of the local Docker registries instead of needing an Internet connection.
|
||||||
image := "busybox:latest"
|
image := "busybox:latest"
|
||||||
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
imageRef, err := docker.ParseReference(fmt.Sprintf("//%s", image))
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
@@ -578,7 +578,7 @@ func (s *SyncSuite) TestFailsWithDockerSourceUnauthorized(c *check.C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SyncSuite) TestFailsWithDockerSourceNotExisting(c *check.C) {
|
func (s *SyncSuite) TestFailsWithDockerSourceNotExisting(c *check.C) {
|
||||||
repo := path.Join(v2DockerRegistryURL, "imagedoesdotexist")
|
repo := path.Join(v2DockerRegistryURL, "imagedoesnotexist")
|
||||||
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
|
tmpDir, err := ioutil.TempDir("", "skopeo-sync-test")
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
@@ -11,7 +11,7 @@ Travis workflow has 3 major pieces:
|
|||||||
- `image-build-push` - build and push container images with several Travis jobs running in parallel to build images for several architectures (linux/amd64, linux/s390x, linux/ppc64le). Build part is done for each PR, push part is executed only in case of cron job or master branch update.
|
- `image-build-push` - build and push container images with several Travis jobs running in parallel to build images for several architectures (linux/amd64, linux/s390x, linux/ppc64le). Build part is done for each PR, push part is executed only in case of cron job or master branch update.
|
||||||
- `manifest-multiarch-push` - create and push image manifests, which consists of architecture specific images from previous step. Executed only in case of cron job or master branch update.
|
- `manifest-multiarch-push` - create and push image manifests, which consists of architecture specific images from previous step. Executed only in case of cron job or master branch update.
|
||||||
|
|
||||||
## Ways to have full worklow run
|
## Ways to have full workflow run
|
||||||
- [cron job](https://docs.travis-ci.com/user/cron-jobs/#adding-cron-jobs)
|
- [cron job](https://docs.travis-ci.com/user/cron-jobs/#adding-cron-jobs)
|
||||||
- Trigger build from Travis CI
|
- Trigger build from Travis CI
|
||||||
- Update code in master branch
|
- Update code in master branch
|
||||||
@@ -26,7 +26,7 @@ Several environment variables are used to customize image names and keep private
|
|||||||
- `CONTAINERS_QUAY_USERNAME` and `CONTAINERS_QUAY_PASSWORD` are credentials to push images to `quay.io/containers/skopeo` repos, and require the credentials to have write permissions. These variables should be specified in [Travis](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings).
|
- `CONTAINERS_QUAY_USERNAME` and `CONTAINERS_QUAY_PASSWORD` are credentials to push images to `quay.io/containers/skopeo` repos, and require the credentials to have write permissions. These variables should be specified in [Travis](https://docs.travis-ci.com/user/environment-variables/#defining-variables-in-repository-settings).
|
||||||
|
|
||||||
Variables in .travis.yml
|
Variables in .travis.yml
|
||||||
- `MULTIARCH_MANIFEST_ARCHITECTURES` is a list with architecture shortnames, to apprear in final multiarch manifest. The values should fit to architectures used in the `image-build-push` Travis step.
|
- `MULTIARCH_MANIFEST_ARCHITECTURES` is a list with architecture shortnames, to appear in final multiarch manifest. The values should fit to architectures used in the `image-build-push` Travis step.
|
||||||
- `STABLE_IMAGE`, `EXTRA_STABLE_IMAGE` are image names to publish stable Skopeo.
|
- `STABLE_IMAGE`, `EXTRA_STABLE_IMAGE` are image names to publish stable Skopeo.
|
||||||
- `UPSTREAM_IMAGE` is an image name to publish upstream Skopeo.
|
- `UPSTREAM_IMAGE` is an image name to publish upstream Skopeo.
|
||||||
|
|
||||||
|
@@ -194,7 +194,7 @@ function expect_output() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# This is a multi-line message, which may in turn contain multi-line
|
# This is a multi-line message, which may in turn contain multi-line
|
||||||
# output, so let's format it ourself, readably
|
# output, so let's format it ourselves, readably
|
||||||
local -a actual_split
|
local -a actual_split
|
||||||
readarray -t actual_split <<<"$actual"
|
readarray -t actual_split <<<"$actual"
|
||||||
printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2
|
printf "#/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n" >&2
|
||||||
|
Reference in New Issue
Block a user