Skip to content

Commit

Permalink
some working code
Browse files Browse the repository at this point in the history
Signed-off-by: Xiaoxuan Wang <xiaoxuanwang@microsoft.com>
  • Loading branch information
Xiaoxuan Wang committed Aug 16, 2024
1 parent ff951da commit 9dc81ae
Showing 1 changed file with 72 additions and 11 deletions.
83 changes: 72 additions & 11 deletions cmd/oras/root/manifest/index/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ import (
"fmt"
"strings"

"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/errdef"
"oras.land/oras/cmd/oras/internal/argument"
"oras.land/oras/cmd/oras/internal/command"
oerrors "oras.land/oras/cmd/oras/internal/errors"
"oras.land/oras/cmd/oras/internal/option"
Expand All @@ -36,21 +38,26 @@ type updateOptions struct {
option.Common
option.Target

extraRefs []string
addArguments []string
extraRefs []string
addArguments []string
mergeArguments []string
removeArguments []string
}

func updateCmd() *cobra.Command {
var opts updateOptions
cmd := &cobra.Command{
Use: "update <name>{:<tag>|@<digest>} {--add} {<tag>|<digest>} [...]",
Short: "Update an image index",
Long: `Update an image index and push to the repository or OCI image layout
Use: "update <name>{:<tag>|@<digest>} {--add/--merge/--remove} {<tag>|<digest>} [...]",
Short: "[Experimental] Update and push an image index",
Long: `[Experimental] Update and push an image index
Example - add one manifest from an index:
oras manifest index update localhost:5000/hello:latest --add sha256:xxx
Example - add one manifest and remove two manifests from an index tagged 'latest':
oras manifest index update localhost:5000/hello:latest --add sha256:xxx --remove sha256:xxx
Example - remove a manifest and merge manifests from indexes tagged as 'index01' and 'index02':
oras manifest index update localhost:5000/hello:latest --remove sha256:xxx --merge index01 --merge index02
`,
Args: cobra.MinimumNArgs(1),
Args: oerrors.CheckArgs(argument.AtLeast(1), "the destination index to update"),
PreRunE: func(cmd *cobra.Command, args []string) error {
refs := strings.Split(args[0], ",")
opts.RawReference = refs[0]
Expand All @@ -63,13 +70,15 @@ Example - add one manifest from an index:
}
option.ApplyFlags(&opts, cmd.Flags())
cmd.Flags().StringArrayVarP(&opts.addArguments, "add", "", nil, "add manifests to the index")
cmd.Flags().StringArrayVarP(&opts.mergeArguments, "merge", "", nil, "merge the manifests of another index")
cmd.Flags().StringArrayVarP(&opts.removeArguments, "remove", "", nil, "manifests to remove from the index")
return oerrors.Command(cmd, &opts.Target)
}

func updateIndex(cmd *cobra.Command, opts updateOptions) error {
// if --add is not used, then there's nothing to update
if !cmd.Flags().Changed("add") {
opts.Println("--add is not used. There's nothing to update.")
if !cmd.Flags().Changed("add") && !cmd.Flags().Changed("remove") && !cmd.Flags().Changed("merge") {
opts.Println("No update flag is used. There's nothing to update.")
return nil
}
ctx, logger := command.GetLogger(cmd, &opts.Common)
Expand All @@ -80,7 +89,7 @@ func updateIndex(cmd *cobra.Command, opts updateOptions) error {
if err := opts.EnsureReferenceNotEmpty(cmd, true); err != nil {
return err
}
// if a digest is given as the index reference, we need to ignore it
// if a digest is given as the index reference, we need to ignore it to successfully push
opts.RawReference, _, _ = strings.Cut(opts.RawReference, "@")
index, err := fetchIndex(ctx, target, opts)
if err != nil {
Expand All @@ -90,6 +99,14 @@ func updateIndex(cmd *cobra.Command, opts updateOptions) error {
if err != nil {
return err
}
manifests, err = mergeIndexes(ctx, manifests, target, opts)
if err != nil {
return err

Check warning on line 104 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L104

Added line #L104 was not covered by tests
}
manifests, err = removeManifests(ctx, manifests, target, opts.removeArguments)
if err != nil {
return err

Check warning on line 108 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L108

Added line #L108 was not covered by tests
}
desc, content, err := packIndex(&index, manifests)
if err != nil {
return err

Check warning on line 112 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L112

Added line #L112 was not covered by tests
Expand Down Expand Up @@ -134,3 +151,47 @@ func addManifests(ctx context.Context, manifests []ocispec.Descriptor, target or
}
return manifests, nil
}

func mergeIndexes(ctx context.Context, manifests []ocispec.Descriptor, target oras.ReadOnlyTarget, opts updateOptions) ([]ocispec.Descriptor, error) {
for _, index := range opts.mergeArguments {
desc, content, err := oras.FetchBytes(ctx, target, index, oras.DefaultFetchBytesOptions)
if err != nil {
if errors.Is(err, errdef.ErrNotFound) {
return nil, fmt.Errorf("could not resolve %s. Make sure the manifest exists in %s", index, opts.Path)

Check warning on line 160 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L157-L160

Added lines #L157 - L160 were not covered by tests
}
return nil, err

Check warning on line 162 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L162

Added line #L162 was not covered by tests
}
if desc.MediaType != ocispec.MediaTypeImageIndex {
return nil, fmt.Errorf("%s is not an image index", index)

Check warning on line 165 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L164-L165

Added lines #L164 - L165 were not covered by tests
}
opts.Println("Resolved index", index)
var index ocispec.Index
if err := json.Unmarshal(content, &index); err != nil {
return nil, err

Check warning on line 170 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L167-L170

Added lines #L167 - L170 were not covered by tests
}
manifests = append(manifests, index.Manifests...)

Check warning on line 172 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L172

Added line #L172 was not covered by tests
}
return manifests, nil
}

func removeManifests(ctx context.Context, manifests []ocispec.Descriptor, target oras.ReadOnlyTarget, removes []string) ([]ocispec.Descriptor, error) {
set := make(map[digest.Digest]struct{})
for _, rem := range removes {
desc, _, err := oras.FetchBytes(ctx, target, rem, oras.DefaultFetchBytesOptions)
if err != nil {
return nil, err

Check warning on line 182 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L180-L182

Added lines #L180 - L182 were not covered by tests
}
set[desc.Digest] = struct{}{}

Check warning on line 184 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L184

Added line #L184 was not covered by tests
}
pointer := len(manifests) - 1
for i, m := range manifests {
if _, exists := set[m.Digest]; exists {
// swap the to-be-removed manifest to the end of slice
manifests[i] = manifests[pointer]
pointer = pointer - 1

Check warning on line 191 in cmd/oras/root/manifest/index/update.go

View check run for this annotation

Codecov / codecov/patch

cmd/oras/root/manifest/index/update.go#L190-L191

Added lines #L190 - L191 were not covered by tests
}
}
// shrink the slice to remove the manifests
manifests = manifests[:pointer+1]
return manifests, nil
}

0 comments on commit 9dc81ae

Please sign in to comment.