mirror of
https://github.com/containers/skopeo.git
synced 2025-07-13 14:34:44 +00:00
Merge pull request #337 from mtrmac/docker-push-to-tag
Update name/tag embedded into schema1 manifests
This commit is contained in:
commit
105be6a0ab
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -103,9 +104,9 @@ func (s *CopySuite) TestCopySimpleAtomicRegistry(c *check.C) {
|
|||||||
assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:amd64", "dir:"+dir1)
|
assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:amd64", "dir:"+dir1)
|
||||||
// "push": dir: → atomic:
|
// "push": dir: → atomic:
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--debug", "copy", "dir:"+dir1, "atomic:localhost:5000/myns/unsigned:unsigned")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--debug", "copy", "dir:"+dir1, "atomic:localhost:5000/myns/unsigned:unsigned")
|
||||||
// The result of pushing and pulling is an unmodified image.
|
// The result of pushing and pulling is an equivalent image, except for schema1 embedded names.
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/unsigned:unsigned", "dir:"+dir2)
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/unsigned:unsigned", "dir:"+dir2)
|
||||||
destructiveCheckDirImagesAreEqual(c, dir1, dir2)
|
assertSchema1DirImagesAreEqualExceptNames(c, dir1, "estesp/busybox:amd64", dir2, "myns/unsigned:unsigned")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The most basic (skopeo copy) use:
|
// The most basic (skopeo copy) use:
|
||||||
@ -139,11 +140,10 @@ func (s *CopySuite) TestCopySimple(c *check.C) {
|
|||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether dir: images in dir1 and dir2 are equal.
|
// Check whether dir: images in dir1 and dir2 are equal, ignoring schema1 signatures.
|
||||||
// WARNING: This modifies the contents of dir1 and dir2!
|
func assertDirImagesAreEqual(c *check.C, dir1, dir2 string) {
|
||||||
func destructiveCheckDirImagesAreEqual(c *check.C, dir1, dir2 string) {
|
|
||||||
// The manifests may have different JWS signatures; so, compare the manifests by digests, which
|
// The manifests may have different JWS signatures; so, compare the manifests by digests, which
|
||||||
// strips the signatures, and remove them, comparing the rest file by file.
|
// strips the signatures.
|
||||||
digests := []digest.Digest{}
|
digests := []digest.Digest{}
|
||||||
for _, dir := range []string{dir1, dir2} {
|
for _, dir := range []string{dir1, dir2} {
|
||||||
manifestPath := filepath.Join(dir, "manifest.json")
|
manifestPath := filepath.Join(dir, "manifest.json")
|
||||||
@ -152,12 +152,37 @@ func destructiveCheckDirImagesAreEqual(c *check.C, dir1, dir2 string) {
|
|||||||
digest, err := manifest.Digest(m)
|
digest, err := manifest.Digest(m)
|
||||||
c.Assert(err, check.IsNil)
|
c.Assert(err, check.IsNil)
|
||||||
digests = append(digests, digest)
|
digests = append(digests, digest)
|
||||||
err = os.Remove(manifestPath)
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Logf("Manifest file %s (digest %s) removed", manifestPath, digest)
|
|
||||||
}
|
}
|
||||||
c.Assert(digests[0], check.Equals, digests[1])
|
c.Assert(digests[0], check.Equals, digests[1])
|
||||||
out := combinedOutputOfCommand(c, "diff", "-urN", dir1, dir2)
|
// Then compare the rest file by file.
|
||||||
|
out := combinedOutputOfCommand(c, "diff", "-urN", "-x", "manifest.json", dir1, dir2)
|
||||||
|
c.Assert(out, check.Equals, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether schema1 dir: images in dir1 and dir2 are equal, ignoring schema1 signatures and the embedded path/tag values, which should have the expected values.
|
||||||
|
func assertSchema1DirImagesAreEqualExceptNames(c *check.C, dir1, ref1, dir2, ref2 string) {
|
||||||
|
// The manifests may have different JWS signatures and names; so, unmarshal and delete these elements.
|
||||||
|
manifests := []map[string]interface{}{}
|
||||||
|
for dir, ref := range map[string]string{dir1: ref1, dir2: ref2} {
|
||||||
|
manifestPath := filepath.Join(dir, "manifest.json")
|
||||||
|
m, err := ioutil.ReadFile(manifestPath)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
data := map[string]interface{}{}
|
||||||
|
err = json.Unmarshal(m, &data)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(data["schemaVersion"], check.Equals, float64(1))
|
||||||
|
colon := strings.LastIndex(ref, ":")
|
||||||
|
c.Assert(colon, check.Not(check.Equals), -1)
|
||||||
|
c.Assert(data["name"], check.Equals, ref[:colon])
|
||||||
|
c.Assert(data["tag"], check.Equals, ref[colon+1:])
|
||||||
|
for _, key := range []string{"signatures", "name", "tag"} {
|
||||||
|
delete(data, key)
|
||||||
|
}
|
||||||
|
manifests = append(manifests, data)
|
||||||
|
}
|
||||||
|
c.Assert(manifests[0], check.DeepEquals, manifests[1])
|
||||||
|
// Then compare the rest file by file.
|
||||||
|
out := combinedOutputOfCommand(c, "diff", "-urN", "-x", "manifest.json", dir1, dir2)
|
||||||
c.Assert(out, check.Equals, "")
|
c.Assert(out, check.Equals, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +201,7 @@ func (s *CopySuite) TestCopyStreaming(c *check.C) {
|
|||||||
// Compare (copies of) the original and the copy:
|
// Compare (copies of) the original and the copy:
|
||||||
assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:amd64", "dir:"+dir1)
|
assertSkopeoSucceeds(c, "", "copy", "docker://estesp/busybox:amd64", "dir:"+dir1)
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/unsigned:streaming", "dir:"+dir2)
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/unsigned:streaming", "dir:"+dir2)
|
||||||
destructiveCheckDirImagesAreEqual(c, dir1, dir2)
|
assertSchema1DirImagesAreEqualExceptNames(c, dir1, "estesp/busybox:amd64", dir2, "myns/unsigned:streaming")
|
||||||
// FIXME: Also check pushing to docker://
|
// FIXME: Also check pushing to docker://
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,34 +278,34 @@ func (s *CopySuite) TestCopySignatures(c *check.C) {
|
|||||||
|
|
||||||
// type: signedBy
|
// type: signedBy
|
||||||
// Sign the images
|
// Sign the images
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--sign-by", "personal@example.com", "docker://busybox:1.23", "atomic:localhost:5000/myns/personal:personal")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--sign-by", "personal@example.com", "docker://busybox:1.26", "atomic:localhost:5006/myns/personal:personal")
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--sign-by", "official@example.com", "docker://busybox:1.23.2", "atomic:localhost:5000/myns/official:official")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--sign-by", "official@example.com", "docker://busybox:1.26.1", "atomic:localhost:5006/myns/official:official")
|
||||||
// Verify that we can pull them
|
// Verify that we can pull them
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5000/myns/personal:personal", dirDest)
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/personal:personal", dirDest)
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5000/myns/official:official", dirDest)
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/official:official", dirDest)
|
||||||
// Verify that mis-signed images are rejected
|
// Verify that mis-signed images are rejected
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/personal:personal", "atomic:localhost:5000/myns/official:attack")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5006/myns/personal:personal", "atomic:localhost:5006/myns/official:attack")
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/personal:attack")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5006/myns/official:official", "atomic:localhost:5006/myns/personal:attack")
|
||||||
assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*",
|
assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*",
|
||||||
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5000/myns/personal:attack", dirDest)
|
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/personal:attack", dirDest)
|
||||||
assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*",
|
assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*",
|
||||||
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5000/myns/official:attack", dirDest)
|
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/official:attack", dirDest)
|
||||||
|
|
||||||
// Verify that signed identity is verified.
|
// Verify that signed identity is verified.
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/naming:test1")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5006/myns/official:official", "atomic:localhost:5006/myns/naming:test1")
|
||||||
assertSkopeoFails(c, ".*Source image rejected: Signature for identity localhost:5000/myns/official:official is not accepted.*",
|
assertSkopeoFails(c, ".*Source image rejected: Signature for identity localhost:5006/myns/official:official is not accepted.*",
|
||||||
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5000/myns/naming:test1", dirDest)
|
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/naming:test1", dirDest)
|
||||||
// signedIdentity works
|
// signedIdentity works
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/naming:naming")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5006/myns/official:official", "atomic:localhost:5006/myns/naming:naming")
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5000/myns/naming:naming", dirDest)
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/naming:naming", dirDest)
|
||||||
|
|
||||||
// Verify that cosigning requirements are enforced
|
// Verify that cosigning requirements are enforced
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/cosigned:cosigned")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "atomic:localhost:5006/myns/official:official", "atomic:localhost:5006/myns/cosigned:cosigned")
|
||||||
assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*",
|
assertSkopeoFails(c, ".*Source image rejected: Invalid GPG signature.*",
|
||||||
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5000/myns/cosigned:cosigned", dirDest)
|
"--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/cosigned:cosigned", dirDest)
|
||||||
|
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--sign-by", "personal@example.com", "atomic:localhost:5000/myns/official:official", "atomic:localhost:5000/myns/cosigned:cosigned")
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--sign-by", "personal@example.com", "atomic:localhost:5006/myns/official:official", "atomic:localhost:5006/myns/cosigned:cosigned")
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5000/myns/cosigned:cosigned", dirDest)
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "copy", "atomic:localhost:5006/myns/cosigned:cosigned", dirDest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --policy copy for dir: sources
|
// --policy copy for dir: sources
|
||||||
@ -342,10 +367,10 @@ func (s *CopySuite) TestCopyCompression(c *check.C) {
|
|||||||
defer os.RemoveAll(topDir)
|
defer os.RemoveAll(topDir)
|
||||||
|
|
||||||
for i, t := range []struct{ fixture, remote string }{
|
for i, t := range []struct{ fixture, remote string }{
|
||||||
//{"uncompressed-image-s1", "docker://" + v2DockerRegistryURL + "/compression/compression:s1"}, // FIXME: depends on push to tag working
|
{"uncompressed-image-s1", "docker://" + v2DockerRegistryURL + "/compression/compression:s1"},
|
||||||
//{"uncompressed-image-s2", "docker://" + v2DockerRegistryURL + "/compression/compression:s2"}, // FIXME: depends on push to tag working
|
{"uncompressed-image-s2", "docker://" + v2DockerRegistryURL + "/compression/compression:s2"},
|
||||||
{"uncompressed-image-s1", "atomic:localhost:5000/myns/compression:s1"},
|
{"uncompressed-image-s1", "atomic:localhost:5000/myns/compression:s1"},
|
||||||
//{"uncompressed-image-s2", "atomic:localhost:5000/myns/compression:s2"}, // FIXME: The unresolved "MANIFEST_UNKNOWN"/"unexpected end of JSON input" failure
|
{"uncompressed-image-s2", "atomic:localhost:5000/myns/compression:s2"},
|
||||||
} {
|
} {
|
||||||
dir := filepath.Join(topDir, fmt.Sprintf("case%d", i))
|
dir := filepath.Join(topDir, fmt.Sprintf("case%d", i))
|
||||||
err := os.MkdirAll(dir, 0755)
|
err := os.MkdirAll(dir, 0755)
|
||||||
@ -501,7 +526,7 @@ func (s *CopySuite) TestCopyAtomicExtension(c *check.C) {
|
|||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir,
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir,
|
||||||
"copy", "docker://localhost:5000/myns/extension:atomic", dirDest+"/dirAD")
|
"copy", "docker://localhost:5000/myns/extension:atomic", dirDest+"/dirAD")
|
||||||
// Both access methods result in the same data.
|
// Both access methods result in the same data.
|
||||||
destructiveCheckDirImagesAreEqual(c, filepath.Join(topDir, "dirAA"), filepath.Join(topDir, "dirAD"))
|
assertDirImagesAreEqual(c, filepath.Join(topDir, "dirAA"), filepath.Join(topDir, "dirAD"))
|
||||||
|
|
||||||
// Get another image (different so that they don't share signatures, and sign it using docker://)
|
// Get another image (different so that they don't share signatures, and sign it using docker://)
|
||||||
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir,
|
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--registries.d", registriesDir,
|
||||||
@ -514,7 +539,7 @@ func (s *CopySuite) TestCopyAtomicExtension(c *check.C) {
|
|||||||
assertSkopeoSucceeds(c, "", "--debug", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir,
|
assertSkopeoSucceeds(c, "", "--debug", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir,
|
||||||
"copy", "docker://localhost:5000/myns/extension:extension", dirDest+"/dirDD")
|
"copy", "docker://localhost:5000/myns/extension:extension", dirDest+"/dirDD")
|
||||||
// Both access methods result in the same data.
|
// Both access methods result in the same data.
|
||||||
destructiveCheckDirImagesAreEqual(c, filepath.Join(topDir, "dirDA"), filepath.Join(topDir, "dirDD"))
|
assertDirImagesAreEqual(c, filepath.Join(topDir, "dirDA"), filepath.Join(topDir, "dirDD"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SkopeoSuite) TestCopySrcWithAuth(c *check.C) {
|
func (s *SkopeoSuite) TestCopySrcWithAuth(c *check.C) {
|
||||||
@ -547,14 +572,8 @@ func (s *CopySuite) TestCopySchemaConversion(c *check.C) {
|
|||||||
// Test conversion / schema autodetection both for the OpenShift embedded registry…
|
// Test conversion / schema autodetection both for the OpenShift embedded registry…
|
||||||
s.testCopySchemaConversionRegistries(c, "docker://localhost:5005/myns/schema1", "docker://localhost:5006/myns/schema2")
|
s.testCopySchemaConversionRegistries(c, "docker://localhost:5005/myns/schema1", "docker://localhost:5006/myns/schema2")
|
||||||
// … and for various docker/distribution registry versions.
|
// … and for various docker/distribution registry versions.
|
||||||
if false {
|
|
||||||
// FIXME: This does not currently work, because the schema1-only docker/distribution registry we have (unlike newer versions)
|
|
||||||
// enforces that a schema1 manifest contains a matching tag field. Our _s2→s1 conversion_ code does set the tag correctly,
|
|
||||||
// but a mere copy of schema1→schema1 changing the tag does not update the manifest.
|
|
||||||
// So, enabling this test results in a successful schema2→schema1 conversion, followed by a schema1→schema1 copy failure.
|
|
||||||
s.testCopySchemaConversionRegistries(c, "docker://"+v2s1DockerRegistryURL+"/schema1", "docker://"+v2DockerRegistryURL+"/schema2")
|
s.testCopySchemaConversionRegistries(c, "docker://"+v2s1DockerRegistryURL+"/schema1", "docker://"+v2DockerRegistryURL+"/schema2")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (s *CopySuite) testCopySchemaConversionRegistries(c *check.C, schema1Registry, schema2Registry string) {
|
func (s *CopySuite) testCopySchemaConversionRegistries(c *check.C, schema1Registry, schema2Registry string) {
|
||||||
topDir, err := ioutil.TempDir("", "schema-conversion")
|
topDir, err := ioutil.TempDir("", "schema-conversion")
|
||||||
|
@ -45,46 +45,46 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"atomic": {
|
"atomic": {
|
||||||
"localhost:5000/myns/personal": [
|
"localhost:5006/myns/personal": [
|
||||||
{
|
{
|
||||||
"type": "signedBy",
|
"type": "signedBy",
|
||||||
"keyType": "GPGKeys",
|
"keyType": "GPGKeys",
|
||||||
"keyPath": "@keydir@/personal-pubkey.gpg"
|
"keyPath": "@keydir@/personal-pubkey.gpg"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"localhost:5000/myns/official": [
|
"localhost:5006/myns/official": [
|
||||||
{
|
{
|
||||||
"type": "signedBy",
|
"type": "signedBy",
|
||||||
"keyType": "GPGKeys",
|
"keyType": "GPGKeys",
|
||||||
"keyPath": "@keydir@/official-pubkey.gpg"
|
"keyPath": "@keydir@/official-pubkey.gpg"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"localhost:5000/myns/naming:test1": [
|
"localhost:5006/myns/naming:test1": [
|
||||||
{
|
{
|
||||||
"type": "signedBy",
|
"type": "signedBy",
|
||||||
"keyType": "GPGKeys",
|
"keyType": "GPGKeys",
|
||||||
"keyPath": "@keydir@/official-pubkey.gpg"
|
"keyPath": "@keydir@/official-pubkey.gpg"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"localhost:5000/myns/naming:naming": [
|
"localhost:5006/myns/naming:naming": [
|
||||||
{
|
{
|
||||||
"type": "signedBy",
|
"type": "signedBy",
|
||||||
"keyType": "GPGKeys",
|
"keyType": "GPGKeys",
|
||||||
"keyPath": "@keydir@/official-pubkey.gpg",
|
"keyPath": "@keydir@/official-pubkey.gpg",
|
||||||
"signedIdentity": {
|
"signedIdentity": {
|
||||||
"type": "exactRepository",
|
"type": "exactRepository",
|
||||||
"dockerRepository": "localhost:5000/myns/official"
|
"dockerRepository": "localhost:5006/myns/official"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"localhost:5000/myns/cosigned:cosigned": [
|
"localhost:5006/myns/cosigned:cosigned": [
|
||||||
{
|
{
|
||||||
"type": "signedBy",
|
"type": "signedBy",
|
||||||
"keyType": "GPGKeys",
|
"keyType": "GPGKeys",
|
||||||
"keyPath": "@keydir@/official-pubkey.gpg",
|
"keyPath": "@keydir@/official-pubkey.gpg",
|
||||||
"signedIdentity": {
|
"signedIdentity": {
|
||||||
"type": "exactRepository",
|
"type": "exactRepository",
|
||||||
"dockerRepository": "localhost:5000/myns/official"
|
"dockerRepository": "localhost:5006/myns/official"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -109,6 +109,7 @@ http:
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(binary, confPath)
|
cmd := exec.Command(binary, confPath)
|
||||||
|
consumeAndLogOutputs(c, fmt.Sprintf("registry-%s", url), cmd)
|
||||||
if err := cmd.Start(); err != nil {
|
if err := cmd.Start(); err != nil {
|
||||||
os.RemoveAll(tmp)
|
os.RemoveAll(tmp)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
22
vendor/github.com/containers/image/copy/copy.go
generated
vendored
22
vendor/github.com/containers/image/copy/copy.go
generated
vendored
@ -183,6 +183,10 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
|
|||||||
manifestUpdates := types.ManifestUpdateOptions{}
|
manifestUpdates := types.ManifestUpdateOptions{}
|
||||||
manifestUpdates.InformationOnly.Destination = dest
|
manifestUpdates.InformationOnly.Destination = dest
|
||||||
|
|
||||||
|
if err := updateEmbeddedDockerReference(&manifestUpdates, dest, src, canModifyManifest); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// We compute preferredManifestMIMEType only to show it in error messages.
|
// We compute preferredManifestMIMEType only to show it in error messages.
|
||||||
// Without having to add this context in an error message, we would be happy enough to know only that no conversion is needed.
|
// Without having to add this context in an error message, we would be happy enough to know only that no conversion is needed.
|
||||||
preferredManifestMIMEType, otherManifestMIMETypeCandidates, err := determineManifestConversion(&manifestUpdates, src, destSupportedManifestMIMETypes, canModifyManifest)
|
preferredManifestMIMEType, otherManifestMIMETypeCandidates, err := determineManifestConversion(&manifestUpdates, src, destSupportedManifestMIMETypes, canModifyManifest)
|
||||||
@ -273,6 +277,24 @@ func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageRe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateEmbeddedDockerReference handles the Docker reference embedded in Docker schema1 manifests.
|
||||||
|
func updateEmbeddedDockerReference(manifestUpdates *types.ManifestUpdateOptions, dest types.ImageDestination, src types.Image, canModifyManifest bool) error {
|
||||||
|
destRef := dest.Reference().DockerReference()
|
||||||
|
if destRef == nil {
|
||||||
|
return nil // Destination does not care about Docker references
|
||||||
|
}
|
||||||
|
if !src.EmbeddedDockerReferenceConflicts(destRef) {
|
||||||
|
return nil // No reference embedded in the manifest, or it matches destRef already.
|
||||||
|
}
|
||||||
|
|
||||||
|
if !canModifyManifest {
|
||||||
|
return errors.Errorf("Copying a schema1 image with an embedded Docker reference to %s (Docker reference %s) would invalidate existing signatures. Explicitly enable signature removal to proceed anyway",
|
||||||
|
transports.ImageName(dest.Reference()), destRef.String())
|
||||||
|
}
|
||||||
|
manifestUpdates.EmbeddedDockerReference = destRef
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// copyLayers copies layers from src/rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest.
|
// copyLayers copies layers from src/rawSource to dest, using and updating ic.manifestUpdates if necessary and ic.canModifyManifest.
|
||||||
func (ic *imageCopier) copyLayers() error {
|
func (ic *imageCopier) copyLayers() error {
|
||||||
srcInfos := ic.src.LayerInfos()
|
srcInfos := ic.src.LayerInfos()
|
||||||
|
29
vendor/github.com/containers/image/image/docker_schema1.go
generated
vendored
29
vendor/github.com/containers/image/image/docker_schema1.go
generated
vendored
@ -135,6 +135,27 @@ func (m *manifestSchema1) LayerInfos() []types.BlobInfo {
|
|||||||
return layers
|
return layers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
// It returns false if the manifest does not embed a Docker reference.
|
||||||
|
// (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.)
|
||||||
|
func (m *manifestSchema1) EmbeddedDockerReferenceConflicts(ref reference.Named) bool {
|
||||||
|
// This is a bit convoluted: We can’t just have a "get embedded docker reference" method
|
||||||
|
// and have the “does it conflict” logic in the generic copy code, because the manifest does not actually
|
||||||
|
// embed a full docker/distribution reference, but only the repo name and tag (without the host name).
|
||||||
|
// So we would have to provide a “return repo without host name, and tag” getter for the generic code,
|
||||||
|
// which would be very awkward. Instead, we do the matching here in schema1-specific code, and all the
|
||||||
|
// generic copy code needs to know about is reference.Named and that a manifest may need updating
|
||||||
|
// for some destinations.
|
||||||
|
name := reference.Path(ref)
|
||||||
|
var tag string
|
||||||
|
if tagged, isTagged := ref.(reference.NamedTagged); isTagged {
|
||||||
|
tag = tagged.Tag()
|
||||||
|
} else {
|
||||||
|
tag = ""
|
||||||
|
}
|
||||||
|
return m.Name != name || m.Tag != tag
|
||||||
|
}
|
||||||
|
|
||||||
func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
v1 := &v1Image{}
|
v1 := &v1Image{}
|
||||||
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), v1); err != nil {
|
if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), v1); err != nil {
|
||||||
@ -173,6 +194,14 @@ func (m *manifestSchema1) UpdatedImage(options types.ManifestUpdateOptions) (typ
|
|||||||
copy.FSLayers[(len(options.LayerInfos)-1)-i].BlobSum = info.Digest
|
copy.FSLayers[(len(options.LayerInfos)-1)-i].BlobSum = info.Digest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if options.EmbeddedDockerReference != nil {
|
||||||
|
copy.Name = reference.Path(options.EmbeddedDockerReference)
|
||||||
|
if tagged, isTagged := options.EmbeddedDockerReference.(reference.NamedTagged); isTagged {
|
||||||
|
copy.Tag = tagged.Tag()
|
||||||
|
} else {
|
||||||
|
copy.Tag = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch options.ManifestMIMEType {
|
switch options.ManifestMIMEType {
|
||||||
case "": // No conversion, OK
|
case "": // No conversion, OK
|
||||||
|
9
vendor/github.com/containers/image/image/docker_schema2.go
generated
vendored
9
vendor/github.com/containers/image/image/docker_schema2.go
generated
vendored
@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
@ -140,6 +141,13 @@ func (m *manifestSchema2) LayerInfos() []types.BlobInfo {
|
|||||||
return blobs
|
return blobs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
// It returns false if the manifest does not embed a Docker reference.
|
||||||
|
// (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.)
|
||||||
|
func (m *manifestSchema2) EmbeddedDockerReferenceConflicts(ref reference.Named) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (m *manifestSchema2) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
func (m *manifestSchema2) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
config, err := m.ConfigBlob()
|
config, err := m.ConfigBlob()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -180,6 +188,7 @@ func (m *manifestSchema2) UpdatedImage(options types.ManifestUpdateOptions) (typ
|
|||||||
copy.LayersDescriptors[i].URLs = info.URLs
|
copy.LayersDescriptors[i].URLs = info.URLs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1 to schema2, but we really don't care.
|
||||||
|
|
||||||
switch options.ManifestMIMEType {
|
switch options.ManifestMIMEType {
|
||||||
case "": // No conversion, OK
|
case "": // No conversion, OK
|
||||||
|
5
vendor/github.com/containers/image/image/manifest.go
generated
vendored
5
vendor/github.com/containers/image/image/manifest.go
generated
vendored
@ -3,6 +3,7 @@ package image
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/pkg/strslice"
|
"github.com/containers/image/pkg/strslice"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
@ -72,6 +73,10 @@ type genericManifest interface {
|
|||||||
// The Digest field is guaranteed to be provided; Size may be -1.
|
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||||
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
LayerInfos() []types.BlobInfo
|
LayerInfos() []types.BlobInfo
|
||||||
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
// It returns false if the manifest does not embed a Docker reference.
|
||||||
|
// (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.)
|
||||||
|
EmbeddedDockerReferenceConflicts(ref reference.Named) bool
|
||||||
imageInspectInfo() (*types.ImageInspectInfo, error) // To be called by inspectManifest
|
imageInspectInfo() (*types.ImageInspectInfo, error) // To be called by inspectManifest
|
||||||
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
||||||
// This is a horribly specific interface, but computing InformationOnly.LayerDiffIDs can be very expensive to compute
|
// This is a horribly specific interface, but computing InformationOnly.LayerDiffIDs can be very expensive to compute
|
||||||
|
9
vendor/github.com/containers/image/image/oci.go
generated
vendored
9
vendor/github.com/containers/image/image/oci.go
generated
vendored
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/containers/image/docker/reference"
|
||||||
"github.com/containers/image/manifest"
|
"github.com/containers/image/manifest"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
@ -107,6 +108,13 @@ func (m *manifestOCI1) LayerInfos() []types.BlobInfo {
|
|||||||
return blobs
|
return blobs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
// It returns false if the manifest does not embed a Docker reference.
|
||||||
|
// (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.)
|
||||||
|
func (m *manifestOCI1) EmbeddedDockerReferenceConflicts(ref reference.Named) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (m *manifestOCI1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
func (m *manifestOCI1) imageInspectInfo() (*types.ImageInspectInfo, error) {
|
||||||
config, err := m.ConfigBlob()
|
config, err := m.ConfigBlob()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -146,6 +154,7 @@ func (m *manifestOCI1) UpdatedImage(options types.ManifestUpdateOptions) (types.
|
|||||||
copy.LayersDescriptors[i].Size = info.Size
|
copy.LayersDescriptors[i].Size = info.Size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Ignore options.EmbeddedDockerReference: it may be set when converting from schema1, but we really don't care.
|
||||||
|
|
||||||
switch options.ManifestMIMEType {
|
switch options.ManifestMIMEType {
|
||||||
case "": // No conversion, OK
|
case "": // No conversion, OK
|
||||||
|
5
vendor/github.com/containers/image/types/types.go
generated
vendored
5
vendor/github.com/containers/image/types/types.go
generated
vendored
@ -226,6 +226,10 @@ type Image interface {
|
|||||||
// The Digest field is guaranteed to be provided; Size may be -1.
|
// The Digest field is guaranteed to be provided; Size may be -1.
|
||||||
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
// WARNING: The list may contain duplicates, and they are semantically relevant.
|
||||||
LayerInfos() []BlobInfo
|
LayerInfos() []BlobInfo
|
||||||
|
// EmbeddedDockerReferenceConflicts whether a Docker reference embedded in the manifest, if any, conflicts with destination ref.
|
||||||
|
// It returns false if the manifest does not embed a Docker reference.
|
||||||
|
// (This embedding unfortunately happens for Docker schema1, please do not add support for this in any new formats.)
|
||||||
|
EmbeddedDockerReferenceConflicts(ref reference.Named) bool
|
||||||
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
|
||||||
Inspect() (*ImageInspectInfo, error)
|
Inspect() (*ImageInspectInfo, error)
|
||||||
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
// UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs.
|
||||||
@ -246,6 +250,7 @@ type Image interface {
|
|||||||
// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest
|
// ManifestUpdateOptions is a way to pass named optional arguments to Image.UpdatedManifest
|
||||||
type ManifestUpdateOptions struct {
|
type ManifestUpdateOptions struct {
|
||||||
LayerInfos []BlobInfo // Complete BlobInfos (size+digest+urls) which should replace the originals, in order (the root layer first, and then successive layered layers)
|
LayerInfos []BlobInfo // Complete BlobInfos (size+digest+urls) which should replace the originals, in order (the root layer first, and then successive layered layers)
|
||||||
|
EmbeddedDockerReference reference.Named
|
||||||
ManifestMIMEType string
|
ManifestMIMEType string
|
||||||
// The values below are NOT requests to modify the image; they provide optional context which may or may not be used.
|
// The values below are NOT requests to modify the image; they provide optional context which may or may not be used.
|
||||||
InformationOnly ManifestUpdateInformation
|
InformationOnly ManifestUpdateInformation
|
||||||
|
Loading…
Reference in New Issue
Block a user