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

Add warnings for env_file entries not copied #610

Merged
merged 1 commit into from
Nov 8, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions e2e/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,64 @@ func TestRenderFormatters(t *testing.T) {
})
}

func checkFileWarning(t *testing.T, goldenFile, composeData string) {
cmd, cleanup := dockerCli.createTestCmd()
defer cleanup()

tmpDir := fs.NewDir(t, "app_input",
fs.WithFile(internal.ComposeFileName, composeData),
)
defer tmpDir.Remove()

cmd.Dir = tmpDir.Path()
cmd.Command = dockerCli.Command("app", "init", "app-test",
"--compose-file", tmpDir.Join(internal.ComposeFileName))
stdErr := icmd.RunCmd(cmd).Assert(t, icmd.Success).Stderr()
golden.Assert(t, stdErr, goldenFile)
}

func TestInitWarningEnvFiles(t *testing.T) {
ulyssessouza marked this conversation as resolved.
Show resolved Hide resolved
testCases := []struct {
name string
golden string
compose string
}{
{
name: "initWarningSingleEnvFileTest",
golden: "init-output-warning-single-envfile.golden",
compose: `version: "3.2"
services:
nginx:
image: nginx:latest
env_file: myenv1.env`,
},
{
name: "initWarningMultipleEnvFilesTest",
golden: "init-output-warning-multiple-envfiles.golden",
compose: `version: "3.2"
services:
nginx:
image: nginx:latest
env_file:
- myenv1.env
- myenv2.env`,
},
{
name: "initNoEnvFilesTest",
golden: "init-output-no-envfile.golden",
compose: `version: "3.2"
services:
nginx:
image: nginx:latest`,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
checkFileWarning(t, test.golden, test.compose)
})
}
}

