mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-05-19 01:47:43 +08:00
bake: fix compose consistency check
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
parent
a8bb25d1b5
commit
5ce3909c48
@ -278,9 +278,19 @@ services:
|
|||||||
`),
|
`),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fp3 := File{
|
||||||
|
Name: "docker-compose3.yml",
|
||||||
|
Data: []byte(
|
||||||
|
`version: "3"
|
||||||
|
services:
|
||||||
|
webapp:
|
||||||
|
entrypoint: echo 1
|
||||||
|
`),
|
||||||
|
}
|
||||||
|
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
m, g, err := ReadTargets(ctx, []File{fp, fp2}, []string{"default"}, nil, nil)
|
m, g, err := ReadTargets(ctx, []File{fp, fp2, fp3}, []string{"default"}, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 3, len(m))
|
require.Equal(t, 3, len(m))
|
||||||
@ -446,6 +456,40 @@ func TestReadContextFromTargetUnknown(t *testing.T) {
|
|||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Contains(t, err.Error(), "failed to find target bar")
|
require.Contains(t, err.Error(), "failed to find target bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadEmptyTargets(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
fp := File{
|
||||||
|
Name: "docker-bake.hcl",
|
||||||
|
Data: []byte(`target "app1" {}`),
|
||||||
|
}
|
||||||
|
|
||||||
|
fp2 := File{
|
||||||
|
Name: "docker-compose.yml",
|
||||||
|
Data: []byte(`
|
||||||
|
services:
|
||||||
|
app2: {}
|
||||||
|
`),
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
m, _, err := ReadTargets(ctx, []File{fp, fp2}, []string{"app1", "app2"}, nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 2, len(m))
|
||||||
|
_, ok := m["app1"]
|
||||||
|
require.True(t, ok)
|
||||||
|
_, ok = m["app2"]
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
require.Equal(t, "Dockerfile", *m["app1"].Dockerfile)
|
||||||
|
require.Equal(t, ".", *m["app1"].Context)
|
||||||
|
require.Equal(t, "Dockerfile", *m["app2"].Dockerfile)
|
||||||
|
require.Equal(t, ".", *m["app2"].Context)
|
||||||
|
}
|
||||||
|
|
||||||
func TestReadContextFromTargetChain(t *testing.T) {
|
func TestReadContextFromTargetChain(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
fp := File{
|
fp := File{
|
||||||
|
@ -3,7 +3,6 @@ package bake
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/loader"
|
"github.com/compose-spec/compose-go/loader"
|
||||||
@ -12,6 +11,9 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// errComposeInvalid is returned when a compose file is invalid
|
||||||
|
var errComposeInvalid = errors.New("invalid compose file")
|
||||||
|
|
||||||
func parseCompose(dt []byte) (*compose.Project, error) {
|
func parseCompose(dt []byte) (*compose.Project, error) {
|
||||||
return loader.Load(compose.ConfigDetails{
|
return loader.Load(compose.ConfigDetails{
|
||||||
ConfigFiles: []compose.ConfigFile{
|
ConfigFiles: []compose.ConfigFile{
|
||||||
@ -22,6 +24,7 @@ func parseCompose(dt []byte) (*compose.Project, error) {
|
|||||||
Environment: envMap(os.Environ()),
|
Environment: envMap(os.Environ()),
|
||||||
}, func(options *loader.Options) {
|
}, func(options *loader.Options) {
|
||||||
options.SkipNormalization = true
|
options.SkipNormalization = true
|
||||||
|
options.SkipConsistencyCheck = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,9 +45,11 @@ func ParseCompose(dt []byte) (*Config, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err = composeValidate(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var c Config
|
var c Config
|
||||||
var zeroBuildConfig compose.BuildConfig
|
|
||||||
if len(cfg.Services) > 0 {
|
if len(cfg.Services) > 0 {
|
||||||
c.Groups = []*Group{}
|
c.Groups = []*Group{}
|
||||||
c.Targets = []*Target{}
|
c.Targets = []*Target{}
|
||||||
@ -52,13 +57,8 @@ func ParseCompose(dt []byte) (*Config, error) {
|
|||||||
g := &Group{Name: "default"}
|
g := &Group{Name: "default"}
|
||||||
|
|
||||||
for _, s := range cfg.Services {
|
for _, s := range cfg.Services {
|
||||||
|
if s.Build == nil {
|
||||||
if s.Build == nil || reflect.DeepEqual(s.Build, zeroBuildConfig) {
|
s.Build = &compose.BuildConfig{}
|
||||||
// if not make sure they're setting an image or it's invalid d-c.yml
|
|
||||||
if s.Image == "" {
|
|
||||||
return nil, fmt.Errorf("compose file invalid: service %s has neither an image nor a build context specified. At least one must be provided", s.Name)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = validateTargetName(s.Name); err != nil {
|
if err = validateTargetName(s.Name); err != nil {
|
||||||
@ -219,6 +219,28 @@ func (t *Target) composeExtTarget(exts map[string]interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// compposeValidate validates a compose file
|
||||||
|
func composeValidate(project *compose.Project) error {
|
||||||
|
for _, s := range project.Services {
|
||||||
|
if s.Build != nil {
|
||||||
|
for _, secret := range s.Build.Secrets {
|
||||||
|
if _, ok := project.Secrets[secret.Source]; !ok {
|
||||||
|
return errors.Wrap(errComposeInvalid, fmt.Sprintf("service %q refers to undefined build secret %s", s.Name, secret.Source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for name, secret := range project.Secrets {
|
||||||
|
if secret.External.External {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if secret.File == "" && secret.Environment == "" {
|
||||||
|
return errors.Wrap(errComposeInvalid, fmt.Sprintf("secret %q must declare either `file` or `environment`", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// composeToBuildkitSecret converts secret from compose format to buildkit's
|
// composeToBuildkitSecret converts secret from compose format to buildkit's
|
||||||
// csv format.
|
// csv format.
|
||||||
func composeToBuildkitSecret(inp compose.ServiceSecretConfig, psecret compose.SecretConfig) (string, error) {
|
func composeToBuildkitSecret(inp compose.ServiceSecretConfig, psecret compose.SecretConfig) (string, error) {
|
||||||
|
@ -153,21 +153,15 @@ services:
|
|||||||
require.Equal(t, c.Targets[0].Args["BRB"], "FOO")
|
require.Equal(t, c.Targets[0].Args["BRB"], "FOO")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBogusCompose(t *testing.T) {
|
func TestInconsistentComposeFile(t *testing.T) {
|
||||||
var dt = []byte(`
|
var dt = []byte(`
|
||||||
services:
|
services:
|
||||||
db:
|
|
||||||
labels:
|
|
||||||
- "foo"
|
|
||||||
webapp:
|
webapp:
|
||||||
build:
|
entrypoint: echo 1
|
||||||
context: .
|
|
||||||
target: webapp
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
_, err := ParseCompose(dt)
|
_, err := ParseCompose(dt)
|
||||||
require.Error(t, err)
|
require.NoError(t, err)
|
||||||
require.Contains(t, err.Error(), "has neither an image nor a build context specified: invalid compose project")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdvancedNetwork(t *testing.T) {
|
func TestAdvancedNetwork(t *testing.T) {
|
||||||
@ -420,3 +414,69 @@ services:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateComposeSecret(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
dt []byte
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "secret set by file",
|
||||||
|
dt: []byte(`
|
||||||
|
secrets:
|
||||||
|
foo:
|
||||||
|
file: .secret
|
||||||
|
`),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "secret set by environment",
|
||||||
|
dt: []byte(`
|
||||||
|
secrets:
|
||||||
|
foo:
|
||||||
|
environment: TOKEN
|
||||||
|
`),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "external secret",
|
||||||
|
dt: []byte(`
|
||||||
|
secrets:
|
||||||
|
foo:
|
||||||
|
external: true
|
||||||
|
`),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unset secret",
|
||||||
|
dt: []byte(`
|
||||||
|
secrets:
|
||||||
|
foo: {}
|
||||||
|
`),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "undefined secret",
|
||||||
|
dt: []byte(`
|
||||||
|
services:
|
||||||
|
foo:
|
||||||
|
build:
|
||||||
|
secrets:
|
||||||
|
- token
|
||||||
|
`),
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range cases {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
_, err := ParseCompose(tt.dt)
|
||||||
|
if tt.wantErr {
|
||||||
|
require.Error(t, err)
|
||||||
|
} else {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user