Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 75 additions & 10 deletions pkg/cli/image/append/append.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ import (
"strconv"
"time"

units "github.com/docker/go-units"
"github.com/spf13/cobra"
"k8s.io/klog/v2"

"github.com/docker/distribution"
"github.com/docker/distribution/manifest/manifestlist"
"github.com/docker/distribution/manifest/schema2"
"github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/client"
units "github.com/docker/go-units"
digest "github.com/opencontainers/go-digest"

"k8s.io/cli-runtime/pkg/genericclioptions"
Expand Down Expand Up @@ -104,6 +105,9 @@ type AppendImageOptions struct {
DropHistory bool
CreatedAt string

// exposed only to be used by the `oc adm release`
KeepManifestList bool

SecurityOptions imagemanifest.SecurityOptions
FilterOptions imagemanifest.FilterOptions
ParallelOptions imagemanifest.ParallelOptions
Expand Down Expand Up @@ -259,26 +263,87 @@ func (o *AppendImageOptions) Run() error {
}

var (
base *dockerv1client.DockerImageConfig
baseDigest digest.Digest
baseContentDigest digest.Digest
layers []distribution.Descriptor
fromRepo distribution.Repository
repo distribution.Repository
manifestLocation imagemanifest.ManifestLocation
srcManifest distribution.Manifest
)
if from != nil {
repo, err := fromOptions.Repository(ctx, *from)
repo, err = fromOptions.Repository(ctx, *from)
if err != nil {
return err
}
fromRepo = repo

srcManifest, manifestLocation, err := imagemanifest.FirstManifest(ctx, from.Ref, repo, o.FilterOptions.Include)
srcManifest, manifestLocation, err = imagemanifest.FirstManifest(ctx, from.Ref, repo, o.FilterOptions.Include)
if err != nil {
return fmt.Errorf("unable to read image %s: %v", from, err)
}

if o.KeepManifestList {
return o.appendManifestList(ctx, createdAt, from, to, repo, srcManifest, manifestLocation, toRepo, toManifests)
}
}

return o.append(ctx, createdAt, from, to, repo, srcManifest, manifestLocation, toRepo, toManifests)
}

func (o *AppendImageOptions) appendManifestList(ctx context.Context, createdAt *time.Time,
from *imagesource.TypedImageReference, to imagesource.TypedImageReference,
repo distribution.Repository, srcManifest distribution.Manifest, manifestLocation imagemanifest.ManifestLocation,
toRepo distribution.Repository, toManifests distribution.ManifestService) error {
// process manifestlist
// oldDigest:newDigest mappging so that we can create a new manifestlist
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small typo here on mapping

newDigests := make(map[digest.Digest]digest.Digest)
manifestMap, oldList, _, err := imagemanifest.AllManifests(ctx, from.Ref, repo)
for digest, srcManifest := range manifestMap {
err = o.append(ctx, createdAt, from, to, repo, srcManifest, manifestLocation, toRepo, toManifests)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the to argument here: IIUC it points to an image by tag and, as a consequence, each time we call append() the tag latest will be updated remotely to point to a single manifest (i.e. it will overwrite the previous manifest list reference with a reference to a single manifest).

Shouldn't to be updated to point to an image by sha before calling append() ? If we could do that then we would assure we would not stomp with the remote tag until the very last moment (when we upload the new manifest list).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, lemme try to figure something out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The caller needs to define if we should push a tag or not, which can be skipped in PutManifestInCompatibleSchema so I went with a boolean passed to append which tells if tag should or not be skipped, which I set in appendManifestlList.

if err != nil {
return fmt.Errorf("error appending image %s: %w", digest, err)
}
newDigests[digest] = o.ToDigest
}
// create new manifestlist from the old one swapping digest with the new ones
newDescriptors := make([]manifestlist.ManifestDescriptor, 0, len(oldList.Manifests))
for _, manifest := range oldList.Manifests {
manifest.Digest = newDigests[manifest.Digest]
newDescriptors = append(newDescriptors, manifest)

}
forPush, err := manifestlist.FromDescriptors(newDescriptors)
if err != nil {
return fmt.Errorf("error creating new manifestlist: %#v", err)
}
// push new manifestlist to registry
toDigest, err := imagemanifest.PutManifestInCompatibleSchema(ctx, forPush, to.Ref.Tag, toManifests, toRepo.Named(), nil, nil)
if err != nil {
return fmt.Errorf("unable to push manifestlist: %#v", err)
}
o.ToDigest = toDigest
if !o.DryRun {
fmt.Fprintf(o.Out, "Pushed %s to %s\n", toDigest, to)
}

return nil
}

func (o *AppendImageOptions) append(ctx context.Context, createdAt *time.Time,
from *imagesource.TypedImageReference, to imagesource.TypedImageReference,
repo distribution.Repository, srcManifest distribution.Manifest, manifestLocation imagemanifest.ManifestLocation,
toRepo distribution.Repository, toManifests distribution.ManifestService) error {
var (
base *dockerv1client.DockerImageConfig
baseDigest digest.Digest
baseContentDigest digest.Digest
err error
layers []distribution.Descriptor
fromRepo distribution.Repository
)
if repo != nil || srcManifest != nil {
fromRepo = repo

base, layers, err = imagemanifest.ManifestToImageConfig(ctx, srcManifest, repo.Blobs(ctx), manifestLocation)
if err != nil {
return fmt.Errorf("unable to parse image %s: %v", from, err)
// return fmt.Errorf("unable to parse image %s: %v", from, err)
return err

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you forget to remove or un-comment this line?

}

contentDigest, err := registryclient.ContentDigestForManifest(srcManifest, manifestLocation.Manifest.Algorithm())
Expand Down