Skip to content
This repository has been archived by the owner on Jun 13, 2021. It is now read-only.

Check compose file don't use relative paths for bind mount #742

Merged
merged 2 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions internal/commands/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/docker/buildx/util/progress"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
compose "github.com/docker/cli/cli/compose/types"
"github.com/docker/cnab-to-oci/remotes"
"github.com/docker/distribution/reference"
"github.com/moby/buildkit/client"
Expand Down Expand Up @@ -194,17 +195,17 @@ func buildImageUsingBuildx(app *types.App, contextPath string, opt buildOptions,
return bundle, nil
}

func fixServiceImageReferences(ctx context.Context, dockerCli command.Cli, bundle *bundle.Bundle, pulledServices []ServiceConfig) error {
func fixServiceImageReferences(ctx context.Context, dockerCli command.Cli, bundle *bundle.Bundle, pulledServices []compose.ServiceConfig) error {
insecureRegistries, err := internal.InsecureRegistriesFromEngine(dockerCli)
if err != nil {
return errors.Wrapf(err, "could not retrieve insecure registries")
}
resolver := remotes.CreateResolver(dockerCli.ConfigFile(), insecureRegistries...)
for _, service := range pulledServices {
image := bundle.Images[service.Name]
ref, err := reference.ParseDockerRef(*service.Image)
ref, err := reference.ParseDockerRef(service.Image)
if err != nil {
return errors.Wrapf(err, "could not resolve image %s", *service.Image)
return errors.Wrapf(err, "could not resolve image %s", service.Image)
}
_, desc, err := resolver.Resolve(ctx, ref.String())
if err != nil {
Expand Down
60 changes: 47 additions & 13 deletions internal/commands/build/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,43 @@ package build
import (
"fmt"
"path"
"path/filepath"
"strings"

"github.com/docker/app/render"

"github.com/docker/app/types"
"github.com/docker/buildx/build"
"github.com/docker/cli/cli/compose/loader"
compose "github.com/docker/cli/cli/compose/types"
)

// parseCompose do parse app compose file and extract buildx Options
// We don't rely on bake's ReadTargets + TargetsToBuildOpt here as we have to skip environment variable interpolation
func parseCompose(app *types.App, contextPath string, options buildOptions) (map[string]build.Options, []ServiceConfig, error) {
parsed, err := loader.ParseYAML(app.Composes()[0])
func parseCompose(app *types.App, contextPath string, options buildOptions) (map[string]build.Options, []compose.ServiceConfig, error) {
comp, err := render.Render(app, nil, nil)
if err != nil {
return nil, nil, err
}

services, err := load(parsed, options.args)
if err != nil {
return nil, nil, fmt.Errorf("Failed to parse compose file: %s", err)
}
buildArgs := buildArgsToMap(options.args)

pulledServices := []ServiceConfig{}
pulledServices := []compose.ServiceConfig{}
opts := map[string]build.Options{}
for _, service := range services {
if service.Build == nil {
for _, service := range comp.Services {
// Sanity check
for _, vol := range service.Volumes {
if vol.Type == "bind" && !filepath.IsAbs(vol.Source) {
return nil, nil, fmt.Errorf("invalid service %q: can't use relative path as volume source", service.Name)
}
}

if service.Build.Context == "" {
pulledServices = append(pulledServices, service)
continue
}
var tags []string
if service.Image != nil {
tags = append(tags, *service.Image)
if service.Image != "" {
tags = append(tags, service.Image)
}

if service.Build.Dockerfile == "" {
Expand All @@ -43,7 +50,7 @@ func parseCompose(app *types.App, contextPath string, options buildOptions) (map
ContextPath: path.Join(contextPath, service.Build.Context),
DockerfilePath: path.Join(contextPath, service.Build.Context, service.Build.Dockerfile),
},
BuildArgs: flatten(service.Build.Args),
BuildArgs: flatten(mergeArgs(service.Build.Args, buildArgs)),
NoCache: options.noCache,
Pull: options.pull,
Tags: tags,
Expand All @@ -52,6 +59,33 @@ func parseCompose(app *types.App, contextPath string, options buildOptions) (map
return opts, pulledServices, nil
}

func buildArgsToMap(array []string) map[string]string {
result := make(map[string]string)
for _, value := range array {
parts := strings.SplitN(value, "=", 2)
key := parts[0]
if len(parts) == 1 {
result[key] = ""
} else {
result[key] = parts[1]
}
}
return result
}

func mergeArgs(src compose.MappingWithEquals, values map[string]string) compose.MappingWithEquals {
for key := range src {
if val, ok := values[key]; ok {
if val == "" {
src[key] = nil
} else {
src[key] = &val
}
}
}
return src
}

func flatten(in compose.MappingWithEquals) map[string]string {
if len(in) == 0 {
return nil
Expand Down
104 changes: 0 additions & 104 deletions internal/commands/build/types.go

This file was deleted.

47 changes: 47 additions & 0 deletions internal/packager/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,50 @@ func checkEnvFiles(errWriter io.Writer, appName string, cfgMap map[string]interf
return nil
}

func checkRelativePaths(cfgMap map[string]interface{}) error {
services := cfgMap["services"]
servicesMap, ok := services.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid Compose file")
}
for svcName, svc := range servicesMap {
svcContent, ok := svc.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid service %q", svcName)
}
v, ok := svcContent["volumes"]
if !ok {
continue
}
volumes, ok := v.([]interface{})
if !ok {
return fmt.Errorf("invalid Compose file")
}
for _, volume := range volumes {
switch volume.(type) {
case string:
svol := volume.(string)
source := strings.TrimRight(svol, ":")
if !filepath.IsAbs(source) {
return fmt.Errorf("invalid service %q: can't use relative path as volume source", svcName)
}
case map[string]interface{}:
lvol := volume.(map[string]interface{})
src, ok := lvol["source"]
if !ok {
return fmt.Errorf("invalid volume in service %q", svcName)
}
if !filepath.IsAbs(src.(string)) {
return fmt.Errorf("invalid service %q: can't use relative path as volume source", svcName)
}
default:
return fmt.Errorf("invalid Compose file")
}
}
}
return nil
}

func getParamsFromDefaultEnvFile(composeFile string, composeRaw []byte) (map[string]string, bool, error) {
params := make(map[string]string)
envs, err := opts.ParseEnvFile(filepath.Join(filepath.Dir(composeFile), ".env"))
Expand Down Expand Up @@ -173,6 +217,9 @@ func initFromComposeFile(errWriter io.Writer, name string, composeFile string) e
if err := checkEnvFiles(errWriter, name, cfgMap); err != nil {
return err
}
if err := checkRelativePaths(cfgMap); err != nil {
return err
}
params, needsFilling, err := getParamsFromDefaultEnvFile(composeFile, composeRaw)
if err != nil {
return err
Expand Down
36 changes: 36 additions & 0 deletions internal/packager/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,39 @@ maintainers:
)
assert.Assert(t, fs.Equal(tmpdir.Path(), manifest))
}

func TestInitRelativeVolumePath(t *testing.T) {
for _, composeData := range []string{`
version: '3.7'
services:
nginx:
image: nginx
volumes:
- ./foo:/data
`,
`
version: '3.7'
services:
nginx:
image: nginx
volumes:
- type: bind
source: ./foo
target: /data
`,
} {
inputDir := fs.NewDir(t, "app_input_",
fs.WithFile(internal.ComposeFileName, composeData),
)
defer inputDir.Remove()

appName := "my.dockerapp"
dir := fs.NewDir(t, "app_",
fs.WithDir(appName),
)
defer dir.Remove()

err := initFromComposeFile(nil, dir.Join(appName), inputDir.Join(internal.ComposeFileName))
assert.ErrorContains(t, err, "can't use relative path")
}
}