imagetools: fix oci-layout index update when blob exists
When pushing to an OCI layout where the top-level descriptor blob already existed, pushOCILayout returned early without updating index.json or writing pending referrers. Restructure the control flow so the blob-exists case skips only the write but still updates the index and flushes referrers. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
@@ -35,6 +35,7 @@ var imagetoolsTests = []func(t *testing.T, sb integration.Sandbox){
|
|||||||
testImagetoolsOCILayoutInspect,
|
testImagetoolsOCILayoutInspect,
|
||||||
testImagetoolsOCILayoutCreateSourceAndTarget,
|
testImagetoolsOCILayoutCreateSourceAndTarget,
|
||||||
testImagetoolsOCILayoutReferrers,
|
testImagetoolsOCILayoutReferrers,
|
||||||
|
testImagetoolsOCILayoutExistingContent,
|
||||||
testImagetoolsOCILayoutMergeSources,
|
testImagetoolsOCILayoutMergeSources,
|
||||||
testImagetoolsOCILayoutTargetDigest,
|
testImagetoolsOCILayoutTargetDigest,
|
||||||
testImagetoolsAppend,
|
testImagetoolsAppend,
|
||||||
@@ -566,6 +567,90 @@ func testImagetoolsOCILayoutReferrers(t *testing.T, sb integration.Sandbox) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// testImagetoolsOCILayoutExistingContent verifies importing into an existing OCI
|
||||||
|
// layout still updates index.json state when the top-level descriptor blob is
|
||||||
|
// already present.
|
||||||
|
func testImagetoolsOCILayoutExistingContent(t *testing.T, sb integration.Sandbox) {
|
||||||
|
if !isDockerContainerWorker(sb) {
|
||||||
|
t.Skip("only testing with docker-container worker, imagetools only runs on docker-container")
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := createDockerfileWithArches(t, "amd64", "arm64")
|
||||||
|
registry, err := sb.NewRegistry()
|
||||||
|
if errors.Is(err, integration.ErrRequirements) {
|
||||||
|
t.Skip(err.Error())
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
source := registry + "/buildx/imtools-oci-layout-existing-content-src:latest"
|
||||||
|
out, err := buildCmd(sb, withArgs(
|
||||||
|
"--output", "type=image,name="+source+",push=true,oci-mediatypes=true,oci-artifact=true",
|
||||||
|
"--platform=linux/amd64,linux/arm64",
|
||||||
|
"--provenance=mode=min",
|
||||||
|
dir,
|
||||||
|
))
|
||||||
|
require.NoError(t, err, string(out))
|
||||||
|
|
||||||
|
layoutPath := filepath.Join(dir, "layout-existing-content")
|
||||||
|
initialRef := "oci-layout://" + layoutPath + ":latest"
|
||||||
|
cmd := buildxCmd(sb, withArgs("imagetools", "create", "-t", initialRef, source))
|
||||||
|
dt, err := cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "inspect", source, "--raw"))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
var srcIdx ocispecs.Index
|
||||||
|
err = json.Unmarshal(dt, &srcIdx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var attestations []ocispecs.Descriptor
|
||||||
|
for _, mfst := range srcIdx.Manifests {
|
||||||
|
if mfst.Annotations["vnd.docker.reference.type"] == "attestation-manifest" {
|
||||||
|
attestations = append(attestations, mfst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.Len(t, attestations, 2)
|
||||||
|
|
||||||
|
signatures := make([]ocispecs.Descriptor, 0, len(attestations))
|
||||||
|
for _, attestation := range attestations {
|
||||||
|
signatures = append(signatures, pushFakeSignatureReferrer(t, source, attestation))
|
||||||
|
}
|
||||||
|
|
||||||
|
secondRef := "oci-layout://" + layoutPath + ":second"
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "create", "-t", secondRef, source))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
cmd = buildxCmd(sb, withArgs("imagetools", "inspect", secondRef, "--raw"))
|
||||||
|
dt, err = cmd.CombinedOutput()
|
||||||
|
require.NoError(t, err, string(dt))
|
||||||
|
|
||||||
|
idxBytes, err := os.ReadFile(filepath.Join(layoutPath, "index.json"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var layoutIdx ocispecs.Index
|
||||||
|
err = json.Unmarshal(idxBytes, &layoutIdx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
directReferrers := map[digest.Digest]ocispecs.Descriptor{}
|
||||||
|
directReferrerCount := 0
|
||||||
|
for _, desc := range layoutIdx.Manifests {
|
||||||
|
if desc.Annotations["io.containerd.manifest.subject"] != "" {
|
||||||
|
directReferrerCount++
|
||||||
|
directReferrers[desc.Digest] = desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.Len(t, directReferrers, directReferrerCount)
|
||||||
|
require.Len(t, directReferrers, len(signatures))
|
||||||
|
for i, sig := range signatures {
|
||||||
|
desc, ok := directReferrers[sig.Digest]
|
||||||
|
require.True(t, ok)
|
||||||
|
require.Equal(t, attestations[i].Digest.String(), desc.Annotations["io.containerd.manifest.subject"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// testImagetoolsOCILayoutMergeSources verifies create merges registry and local OCI layout sources.
|
// testImagetoolsOCILayoutMergeSources verifies create merges registry and local OCI layout sources.
|
||||||
func testImagetoolsOCILayoutMergeSources(t *testing.T, sb integration.Sandbox) {
|
func testImagetoolsOCILayoutMergeSources(t *testing.T, sb integration.Sandbox) {
|
||||||
if !isDockerContainerWorker(sb) {
|
if !isDockerContainerWorker(sb) {
|
||||||
|
|||||||
@@ -558,14 +558,14 @@ func (r *Resolver) pushOCILayout(ctx context.Context, ref *Location, desc ocispe
|
|||||||
}
|
}
|
||||||
w, err := store.Writer(ctx, content.WithRef(desc.Digest.String()), content.WithDescriptor(desc))
|
w, err := store.Writer(ctx, content.WithRef(desc.Digest.String()), content.WithDescriptor(desc))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errdefs.IsAlreadyExists(err) {
|
if !errdefs.IsAlreadyExists(err) {
|
||||||
return nil
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = content.Copy(ctx, w, bytes.NewReader(dt), desc.Size, desc.Digest)
|
||||||
|
if err != nil && !errdefs.IsAlreadyExists(err) {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = content.Copy(ctx, w, bytes.NewReader(dt), desc.Size, desc.Digest)
|
|
||||||
if err != nil && !errdefs.IsAlreadyExists(err) {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
idx := ociindex.NewStoreIndex(ref.OCILayout().Path)
|
idx := ociindex.NewStoreIndex(ref.OCILayout().Path)
|
||||||
|
|||||||
Reference in New Issue
Block a user