From 8f70d8e665a442d3e067f9fccbc50d144707f3bd Mon Sep 17 00:00:00 2001 From: Christopher Crone Date: Tue, 4 Dec 2018 16:16:59 +0100 Subject: [PATCH] Add CNAB support Signed-off-by: Christopher Crone --- .gitignore | 3 +- BUILDING.md | 18 +- Dockerfile | 33 +- Gopkg.lock | 141 +- Gopkg.toml | 19 +- Jenkinsfile | 20 + Jenkinsfile.baguette | 185 ++- README.md | 88 +- cmd/docker-app/bundle.go | 154 +++ cmd/docker-app/bundle_test.go | 105 ++ cmd/docker-app/credentialset.go | 78 ++ cmd/docker-app/deploy.go | 90 -- cmd/docker-app/fork.go | 31 - cmd/docker-app/helm.go | 56 - cmd/docker-app/image-add.go | 12 +- cmd/docker-app/inspect.go | 71 +- cmd/docker-app/install.go | 342 +++++ cmd/docker-app/merge.go | 2 +- cmd/docker-app/pull.go | 73 +- cmd/docker-app/push.go | 111 +- cmd/docker-app/render.go | 20 +- cmd/docker-app/root.go | 26 +- cmd/docker-app/status.go | 57 + cmd/docker-app/uninstall.go | 71 + cmd/docker-app/upgrade.go | 94 ++ cmd/docker-app/validate.go | 16 +- cmd/run/env.go | 33 + cmd/run/inspect.go | 19 + cmd/run/install.go | 55 + cmd/run/main.go | 81 ++ cmd/run/status.go | 26 + cmd/run/uninstall.go | 24 + docker.Makefile | 46 +- e2e/commands_test.go | 139 +- .../{settings.yml => parameters.yml} | 0 e2e/testdata/envvariables-inspect.golden | 4 +- .../expected-fork-metadata-no-rename.golden | 16 - e2e/testdata/expected-fork-metadata.golden | 16 - e2e/testdata/helloworld-inspect.golden | 8 +- e2e/testdata/helm-expected.chart/Chart.yaml | 7 - .../helm-expected.chart/templates/stack.yaml | 66 - .../templates/stackv1beta1.yaml | 68 - .../templates/stackv1beta2.yaml | 66 - e2e/testdata/helm-expected.chart/values.yaml | 20 - e2e/testdata/helm.dockerapp/settings.yml | 17 - e2e/testdata/init-singlefile.dockerapp | 4 +- .../{settings.yml => parameters.yml} | 0 .../{settings-0.yml => parameters-0.yml} | 0 .../simple.dockerapp/docker-compose.yml | 0 .../simple.dockerapp/metadata.yml | 0 .../simple.dockerapp/parameters.yml} | 0 .../{settings.yml => parameters.yml} | 0 .../{settings-0.yml => parameters-0.yml} | 0 .../{settings.yml => parameters.yml} | 0 .../{settings-0.yml => parameters-0.yml} | 0 examples/hello-world/README.md | 10 +- examples/hello-world/hello-world.dockerapp | 4 +- examples/voting-app/Makefile | 19 +- examples/voting-app/README.md | 12 +- .../voting-app.dockerapp/metadata.yml | 2 +- .../{settings.yml => parameters.yml} | 0 .../settings/development.yml | 11 - .../settings/production.yml | 9 - examples/wordpress/README.md | 30 +- ...{prod-settings.yml => prod-parameters.yml} | 0 .../wordpress.dockerapp/docker-compose.yml | 4 +- .../wordpress.dockerapp/settings.yml | 22 - examples_test.go | 2 +- .../example/simple.dockerapp/settings.yml | 16 - integrations/intellij/README.md | 4 +- .../intellij/src/main/java/DeployApp.java | 12 +- .../java/{Settings.java => Parameters.java} | 6 +- ...tingsDialog.form => ParametersDialog.form} | 4 +- ...tingsDialog.java => ParametersDialog.java} | 4 +- .../intellij/src/main/java/RenderApp.java | 12 +- .../src/main/resources/META-INF/plugin.xml | 2 +- integrations/visualstudio/README.md | 4 +- ...ageSettings.cs => AppPackageParameters.cs} | 16 +- .../dockerappvsix/CommandDeploy.cs | 6 +- .../visualstudio/dockerappvsix/CommandNew.cs | 2 +- ...ommandSettings.cs => CommandParameters.cs} | 16 +- .../dockerappvsix/CommandRender.cs | 2 +- .../dockerappvsix/CommandSelectApp.cs | 2 +- .../dockerappvsix/DockerAppPackage.cs | 2 +- .../dockerappvsix/DockerAppPackage.vsct | 10 +- .../dockerappvsix/NewAppDialog.xaml | 2 +- .../dockerappvsix/NewAppDialog.xaml.cs | 4 +- ...{NewAppSettings.cs => NewAppParameters.cs} | 2 +- ...tingsDialog.xaml => ParametersDialog.xaml} | 12 +- ...ialog.xaml.cs => ParametersDialog.xaml.cs} | 14 +- ...mandSettings.png => CommandParameters.png} | Bin .../dockerappvsix/dockerappvsix.csproj | 16 +- internal/helm/helm.go | 343 ----- internal/helm/templateconversion/convert.go | 275 ---- internal/helm/templateloader/interpolate.go | 19 - internal/helm/templateloader/loader.go | 611 --------- internal/helm/templateloader/volume.go | 123 -- internal/helm/templatetypes/types.go | 261 ---- internal/helm/templatev1beta2/stack.go | 277 ---- internal/inspect/inspect.go | 38 +- internal/inspect/inspect_test.go | 14 +- internal/inspect/testdata/inspect-full.golden | 8 +- ...gs.golden => inspect-no-parameters.golden} | 0 .../testdata/inspect-overridden.golden | 6 +- internal/names.go | 9 +- internal/packager/cnab.go | 86 ++ internal/packager/cnab_test.go | 77 ++ internal/packager/errors.go | 25 + internal/packager/extract.go | 60 +- internal/packager/extract_test.go | 36 - internal/packager/fork.go | 100 -- internal/packager/fork_test.go | 96 -- internal/packager/init.go | 26 +- internal/packager/init_test.go | 4 +- internal/packager/packing.go | 47 +- internal/packager/packing_test.go | 27 + internal/packager/parameter.go | 60 + internal/packager/parameter_test.go | 66 + internal/packager/registry.go | 6 +- internal/packager/registry_test.go | 4 +- internal/packager/split.go | 12 +- .../packages/packing.dockerapp/config.cfg | 1 + .../packing.dockerapp}/docker-compose.yml | 8 - .../packages/packing.dockerapp}/metadata.yml | 5 +- .../packing.dockerapp/nesteddir/config2.cfg | 1 + .../nesteddir/nested2/nested3/config3.cfg | 1 + .../packages/packing.dockerapp/parameters.yml | 2 + internal/packager/testdata/parameters.yml | 9 + internal/renderer/driver/driver.go | 4 +- internal/renderer/gotemplate/driver.go | 6 +- internal/renderer/gotemplate/driver_test.go | 10 +- internal/renderer/mustache/driver.go | 6 +- internal/renderer/mustache/driver_test.go | 8 +- internal/renderer/renderer.go | 6 +- internal/renderer/renderer_test.go | 4 +- internal/renderer/yatee/driver.go | 6 +- internal/renderer/yatee/driver_test.go | 4 +- loader/loader.go | 27 +- loader/loader_test.go | 24 +- pkg/yatee/README.md | 20 +- pkg/yatee/gopher/main.go | 4 +- pkg/yatee/gopher/yatee.html | 4 +- pkg/yatee/samples/main.go | 4 +- pkg/yatee/yatee.go | 42 +- pkg/yatee/yatee_test.go | 22 +- render/render.go | 24 +- render/render_test.go | 24 +- specification/README.md | 22 +- types/metadata/metadata.go | 63 - types/{settings => parameters}/load.go | 42 +- types/{settings => parameters}/load_test.go | 20 +- types/parameters/merge.go | 17 + types/{settings => parameters}/merge_test.go | 6 +- types/{settings => parameters}/opts.go | 6 +- .../settings.go => parameters/parameters.go} | 18 +- .../parameters_test.go} | 4 +- types/settings/merge.go | 17 - types/types.go | 64 +- types/types_test.go | 56 +- vars.mk | 2 +- .../github.com/Masterminds/semver/LICENSE.txt | 20 + .../Masterminds/semver/collection.go | 24 + .../Masterminds/semver/constraints.go | 426 ++++++ vendor/github.com/Masterminds/semver/doc.go | 115 ++ .../github.com/Masterminds/semver/version.go | 421 ++++++ .../asaskevich/govalidator/arrays.go | 58 - .../asaskevich/govalidator/converter.go | 64 - .../asaskevich/govalidator/error.go | 36 - .../asaskevich/govalidator/numerics.go | 97 -- .../asaskevich/govalidator/patterns.go | 97 -- .../asaskevich/govalidator/types.go | 616 --------- .../asaskevich/govalidator/utils.go | 264 ---- .../asaskevich/govalidator/validator.go | 1213 ----------------- .../coreos/etcd/Documentation/README.md | 1 - vendor/github.com/coreos/etcd/cmd/etcd | 1 - vendor/github.com/coreos/etcd/cmd/etcdctl | 1 - vendor/github.com/coreos/etcd/cmd/functional | 1 - vendor/github.com/coreos/etcd/cmd/tools | 1 - vendor/github.com/deis/duffle/.gitignore | 8 + vendor/github.com/deis/duffle/.travis.yml | 9 + .../deis/duffle/.vscode/launch.json | 28 + vendor/github.com/deis/duffle/Dockerfile | 7 + vendor/github.com/deis/duffle/Gopkg.lock | 767 +++++++++++ vendor/github.com/deis/duffle/Gopkg.toml | 49 + vendor/github.com/deis/duffle/LICENSE | 21 + vendor/github.com/deis/duffle/Makefile | 92 ++ vendor/github.com/deis/duffle/NOTICE | 14 + vendor/github.com/deis/duffle/README.md | 69 + .../deis/duffle/azure-pipelines.yml | 31 + vendor/github.com/deis/duffle/brigade.js | 309 +++++ .../deis/duffle/cmd/duffle/build.go | 249 ++++ .../deis/duffle/cmd/duffle/bundle.go | 29 + .../deis/duffle/cmd/duffle/bundle_list.go | 119 ++ .../deis/duffle/cmd/duffle/bundle_pull.go | 58 + .../deis/duffle/cmd/duffle/bundle_push.go | 62 + .../deis/duffle/cmd/duffle/bundle_remove.go | 156 +++ .../deis/duffle/cmd/duffle/bundle_sign.go | 181 +++ .../deis/duffle/cmd/duffle/bundle_verify.go | 87 ++ .../deis/duffle/cmd/duffle/claims.go | 31 + .../deis/duffle/cmd/duffle/claims_list.go | 26 + .../deis/duffle/cmd/duffle/claims_show.go | 57 + .../deis/duffle/cmd/duffle/create.go | 71 + .../deis/duffle/cmd/duffle/credential_add.go | 144 ++ .../deis/duffle/cmd/duffle/credential_edit.go | 91 ++ .../duffle/cmd/duffle/credential_generate.go | 180 +++ .../deis/duffle/cmd/duffle/credential_list.go | 97 ++ .../duffle/cmd/duffle/credential_remove.go | 78 ++ .../deis/duffle/cmd/duffle/credential_show.go | 81 ++ .../deis/duffle/cmd/duffle/credentials.go | 57 + .../deis/duffle/cmd/duffle/default_user.go | 38 + .../deis/duffle/cmd/duffle/duffle.go | 22 + .../deis/duffle/cmd/duffle/export.go | 80 ++ .../deis/duffle/cmd/duffle/import.go | 76 ++ .../github.com/deis/duffle/cmd/duffle/init.go | 278 ++++ .../deis/duffle/cmd/duffle/inspect.go | 51 + .../deis/duffle/cmd/duffle/install.go | 291 ++++ .../github.com/deis/duffle/cmd/duffle/key.go | 27 + .../deis/duffle/cmd/duffle/key_export.go | 91 ++ .../deis/duffle/cmd/duffle/key_import.go | 74 + .../deis/duffle/cmd/duffle/key_list.go | 96 ++ .../github.com/deis/duffle/cmd/duffle/list.go | 61 + .../github.com/deis/duffle/cmd/duffle/main.go | 128 ++ .../github.com/deis/duffle/cmd/duffle/pull.go | 186 +++ .../github.com/deis/duffle/cmd/duffle/push.go | 74 + .../github.com/deis/duffle/cmd/duffle/root.go | 66 + .../github.com/deis/duffle/cmd/duffle/run.go | 102 ++ .../deis/duffle/cmd/duffle/search.go | 176 +++ .../deis/duffle/cmd/duffle/status.go | 85 ++ .../dufflehome/credentials/another.yaml | 14 + .../dufflehome/credentials/example.yaml | 14 + .../dufflehome/credentials/testing.yaml | 7 + .../invalidhome/credentials/invalid.yaml | 9 + .../malformedhome/credentials/example.yaml | 14 + .../malformedhome/credentials/malformed.yaml | 1 + .../duffle/testdata/testbundle/duffle.json | 10 + .../deis/duffle/cmd/duffle/uninstall.go | 116 ++ .../deis/duffle/cmd/duffle/upgrade.go | 123 ++ .../deis/duffle/cmd/duffle/version.go | 29 + vendor/github.com/deis/duffle/docs/README.md | 14 + .../github.com/deis/duffle/docs/debugging.md | 38 + .../deis/duffle/docs/guides/bundle-guide.md | 143 ++ .../deis/duffle/docs/guides/signing-guide.md | 132 ++ .../duffle/docs/images/signals-duffle.png | Bin 0 -> 76041 bytes .../deis/duffle/docs/proposal/200-duffle.md | 101 ++ .../duffle/docs/proposal/201-credentialset.md | 157 +++ .../deis/duffle/docs/proposal/202-drivers.md | 127 ++ .../duffle/docs/proposal/203-duffle-build.md | 5 + .../deis/duffle/drivers/azure-vm/README.md | 78 ++ .../duffle/drivers/azure-vm/azure-packer.json | 48 + .../duffle/drivers/azure-vm/cnab-azure-vm.py | 61 + .../duffle/drivers/azure-vm/duffle-azvm.sh | 22 + .../duffle/drivers/azure-vm/requirements.txt | 16 + .../deis/duffle/examples/duffle-foo | 19 + .../deis/duffle/examples/helloazure/README.md | 8 + .../duffle/examples/helloazure/cnab/app/run | 28 + .../examples/helloworld/cnab/Dockerfile | 7 + .../duffle/examples/helloworld/cnab/app/run | 30 + .../helloworld/cnab/sigs/bundle.json.asc | 29 + .../duffle/examples/helloworld/duffle.toml | 24 + .../examples/multi-component/cnab/Dockerfile | 7 + .../examples/multi-component/cnab/app/run | 28 + .../multi-component/component-a/Dockerfile | 1 + .../multi-component/component-b/Dockerfile | 1 + .../examples/multi-component/duffle.toml | 22 + .../multi-component/mock-builder/mock | 1 + .../deis/duffle/examples/values.toml | 2 + vendor/github.com/deis/duffle/golangci.yml | 19 + .../deis/duffle/pkg/action/action.go | 87 ++ .../deis/duffle/pkg/action/install.go | 36 + .../deis/duffle/pkg/action/run_custom.go | 67 + .../deis/duffle/pkg/action/status.go | 28 + .../deis/duffle/pkg/action/uninstall.go | 35 + .../deis/duffle/pkg/action/upgrade.go | 35 + .../deis/duffle/pkg/builder/builder.go | 182 +++ .../deis/duffle/pkg/builder/docker/builder.go | 197 +++ .../deis/duffle/pkg/builder/errors.go | 8 + .../deis/duffle/pkg/builder/mock/builder.go | 47 + .../deis/duffle/pkg/builder/summary.go | 45 + .../deis/duffle/pkg/builder/ulid.go | 23 + .../deis/duffle/pkg/bundle/bundle.go | 206 +++ .../deis/duffle/pkg/bundle/parameters.go | 180 +++ .../pkg/bundle/replacement/jsonreplacer.go | 55 + .../duffle/pkg/bundle/replacement/replacer.go | 14 + .../duffle/pkg/bundle/replacement/utils.go | 36 + .../pkg/bundle/replacement/yamlreplacer.go | 52 + .../github.com/deis/duffle/pkg/claim/claim.go | 95 ++ .../deis/duffle/pkg/claim/claimstore.go | 77 ++ .../duffle/pkg/credentials/credentialset.go | 166 +++ .../pkg/credentials/testdata/someconfig.txt | 1 + .../pkg/credentials/testdata/staging.yaml | 19 + .../deis/duffle/pkg/crypto/digest/digest.go | 37 + .../deis/duffle/pkg/driver/command_driver.go | 75 + .../deis/duffle/pkg/driver/docker_driver.go | 208 +++ .../deis/duffle/pkg/driver/driver.go | 107 ++ .../deis/duffle/pkg/duffle/create.go | 6 + .../deis/duffle/pkg/duffle/home/home.go | 93 ++ .../deis/duffle/pkg/duffle/manifest/create.go | 75 + .../deis/duffle/pkg/duffle/manifest/load.go | 29 + .../duffle/pkg/duffle/manifest/manifest.go | 49 + .../pkg/duffle/manifest/testdata/duffle.json | 25 + .../pkg/duffle/manifest/testdata/duffle.toml | 14 + .../pkg/duffle/manifest/testdata/duffle.yaml | 16 + .../github.com/deis/duffle/pkg/image/pull.go | 76 ++ .../deis/duffle/pkg/image/pull_bundle.go | 36 + .../deis/duffle/pkg/image/push_bundle.go | 169 +++ .../deis/duffle/pkg/image/resolver.go | 97 ++ vendor/github.com/deis/duffle/pkg/io/multi.go | 63 + .../duffle/pkg/loader/detecting_loader.go | 46 + .../deis/duffle/pkg/loader/loader.go | 21 + .../deis/duffle/pkg/loader/secure_loader.go | 42 + .../deis/duffle/pkg/loader/unsigned_loader.go | 70 + .../github.com/deis/duffle/pkg/ohai/ohai.go | 96 ++ .../deis/duffle/pkg/osutil/osutil.go | 47 + .../deis/duffle/pkg/packager/export.go | 176 +++ .../deis/duffle/pkg/packager/import.go | 128 ++ .../packager/testdata/examplebun-0.1.0.tgz | Bin 0 -> 415 bytes .../pkg/packager/testdata/examplebun/cnab/run | 1 + .../pkg/packager/testdata/malformed-0.1.0.tgz | Bin 0 -> 1023 bytes .../github.com/deis/duffle/pkg/repo/index.go | 192 +++ .../deis/duffle/pkg/repo/remote/index.go | 195 +++ .../deis/duffle/pkg/signature/doc.go | 9 + .../deis/duffle/pkg/signature/keyring.go | 348 +++++ .../deis/duffle/pkg/signature/keys.go | 143 ++ .../deis/duffle/pkg/signature/signature.go | 153 +++ .../duffle/pkg/signature/testdata/README.md | 26 + .../duffle/pkg/signature/testdata/broken.gpg | Bin 0 -> 1455 bytes .../duffle/pkg/signature/testdata/extra.asc | 58 + .../duffle/pkg/signature/testdata/extra.gpg | 58 + .../duffle/pkg/signature/testdata/extra.kbx | Bin 0 -> 1457 bytes .../duffle/pkg/signature/testdata/extra.kbx~ | Bin 0 -> 32 bytes .../pkg/signature/testdata/extra1-public.key | Bin 0 -> 1235 bytes .../pkg/signature/testdata/fail-signed.json | 3 + .../signature/testdata/fail-signed.json.asc | 18 + .../duffle/pkg/signature/testdata/keyring.asc | 113 ++ .../duffle/pkg/signature/testdata/keyring.gpg | Bin 0 -> 5147 bytes .../duffle/pkg/signature/testdata/keyring.kbx | Bin 0 -> 2864 bytes .../pkg/signature/testdata/keyring.kbx~ | Bin 0 -> 1455 bytes .../pkg/signature/testdata/noclobber.empty | 0 .../duffle/pkg/signature/testdata/public.asc | 57 + .../duffle/pkg/signature/testdata/public.gpg | Bin 0 -> 2452 bytes .../duffle/pkg/signature/testdata/signed.json | 3 + .../pkg/signature/testdata/signed.json.asc | 18 + .../deis/duffle/pkg/signature/user_id.go | 67 + .../deis/duffle/pkg/signature/verifier.go | 68 + .../deis/duffle/pkg/utils/crud/filesystem.go | 115 ++ .../deis/duffle/pkg/utils/crud/store.go | 9 + .../deis/duffle/pkg/version/version.go | 4 + .../tests/testdata/builder/simple/Dockerfile | 1 + .../duffle/tests/testdata/bundles/foo.json | 44 + .../tests/testdata/home/bundles/.gitkeep | 0 .../tests/testdata/home/repositories.json | 8 + .../customorg/duffle-bundles/bundles/foo.json | 32 + .../deis/bundles.git/bundles/foo.json | 44 + vendor/github.com/dgrijalva/jwt-go/LICENSE | 8 - vendor/github.com/dgrijalva/jwt-go/doc.go | 4 - vendor/github.com/dgrijalva/jwt-go/ecdsa.go | 147 -- .../dgrijalva/jwt-go/ecdsa_utils.go | 67 - vendor/github.com/dgrijalva/jwt-go/errors.go | 51 - vendor/github.com/dgrijalva/jwt-go/hmac.go | 94 -- vendor/github.com/dgrijalva/jwt-go/none.go | 52 - vendor/github.com/dgrijalva/jwt-go/parser.go | 138 -- vendor/github.com/dgrijalva/jwt-go/rsa.go | 114 -- vendor/github.com/dgrijalva/jwt-go/rsa_pss.go | 126 -- .../github.com/dgrijalva/jwt-go/rsa_utils.go | 68 - .../dgrijalva/jwt-go/signing_method.go | 24 - vendor/github.com/dgrijalva/jwt-go/token.go | 126 -- .../github.com/docker/cli/cli/command/cli.go | 266 ++-- .../docker/cli/cli/command/context.go | 38 + .../docker/cli/cli/command/context/cmd.go | 29 + .../docker/cli/cli/command/context/create.go | 87 ++ .../docker/cli/cli/command/context/export.go | 93 ++ .../cli/command/context/formatter/context.go | 81 ++ .../docker/cli/cli/command/context/import.go | 60 + .../docker/cli/cli/command/context/list.go | 86 ++ .../docker/cli/cli/command/context/options.go | 189 +++ .../docker/cli/cli/command/context/remove.go | 32 + .../cli/cli/command/context/setdocker.go | 29 + .../cli/cli/command/context/setkubernetes.go | 41 + .../cli/cli/command/context/setoptions.go | 46 + .../cli/cli/command/formatter/buildcache.go | 40 +- .../cli/cli/command/formatter/checkpoint.go | 52 - .../cli/cli/command/formatter/config.go | 171 --- .../cli/cli/command/formatter/container.go | 47 +- .../cli/cli/command/formatter/custom.go | 42 +- .../docker/cli/cli/command/formatter/diff.go | 72 - .../cli/cli/command/formatter/disk_usage.go | 133 +- .../cli/cli/command/formatter/formatter.go | 12 +- .../cli/cli/command/formatter/history.go | 109 -- .../docker/cli/cli/command/formatter/image.go | 18 +- .../cli/cli/command/formatter/licenses.go | 154 --- .../cli/cli/command/formatter/network.go | 129 -- .../cli/cli/command/formatter/plugin.go | 94 -- .../cli/cli/command/formatter/reflect.go | 4 +- .../cli/cli/command/formatter/search.go | 103 -- .../cli/cli/command/formatter/secret.go | 178 --- .../docker/cli/cli/command/formatter/stats.go | 224 --- .../docker/cli/cli/command/formatter/trust.go | 135 -- .../cli/cli/command/formatter/updates.go | 73 - .../cli/cli/command/formatter/volume.go | 24 +- .../{formatter/node.go => node/formatter.go} | 48 +- .../docker/cli/cli/command/node/inspect.go | 4 +- .../docker/cli/cli/command/node/list.go | 4 +- .../docker/cli/cli/command/orchestrator.go | 15 +- .../service.go => service/formatter.go} | 57 +- .../docker/cli/cli/command/service/inspect.go | 4 +- .../docker/cli/cli/command/service/list.go | 16 +- .../docker/cli/cli/command/stack/cmd.go | 20 +- .../docker/cli/cli/command/stack/common.go | 19 + .../docker/cli/cli/command/stack/deploy.go | 17 +- .../stack.go => stack/formatter/formatter.go} | 23 +- .../cli/cli/command/stack/kubernetes/cli.go | 7 +- .../command/stack/kubernetes/conversion.go | 10 +- .../cli/cli/command/stack/kubernetes/list.go | 2 +- .../cli/cli/command/stack/kubernetes/ps.go | 6 +- .../cli/command/stack/kubernetes/services.go | 9 +- .../docker/cli/cli/command/stack/list.go | 2 +- .../docker/cli/cli/command/stack/ps.go | 22 +- .../docker/cli/cli/command/stack/remove.go | 22 +- .../docker/cli/cli/command/stack/services.go | 22 +- .../cli/cli/command/stack/swarm/list.go | 2 +- .../cli/cli/command/stack/swarm/services.go | 8 +- .../{formatter/task.go => task/formatter.go} | 33 +- .../docker/cli/cli/command/task/print.go | 4 +- .../docker/cli/cli/compose/convert/volume.go | 24 +- .../docker/cli/cli/compose/loader/loader.go | 4 +- .../cli/cli/compose/template/template.go | 16 +- .../docker/cli/cli/config/config.go | 6 + .../docker/cli/cli/connhelper/connhelper.go | 83 +- .../cli/cli/context/docker/constants.go | 7 + .../docker/cli/cli/context/docker/load.go | 176 +++ .../docker/cli/cli/context/docker/save.go | 33 + .../docker/cli/cli/context/endpoint.go | 43 + .../cli/cli/context/kubernetes/constants.go | 7 + .../docker/cli/cli/context/kubernetes/load.go | 100 ++ .../docker/cli/cli/context/kubernetes/save.go | 89 ++ .../docker/cli/cli/context/store/doc.go | 21 + .../cli/cli/context/store/metadatastore.go | 108 ++ .../docker/cli/cli/context/store/store.go | 304 +++++ .../docker/cli/cli/context/store/tlsstore.go | 99 ++ .../docker/cli/cli/context/tlsdata.go | 92 ++ .../github.com/docker/cli/cli/flags/common.go | 4 + .../docker/cli/cli/registry/client/fetcher.go | 14 +- .../docker/cli/internal/licenseutils/types.go | 25 - .../docker/cli/internal/licenseutils/utils.go | 189 --- .../docker/cli/kubernetes/config.go | 5 +- vendor/github.com/docker/cli/opts/hosts.go | 2 + vendor/github.com/docker/cli/types/types.go | 43 +- .../integration-cli/fixtures/https/ca.pem | 1 - .../fixtures/https/client-cert.pem | 1 - .../fixtures/https/client-key.pem | 1 - .../fixtures/https/server-cert.pem | 1 - .../fixtures/https/server-key.pem | 1 - .../docker/docker/project/CONTRIBUTING.md | 1 - .../docker/go-connections/nat/nat.go | 2 +- .../tlsconfig/certpool_other.go | 1 - .../docker/go-connections/tlsconfig/config.go | 26 +- .../github.com/docker/licensing/accounts.go | 32 - vendor/github.com/docker/licensing/client.go | 279 ---- .../docker/licensing/lib/errors/error.go | 112 -- .../docker/licensing/lib/errors/herror.go | 101 -- .../docker/licensing/lib/errors/stack.go | 65 - .../docker/licensing/lib/errors/wrap.go | 106 -- .../lib/go-auth/identity/identity.go | 45 - .../licensing/lib/go-auth/jwt/context.go | 20 - .../docker/licensing/lib/go-auth/jwt/jwt.go | 282 ---- .../licensing/lib/go-clientlib/client.go | 305 ----- .../licensing/lib/go-validation/validation.go | 141 -- vendor/github.com/docker/licensing/license.go | 148 -- .../docker/licensing/model/accounts.go | 61 - .../docker/licensing/model/license.go | 38 - .../docker/licensing/model/subscriptions.go | 189 --- .../docker/licensing/model/users.go | 95 -- vendor/github.com/docker/licensing/storage.go | 208 --- .../docker/licensing/subscriptions.go | 74 - vendor/github.com/docker/licensing/users.go | 90 -- .../govalidator => genuinetools/reg}/LICENSE | 4 +- .../reg/registry/authchallenge.go | 73 + .../reg/registry/basictransport.go | 25 + .../genuinetools/reg/registry/catalog.go | 39 + .../reg/registry/customtransport.go | 24 + .../genuinetools/reg/registry/delete.go | 35 + .../genuinetools/reg/registry/digest.go | 39 + .../reg/registry/errortransport.go | 46 + .../genuinetools/reg/registry/image.go | 67 + .../genuinetools/reg/registry/layer.go | 92 ++ .../genuinetools/reg/registry/manifest.go | 125 ++ .../genuinetools/reg/registry/ping.go | 12 + .../genuinetools/reg/registry/registry.go | 170 +++ .../genuinetools/reg/registry/tags.go | 18 + .../reg/registry/tokentransport.go | 226 +++ vendor/github.com/oklog/ulid/AUTHORS.md | 2 + vendor/github.com/oklog/ulid/LICENSE | 201 +++ vendor/github.com/oklog/ulid/ulid.go | 614 +++++++++ vendor/github.com/peterhellberg/link/doc.go | 40 + vendor/github.com/peterhellberg/link/link.go | 111 ++ vendor/github.com/satori/go.uuid/LICENSE | 20 - vendor/github.com/satori/go.uuid/codec.go | 206 --- vendor/github.com/satori/go.uuid/generator.go | 239 ---- vendor/github.com/satori/go.uuid/sql.go | 78 -- vendor/github.com/satori/go.uuid/uuid.go | 161 --- vendor/golang.org/x/crypto/cast5/cast5.go | 526 +++++++ .../x/crypto/openpgp/armor/armor.go | 219 +++ .../x/crypto/openpgp/armor/encode.go | 160 +++ .../x/crypto/openpgp/canonical_text.go | 59 + .../x/crypto/openpgp/clearsign/clearsign.go | 376 +++++ .../x/crypto/openpgp/elgamal/elgamal.go | 122 ++ .../x/crypto/openpgp/errors/errors.go | 72 + vendor/golang.org/x/crypto/openpgp/keys.go | 652 +++++++++ .../x/crypto/openpgp/packet/compressed.go | 123 ++ .../x/crypto/openpgp/packet/config.go | 91 ++ .../x/crypto/openpgp/packet/encrypted_key.go | 206 +++ .../x/crypto/openpgp/packet/literal.go | 89 ++ .../x/crypto/openpgp/packet/ocfb.go | 143 ++ .../openpgp/packet/one_pass_signature.go | 73 + .../x/crypto/openpgp/packet/opaque.go | 162 +++ .../x/crypto/openpgp/packet/packet.go | 549 ++++++++ .../x/crypto/openpgp/packet/private_key.go | 380 ++++++ .../x/crypto/openpgp/packet/public_key.go | 753 ++++++++++ .../x/crypto/openpgp/packet/public_key_v3.go | 279 ++++ .../x/crypto/openpgp/packet/reader.go | 76 ++ .../x/crypto/openpgp/packet/signature.go | 731 ++++++++++ .../x/crypto/openpgp/packet/signature_v3.go | 146 ++ .../openpgp/packet/symmetric_key_encrypted.go | 155 +++ .../openpgp/packet/symmetrically_encrypted.go | 290 ++++ .../x/crypto/openpgp/packet/userattribute.go | 91 ++ .../x/crypto/openpgp/packet/userid.go | 160 +++ vendor/golang.org/x/crypto/openpgp/read.go | 442 ++++++ vendor/golang.org/x/crypto/openpgp/s2k/s2k.go | 273 ++++ vendor/golang.org/x/crypto/openpgp/write.go | 416 ++++++ vendor/k8s.io/kubernetes/.bazelrc | 1 - vendor/k8s.io/kubernetes/.kazelcfg.json | 1 - vendor/k8s.io/kubernetes/BUILD.bazel | 1 - vendor/k8s.io/kubernetes/Makefile | 1 - .../kubernetes/Makefile.generated_files | 1 - vendor/k8s.io/kubernetes/WORKSPACE | 1 - vendor/k8s.io/kubernetes/cluster/gce/cos | 1 - vendor/k8s.io/kubernetes/cluster/gce/custom | 1 - vendor/k8s.io/kubernetes/cluster/gce/ubuntu | 1 - .../actions/namespace-delete | 1 - .../kubernetes-master/actions/namespace-list | 1 - 540 files changed, 28079 insertions(+), 12374 deletions(-) create mode 100644 cmd/docker-app/bundle.go create mode 100644 cmd/docker-app/bundle_test.go create mode 100644 cmd/docker-app/credentialset.go delete mode 100644 cmd/docker-app/deploy.go delete mode 100644 cmd/docker-app/fork.go delete mode 100644 cmd/docker-app/helm.go create mode 100644 cmd/docker-app/install.go create mode 100644 cmd/docker-app/status.go create mode 100644 cmd/docker-app/uninstall.go create mode 100644 cmd/docker-app/upgrade.go create mode 100644 cmd/run/env.go create mode 100644 cmd/run/inspect.go create mode 100644 cmd/run/install.go create mode 100644 cmd/run/main.go create mode 100644 cmd/run/status.go create mode 100644 cmd/run/uninstall.go rename e2e/testdata/attachments.dockerapp/{settings.yml => parameters.yml} (100%) delete mode 100644 e2e/testdata/expected-fork-metadata-no-rename.golden delete mode 100644 e2e/testdata/expected-fork-metadata.golden delete mode 100644 e2e/testdata/helm-expected.chart/Chart.yaml delete mode 100644 e2e/testdata/helm-expected.chart/templates/stack.yaml delete mode 100644 e2e/testdata/helm-expected.chart/templates/stackv1beta1.yaml delete mode 100644 e2e/testdata/helm-expected.chart/templates/stackv1beta2.yaml delete mode 100644 e2e/testdata/helm-expected.chart/values.yaml delete mode 100644 e2e/testdata/helm.dockerapp/settings.yml rename e2e/testdata/render/envvariables/my.dockerapp/{settings.yml => parameters.yml} (100%) rename e2e/testdata/render/envvariables/{settings-0.yml => parameters-0.yml} (100%) rename e2e/testdata/{fork => simple}/simple.dockerapp/docker-compose.yml (100%) rename e2e/testdata/{fork => simple}/simple.dockerapp/metadata.yml (100%) rename e2e/testdata/{fork/simple.dockerapp/settings.yml => simple/simple.dockerapp/parameters.yml} (100%) rename e2e/testdata/templates/gotemplate/my.dockerapp/{settings.yml => parameters.yml} (100%) rename e2e/testdata/templates/gotemplate/{settings-0.yml => parameters-0.yml} (100%) rename e2e/testdata/templates/mustache/my.dockerapp/{settings.yml => parameters.yml} (100%) rename e2e/testdata/templates/mustache/{settings-0.yml => parameters-0.yml} (100%) rename examples/voting-app/voting-app.dockerapp/{settings.yml => parameters.yml} (100%) delete mode 100644 examples/voting-app/voting-app.dockerapp/settings/development.yml delete mode 100644 examples/voting-app/voting-app.dockerapp/settings/production.yml rename examples/wordpress/{prod-settings.yml => prod-parameters.yml} (100%) delete mode 100644 examples/wordpress/wordpress.dockerapp/settings.yml delete mode 100644 integrations/gradle/example/simple.dockerapp/settings.yml rename integrations/intellij/src/main/java/{Settings.java => Parameters.java} (70%) rename integrations/intellij/src/main/java/{SettingsDialog.form => ParametersDialog.form} (98%) rename integrations/intellij/src/main/java/{SettingsDialog.java => ParametersDialog.java} (97%) rename integrations/visualstudio/dockerappvsix/{AppPackageSettings.cs => AppPackageParameters.cs} (87%) rename integrations/visualstudio/dockerappvsix/{CommandSettings.cs => CommandParameters.cs} (80%) rename integrations/visualstudio/dockerappvsix/{NewAppSettings.cs => NewAppParameters.cs} (90%) rename integrations/visualstudio/dockerappvsix/{SettingsDialog.xaml => ParametersDialog.xaml} (92%) rename integrations/visualstudio/dockerappvsix/{SettingsDialog.xaml.cs => ParametersDialog.xaml.cs} (76%) rename integrations/visualstudio/dockerappvsix/Resources/{CommandSettings.png => CommandParameters.png} (100%) delete mode 100644 internal/helm/helm.go delete mode 100644 internal/helm/templateconversion/convert.go delete mode 100644 internal/helm/templateloader/interpolate.go delete mode 100644 internal/helm/templateloader/loader.go delete mode 100644 internal/helm/templateloader/volume.go delete mode 100644 internal/helm/templatetypes/types.go delete mode 100644 internal/helm/templatev1beta2/stack.go rename internal/inspect/testdata/{inspect-no-settings.golden => inspect-no-parameters.golden} (100%) create mode 100644 internal/packager/cnab.go create mode 100644 internal/packager/cnab_test.go create mode 100644 internal/packager/errors.go delete mode 100644 internal/packager/extract_test.go delete mode 100644 internal/packager/fork.go delete mode 100644 internal/packager/fork_test.go create mode 100644 internal/packager/packing_test.go create mode 100644 internal/packager/parameter.go create mode 100644 internal/packager/parameter_test.go create mode 100644 internal/packager/testdata/packages/packing.dockerapp/config.cfg rename {e2e/testdata/helm.dockerapp => internal/packager/testdata/packages/packing.dockerapp}/docker-compose.yml (83%) rename {e2e/testdata/helm.dockerapp => internal/packager/testdata/packages/packing.dockerapp}/metadata.yml (66%) create mode 100644 internal/packager/testdata/packages/packing.dockerapp/nesteddir/config2.cfg create mode 100644 internal/packager/testdata/packages/packing.dockerapp/nesteddir/nested2/nested3/config3.cfg create mode 100644 internal/packager/testdata/packages/packing.dockerapp/parameters.yml create mode 100644 internal/packager/testdata/parameters.yml rename types/{settings => parameters}/load.go (68%) rename types/{settings => parameters}/load_test.go (70%) create mode 100644 types/parameters/merge.go rename types/{settings => parameters}/merge_test.go (88%) rename types/{settings => parameters}/opts.go (52%) rename types/{settings/settings.go => parameters/parameters.go} (85%) rename types/{settings/settings_test.go => parameters/parameters_test.go} (95%) delete mode 100644 types/settings/merge.go create mode 100644 vendor/github.com/Masterminds/semver/LICENSE.txt create mode 100644 vendor/github.com/Masterminds/semver/collection.go create mode 100644 vendor/github.com/Masterminds/semver/constraints.go create mode 100644 vendor/github.com/Masterminds/semver/doc.go create mode 100644 vendor/github.com/Masterminds/semver/version.go delete mode 100644 vendor/github.com/asaskevich/govalidator/arrays.go delete mode 100644 vendor/github.com/asaskevich/govalidator/converter.go delete mode 100644 vendor/github.com/asaskevich/govalidator/error.go delete mode 100644 vendor/github.com/asaskevich/govalidator/numerics.go delete mode 100644 vendor/github.com/asaskevich/govalidator/patterns.go delete mode 100644 vendor/github.com/asaskevich/govalidator/types.go delete mode 100644 vendor/github.com/asaskevich/govalidator/utils.go delete mode 100644 vendor/github.com/asaskevich/govalidator/validator.go delete mode 120000 vendor/github.com/coreos/etcd/Documentation/README.md delete mode 120000 vendor/github.com/coreos/etcd/cmd/etcd delete mode 120000 vendor/github.com/coreos/etcd/cmd/etcdctl delete mode 120000 vendor/github.com/coreos/etcd/cmd/functional delete mode 120000 vendor/github.com/coreos/etcd/cmd/tools create mode 100644 vendor/github.com/deis/duffle/.gitignore create mode 100644 vendor/github.com/deis/duffle/.travis.yml create mode 100644 vendor/github.com/deis/duffle/.vscode/launch.json create mode 100644 vendor/github.com/deis/duffle/Dockerfile create mode 100644 vendor/github.com/deis/duffle/Gopkg.lock create mode 100644 vendor/github.com/deis/duffle/Gopkg.toml create mode 100644 vendor/github.com/deis/duffle/LICENSE create mode 100644 vendor/github.com/deis/duffle/Makefile create mode 100644 vendor/github.com/deis/duffle/NOTICE create mode 100644 vendor/github.com/deis/duffle/README.md create mode 100644 vendor/github.com/deis/duffle/azure-pipelines.yml create mode 100644 vendor/github.com/deis/duffle/brigade.js create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/build.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/bundle.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/bundle_list.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/bundle_pull.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/bundle_push.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/bundle_remove.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/bundle_sign.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/bundle_verify.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/claims.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/claims_list.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/claims_show.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/create.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/credential_add.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/credential_edit.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/credential_generate.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/credential_list.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/credential_remove.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/credential_show.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/credentials.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/default_user.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/duffle.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/export.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/import.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/init.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/inspect.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/install.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/key.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/key_export.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/key_import.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/key_list.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/list.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/main.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/pull.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/push.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/root.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/run.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/search.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/status.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/testdata/dufflehome/credentials/another.yaml create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/testdata/dufflehome/credentials/example.yaml create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/testdata/dufflehome/credentials/testing.yaml create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/testdata/invalidhome/credentials/invalid.yaml create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/testdata/malformedhome/credentials/example.yaml create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/testdata/malformedhome/credentials/malformed.yaml create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/testdata/testbundle/duffle.json create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/uninstall.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/upgrade.go create mode 100644 vendor/github.com/deis/duffle/cmd/duffle/version.go create mode 100644 vendor/github.com/deis/duffle/docs/README.md create mode 100644 vendor/github.com/deis/duffle/docs/debugging.md create mode 100644 vendor/github.com/deis/duffle/docs/guides/bundle-guide.md create mode 100644 vendor/github.com/deis/duffle/docs/guides/signing-guide.md create mode 100644 vendor/github.com/deis/duffle/docs/images/signals-duffle.png create mode 100644 vendor/github.com/deis/duffle/docs/proposal/200-duffle.md create mode 100644 vendor/github.com/deis/duffle/docs/proposal/201-credentialset.md create mode 100644 vendor/github.com/deis/duffle/docs/proposal/202-drivers.md create mode 100644 vendor/github.com/deis/duffle/docs/proposal/203-duffle-build.md create mode 100644 vendor/github.com/deis/duffle/drivers/azure-vm/README.md create mode 100644 vendor/github.com/deis/duffle/drivers/azure-vm/azure-packer.json create mode 100644 vendor/github.com/deis/duffle/drivers/azure-vm/cnab-azure-vm.py create mode 100755 vendor/github.com/deis/duffle/drivers/azure-vm/duffle-azvm.sh create mode 100644 vendor/github.com/deis/duffle/drivers/azure-vm/requirements.txt create mode 100755 vendor/github.com/deis/duffle/examples/duffle-foo create mode 100644 vendor/github.com/deis/duffle/examples/helloazure/README.md create mode 100755 vendor/github.com/deis/duffle/examples/helloazure/cnab/app/run create mode 100644 vendor/github.com/deis/duffle/examples/helloworld/cnab/Dockerfile create mode 100755 vendor/github.com/deis/duffle/examples/helloworld/cnab/app/run create mode 100644 vendor/github.com/deis/duffle/examples/helloworld/cnab/sigs/bundle.json.asc create mode 100644 vendor/github.com/deis/duffle/examples/helloworld/duffle.toml create mode 100644 vendor/github.com/deis/duffle/examples/multi-component/cnab/Dockerfile create mode 100644 vendor/github.com/deis/duffle/examples/multi-component/cnab/app/run create mode 100644 vendor/github.com/deis/duffle/examples/multi-component/component-a/Dockerfile create mode 100644 vendor/github.com/deis/duffle/examples/multi-component/component-b/Dockerfile create mode 100644 vendor/github.com/deis/duffle/examples/multi-component/duffle.toml create mode 100644 vendor/github.com/deis/duffle/examples/multi-component/mock-builder/mock create mode 100644 vendor/github.com/deis/duffle/examples/values.toml create mode 100644 vendor/github.com/deis/duffle/golangci.yml create mode 100644 vendor/github.com/deis/duffle/pkg/action/action.go create mode 100644 vendor/github.com/deis/duffle/pkg/action/install.go create mode 100644 vendor/github.com/deis/duffle/pkg/action/run_custom.go create mode 100644 vendor/github.com/deis/duffle/pkg/action/status.go create mode 100644 vendor/github.com/deis/duffle/pkg/action/uninstall.go create mode 100644 vendor/github.com/deis/duffle/pkg/action/upgrade.go create mode 100644 vendor/github.com/deis/duffle/pkg/builder/builder.go create mode 100644 vendor/github.com/deis/duffle/pkg/builder/docker/builder.go create mode 100644 vendor/github.com/deis/duffle/pkg/builder/errors.go create mode 100644 vendor/github.com/deis/duffle/pkg/builder/mock/builder.go create mode 100644 vendor/github.com/deis/duffle/pkg/builder/summary.go create mode 100644 vendor/github.com/deis/duffle/pkg/builder/ulid.go create mode 100644 vendor/github.com/deis/duffle/pkg/bundle/bundle.go create mode 100644 vendor/github.com/deis/duffle/pkg/bundle/parameters.go create mode 100644 vendor/github.com/deis/duffle/pkg/bundle/replacement/jsonreplacer.go create mode 100644 vendor/github.com/deis/duffle/pkg/bundle/replacement/replacer.go create mode 100644 vendor/github.com/deis/duffle/pkg/bundle/replacement/utils.go create mode 100644 vendor/github.com/deis/duffle/pkg/bundle/replacement/yamlreplacer.go create mode 100644 vendor/github.com/deis/duffle/pkg/claim/claim.go create mode 100644 vendor/github.com/deis/duffle/pkg/claim/claimstore.go create mode 100644 vendor/github.com/deis/duffle/pkg/credentials/credentialset.go create mode 100644 vendor/github.com/deis/duffle/pkg/credentials/testdata/someconfig.txt create mode 100644 vendor/github.com/deis/duffle/pkg/credentials/testdata/staging.yaml create mode 100644 vendor/github.com/deis/duffle/pkg/crypto/digest/digest.go create mode 100644 vendor/github.com/deis/duffle/pkg/driver/command_driver.go create mode 100644 vendor/github.com/deis/duffle/pkg/driver/docker_driver.go create mode 100644 vendor/github.com/deis/duffle/pkg/driver/driver.go create mode 100644 vendor/github.com/deis/duffle/pkg/duffle/create.go create mode 100644 vendor/github.com/deis/duffle/pkg/duffle/home/home.go create mode 100644 vendor/github.com/deis/duffle/pkg/duffle/manifest/create.go create mode 100644 vendor/github.com/deis/duffle/pkg/duffle/manifest/load.go create mode 100644 vendor/github.com/deis/duffle/pkg/duffle/manifest/manifest.go create mode 100644 vendor/github.com/deis/duffle/pkg/duffle/manifest/testdata/duffle.json create mode 100644 vendor/github.com/deis/duffle/pkg/duffle/manifest/testdata/duffle.toml create mode 100644 vendor/github.com/deis/duffle/pkg/duffle/manifest/testdata/duffle.yaml create mode 100644 vendor/github.com/deis/duffle/pkg/image/pull.go create mode 100644 vendor/github.com/deis/duffle/pkg/image/pull_bundle.go create mode 100644 vendor/github.com/deis/duffle/pkg/image/push_bundle.go create mode 100644 vendor/github.com/deis/duffle/pkg/image/resolver.go create mode 100644 vendor/github.com/deis/duffle/pkg/io/multi.go create mode 100644 vendor/github.com/deis/duffle/pkg/loader/detecting_loader.go create mode 100644 vendor/github.com/deis/duffle/pkg/loader/loader.go create mode 100644 vendor/github.com/deis/duffle/pkg/loader/secure_loader.go create mode 100644 vendor/github.com/deis/duffle/pkg/loader/unsigned_loader.go create mode 100644 vendor/github.com/deis/duffle/pkg/ohai/ohai.go create mode 100644 vendor/github.com/deis/duffle/pkg/osutil/osutil.go create mode 100644 vendor/github.com/deis/duffle/pkg/packager/export.go create mode 100644 vendor/github.com/deis/duffle/pkg/packager/import.go create mode 100644 vendor/github.com/deis/duffle/pkg/packager/testdata/examplebun-0.1.0.tgz create mode 100644 vendor/github.com/deis/duffle/pkg/packager/testdata/examplebun/cnab/run create mode 100644 vendor/github.com/deis/duffle/pkg/packager/testdata/malformed-0.1.0.tgz create mode 100644 vendor/github.com/deis/duffle/pkg/repo/index.go create mode 100644 vendor/github.com/deis/duffle/pkg/repo/remote/index.go create mode 100644 vendor/github.com/deis/duffle/pkg/signature/doc.go create mode 100644 vendor/github.com/deis/duffle/pkg/signature/keyring.go create mode 100644 vendor/github.com/deis/duffle/pkg/signature/keys.go create mode 100644 vendor/github.com/deis/duffle/pkg/signature/signature.go create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/README.md create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/broken.gpg create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/extra.asc create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/extra.gpg create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/extra.kbx create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/extra.kbx~ create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/extra1-public.key create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/fail-signed.json create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/fail-signed.json.asc create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/keyring.asc create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/keyring.gpg create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/keyring.kbx create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/keyring.kbx~ create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/noclobber.empty create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/public.asc create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/public.gpg create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/signed.json create mode 100644 vendor/github.com/deis/duffle/pkg/signature/testdata/signed.json.asc create mode 100644 vendor/github.com/deis/duffle/pkg/signature/user_id.go create mode 100644 vendor/github.com/deis/duffle/pkg/signature/verifier.go create mode 100644 vendor/github.com/deis/duffle/pkg/utils/crud/filesystem.go create mode 100644 vendor/github.com/deis/duffle/pkg/utils/crud/store.go create mode 100644 vendor/github.com/deis/duffle/pkg/version/version.go create mode 100644 vendor/github.com/deis/duffle/tests/testdata/builder/simple/Dockerfile create mode 100644 vendor/github.com/deis/duffle/tests/testdata/bundles/foo.json create mode 100644 vendor/github.com/deis/duffle/tests/testdata/home/bundles/.gitkeep create mode 100644 vendor/github.com/deis/duffle/tests/testdata/home/repositories.json create mode 100644 vendor/github.com/deis/duffle/tests/testdata/home/repositories/github.com/customorg/duffle-bundles/bundles/foo.json create mode 100644 vendor/github.com/deis/duffle/tests/testdata/home/repositories/github.com/deis/bundles.git/bundles/foo.json delete mode 100644 vendor/github.com/dgrijalva/jwt-go/LICENSE delete mode 100644 vendor/github.com/dgrijalva/jwt-go/doc.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/ecdsa.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/errors.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/hmac.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/none.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/parser.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/rsa.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/rsa_pss.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/rsa_utils.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/signing_method.go delete mode 100644 vendor/github.com/dgrijalva/jwt-go/token.go create mode 100644 vendor/github.com/docker/cli/cli/command/context.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/cmd.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/create.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/export.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/formatter/context.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/import.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/list.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/options.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/remove.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/setdocker.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/setkubernetes.go create mode 100644 vendor/github.com/docker/cli/cli/command/context/setoptions.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/checkpoint.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/config.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/diff.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/history.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/licenses.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/network.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/plugin.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/search.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/secret.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/stats.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/trust.go delete mode 100644 vendor/github.com/docker/cli/cli/command/formatter/updates.go rename vendor/github.com/docker/cli/cli/command/{formatter/node.go => node/formatter.go} (86%) rename vendor/github.com/docker/cli/cli/command/{formatter/service.go => service/formatter.go} (92%) rename vendor/github.com/docker/cli/cli/command/{formatter/stack.go => stack/formatter/formatter.go} (73%) rename vendor/github.com/docker/cli/cli/command/{formatter/task.go => task/formatter.go} (81%) create mode 100644 vendor/github.com/docker/cli/cli/context/docker/constants.go create mode 100644 vendor/github.com/docker/cli/cli/context/docker/load.go create mode 100644 vendor/github.com/docker/cli/cli/context/docker/save.go create mode 100644 vendor/github.com/docker/cli/cli/context/endpoint.go create mode 100644 vendor/github.com/docker/cli/cli/context/kubernetes/constants.go create mode 100644 vendor/github.com/docker/cli/cli/context/kubernetes/load.go create mode 100644 vendor/github.com/docker/cli/cli/context/kubernetes/save.go create mode 100644 vendor/github.com/docker/cli/cli/context/store/doc.go create mode 100644 vendor/github.com/docker/cli/cli/context/store/metadatastore.go create mode 100644 vendor/github.com/docker/cli/cli/context/store/store.go create mode 100644 vendor/github.com/docker/cli/cli/context/store/tlsstore.go create mode 100644 vendor/github.com/docker/cli/cli/context/tlsdata.go delete mode 100644 vendor/github.com/docker/cli/internal/licenseutils/types.go delete mode 100644 vendor/github.com/docker/cli/internal/licenseutils/utils.go delete mode 120000 vendor/github.com/docker/docker/integration-cli/fixtures/https/ca.pem delete mode 120000 vendor/github.com/docker/docker/integration-cli/fixtures/https/client-cert.pem delete mode 120000 vendor/github.com/docker/docker/integration-cli/fixtures/https/client-key.pem delete mode 120000 vendor/github.com/docker/docker/integration-cli/fixtures/https/server-cert.pem delete mode 120000 vendor/github.com/docker/docker/integration-cli/fixtures/https/server-key.pem delete mode 120000 vendor/github.com/docker/docker/project/CONTRIBUTING.md delete mode 100644 vendor/github.com/docker/licensing/accounts.go delete mode 100644 vendor/github.com/docker/licensing/client.go delete mode 100644 vendor/github.com/docker/licensing/lib/errors/error.go delete mode 100644 vendor/github.com/docker/licensing/lib/errors/herror.go delete mode 100644 vendor/github.com/docker/licensing/lib/errors/stack.go delete mode 100644 vendor/github.com/docker/licensing/lib/errors/wrap.go delete mode 100644 vendor/github.com/docker/licensing/lib/go-auth/identity/identity.go delete mode 100644 vendor/github.com/docker/licensing/lib/go-auth/jwt/context.go delete mode 100644 vendor/github.com/docker/licensing/lib/go-auth/jwt/jwt.go delete mode 100644 vendor/github.com/docker/licensing/lib/go-clientlib/client.go delete mode 100644 vendor/github.com/docker/licensing/lib/go-validation/validation.go delete mode 100644 vendor/github.com/docker/licensing/license.go delete mode 100644 vendor/github.com/docker/licensing/model/accounts.go delete mode 100644 vendor/github.com/docker/licensing/model/license.go delete mode 100644 vendor/github.com/docker/licensing/model/subscriptions.go delete mode 100644 vendor/github.com/docker/licensing/model/users.go delete mode 100644 vendor/github.com/docker/licensing/storage.go delete mode 100644 vendor/github.com/docker/licensing/subscriptions.go delete mode 100644 vendor/github.com/docker/licensing/users.go rename vendor/github.com/{asaskevich/govalidator => genuinetools/reg}/LICENSE (95%) create mode 100644 vendor/github.com/genuinetools/reg/registry/authchallenge.go create mode 100644 vendor/github.com/genuinetools/reg/registry/basictransport.go create mode 100644 vendor/github.com/genuinetools/reg/registry/catalog.go create mode 100644 vendor/github.com/genuinetools/reg/registry/customtransport.go create mode 100644 vendor/github.com/genuinetools/reg/registry/delete.go create mode 100644 vendor/github.com/genuinetools/reg/registry/digest.go create mode 100644 vendor/github.com/genuinetools/reg/registry/errortransport.go create mode 100644 vendor/github.com/genuinetools/reg/registry/image.go create mode 100644 vendor/github.com/genuinetools/reg/registry/layer.go create mode 100644 vendor/github.com/genuinetools/reg/registry/manifest.go create mode 100644 vendor/github.com/genuinetools/reg/registry/ping.go create mode 100644 vendor/github.com/genuinetools/reg/registry/registry.go create mode 100644 vendor/github.com/genuinetools/reg/registry/tags.go create mode 100644 vendor/github.com/genuinetools/reg/registry/tokentransport.go create mode 100644 vendor/github.com/oklog/ulid/AUTHORS.md create mode 100644 vendor/github.com/oklog/ulid/LICENSE create mode 100644 vendor/github.com/oklog/ulid/ulid.go create mode 100644 vendor/github.com/peterhellberg/link/doc.go create mode 100644 vendor/github.com/peterhellberg/link/link.go delete mode 100644 vendor/github.com/satori/go.uuid/LICENSE delete mode 100644 vendor/github.com/satori/go.uuid/codec.go delete mode 100644 vendor/github.com/satori/go.uuid/generator.go delete mode 100644 vendor/github.com/satori/go.uuid/sql.go delete mode 100644 vendor/github.com/satori/go.uuid/uuid.go create mode 100644 vendor/golang.org/x/crypto/cast5/cast5.go create mode 100644 vendor/golang.org/x/crypto/openpgp/armor/armor.go create mode 100644 vendor/golang.org/x/crypto/openpgp/armor/encode.go create mode 100644 vendor/golang.org/x/crypto/openpgp/canonical_text.go create mode 100644 vendor/golang.org/x/crypto/openpgp/clearsign/clearsign.go create mode 100644 vendor/golang.org/x/crypto/openpgp/elgamal/elgamal.go create mode 100644 vendor/golang.org/x/crypto/openpgp/errors/errors.go create mode 100644 vendor/golang.org/x/crypto/openpgp/keys.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/compressed.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/config.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/encrypted_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/literal.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/ocfb.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/one_pass_signature.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/opaque.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/packet.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/private_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/public_key.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/public_key_v3.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/reader.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/signature.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/signature_v3.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/symmetric_key_encrypted.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/symmetrically_encrypted.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/userattribute.go create mode 100644 vendor/golang.org/x/crypto/openpgp/packet/userid.go create mode 100644 vendor/golang.org/x/crypto/openpgp/read.go create mode 100644 vendor/golang.org/x/crypto/openpgp/s2k/s2k.go create mode 100644 vendor/golang.org/x/crypto/openpgp/write.go delete mode 120000 vendor/k8s.io/kubernetes/.bazelrc delete mode 120000 vendor/k8s.io/kubernetes/.kazelcfg.json delete mode 120000 vendor/k8s.io/kubernetes/BUILD.bazel delete mode 120000 vendor/k8s.io/kubernetes/Makefile delete mode 120000 vendor/k8s.io/kubernetes/Makefile.generated_files delete mode 120000 vendor/k8s.io/kubernetes/WORKSPACE delete mode 120000 vendor/k8s.io/kubernetes/cluster/gce/cos delete mode 120000 vendor/k8s.io/kubernetes/cluster/gce/custom delete mode 120000 vendor/k8s.io/kubernetes/cluster/gce/ubuntu delete mode 120000 vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-master/actions/namespace-delete delete mode 120000 vendor/k8s.io/kubernetes/cluster/juju/layers/kubernetes-master/actions/namespace-list diff --git a/.gitignore b/.gitignore index 7dd0384d7..f75bed783 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,8 @@ _build/ bin/ !examples/simple/*.dockerapp -!e2e/**/*.dockerapp +!**/testdata/**/*.dockerapp .gradle coverage.html cover.out +**/bundle.json diff --git a/BUILDING.md b/BUILDING.md index 298ef14ad..37e186458 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -30,7 +30,7 @@ end you need to have `GOPATH` set in your environment. At this point you can use `go` to checkout `docker-app` in your `GOPATH`: -```console +```sh go get github.com/docker/app ``` @@ -39,14 +39,14 @@ You are ready to build `docker-app` yourself! `docker-app` uses `make` to create a repeatable build flow. It means that you can run: -```console +```sh make ``` This is going to build all the project binaries in the `./bin/` directory, run tests (unit and end-to-end). -```console +```sh make bin/docker-app # builds the docker-app binary make bin/docker-app-darwin # builds the docker-app binary for darwin make bin/docker-app-windows.exe # builds the docker-app binary for windows @@ -59,16 +59,6 @@ make test-e2e # run the end-to-end tests Vendoring of external imports uses the [`dep`](https://github.com/golang/dep) tool. Please refer to its documentation if you need to update a dependency. -### Experimental - -Just add the `EXPERIMENTAL=on` flag before invoking a build target: -```console -$ make EXPERIMEMTAL=on bin/docker-app -$ ./bin/docker-app version -... -Experimental: on -``` - ## Build using Docker If you don't have Go installed but Docker is present, you can also use @@ -99,7 +89,7 @@ capabilities without using the `Makefile` targets. The following examples show how to specify a test name and also how to use the flag directly against `go test` to run root-requiring tests. -```console +```sh # run the test : go test -v -run "" . ``` diff --git a/Dockerfile b/Dockerfile index 86d7582c4..529cec876 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,13 +10,15 @@ RUN apk add --no-cache \ git \ curl \ util-linux \ - coreutils + coreutils \ + build-base RUN curl -Ls https://download.docker.com/linux/static/$DOCKERCLI_CHANNEL/x86_64/docker-$DOCKERCLI_VERSION.tgz | \ tar -xz docker/docker && \ mv docker/docker /usr/bin/docker WORKDIR /go/src/github.com/docker/app/ +# main dev image FROM build AS dev ENV PATH=${PATH}:/go/src/github.com/docker/app/bin/ ARG DEP_VERSION=v0.5.0 @@ -26,14 +28,39 @@ RUN go get -d gopkg.in/mjibson/esc.v0 && \ cd /go/src/github.com/mjibson/esc && \ go build -v -o /usr/bin/esc . && \ rm -rf /go/src/* /go/pkg/* /go/bin/* +COPY vendor/github.com/deis/duffle /go/src/github.com/deis/duffle +# Build duffle and init +RUN (cd /go/src/github.com/deis/duffle && \ + make bootstrap build-release && \ + ./bin/duffle-linux-amd64 init) COPY . . # FIXME(vdemeester) change from docker-app to dev once buildkit is merged in moby/docker FROM dev AS cross ARG EXPERIMENTAL="off" -RUN make EXPERIMENTAL=${EXPERIMENTAL} cross +ARG TAG +ARG COMMIT +RUN make EXPERIMENTAL=${EXPERIMENTAL} TAG=${TAG} COMMIT=${COMMIT} cross # FIXME(vdemeester) change from docker-app to dev once buildkit is merged in moby/docker FROM cross AS e2e-cross ARG EXPERIMENTAL="off" -RUN make EXPERIMENTAL=${EXPERIMENTAL} e2e-cross +# Run e2e tests +ARG TAG +ARG COMMIT +RUN make EXPERIMENTAL=${EXPERIMENTAL} TAG=${TAG} COMMIT=${COMMIT} e2e-cross + +# builder of invocation image entrypoint +FROM build AS invocation-build +COPY . . +ARG EXPERIMENTAL="off" +ARG TAG +ARG COMMIT +RUN make EXPERIMENTAL=${EXPERIMENTAL} TAG=${TAG} COMMIT=${COMMIT} bin/run + +# cnab invocation image +FROM alpine:${ALPINE_VERSION} AS invocation +RUN apk add --no-cache ca-certificates +COPY --from=invocation-build /go/src/github.com/docker/app/bin/run /cnab/app/run +WORKDIR /cnab/app +CMD /cnab/app/run diff --git a/Gopkg.lock b/Gopkg.lock index 39c24e25b..b51d64344 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -12,6 +12,14 @@ pruneopts = "NUT" revision = "d6e3b3328b783f23731bc4d058875b0371ff8109" +[[projects]] + digest = "1:a26f8da48b22e6176c1c6a2459904bb30bd0c49ada04b2963c2c3a203e81a620" + name = "github.com/Masterminds/semver" + packages = ["."] + pruneopts = "NUT" + revision = "c7af12943936e8c39859482e61f0574c2fd7fc75" + version = "v1.4.2" + [[projects]] digest = "1:99632bb2b4e2c066233ee9d19c1370a695ebd2be18625e31390476d356db5d8a" name = "github.com/Microsoft/go-winio" @@ -39,14 +47,6 @@ pruneopts = "NUT" revision = "5312a61534124124185d41f09206b9fef1d88403" -[[projects]] - digest = "1:5a23cd3a5496a0b2da7e3b348d14e77b11a210738c398200d7d6f04ea8cf3bd8" - name = "github.com/asaskevich/govalidator" - packages = ["."] - pruneopts = "NUT" - revision = "ccb8e960c48f04d6935e72476ae4a51028f9e22f" - version = "v9" - [[projects]] branch = "master" digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd" @@ -106,20 +106,37 @@ version = "v1.1.0" [[projects]] - digest = "1:8d9e9a68c0fd72cc68958c4e0388981cd916411db9af24fc0bab7ae6447c9432" - name = "github.com/dgrijalva/jwt-go" - packages = ["."] - pruneopts = "NUT" - revision = "v2.6.0" + branch = "dockercon-version" + digest = "1:556ecb25c7f3793960db62c5455ba73079c4929afc235acf8defa9f9ad980bfd" + name = "github.com/deis/duffle" + packages = [ + "pkg/action", + "pkg/bundle", + "pkg/claim", + "pkg/credentials", + "pkg/crypto/digest", + "pkg/driver", + "pkg/duffle/home", + "pkg/image", + "pkg/loader", + "pkg/repo", + "pkg/signature", + "pkg/utils/crud", + ] + pruneopts = "T" + revision = "5b87a9f0576e5da01f3be2b610797e07ff7e6711" + source = "git@github.com:simonferquel/duffle.git" [[projects]] - branch = "master" - digest = "1:98bc0775999b81adb85e06a356d80d195f75ac4f8aa25c1623aee3eb5c5ccf1c" + branch = "docker-app-fork" + digest = "1:9d912cbbb32ca589bdcbfa8c30e21533d3f7230c8222c5fc6e7770c7f72ddcb6" name = "github.com/docker/cli" packages = [ "cli", "cli/command", "cli/command/bundlefile", + "cli/command/context", + "cli/command/context/formatter", "cli/command/formatter", "cli/command/idresolver", "cli/command/inspect", @@ -127,6 +144,7 @@ "cli/command/service", "cli/command/service/progress", "cli/command/stack", + "cli/command/stack/formatter", "cli/command/stack/kubernetes", "cli/command/stack/loader", "cli/command/stack/options", @@ -143,13 +161,16 @@ "cli/config/credentials", "cli/connhelper", "cli/connhelper/ssh", + "cli/context", + "cli/context/docker", + "cli/context/kubernetes", + "cli/context/store", "cli/debug", "cli/flags", "cli/manifest/store", "cli/manifest/types", "cli/registry/client", "cli/trust", - "internal/licenseutils", "kubernetes", "kubernetes/client/clientset/scheme", "kubernetes/client/clientset/typed/compose/v1beta1", @@ -165,7 +186,8 @@ "types", ] pruneopts = "UT" - revision = "94efcf4886e6d4a1356c0dcf2228b709ade82df3" + revision = "2e4b9ab1898f4f8d062dbfa871783f2c43eb8e97" + source = "git@github.com:simonferquel/cli.git" [[projects]] digest = "1:1cb756deb458cb9be52d2e3fe67a19a7185b8c8453c691ba5eb0c7db00a51205" @@ -277,7 +299,7 @@ version = "v1.5.1-1" [[projects]] - digest = "1:f133477f38c590bdcd6fc534617df17983f7a21e5b686d4a3495abeb21c631ec" + digest = "1:2a47f7eb1a2c30428d1ee6808cb66d4deb17e68a3e55d696f03c8068552ba5e8" name = "github.com/docker/go-connections" packages = [ "nat", @@ -285,8 +307,7 @@ "tlsconfig", ] pruneopts = "NUT" - revision = "3ede32e2033de7505e6500d6c868c2b9ed9f169d" - version = "v0.3.0" + revision = "7395e3f8aa162843a74ed6d48e79627d9792ac55" [[projects]] branch = "master" @@ -319,21 +340,6 @@ pruneopts = "NUT" revision = "aabc10ec26b754e797f9028f4589c5b7bd90dc20" -[[projects]] - digest = "1:63255ad9ddd0cd677b1f5d70eaaf2dbb27241fecbedaa5ad3b1554fd2ac40efe" - name = "github.com/docker/licensing" - packages = [ - ".", - "lib/errors", - "lib/go-auth/identity", - "lib/go-auth/jwt", - "lib/go-clientlib", - "lib/go-validation", - "model", - ] - pruneopts = "NUT" - revision = "369e5301dc601b478021c0f588f5c3d04626d804" - [[projects]] digest = "1:155bae7a907260715d88c11d4d15c70e9b614c59fd79f0c2286ef7b2670c44ea" name = "github.com/docker/swarmkit" @@ -356,6 +362,14 @@ pruneopts = "NUT" revision = "3f9db97f856818214da2e1057f8ad84803971cff" +[[projects]] + digest = "1:0cb5b9c5e5754914abee038463a4c887f873861ec294c2535e3967e8042d3bcd" + name = "github.com/genuinetools/reg" + packages = ["registry"] + pruneopts = "NUT" + revision = "4a4d0e5d108ca9558879bdf1aba94d09e921cf1e" + version = "v0.16.0" + [[projects]] digest = "1:81466b4218bf6adddac2572a30ac733a9255919bc2f470b4827a317bd4ee1756" name = "github.com/ghodss/yaml" @@ -575,6 +589,14 @@ pruneopts = "NUT" revision = "39771216ff4c63d11f5e604076f9c45e8be1067b" +[[projects]] + digest = "1:deffbc414da9eff0c34c5d2d97c89f6647432e1dd44c22acd9130be7c4e9401e" + name = "github.com/oklog/ulid" + packages = ["."] + pruneopts = "NUT" + revision = "02a8604050d8466dd915307496174adb9be4593a" + version = "v1.3.1" + [[projects]] digest = "1:e0cc8395ea893c898ff5eb0850f4d9851c1f57c78c232304a026379a47a552d0" name = "github.com/opencontainers/go-digest" @@ -631,6 +653,14 @@ revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" +[[projects]] + digest = "1:014d3763649dd2f071fcf4acb03f3a2964c0c6dcf5581f4585fdd0d492235518" + name = "github.com/peterhellberg/link" + packages = ["."] + pruneopts = "NUT" + revision = "d1cebc7ea14a5fc0de7cb4a45acae773161642c6" + version = "v1.0.0" + [[projects]] digest = "1:5cf3f025cbee5951a4ee961de067c8a89fc95a5adabead774f82822efabab121" name = "github.com/pkg/errors" @@ -680,14 +710,6 @@ pruneopts = "NUT" revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a" -[[projects]] - digest = "1:6bc0652ea6e39e22ccd522458b8bdd8665bf23bdc5a20eec90056e4dc7e273ca" - name = "github.com/satori/go.uuid" - packages = ["."] - pruneopts = "NUT" - revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" - version = "v1.2.0" - [[projects]] digest = "1:b2339e83ce9b5c4f79405f949429a7f68a9a904fed903c672aac1e7ceb7f5f02" name = "github.com/sirupsen/logrus" @@ -780,9 +802,17 @@ [[projects]] branch = "master" - digest = "1:cb77e5934866333fa0784326a57e64c4da128001c94fbd1d29819d79bd3b1087" + digest = "1:4b660675009b1bb13d0faea73f8356cd81b5f560f40a35b366acfc6f63d35674" name = "golang.org/x/crypto" packages = [ + "cast5", + "openpgp", + "openpgp/armor", + "openpgp/clearsign", + "openpgp/elgamal", + "openpgp/errors", + "openpgp/packet", + "openpgp/s2k", "pbkdf2", "ssh/terminal", ] @@ -1092,10 +1122,22 @@ analyzer-version = 1 input-imports = [ "github.com/cbroglie/mustache", + "github.com/deis/duffle/pkg/action", + "github.com/deis/duffle/pkg/bundle", + "github.com/deis/duffle/pkg/claim", + "github.com/deis/duffle/pkg/credentials", + "github.com/deis/duffle/pkg/crypto/digest", + "github.com/deis/duffle/pkg/driver", + "github.com/deis/duffle/pkg/duffle/home", + "github.com/deis/duffle/pkg/image", + "github.com/deis/duffle/pkg/loader", + "github.com/deis/duffle/pkg/repo", + "github.com/deis/duffle/pkg/signature", + "github.com/deis/duffle/pkg/utils/crud", "github.com/docker/cli/cli", "github.com/docker/cli/cli/command", + "github.com/docker/cli/cli/command/context", "github.com/docker/cli/cli/command/stack", - "github.com/docker/cli/cli/command/stack/kubernetes", "github.com/docker/cli/cli/command/stack/options", "github.com/docker/cli/cli/command/stack/swarm", "github.com/docker/cli/cli/compose/loader", @@ -1103,10 +1145,9 @@ "github.com/docker/cli/cli/compose/template", "github.com/docker/cli/cli/compose/types", "github.com/docker/cli/cli/config", + "github.com/docker/cli/cli/context/store", "github.com/docker/cli/cli/debug", "github.com/docker/cli/cli/flags", - "github.com/docker/cli/kubernetes/compose/v1beta1", - "github.com/docker/cli/kubernetes/compose/v1beta2", "github.com/docker/cli/opts", "github.com/docker/distribution", "github.com/docker/distribution/manifest", @@ -1116,12 +1157,12 @@ "github.com/docker/distribution/registry/client/auth", "github.com/docker/distribution/registry/client/transport", "github.com/docker/docker/api/types", - "github.com/docker/docker/api/types/mount", "github.com/docker/docker/distribution", "github.com/docker/docker/pkg/archive", + "github.com/docker/docker/pkg/homedir", + "github.com/docker/docker/pkg/jsonmessage", "github.com/docker/docker/pkg/term", "github.com/docker/docker/registry", - "github.com/docker/go-connections/nat", "github.com/docker/go-units", "github.com/gopherjs/gopherjs/js", "github.com/imdario/mergo", @@ -1141,8 +1182,6 @@ "gotest.tools/golden", "gotest.tools/icmd", "gotest.tools/skip", - "k8s.io/apimachinery/pkg/apis/meta/v1", - "k8s.io/apimachinery/pkg/runtime", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 8fb84532e..49004c481 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -27,10 +27,20 @@ required = ["github.com/wadey/gocovmerge"] name = "github.com/opencontainers/runc" version = "v1.0.0-rc5" -[[constraint]] +[[override]] name = "github.com/docker/cli" - branch = "master" + branch = "docker-app-fork" + source = "git@github.com:simonferquel/cli.git" + +[[override]] + name = "github.com/docker/go-connections" + revision = "7395e3f8aa162843a74ed6d48e79627d9792ac55" +[[constraint]] + name = "github.com/deis/duffle" + branch = "dockercon-version" + source = "git@github.com:simonferquel/duffle.git" + [[constraint]] name = "github.com/sirupsen/logrus" version = "v1.0.5" @@ -106,3 +116,8 @@ required = ["github.com/wadey/gocovmerge"] [[prune.project]] name = "github.com/docker/cli" non-go = false + + [[prune.project]] + name = "github.com/deis/duffle" + unused-packages = false + non-go = false diff --git a/Jenkinsfile b/Jenkinsfile index 4567bd4ec..4af8abf37 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -60,6 +60,23 @@ pipeline { } } } + stage('Build Invocation image'){ + agent { + label 'ubuntu-1604-aufs-edge' + } + steps { + dir('src/github.com/docker/app') { + checkout scm + sh 'make -f docker.Makefile save-invocation-image' + stash name: 'invocation-image', includes: 'invocation-image.tar' + } + } + post { + always { + deleteDir() + } + } + } } } stage('Test') { @@ -116,6 +133,9 @@ pipeline { } steps { dir('src/github.com/docker/app') { + checkout scm + unstash "invocation-image" + sh 'make -f docker.Makefile load-invocation-image' unstash "binaries" dir('examples') { unstash "examples" diff --git a/Jenkinsfile.baguette b/Jenkinsfile.baguette index cb2eac2ea..845469ff1 100644 --- a/Jenkinsfile.baguette +++ b/Jenkinsfile.baguette @@ -8,64 +8,77 @@ pipeline { options { skipDefaultCheckout(true) } + environment{ + DOCKER_BUILDKIT=true + } stages { stage('Build') { - agent { - label 'team-local && linux' - } - steps { - dir('src/github.com/docker/app') { - script { - try { - checkout scm - sh 'make -f docker.Makefile lint' - sh 'make -f docker.Makefile cross e2e-cross tars' - dir('bin') { - stash name: 'binaries' - } - dir('e2e') { - stash name: 'e2e' - } - dir('examples') { - stash name: 'examples' - } - if(!(env.BRANCH_NAME ==~ "PR-\\d+")) { - stash name: 'artifacts', includes: 'bin/*.tar.gz', excludes: 'bin/*-e2e-*' - archiveArtifacts 'bin/*.tar.gz' + parallel { + stage('Binaries'){ + agent { + label 'team-local && linux' + } + steps { + dir('src/github.com/docker/app') { + script { + try { + checkout scm + sh 'make -f docker.Makefile lint' + sh 'make -f docker.Makefile cross e2e-cross tars' + dir('bin') { + stash name: 'binaries' + } + dir('e2e') { + stash name: 'e2e' + } + dir('examples') { + stash name: 'examples' + } + if(!(env.BRANCH_NAME ==~ "PR-\\d+")) { + stash name: 'artifacts', includes: 'bin/*.tar.gz', excludes: 'bin/*-e2e-*' + } + archiveArtifacts 'bin/*.tar.gz' + } finally { + def clean_images = /docker image ls --format "{{.ID}}\t{{.Tag}}" | grep $(git describe --always --dirty) | awk '{print $1}' | xargs docker image rm -f/ + sh clean_images + } } - } finally { - def clean_images = /docker image ls --format "{{.ID}}\t{{.Tag}}" | grep $(git describe --always --dirty) | awk '{print $1}' | xargs docker image rm -f/ - sh clean_images } } - } - } - post { - always { - deleteDir() - } - } - } - stage('Test') { - parallel { - stage("Coverage") { - environment { - CODECOV_TOKEN = credentials('jenkins-codecov-token') + post { + always { + deleteDir() + } } + } + stage('Invocation image'){ agent { label 'team-local && linux' } steps { dir('src/github.com/docker/app') { checkout scm - sh 'make -f docker.Makefile coverage' - archiveArtifacts '_build/ci-cov/all.out' - archiveArtifacts '_build/ci-cov/coverage.html' - sh 'curl -s https://codecov.io/bash | bash -s - -f _build/ci-cov/all.out -K' + sh 'make -f docker.Makefile save-invocation-image' + } + dir('/tmp') { + stash name: 'invocation-image', includes: 'invocation-image-*.tar' + archiveArtifacts 'invocation-image-*.tar' + } + dir('src/github.com/docker/app') { + sh 'make -f docker.Makefile clean-invocation-image' + } + } + post { + always { + deleteDir() } } } + } + } + stage('Test') { + parallel { stage("Gradle test") { agent { label 'team-local && linux' @@ -84,11 +97,17 @@ pipeline { agent { label 'team-local && linux' } - environment { - DOCKERAPP_BINARY = '../docker-app-linux' - } + environment { + DOCKERAPP_BINARY = '../docker-app-linux' + DUFFLE_HOME = "$WORKSPACE/.duffle_home" + } steps { + dir('/tmp') { + unstash "invocation-image" + } dir('src/github.com/docker/app') { + checkout scm + sh 'make -f docker.Makefile load-invocation-image' unstash "binaries" dir('examples') { unstash "examples" @@ -96,6 +115,7 @@ pipeline { dir('e2e'){ unstash "e2e" } + sh './duffle-linux init' sh './docker-app-e2e-linux --e2e-path=e2e' } } @@ -109,11 +129,17 @@ pipeline { agent { label 'team-local && mac' } - environment { - DOCKERAPP_BINARY = '../docker-app-darwin' - } + environment { + DOCKERAPP_BINARY = '../docker-app-darwin' + DUFFLE_HOME = "$WORKSPACE/.duffle_home" + } steps { + dir('/tmp') { + unstash "invocation-image" + } dir('src/github.com/docker/app') { + checkout scm + sh 'make -f docker.Makefile load-invocation-image' unstash "binaries" dir('examples') { unstash "examples" @@ -121,6 +147,7 @@ pipeline { dir('e2e'){ unstash "e2e" } + sh './duffle-darwin init' sh './docker-app-e2e-darwin --e2e-path=e2e' } } @@ -134,11 +161,17 @@ pipeline { agent { label 'team-local && windows && linux-containers' } - environment { - DOCKERAPP_BINARY = '../docker-app-windows.exe' - } + environment { + DOCKERAPP_BINARY = '../docker-app-windows.exe' + DUFFLE_HOME = "$WORKSPACE\\.duffle_home" + } steps { + dir('/tmp') { + unstash "invocation-image" + } dir('src/github.com/docker/app') { + checkout scm + bat 'make -f docker.Makefile load-invocation-image' unstash "binaries" dir('examples') { unstash "examples" @@ -146,6 +179,7 @@ pipeline { dir('e2e'){ unstash "e2e" } + bat 'duffle-windows.exe init' bat 'docker-app-e2e-windows.exe --e2e-path=e2e' } } @@ -158,24 +192,47 @@ pipeline { } } stage('Release') { - agent { - label 'team-local && linux' - } when { buildingTag() } - steps { - dir('src/github.com/docker/app') { - unstash 'artifacts' - echo "Releasing $TAG_NAME" - dir('bin') { - release('docker/app') + parallel { + stage('Push invocation image'){ + agent { + label 'team-local && linux' + } + environment { + DOCKERHUB_CREDS=credentials('dockerhub-dockerdsgcibot') + } + steps{ + sh 'docker login --username "${DOCKERHUB_CREDS_USR}" --password "${DOCKERHUB_CREDS_PSW}"' + dir('/tmp') { + unstash "invocation-image" + } + dir('src/github.com/docker/app') { + checkout scm + sh 'make -f docker.Makefile load-invocation-image' + sh 'make -f docker.Makefile push-invocation-image' + } } } - } - post { - always { - deleteDir() + stage('Release Github binaries') { + agent { + label 'team-local && linux' + } + steps { + dir('src/github.com/docker/app') { + unstash 'artifacts' + echo "Releasing $TAG_NAME" + dir('bin') { + release('docker/app-cnab') + } + } + } + post { + always { + deleteDir() + } + } } } } diff --git a/README.md b/README.md index 1cbfca43d..e85542615 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ An *experimental* utility to help make Compose files more reusable and sharable. + ## The problem application packages solve Compose files do a great job of describing a set of related services. Not only are Compose files easy to write, they are generally easy to read as well. However, a couple of problems often emerge: @@ -35,9 +36,9 @@ hello.dockerapp ``` We created a new file `hello.dockerapp` that contains three YAML documents: -- metadatas +- metadata - the Compose file -- settings for your application +- parameters for your application It should look like this: @@ -63,7 +64,7 @@ services: {} ``` -Let's edit the settings section and add the following default values for our application: +Let's edit the parameters section and add the following default values for our application: ```yaml port: 5678 @@ -107,7 +108,7 @@ You can then use that Compose file like any other. You could save it to disk or $ docker-app render | docker-compose -f - up ``` -This is where it gets interesting. We can override those settings at runtime, using the `--set` option. Let's specify different option and run `render` again: +This is where it gets interesting. We can override those parameters at runtime, using the `--set` option. Let's specify different option and run `render` again: ``` $ docker-app render --set version=0.2.3 --set port=4567 --set text="hello production" @@ -125,7 +126,7 @@ services: protocol: tcp ``` -If you prefer you can create a standalone configuration file to store those settings. Let's create `prod.yml` with the following contents: +If you prefer you can create a standalone configuration file to store those parameters. Let's create `prod.yml` with the following contents: ```yaml version: 0.2.3 @@ -154,31 +155,11 @@ cp docker-app-linux /usr/local/bin/docker-app **Note:** To use Application Packages as images (i.e.: `save`, `push`, or `deploy` when package is not present locally) on Windows, one must be in Linux container mode. -## Integrating with Helm - -`docker-app` comes with a few other helpful commands as well, in particular the ability to create Helm Charts from your Docker Applications. This can be useful if you're adopting Kubernetes, and standardising on Helm to manage the lifecycle of your application components, but want to maintain the simplicity of Compose when writing you applications. This also makes it easy to run the same applications locally just using Docker, if you don't want to be running a full Kubernetes cluster. - -``` -$ docker-app helm -``` - -This will create a folder, `.chart`, in the current directory. The folder contains the required `Chart.yaml` file and templates describing the `stack` Kubernetes object based on the Compose file in your application. - -_Note that this requires the Compose Kubernetes controller available in Docker for Windows and Docker for Mac, and in Docker Enterprise Edition._ - -### Helm chart for Docker EE 2.0 - -In order to create a helm chart that is compatible with version 2.0 of Docker Enterprise Edition, you will need to use the `--stack-version` flag to create a compatible version of the helm chart using `v1beta1` like so: - -```bash -$ docker-app helm --stack-version=v1beta1 -``` - ## Single file or directory representation If you prefer having the three core documents in separate YAML files, omit the `-s` / `--single-file` option to the `docker-app init` command. This will create a directory instead of a single file, containing -`metadata.yml`, `docker-compose.yml` and `settings.yml`. +`metadata.yml`, `docker-compose.yml` and `parameters.yml`. Converting between the two formats can be achieved by using the `docker-app split` and `docker-app merge` commands. @@ -207,33 +188,16 @@ All `docker-app` commands accept an image name as input, which means you can run $ docker-app inspect myhubuser/hello ``` -## Forking an existing image - -Found an app on a remote registry you'd like to modify to better suit your needs? Use the `fork` subcommand: - -```bash -$ docker-app fork remote/hello.dockerapp:1.0.0 mine/hello2 -m "Bob Dylan:bob@aol.com" -``` - -This command will create a local, editable copy of the app on your system. By default, the copy is created inside the current directory; you may use the `--path` flag to configure a different destination. - -For example, the following will create the `/opt/myapps/hello2.dockerapp` folder containing the forked app's files: - -```bash -$ docker-app fork remote/hello.dockerapp:1.0.0 mine/hello2 --path /opt/myapps -``` - ## Next steps We have lots of ideas for making Compose-based applications easier to share and reuse, and making applications a first-class part of the Docker toolchain. Please let us know what you think about this initial release and about any of the ideas below: -* Introducing environments to the settings file +* Introducing environments to the parameters file * Docker images which launch the application when run * Built-in commands for running applications * Saving required images into the application artifact to support offline installation * Signing applications with notary -If you're interested in contributing to the project, jump to [BUILDING.md](BUILDING.md) and [CONTRIBUTING.md](CONTRIBUTING.md). ## Usage @@ -242,9 +206,10 @@ $ docker-app Usage: docker-app [OPTIONS] COMMAND -Docker Application Packages +Build and deploy Docker Application Packages. Options: + -c, --context string context to use to connect to the daemon (overrides host flag, DOCKER_HOST env var and default context set with "docker context use") -D, --debug Enable debug mode -H, --host list Daemon socket(s) to connect to -l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info") @@ -256,18 +221,21 @@ Options: -v, --version Print version information Commands: - completion Generates completion scripts for the specified shell (bash or zsh) - deploy Deploy or update an application - fork Create a fork of an existing application to be modified - helm Generate a Helm chart - init Start building a Docker application - inspect Shows metadata, settings and a summary of the compose file for a given application - merge Merge a multi-file application into a single file - push Push the application to a registry - render Render the Compose file for the application - split Split a single-file application into multiple files - validate Checks the rendered application is syntactically correct - version Print version information + add-credentialset Add a CNAB credentialset in the credential store for the given Docker Context + bundle Create a CNAB invocation image and bundle.json for the application. + completion Generates completion scripts for the specified shell (bash or zsh) + init Start building a Docker application + inspect Shows metadata, parameters and a summary of the compose file for a given application + install Install an application + merge Merge a multi-file application into a single file + pull Pull an application from a registry + push Push the application to a registry + render Render the Compose file for the application + split Split a single-file application into multiple files + status Get an application status + uninstall Uninstall an application + validate Checks the rendered application is syntactically correct + version Print version information Run 'docker-app COMMAND --help' for more information on a command. ``` @@ -296,9 +264,3 @@ Set the docker-app completion code for zsh to autoload on startup in your ~/.zsh ```sh source <(docker-app completion zsh) ``` - -## Experimental - -Some commands are flagged as experimental and will remain in this state until they mature. These commands are only accessible using an experimental binary. Feel free to test these commands and give us some feedback! - -See [BUILDING.md/Experimental](BUILDING.md#experimental). diff --git a/cmd/docker-app/bundle.go b/cmd/docker-app/bundle.go new file mode 100644 index 000000000..2ae64df8e --- /dev/null +++ b/cmd/docker-app/bundle.go @@ -0,0 +1,154 @@ +package main + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + + "github.com/deis/duffle/pkg/bundle" + "github.com/docker/app/internal/packager" + "github.com/docker/app/types" + "github.com/docker/app/types/metadata" + "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" + "github.com/docker/distribution/reference" + dockertypes "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/jsonmessage" + "github.com/docker/docker/registry" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +type bundleOptions struct { + invocationImageName string + namespace string + out string +} + +func bundleCmd(dockerCli command.Cli) *cobra.Command { + var opts bundleOptions + cmd := &cobra.Command{ + Use: "bundle []", + Short: "Create a CNAB invocation image and bundle.json for the application.", + Args: cli.RequiresMaxArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return runBundle(dockerCli, firstOrEmpty(args), opts) + }, + } + + cmd.Flags().StringVarP(&opts.invocationImageName, "invocation-image", "i", "", "specify the name of invocation image to build") + cmd.Flags().StringVar(&opts.namespace, "namespace", "", "namespace to use (default: namespace in metadata)") + cmd.Flags().StringVarP(&opts.out, "out", "o", "bundle.json", "path to the output bundle.json (- for stdout)") + return cmd +} + +func runBundle(dockerCli command.Cli, appName string, opts bundleOptions) error { + bundle, err := makeBundle(dockerCli, appName, opts.namespace, opts.invocationImageName) + if err != nil { + return err + } + if bundle == nil || len(bundle.InvocationImages) == 0 { + return fmt.Errorf("failed to create bundle %q", appName) + } + fmt.Fprintf(dockerCli.Out(), "Invocation image %q successfully built\n", bundle.InvocationImages[0].Image) + bundleBytes, err := json.MarshalIndent(bundle, "", "\t") + if err != nil { + return err + } + if opts.out == "-" { + _, err = dockerCli.Out().Write(bundleBytes) + return err + } + return ioutil.WriteFile(opts.out, bundleBytes, 0644) +} + +func makeBundle(dockerCli command.Cli, appName, namespace, invocationImageName string) (*bundle.Bundle, error) { + app, err := packager.Extract(appName) + if err != nil { + return nil, err + } + defer app.Cleanup() + return makeBundleFromApp(dockerCli, app, namespace, invocationImageName) +} + +func resolveAuthConfigsForBuild(dockerCli command.Cli) (map[string]dockertypes.AuthConfig, error) { + ref, err := reference.ParseNormalizedNamed("docker/cnab-app-base") + if err != nil { + return nil, err + } + repoInfo, err := registry.ParseRepositoryInfo(ref) + if err != nil { + return nil, err + } + return map[string]dockertypes.AuthConfig{ + registry.GetAuthConfigKey(repoInfo.Index): command.ResolveAuthConfig(context.TODO(), dockerCli, repoInfo.Index), + }, nil +} + +func makeBundleFromApp(dockerCli command.Cli, app *types.App, namespace, invocationImageName string) (*bundle.Bundle, error) { + meta := app.Metadata() + invocationImageName, err := makeInvocationImageName(meta, namespace, invocationImageName) + if err != nil { + return nil, err + } + if err := checkAppImage(app.Metadata(), namespace); err != nil { + return nil, err + } + + buildContext := bytes.NewBuffer(nil) + if err := packager.PackInvocationImageContext(app, buildContext); err != nil { + return nil, err + } + + authConfigs, err := resolveAuthConfigsForBuild(dockerCli) + if err != nil { + return nil, err + } + + buildResp, err := dockerCli.Client().ImageBuild(context.TODO(), buildContext, dockertypes.ImageBuildOptions{ + Dockerfile: "Dockerfile", + Tags: []string{invocationImageName}, + AuthConfigs: authConfigs, + }) + if err != nil { + return nil, err + } + defer buildResp.Body.Close() + + if err := jsonmessage.DisplayJSONMessagesStream(buildResp.Body, ioutil.Discard, 0, false, func(jsonmessage.JSONMessage) {}); err != nil { + return nil, err + } + return packager.ToCNAB(app, invocationImageName), nil +} + +func makeInvocationImageName(meta metadata.AppMetadata, namespace, name string) (string, error) { + if name == "" { + name = fmt.Sprintf("%s:%s-invoc", meta.Name, meta.Version) + } + ns := namespace + if ns == "" { + ns = meta.Namespace + } + if ns != "" { + name = fmt.Sprintf("%s/%s", ns, name) + } + if _, err := reference.ParseNormalizedNamed(name); err != nil { + return "", errors.Wrapf(err, "invocation image name %q is invalid", name) + } + return name, nil +} + +func checkAppImage(meta metadata.AppMetadata, namespace string) error { + name := fmt.Sprintf("%s:%s", meta.Name, meta.Version) + ns := namespace + if ns == "" { + ns = meta.Namespace + } + if ns != "" { + name = fmt.Sprintf("%s/%s", ns, name) + } + _, err := reference.ParseNormalizedNamed(name) + return errors.Wrapf(err, "generated app image name %q is invalid, please check namespace, name and version fields", name) +} diff --git a/cmd/docker-app/bundle_test.go b/cmd/docker-app/bundle_test.go new file mode 100644 index 000000000..75667b409 --- /dev/null +++ b/cmd/docker-app/bundle_test.go @@ -0,0 +1,105 @@ +package main + +import ( + "testing" + + "github.com/docker/app/types/metadata" + "gotest.tools/assert" +) + +func TestMakeInvocationImage(t *testing.T) { + testcases := []struct { + name string + imageName string + namespace string + meta metadata.AppMetadata + expected string + err string + }{ + { + name: "specify-image-name", + imageName: "my-invocation-image", + expected: "my-invocation-image", + }, + { + name: "specify-image-name-and-namespace", + imageName: "my-invocation-image", + namespace: "my-namespace", + expected: "my-namespace/my-invocation-image", + }, + { + name: "simple-metadata", + meta: metadata.AppMetadata{Name: "name", Version: "version"}, + expected: "name:version-invoc", + }, + { + name: "simple-metadata-with-overridden-namespace", + namespace: "my-namespace", + meta: metadata.AppMetadata{Name: "name", Version: "version"}, + expected: "my-namespace/name:version-invoc", + }, + { + name: "metadata-with-namespace", + meta: metadata.AppMetadata{Name: "name", Version: "version", Namespace: "namespace"}, + expected: "namespace/name:version-invoc", + }, + { + name: "metadata-with-namespace-and-overridden-namespace", + namespace: "my-namespace", + meta: metadata.AppMetadata{Name: "name", Version: "version", Namespace: "namespace"}, + expected: "my-namespace/name:version-invoc", + }, + { + name: "simple-metadata", + meta: metadata.AppMetadata{Name: "WrongName&%*", Version: "version"}, + err: "invalid", + }, + } + for _, c := range testcases { + t.Run(c.name, func(t *testing.T) { + actual, err := makeInvocationImageName(c.meta, c.namespace, c.imageName) + if c.err != "" { + assert.ErrorContains(t, err, c.err) + assert.Equal(t, actual, "") + } else { + assert.NilError(t, err) + assert.Equal(t, actual, c.expected) + } + }) + } +} + +func TestCheckAppImage(t *testing.T) { + testcases := []struct { + name string + meta metadata.AppMetadata + namespace string + err string + }{ + { + name: "metadata-with-namespace", + meta: metadata.AppMetadata{Name: "name", Version: "version", Namespace: "namespace"}, + }, + { + name: "metadata-with-wrong-namespace", + meta: metadata.AppMetadata{Name: "name", Version: "version", Namespace: "namespace"}, + namespace: "WrongNamespace&%*", + err: "invalid", + }, + { + name: "simple-metadata", + meta: metadata.AppMetadata{Name: "WrongName&%*", Version: "version"}, + err: "invalid", + }, + } + for _, c := range testcases { + t.Run(c.name, func(t *testing.T) { + err := checkAppImage(c.meta, c.namespace) + if c.err != "" { + assert.ErrorContains(t, err, c.err) + } else { + assert.NilError(t, err) + } + }) + } +} diff --git a/cmd/docker-app/credentialset.go b/cmd/docker-app/credentialset.go new file mode 100644 index 000000000..343f29886 --- /dev/null +++ b/cmd/docker-app/credentialset.go @@ -0,0 +1,78 @@ +package main + +import ( + "fmt" + "io" + "os" + "path/filepath" + + "github.com/deis/duffle/pkg/credentials" + "github.com/deis/duffle/pkg/duffle/home" + "github.com/docker/app/internal/yaml" + "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" + "github.com/spf13/cobra" +) + +type credentialSetOptions struct { + name string + contextName string + force bool + output string +} + +func credentialSetCmd(dockerCli command.Cli) *cobra.Command { + var opts credentialSetOptions + cmd := &cobra.Command{ + Use: "add-credentialset ", + Short: "Add a CNAB credentialset in the credential store for the given Docker Context", + Args: cli.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + opts.name = args[0] + opts.contextName = args[1] + return runCredentials(dockerCli, opts) + }, + } + cmd.Flags().BoolVar(&opts.force, "force", false, "Overwrites existing credentialset") + cmd.Flags().StringVar(&opts.output, "out", "", "Specify an alternate output for the credentialset (- for stdout)") + return cmd +} + +func runCredentials(dockerCli command.Cli, opts credentialSetOptions) error { + output := opts.output + var writer io.Writer + if output == "-" { + writer = dockerCli.Out() + } else { + if output == "" { + h := home.Home(home.DefaultHome()) + output = filepath.Join(h.Credentials(), opts.name) + ".yaml" + } + if _, err := os.Stat(output); err == nil && !opts.force { + return fmt.Errorf("credentialset %q already exists, use --force to overwrite", opts.name) + } + f, err := os.OpenFile(output, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600) + if err != nil { + return err + } + defer f.Close() + writer = f + } + creds := credentials.CredentialSet{ + Name: opts.name, + Credentials: []credentials.CredentialStrategy{ + { + Name: "docker.context", + Source: credentials.Source{ + Command: fmt.Sprintf(`docker-app context export %s -`, opts.contextName), + }, + }, + }, + } + payload, err := yaml.Marshal(creds) + if err != nil { + return err + } + _, err = writer.Write(payload) + return err +} diff --git a/cmd/docker-app/deploy.go b/cmd/docker-app/deploy.go deleted file mode 100644 index 79b1d0d06..000000000 --- a/cmd/docker-app/deploy.go +++ /dev/null @@ -1,90 +0,0 @@ -package main - -import ( - "os" - - "github.com/docker/app/internal" - "github.com/docker/app/internal/packager" - "github.com/docker/app/render" - "github.com/docker/app/types" - "github.com/docker/cli/cli" - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/stack" - "github.com/docker/cli/cli/command/stack/options" - "github.com/docker/cli/cli/command/stack/swarm" - cliopts "github.com/docker/cli/opts" - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -type deployOptions struct { - deployComposeFiles []string - deploySettingsFiles []string - deployEnv []string - deployOrchestrator string - deployKubeConfig string - deployNamespace string - deployStackName string - deploySendRegistryAuth bool -} - -// deployCmd represents the deploy command -func deployCmd(dockerCli command.Cli) *cobra.Command { - var opts deployOptions - - cmd := &cobra.Command{ - Use: "deploy []", - Short: "Deploy or update an application", - Long: `Deploy the application on either Swarm or Kubernetes.`, - Args: cli.RequiresMaxArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - return runDeploy(dockerCli, cmd.Flags(), firstOrEmpty(args), opts) - }, - } - - cmd.Flags().StringArrayVarP(&opts.deploySettingsFiles, "settings-files", "f", []string{}, "Override settings files") - cmd.Flags().StringArrayVarP(&opts.deployEnv, "set", "s", []string{}, "Override settings values") - cmd.Flags().StringVarP(&opts.deployOrchestrator, "orchestrator", "o", "swarm", "Orchestrator to deploy on (swarm, kubernetes)") - cmd.Flags().StringVarP(&opts.deployKubeConfig, "kubeconfig", "k", "", "Kubernetes config file to use") - cmd.Flags().StringVarP(&opts.deployNamespace, "namespace", "n", "default", "Kubernetes namespace to deploy into") - cmd.Flags().StringVarP(&opts.deployStackName, "name", "d", "", "Stack name (default: app name)") - cmd.Flags().BoolVarP(&opts.deploySendRegistryAuth, "with-registry-auth", "", false, "Sends registry auth") - if internal.Experimental == "on" { - cmd.Flags().StringArrayVarP(&opts.deployComposeFiles, "compose-files", "c", []string{}, "Override Compose files") - } - return cmd -} - -func runDeploy(dockerCli command.Cli, flags *pflag.FlagSet, appname string, opts deployOptions) error { - app, err := packager.Extract(appname, - types.WithSettingsFiles(opts.deploySettingsFiles...), - types.WithComposeFiles(opts.deployComposeFiles...), - ) - if err != nil { - return err - } - defer app.Cleanup() - deployOrchestrator, err := command.GetStackOrchestrator(opts.deployOrchestrator, dockerCli.ConfigFile().StackOrchestrator, dockerCli.Err()) - if err != nil { - return err - } - d := cliopts.ConvertKVStringsToMap(opts.deployEnv) - rendered, err := render.Render(app, d) - if err != nil { - return err - } - stackName := opts.deployStackName - if stackName == "" { - stackName = internal.AppNameFromDir(app.Name) - } - if app.Source.ShouldRunInsideDirectory() { - if err := os.Chdir(app.Path); err != nil { - return err - } - } - return stack.RunDeploy(dockerCli, flags, rendered, deployOrchestrator, options.Deploy{ - Namespace: stackName, - ResolveImage: swarm.ResolveImageAlways, - SendRegistryAuth: opts.deploySendRegistryAuth, - }) -} diff --git a/cmd/docker-app/fork.go b/cmd/docker-app/fork.go deleted file mode 100644 index 712a57f3f..000000000 --- a/cmd/docker-app/fork.go +++ /dev/null @@ -1,31 +0,0 @@ -package main - -import ( - "github.com/docker/app/internal/packager" - "github.com/docker/cli/cli" - "github.com/spf13/cobra" -) - -var ( - forkMaintainers []string - outputDir string -) - -func forkCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "fork [fork-name] [-p outputdir] [-m name:email ...]", - Short: "Create a fork of an existing application to be modified", - Args: cli.RequiresRangeArgs(1, 2), - RunE: func(cmd *cobra.Command, args []string) error { - forkName := "" - if len(args) >= 2 { - forkName = args[1] - } - return packager.Fork(args[0], forkName, outputDir, forkMaintainers) - }, - } - cmd.Flags().StringArrayVarP(&forkMaintainers, "maintainer", "m", []string{}, "Maintainer (name:email) (optional)") - cmd.Flags().StringVarP(&outputDir, "path", "p", ".", "Directory where the application will be extracted") - - return cmd -} diff --git a/cmd/docker-app/helm.go b/cmd/docker-app/helm.go deleted file mode 100644 index b7d8d070c..000000000 --- a/cmd/docker-app/helm.go +++ /dev/null @@ -1,56 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/docker/app/internal" - "github.com/docker/app/internal/helm" - "github.com/docker/app/internal/packager" - "github.com/docker/app/types" - "github.com/docker/cli/cli" - cliopts "github.com/docker/cli/opts" - "github.com/spf13/cobra" -) - -var ( - helmComposeFiles []string - helmSettingsFile []string - helmEnv []string - helmRender bool - stackVersion string -) - -func helmCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "helm [] [-s key=value...] [-f settings-file...]", - Short: "Generate a Helm chart", - Long: `Generate a Helm chart for the application.`, - Args: cli.RequiresMaxArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - app, err := packager.Extract(firstOrEmpty(args), - types.WithSettingsFiles(helmSettingsFile...), - types.WithComposeFiles(helmComposeFiles...), - ) - if err != nil { - return err - } - defer app.Cleanup() - d := cliopts.ConvertKVStringsToMap(helmEnv) - if stackVersion != helm.V1Beta1 && stackVersion != helm.V1Beta2 { - return fmt.Errorf("invalid stack version %q (accepted values: %s, %s)", stackVersion, helm.V1Beta1, helm.V1Beta2) - } - return helm.Helm(app, d, helmRender, stackVersion) - }, - } - if internal.Experimental == "on" { - cmd.Flags().StringArrayVarP(&helmComposeFiles, "compose-files", "c", []string{}, "Override Compose files") - cmd.Use += " [-c ...]" - cmd.Flags().BoolVarP(&helmRender, "render", "r", false, "Render the template instead of exporting it") - cmd.Long += ` If the --render option is used, the docker-compose.yml will -be rendered instead of exported as a template.` - } - cmd.Flags().StringArrayVarP(&helmSettingsFile, "settings-files", "f", []string{}, "Override settings files") - cmd.Flags().StringArrayVarP(&helmEnv, "set", "s", []string{}, "Override settings values") - cmd.Flags().StringVarP(&stackVersion, "stack-version", "", helm.V1Beta2, "Version of the stack specification for the produced helm chart (v1beta1 / v1beta2)") - return cmd -} diff --git a/cmd/docker-app/image-add.go b/cmd/docker-app/image-add.go index 8bb2207ad..b9ed874ca 100644 --- a/cmd/docker-app/image-add.go +++ b/cmd/docker-app/image-add.go @@ -13,9 +13,9 @@ import ( ) var ( - imageAddComposeFiles []string - imageAddSettingsFile []string - imageAddEnv []string + imageAddComposeFiles []string + imageAddParametersFile []string + imageAddEnv []string ) func imageAddCmd() *cobra.Command { @@ -29,7 +29,7 @@ subdirectory.`, RunE: func(cmd *cobra.Command, args []string) error { oappname := args[0] app, err := packager.Extract(oappname, - types.WithSettingsFiles(imageAddSettingsFile...), + types.WithParametersFiles(imageAddParametersFile...), types.WithComposeFiles(imageAddComposeFiles...), ) if err != nil { @@ -66,8 +66,8 @@ subdirectory.`, }, } if internal.Experimental == "on" { - cmd.Flags().StringArrayVarP(&imageAddComposeFiles, "compose-files", "c", []string{}, "Override Compose files") - cmd.Flags().StringArrayVarP(&imageAddSettingsFile, "settings-files", "s", []string{}, "Override settings files") + cmd.Flags().StringArrayVarP(&imageAddComposeFiles, "compose-files", "c", []string{}, "Override Compose file") + cmd.Flags().StringArrayVarP(&imageAddParametersFile, "parameters-files", "f", []string{}, "Override with parameters from files") cmd.Flags().StringArrayVarP(&imageAddEnv, "env", "e", []string{}, "Override environment values") } return cmd diff --git a/cmd/docker-app/inspect.go b/cmd/docker-app/inspect.go index 987bbee33..4110b0018 100644 --- a/cmd/docker-app/inspect.go +++ b/cmd/docker-app/inspect.go @@ -1,9 +1,12 @@ package main import ( - "github.com/docker/app/internal/inspect" - "github.com/docker/app/internal/packager" - "github.com/docker/app/types" + "errors" + "io/ioutil" + + "github.com/deis/duffle/pkg/action" + "github.com/deis/duffle/pkg/claim" + "github.com/docker/app/types/parameters" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" cliopts "github.com/docker/cli/opts" @@ -11,29 +14,67 @@ import ( ) var ( - inspectSettingsFile []string - inspectEnv []string + inspectParametersFile []string + inspectEnv []string + inspectInsecure bool ) // inspectCmd represents the inspect command func inspectCmd(dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ - Use: "inspect [] [-s key=value...] [-f settings-file...]", - Short: "Shows metadata, settings and a summary of the compose file for a given application", + Use: "inspect [] [-s key=value...] [-f parameters-file...]", + Short: "Shows metadata, parameters and a summary of the compose file for a given application", Args: cli.RequiresMaxArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - app, err := packager.Extract(firstOrEmpty(args), - types.WithSettingsFiles(inspectSettingsFile...), - ) + muteDockerCli(dockerCli) + appname := firstOrEmpty(args) + bndl, err := resolveBundle(dockerCli, "", appname, inspectInsecure) + if err != nil { + return err + } + if bndl.Actions == nil { + return errors.New(`specified bundle has no "inspect" action`) + } + if _, ok := bndl.Actions["inspect"]; !ok { + return errors.New(`specified bundle has no "inspect" action`) + } + s, err := parameters.LoadFiles(inspectParametersFile) + if err != nil { + return err + } + d := cliopts.ConvertKVStringsToMap(inspectEnv) + overrides, err := parameters.FromFlatten(d) + if err != nil { + return err + } + if s, err = parameters.Merge(s, overrides); err != nil { + return err + } + settingValues := s.Flatten() + c, err := claim.New("inspect") + if err != nil { + return err + } + driverImpl, err := prepareDriver(dockerCli) if err != nil { return err } - defer app.Cleanup() - argSettings := cliopts.ConvertKVStringsToMap(inspectEnv) - return inspect.Inspect(dockerCli.Out(), app, argSettings) + c.Bundle = bndl + c.Parameters = stringsKVToStringInterface(settingValues) + a := &action.RunCustom{ + Action: "inspect", + Driver: driverImpl, + } + return a.Run(c, map[string]string{"docker.context": ""}, dockerCli.Out()) }, } - cmd.Flags().StringArrayVarP(&inspectSettingsFile, "settings-files", "f", []string{}, "Override settings files") - cmd.Flags().StringArrayVarP(&inspectEnv, "set", "s", []string{}, "Override settings values") + cmd.Flags().StringArrayVarP(&inspectParametersFile, "parameters-files", "f", []string{}, "Override with parameters from files") + cmd.Flags().StringArrayVarP(&inspectEnv, "set", "s", []string{}, "Override parameters values") + cmd.Flags().BoolVar(&inspectInsecure, "insecure", false, "Use insecure registry, without SSL") return cmd } + +func muteDockerCli(dockerCli command.Cli) { + dockerCli.SetOut(command.NewOutStream(ioutil.Discard)) + dockerCli.SetErr(ioutil.Discard) +} diff --git a/cmd/docker-app/install.go b/cmd/docker-app/install.go new file mode 100644 index 000000000..d05a23a3b --- /dev/null +++ b/cmd/docker-app/install.go @@ -0,0 +1,342 @@ +package main + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/deis/duffle/pkg/action" + "github.com/deis/duffle/pkg/bundle" + "github.com/deis/duffle/pkg/claim" + "github.com/deis/duffle/pkg/credentials" + "github.com/deis/duffle/pkg/driver" + "github.com/deis/duffle/pkg/duffle/home" + "github.com/deis/duffle/pkg/loader" + "github.com/deis/duffle/pkg/utils/crud" + "github.com/docker/app/internal" + "github.com/docker/app/internal/packager" + "github.com/docker/app/types/parameters" + "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/context/store" + cliopts "github.com/docker/cli/opts" + "github.com/docker/docker/pkg/homedir" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +type installOptions struct { + parametersOptions + orchestrator string + namespace string + kubeNamespace string + stackName string + insecure bool + sendRegistryAuth bool + targetContext string + credentialsets []string +} + +type parametersOptions struct { + parametersFiles []string + env []string +} + +func (o *parametersOptions) addFlags(flags *pflag.FlagSet) { + flags.StringArrayVarP(&o.parametersFiles, "parameters-files", "f", []string{}, "Override parameters files") + flags.StringArrayVarP(&o.env, "set", "s", []string{}, "Override parameters values") +} + +type nameKind uint + +const ( + _ nameKind = iota + nameKindEmpty + nameKindFile + nameKindDir + nameKindReference +) + +const longDescription = `Install the application on either Swarm or Kubernetes. +Bundle name is optional, and can be +- empty: resolve to any *.dockerapp file or directory in working dir +- existing file path: work with any *.dockerapp file or dir, or any CNAB bundle file (signed or unsigned) +- match a bundle name in the local duffle bundle repository +- refers to a CNAB bundle in a container registry +` + +// installCmd represents the install command +func installCmd(dockerCli command.Cli) *cobra.Command { + var opts installOptions + + cmd := &cobra.Command{ + Use: "install [] [options]", + Aliases: []string{"deploy"}, + Short: "Install an application", + Long: longDescription, + Args: cli.RequiresMaxArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return runInstall(dockerCli, firstOrEmpty(args), opts) + }, + } + opts.parametersOptions.addFlags(cmd.Flags()) + cmd.Flags().StringVarP(&opts.orchestrator, "orchestrator", "o", "", "Orchestrator to install on (swarm, kubernetes)") + cmd.Flags().StringVar(&opts.namespace, "namespace", "", "Namespace to use (default: namespace in metadata)") + cmd.Flags().StringVar(&opts.kubeNamespace, "kube-namespace", "default", "Kubernetes namespace to install into") + cmd.Flags().StringVar(&opts.stackName, "name", "", "Installation name (defaults to application name)") + cmd.Flags().StringVar(&opts.targetContext, "target-context", "", "Context on which to install the application") + cmd.Flags().BoolVar(&opts.insecure, "insecure", false, "Use insecure registry, without SSL") + cmd.Flags().BoolVar(&opts.sendRegistryAuth, "with-registry-auth", false, "Sends registry auth") + cmd.Flags().StringArrayVarP(&opts.credentialsets, "credential-set", "c", []string{}, "Use a duffle credentialset (either a YAML file, or a credential set present in the duffle credential store)") + + return cmd +} + +func runInstall(dockerCli command.Cli, appname string, opts installOptions) error { + muteDockerCli(dockerCli) + targetContext := getTargetContext(opts.targetContext) + parameterValues, err := prepareParameters(opts.parametersOptions) + if err != nil { + return err + } + + bndl, err := resolveBundle(dockerCli, opts.namespace, appname, opts.insecure) + if err != nil { + return err + } + if opts.sendRegistryAuth { + return errors.New("with-registry-auth is not supported at the moment") + } + if err := bndl.Validate(); err != nil { + return err + } + h := duffleHome() + claimName := opts.stackName + if claimName == "" { + claimName = bndl.Name + } + claimStore := claim.NewClaimStore(crud.NewFileSystemStore(h.Claims(), "json")) + if _, err = claimStore.Read(claimName); err == nil { + return fmt.Errorf("installation %q already exists", claimName) + } + c, err := claim.New(claimName) + if err != nil { + return err + } + if _, ok := bndl.Parameters["docker.orchestrator"]; ok { + parameterValues["docker.orchestrator"] = opts.orchestrator + } + if _, ok := bndl.Parameters["docker.kubernetes-namespace"]; ok { + parameterValues["docker.kubernetes-namespace"] = opts.kubeNamespace + } + + driverImpl, err := prepareDriver(dockerCli) + if err != nil { + return err + } + creds, err := prepareCredentialSet(targetContext, dockerCli.ContextStore(), bndl, opts.credentialsets) + if err != nil { + return err + } + if err := credentials.Validate(creds, bndl.Credentials); err != nil { + return err + } + + c.Bundle = bndl + convertedParamValues := map[string]interface{}{} + + if err := applyParameterValues(parameterValues, bndl.Parameters, convertedParamValues); err != nil { + return err + } + + c.Parameters, err = bundle.ValuesOrDefaults(convertedParamValues, bndl) + if err != nil { + return err + } + inst := &action.Install{ + Driver: driverImpl, + } + err = inst.Run(c, creds, dockerCli.Out()) + err2 := claimStore.Store(*c) + if err != nil { + return fmt.Errorf("install failed: %v", err) + } + return err2 +} + +func applyParameterValues(parameterValues map[string]string, parameterDefinitions map[string]bundle.ParameterDefinition, finalValues map[string]interface{}) error { + for k, v := range parameterValues { + pd, ok := parameterDefinitions[k] + if !ok { + return fmt.Errorf("parameter %q is not defined in the bundle", k) + } + value, err := pd.ConvertValue(v) + if err != nil { + return errors.Wrapf(err, "invalid value for parameter %q", k) + } + if err := pd.ValidateParameterValue(value); err != nil { + return errors.Wrapf(err, "invalid value for parameter %q", k) + } + finalValues[k] = value + } + return nil +} + +func prepareParameters(opts parametersOptions) (map[string]string, error) { + p, err := parameters.LoadFiles(opts.parametersFiles) + if err != nil { + return nil, err + } + d := cliopts.ConvertKVStringsToMap(opts.env) + overrides, err := parameters.FromFlatten(d) + if err != nil { + return nil, err + } + if p, err = parameters.Merge(p, overrides); err != nil { + return nil, err + } + return p.Flatten(), nil +} + +func getAppNameKind(name string) (string, nameKind) { + if name == "" { + return name, nameKindEmpty + } + // name can be a bundle.json file, a single dockerapp file, or a dockerapp directory + st, err := os.Stat(name) + if os.IsNotExist(err) { + // try with .dockerapp extension + st, err = os.Stat(name + internal.AppExtension) + if err == nil { + name += internal.AppExtension + } + } + if err != nil { + return name, nameKindReference + } + if st.IsDir() { + return name, nameKindDir + } + return name, nameKindFile +} + +func extractAndLoadAppBasedBundle(dockerCli command.Cli, namespace, name string) (*bundle.Bundle, error) { + app, err := packager.Extract(name) + if err != nil { + return nil, err + } + defer app.Cleanup() + return makeBundleFromApp(dockerCli, app, namespace, "") +} + +func resolveBundle(dockerCli command.Cli, namespace, name string, insecure bool) (*bundle.Bundle, error) { + // resolution logic: + // - if there is a docker-app package in working directory, or an http:// / https:// prefix, use packager.Extract result + // - the name has a .json or .cnab extension and refers to an existing file or web resource: load the bundle + // - name matches a bundle name:version stored in duffle bundle store: use it + // - pull the bundle from the registry and add it to the bundle store + name, kind := getAppNameKind(name) + switch kind { + case nameKindFile: + if strings.HasSuffix(name, internal.AppExtension) { + return extractAndLoadAppBasedBundle(dockerCli, namespace, name) + } + return loader.NewDetectingLoader().Load(name) + case nameKindDir, nameKindEmpty: + return extractAndLoadAppBasedBundle(dockerCli, namespace, name) + case nameKindReference: + // revert to local duffle bundle store or pull the bundle image + return pullBundle(dockerCli, name, false, insecure) + } + return nil, fmt.Errorf("could not resolve bundle %q", name) +} + +func getTargetContext(optstargetContext string) string { + var targetContext string + switch { + case optstargetContext != "": + targetContext = optstargetContext + case os.Getenv("DOCKER_TARGET_CONTEXT") != "": + targetContext = os.Getenv("DOCKER_TARGET_CONTEXT") + } + if targetContext == command.ContextDockerHost { + targetContext = "" + } + return targetContext +} + +func stringsKVToStringInterface(src map[string]string) map[string]interface{} { + result := map[string]interface{}{} + for k, v := range src { + result[k] = v + } + return result +} + +func prepareCredentialSet(contextName string, contextStore store.Store, b *bundle.Bundle, namedCredentialsets []string) (map[string]string, error) { + creds := map[string]string{} + for _, file := range namedCredentialsets { + if _, err := os.Stat(file); err != nil { + file = filepath.Join(duffleHome().Credentials(), file+".yaml") + } + c, err := credentials.Load(file) + if err != nil { + return nil, err + } + values, err := c.Resolve() + if err != nil { + return nil, err + } + for k, v := range values { + if _, ok := creds[k]; ok { + return nil, fmt.Errorf("ambiguous credential resolution: %q is present in multiple credential sets", k) + } + creds[k] = v + } + } + if contextName != "" { + data, err := ioutil.ReadAll(store.Export(contextName, contextStore)) + if err != nil { + return nil, err + } + creds["docker.context"] = string(data) + } + _, requiresDockerContext := b.Credentials["docker.context"] + _, hasDockerContext := creds["docker.context"] + if requiresDockerContext && !hasDockerContext { + return nil, errors.New("no target context specified. use use --target-context= or DOCKER_TARGET_CONTEXT= to define it") + } + return creds, nil +} + +func duffleHome() home.Home { + if h := os.Getenv(home.HomeEnvVar); h != "" { + return home.Home(h) + } + return home.Home(filepath.Join(homedir.Get(), ".duffle")) +} + +// prepareDriver prepares a driver per the user's request. +func prepareDriver(dockerCli command.Cli) (driver.Driver, error) { + driverImpl, err := driver.Lookup("docker") + if err != nil { + return driverImpl, err + } + if d, ok := driverImpl.(*driver.DockerDriver); ok { + d.SetDockerCli(dockerCli) + } + + // Load any driver-specific config out of the environment. + if configurable, ok := driverImpl.(driver.Configurable); ok { + driverCfg := map[string]string{} + for env := range configurable.Config() { + driverCfg[env] = os.Getenv(env) + } + configurable.SetConfig(driverCfg) + } + + return driverImpl, err +} diff --git a/cmd/docker-app/merge.go b/cmd/docker-app/merge.go index 7a09d70e7..304947466 100644 --- a/cmd/docker-app/merge.go +++ b/cmd/docker-app/merge.go @@ -42,7 +42,7 @@ func extraFiles(appname string) ([]string, error) { //handleInPlace returns the operation target path and if it's in-place func handleInPlace(app *types.App) (string, bool) { - if app.Source == types.AppSourceURL || app.Source == types.AppSourceImage { + if app.Source == types.AppSourceImage { return internal.DirNameFromAppName(app.Name), false } return app.Path + ".tmp", true diff --git a/cmd/docker-app/pull.go b/cmd/docker-app/pull.go index 1e1275577..5984c53a4 100644 --- a/cmd/docker-app/pull.go +++ b/cmd/docker-app/pull.go @@ -1,19 +1,84 @@ package main import ( - "github.com/docker/app/internal/packager" + "context" + "fmt" + "io/ioutil" + "path/filepath" + + "github.com/deis/duffle/pkg/bundle" + "github.com/deis/duffle/pkg/crypto/digest" + "github.com/deis/duffle/pkg/image" + "github.com/deis/duffle/pkg/loader" + "github.com/deis/duffle/pkg/repo" "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" + "github.com/docker/distribution/reference" "github.com/spf13/cobra" ) -func pullCmd() *cobra.Command { - return &cobra.Command{ +type pullOptions struct { + insecure bool +} + +func pullCmd(dockerCli command.Cli) *cobra.Command { + var opts pullOptions + cmd := &cobra.Command{ Use: "pull ", Short: "Pull an application from a registry", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - _, err := packager.Pull(args[0], ".") + _, err := pullBundle(dockerCli, args[0], true, opts.insecure) return err }, } + cmd.Flags().BoolVar(&opts.insecure, "insecure", false, "Use insecure registry, without SSL") + return cmd +} + +func pullBundle(dockerCli command.Cli, name string, force, insecure bool) (*bundle.Bundle, error) { + named, err := reference.ParseNormalizedNamed(name) + if err != nil { + return nil, err + } + tagged, ok := named.(reference.NamedTagged) + if !ok { + return nil, fmt.Errorf("%q is not a tagged image name", name) + } + h := duffleHome() + index, err := repo.LoadIndex(h.Repositories()) + if err != nil { + return nil, err + } + if !force { + sha, err := index.GetExactly(tagged) + if err == nil { + fpath := filepath.Join(h.Bundles(), sha) + return loader.NewDetectingLoader().Load(fpath) + } + } + + signedBundle, err := image.PullBundle(context.TODO(), dockerCli, insecure, name) + if err != nil { + return nil, err + } + bndl, err := loader.NewDetectingLoader().LoadData(signedBundle) + if err != nil { + return nil, err + } + sha, err := digest.OfBuffer(signedBundle) + if err != nil { + return nil, fmt.Errorf("cannot compute digest from bundle: %v", err) + } + + fpath := filepath.Join(h.Bundles(), sha) + if err := ioutil.WriteFile(fpath, signedBundle, 0644); err != nil { + return nil, err + } + index.Add(tagged, sha) + + if err := index.WriteFile(h.Repositories(), 0644); err != nil { + return nil, fmt.Errorf("could not write to %s: %v", h.Repositories(), err) + } + return bndl, nil } diff --git a/cmd/docker-app/push.go b/cmd/docker-app/push.go index 5a1af6b69..0c2ef3ded 100644 --- a/cmd/docker-app/push.go +++ b/cmd/docker-app/push.go @@ -1,10 +1,18 @@ package main import ( + "context" + "errors" "fmt" + "os" + "github.com/deis/duffle/pkg/bundle" + "github.com/deis/duffle/pkg/image" + "github.com/deis/duffle/pkg/signature" "github.com/docker/app/internal/packager" + "github.com/docker/app/types" "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" "github.com/spf13/cobra" ) @@ -12,29 +20,110 @@ type pushOptions struct { namespace string tag string repo string + insecure bool } -func pushCmd() *cobra.Command { +func pushCmd(dockerCli command.Cli) *cobra.Command { var opts pushOptions cmd := &cobra.Command{ Use: "push []", Short: "Push the application to a registry", Args: cli.RequiresMaxArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - app, err := packager.Extract(firstOrEmpty(args)) - if err != nil { - return err - } - defer app.Cleanup() - dgst, err := packager.Push(app, opts.namespace, opts.tag, opts.repo) - if err == nil { - fmt.Println(dgst) - } - return err + return runPush(dockerCli, firstOrEmpty(args), opts) }, } cmd.Flags().StringVar(&opts.namespace, "namespace", "", "Namespace to use (default: namespace in metadata)") cmd.Flags().StringVarP(&opts.tag, "tag", "t", "", "Tag to use (default: version in metadata)") cmd.Flags().StringVar(&opts.repo, "repo", "", "Name of the remote repository (default: .dockerapp)") + cmd.Flags().BoolVar(&opts.insecure, "insecure", false, "Use insecure registry, without SSL") return cmd } + +func runPush(dockerCli command.Cli, name string, opts pushOptions) error { + app, err := packager.Extract(name) + if err != nil { + return err + } + defer app.Cleanup() + + ref := makeReference(app, opts) + bndle, err := makeBundleFromApp(dockerCli, app, opts.namespace, "") + if err != nil { + return err + } + if err := fixupContainerImages(dockerCli, bndle); err != nil { + return err + } + signedData, err := makeSignedData(bndle) + if err != nil { + return err + } + digest, err := image.PushBundle(context.TODO(), dockerCli, opts.insecure, signedData, ref) + if err != nil { + return err + } + fmt.Printf("Successfully pushed %s@%s\n", ref, digest) + return nil +} + +func fixupContainerImages(dockerCli command.Cli, bndle *bundle.Bundle) error { + imageResolver := image.NewResolver(true, dockerCli) + if err := bndle.FixupContainerImages(imageResolver); err != nil { + return err + } + return bndle.Validate() +} + +func makeSignedData(bndle *bundle.Bundle) ([]byte, error) { + keyRingFile := duffleHome().SecretKeyRing() + if _, err := os.Stat(keyRingFile); err != nil { + return nil, errors.New(`duffle home has not been initialized, please run "duffle init" first`) + } + // Load keyring + kr, err := signature.LoadKeyRing(keyRingFile) + if err != nil { + return nil, err + } + // Find identity + var k *signature.Key + all := kr.PrivateKeys() + if len(all) == 0 { + return nil, errors.New("no private keys found") + } + k = all[0] + + // Sign the file + s := signature.NewSigner(k) + data, err := s.Clearsign(bndle) + if err != nil { + return nil, err + } + data = append(data, '\n') + return data, nil +} + +func makeReference(app *types.App, opts pushOptions) string { + meta := app.Metadata() + + ref := opts.repo + if ref == "" { + ref = meta.Name + } + namespace := opts.namespace + if namespace == "" { + namespace = meta.Namespace + } + version := opts.tag + if version == "" { + version = meta.Version + } + + if namespace != "" { + ref = fmt.Sprintf("%s/%s", namespace, ref) + } + if version != "" { + ref = fmt.Sprintf("%s:%s", ref, version) + } + return ref +} diff --git a/cmd/docker-app/render.go b/cmd/docker-app/render.go index b400be167..ba6882ba9 100644 --- a/cmd/docker-app/render.go +++ b/cmd/docker-app/render.go @@ -16,22 +16,22 @@ import ( ) var ( - formatDriver string - renderComposeFiles []string - renderSettingsFile []string - renderEnv []string - renderOutput string + formatDriver string + renderComposeFiles []string + renderParametersFile []string + renderEnv []string + renderOutput string ) func renderCmd(dockerCli command.Cli) *cobra.Command { cmd := &cobra.Command{ - Use: "render [-s key=value...] [-f settings-file...]", + Use: "render [-s key=value...] [-f parameters-file...]", Short: "Render the Compose file for the application", Long: `Render the Compose file for the application.`, Args: cli.RequiresMaxArgs(1), RunE: func(cmd *cobra.Command, args []string) error { app, err := packager.Extract(firstOrEmpty(args), - types.WithSettingsFiles(renderSettingsFile...), + types.WithParametersFiles(renderParametersFile...), types.WithComposeFiles(renderComposeFiles...), ) if err != nil { @@ -64,10 +64,10 @@ func renderCmd(dockerCli command.Cli) *cobra.Command { cmd.Long += `- External Compose files or template Compose files can be specified with the -c flag. (Repeat the flag for multiple files). These files will be merged in order with the app's own Compose file.` - cmd.Flags().StringArrayVarP(&renderComposeFiles, "compose-files", "c", []string{}, "Override Compose files") + cmd.Flags().StringArrayVarP(&renderComposeFiles, "compose-files", "c", []string{}, "Override Compose file") } - cmd.Flags().StringArrayVarP(&renderSettingsFile, "settings-files", "f", []string{}, "Override settings files") - cmd.Flags().StringArrayVarP(&renderEnv, "set", "s", []string{}, "Override settings values") + cmd.Flags().StringArrayVarP(&renderParametersFile, "parameters-files", "f", []string{}, "Override with parameters from files") + cmd.Flags().StringArrayVarP(&renderEnv, "set", "s", []string{}, "Override parameters values") cmd.Flags().StringVarP(&renderOutput, "output", "o", "-", "Output file") cmd.Flags().StringVar(&formatDriver, "formatter", "yaml", "Configure the output format (yaml|json)") return cmd diff --git a/cmd/docker-app/root.go b/cmd/docker-app/root.go index 8361b746b..fd8e251f8 100644 --- a/cmd/docker-app/root.go +++ b/cmd/docker-app/root.go @@ -6,6 +6,7 @@ import ( "github.com/docker/app/internal" "github.com/docker/cli/cli" "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/context" cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/debug" cliflags "github.com/docker/cli/cli/flags" @@ -43,25 +44,34 @@ func newRootCmd(dockerCli *command.DockerCli) *cobra.Command { // addCommands adds all the commands from cli/command to the root command func addCommands(cmd *cobra.Command, dockerCli command.Cli) { cmd.AddCommand( - deployCmd(dockerCli), - forkCmd(), - helmCmd(), initCmd(), - inspectCmd(dockerCli), - mergeCmd(dockerCli), - pushCmd(), + // Bundle commands + bundleCmd(dockerCli), + installCmd(dockerCli), + uninstallCmd(dockerCli), + statusCmd(dockerCli), + upgradeCmd(dockerCli), + // Package commands renderCmd(dockerCli), - splitCmd(), + inspectCmd(dockerCli), validateCmd(), + splitCmd(), + mergeCmd(dockerCli), + // Share commands + pushCmd(dockerCli), + pullCmd(dockerCli), + // Tools commands versionCmd(dockerCli), completionCmd(dockerCli, cmd), + // Environment commands + credentialSetCmd(dockerCli), + context.NewContextCommand(dockerCli), ) if internal.Experimental == "on" { cmd.AddCommand( imageAddCmd(), imageLoadCmd(), packCmd(dockerCli), - pullCmd(), unpackCmd(), ) } diff --git a/cmd/docker-app/status.go b/cmd/docker-app/status.go new file mode 100644 index 000000000..c442fe953 --- /dev/null +++ b/cmd/docker-app/status.go @@ -0,0 +1,57 @@ +package main + +import ( + "github.com/deis/duffle/pkg/action" + "github.com/deis/duffle/pkg/claim" + "github.com/deis/duffle/pkg/credentials" + "github.com/deis/duffle/pkg/utils/crud" + "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" + "github.com/spf13/cobra" +) + +func statusCmd(dockerCli command.Cli) *cobra.Command { + var opts uninstallOptions + + cmd := &cobra.Command{ + Use: "status ", + Short: "Get an application status", + Args: cli.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return runStatus(dockerCli, args[0], opts) + }, + } + + cmd.Flags().StringVar(&opts.targetContext, "target-context", "", "Context on which to request the application status") + cmd.Flags().StringArrayVarP(&opts.credentialsets, "credential-set", "c", []string{}, "Use a duffle credentialset (either a YAML file, or a credential set present in the duffle credential store)") + + return cmd +} + +func runStatus(dockerCli command.Cli, claimName string, opts uninstallOptions) error { + muteDockerCli(dockerCli) + h := duffleHome() + + claimStore := claim.NewClaimStore(crud.NewFileSystemStore(h.Claims(), "json")) + c, err := claimStore.Read(claimName) + if err != nil { + return err + } + targetContext := getTargetContext(opts.targetContext) + + driverImpl, err := prepareDriver(dockerCli) + if err != nil { + return err + } + creds, err := prepareCredentialSet(targetContext, dockerCli.ContextStore(), c.Bundle, opts.credentialsets) + if err != nil { + return err + } + if err := credentials.Validate(creds, c.Bundle.Credentials); err != nil { + return err + } + status := &action.Status{ + Driver: driverImpl, + } + return status.Run(&c, creds, dockerCli.Out()) +} diff --git a/cmd/docker-app/uninstall.go b/cmd/docker-app/uninstall.go new file mode 100644 index 000000000..9a992747b --- /dev/null +++ b/cmd/docker-app/uninstall.go @@ -0,0 +1,71 @@ +package main + +import ( + "fmt" + + "github.com/deis/duffle/pkg/action" + "github.com/deis/duffle/pkg/claim" + "github.com/deis/duffle/pkg/credentials" + "github.com/deis/duffle/pkg/utils/crud" + "github.com/docker/cli/cli" + "github.com/docker/cli/cli/command" + "github.com/spf13/cobra" +) + +type uninstallOptions struct { + targetContext string + credentialsets []string +} + +func uninstallCmd(dockerCli command.Cli) *cobra.Command { + var opts uninstallOptions + + cmd := &cobra.Command{ + Use: "uninstall ", + Short: "Uninstall an application", + Args: cli.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return runUninstall(dockerCli, args[0], opts) + }, + } + + cmd.Flags().StringVar(&opts.targetContext, "target-context", "", "Context on which to uninstall the application") + cmd.Flags().StringArrayVarP(&opts.credentialsets, "credential-set", "c", []string{}, "Use a duffle credentialset (either a YAML file, or a credential set present in the duffle credential store)") + + return cmd +} + +func runUninstall(dockerCli command.Cli, claimName string, opts uninstallOptions) error { + muteDockerCli(dockerCli) + h := duffleHome() + + claimStore := claim.NewClaimStore(crud.NewFileSystemStore(h.Claims(), "json")) + c, err := claimStore.Read(claimName) + if err != nil { + return err + } + targetContext := getTargetContext(opts.targetContext) + + driverImpl, err := prepareDriver(dockerCli) + if err != nil { + return err + } + creds, err := prepareCredentialSet(targetContext, dockerCli.ContextStore(), c.Bundle, opts.credentialsets) + if err != nil { + return err + } + if err := credentials.Validate(creds, c.Bundle.Credentials); err != nil { + return err + } + uninst := &action.Uninstall{ + Driver: driverImpl, + } + err = uninst.Run(&c, creds, dockerCli.Out()) + if err == nil { + return claimStore.Delete(claimName) + } + if err2 := claimStore.Store(c); err2 != nil { + fmt.Fprintf(dockerCli.Err(), "failed to update claim: %s\n", err2) + } + return err +} diff --git a/cmd/docker-app/upgrade.go b/cmd/docker-app/upgrade.go new file mode 100644 index 000000000..84af9583a --- /dev/null +++ b/cmd/docker-app/upgrade.go @@ -0,0 +1,94 @@ +package main + +import ( + "fmt" + + "github.com/deis/duffle/pkg/action" + "github.com/deis/duffle/pkg/bundle" + "github.com/deis/duffle/pkg/claim" + "github.com/deis/duffle/pkg/credentials" + "github.com/deis/duffle/pkg/utils/crud" + "github.com/docker/cli/cli/command" + "github.com/spf13/cobra" +) + +type upgradeOptions struct { + parametersOptions + targetContext string + credentialsets []string + bundleOrDockerApp string + namespace string + insecure bool +} + +func upgradeCmd(dockerCli command.Cli) *cobra.Command { + var opts upgradeOptions + cmd := &cobra.Command{ + Use: "upgrade [options]", + Short: "Upgrade an installed application", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return runUpgrade(dockerCli, args[0], opts) + }, + } + opts.parametersOptions.addFlags(cmd.Flags()) + cmd.Flags().StringVar(&opts.targetContext, "target-context", "", "Context on which to upgrade the application") + cmd.Flags().StringArrayVarP(&opts.credentialsets, "credential-set", "c", []string{}, "Use a duffle credentialset (either a YAML file, or a credential set present in the duffle credential store)") + cmd.Flags().StringVar(&opts.bundleOrDockerApp, "bundle", "", "Override with new bundle or Docker App") + cmd.Flags().StringVar(&opts.namespace, "namespace", "", "Namespace to use (default: namespace in metadata)") + cmd.Flags().BoolVar(&opts.insecure, "insecure", false, "Use insecure registry, without SSL") + + return cmd +} + +func runUpgrade(dockerCli command.Cli, installationName string, opts upgradeOptions) error { + muteDockerCli(dockerCli) + targetContext := getTargetContext(opts.targetContext) + parameterValues, err := prepareParameters(opts.parametersOptions) + if err != nil { + return err + } + h := duffleHome() + claimStore := claim.NewClaimStore(crud.NewFileSystemStore(h.Claims(), "json")) + c, err := claimStore.Read(installationName) + if err != nil { + return err + } + + if opts.bundleOrDockerApp != "" { + b, err := resolveBundle(dockerCli, opts.namespace, opts.bundleOrDockerApp, opts.insecure) + if err != nil { + return err + } + c.Bundle = b + } + driverImpl, err := prepareDriver(dockerCli) + if err != nil { + return err + } + creds, err := prepareCredentialSet(targetContext, dockerCli.ContextStore(), c.Bundle, opts.credentialsets) + if err != nil { + return err + } + if err := credentials.Validate(creds, c.Bundle.Credentials); err != nil { + return err + } + convertedParamValues := c.Parameters + if err := applyParameterValues(parameterValues, c.Bundle.Parameters, convertedParamValues); err != nil { + return err + } + + c.Parameters, err = bundle.ValuesOrDefaults(convertedParamValues, c.Bundle) + if err != nil { + return err + } + u := &action.Upgrade{ + Driver: driverImpl, + } + err = u.Run(&c, creds, dockerCli.Out()) + err2 := claimStore.Store(c) + if err != nil { + return fmt.Errorf("upgrade failed: %v", err) + } + return err2 +} diff --git a/cmd/docker-app/validate.go b/cmd/docker-app/validate.go index 01bed3677..3d7f6b67d 100644 --- a/cmd/docker-app/validate.go +++ b/cmd/docker-app/validate.go @@ -10,29 +10,29 @@ import ( ) var ( - validateSettingsFile []string - validateEnv []string + validateParametersFile []string + validateEnv []string ) func validateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "validate [] [-s key=value...] [-f settings-file...]", + Use: "validate [] [-s key=value...] [-f parameters-file...]", 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.WithSettingsFiles(validateSettingsFile...), + types.WithParametersFiles(validateParametersFile...), ) if err != nil { return err } defer app.Cleanup() - argSettings := cliopts.ConvertKVStringsToMap(validateEnv) - _, err = render.Render(app, argSettings) + argParameters := cliopts.ConvertKVStringsToMap(validateEnv) + _, err = render.Render(app, argParameters) return err }, } - cmd.Flags().StringArrayVarP(&validateSettingsFile, "settings-files", "f", []string{}, "Override settings files") - cmd.Flags().StringArrayVarP(&validateEnv, "set", "s", []string{}, "Override settings values") + cmd.Flags().StringArrayVarP(&validateParametersFile, "parameters-files", "f", []string{}, "Override with parameters from files") + cmd.Flags().StringArrayVarP(&validateEnv, "set", "s", []string{}, "Override parameters values") return cmd } diff --git a/cmd/run/env.go b/cmd/run/env.go new file mode 100644 index 000000000..fd2955e78 --- /dev/null +++ b/cmd/run/env.go @@ -0,0 +1,33 @@ +package main + +import ( + "os" + + "github.com/docker/cli/cli/command" + cliconfig "github.com/docker/cli/cli/config" + contextstore "github.com/docker/cli/cli/context/store" + cliflags "github.com/docker/cli/cli/flags" +) + +const ( + envVarOchestrator = "DOCKER_STACK_ORCHESTRATOR" + fileDockerContext = "/cnab/app/context.dockercontext" +) + +func setupDockerContext() (command.Cli, error) { + s := contextstore.New(cliconfig.ContextStoreDir()) + f, err := os.Open(fileDockerContext) + if err != nil { + return nil, err + } + defer f.Close() + if err := contextstore.Import("cnab", s, f); err != nil { + return nil, err + } + cli := command.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, false, nil) + return cli, cli.Initialize(&cliflags.ClientOptions{ + Common: &cliflags.CommonOptions{ + Context: "cnab", + }, + }) +} diff --git a/cmd/run/inspect.go b/cmd/run/inspect.go new file mode 100644 index 000000000..67d21d31b --- /dev/null +++ b/cmd/run/inspect.go @@ -0,0 +1,19 @@ +package main + +import ( + "os" + + appinspect "github.com/docker/app/internal/inspect" + "github.com/docker/app/internal/packager" +) + +func inspect() error { + app, err := packager.Extract("") + // todo: merge addition compose file + if err != nil { + return err + } + defer app.Cleanup() + parameters := packager.ExtractCNABParametersValues(packager.ExtractCNABParameterMapping(app.Parameters()), os.Environ()) + return appinspect.Inspect(os.Stdout, app, parameters) +} diff --git a/cmd/run/install.go b/cmd/run/install.go new file mode 100644 index 000000000..3208f5853 --- /dev/null +++ b/cmd/run/install.go @@ -0,0 +1,55 @@ +package main + +import ( + "os" + + "github.com/docker/app/internal/packager" + "github.com/docker/app/render" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/stack" + "github.com/docker/cli/cli/command/stack/options" + "github.com/docker/cli/cli/command/stack/swarm" + "github.com/pkg/errors" + "github.com/spf13/pflag" +) + +func install(instanceName string) error { + cli, err := setupDockerContext() + if err != nil { + return errors.Wrap(err, "unable to restore docker context") + } + app, err := packager.Extract("") + // todo: merge addition compose file + if err != nil { + return err + } + defer app.Cleanup() + + orchestratorRaw := os.Getenv(envVarOchestrator) + orchestrator, err := cli.StackOrchestrator(orchestratorRaw) + if err != nil { + return err + } + parameters := packager.ExtractCNABParametersValues(packager.ExtractCNABParameterMapping(app.Parameters()), os.Environ()) + rendered, err := render.Render(app, parameters) + if err != nil { + return err + } + if err := os.Chdir(app.Path); err != nil { + return err + } + // todo: pass registry auth to invocation image + return stack.RunDeploy(cli, getFlagset(orchestrator), rendered, orchestrator, options.Deploy{ + Namespace: instanceName, + ResolveImage: swarm.ResolveImageAlways, + SendRegistryAuth: false, + }) +} + +func getFlagset(orchestrator command.Orchestrator) *pflag.FlagSet { + result := pflag.NewFlagSet("", pflag.ContinueOnError) + if orchestrator == command.OrchestratorKubernetes { + result.String("namespace", os.Getenv("DOCKER_KUBERNETES_NAMESPACE"), "") + } + return result +} diff --git a/cmd/run/main.go b/cmd/run/main.go new file mode 100644 index 000000000..c3c900544 --- /dev/null +++ b/cmd/run/main.go @@ -0,0 +1,81 @@ +package main + +import ( + "errors" + "fmt" + "os" +) + +type cnabAction string + +const ( + cnabActionInstall = cnabAction("install") + cnabActionUninstall = cnabAction("uninstall") + cnabActionUpgrade = cnabAction("upgrade") + cnabActionStatus = cnabAction("status") + cnabActionInspect = cnabAction("inspect") +) + +type cnabOperation struct { + action cnabAction + installation string +} + +func getCnabAction() (cnabAction, error) { + action, ok := os.LookupEnv("CNAB_ACTION") + if !ok { + return "", errors.New("no CNAB action specified") + } + return cnabAction(action), nil +} + +func getCnabOperation() (cnabOperation, error) { + // CNAB_ACTION should always be set. but in future we want to have + // claim-less actions. So we don't fail if no installation is set + action, err := getCnabAction() + if err != nil { + return cnabOperation{}, err + } + return cnabOperation{ + action: action, + installation: os.Getenv("CNAB_INSTALLATION_NAME"), + }, nil +} + +func main() { + op, err := getCnabOperation() + if err != nil { + fmt.Fprintf(os.Stderr, "Error while parsing cnab operation: %s", err) + os.Exit(1) + } + switch op.action { + case cnabActionInstall: + if err := install(op.installation); err != nil { + fmt.Fprintf(os.Stderr, "Install failed: %s", err) + os.Exit(1) + } + case cnabActionUpgrade: + if err := install(op.installation); err != nil { + fmt.Fprintf(os.Stderr, "Upgrade failed: %s", err) + os.Exit(1) + } + case cnabActionUninstall: + if err := uninstall(op.installation); err != nil { + fmt.Fprintf(os.Stderr, "Uninstall failed: %s", err) + os.Exit(1) + } + case cnabActionStatus: + if err := status(op.installation); err != nil { + fmt.Fprintf(os.Stderr, "Status failed: %s", err) + os.Exit(1) + } + case cnabActionInspect: + if err := inspect(); err != nil { + fmt.Fprintf(os.Stderr, "Inspect failed: %s", err) + os.Exit(1) + } + default: + fmt.Fprintf(os.Stderr, "Action %q is not supported", op.action) + os.Exit(1) + } +} diff --git a/cmd/run/status.go b/cmd/run/status.go new file mode 100644 index 000000000..d5c7c5585 --- /dev/null +++ b/cmd/run/status.go @@ -0,0 +1,26 @@ +package main + +import ( + "os" + + "github.com/docker/cli/cli/command/stack" + "github.com/docker/cli/cli/command/stack/options" + "github.com/docker/cli/opts" + "github.com/pkg/errors" +) + +func status(instanceName string) error { + cli, err := setupDockerContext() + if err != nil { + return errors.Wrap(err, "unable to restore docker context") + } + orchestratorRaw := os.Getenv(envVarOchestrator) + orchestrator, err := cli.StackOrchestrator(orchestratorRaw) + if err != nil { + return err + } + return stack.RunServices(cli, getFlagset(orchestrator), orchestrator, options.Services{ + Namespace: instanceName, + Filter: opts.NewFilterOpt(), + }) +} diff --git a/cmd/run/uninstall.go b/cmd/run/uninstall.go new file mode 100644 index 000000000..ec8720b3a --- /dev/null +++ b/cmd/run/uninstall.go @@ -0,0 +1,24 @@ +package main + +import ( + "os" + + "github.com/docker/cli/cli/command/stack" + "github.com/docker/cli/cli/command/stack/options" + "github.com/pkg/errors" +) + +func uninstall(instanceName string) error { + cli, err := setupDockerContext() + if err != nil { + return errors.Wrap(err, "unable to restore docker context") + } + orchestratorRaw := os.Getenv(envVarOchestrator) + orchestrator, err := cli.StackOrchestrator(orchestratorRaw) + if err != nil { + return err + } + return stack.RunRemove(cli, getFlagset(orchestrator), orchestrator, options.Remove{ + Namespaces: []string{instanceName}, + }) +} diff --git a/docker.Makefile b/docker.Makefile index bf94d8a14..304b68217 100644 --- a/docker.Makefile +++ b/docker.Makefile @@ -13,10 +13,22 @@ E2E_CROSS_CTNR_NAME := $(BIN_NAME)-e2e-cross-$(TAG) COV_CTNR_NAME := $(BIN_NAME)-cov-$(TAG) SCHEMAS_CTNR_NAME := $(BIN_NAME)-schemas-$(TAG) -BUILD_ARGS="--build-arg=EXPERIMENTAL=$(EXPERIMENTAL)" +DUFFLE_BIN_NAME := duffle +DUFFLE_PKG := /go/src/github.com/deis/duffle + +BUILD_ARGS=--build-arg=EXPERIMENTAL=$(EXPERIMENTAL) --build-arg=TAG=$(TAG) --build-arg=COMMIT=$(COMMIT) PKG_PATH := /go/src/$(PKG_NAME) +CNAB_BASE_INVOCATION_IMAGE_NAME := docker/cnab-app-base:$(TAG) + +CNAB_BASE_INVOCATION_IMAGE_PATH := /tmp/invocation-image-$(TAG).tar +RM := rm +ifeq ($(OS),Windows_NT) + CNAB_BASE_INVOCATION_IMAGE_PATH := c:\tmp\invocation-image-$(TAG).tar + RM := del +endif + .DEFAULT: all all: cross test @@ -35,10 +47,16 @@ cross: create_bin ## cross-compile binaries (linux, darwin, windows) docker cp $(CROSS_CTNR_NAME):$(PKG_PATH)/bin/$(BIN_NAME)-linux bin/$(BIN_NAME)-linux docker cp $(CROSS_CTNR_NAME):$(PKG_PATH)/bin/$(BIN_NAME)-darwin bin/$(BIN_NAME)-darwin docker cp $(CROSS_CTNR_NAME):$(PKG_PATH)/bin/$(BIN_NAME)-windows.exe bin/$(BIN_NAME)-windows.exe + docker cp $(CROSS_CTNR_NAME):$(DUFFLE_PKG)/bin/$(DUFFLE_BIN_NAME)-linux-amd64 bin/$(DUFFLE_BIN_NAME)-linux + docker cp $(CROSS_CTNR_NAME):$(DUFFLE_PKG)/bin/$(DUFFLE_BIN_NAME)-darwin-amd64 bin/$(DUFFLE_BIN_NAME)-darwin + docker cp $(CROSS_CTNR_NAME):$(DUFFLE_PKG)/bin/$(DUFFLE_BIN_NAME)-windows-amd64.exe bin/$(DUFFLE_BIN_NAME)-windows.exe docker rm $(CROSS_CTNR_NAME) @$(call chmod,+x,bin/$(BIN_NAME)-linux) @$(call chmod,+x,bin/$(BIN_NAME)-darwin) @$(call chmod,+x,bin/$(BIN_NAME)-windows.exe) + @$(call chmod,+x,bin/$(DUFFLE_BIN_NAME)-linux) + @$(call chmod,+x,bin/$(DUFFLE_BIN_NAME)-darwin) + @$(call chmod,+x,bin/$(DUFFLE_BIN_NAME)-windows.exe) e2e-cross: create_bin docker build $(BUILD_ARGS) --target=e2e-cross -t $(E2E_CROSS_IMAGE_NAME) . @@ -52,11 +70,11 @@ e2e-cross: create_bin @$(call chmod,+x,bin/$(BIN_NAME)-e2e-windows.exe) tars: - tar czf bin/$(BIN_NAME)-linux.tar.gz -C bin $(BIN_NAME)-linux + tar czf bin/$(BIN_NAME)-linux.tar.gz -C bin $(BIN_NAME)-linux $(DUFFLE_BIN_NAME)-linux tar czf bin/$(BIN_NAME)-e2e-linux.tar.gz -C bin $(BIN_NAME)-e2e-linux - tar czf bin/$(BIN_NAME)-darwin.tar.gz -C bin $(BIN_NAME)-darwin + tar czf bin/$(BIN_NAME)-darwin.tar.gz -C bin $(BIN_NAME)-darwin $(DUFFLE_BIN_NAME)-darwin tar czf bin/$(BIN_NAME)-e2e-darwin.tar.gz -C bin $(BIN_NAME)-e2e-darwin - tar czf bin/$(BIN_NAME)-windows.tar.gz -C bin $(BIN_NAME)-windows.exe + tar czf bin/$(BIN_NAME)-windows.tar.gz -C bin $(BIN_NAME)-windows.exe $(DUFFLE_BIN_NAME)-windows.exe tar czf bin/$(BIN_NAME)-e2e-windows.tar.gz -C bin $(BIN_NAME)-e2e-windows.exe test: test-unit test-e2e ## run all tests @@ -64,7 +82,7 @@ test: test-unit test-e2e ## run all tests test-unit: build_dev_image ## run unit tests docker run --rm $(DEV_IMAGE_NAME) make EXPERIMENTAL=$(EXPERIMENTAL) test-unit -test-e2e: build_dev_image ## run end-to-end tests +test-e2e: build_dev_image invocation-image ## run end-to-end tests docker run -v /var/run:/var/run:ro --rm --network="host" $(DEV_IMAGE_NAME) make EXPERIMENTAL=$(EXPERIMENTAL) bin/$(BIN_NAME) test-e2e COV_LABEL := com.docker.app.cov-run=$(TAG) @@ -95,7 +113,23 @@ specification/bindata.go: specification/schemas/*.json build_dev_image schemas: specification/bindata.go ## generate specification/bindata.go from json schemas +invocation-image: + docker build $(BUILD_ARGS) --target=invocation -t $(CNAB_BASE_INVOCATION_IMAGE_NAME) . + +save-invocation-image: invocation-image + docker save $(CNAB_BASE_INVOCATION_IMAGE_NAME) -o $(CNAB_BASE_INVOCATION_IMAGE_PATH) + +load-invocation-image: + docker load -i $(CNAB_BASE_INVOCATION_IMAGE_PATH) + $(RM) $(CNAB_BASE_INVOCATION_IMAGE_PATH) + +clean-invocation-image: + $(RM) $(CNAB_BASE_INVOCATION_IMAGE_PATH) + +push-invocation-image: + docker push $(CNAB_BASE_INVOCATION_IMAGE_NAME) + help: ## this help @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) | sort -.PHONY: lint test-e2e test-unit test cross e2e-cross coverage gradle-test shell build_dev_image tars vendor schemas help +.PHONY: lint test-e2e test-unit test cross e2e-cross coverage gradle-test shell build_dev_image tars vendor schemas help invocation-image diff --git a/e2e/commands_test.go b/e2e/commands_test.go index dcf1edc1e..2235602f7 100644 --- a/e2e/commands_test.go +++ b/e2e/commands_test.go @@ -29,7 +29,7 @@ services: hello-world: image: hello-world --- -# This section contains the default values for your application settings.` +# This section contains the default values for your application parameters.` ) func TestRenderTemplates(t *testing.T) { @@ -59,14 +59,14 @@ func TestRender(t *testing.T) { func testRenderApp(appPath string, env ...string) func(*testing.T) { return func(t *testing.T) { - envSettings := map[string]string{} + envParameters := map[string]string{} data, err := ioutil.ReadFile(filepath.Join(appPath, "env.yml")) assert.NilError(t, err) - assert.NilError(t, yaml.Unmarshal(data, &envSettings)) + assert.NilError(t, yaml.Unmarshal(data, &envParameters)) args := []string{dockerApp, "render", filepath.Join(appPath, "my.dockerapp"), - "-f", filepath.Join(appPath, "settings-0.yml"), + "-f", filepath.Join(appPath, "parameters-0.yml"), } - for k, v := range envSettings { + for k, v := range envParameters { args = append(args, "-s", fmt.Sprintf("%s=%s", k, v)) } result := icmd.RunCmd(icmd.Cmd{ @@ -78,7 +78,7 @@ func testRenderApp(appPath string, env ...string) func(*testing.T) { } func TestRenderFormatters(t *testing.T) { - appPath := filepath.Join("testdata", "fork", "simple.dockerapp") + appPath := filepath.Join("testdata", "simple", "simple.dockerapp") result := icmd.RunCommand(dockerApp, "render", "--formatter", "json", appPath).Assert(t, icmd.Success) assert.Assert(t, golden.String(result.Stdout(), "expected-json-render.golden")) @@ -100,7 +100,7 @@ name: app-test # A short description of the application description: my cool app # Namespace to use when pushing to a registry. This is typically your Hub username. -#namespace: myHubUsername +#namespace: myhubusername # List of application maintainers with name and email for each maintainers: - name: bob @@ -130,7 +130,7 @@ maintainers: fs.WithMode(0755), fs.WithFile(internal.MetadataFileName, meta, fs.WithMode(0644)), // too many variables, cheating fs.WithFile(internal.ComposeFileName, composeData, fs.WithMode(0644)), - fs.WithFile(internal.SettingsFileName, "NGINX_ARGS: FILL ME\nNGINX_VERSION: latest\n", fs.WithMode(0644)), + fs.WithFile(internal.ParametersFileName, "NGINX_ARGS: FILL ME\nNGINX_VERSION: latest\n", fs.WithMode(0644)), ) assert.Assert(t, fs.Equal(dirName, manifest)) @@ -157,7 +157,7 @@ maintainers: func TestDetectApp(t *testing.T) { // cwd = e2e dir := fs.NewDir(t, "detect-app-binary", - fs.WithDir("helm.dockerapp", fs.FromDir("testdata/helm.dockerapp")), + fs.WithDir("attachments.dockerapp", fs.FromDir("testdata/attachments.dockerapp")), fs.WithDir("render", fs.WithDir("app1.dockerapp", fs.FromDir("testdata/render/envvariables/my.dockerapp")), fs.WithDir("app2.dockerapp", fs.FromDir("testdata/render/envvariables/my.dockerapp")), @@ -170,11 +170,11 @@ func TestDetectApp(t *testing.T) { }).Assert(t, icmd.Success) icmd.RunCmd(icmd.Cmd{ Command: []string{dockerApp, "inspect"}, - Dir: dir.Join("helm.dockerapp"), + Dir: dir.Join("attachments.dockerapp"), }).Assert(t, icmd.Success) icmd.RunCmd(icmd.Cmd{ Command: []string{dockerApp, "inspect", "."}, - Dir: dir.Join("helm.dockerapp"), + Dir: dir.Join("attachments.dockerapp"), }).Assert(t, icmd.Success) result := icmd.RunCmd(icmd.Cmd{ Command: []string{dockerApp, "inspect"}, @@ -191,7 +191,7 @@ func TestPack(t *testing.T) { tempDir, err := ioutil.TempDir("", "dockerapp") assert.NilError(t, err) defer os.RemoveAll(tempDir) - icmd.RunCommand(dockerApp, "pack", "testdata/helm", "-o", filepath.Join(tempDir, "test.dockerapp")).Assert(t, icmd.Success) + icmd.RunCommand(dockerApp, "pack", "testdata/attachments", "-o", filepath.Join(tempDir, "test.dockerapp")).Assert(t, icmd.Success) // check that our commands run on the packed version icmd.RunCommand(dockerApp, "inspect", filepath.Join(tempDir, "test")).Assert(t, icmd.Expected{ Out: "myapp", @@ -199,12 +199,6 @@ func TestPack(t *testing.T) { icmd.RunCommand(dockerApp, "render", filepath.Join(tempDir, "test")).Assert(t, icmd.Expected{ Out: "nginx", }) - icmd.RunCmd(icmd.Cmd{ - Command: []string{dockerApp, "helm", "test"}, - Dir: tempDir, - }).Assert(t, icmd.Success) - _, err = os.Stat(filepath.Join(tempDir, "test.chart", "Chart.yaml")) - assert.NilError(t, err) assert.NilError(t, os.Mkdir(filepath.Join(tempDir, "output"), 0755)) icmd.RunCmd(icmd.Cmd{ Command: []string{dockerApp, "unpack", "test", "-o", "output"}, @@ -214,41 +208,6 @@ func TestPack(t *testing.T) { assert.NilError(t, err) } -func TestHelm(t *testing.T) { - t.Run("default", testHelm("")) - t.Run("v1beta1", testHelm("v1beta1")) - t.Run("v1beta2", testHelm("v1beta2")) -} - -func testHelm(version string) func(*testing.T) { - return func(t *testing.T) { - dir := fs.NewDir(t, "testHelmBinary", fs.FromDir("testdata")) - defer dir.Remove() - cmd := []string{dockerApp, "helm", "helm", "-s", "myapp.nginx_version=2"} - if version != "" { - cmd = append(cmd, "--stack-version", version) - } - icmd.RunCmd(icmd.Cmd{ - Command: cmd, - Dir: dir.Path(), - }).Assert(t, icmd.Success) - - chart := golden.Get(t, dir.Join("helm.chart/Chart.yaml")) - values := golden.Get(t, dir.Join("helm.chart/values.yaml")) - stack := golden.Get(t, dir.Join("helm.chart/templates/stack.yaml")) - assert.Check(t, golden.String(string(chart), "helm-expected.chart/Chart.yaml")) - assert.Check(t, golden.String(string(values), "helm-expected.chart/values.yaml")) - assert.Check(t, golden.String(string(stack), "helm-expected.chart/templates/stack"+version+".yaml")) - } -} - -func TestHelmInvalidStackVersion(t *testing.T) { - icmd.RunCommand(dockerApp, "helm", "testdata/helm", "--stack-version", "foobar").Assert(t, icmd.Expected{ - ExitCode: 1, - Err: `Error: invalid stack version "foobar" (accepted values: v1beta1, v1beta2)`, - }) -} - func TestSplitMerge(t *testing.T) { icmd.RunCommand(dockerApp, "merge", "testdata/render/envvariables/my.dockerapp", "-o", "remerged.dockerapp").Assert(t, icmd.Success) defer os.Remove("remerged.dockerapp") @@ -265,57 +224,23 @@ func TestSplitMerge(t *testing.T) { icmd.RunCommand(dockerApp, "split", "split").Assert(t, icmd.Success) } -func TestURL(t *testing.T) { - url := "https://raw.githubusercontent.com/docker/app/v0.4.1/examples/hello-world/hello-world.dockerapp" - result := icmd.RunCommand(dockerApp, "inspect", url).Assert(t, icmd.Success) - assert.Assert(t, golden.String(result.Combined(), "helloworld-inspect.golden")) -} - func TestWithRegistry(t *testing.T) { r := startRegistry(t) defer r.Stop(t) registry := r.GetAddress(t) - t.Run("image", testImage(registry)) - t.Run("fork", testFork(registry)) -} - -func testImage(registry string) func(*testing.T) { - return func(t *testing.T) { - // push to a registry - icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/myuser", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success) - icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/myuser", "-t", "latest", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success) - icmd.RunCommand(dockerApp, "inspect", registry+"/myuser/my.dockerapp:0.1.0").Assert(t, icmd.Success) - icmd.RunCommand(dockerApp, "inspect", registry+"/myuser/my.dockerapp").Assert(t, icmd.Success) - icmd.RunCommand(dockerApp, "inspect", registry+"/myuser/my").Assert(t, icmd.Success) - icmd.RunCommand(dockerApp, "inspect", registry+"/myuser/my:0.1.0").Assert(t, icmd.Success) - // push a single-file app to a registry - dir := fs.NewDir(t, "save-prepare-build", fs.WithFile("my.dockerapp", singleFileApp)) - defer dir.Remove() - icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/myuser", dir.Join("my.dockerapp")).Assert(t, icmd.Success) - - // push with custom repo name - icmd.RunCommand(dockerApp, "push", "-t", "marshmallows", "--namespace", registry+"/rainbows", "--repo", "unicorns", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success) - icmd.RunCommand(dockerApp, "inspect", registry+"/rainbows/unicorns:marshmallows").Assert(t, icmd.Success) - } -} - -func testFork(registry string) func(*testing.T) { - return func(t *testing.T) { - icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/acmecorp", "testdata/fork/simple").Assert(t, icmd.Success) - - tempDir := fs.NewDir(t, "dockerapptest") - defer tempDir.Remove() - - icmd.RunCommand(dockerApp, "fork", registry+"/acmecorp/simple.dockerapp:1.1.0-beta1", "acmecorp/scarlet.devil", - "-p", tempDir.Path(), "-m", "Remilia Scarlet:remilia@acmecorp.cool").Assert(t, icmd.Success) - metadata := golden.Get(t, tempDir.Join("scarlet.devil.dockerapp", "metadata.yml")) - assert.Assert(t, golden.Bytes(metadata, "expected-fork-metadata.golden")) + // push to a registry + icmd.RunCommand(dockerApp, "push", "--insecure", "--namespace", registry+"/myuser", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success) + icmd.RunCommand(dockerApp, "push", "--insecure", "--namespace", registry+"/myuser", "-t", "latest", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success) + icmd.RunCommand(dockerApp, "inspect", "--insecure", registry+"/myuser/myapp:0.1.0").Assert(t, icmd.Success) + icmd.RunCommand(dockerApp, "inspect", "--insecure", registry+"/myuser/myapp:latest").Assert(t, icmd.Success) + // push a single-file app to a registry + dir := fs.NewDir(t, "save-prepare-build", fs.WithFile("my.dockerapp", singleFileApp)) + defer dir.Remove() + icmd.RunCommand(dockerApp, "push", "--insecure", "--namespace", registry+"/myuser", dir.Join("my.dockerapp")).Assert(t, icmd.Success) - icmd.RunCommand(dockerApp, "fork", registry+"/acmecorp/simple.dockerapp:1.1.0-beta1", - "-p", tempDir.Path(), "-m", "Remilia Scarlet:remilia@acmecorp.cool").Assert(t, icmd.Success) - metadata2 := golden.Get(t, tempDir.Join("simple.dockerapp", "metadata.yml")) - assert.Assert(t, golden.Bytes(metadata2, "expected-fork-metadata-no-rename.golden")) - } + // push with custom repo name + icmd.RunCommand(dockerApp, "push", "--insecure", "-t", "marshmallows", "--namespace", registry+"/rainbows", "--repo", "unicorns", "testdata/render/envvariables/my.dockerapp").Assert(t, icmd.Success) + icmd.RunCommand(dockerApp, "inspect", "--insecure", registry+"/rainbows/unicorns:marshmallows").Assert(t, icmd.Success) } func TestAttachmentsWithRegistry(t *testing.T) { @@ -328,10 +253,10 @@ func TestAttachmentsWithRegistry(t *testing.T) { ) defer dir.Remove() - icmd.RunCommand(dockerApp, "push", "--namespace", registry+"/acmecorp", dir.Join("attachments.dockerapp")).Assert(t, icmd.Success) + icmd.RunCommand(dockerApp, "push", "--insecure", "--namespace", registry+"/acmecorp", "--repo", "attachments", dir.Join("attachments.dockerapp")).Assert(t, icmd.Success) // inspect will run the core pull code too - result := icmd.RunCommand(dockerApp, "inspect", registry+"/acmecorp/attachments.dockerapp:0.1.0") + result := icmd.RunCommand(dockerApp, "inspect", "--insecure", registry+"/acmecorp/attachments:0.1.0") result.Assert(t, icmd.Success) resultOutput := result.Combined() @@ -339,16 +264,4 @@ func TestAttachmentsWithRegistry(t *testing.T) { assert.Assert(t, strings.Contains(resultOutput, "config.cfg")) assert.Assert(t, strings.Contains(resultOutput, "nesteddir/config2.cfg")) assert.Assert(t, strings.Contains(resultOutput, "nesteddir/nested2/nested3/config3.cfg")) - - // Test forking with external files - tempDir := fs.NewDir(t, "dockerapptest") - defer tempDir.Remove() - - icmd.RunCommand(dockerApp, "fork", registry+"/acmecorp/attachments.dockerapp:0.1.0", - "-p", tempDir.Path()).Assert(t, icmd.Success) - externalFile := golden.Get(t, tempDir.Join("attachments.dockerapp", "config.cfg")) - assert.Assert(t, golden.Bytes(externalFile, filepath.Join("attachments.dockerapp", "config.cfg"))) - - nestedAttachment := golden.Get(t, tempDir.Join("attachments.dockerapp", "nesteddir", "config2.cfg")) - assert.Assert(t, golden.Bytes(nestedAttachment, filepath.Join("attachments.dockerapp", "nesteddir", "config2.cfg"))) } diff --git a/e2e/testdata/attachments.dockerapp/settings.yml b/e2e/testdata/attachments.dockerapp/parameters.yml similarity index 100% rename from e2e/testdata/attachments.dockerapp/settings.yml rename to e2e/testdata/attachments.dockerapp/parameters.yml diff --git a/e2e/testdata/envvariables-inspect.golden b/e2e/testdata/envvariables-inspect.golden index 8d74c6381..5e3579325 100644 --- a/e2e/testdata/envvariables-inspect.golden +++ b/e2e/testdata/envvariables-inspect.golden @@ -6,8 +6,8 @@ Service (1) Replicas Ports Image ----------- -------- ----- ----- test 1 alpine:latest -Settings (4) Value ------------- ----- +Parameters (4) Value +-------------- ----- myapp.alpine_version latest myapp.command1 cat myapp.command2 foo diff --git a/e2e/testdata/expected-fork-metadata-no-rename.golden b/e2e/testdata/expected-fork-metadata-no-rename.golden deleted file mode 100644 index 1c7038a75..000000000 --- a/e2e/testdata/expected-fork-metadata-no-rename.golden +++ /dev/null @@ -1,16 +0,0 @@ -version: 1.1.0-beta1 -name: simple -description: new fancy webapp with microservices -namespace: "" -maintainers: -- name: Remilia Scarlet - email: remilia@acmecorp.cool -parents: -- name: simple - namespace: acmecorp - version: 1.1.0-beta1 - maintainers: - - name: John Developer - email: john.dev@acmecorp.cool - - name: Jane Developer - email: jane.dev@acmecorp.cool diff --git a/e2e/testdata/expected-fork-metadata.golden b/e2e/testdata/expected-fork-metadata.golden deleted file mode 100644 index 0d6c490bc..000000000 --- a/e2e/testdata/expected-fork-metadata.golden +++ /dev/null @@ -1,16 +0,0 @@ -version: 1.1.0-beta1 -name: scarlet.devil -description: new fancy webapp with microservices -namespace: acmecorp -maintainers: -- name: Remilia Scarlet - email: remilia@acmecorp.cool -parents: -- name: simple - namespace: acmecorp - version: 1.1.0-beta1 - maintainers: - - name: John Developer - email: john.dev@acmecorp.cool - - name: Jane Developer - email: jane.dev@acmecorp.cool diff --git a/e2e/testdata/helloworld-inspect.golden b/e2e/testdata/helloworld-inspect.golden index f82234ad9..625a0cd51 100644 --- a/e2e/testdata/helloworld-inspect.golden +++ b/e2e/testdata/helloworld-inspect.golden @@ -8,7 +8,7 @@ Service (1) Replicas Ports Image ----------- -------- ----- ----- hello 1 8080 hashicorp/http-echo -Settings (2) Value ------------- ----- -port 8080 -text Hello, World! +Parameters (2) Value +-------------- ----- +port 8080 +text Hello, World! diff --git a/e2e/testdata/helm-expected.chart/Chart.yaml b/e2e/testdata/helm-expected.chart/Chart.yaml deleted file mode 100644 index 0bb260ca9..000000000 --- a/e2e/testdata/helm-expected.chart/Chart.yaml +++ /dev/null @@ -1,7 +0,0 @@ -description: "" -keywords: [] -maintainers: -- name: bearclaw -- name: bob -name: myapp -version: 0.1.0 diff --git a/e2e/testdata/helm-expected.chart/templates/stack.yaml b/e2e/testdata/helm-expected.chart/templates/stack.yaml deleted file mode 100644 index 1582d53c7..000000000 --- a/e2e/testdata/helm-expected.chart/templates/stack.yaml +++ /dev/null @@ -1,66 +0,0 @@ -kind: Stack -apiVersion: compose.docker.com/v1beta2 -metadata: - name: helm - generatename: "" - namespace: "" - selflink: "" - uid: "" - resourceversion: "" - generation: 0 - creationtimestamp: "0001-01-01T00:00:00Z" - deletiontimestamp: null - deletiongraceperiodseconds: null - labels: {} - annotations: {} - ownerreferences: [] - initializers: null - finalizers: [] - clustername: "" -spec: - services: - - name: app-watcher - image: {{.Values.watcher.image}} - - name: debug - deploy: - resources: - limits: - memory: {{.Values.memory}} - environment: - foo: {{.Values.bar}} - health_check: - test: - - /ping - - debug - timeout: {{.Values.timeout}} - interval: 2m0s - image: busybox:latest - labels: - foo: {{.Values.bar}} - ports: - - mode: ingress - target: {{.Values.aport}} - protocol: tcp - - mode: ingress - target: {{.Values.sport}} - published: {{.Values.dport}} - protocol: tcp - privileged: {{.Values.privileged}} - read_only: {{.Values.read_only}} - stdin_open: {{.Values.stdin_open}} - tty: {{.Values.tty}} - - name: front - deploy: - replicas: {{.Values.myapp.nginx_replicas}} - environment: - {{.Values.foo}}: {{.Values.bar}} - image: nginx:{{.Values.myapp.nginx_version}} - labels: - {{.Values.foo}}: {{.Values.bar}} - - name: monitor - command: - - monitor - - --source - - '{{.Values.app.name}}-{{.Values.app.version}}' - - $dollar - image: busybox:latest diff --git a/e2e/testdata/helm-expected.chart/templates/stackv1beta1.yaml b/e2e/testdata/helm-expected.chart/templates/stackv1beta1.yaml deleted file mode 100644 index b92616f14..000000000 --- a/e2e/testdata/helm-expected.chart/templates/stackv1beta1.yaml +++ /dev/null @@ -1,68 +0,0 @@ -kind: Stack -apiVersion: compose.docker.com/v1beta1 -metadata: - name: helm - generatename: "" - namespace: "" - selflink: "" - uid: "" - resourceversion: "" - generation: 0 - creationtimestamp: "0001-01-01T00:00:00Z" - deletiontimestamp: null - deletiongraceperiodseconds: null - labels: {} - annotations: {} - ownerreferences: [] - initializers: null - finalizers: [] - clustername: "" -spec: - composeFile: | - version: "3.7" - services: - app-watcher: - image: {{.Values.watcher.image}} - debug: - deploy: - resources: - limits: - memory: {{.Values.memory}} - environment: - foo: {{.Values.bar}} - healthcheck: - test: - - /ping - - debug - timeout: {{.Values.timeout}} - interval: 2m0s - image: busybox:latest - labels: - foo: {{.Values.bar}} - ports: - - mode: ingress - target: {{.Values.aport}} - protocol: tcp - - mode: ingress - target: {{.Values.sport}} - published: {{.Values.dport}} - protocol: tcp - privileged: {{.Values.privileged}} - read_only: {{.Values.read_only}} - stdin_open: {{.Values.stdin_open}} - tty: {{.Values.tty}} - front: - deploy: - replicas: {{.Values.myapp.nginx_replicas}} - environment: - {{.Values.foo}}: {{.Values.bar}} - image: nginx:{{.Values.myapp.nginx_version}} - labels: - {{.Values.foo}}: {{.Values.bar}} - monitor: - command: - - monitor - - --source - - '{{.Values.app.name}}-{{.Values.app.version}}' - - $dollar - image: busybox:latest diff --git a/e2e/testdata/helm-expected.chart/templates/stackv1beta2.yaml b/e2e/testdata/helm-expected.chart/templates/stackv1beta2.yaml deleted file mode 100644 index 1582d53c7..000000000 --- a/e2e/testdata/helm-expected.chart/templates/stackv1beta2.yaml +++ /dev/null @@ -1,66 +0,0 @@ -kind: Stack -apiVersion: compose.docker.com/v1beta2 -metadata: - name: helm - generatename: "" - namespace: "" - selflink: "" - uid: "" - resourceversion: "" - generation: 0 - creationtimestamp: "0001-01-01T00:00:00Z" - deletiontimestamp: null - deletiongraceperiodseconds: null - labels: {} - annotations: {} - ownerreferences: [] - initializers: null - finalizers: [] - clustername: "" -spec: - services: - - name: app-watcher - image: {{.Values.watcher.image}} - - name: debug - deploy: - resources: - limits: - memory: {{.Values.memory}} - environment: - foo: {{.Values.bar}} - health_check: - test: - - /ping - - debug - timeout: {{.Values.timeout}} - interval: 2m0s - image: busybox:latest - labels: - foo: {{.Values.bar}} - ports: - - mode: ingress - target: {{.Values.aport}} - protocol: tcp - - mode: ingress - target: {{.Values.sport}} - published: {{.Values.dport}} - protocol: tcp - privileged: {{.Values.privileged}} - read_only: {{.Values.read_only}} - stdin_open: {{.Values.stdin_open}} - tty: {{.Values.tty}} - - name: front - deploy: - replicas: {{.Values.myapp.nginx_replicas}} - environment: - {{.Values.foo}}: {{.Values.bar}} - image: nginx:{{.Values.myapp.nginx_version}} - labels: - {{.Values.foo}}: {{.Values.bar}} - - name: monitor - command: - - monitor - - --source - - '{{.Values.app.name}}-{{.Values.app.version}}' - - $dollar - image: busybox:latest diff --git a/e2e/testdata/helm-expected.chart/values.yaml b/e2e/testdata/helm-expected.chart/values.yaml deleted file mode 100644 index 529b3a512..000000000 --- a/e2e/testdata/helm-expected.chart/values.yaml +++ /dev/null @@ -1,20 +0,0 @@ -aport: 10000 -app: - name: myapp - version: 0.1.0 -bar: barvalue -dport: 12000 -foo: foovalue -memory: "100000000" -myapp: - debug: false - nginx_replicas: 2 - nginx_version: 2 -privileged: false -read_only: true -sport: 11000 -stdin_open: true -timeout: 10s -tty: true -watcher: - image: watcher:latest diff --git a/e2e/testdata/helm.dockerapp/settings.yml b/e2e/testdata/helm.dockerapp/settings.yml deleted file mode 100644 index eb1b89ba8..000000000 --- a/e2e/testdata/helm.dockerapp/settings.yml +++ /dev/null @@ -1,17 +0,0 @@ -privileged: false -myapp: - debug: false - nginx_version: latest - nginx_replicas: 2 -watcher: - image: watcher:latest -read_only: true -stdin_open: true -tty: true -aport: 10000 -sport: 11000 -dport: 12000 -memory: '100000000' -timeout: 10s -bar: barvalue -foo: foovalue diff --git a/e2e/testdata/init-singlefile.dockerapp b/e2e/testdata/init-singlefile.dockerapp index b183340d8..a27f85593 100644 --- a/e2e/testdata/init-singlefile.dockerapp +++ b/e2e/testdata/init-singlefile.dockerapp @@ -6,7 +6,7 @@ name: tac # A short description of the application description: my cool app # Namespace to use when pushing to a registry. This is typically your Hub username. -#namespace: myHubUsername +#namespace: myhubusername # List of application maintainers with name and email for each maintainers: - name: bob @@ -23,6 +23,6 @@ services: command: nginx $NGINX_ARGS --- -# This section contains the default values for your application settings. +# This section contains the default values for your application parameters. NGINX_ARGS: FILL ME NGINX_VERSION: latest diff --git a/e2e/testdata/render/envvariables/my.dockerapp/settings.yml b/e2e/testdata/render/envvariables/my.dockerapp/parameters.yml similarity index 100% rename from e2e/testdata/render/envvariables/my.dockerapp/settings.yml rename to e2e/testdata/render/envvariables/my.dockerapp/parameters.yml diff --git a/e2e/testdata/render/envvariables/settings-0.yml b/e2e/testdata/render/envvariables/parameters-0.yml similarity index 100% rename from e2e/testdata/render/envvariables/settings-0.yml rename to e2e/testdata/render/envvariables/parameters-0.yml diff --git a/e2e/testdata/fork/simple.dockerapp/docker-compose.yml b/e2e/testdata/simple/simple.dockerapp/docker-compose.yml similarity index 100% rename from e2e/testdata/fork/simple.dockerapp/docker-compose.yml rename to e2e/testdata/simple/simple.dockerapp/docker-compose.yml diff --git a/e2e/testdata/fork/simple.dockerapp/metadata.yml b/e2e/testdata/simple/simple.dockerapp/metadata.yml similarity index 100% rename from e2e/testdata/fork/simple.dockerapp/metadata.yml rename to e2e/testdata/simple/simple.dockerapp/metadata.yml diff --git a/e2e/testdata/fork/simple.dockerapp/settings.yml b/e2e/testdata/simple/simple.dockerapp/parameters.yml similarity index 100% rename from e2e/testdata/fork/simple.dockerapp/settings.yml rename to e2e/testdata/simple/simple.dockerapp/parameters.yml diff --git a/e2e/testdata/templates/gotemplate/my.dockerapp/settings.yml b/e2e/testdata/templates/gotemplate/my.dockerapp/parameters.yml similarity index 100% rename from e2e/testdata/templates/gotemplate/my.dockerapp/settings.yml rename to e2e/testdata/templates/gotemplate/my.dockerapp/parameters.yml diff --git a/e2e/testdata/templates/gotemplate/settings-0.yml b/e2e/testdata/templates/gotemplate/parameters-0.yml similarity index 100% rename from e2e/testdata/templates/gotemplate/settings-0.yml rename to e2e/testdata/templates/gotemplate/parameters-0.yml diff --git a/e2e/testdata/templates/mustache/my.dockerapp/settings.yml b/e2e/testdata/templates/mustache/my.dockerapp/parameters.yml similarity index 100% rename from e2e/testdata/templates/mustache/my.dockerapp/settings.yml rename to e2e/testdata/templates/mustache/my.dockerapp/parameters.yml diff --git a/e2e/testdata/templates/mustache/settings-0.yml b/e2e/testdata/templates/mustache/parameters-0.yml similarity index 100% rename from e2e/testdata/templates/mustache/settings-0.yml rename to e2e/testdata/templates/mustache/parameters-0.yml diff --git a/examples/hello-world/README.md b/examples/hello-world/README.md index 308ddfe31..c4aeb76b1 100644 --- a/examples/hello-world/README.md +++ b/examples/hello-world/README.md @@ -4,7 +4,7 @@ In this example, we will create a single service application that deploys a web page displaying a message. -Initialize the single file project using `docker-app init --single-file hello-world`. A single file application contains the three sections, `metadata` which corresponds to `metadata.yml`, `settings` which corresponds to `settings.yml` and `services` which corresponds to `docker-compose.yml`. +Initialize the single file project using `docker-app init --single-file hello-world`. A single file application contains the three sections, `metadata` which corresponds to `metadata.yml`, `parameters` which corresponds to `parameters.yml` and `services` which corresponds to `docker-compose.yml`. ```bash $ ls -l @@ -22,7 +22,7 @@ name: hello-world # A short description of the application description: # Namespace to use when pushing to a registry. This is typically your Hub username. -#namespace: myHubUsername +#namespace: myhubusername # List of application maintainers with name and email for each maintainers: - name: user @@ -34,7 +34,7 @@ version: "3.6" services: {} --- -# This section contains the default values for your application settings. +# This section contains the default values for your application parameters. ``` Open `hello-world.dockerapp` with your favorite text editor. @@ -67,7 +67,7 @@ services: ### Give variables their default value -In the settings section, add every variables with the default value you want, e.g.: +In the parameters section, add every variables with the default value you want, e.g.: --- @@ -75,7 +75,7 @@ In the settings section, add every variables with the default value you want, e. ```yml [...] --- -# This section contains the default values for your application settings. +# This section contains the default values for your application parameters. port: 8080 text: Hello, World! ``` diff --git a/examples/hello-world/hello-world.dockerapp b/examples/hello-world/hello-world.dockerapp index 64a1af044..ccfbbb75c 100644 --- a/examples/hello-world/hello-world.dockerapp +++ b/examples/hello-world/hello-world.dockerapp @@ -6,7 +6,7 @@ name: hello-world # A short description of the application description: "Hello, World!" # Namespace to use when pushing to a registry. This is typically your Hub username. -namespace: myHubUsername +namespace: myhubusername # List of application maintainers with name and email for each maintainers: - name: user @@ -23,6 +23,6 @@ services: - ${port}:5678 --- -# This section contains the default values for your application settings. +# This section contains the default values for your application parameters. port: 8080 text: Hello, World! diff --git a/examples/voting-app/Makefile b/examples/voting-app/Makefile index 9fb794c73..9f5997f93 100644 --- a/examples/voting-app/Makefile +++ b/examples/voting-app/Makefile @@ -1,7 +1,7 @@ # Input. APP_NAME := voting-app APP_FOLDER := $(APP_NAME).dockerapp -SETTINGS_DIR ?= $(APP_FOLDER)/settings +SETTINGS_DIR ?= $(APP_FOLDER)/parameters # Output. DEVELOPMENT_DIR := build/development @@ -24,11 +24,11 @@ cleanup: cleanup/production cleanup/development # render/production: cleanup/production @mkdir -p $(PRODUCTION_DIR) - docker-app render --settings-files $(SETTINGS_DIR)/production.yml > $(PRODUCTION_DIR)/docker-compose.yml + docker-app render --parameters-files $(SETTINGS_DIR)/production.yml > $(PRODUCTION_DIR)/docker-compose.yml render/development: cleanup/development @mkdir -p $(DEVELOPMENT_DIR) - docker-app render --settings-files $(SETTINGS_DIR)/development.yml > $(DEVELOPMENT_DIR)/docker-compose.yml + docker-app render --parameters-files $(SETTINGS_DIR)/development.yml > $(DEVELOPMENT_DIR)/docker-compose.yml render: render/production render/development @@ -47,22 +47,13 @@ stop: stop/production stop/development # Deploy. # deploy/production: render/production stop/production - docker-app deploy --settings-files $(SETTINGS_DIR)/production.yml + docker-app deploy --parameters-files $(SETTINGS_DIR)/production.yml deploy/development: render/development stop/development - docker-app deploy --settings-files $(SETTINGS_DIR)/development.yml + docker-app deploy --parameters-files $(SETTINGS_DIR)/development.yml # # Pack. # pack: docker-app pack -o $(PACK) - -# -# Helm. -# -helm/production: - docker-app helm --settings-files $(SETTINGS_DIR)/production.yml - -helm/development: - docker-app helm --settings-files $(SETTINGS_DIR)/development.yml diff --git a/examples/voting-app/README.md b/examples/voting-app/README.md index 480a47e5f..28dcd2d87 100644 --- a/examples/voting-app/README.md +++ b/examples/voting-app/README.md @@ -101,11 +101,11 @@ Change default replicas, from: ### Give variables their default value -Open `settings.yml` and add every variables with the default value you want, e.g.: +Open `parameters.yml` and add every variables with the default value you want, e.g.: --- -[voting-app.dockerapp/settings.yml](voting-app.dockerapp/settings.yml): +[voting-app.dockerapp/parameters.yml](voting-app.dockerapp/parameters.yml): ```yml # Vote. vote: @@ -140,13 +140,13 @@ worker: Test your application by running `docker-app render`. -### Add settings for production and development environments +### Add parameters for production and development environments -Create `settings/development.yml` and `settings/production.yml` and add your target-specific variables. +Create `parameters/development.yml` and `parameters/production.yml` and add your target-specific variables. --- -[voting-app.dockerapp/settings/development.yml](voting-app.dockerapp/settings/development.yml): +[voting-app.dockerapp/parameters/development.yml](voting-app.dockerapp/parameters/development.yml): ```yml # Vote. vote: @@ -160,7 +160,7 @@ result: ``` --- -[voting-app.dockerapp/settings/production.yml](voting-app.dockerapp/settings/production.yml): +[voting-app.dockerapp/parameters/production.yml](voting-app.dockerapp/parameters/production.yml): ```yml # Vote. vote: diff --git a/examples/voting-app/voting-app.dockerapp/metadata.yml b/examples/voting-app/voting-app.dockerapp/metadata.yml index 9b40ef0de..120300fcc 100644 --- a/examples/voting-app/voting-app.dockerapp/metadata.yml +++ b/examples/voting-app/voting-app.dockerapp/metadata.yml @@ -5,7 +5,7 @@ name: voting-app # A short description of the application description: "Dogs or cats?" # Namespace to use when pushing to a registry. This is typically your Hub username. -namespace: myHubUsername +namespace: myhubusername # List of application maintainers with name and email for each maintainers: - name: user diff --git a/examples/voting-app/voting-app.dockerapp/settings.yml b/examples/voting-app/voting-app.dockerapp/parameters.yml similarity index 100% rename from examples/voting-app/voting-app.dockerapp/settings.yml rename to examples/voting-app/voting-app.dockerapp/parameters.yml diff --git a/examples/voting-app/voting-app.dockerapp/settings/development.yml b/examples/voting-app/voting-app.dockerapp/settings/development.yml deleted file mode 100644 index e24597fe1..000000000 --- a/examples/voting-app/voting-app.dockerapp/settings/development.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Vote. -vote: - image: - # Use your own image. - name: vote - -# Result. -result: - image: - # Use your own image. - name: result diff --git a/examples/voting-app/voting-app.dockerapp/settings/production.yml b/examples/voting-app/voting-app.dockerapp/settings/production.yml deleted file mode 100644 index f82892edd..000000000 --- a/examples/voting-app/voting-app.dockerapp/settings/production.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Vote. -vote: - port: 80 - replicas: 3 - -# Result. -result: - port: 80 - replicas: 5 diff --git a/examples/wordpress/README.md b/examples/wordpress/README.md index a1165c2d2..71cc8be94 100644 --- a/examples/wordpress/README.md +++ b/examples/wordpress/README.md @@ -51,10 +51,10 @@ volumes: name: db_data ``` -**Override default settings with file**. This example sets `debug` to `"false"` and the wordpress service published port to 80 as defined in `prod-settings.yml`. +**Override default parameters with file**. This example sets `debug` to `"false"` and the wordpress service published port to 80 as defined in `prod-parameters.yml`. ```yaml -# docker-app render wordpress --settings-files prod-settings.yml +# docker-app render wordpress --parameters-files prod-parameters.yml version: "3.6" [...] environment: @@ -121,32 +121,6 @@ wordpress.scale.mode replicated wordpress.scale.replicas 1 ``` -### Generate helm package - -`docker-app helm wordpress` will output a Helm package in the `./wordpress.helm` folder. `--compose-file` (or `-c`), `--set` (or `-e`) and `--settings-files` (or `-f`) flags apply the same way they do for the `render` subcommand. - -``` -$ docker-app helm wordpress --settings-files prod-settings.yml --set mysql.user.name=mollydock -$ tree wordpress.chart -wordpress.chart -├── Chart.yaml -├── templates -│   └── stack.yaml -└── values.yaml - -1 directory, 3 files -$ cat wordpress.chart/templates/stack.yaml -apiversion: v1beta2 -kind: stacks.compose.docker.com -metadata: - annotations: {} -[...] -spec: - services: - - deploy: - [...] -``` - ### Generate distributable app package **Note:** If using Windows, this only works in Linux container mode. diff --git a/examples/wordpress/prod-settings.yml b/examples/wordpress/prod-parameters.yml similarity index 100% rename from examples/wordpress/prod-settings.yml rename to examples/wordpress/prod-parameters.yml diff --git a/examples/wordpress/wordpress.dockerapp/docker-compose.yml b/examples/wordpress/wordpress.dockerapp/docker-compose.yml index b177283bd..c02823d1e 100644 --- a/examples/wordpress/wordpress.dockerapp/docker-compose.yml +++ b/examples/wordpress/wordpress.dockerapp/docker-compose.yml @@ -10,7 +10,7 @@ services: MYSQL_USER: ${mysql.user.name} MYSQL_PASSWORD: ${mysql.user.password} volumes: - - source: db_data + - source: db-data target: /var/lib/mysql type: volume networks: @@ -40,7 +40,7 @@ services: - mysql volumes: - db_data: + db-data: name: ${volumes.db_data.name} networks: diff --git a/examples/wordpress/wordpress.dockerapp/settings.yml b/examples/wordpress/wordpress.dockerapp/settings.yml deleted file mode 100644 index 496923396..000000000 --- a/examples/wordpress/wordpress.dockerapp/settings.yml +++ /dev/null @@ -1,22 +0,0 @@ -debug: "true" -mysql: - image: - version: 5.6 - rootpass: axx[<^cz3d.fPb - database: wordpressdata - user: - name: wordpress - password: wordpress - scale: - endpoint_mode: dnsrr - mode: replicated - replicas: 1 -wordpress: - scale: - mode: replicated - replicas: 1 - endpoint_mode: vip - port: 8080 -volumes: - db_data: - name: db_data diff --git a/examples_test.go b/examples_test.go index a42b62f95..2b904e8a5 100644 --- a/examples_test.go +++ b/examples_test.go @@ -20,7 +20,7 @@ func Example() { if err != nil { panic("cannot load application") } - // Render the app to a composefile format, using some user provided settings + // Render the app to a composefile format, using some user provided parameters c, err := render.Render(app, map[string]string{ "text": "hello examples!", }) diff --git a/integrations/gradle/example/simple.dockerapp/settings.yml b/integrations/gradle/example/simple.dockerapp/settings.yml deleted file mode 100644 index 999a402b5..000000000 --- a/integrations/gradle/example/simple.dockerapp/settings.yml +++ /dev/null @@ -1,16 +0,0 @@ -debug: true -mysql: - image: - version: 8 - rootpass: axx[<^cz3d.fPb - database: wordpressdata - user: - name: wordpress - password: wordpress -wordpress: - scale: - mode: global - replicas: 0 -volumes: - db_data: - name: db_data diff --git a/integrations/intellij/README.md b/integrations/intellij/README.md index be90932b4..2cb8a37b0 100644 --- a/integrations/intellij/README.md +++ b/integrations/intellij/README.md @@ -34,9 +34,9 @@ By default all operations will look for a single Docker Application at the root `Render` renders the application in a popup window. -## Settings +## Parameters -`Settings` pops-up a dialog that can be used to configure deployment parameters, such as which orchestrator to use, the stack name and namespace, and settings overrides. +`Parameters` pops-up a dialog that can be used to configure deployment parameters, such as which orchestrator to use, the stack name and namespace, and parameters overrides. ## Deploy diff --git a/integrations/intellij/src/main/java/DeployApp.java b/integrations/intellij/src/main/java/DeployApp.java index 9f2027801..8c8f2fa4b 100644 --- a/integrations/intellij/src/main/java/DeployApp.java +++ b/integrations/intellij/src/main/java/DeployApp.java @@ -28,12 +28,12 @@ public void actionPerformed(AnActionEvent event) { String orchestrator = "swarm"; if (pc.getValue("docker_app_orchestrator").equals("kubernetes")) orchestrator = "kubernetes"; - String rawSettings = pc.getValue("docker_app_overrides"); - String settings = ""; - if (!rawSettings.isEmpty()) { - String[] split = rawSettings.split("\n"); + String rawParameters = pc.getValue("docker_app_overrides"); + String parameters = ""; + if (!rawParameters.isEmpty()) { + String[] split = rawParameters.split("\n"); for (String l: split) { - settings += " -s " + l; + parameters += " -s " + l; } } String kubeconfig = pc.getValue("docker_app_kubeconfig"); @@ -53,7 +53,7 @@ public void actionPerformed(AnActionEvent event) { + kubeconfig + namespace + name - + settings; + + parameters; Process p = Runtime.getRuntime().exec(cmd,null, new File(project.getBasePath())); BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream())); String line; diff --git a/integrations/intellij/src/main/java/Settings.java b/integrations/intellij/src/main/java/Parameters.java similarity index 70% rename from integrations/intellij/src/main/java/Settings.java rename to integrations/intellij/src/main/java/Parameters.java index ddba24594..87c043cf3 100644 --- a/integrations/intellij/src/main/java/Settings.java +++ b/integrations/intellij/src/main/java/Parameters.java @@ -3,11 +3,11 @@ import com.intellij.openapi.ui.*; import com.intellij.openapi.ui.popup.*; -public class Settings extends AnAction { - public Settings() { super("Settings"); } +public class Parameters extends AnAction { + public Parameters() { super("Parameters"); } public void actionPerformed(AnActionEvent event) { - SettingsDialog sf = new SettingsDialog(); + ParametersDialog sf = new ParametersDialog(); sf.pack(); sf.load(event.getProject()); sf.setVisible(true); diff --git a/integrations/intellij/src/main/java/SettingsDialog.form b/integrations/intellij/src/main/java/ParametersDialog.form similarity index 98% rename from integrations/intellij/src/main/java/SettingsDialog.form rename to integrations/intellij/src/main/java/ParametersDialog.form index 7786418b1..4b3acd472 100644 --- a/integrations/intellij/src/main/java/SettingsDialog.form +++ b/integrations/intellij/src/main/java/ParametersDialog.form @@ -1,5 +1,5 @@ -
+ @@ -71,7 +71,7 @@ - + diff --git a/integrations/intellij/src/main/java/SettingsDialog.java b/integrations/intellij/src/main/java/ParametersDialog.java similarity index 97% rename from integrations/intellij/src/main/java/SettingsDialog.java rename to integrations/intellij/src/main/java/ParametersDialog.java index ed34fa850..14a1dac2c 100644 --- a/integrations/intellij/src/main/java/SettingsDialog.java +++ b/integrations/intellij/src/main/java/ParametersDialog.java @@ -3,7 +3,7 @@ import com.intellij.openapi.project.Project; import com.intellij.ide.util.PropertiesComponent; -public class SettingsDialog extends JDialog { +public class ParametersDialog extends JDialog { private JPanel contentPane; private JButton buttonOK; private JButton buttonCancel; @@ -15,7 +15,7 @@ public class SettingsDialog extends JDialog { private JTextField tStackName; private boolean validated; - public SettingsDialog() { + public ParametersDialog() { setContentPane(contentPane); setModal(true); getRootPane().setDefaultButton(buttonOK); diff --git a/integrations/intellij/src/main/java/RenderApp.java b/integrations/intellij/src/main/java/RenderApp.java index 4826b1175..1bc428d71 100644 --- a/integrations/intellij/src/main/java/RenderApp.java +++ b/integrations/intellij/src/main/java/RenderApp.java @@ -17,15 +17,15 @@ public void actionPerformed(AnActionEvent event) { PropertiesComponent pc = PropertiesComponent.getInstance(project); String appPath = pc.getValue("docker_app_path"); try { - String rawSettings = pc.getValue("docker_app_overrides"); - String settings = ""; - if (!rawSettings.isEmpty()) { - String[] split = rawSettings.split("\n"); + String rawParameters = pc.getValue("docker_app_overrides"); + String parameters = ""; + if (!rawParameters.isEmpty()) { + String[] split = rawParameters.split("\n"); for (String l: split) { - settings += " -s " + l; + parameters += " -s " + l; } } - Process p = Runtime.getRuntime().exec("docker-app render " + appPath + settings, null, new File(project.getBasePath())); + Process p = Runtime.getRuntime().exec("docker-app render " + appPath + parameters, null, new File(project.getBasePath())); Scanner se = new Scanner(p.getErrorStream()).useDelimiter("\\A"); String stderr = se.hasNext() ? se.next() : ""; Scanner so = new Scanner(p.getInputStream()).useDelimiter("\\A"); diff --git a/integrations/intellij/src/main/resources/META-INF/plugin.xml b/integrations/intellij/src/main/resources/META-INF/plugin.xml index 6efee8b77..fd72e07cb 100644 --- a/integrations/intellij/src/main/resources/META-INF/plugin.xml +++ b/integrations/intellij/src/main/resources/META-INF/plugin.xml @@ -23,7 +23,7 @@ - + diff --git a/integrations/visualstudio/README.md b/integrations/visualstudio/README.md index 5664d11e6..9ea0855f5 100644 --- a/integrations/visualstudio/README.md +++ b/integrations/visualstudio/README.md @@ -37,9 +37,9 @@ By default all operations will look for a single Docker Application at the root `Render` simply renders the application in a popup window. -## Settings +## Parameters -`Settings` pops-up a dialog that can be used to configure deployment parameters, such as which orchestrator to use, the stack name and namespace, and settings overrides. +`Parameters` pops-up a dialog that can be used to configure deployment parameters, such as which orchestrator to use, the stack name and namespace, and parameters overrides. ## Deploy diff --git a/integrations/visualstudio/dockerappvsix/AppPackageSettings.cs b/integrations/visualstudio/dockerappvsix/AppPackageParameters.cs similarity index 87% rename from integrations/visualstudio/dockerappvsix/AppPackageSettings.cs rename to integrations/visualstudio/dockerappvsix/AppPackageParameters.cs index abf3e8fc7..7315d13fe 100644 --- a/integrations/visualstudio/dockerappvsix/AppPackageSettings.cs +++ b/integrations/visualstudio/dockerappvsix/AppPackageParameters.cs @@ -9,14 +9,14 @@ namespace dockerappvsix { - public class AppPackageSettings : INotifyPropertyChanged + public class AppPackageParameters : INotifyPropertyChanged { private bool _isSwarm; private bool _isKubernetes; private string _kubeConfig; private string _namespace; private string _stackName; - private string _settings; + private string _parameters; public bool IsSwarm { @@ -63,12 +63,12 @@ public string StackName OnPropertyChanged(); } } - public string Settings + public string Parameters { - get => _settings; + get => _parameters; set { - _settings = value; + _parameters = value; OnPropertyChanged(); } } @@ -81,7 +81,7 @@ public void LoadFromSolution(Globals g) KubeConfig = g.GetOrNull("dockerapp_kubeconfig"); Namespace = g.GetOrNull("dockerapp_namespace"); StackName = g.GetOrNull("dockerapp_stackname"); - Settings = g.GetOrNull("dockerapp_settings"); + Parameters = g.GetOrNull("dockerapp_parameters"); } public void Save(Globals g) @@ -95,8 +95,8 @@ public void Save(Globals g) g.VariablePersists["dockerapp_namespace"] = true; g["dockerapp_stackname"] = StackName; g.VariablePersists["dockerapp_stackname"] = true; - g["dockerapp_settings"] = Settings; - g.VariablePersists["dockerapp_settings"] = true; + g["dockerapp_parameters"] = Parameters; + g.VariablePersists["dockerapp_parameters"] = true; } private void OnPropertyChanged([CallerMemberName]string name = null) diff --git a/integrations/visualstudio/dockerappvsix/CommandDeploy.cs b/integrations/visualstudio/dockerappvsix/CommandDeploy.cs index 05f9769f7..9012ca412 100755 --- a/integrations/visualstudio/dockerappvsix/CommandDeploy.cs +++ b/integrations/visualstudio/dockerappvsix/CommandDeploy.cs @@ -76,11 +76,11 @@ private async void ExecuteAsync(object sender, EventArgs e) AddArgIfExists(g, "dockerapp_stackname", "--name", argsBuilder); AddArgIfExists(g, "dockerapp_namespace", "--namespace", argsBuilder); AddArgIfExists(g, "dockerapp_kubeconfig", "--kubeconfig", argsBuilder); - var settings = g.GetOrNull("dockerapp_settings"); + var parameters = g.GetOrNull("dockerapp_parameters"); - if (settings !=null) + if (parameters !=null) { - foreach (string s in (settings).Split('\n')) { + foreach (string s in (parameters).Split('\n')) { argsBuilder.Append($" -s {s}"); } } diff --git a/integrations/visualstudio/dockerappvsix/CommandNew.cs b/integrations/visualstudio/dockerappvsix/CommandNew.cs index 088c8dc97..773eebf8b 100755 --- a/integrations/visualstudio/dockerappvsix/CommandNew.cs +++ b/integrations/visualstudio/dockerappvsix/CommandNew.cs @@ -63,7 +63,7 @@ private async void ExecuteAsync(object sender, EventArgs e) if (!(ns.ShowDialog() ?? false)) { return; } - var s = ns.Settings; + var s = ns.Parameters; var argsBuilder = new StringBuilder("init " + s.Name.QuoteAndStripCarriageReturns()); if (s.Description != "") argsBuilder.Append($" --description {s.Description.QuoteAndStripCarriageReturns()}"); diff --git a/integrations/visualstudio/dockerappvsix/CommandSettings.cs b/integrations/visualstudio/dockerappvsix/CommandParameters.cs similarity index 80% rename from integrations/visualstudio/dockerappvsix/CommandSettings.cs rename to integrations/visualstudio/dockerappvsix/CommandParameters.cs index c6b7c27fb..ee7b19bf8 100755 --- a/integrations/visualstudio/dockerappvsix/CommandSettings.cs +++ b/integrations/visualstudio/dockerappvsix/CommandParameters.cs @@ -14,7 +14,7 @@ namespace dockerappvsix { - internal sealed class CommandSettings + internal sealed class CommandParameters { public const int CommandId = 4130; @@ -25,7 +25,7 @@ internal sealed class CommandSettings private readonly AsyncPackage _package; - private CommandSettings(AsyncPackage package, OleMenuCommandService commandService) + private CommandParameters(AsyncPackage package, OleMenuCommandService commandService) { _package = package ?? throw new ArgumentNullException(nameof(package)); commandService = commandService ?? throw new ArgumentNullException(nameof(commandService)); @@ -35,7 +35,7 @@ private CommandSettings(AsyncPackage package, OleMenuCommandService commandServi commandService.AddCommand(menuItem); } - public static CommandSettings Instance + public static CommandParameters Instance { get; private set; @@ -51,12 +51,12 @@ private Microsoft.VisualStudio.Shell.IAsyncServiceProvider ServiceProvider public static async Task InitializeAsync(AsyncPackage package) { - // Verify the current thread is the UI thread - the call to AddCommand in CommandSettings's constructor requires + // Verify the current thread is the UI thread - the call to AddCommand in CommandParameters's constructor requires // the UI thread. ThreadHelper.ThrowIfNotOnUIThread(); OleMenuCommandService commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as OleMenuCommandService; - Instance = new CommandSettings(package, commandService); + Instance = new CommandParameters(package, commandService); } private async void ExecuteAsync(object sender, EventArgs e) @@ -64,10 +64,10 @@ private async void ExecuteAsync(object sender, EventArgs e) ThreadHelper.ThrowIfNotOnUIThread(); DTE dte = await this._package.GetServiceAsync(typeof(DTE)) as DTE; Globals g = dte.Solution.Globals; - SettingsDialog sd = new SettingsDialog(); - sd.Settings.LoadFromSolution(g); + ParametersDialog sd = new ParametersDialog(); + sd.Parameters.LoadFromSolution(g); if (sd.ShowDialog() ?? false) { - sd.Settings.Save(g); + sd.Parameters.Save(g); } } } diff --git a/integrations/visualstudio/dockerappvsix/CommandRender.cs b/integrations/visualstudio/dockerappvsix/CommandRender.cs index eb6f093d0..87c99da14 100644 --- a/integrations/visualstudio/dockerappvsix/CommandRender.cs +++ b/integrations/visualstudio/dockerappvsix/CommandRender.cs @@ -7,7 +7,7 @@ using System.Threading; using System.Threading.Tasks; using EnvDTE; -using Microsoft.VisualStudio.Settings; +using Microsoft.VisualStudio.Parameters; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Task = System.Threading.Tasks.Task; diff --git a/integrations/visualstudio/dockerappvsix/CommandSelectApp.cs b/integrations/visualstudio/dockerappvsix/CommandSelectApp.cs index 6144431d0..0a4ca947b 100755 --- a/integrations/visualstudio/dockerappvsix/CommandSelectApp.cs +++ b/integrations/visualstudio/dockerappvsix/CommandSelectApp.cs @@ -66,7 +66,7 @@ private async void ExecuteAsync(object sender, EventArgs e) { string app = file.FileName; string f = Path.GetFileName(app); - if (f == "docker-compose.yml" || f == "settings.yml" || f == "metadata.yml") + if (f == "docker-compose.yml" || f == "parameters.yml" || f == "metadata.yml") app = Path.GetDirectoryName(app); message = "Docker Application set to " + app; g["dockerapp_applocation"] = app; diff --git a/integrations/visualstudio/dockerappvsix/DockerAppPackage.cs b/integrations/visualstudio/dockerappvsix/DockerAppPackage.cs index 1e6a6d52a..c25544ea9 100644 --- a/integrations/visualstudio/dockerappvsix/DockerAppPackage.cs +++ b/integrations/visualstudio/dockerappvsix/DockerAppPackage.cs @@ -71,7 +71,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); await CommandRender.InitializeAsync(this); await CommandSelectApp.InitializeAsync(this); - await CommandSettings.InitializeAsync(this); + await CommandParameters.InitializeAsync(this); await CommandDeploy.InitializeAsync(this); await CommandNew.InitializeAsync(this); } diff --git a/integrations/visualstudio/dockerappvsix/DockerAppPackage.vsct b/integrations/visualstudio/dockerappvsix/DockerAppPackage.vsct index 17fab8052..658b16c7f 100644 --- a/integrations/visualstudio/dockerappvsix/DockerAppPackage.vsct +++ b/integrations/visualstudio/dockerappvsix/DockerAppPackage.vsct @@ -80,13 +80,13 @@ Select Application... -