diff --git a/e2e/testdata/invalid/unused_parameter.dockerapp/docker-compose.yml b/e2e/testdata/invalid/unused_parameter.dockerapp/docker-compose.yml new file mode 100644 index 000000000..165694ddb --- /dev/null +++ b/e2e/testdata/invalid/unused_parameter.dockerapp/docker-compose.yml @@ -0,0 +1,29 @@ +version: "3.6" +services: + api: + image: python:3.6 + networks: + back: + front: + aliases: + - api.example.com + - ${api.host} + web: + image: nginx:latest + networks: + - front + volumes: + - static:/opt/${static_subdir} + ports: + - ${web.port}:80 + db: + image: postgres:9.3 + networks: + - back +networks: + front: + back: +volumes: + static: + external: true + name: corp/web-static-data diff --git a/e2e/testdata/invalid/unused_parameter.dockerapp/metadata.yml b/e2e/testdata/invalid/unused_parameter.dockerapp/metadata.yml new file mode 100644 index 000000000..c09a25fc2 --- /dev/null +++ b/e2e/testdata/invalid/unused_parameter.dockerapp/metadata.yml @@ -0,0 +1,8 @@ +version: 1.1.0-beta1 +name: simple +description: "new fancy webapp with microservices" +maintainers: + - name: John Developer + email: john.dev@example.com + - name: Jane Developer + email: jane.dev@example.com diff --git a/e2e/testdata/invalid/unused_parameter.dockerapp/parameters.yml b/e2e/testdata/invalid/unused_parameter.dockerapp/parameters.yml new file mode 100644 index 000000000..949bae74b --- /dev/null +++ b/e2e/testdata/invalid/unused_parameter.dockerapp/parameters.yml @@ -0,0 +1,7 @@ +web: + port: '8082' +api: + host: example.com +static_subdir: data/static +unused: + parameter: FOO diff --git a/e2e/validate_test.go b/e2e/validate_test.go new file mode 100644 index 000000000..514dbd944 --- /dev/null +++ b/e2e/validate_test.go @@ -0,0 +1,20 @@ +package e2e + +import ( + "path" + "strings" + "testing" + + "gotest.tools/assert" + + "gotest.tools/icmd" +) + +func TestOrphanedParameter(t *testing.T) { + cmd, cleanup := dockerCli.createTestCmd() + defer cleanup() + p := path.Join("testdata", "invalid", "unused_parameter") + cmd.Command = dockerCli.Command("app", "validate", p) + out := icmd.RunCmd(cmd).Assert(t, icmd.Expected{ExitCode: 1}).Combined() + assert.Assert(t, strings.Contains(out, "unused.parameter is declared as parameter but not used by the compose file")) +} diff --git a/internal/commands/validate.go b/internal/commands/validate.go index 2bdbb15df..8429f9834 100644 --- a/internal/commands/validate.go +++ b/internal/commands/validate.go @@ -4,11 +4,13 @@ import ( "fmt" "os" + "github.com/docker/app/internal/compose" "github.com/docker/app/internal/packager" "github.com/docker/app/render" "github.com/docker/app/types" "github.com/docker/cli/cli" cliopts "github.com/docker/cli/opts" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -23,22 +25,37 @@ func validateCmd() *cobra.Command { Short: "Checks the rendered application is syntactically correct", Args: cli.RequiresMaxArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - app, err := packager.Extract(firstOrEmpty(args), - types.WithParametersFiles(opts.parametersFiles...), - ) - if err != nil { - return err - } - defer app.Cleanup() - argParameters := cliopts.ConvertKVStringsToMap(opts.overrides) - _, err = render.Render(app, argParameters, nil) - if err != nil { - return err - } - fmt.Fprintf(os.Stdout, "Validated %q\n", app.Path) - return nil + return runValidate(args, opts) }, } opts.parametersOptions.addFlags(cmd.Flags()) return cmd } + +func runValidate(args []string, opts validateOptions) error { + app, err := packager.Extract(firstOrEmpty(args), + types.WithParametersFiles(opts.parametersFiles...), + ) + if err != nil { + return err + } + defer app.Cleanup() + argParameters := cliopts.ConvertKVStringsToMap(opts.overrides) + _, err = render.Render(app, argParameters, nil) + if err != nil { + return err + } + + vars, err := compose.ExtractVariables(app.Composes()[0], compose.ExtrapolationPattern) + if err != nil { + return errors.Wrap(err, "failed to parse compose file") + } + for k := range app.Parameters().Flatten() { + if _, ok := vars[k]; !ok { + return fmt.Errorf("%s is declared as parameter but not used by the compose file", k) + } + } + + fmt.Fprintf(os.Stdout, "Validated %q\n", app.Path) + return nil +}