@@ -35,6 +35,7 @@ var imagetoolsTests = []func(t *testing.T, sb integration.Sandbox){
3535 testImagetoolsOCILayoutInspect ,
3636 testImagetoolsOCILayoutCreateSourceAndTarget ,
3737 testImagetoolsOCILayoutReferrers ,
38+ testImagetoolsOCILayoutExistingContent ,
3839 testImagetoolsOCILayoutMergeSources ,
3940 testImagetoolsOCILayoutTargetDigest ,
4041 testImagetoolsAppend ,
@@ -566,6 +567,90 @@ func testImagetoolsOCILayoutReferrers(t *testing.T, sb integration.Sandbox) {
566567 }
567568}
568569
570+ // testImagetoolsOCILayoutExistingContent verifies importing into an existing OCI
571+ // layout still updates index.json state when the top-level descriptor blob is
572+ // already present.
573+ func testImagetoolsOCILayoutExistingContent (t * testing.T , sb integration.Sandbox ) {
574+ if ! isDockerContainerWorker (sb ) {
575+ t .Skip ("only testing with docker-container worker, imagetools only runs on docker-container" )
576+ }
577+
578+ dir := createDockerfileWithArches (t , "amd64" , "arm64" )
579+ registry , err := sb .NewRegistry ()
580+ if errors .Is (err , integration .ErrRequirements ) {
581+ t .Skip (err .Error ())
582+ }
583+ require .NoError (t , err )
584+
585+ source := registry + "/buildx/imtools-oci-layout-existing-content-src:latest"
586+ out , err := buildCmd (sb , withArgs (
587+ "--output" , "type=image,name=" + source + ",push=true,oci-mediatypes=true,oci-artifact=true" ,
588+ "--platform=linux/amd64,linux/arm64" ,
589+ "--provenance=mode=min" ,
590+ dir ,
591+ ))
592+ require .NoError (t , err , string (out ))
593+
594+ layoutPath := filepath .Join (dir , "layout-existing-content" )
595+ initialRef := "oci-layout://" + layoutPath + ":latest"
596+ cmd := buildxCmd (sb , withArgs ("imagetools" , "create" , "-t" , initialRef , source ))
597+ dt , err := cmd .CombinedOutput ()
598+ require .NoError (t , err , string (dt ))
599+
600+ cmd = buildxCmd (sb , withArgs ("imagetools" , "inspect" , source , "--raw" ))
601+ dt , err = cmd .CombinedOutput ()
602+ require .NoError (t , err , string (dt ))
603+
604+ var srcIdx ocispecs.Index
605+ err = json .Unmarshal (dt , & srcIdx )
606+ require .NoError (t , err )
607+
608+ var attestations []ocispecs.Descriptor
609+ for _ , mfst := range srcIdx .Manifests {
610+ if mfst .Annotations ["vnd.docker.reference.type" ] == "attestation-manifest" {
611+ attestations = append (attestations , mfst )
612+ }
613+ }
614+ require .Len (t , attestations , 2 )
615+
616+ signatures := make ([]ocispecs.Descriptor , 0 , len (attestations ))
617+ for _ , attestation := range attestations {
618+ signatures = append (signatures , pushFakeSignatureReferrer (t , source , attestation ))
619+ }
620+
621+ secondRef := "oci-layout://" + layoutPath + ":second"
622+ cmd = buildxCmd (sb , withArgs ("imagetools" , "create" , "-t" , secondRef , source ))
623+ dt , err = cmd .CombinedOutput ()
624+ require .NoError (t , err , string (dt ))
625+
626+ cmd = buildxCmd (sb , withArgs ("imagetools" , "inspect" , secondRef , "--raw" ))
627+ dt , err = cmd .CombinedOutput ()
628+ require .NoError (t , err , string (dt ))
629+
630+ idxBytes , err := os .ReadFile (filepath .Join (layoutPath , "index.json" ))
631+ require .NoError (t , err )
632+
633+ var layoutIdx ocispecs.Index
634+ err = json .Unmarshal (idxBytes , & layoutIdx )
635+ require .NoError (t , err )
636+
637+ directReferrers := map [digest.Digest ]ocispecs.Descriptor {}
638+ directReferrerCount := 0
639+ for _ , desc := range layoutIdx .Manifests {
640+ if desc .Annotations ["io.containerd.manifest.subject" ] != "" {
641+ directReferrerCount ++
642+ directReferrers [desc .Digest ] = desc
643+ }
644+ }
645+ require .Len (t , directReferrers , directReferrerCount )
646+ require .Len (t , directReferrers , len (signatures ))
647+ for i , sig := range signatures {
648+ desc , ok := directReferrers [sig .Digest ]
649+ require .True (t , ok )
650+ require .Equal (t , attestations [i ].Digest .String (), desc .Annotations ["io.containerd.manifest.subject" ])
651+ }
652+ }
653+
569654// testImagetoolsOCILayoutMergeSources verifies create merges registry and local OCI layout sources.
570655func testImagetoolsOCILayoutMergeSources (t * testing.T , sb integration.Sandbox ) {
571656 if ! isDockerContainerWorker (sb ) {
0 commit comments