diff --git a/src/cmd/linuxkit/cache/push.go b/src/cmd/linuxkit/cache/push.go index 1c8099b27..964e92981 100644 --- a/src/cmd/linuxkit/cache/push.go +++ b/src/cmd/linuxkit/cache/push.go @@ -18,7 +18,7 @@ import ( // If withArchSpecificTags is true, it will push all arch-specific images in the index, each as // their own tag with the same name as the index, but with the architecture appended, e.g. // image:foo will have image:foo-amd64, image:foo-arm64, etc. -func (p *Provider) Push(name, remoteName string, withArchSpecificTags bool) error { +func (p *Provider) Push(name, remoteName string, withArchSpecificTags, override bool) error { var ( err error options []remote.Option @@ -30,14 +30,25 @@ func (p *Provider) Push(name, remoteName string, withArchSpecificTags bool) erro if err != nil { return err } + options = append(options, remote.WithAuthFromKeychain(authn.DefaultKeychain)) fmt.Printf("Pushing local %s as %s\n", name, remoteName) + + // check if it already exists, unless override is explicit + if !override { + if _, err := remote.Get(ref, options...); err == nil { + log.Infof("image %s already exists in the registry, skipping", remoteName) + return nil + } + } + + // if we made it this far, either we had a specific override, or we do not have the image remotely + // do we even have the given one? root, err := p.FindRoot(name) if err != nil { return err } - options = append(options, remote.WithAuthFromKeychain(authn.DefaultKeychain)) img, err1 := root.Image() ii, err2 := root.ImageIndex() diff --git a/src/cmd/linuxkit/cache_push.go b/src/cmd/linuxkit/cache_push.go index 1f0dbfbbd..102b600ce 100644 --- a/src/cmd/linuxkit/cache_push.go +++ b/src/cmd/linuxkit/cache_push.go @@ -11,6 +11,7 @@ func cachePushCmd() *cobra.Command { var ( remoteName string pushArchSpecificTags bool + override bool ) cmd := &cobra.Command{ Use: "push", @@ -29,7 +30,7 @@ func cachePushCmd() *cobra.Command { log.Fatalf("unable to read a local cache: %v", err) } - if err := p.Push(fullname, remoteName, pushArchSpecificTags); err != nil { + if err := p.Push(fullname, remoteName, pushArchSpecificTags, override); err != nil { log.Fatalf("unable to push image named %s: %v", name, err) } } @@ -38,5 +39,6 @@ func cachePushCmd() *cobra.Command { } cmd.Flags().StringVar(&remoteName, "remote-name", "", "Push it under a different name, e.g. push local image foo/bar:mine as baz/bee:yours. If blank, uses same local name.") cmd.Flags().BoolVar(&pushArchSpecificTags, "with-arch-tags", false, "When the local reference is an index, add to the remote arch-specific tags for each arch in the index, each as their own tag with the same name as the index, but with the architecture appended, e.g. image:foo will have image:foo-amd64, image:foo-arm64, etc.") + cmd.Flags().BoolVar(&override, "override", false, "Even if the image already exists in the registry, push it again, overwriting the existing image.") return cmd } diff --git a/src/cmd/linuxkit/pkglib/build.go b/src/cmd/linuxkit/pkglib/build.go index 31b7e4d45..8945b5628 100644 --- a/src/cmd/linuxkit/pkglib/build.go +++ b/src/cmd/linuxkit/pkglib/build.go @@ -524,7 +524,7 @@ func (p Pkg) Build(bos ...BuildOpt) error { } // push the manifest - if err := c.Push(p.FullTag(), "", bo.manifest); err != nil { + if err := c.Push(p.FullTag(), "", bo.manifest, true); err != nil { return err } @@ -546,7 +546,7 @@ func (p Pkg) Build(bos ...BuildOpt) error { if _, err := c.DescriptorWrite(&ref, *desc); err != nil { return err } - if err := c.Push(fullRelTag, "", bo.manifest); err != nil { + if err := c.Push(fullRelTag, "", bo.manifest, true); err != nil { return err } diff --git a/src/cmd/linuxkit/pkglib/build_test.go b/src/cmd/linuxkit/pkglib/build_test.go index 14058f3f1..f0ffa3cbf 100644 --- a/src/cmd/linuxkit/pkglib/build_test.go +++ b/src/cmd/linuxkit/pkglib/build_test.go @@ -392,7 +392,7 @@ func (c *cacheMocker) IndexWrite(ref *reference.Spec, descriptors ...registry.De return c.NewSource(ref, "", &desc), nil } -func (c *cacheMocker) Push(name, remoteName string, withManifest bool) error { +func (c *cacheMocker) Push(name, remoteName string, withManifest, override bool) error { if !c.enablePush { return errors.New("push disabled") } diff --git a/src/cmd/linuxkit/spec/cache.go b/src/cmd/linuxkit/spec/cache.go index 9b6295631..856950631 100644 --- a/src/cmd/linuxkit/spec/cache.go +++ b/src/cmd/linuxkit/spec/cache.go @@ -41,7 +41,7 @@ type CacheProvider interface { // name is the name as referenced in the local cache, remoteName is the name to give it remotely. // If remoteName is empty, it is the same as name. // if withManifest defined will push a multi-arch manifest - Push(name, remoteName string, withManifest bool) error + Push(name, remoteName string, withManifest, override bool) error // NewSource return an ImageSource for a specific ref and architecture in the cache. NewSource(ref *reference.Spec, architecture string, descriptor *v1.Descriptor) ImageSource // GetContent returns an io.Reader to the provided content as is, given a specific digest. It is