Merge pull request #1247 from tonistiigi/dangling-naming

exporter: add canonical and dangling image naming
v0.7
Tõnis Tiigi 2019-11-12 10:02:04 -08:00 committed by GitHub
commit e486c1193f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 61 additions and 23 deletions

View File

@ -45,7 +45,7 @@ You don't need to read this document unless you want to use the full-featured st
- [Building a Dockerfile using external frontend:](#building-a-dockerfile-using-external-frontend) - [Building a Dockerfile using external frontend:](#building-a-dockerfile-using-external-frontend)
- [Building a Dockerfile with experimental features like `RUN --mount=type=(bind|cache|tmpfs|secret|ssh)`](#building-a-dockerfile-with-experimental-features-like-run---mounttypebindcachetmpfssecretssh) - [Building a Dockerfile with experimental features like `RUN --mount=type=(bind|cache|tmpfs|secret|ssh)`](#building-a-dockerfile-with-experimental-features-like-run---mounttypebindcachetmpfssecretssh)
- [Output](#output) - [Output](#output)
- [Registry](#registry) - [Image/Registry](#imageregistry)
- [Local directory](#local-directory) - [Local directory](#local-directory)
- [Docker tarball](#docker-tarball) - [Docker tarball](#docker-tarball)
- [OCI tarball](#oci-tarball) - [OCI tarball](#oci-tarball)
@ -199,7 +199,7 @@ See [`frontend/dockerfile/docs/experimental.md`](frontend/dockerfile/docs/experi
By default, the build result and intermediate cache will only remain internally in BuildKit. An output needs to be specified to retrieve the result. By default, the build result and intermediate cache will only remain internally in BuildKit. An output needs to be specified to retrieve the result.
#### Registry #### Image/Registry
```bash ```bash
buildctl build ... --output type=image,name=docker.io/username/image,push=true buildctl build ... --output type=image,name=docker.io/username/image,push=true
@ -215,6 +215,17 @@ buildctl build ...\
--import-cache type=registry,ref=docker.io/username/image --import-cache type=registry,ref=docker.io/username/image
``` ```
Keys supported by image output:
* `name=[value]`: image name
* `push=true`: push after creating the image
* `push-by-digest=true`: push unnamed image
* `registry.insecure=true`: push to insecure HTTP registry
* `oci-mediatypes=true`: use OCI mediatypes in configuration JSON instead of Docker's
* `unpack=true`: unpack image after creation (for use with containerd)
* `dangling-name-prefix=[value]`: name image with `prefix@<digest>` , used for anonymous images
* `name-canonical=true`: add additional canonical name `name@<digest>`
If credentials are required, `buildctl` will attempt to read Docker configuration file `$DOCKER_CONFIG/config.json`. If credentials are required, `buildctl` will attempt to read Docker configuration file `$DOCKER_CONFIG/config.json`.
`$DOCKER_CONFIG` defaults to `~/.docker`. `$DOCKER_CONFIG` defaults to `~/.docker`.

View File

@ -26,12 +26,14 @@ import (
) )
const ( const (
keyImageName = "name" keyImageName = "name"
keyPush = "push" keyPush = "push"
keyPushByDigest = "push-by-digest" keyPushByDigest = "push-by-digest"
keyInsecure = "registry.insecure" keyInsecure = "registry.insecure"
keyUnpack = "unpack" keyUnpack = "unpack"
ociTypes = "oci-mediatypes" keyDanglingPrefix = "dangling-name-prefix"
keyNameCanonical = "name-canonical"
ociTypes = "oci-mediatypes"
) )
type Opt struct { type Opt struct {
@ -111,6 +113,18 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp
return nil, errors.Wrapf(err, "non-bool value specified for %s", k) return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
} }
i.ociTypes = b i.ociTypes = b
case keyDanglingPrefix:
i.danglingPrefix = v
case keyNameCanonical:
if v == "" {
i.nameCanonical = true
continue
}
b, err := strconv.ParseBool(v)
if err != nil {
return nil, errors.Wrapf(err, "non-bool value specified for %s", k)
}
i.nameCanonical = b
default: default:
if i.meta == nil { if i.meta == nil {
i.meta = make(map[string][]byte) i.meta = make(map[string][]byte)
@ -123,13 +137,15 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp
type imageExporterInstance struct { type imageExporterInstance struct {
*imageExporter *imageExporter
targetName string targetName string
push bool push bool
pushByDigest bool pushByDigest bool
unpack bool unpack bool
insecure bool insecure bool
ociTypes bool ociTypes bool
meta map[string][]byte nameCanonical bool
danglingPrefix string
meta map[string][]byte
} }
func (e *imageExporterInstance) Name() string { func (e *imageExporterInstance) Name() string {
@ -165,24 +181,35 @@ func (e *imageExporterInstance) Export(ctx context.Context, src exporter.Source)
e.targetName = string(n) e.targetName = string(n)
} }
nameCanonical := e.nameCanonical
if e.targetName == "" && e.danglingPrefix != "" {
e.targetName = e.danglingPrefix + "@" + desc.Digest.String()
nameCanonical = false
}
if e.targetName != "" { if e.targetName != "" {
targetNames := strings.Split(e.targetName, ",") targetNames := strings.Split(e.targetName, ",")
for _, targetName := range targetNames { for _, targetName := range targetNames {
if e.opt.Images != nil { if e.opt.Images != nil {
tagDone := oneOffProgress(ctx, "naming to "+targetName) tagDone := oneOffProgress(ctx, "naming to "+targetName)
img := images.Image{ img := images.Image{
Name: targetName,
Target: *desc, Target: *desc,
CreatedAt: time.Now(), CreatedAt: time.Now(),
} }
sfx := []string{""}
if nameCanonical {
sfx = append(sfx, "@"+desc.Digest.String())
}
for _, sfx := range sfx {
img.Name = targetName + sfx
if _, err := e.opt.Images.Update(ctx, img); err != nil {
if !errdefs.IsNotFound(err) {
return nil, tagDone(err)
}
if _, err := e.opt.Images.Update(ctx, img); err != nil { if _, err := e.opt.Images.Create(ctx, img); err != nil {
if !errdefs.IsNotFound(err) { return nil, tagDone(err)
return nil, tagDone(err) }
}
if _, err := e.opt.Images.Create(ctx, img); err != nil {
return nil, tagDone(err)
} }
} }
tagDone(nil) tagDone(nil)