Merge pull request #3688 from deitch/manifest-anywhere

Push arch-specific tags, always build index from registry
This commit is contained in:
Avi Deitcher 2021-06-14 14:25:50 +03:00 committed by GitHub
commit acc34e5ee3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 60 deletions

View File

@ -7,65 +7,9 @@ import (
namepkg "github.com/google/go-containerregistry/pkg/name" namepkg "github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/linuxkit/linuxkit/src/cmd/linuxkit/registry" "github.com/linuxkit/linuxkit/src/cmd/linuxkit/registry"
log "github.com/sirupsen/logrus"
) )
// PushWithManifest push an image along with, optionally, a multi-arch index.
func (p *Provider) PushWithManifest(name, suffix string, pushImage, pushManifest bool) error {
var (
err error
options []remote.Option
)
imageName := name + suffix
ref, err := namepkg.ParseReference(imageName)
if err != nil {
return err
}
if pushImage {
fmt.Printf("Pushing %s\n", imageName)
// do we even have the given one?
root, err := p.FindRoot(imageName)
if err != nil {
return err
}
options = append(options, remote.WithAuthFromKeychain(authn.DefaultKeychain))
img, err1 := root.Image()
ii, err2 := root.ImageIndex()
switch {
case err1 == nil:
if err := remote.Write(ref, img, options...); err != nil {
return err
}
fmt.Printf("Pushed image %s\n", imageName)
case err2 == nil:
if err := remote.WriteIndex(ref, ii, options...); err != nil {
return err
}
fmt.Printf("Pushed index %s\n", imageName)
default:
return fmt.Errorf("name %s unknown in cache", imageName)
}
} else {
fmt.Print("Image push disabled, skipping...\n")
}
auth, err := registry.GetDockerAuth()
if err != nil {
return fmt.Errorf("failed to get auth: %v", err)
}
if pushManifest {
fmt.Printf("Pushing %s to manifest %s\n", imageName, name)
_, _, err = registry.PushManifest(name, auth)
if err != nil {
return err
}
} else {
fmt.Print("Manifest push disabled, skipping...\n")
}
return nil
}
// Push push an image along with a multi-arch index. // Push push an image along with a multi-arch index.
func (p *Provider) Push(name string) error { func (p *Provider) Push(name string) error {
var ( var (
@ -88,17 +32,61 @@ func (p *Provider) Push(name string) error {
ii, err2 := root.ImageIndex() ii, err2 := root.ImageIndex()
switch { switch {
case err1 == nil: case err1 == nil:
log.Debugf("pushing image %s", name)
if err := remote.Write(ref, img, options...); err != nil { if err := remote.Write(ref, img, options...); err != nil {
return err return err
} }
fmt.Printf("Pushed image %s\n", name) fmt.Printf("Pushed image %s\n", name)
case err2 == nil: case err2 == nil:
log.Debugf("pushing index %s", name)
// this is an index, so we not only want to write the index, but tags for each arch-specific image in it
if err := remote.WriteIndex(ref, ii, options...); err != nil { if err := remote.WriteIndex(ref, ii, options...); err != nil {
return err return err
} }
fmt.Printf("Pushed index %s\n", name) fmt.Printf("Pushed index %s\n", name)
manifest, err := ii.IndexManifest()
if err != nil {
return fmt.Errorf("successfully pushed index, but could not read images in index: %v", err)
}
log.Debugf("pushing individual images in the index %s", name)
for _, m := range manifest.Manifests {
if m.Platform == nil || m.Platform.Architecture == "" {
continue
}
archTag := fmt.Sprintf("%s-%s", name, m.Platform.Architecture)
tag, err := namepkg.NewTag(archTag)
if err != nil {
return fmt.Errorf("could not create a valid arch-specific tag %s: %v", archTag, err)
}
image, err := p.FindRoot(archTag)
if err != nil {
return fmt.Errorf("could not find arch-specific image in cache %s: %v", archTag, err)
}
img, err := image.Image()
if err != nil {
return fmt.Errorf("found arch-specific image in cache %s, but could not resolve to actual image: %v", archTag, err)
}
log.Debugf("pushing image %s", tag)
if err := remote.Tag(tag, img, options...); err != nil {
return fmt.Errorf("error creating tag %s: %v", archTag, err)
}
}
default: default:
return fmt.Errorf("name %s unknown in cache", name) return fmt.Errorf("name %s unknown in cache", name)
} }
// Even though we may have pushed the index, we want to be sure that we have an index that includes every architecture on the registry,
// not just those that were in our local cache. So we use manifest-tool library to build a broad index
auth, err := registry.GetDockerAuth()
if err != nil {
return fmt.Errorf("failed to get auth: %v", err)
}
fmt.Printf("Pushing index based on all arch-specific images in registry %s\n", name)
_, _, err = registry.PushManifest(name, auth)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -11,15 +11,23 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
var platforms = []string{ // these platforms are used only as the source for registry.PushManifestList(). Since it is
"linux/amd64", "linux/arm64", "linux/s390x", // configured to ignore missing, we could include dozens if we wanted; it doesn't hurt, adding maybe a few seconds to
// the whole run.
// Ideally, we could just look for all tags that start with linuxkit/foo:<hash>-*, but the registry API
// only supports "list all the tags" and "get a specific tag", no "get by pattern". The "get a specific tag"
// is exactly what registry.PushManifestList() uses, so no benefit to use doing that in advance,
// while "list all tags" is slow, and has to cycle through all of the (growing numbers of) tags
// before we know what exists. Might as well leave it as is.
var platformsToSearchForIndex = []string{
"linux/amd64", "linux/arm64", "linux/s390x", "linux/riscv64", "linux/ppc64le",
} }
// PushManifest create a manifest that supports each of the provided platforms and push it out. // PushManifest create a manifest that supports each of the provided platforms and push it out.
func PushManifest(img string, auth dockertypes.AuthConfig) (hash string, length int, err error) { func PushManifest(img string, auth dockertypes.AuthConfig) (hash string, length int, err error) {
srcImages := []types.ManifestEntry{} srcImages := []types.ManifestEntry{}
for i, platform := range platforms { for i, platform := range platformsToSearchForIndex {
osArchArr := strings.Split(platform, "/") osArchArr := strings.Split(platform, "/")
if len(osArchArr) != 2 && len(osArchArr) != 3 { if len(osArchArr) != 2 && len(osArchArr) != 3 {
return hash, length, fmt.Errorf("platform argument %d is not of form 'os/arch': '%s'", i, platform) return hash, length, fmt.Errorf("platform argument %d is not of form 'os/arch': '%s'", i, platform)