bake: fix using push override with output definition

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi 2021-09-20 16:22:27 -07:00
parent 06541ebd0f
commit 6fc906532b
4 changed files with 169 additions and 62 deletions

View File

@ -2,6 +2,7 @@ package bake
import (
"context"
"encoding/csv"
"fmt"
"io/ioutil"
"os"
@ -29,6 +30,11 @@ type File struct {
Data []byte
}
type Override struct {
Key string
Value string
}
func defaultFilenames() []string {
return []string{
"docker-compose.yml", // support app
@ -240,8 +246,8 @@ func (c Config) expandTargets(pattern string) ([]string, error) {
return names, nil
}
func (c Config) newOverrides(v []string) (map[string]*Target, error) {
m := map[string]*Target{}
func (c Config) newOverrides(v []string) (map[string][]Override, error) {
m := map[string][]Override{}
for _, v := range v {
parts := strings.SplitN(v, "=", 2)
@ -260,72 +266,34 @@ func (c Config) newOverrides(v []string) (map[string]*Target, error) {
return nil, err
}
kk := strings.SplitN(parts[0], ".", 2)
for _, name := range names {
t, ok := m[name]
if !ok {
t = &Target{}
t := m[name]
o := Override{
Key: kk[1],
}
if len(parts) == 2 {
o.Value = parts[1]
}
switch keys[1] {
case "context":
t.Context = &parts[1]
case "dockerfile":
t.Dockerfile = &parts[1]
case "args":
if len(keys) != 3 {
return nil, errors.Errorf("invalid key %s, args requires name", parts[0])
}
if t.Args == nil {
t.Args = map[string]string{}
}
if len(parts) < 2 {
v, ok := os.LookupEnv(keys[2])
if ok {
t.Args[keys[2]] = v
if !ok {
continue
}
} else {
t.Args[keys[2]] = parts[1]
o.Value = v
}
case "labels":
if len(keys) != 3 {
return nil, errors.Errorf("invalid key %s, lanels requires name", parts[0])
}
if t.Labels == nil {
t.Labels = map[string]string{}
}
t.Labels[keys[2]] = parts[1]
case "tags":
t.Tags = append(t.Tags, parts[1])
case "cache-from":
t.CacheFrom = append(t.CacheFrom, parts[1])
case "cache-to":
t.CacheTo = append(t.CacheTo, parts[1])
case "target":
s := parts[1]
t.Target = &s
case "secrets":
t.Secrets = append(t.Secrets, parts[1])
case "ssh":
t.SSH = append(t.SSH, parts[1])
case "platform":
t.Platforms = append(t.Platforms, parts[1])
case "output":
t.Outputs = append(t.Outputs, parts[1])
case "no-cache":
noCache, err := strconv.ParseBool(parts[1])
if err != nil {
return nil, errors.Errorf("invalid value %s for boolean key no-cache", parts[1])
}
t.NoCache = &noCache
case "pull":
pull, err := strconv.ParseBool(parts[1])
if err != nil {
return nil, errors.Errorf("invalid value %s for boolean key pull", parts[1])
}
t.Pull = &pull
default:
return nil, errors.Errorf("unknown key: %s", keys[1])
}
t = append(t, o)
m[name] = t
}
}
@ -358,7 +326,7 @@ func (c Config) group(name string, visited map[string]struct{}) []string {
return targets
}
func (c Config) ResolveTarget(name string, overrides map[string]*Target) (*Target, error) {
func (c Config) ResolveTarget(name string, overrides map[string][]Override) (*Target, error) {
t, err := c.target(name, map[string]struct{}{}, overrides)
if err != nil {
return nil, err
@ -374,7 +342,7 @@ func (c Config) ResolveTarget(name string, overrides map[string]*Target) (*Targe
return t, nil
}
func (c Config) target(name string, visited map[string]struct{}, overrides map[string]*Target) (*Target, error) {
func (c Config) target(name string, visited map[string]struct{}, overrides map[string][]Override) (*Target, error) {
if _, ok := visited[name]; ok {
return nil, nil
}
@ -404,9 +372,10 @@ func (c Config) target(name string, visited map[string]struct{}, overrides map[s
m.Merge(tt)
m.Merge(t)
tt = m
if override, ok := overrides[name]; ok {
tt.Merge(override)
if err := tt.AddOverrides(overrides[name]); err != nil {
return nil, err
}
tt.normalize()
return tt, nil
}
@ -507,6 +476,81 @@ func (t *Target) Merge(t2 *Target) {
t.Inherits = append(t.Inherits, t2.Inherits...)
}
func (t *Target) AddOverrides(overrides []Override) error {
for _, o := range overrides {
value := o.Value
keys := strings.SplitN(o.Key, ".", 2)
switch keys[0] {
case "context":
t.Context = &value
case "dockerfile":
t.Dockerfile = &value
case "args":
if len(keys) != 2 {
return errors.Errorf("args require name")
}
if t.Args == nil {
t.Args = map[string]string{}
}
t.Args[keys[1]] = value
case "labels":
if len(keys) != 2 {
return errors.Errorf("labels require name")
}
if t.Labels == nil {
t.Labels = map[string]string{}
}
t.Labels[keys[1]] = value
case "tags":
t.Tags = append(t.Tags, value)
case "cache-from":
t.CacheFrom = append(t.CacheFrom, value)
case "cache-to":
t.CacheTo = append(t.CacheTo, value)
case "target":
t.Target = &value
case "secrets":
t.Secrets = append(t.Secrets, value)
case "ssh":
t.SSH = append(t.SSH, value)
case "platform":
t.Platforms = append(t.Platforms, value)
case "output":
t.Outputs = append(t.Outputs, value)
case "no-cache":
noCache, err := strconv.ParseBool(value)
if err != nil {
return errors.Errorf("invalid value %s for boolean key no-cache", value)
}
t.NoCache = &noCache
case "pull":
pull, err := strconv.ParseBool(value)
if err != nil {
return errors.Errorf("invalid value %s for boolean key pull", value)
}
t.Pull = &pull
case "push":
_, err := strconv.ParseBool(value)
if err != nil {
return errors.Errorf("invalid value %s for boolean key push", value)
}
if len(t.Outputs) == 0 {
t.Outputs = append(t.Outputs, "type=image,push=true")
} else {
for i, output := range t.Outputs {
if typ := parseOutputType(output); typ == "image" || typ == "registry" {
t.Outputs[i] = t.Outputs[i] + ",push=" + value
}
}
}
default:
return errors.Errorf("unknown key: %s", keys[0])
}
}
return nil
}
func TargetsToBuildOpt(m map[string]*Target, inp *Input) (map[string]build.Options, error) {
m2 := make(map[string]build.Options, len(m))
for k, v := range m {
@ -666,3 +710,20 @@ func removeDupes(s []string) []string {
func isRemoteResource(str string) bool {
return urlutil.IsGitURL(str) || urlutil.IsURL(str)
}
func parseOutputType(str string) string {
csvReader := csv.NewReader(strings.NewReader(str))
fields, err := csvReader.Read()
if err != nil {
return ""
}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) == 2 {
if parts[0] == "type" {
return parts[1]
}
}
}
return ""
}

View File

@ -179,6 +179,51 @@ target "webapp" {
})
}
func TestPushOverride(t *testing.T) {
t.Parallel()
fp := File{
Name: "docker-bake.hc",
Data: []byte(
`target "app" {
output = ["type=image,compression=zstd"]
}`),
}
ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,compression=zstd,push=true", m["app"].Outputs[0])
fp = File{
Name: "docker-bake.hc",
Data: []byte(
`target "app" {
output = ["type=image,compression=zstd"]
}`),
}
ctx = context.TODO()
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=false"}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,compression=zstd,push=false", m["app"].Outputs[0])
fp = File{
Name: "docker-bake.hc",
Data: []byte(
`target "app" {
}`),
}
ctx = context.TODO()
m, _, err = ReadTargets(ctx, []File{fp}, []string{"app"}, []string{"*.push=true"}, nil)
require.NoError(t, err)
require.Equal(t, 1, len(m["app"].Outputs))
require.Equal(t, "type=image,push=true", m["app"].Outputs[0])
}
func TestReadTargetsCompose(t *testing.T) {
t.Parallel()
@ -228,7 +273,6 @@ services:
}
func TestHCLCwdPrefix(t *testing.T) {
fp := File{
Name: "docker-bake.hc",
Data: []byte(

View File

@ -64,7 +64,7 @@ func runBake(dockerCli command.Cli, targets []string, in bakeOptions) (err error
if in.exportLoad {
return errors.Errorf("push and load may not be set together at the moment")
}
overrides = append(overrides, "*.output=type=registry")
overrides = append(overrides, "*.push=true")
} else if in.exportLoad {
overrides = append(overrides, "*.output=type=docker")
}

View File

@ -100,7 +100,9 @@ func ParseOutputs(inp []string) ([]client.ExportEntry, error) {
delete(out.Attrs, "dest")
case "registry":
out.Type = client.ExporterImage
out.Attrs["push"] = "true"
if _, ok := out.Attrs["push"]; !ok {
out.Attrs["push"] = "true"
}
}
outs = append(outs, out)