Skip to content

Commit

Permalink
Add merge command - ease the process to push manifests to remote re…
Browse files Browse the repository at this point in the history
…gistries from a set of images

Signed-off-by: Vincent Boulineau <vincent.boulineau@datadoghq.com>
  • Loading branch information
vboulineau committed Dec 29, 2020
1 parent c95d7bd commit 491aca8
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 0 deletions.
37 changes: 37 additions & 0 deletions cmd/crane/cmd/merge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2018 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"log"

"github.com/google/go-containerregistry/pkg/crane"
"github.com/spf13/cobra"
)

// NewCmdMerge creates a new cobra.Command for the copy subcommand.
func NewCmdMerge(options *[]crane.Option) *cobra.Command {
return &cobra.Command{
Use: "merge SRC [SRC...] DST",
Aliases: []string{"mg"},
Short: "Efficiently merge in an index manifest and copy images from sources to dst",
Args: cobra.MinimumNArgs(2),
Run: func(_ *cobra.Command, args []string) {
if err := crane.Merge(args[:len(args)-1], args[len(args)-1], *options...); err != nil {
log.Fatal(err)
}
},
}
}
1 change: 1 addition & 0 deletions cmd/crane/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func New(use, short string, options []crane.Option) *cobra.Command {
NewCmdList(&options),
NewCmdManifest(&options),
NewCmdOptimize(&options),
NewCmdMerge(&options),
NewCmdPull(&options),
NewCmdPush(&options),
NewCmdRebase(&options),
Expand Down
1 change: 1 addition & 0 deletions cmd/crane/doc/crane.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions cmd/crane/doc/crane_merge.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

128 changes: 128 additions & 0 deletions pkg/crane/merge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2018 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package crane

import (
"fmt"

"github.com/google/go-containerregistry/pkg/logs"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/google/go-containerregistry/pkg/v1/types"
)

// Merge copies a remote image or index from src to dst by merging several images/indexes into a single one.
func Merge(sources []string, dst string, opt ...Option) error {
o := makeOptions(opt...)

if len(sources) == 1 {
return Copy(sources[0], dst, opt...)
}

dstRef, err := name.ParseReference(dst, o.name...)
if err != nil {
return fmt.Errorf("parsing reference for %q: %v", dst, err)
}

logs.Progress.Printf("Building index from %v", sources)
// Build an image for each child from the base and append it to a new index to produce the result.
adds := make([]mutate.IndexAddendum, 0)
for _, src := range sources {
srcRef, err := name.ParseReference(src, o.name...)
if err != nil {
return fmt.Errorf("parsing reference %q: %v", src, err)
}

desc, err := remote.Get(srcRef, o.remote...)
if err != nil {
return fmt.Errorf("fetching %q: %v", src, err)
}

srcAdds, err := buildIndexAddendums(desc)
if err != nil {
return fmt.Errorf("unable to build index entry for src: %q: %v", src, err)
}
adds = append(adds, srcAdds...)
}

destIdx := mutate.IndexMediaType(mutate.AppendManifests(empty.Index, adds...), types.DockerManifestList)

logs.Progress.Printf("Copying from generated index to %v", dstRef)
return remote.WriteIndex(dstRef, destIdx, o.remote...)
}

func buildIndexAddendums(desc *remote.Descriptor) ([]mutate.IndexAddendum, error) {
adds := make([]mutate.IndexAddendum, 0)

switch desc.MediaType {
case types.OCIImageIndex, types.DockerManifestList:
idx, err := desc.ImageIndex()
if err != nil {
return nil, err
}

im, err := idx.IndexManifest()
if err != nil {
return nil, err
}

for _, imDesc := range im.Manifests {
img, err := idx.Image(imDesc.Digest)
if err != nil {
return nil, err
}

adds = append(adds, mutate.IndexAddendum{
Add: img,
Descriptor: v1.Descriptor{
URLs: imDesc.URLs,
MediaType: imDesc.MediaType,
Annotations: imDesc.Annotations,
Platform: imDesc.Platform,
},
})
}
case types.DockerManifestSchema1, types.DockerManifestSchema1Signed:
return nil, fmt.Errorf("merging v1 manifest is not supported")
default:
img, err := desc.Image()
if err != nil {
return nil, err
}
cfg, err := img.ConfigFile()
if err != nil {
return nil, err
}

adds = append(adds, mutate.IndexAddendum{
Add: img,
Descriptor: v1.Descriptor{
URLs: desc.URLs,
MediaType: desc.MediaType,
Annotations: desc.Annotations,
Platform: &v1.Platform{
Architecture: cfg.Architecture,
OS: cfg.OS,
OSVersion: cfg.OSVersion,
},
},
})
}

return adds, nil
}

0 comments on commit 491aca8

Please sign in to comment.