func TestInit(t *testing.T) {
cmd, cleanup := dockerCli.createTestCmd()
defer cleanup()
Expand Down
Empty file.
2 changes: 2 additions & 0 deletions e2e/testdata/init-output-warning-multiple-envfiles.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
nginx.env_file "myenv1.env" will not be copied into app-test.dockerapp. Please copy it manually and update the path accordingly in the compose file.
nginx.env_file "myenv2.env" will not be copied into app-test.dockerapp. Please copy it manually and update the path accordingly in the compose file.
1 change: 1 addition & 0 deletions e2e/testdata/init-output-warning-single-envfile.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nginx.env_file "myenv1.env" will not be copied into app-test.dockerapp. Please copy it manually and update the path accordingly in the compose file.
2 changes: 1 addition & 1 deletion internal/commands/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func initCmd(dockerCli command.Cli) *cobra.Command {
$ docker app init myapp --compose-file docker-compose.yml`,
Args: cli.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
created, err := packager.Init(args[0], initComposeFile)
created, err := packager.Init(dockerCli.Err(), args[0], initComposeFile)
if err != nil {
return err
}
Expand Down
89 changes: 73 additions & 16 deletions internal/packager/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package packager
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"os/user"
Expand All @@ -27,7 +28,7 @@ import (
// Init is the entrypoint initialization function.
// It generates a new application definition based on the provided parameters
// and returns the path to the created application definition.
func Init(name string, composeFile string) (string, error) {
func Init(errWriter io.Writer, name string, composeFile string) (string, error) {
if err := internal.ValidateAppName(name); err != nil {
return "", err
}
Expand All @@ -48,7 +49,7 @@ func Init(name string, composeFile string) (string, error) {
if composeFile == "" {
err = initFromScratch(name)
} else {
err = initFromComposeFile(name, composeFile)
err = initFromComposeFile(errWriter, name, composeFile)
}
if err != nil {
return "", err
Expand Down Expand Up @@ -79,22 +80,52 @@ func checkComposeFileVersion(compose map[string]interface{}) error {
return schema.Validate(compose, fmt.Sprintf("%v", version))
}

func initFromComposeFile(name string, composeFile string) error {
logrus.Debugf("Initializing from compose file %s", composeFile)

dirName := internal.DirNameFromAppName(name)

composeRaw, err := ioutil.ReadFile(composeFile)
if err != nil {
return errors.Wrap(err, "failed to read compose file")
func getEnvFiles(svcName string, envFileEntry interface{}) ([]string, error) {
var envFiles []string
switch envFileEntry.(type) {
case string:
envFiles = append(envFiles, envFileEntry.(string))
case []interface{}:
for _, env := range envFileEntry.([]interface{}) {
envFiles = append(envFiles, env.(string))
}
default:
return nil, fmt.Errorf("unknown entries in 'env_file' for service %s -> %v",
svcName, envFileEntry)
}
cfgMap, err := composeloader.ParseYAML(composeRaw)
if err != nil {
return errors.Wrap(err, "failed to parse compose file")
return envFiles, nil
}

func checkEnvFiles(errWriter io.Writer, appName string, cfgMap map[string]interface{}) error {
services := cfgMap["services"]
servicesMap, ok := services.(map[string]interface{})
ulyssessouza marked this conversation as resolved.
Show resolved Hide resolved
if !ok {
return fmt.Errorf("invalid Compose file")
}
if err := checkComposeFileVersion(cfgMap); err != nil {
return err
for svcName, svc := range servicesMap {
ulyssessouza marked this conversation as resolved.
Show resolved Hide resolved
svcContent, ok := svc.(map[string]interface{})
if !ok {
return fmt.Errorf("invalid service %q", svcName)
}
envFileEntry, ok := svcContent["env_file"]
if !ok {
continue
}
envFiles, err := getEnvFiles(svcName, envFileEntry)
if err != nil {
return errors.Wrap(err, "invalid Compose file")
}
for _, envFilePath := range envFiles {
fmt.Fprintf(errWriter,
"%s.env_file %q will not be copied into %s.dockerapp. "+
"Please copy it manually and update the path accordingly in the compose file.\n",
svcName, envFilePath, appName)
}
}
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"))
if err == nil {
Expand All @@ -107,7 +138,7 @@ func initFromComposeFile(name string, composeFile string) error {
}
vars, err := compose.ExtractVariables(composeRaw, compose.ExtrapolationPattern)
if err != nil {
return errors.Wrap(err, "failed to parse compose file")
return nil, false, errors.Wrap(err, "failed to parse compose file")
}
needsFilling := false
for k, v := range vars {
Expand All @@ -120,6 +151,32 @@ func initFromComposeFile(name string, composeFile string) error {
}
}
}
return params, needsFilling, nil
}

func initFromComposeFile(errWriter io.Writer, name string, composeFile string) error {
logrus.Debugf("Initializing from compose file %s", composeFile)

dirName := internal.DirNameFromAppName(name)

composeRaw, err := ioutil.ReadFile(composeFile)
if err != nil {
return errors.Wrapf(err, "failed to read compose file %q", composeFile)
}
cfgMap, err := composeloader.ParseYAML(composeRaw)
if err != nil {
return errors.Wrap(err, "failed to parse compose file")
}
if err := checkComposeFileVersion(cfgMap); err != nil {
return err
}
if err := checkEnvFiles(errWriter, name, cfgMap); err != nil {
return err
}
params, needsFilling, err := getParamsFromDefaultEnvFile(composeFile, composeRaw)
if err != nil {
return err
}
expandedParams, err := parameters.FromFlatten(params)
if err != nil {
return errors.Wrap(err, "failed to expand parameters")
Expand Down
10 changes: 5 additions & 5 deletions internal/packager/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ services:
)
defer dir.Remove()

err := initFromComposeFile(dir.Join(appName), inputDir.Join(internal.ComposeFileName))
err := initFromComposeFile(nil, dir.Join(appName), inputDir.Join(internal.ComposeFileName))
assert.NilError(t, err)

manifest := fs.Expected(
Expand Down Expand Up @@ -73,7 +73,7 @@ services:
)
defer dir.Remove()

err := initFromComposeFile(dir.Join(appName), inputDir.Join(internal.ComposeFileName))
err := initFromComposeFile(nil, dir.Join(appName), inputDir.Join(internal.ComposeFileName))
assert.NilError(t, err)

const expectedParameters = `ports:
Expand Down Expand Up @@ -108,7 +108,7 @@ services:
}

func TestInitFromInvalidComposeFile(t *testing.T) {
err := initFromComposeFile("my.dockerapp", "doesnotexist")
err := initFromComposeFile(nil, "my.dockerapp", "doesnotexist")
assert.ErrorContains(t, err, "failed to read")
}

Expand All @@ -131,7 +131,7 @@ services:
)
defer dir.Remove()

err := initFromComposeFile(dir.Join(appName), inputDir.Join(internal.ComposeFileName))
err := initFromComposeFile(nil, dir.Join(appName), inputDir.Join(internal.ComposeFileName))
assert.ErrorContains(t, err, "unsupported Compose file version")
}

Expand All @@ -151,7 +151,7 @@ nginx:
)
defer dir.Remove()

err := initFromComposeFile(dir.Join(appName), inputDir.Join(internal.ComposeFileName))
err := initFromComposeFile(nil, dir.Join(appName), inputDir.Join(internal.ComposeFileName))
assert.ErrorContains(t, err, "unsupported Compose file version")
}

Expand Down