mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 17:26:28 +00:00
add support for pushing and pulling images
Signed-off-by: Avi Deitcher <avi@deitcher.net>
This commit is contained in:
parent
8b9b3f673b
commit
54d9db8650
@ -26,5 +26,7 @@ func cacheCmd() *cobra.Command {
|
||||
cmd.AddCommand(cacheLsCmd())
|
||||
cmd.AddCommand(cacheExportCmd())
|
||||
cmd.AddCommand(cacheImportCmd())
|
||||
cmd.AddCommand(cachePullCmd())
|
||||
cmd.AddCommand(cachePushCmd())
|
||||
return cmd
|
||||
}
|
||||
|
82
src/cmd/linuxkit/cache/pull.go
vendored
82
src/cmd/linuxkit/cache/pull.go
vendored
@ -5,10 +5,18 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containerd/containerd/reference"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
namepkg "github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/layout"
|
||||
"github.com/google/go-containerregistry/pkg/v1/match"
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/google/go-containerregistry/pkg/v1/validate"
|
||||
lktspec "github.com/linuxkit/linuxkit/src/cmd/linuxkit/spec"
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -118,3 +126,77 @@ func validateManifestContents(index v1.ImageIndex, digest v1.Hash) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pull pull a reference, whether it points to an arch-specific image or to an index.
|
||||
// If an index, optionally, try to pull its individual named references as well.
|
||||
func (p *Provider) Pull(name string, withArchReferences bool) error {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
fullname := util.ReferenceExpand(name, util.ReferenceWithTag())
|
||||
ref, err := namepkg.ParseReference(fullname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v1ref, err := reference.Parse(ref.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// before we even try to push, let us see if it exists remotely
|
||||
remoteOptions := []remote.Option{remote.WithAuthFromKeychain(authn.DefaultKeychain)}
|
||||
|
||||
desc, err := remote.Get(ref, remoteOptions...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting manifest for trusted image %s: %v", name, err)
|
||||
}
|
||||
|
||||
// use the original image name in the annotation
|
||||
annotations := map[string]string{
|
||||
imagespec.AnnotationRefName: fullname,
|
||||
}
|
||||
|
||||
// first attempt as an index
|
||||
ii, err := desc.ImageIndex()
|
||||
if err == nil {
|
||||
log.Debugf("ImageWrite retrieved %s is index, saving", fullname)
|
||||
|
||||
if err := p.cache.WriteIndex(ii); err != nil {
|
||||
return fmt.Errorf("unable to write index: %v", err)
|
||||
}
|
||||
if _, err := p.DescriptorWrite(&v1ref, desc.Descriptor); err != nil {
|
||||
return fmt.Errorf("unable to write index descriptor to cache: %v", err)
|
||||
}
|
||||
if withArchReferences {
|
||||
im, err := ii.IndexManifest()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get IndexManifest: %v", err)
|
||||
}
|
||||
for _, m := range im.Manifests {
|
||||
if m.MediaType.IsImage() && m.Platform != nil && m.Platform.Architecture != unknown && m.Platform.OS != unknown {
|
||||
archSpecific := fmt.Sprintf("%s-%s", ref.String(), m.Platform.Architecture)
|
||||
archRef, err := reference.Parse(archSpecific)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse arch-specific reference %s: %v", archSpecific, err)
|
||||
}
|
||||
if _, err := p.DescriptorWrite(&archRef, m); err != nil {
|
||||
return fmt.Errorf("unable to write index descriptor to cache: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var im v1.Image
|
||||
// try an image
|
||||
im, err = desc.Image()
|
||||
if err != nil {
|
||||
return fmt.Errorf("provided image is neither an image nor an index: %s", name)
|
||||
}
|
||||
log.Debugf("ImageWrite retrieved %s is image, saving", fullname)
|
||||
if err = p.cache.ReplaceImage(im, match.Name(fullname), layout.WithAnnotations(annotations)); err != nil {
|
||||
return fmt.Errorf("unable to save image to cache: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
37
src/cmd/linuxkit/cache_pull.go
Normal file
37
src/cmd/linuxkit/cache_pull.go
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
cachepkg "github.com/linuxkit/linuxkit/src/cmd/linuxkit/cache"
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func cachePullCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "pull",
|
||||
Short: "pull images to the linuxkit cache from registry",
|
||||
Long: `Pull named images from their registry to the linuxkit cache. Can provide short name, like linuxkit/kernel:6.6.13
|
||||
or nginx, or canonical name, like docker.io/library/nginx:latest. Will be saved into cache as canonical.
|
||||
Will replace in cache if found. Blobs with the same content are not replaced.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
names := args
|
||||
for _, name := range names {
|
||||
fullname := util.ReferenceExpand(name, util.ReferenceWithTag())
|
||||
|
||||
p, err := cachepkg.NewProvider(cacheDir)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to read a local cache: %v", err)
|
||||
}
|
||||
|
||||
if err := p.Pull(fullname, true); err != nil {
|
||||
log.Fatalf("unable to push image named %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
37
src/cmd/linuxkit/cache_push.go
Normal file
37
src/cmd/linuxkit/cache_push.go
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
cachepkg "github.com/linuxkit/linuxkit/src/cmd/linuxkit/cache"
|
||||
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/util"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func cachePushCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "push",
|
||||
Short: "push images from the linuxkit cache",
|
||||
Long: `Push named images from the linuxkit cache to registry. Can provide short name, like linuxkit/kernel:6.6.13
|
||||
or nginx, or canonical name, like docker.io/library/nginx:latest.
|
||||
It is efficient, as blobs with the same content are not replaced.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
names := args
|
||||
for _, name := range names {
|
||||
fullname := util.ReferenceExpand(name)
|
||||
|
||||
p, err := cachepkg.NewProvider(cacheDir)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to read a local cache: %v", err)
|
||||
}
|
||||
|
||||
if err := p.Push(fullname, true); err != nil {
|
||||
log.Fatalf("unable to push image named %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
@ -2,15 +2,37 @@ package util
|
||||
|
||||
import "strings"
|
||||
|
||||
type refOpts struct {
|
||||
withTag bool
|
||||
}
|
||||
type ReferenceOption func(r *refOpts)
|
||||
|
||||
// ReferenceWithTag returns a ReferenceOption that ensures a tag is filled. If the tag is not provided,
|
||||
// the default is added
|
||||
func ReferenceWithTag() ReferenceOption {
|
||||
return func(r *refOpts) {
|
||||
r.withTag = true
|
||||
}
|
||||
}
|
||||
|
||||
// ReferenceExpand expands "redis" to "docker.io/library/redis" so all images have a full domain
|
||||
func ReferenceExpand(ref string) string {
|
||||
func ReferenceExpand(ref string, options ...ReferenceOption) string {
|
||||
var opts refOpts
|
||||
for _, opt := range options {
|
||||
opt(&opts)
|
||||
}
|
||||
var ret string
|
||||
parts := strings.Split(ref, "/")
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
return "docker.io/library/" + ref
|
||||
ret = "docker.io/library/" + ref
|
||||
case 2:
|
||||
return "docker.io/" + ref
|
||||
ret = "docker.io/" + ref
|
||||
default:
|
||||
return ref
|
||||
ret = ref
|
||||
}
|
||||
if opts.withTag && !strings.Contains(ret, ":") {
|
||||
ret += ":latest"
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user