From d7ce60eb09728a525297180bd3975907aaeb4440 Mon Sep 17 00:00:00 2001 From: Efe Karakus Date: Mon, 21 Sep 2020 16:46:11 -0700 Subject: [PATCH] fix(cli): --app flag can now be overridden by users (#1399) Resolves #1329 Removes GlobalOpts struct and the viper lib dependency from the CLI. Previously, if we ran "env init --app other" the overridden value "other" was ignored and only the workspace's app was used. This is an issue with viper as was found in #1329. Instead, with this change we get rid of Global Flags and force commands themselves to write whether they need an --app flag or not. _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- go.mod | 6 +- go.sum | 141 +---------------- internal/pkg/cli/app.go | 8 +- internal/pkg/cli/app_delete.go | 55 +++---- internal/pkg/cli/app_delete_test.go | 14 +- internal/pkg/cli/app_init.go | 68 ++++---- internal/pkg/cli/app_init_test.go | 14 +- internal/pkg/cli/app_list.go | 4 +- internal/pkg/cli/app_show.go | 46 +++--- internal/pkg/cli/app_show_test.go | 18 +-- internal/pkg/cli/cli.go | 51 +----- internal/pkg/cli/deploy.go | 2 +- internal/pkg/cli/env.go | 13 +- internal/pkg/cli/env_delete.go | 77 ++++----- internal/pkg/cli/env_delete_test.go | 32 ++-- internal/pkg/cli/env_init.go | 192 ++++++++++++----------- internal/pkg/cli/env_init_test.go | 42 +++-- internal/pkg/cli/env_list.go | 33 ++-- internal/pkg/cli/env_list_test.go | 28 +--- internal/pkg/cli/env_show.go | 45 +++--- internal/pkg/cli/env_show_test.go | 18 +-- internal/pkg/cli/env_upgrade.go | 19 +-- internal/pkg/cli/init.go | 58 ++++--- internal/pkg/cli/job.go | 8 +- internal/pkg/cli/job_deploy.go | 44 +++--- internal/pkg/cli/job_deploy_test.go | 8 +- internal/pkg/cli/job_init.go | 87 +++++----- internal/pkg/cli/job_init_test.go | 33 ++-- internal/pkg/cli/pipeline.go | 10 +- internal/pkg/cli/pipeline_delete.go | 36 +++-- internal/pkg/cli/pipeline_delete_test.go | 20 +-- internal/pkg/cli/pipeline_init.go | 88 ++++++----- internal/pkg/cli/pipeline_init_test.go | 41 +++-- internal/pkg/cli/pipeline_show.go | 32 ++-- internal/pkg/cli/pipeline_show_test.go | 12 +- internal/pkg/cli/pipeline_status.go | 32 ++-- internal/pkg/cli/pipeline_status_test.go | 12 +- internal/pkg/cli/pipeline_update.go | 55 +++---- internal/pkg/cli/pipeline_update_test.go | 10 +- internal/pkg/cli/storage.go | 2 +- internal/pkg/cli/storage_init.go | 26 +-- internal/pkg/cli/storage_init_test.go | 32 +--- internal/pkg/cli/svc.go | 22 ++- internal/pkg/cli/svc_delete.go | 92 +++++------ internal/pkg/cli/svc_delete_test.go | 32 ++-- internal/pkg/cli/svc_deploy.go | 110 ++++++------- internal/pkg/cli/svc_deploy_test.go | 28 ++-- internal/pkg/cli/svc_init.go | 125 +++++++-------- internal/pkg/cli/svc_init_test.go | 48 +++--- internal/pkg/cli/svc_list.go | 33 ++-- internal/pkg/cli/svc_list_test.go | 30 ++-- internal/pkg/cli/svc_logs.go | 29 ++-- internal/pkg/cli/svc_logs_test.go | 9 +- internal/pkg/cli/svc_package.go | 93 +++++------ internal/pkg/cli/svc_package_test.go | 34 ++-- internal/pkg/cli/svc_show.go | 37 +++-- internal/pkg/cli/svc_show_test.go | 18 +-- internal/pkg/cli/svc_status.go | 33 ++-- internal/pkg/cli/svc_status_test.go | 12 +- internal/pkg/cli/task_run.go | 23 ++- internal/pkg/cli/task_run_test.go | 14 +- 61 files changed, 1049 insertions(+), 1345 deletions(-) diff --git a/go.mod b/go.mod index 34dffd5bb77..12600ebc828 100644 --- a/go.mod +++ b/go.mod @@ -20,19 +20,15 @@ require ( github.com/karrick/godirwalk v1.15.6 // indirect github.com/lnquy/cron v1.0.1 github.com/mattn/go-colorable v0.1.6 // indirect - github.com/mitchellh/mapstructure v1.3.0 // indirect github.com/moby/buildkit v0.7.2 github.com/onsi/ginkgo v1.14.1 github.com/onsi/gomega v1.10.2 - github.com/pelletier/go-toml v1.8.0 // indirect github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.6.0 // indirect + github.com/smartystreets/goconvey v1.6.4 // indirect github.com/spf13/afero v1.4.0 - github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v1.0.0 - github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 github.com/xlab/treeprint v1.0.0 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 // indirect diff --git a/go.sum b/go.sum index a74b4a659fe..1638ae7ef6d 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,6 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AkihiroSuda/containerd-fuse-overlayfs v0.0.0-20200220082720-bb896865146c/go.mod h1:K4kx7xAA5JimeQCnN+dbeLlfaBxzZLaLiDD8lusFI8w= github.com/AlecAivazis/survey/v2 v2.1.1 h1:LEMbHE0pLj75faaVEKClEX1TM4AJmmnOh9eimREzLWI= github.com/AlecAivazis/survey/v2 v2.1.1/go.mod h1:9FJRdMdDm8rnT+zHVbvQT2RTSTLq0Ttd6q3Vl2fahjk= @@ -19,7 +8,6 @@ github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= @@ -35,13 +23,8 @@ github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:H github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/apache/thrift v0.0.0-20161221203622-b2a4d4ae21c7/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/aws/aws-sdk-go v1.34.22 h1:7V2sKilVVgHqdjbW+O/xaVWYfnmuLwZdF/+6JuUh6Cw= -github.com/aws/aws-sdk-go v1.34.22/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.34.27 h1:qBqccUrlz43Zermh0U1O502bHYZsgMlBm+LUVabzBPA= github.com/aws/aws-sdk-go v1.34.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/awslabs/goformation/v4 v4.15.0 h1:yZM85dzEKrRIPpMZIdsUV+EWbvhWsfoqC81Fv/aFPck= @@ -49,9 +32,7 @@ github.com/awslabs/goformation/v4 v4.15.0/go.mod h1:GcJULxCJfloT+3pbqCluXftdEK2A github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/briandowns/spinner v1.11.1 h1:OixPqDEcX3juo5AjQZAnFPbeUA0jvkp2qzB5gOZJ/L0= @@ -88,9 +69,7 @@ github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737/go.mod h1:TB1hU github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -133,7 +112,6 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -144,6 +122,7 @@ github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gq github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= github.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY= github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -157,8 +136,6 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -172,58 +149,35 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/uuid v0.0.0-20160311170451-ebb0a03e909c/go.mod h1:fHzc09UnyJyqyW+bFuq864eh+wC7dj65aXmXLRe5to0= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= github.com/hinshun/vt10x v0.0.0-20180809195222-d55458df857c h1:kp3AxgXgDOmIJFR7bIwqFhwJ2qWar8tEQSE5XXhCfVk= @@ -242,8 +196,6 @@ github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2yCeUc= github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -271,8 +223,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lnquy/cron v1.0.1 h1:yYcu8TBex8/Ew7FTtugYe4+canCL6HX0HhK6tq+nGM4= github.com/lnquy/cron v1.0.1/go.mod h1:hu2Y7H68/8oKk6T4+K4qdbopbnaP4rGltK3ylWiiDss= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= @@ -280,12 +230,10 @@ github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2 github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -293,23 +241,12 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.0 h1:iDwIio/3gk2QtLLEsqU5lInaMzos0hDTz8a6lazSFVw= -github.com/mitchellh/mapstructure v1.3.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/buildkit v0.7.2 h1:wp4R0QMXSqwjTJKhhWlJNOCSQ/OVPnsCf3N8rs09+vQ= github.com/moby/buildkit v0.7.2/go.mod h1:D3DN/Nl4DyMH1LkwpRUJuoghqdigdXd1A6HXt5aZS40= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= @@ -347,10 +284,7 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo github.com/opencontainers/selinux v1.3.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opentracing-contrib/go-stdlib v0.0.0-20171029140428-b1a47cfbdd75/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w= github.com/opentracing/opentracing-go v0.0.0-20171003133519-1361b9cd60be/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw= -github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -360,7 +294,6 @@ github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -380,17 +313,14 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b h1:jUK33OXuZP/l6babJtnLo1qsGvq6G9so9KMflGAm4YA= github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b/go.mod h1:8458kAagoME2+LN5//WxE71ysZ3B7r22fdgb7qVmXSY= github.com/sanathkr/yaml v0.0.0-20170819201035-0056894fa522 h1:fOCp11H0yuyAt2wqlbJtbyPzSgaxHTv8uN1pMpkG1t8= github.com/sanathkr/yaml v0.0.0-20170819201035-0056894fa522/go.mod h1:tQTYKOQgxoH3v6dEmdHiz4JG+nbxWwM5fgPQUpSZqVQ= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.3/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= @@ -408,28 +338,20 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.5 h1:AWZ/w4lcfxuh52NVL78p9Eh8j6r1mCTEGSRFBJyIHAE= -github.com/spf13/afero v1.3.5/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.4.0 h1:jsLTaI1zwYO3vjrzHalkVcIHXTNmdQFepW4OI8H3+x8= github.com/spf13/afero v1.4.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -441,8 +363,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -472,18 +392,14 @@ github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPS github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -492,39 +408,21 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -532,7 +430,6 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -541,16 +438,13 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -559,8 +453,6 @@ golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -576,11 +468,9 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -589,20 +479,10 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE= golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= @@ -611,33 +491,19 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200227132054-3f1135a288c9/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -659,7 +525,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.61.0 h1:LBCdW4FmFYL4s/vDZD1RQYX7oAR6IjujCYgMdbHBR10= gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= @@ -680,9 +545,5 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/pkg/cli/app.go b/internal/pkg/cli/app.go index 82c6e576311..0954ce3359f 100644 --- a/internal/pkg/cli/app.go +++ b/internal/pkg/cli/app.go @@ -20,10 +20,10 @@ Applications are a collection of services and environments.`, Applications are a collection of services and environments.`, } - cmd.AddCommand(BuildAppInitCommand()) - cmd.AddCommand(BuildAppListCommand()) - cmd.AddCommand(BuildAppShowCmd()) - cmd.AddCommand(BuildAppDeleteCommand()) + cmd.AddCommand(buildAppInitCommand()) + cmd.AddCommand(buildAppListCommand()) + cmd.AddCommand(buildAppShowCmd()) + cmd.AddCommand(buildAppDeleteCommand()) cmd.SetUsageTemplate(template.Usage) cmd.Annotations = map[string]string{ diff --git a/internal/pkg/cli/app_delete.go b/internal/pkg/cli/app_delete.go index f2959f2b216..17206dbce0c 100644 --- a/internal/pkg/cli/app_delete.go +++ b/internal/pkg/cli/app_delete.go @@ -42,9 +42,9 @@ var ( ) type deleteAppVars struct { + name string skipConfirmation bool envProfiles map[string]string - *GlobalOpts } type deleteAppOpts struct { @@ -55,6 +55,7 @@ type deleteAppOpts struct { ws wsFileDeleter sessProvider sessionProvider cfn deployer + prompt prompter s3 func(session *session.Session) bucketEmptier executor func(svcName string) (executor, error) askExecutor func(envName, envProfile string) (askExecutor, error) @@ -85,14 +86,15 @@ func newDeleteAppOpts(vars deleteAppVars) (*deleteAppOpts, error) { ws: ws, sessProvider: provider, cfn: cloudformation.New(defaultSession), + prompt: prompt.New(), s3: func(session *session.Session) bucketEmptier { return s3.New(session) }, executor: func(svcName string) (executor, error) { opts, err := newDeleteSvcOpts(deleteSvcVars{ - SkipConfirmation: true, // always skip sub-confirmations - GlobalOpts: NewGlobalOpts(), - Name: svcName, + skipConfirmation: true, // always skip sub-confirmations + name: svcName, + appName: vars.name, }) if err != nil { return nil, err @@ -101,10 +103,10 @@ func newDeleteAppOpts(vars deleteAppVars) (*deleteAppOpts, error) { }, askExecutor: func(envName, envProfile string) (askExecutor, error) { opts, err := newDeleteEnvOpts(deleteEnvVars{ - SkipConfirmation: true, - GlobalOpts: NewGlobalOpts(), - EnvName: envName, - EnvProfile: envProfile, + skipConfirmation: true, + appName: vars.name, + name: envName, + profile: envProfile, }) if err != nil { return nil, err @@ -113,9 +115,9 @@ func newDeleteAppOpts(vars deleteAppVars) (*deleteAppOpts, error) { }, deletePipelineRunner: func() (deletePipelineRunner, error) { opts, err := newDeletePipelineOpts(deletePipelineVars{ - GlobalOpts: NewGlobalOpts(), - SkipConfirmation: true, - DeleteSecret: true, + appName: vars.name, + skipConfirmation: true, + shouldDeleteSecret: true, }) if err != nil { return nil, err @@ -127,7 +129,7 @@ func newDeleteAppOpts(vars deleteAppVars) (*deleteAppOpts, error) { // Validate returns an error if the user's input is invalid. func (o *deleteAppOpts) Validate() error { - if o.AppName() == "" { + if o.name == "" { return errNoAppInWorkspace } return nil @@ -140,7 +142,7 @@ func (o *deleteAppOpts) Ask() error { } manualConfirm, err := o.prompt.Confirm( - fmt.Sprintf(fmtDeleteAppConfirmPrompt, o.AppName()), + fmt.Sprintf(fmtDeleteAppConfirmPrompt, o.name), deleteAppConfirmHelp, prompt.WithTrueDefault()) if err != nil { @@ -192,9 +194,9 @@ func (o *deleteAppOpts) Execute() error { } func (o *deleteAppOpts) deleteSvcs() error { - svcs, err := o.store.ListServices(o.AppName()) + svcs, err := o.store.ListServices(o.name) if err != nil { - return fmt.Errorf("list services for application %s: %w", o.AppName(), err) + return fmt.Errorf("list services for application %s: %w", o.name, err) } for _, svc := range svcs { @@ -210,9 +212,9 @@ func (o *deleteAppOpts) deleteSvcs() error { } func (o *deleteAppOpts) deleteEnvs() error { - envs, err := o.store.ListEnvironments(o.AppName()) + envs, err := o.store.ListEnvironments(o.name) if err != nil { - return fmt.Errorf("list environments for application %s: %w", o.AppName(), err) + return fmt.Errorf("list environments for application %s: %w", o.name, err) } for _, env := range envs { @@ -236,9 +238,9 @@ func (o *deleteAppOpts) deleteEnvs() error { } func (o *deleteAppOpts) emptyS3Bucket() error { - app, err := o.store.GetApplication(o.AppName()) + app, err := o.store.GetApplication(o.name) if err != nil { - return fmt.Errorf("get application %s: %w", o.AppName(), err) + return fmt.Errorf("get application %s: %w", o.name, err) } appResources, err := o.cfn.GetRegionalAppResources(app) if err != nil { @@ -272,7 +274,7 @@ func (o *deleteAppOpts) deletePipeline() error { func (o *deleteAppOpts) deleteAppResources() error { o.spinner.Start(deleteAppResourcesStartMsg) - if err := o.cfn.DeleteApp(o.AppName()); err != nil { + if err := o.cfn.DeleteApp(o.name); err != nil { o.spinner.Stop(log.Serror("Error deleting application resources.")) return fmt.Errorf("delete app resources: %w", err) } @@ -282,9 +284,9 @@ func (o *deleteAppOpts) deleteAppResources() error { func (o *deleteAppOpts) deleteAppConfigs() error { o.spinner.Start(deleteAppConfigStartMsg) - if err := o.store.DeleteApplication(o.AppName()); err != nil { + if err := o.store.DeleteApplication(o.name); err != nil { o.spinner.Stop(log.Serror("Error deleting application configuration.")) - return fmt.Errorf("delete application %s configuration: %w", o.AppName(), err) + return fmt.Errorf("delete application %s configuration: %w", o.name, err) } o.spinner.Stop(log.Ssuccess(deleteAppConfigStopMsg)) return nil @@ -300,11 +302,9 @@ func (o *deleteAppOpts) deleteWs() error { return nil } -// BuildAppDeleteCommand builds the `app delete` subcommand. -func BuildAppDeleteCommand() *cobra.Command { - vars := deleteAppVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildAppDeleteCommand builds the `app delete` subcommand. +func buildAppDeleteCommand() *cobra.Command { + vars := deleteAppVars{} cmd := &cobra.Command{ Use: "delete", Short: "Delete all resources associated with the application.", @@ -327,6 +327,7 @@ func BuildAppDeleteCommand() *cobra.Command { }), } + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, tryReadingAppName(), appFlagDescription) cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, yesFlagDescription) cmd.Flags().StringToStringVar(&vars.envProfiles, envProfilesFlag, nil, envProfilesFlagDescription) return cmd diff --git a/internal/pkg/cli/app_delete_test.go b/internal/pkg/cli/app_delete_test.go index 2419d37da47..98b9f56c7e8 100644 --- a/internal/pkg/cli/app_delete_test.go +++ b/internal/pkg/cli/app_delete_test.go @@ -41,9 +41,7 @@ func TestDeleteAppOpts_Validate(t *testing.T) { t.Run(name, func(t *testing.T) { opts := deleteAppOpts{ deleteAppVars: deleteAppVars{ - GlobalOpts: &GlobalOpts{ - appName: test.name, - }, + name: test.name, }, } @@ -114,12 +112,10 @@ func TestDeleteAppOpts_Ask(t *testing.T) { test.setupMocks(ctrl) opts := deleteAppOpts{ deleteAppVars: deleteAppVars{ - GlobalOpts: &GlobalOpts{ - appName: mockAppName, - prompt: mockPrompter, - }, + name: mockAppName, skipConfirmation: test.skipConfirmation, }, + prompt: mockPrompter, } got := opts.Ask() @@ -303,9 +299,7 @@ func TestDeleteAppOpts_Execute(t *testing.T) { opts := deleteAppOpts{ deleteAppVars: deleteAppVars{ - GlobalOpts: &GlobalOpts{ - appName: mockAppName, - }, + name: mockAppName, }, spinner: mockSpinner, store: mockStore, diff --git a/internal/pkg/cli/app_init.go b/internal/pkg/cli/app_init.go index 75602ab0d51..00561450467 100644 --- a/internal/pkg/cli/app_init.go +++ b/internal/pkg/cli/app_init.go @@ -33,9 +33,9 @@ const ( ) type initAppVars struct { - AppName string - DomainName string - ResourceTags map[string]string + name string + domainName string + resourceTags map[string]string } type initAppOpts struct { @@ -78,16 +78,16 @@ func newInitAppOpts(vars initAppVars) (*initAppOpts, error) { // Validate returns an error if the user's input is invalid. func (o *initAppOpts) Validate() error { - if o.AppName != "" { - if err := o.validateAppName(o.AppName); err != nil { + if o.name != "" { + if err := o.validateAppName(o.name); err != nil { return err } } - if o.DomainName != "" { - if err := validateDomainName(o.DomainName); err != nil { - return fmt.Errorf("domain name %s is invalid: %w", o.DomainName, err) + if o.domainName != "" { + if err := validateDomainName(o.domainName); err != nil { + return fmt.Errorf("domain name %s is invalid: %w", o.domainName, err) } - if err := o.validateDomain(o.DomainName); err != nil { + if err := o.validateDomain(o.domainName); err != nil { return err } } @@ -111,20 +111,20 @@ https://github.com/aws/copilot-cli/wiki/credentials`) // When there's a local application. summary, err := o.ws.Summary() if err == nil { - if o.AppName == "" { + if o.name == "" { log.Infoln(fmt.Sprintf( "Your workspace is registered to application %s.", color.HighlightUserInput(summary.Application))) - o.AppName = summary.Application + o.name = summary.Application return nil } - if o.AppName != summary.Application { + if o.name != summary.Application { log.Errorf(`Workspace is already registered with application %s instead of %s. If you'd like to delete the application locally, you can remove the %s directory. If you'd like to delete the application and all of its resources, run %s. `, summary.Application, - o.AppName, + o.name, workspace.CopilotDirName, color.HighlightCode("copilot app delete")) return fmt.Errorf("workspace already registered with %s", summary.Application) @@ -132,7 +132,7 @@ If you'd like to delete the application and all of its resources, run %s. } // Flag is set by user. - if o.AppName != "" { + if o.name != "" { return nil } @@ -159,28 +159,28 @@ func (o *initAppOpts) Execute() error { return fmt.Errorf("get identity: %w", err) } - err = o.ws.Create(o.AppName) + err = o.ws.Create(o.name) if err != nil { - return fmt.Errorf("create new workspace with application name %s: %w", o.AppName, err) + return fmt.Errorf("create new workspace with application name %s: %w", o.name, err) } - o.prog.Start(fmt.Sprintf(fmtAppInitStart, color.HighlightUserInput(o.AppName))) + o.prog.Start(fmt.Sprintf(fmtAppInitStart, color.HighlightUserInput(o.name))) err = o.cfn.DeployApp(&deploy.CreateAppInput{ - Name: o.AppName, + Name: o.name, AccountID: caller.Account, - DomainName: o.DomainName, - AdditionalTags: o.ResourceTags, + DomainName: o.domainName, + AdditionalTags: o.resourceTags, }) if err != nil { - o.prog.Stop(log.Serrorf(fmtAppInitFailed, color.HighlightUserInput(o.AppName))) + o.prog.Stop(log.Serrorf(fmtAppInitFailed, color.HighlightUserInput(o.name))) return err } - o.prog.Stop(log.Ssuccessf(fmtAppInitComplete, color.HighlightUserInput(o.AppName))) + o.prog.Stop(log.Ssuccessf(fmtAppInitComplete, color.HighlightUserInput(o.name))) return o.store.CreateApplication(&config.Application{ AccountID: caller.Account, - Name: o.AppName, - Domain: o.DomainName, - Tags: o.ResourceTags, + Name: o.name, + Domain: o.domainName, + Tags: o.resourceTags, }) } @@ -196,7 +196,7 @@ func (o *initAppOpts) validateAppName(name string) error { } return fmt.Errorf("get application %s: %w", name, err) } - if o.DomainName != "" && app.Domain != o.DomainName { + if o.domainName != "" && app.Domain != o.domainName { return fmt.Errorf("application named %s already exists with a different domain name %s", name, app.Domain) } return nil @@ -229,7 +229,7 @@ func (o *initAppOpts) askAppName(formatMsg string) error { if err != nil { return fmt.Errorf("prompt get application name: %w", err) } - o.AppName = appName + o.name = appName return nil } @@ -246,12 +246,12 @@ func (o *initAppOpts) askSelectExistingAppName(existingApps []*config.Applicatio if err != nil { return fmt.Errorf("prompt select application name: %w", err) } - o.AppName = name + o.name = name return nil } -// BuildAppInitCommand builds the command for creating a new application. -func BuildAppInitCommand() *cobra.Command { +// buildAppInitCommand builds the command for creating a new application. +func buildAppInitCommand() *cobra.Command { vars := initAppVars{} cmd := &cobra.Command{ Use: "init [name]", @@ -272,7 +272,7 @@ An application is a collection of containerized services that operate together.` return err } if len(args) == 1 { - opts.AppName = args[0] + opts.name = args[0] } if err := opts.Validate(); err != nil { return err @@ -283,7 +283,7 @@ An application is a collection of containerized services that operate together.` if err := opts.Execute(); err != nil { return err } - log.Successf("The directory %s will hold service manifests for application %s.\n", color.HighlightResource(workspace.CopilotDirName), color.HighlightUserInput(opts.AppName)) + log.Successf("The directory %s will hold service manifests for application %s.\n", color.HighlightResource(workspace.CopilotDirName), color.HighlightUserInput(opts.name)) log.Infoln() log.Infoln("Recommended follow-up actions:") for _, followUp := range opts.RecommendedActions() { @@ -292,7 +292,7 @@ An application is a collection of containerized services that operate together.` return nil }), } - cmd.Flags().StringVar(&vars.DomainName, domainNameFlag, "", domainNameFlagDescription) - cmd.Flags().StringToStringVar(&vars.ResourceTags, resourceTagsFlag, nil, resourceTagsFlagDescription) + cmd.Flags().StringVar(&vars.domainName, domainNameFlag, "", domainNameFlagDescription) + cmd.Flags().StringToStringVar(&vars.resourceTags, resourceTagsFlag, nil, resourceTagsFlagDescription) return cmd } diff --git a/internal/pkg/cli/app_init_test.go b/internal/pkg/cli/app_init_test.go index 9636c796e8d..325fa62a5fd 100644 --- a/internal/pkg/cli/app_init_test.go +++ b/internal/pkg/cli/app_init_test.go @@ -121,7 +121,7 @@ func TestInitAppOpts_Ask(t *testing.T) { opts := &initAppOpts{ initAppVars: initAppVars{ - AppName: tc.inAppName, + name: tc.inAppName, }, store: mocks.NewMockstore(ctrl), ws: mocks.NewMockwsAppManager(ctrl), @@ -137,7 +137,7 @@ func TestInitAppOpts_Ask(t *testing.T) { require.EqualError(t, err, tc.wantedErr) } else { require.NoError(t, err) - require.Equal(t, tc.wantedAppName, opts.AppName) + require.Equal(t, tc.wantedAppName, opts.name) } }) } @@ -264,8 +264,8 @@ func TestInitAppOpts_Validate(t *testing.T) { route53: mockRoute53Svc, store: mockStore, initAppVars: initAppVars{ - AppName: tc.inAppName, - DomainName: tc.inDomainName, + name: tc.inAppName, + domainName: tc.inDomainName, }, } @@ -407,9 +407,9 @@ func TestInitAppOpts_Execute(t *testing.T) { opts := &initAppOpts{ initAppVars: initAppVars{ - AppName: "myapp", - DomainName: tc.inDomainName, - ResourceTags: map[string]string{ + name: "myapp", + domainName: tc.inDomainName, + resourceTags: map[string]string{ "owner": "boss", }, }, diff --git a/internal/pkg/cli/app_list.go b/internal/pkg/cli/app_list.go index faae0476c3b..0c855eb0a1a 100644 --- a/internal/pkg/cli/app_list.go +++ b/internal/pkg/cli/app_list.go @@ -31,8 +31,8 @@ func (o *listAppOpts) Execute() error { return nil } -// BuildAppListCommand builds the command to list existing applications. -func BuildAppListCommand() *cobra.Command { +// buildAppListCommand builds the command to list existing applications. +func buildAppListCommand() *cobra.Command { cmd := &cobra.Command{ Use: "ls", Short: "Lists all the applications in your account.", diff --git a/internal/pkg/cli/app_show.go b/internal/pkg/cli/app_show.go index 86d3f6383ba..547cb650c3e 100644 --- a/internal/pkg/cli/app_show.go +++ b/internal/pkg/cli/app_show.go @@ -8,6 +8,7 @@ import ( "io" "github.com/aws/copilot-cli/internal/pkg/config" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/aws/copilot-cli/internal/pkg/describe" @@ -21,16 +22,17 @@ const ( ) type showAppVars struct { - *GlobalOpts + name string shouldOutputJSON bool } type showAppOpts struct { showAppVars - store store - w io.Writer - sel appSelector + prompt prompter + store store + w io.Writer + sel appSelector } func newShowAppOpts(vars showAppVars) (*showAppOpts, error) { @@ -39,20 +41,22 @@ func newShowAppOpts(vars showAppVars) (*showAppOpts, error) { return nil, fmt.Errorf("new config store: %w", err) } + prompter := prompt.New() return &showAppOpts{ showAppVars: vars, store: store, w: log.OutputWriter, - sel: selector.NewSelect(vars.prompt, store), + prompt: prompter, + sel: selector.NewSelect(prompter, store), }, nil } // Validate returns an error if the values provided by the user are invalid. func (o *showAppOpts) Validate() error { - if o.AppName() != "" { - _, err := o.store.GetApplication(o.AppName()) + if o.name != "" { + _, err := o.store.GetApplication(o.name) if err != nil { - return fmt.Errorf("get application %s: %w", o.AppName(), err) + return fmt.Errorf("get application %s: %w", o.name, err) } } @@ -87,17 +91,17 @@ func (o *showAppOpts) Execute() error { } func (o *showAppOpts) description() (*describe.App, error) { - app, err := o.store.GetApplication(o.AppName()) + app, err := o.store.GetApplication(o.name) if err != nil { - return nil, fmt.Errorf("get application %s: %w", o.AppName(), err) + return nil, fmt.Errorf("get application %s: %w", o.name, err) } - envs, err := o.store.ListEnvironments(o.AppName()) + envs, err := o.store.ListEnvironments(o.name) if err != nil { - return nil, fmt.Errorf("list environments in application %s: %w", o.AppName(), err) + return nil, fmt.Errorf("list environments in application %s: %w", o.name, err) } - svcs, err := o.store.ListServices(o.AppName()) + svcs, err := o.store.ListServices(o.name) if err != nil { - return nil, fmt.Errorf("list services in application %s: %w", o.AppName(), err) + return nil, fmt.Errorf("list services in application %s: %w", o.name, err) } var trimmedEnvs []*config.Environment for _, env := range envs { @@ -124,22 +128,20 @@ func (o *showAppOpts) description() (*describe.App, error) { } func (o *showAppOpts) askName() error { - if o.AppName() != "" { + if o.name != "" { return nil } name, err := o.sel.Application(appShowNamePrompt, appShowNameHelpPrompt) if err != nil { return fmt.Errorf("select application: %w", err) } - o.appName = name + o.name = name return nil } -// BuildAppShowCmd builds the command for showing details of an application. -func BuildAppShowCmd() *cobra.Command { - vars := showAppVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildAppShowCmd builds the command for showing details of an application. +func buildAppShowCmd() *cobra.Command { + vars := showAppVars{} cmd := &cobra.Command{ Use: "show", Short: "Shows info about an application.", @@ -167,6 +169,6 @@ func BuildAppShowCmd() *cobra.Command { } // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) cmd.Flags().BoolVar(&vars.shouldOutputJSON, jsonFlag, false, jsonFlagDescription) - cmd.Flags().StringVarP(&vars.appName, nameFlag, nameFlagShort, "" /* default */, appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, tryReadingAppName(), appFlagDescription) return cmd } diff --git a/internal/pkg/cli/app_show_test.go b/internal/pkg/cli/app_show_test.go index 29b1d4591c2..27f77c23fb1 100644 --- a/internal/pkg/cli/app_show_test.go +++ b/internal/pkg/cli/app_show_test.go @@ -66,12 +66,10 @@ func TestShowAppOpts_Validate(t *testing.T) { opts := &showAppOpts{ showAppVars: showAppVars{ - GlobalOpts: &GlobalOpts{ - prompt: mockPrompter, - appName: tc.inAppName, - }, + name: tc.inAppName, }, - store: mockStoreReader, + store: mockStoreReader, + prompt: mockPrompter, } // WHEN @@ -137,9 +135,7 @@ func TestShowAppOpts_Ask(t *testing.T) { opts := &showAppOpts{ showAppVars: showAppVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inApp, - }, + name: tc.inApp, }, sel: mocks.sel, } @@ -152,7 +148,7 @@ func TestShowAppOpts_Ask(t *testing.T) { require.EqualError(t, err, tc.wantedError.Error()) } else { require.NoError(t, err) - require.Equal(t, tc.wantedApp, opts.AppName(), "expected app names to match") + require.Equal(t, tc.wantedApp, opts.name, "expected app names to match") } }) @@ -308,9 +304,7 @@ Services opts := &showAppOpts{ showAppVars: showAppVars{ shouldOutputJSON: tc.shouldOutputJSON, - GlobalOpts: &GlobalOpts{ - appName: testAppName, - }, + name: testAppName, }, store: mockStoreReader, w: b, diff --git a/internal/pkg/cli/cli.go b/internal/pkg/cli/cli.go index 6abe88da3f2..44789785789 100644 --- a/internal/pkg/cli/cli.go +++ b/internal/pkg/cli/cli.go @@ -12,62 +12,23 @@ import ( "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/copilot-cli/internal/pkg/term/color" - "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/workspace" "github.com/spf13/cobra" - "github.com/spf13/viper" ) -// GlobalOpts holds fields that are used across multiple commands. -type GlobalOpts struct { - appName string - prompt prompter -} - -// NewGlobalOpts returns a GlobalOpts with the application name retrieved from viper. -func NewGlobalOpts() *GlobalOpts { - bindAppName() - - return &GlobalOpts{ - // Leave the appName as empty in case it's overwritten by a global flag. - // See https://github.com/aws/copilot-cli/issues/570#issuecomment-569133741 - prompt: prompt.New(), - } -} - -// AppName returns the application name. -// If the name is empty, it caches it after querying viper. -func (o *GlobalOpts) AppName() string { - if o.appName != "" { - return o.appName - } - o.appName = viper.GetString(appFlag) - return o.appName -} - -// bindAppName loads the application's name to viper. -// If there is an error, we swallow the error and leave the default value as empty string. -func bindAppName() { - name, err := loadAppName() - if err != nil { - return - } - viper.SetDefault(appFlag, name) -} - -// loadAppName retrieves the application's name from the workspace if it exists and returns it. -// If there is an error, it returns an empty string and the error. -func loadAppName() (string, error) { +// tryReadingAppName retrieves the application's name from the workspace if it exists and returns it. +// If there is an error while retrieving the workspace summary, returns the empty string. +func tryReadingAppName() string { ws, err := workspace.New() if err != nil { - return "", fmt.Errorf("fetching workspace: %w", err) + return "" } summary, err := ws.Summary() if err != nil { - return "", fmt.Errorf("reading from workspace: %w", err) + return "" } - return summary.Application, nil + return summary.Application } type errReservedArg struct { diff --git a/internal/pkg/cli/deploy.go b/internal/pkg/cli/deploy.go index 07ab94ef81b..9d14711b94c 100644 --- a/internal/pkg/cli/deploy.go +++ b/internal/pkg/cli/deploy.go @@ -12,7 +12,7 @@ import ( // BuildDeployCmd is the deploy command - which is // an alias for app deploy. func BuildDeployCmd() *cobra.Command { - deployCmd := BuildSvcDeployCmd() + deployCmd := buildSvcDeployCmd() deployCmd.Use = "deploy" deployCmd.Short = "Deploy your service." deployCmd.Long = `Command for deploying services to your environments.` diff --git a/internal/pkg/cli/env.go b/internal/pkg/cli/env.go index eafad4c5a83..61a269501dc 100644 --- a/internal/pkg/cli/env.go +++ b/internal/pkg/cli/env.go @@ -7,7 +7,6 @@ import ( "github.com/aws/copilot-cli/cmd/copilot/template" "github.com/aws/copilot-cli/internal/pkg/cli/group" "github.com/spf13/cobra" - "github.com/spf13/viper" ) // BuildEnvCmd is the top level command for environments. @@ -19,14 +18,12 @@ Environments are deployment stages shared between services.`, Long: `Commands for environments. Environments are deployment stages shared between services.`, } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) - cmd.PersistentFlags().StringP(appFlag, appFlagShort, "" /* default */, appFlagDescription) - _ = viper.BindPFlag(appFlag, cmd.PersistentFlags().Lookup(appFlag)) // Ignore err because the flag name is not empty. - cmd.AddCommand(BuildEnvInitCmd()) - cmd.AddCommand(BuildEnvListCmd()) - cmd.AddCommand(BuildEnvDeleteCmd()) - cmd.AddCommand(BuildEnvShowCmd()) + cmd.AddCommand(buildEnvInitCmd()) + cmd.AddCommand(buildEnvListCmd()) + cmd.AddCommand(buildEnvDeleteCmd()) + cmd.AddCommand(buildEnvShowCmd()) + cmd.AddCommand(buildEnvUpgradeCmd()) cmd.SetUsageTemplate(template.Usage) cmd.Annotations = map[string]string{ "group": group.Develop, diff --git a/internal/pkg/cli/env_delete.go b/internal/pkg/cli/env_delete.go index 1dd060942ee..7a106391800 100644 --- a/internal/pkg/cli/env_delete.go +++ b/internal/pkg/cli/env_delete.go @@ -18,6 +18,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/term/color" "github.com/aws/copilot-cli/internal/pkg/term/log" termprogress "github.com/aws/copilot-cli/internal/pkg/term/progress" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/spf13/cobra" ) @@ -46,10 +47,10 @@ type resourceGetter interface { } type deleteEnvVars struct { - *GlobalOpts - EnvName string - EnvProfile string - SkipConfirmation bool + appName string + name string + profile string + skipConfirmation bool } type deleteEnvOpts struct { @@ -60,6 +61,7 @@ type deleteEnvOpts struct { deployClient environmentDeployer profileConfig profileNames prog progress + prompt prompter sel configSelector // initProfileClients is overriden in tests. @@ -76,16 +78,18 @@ func newDeleteEnvOpts(vars deleteEnvVars) (*deleteEnvOpts, error) { return nil, err } + prompter := prompt.New() return &deleteEnvOpts{ deleteEnvVars: vars, store: store, profileConfig: cfg, prog: termprogress.NewSpinner(), - sel: selector.NewConfigSelect(vars.prompt, store), + sel: selector.NewConfigSelect(prompter, store), + prompt: prompter, initProfileClients: func(o *deleteEnvOpts) error { - profileSess, err := sessions.NewProvider().FromProfile(o.EnvProfile) + profileSess, err := sessions.NewProvider().FromProfile(o.profile) if err != nil { - return fmt.Errorf("cannot create session from profile %s: %w", o.EnvProfile, err) + return fmt.Errorf("cannot create session from profile %s: %w", o.profile, err) } o.rgClient = resourcegroupstaggingapi.New(profileSess) o.deployClient = cloudformation.New(profileSess) @@ -96,7 +100,7 @@ func newDeleteEnvOpts(vars deleteEnvVars) (*deleteEnvOpts, error) { // Validate returns an error if the individual user inputs are invalid. func (o *deleteEnvOpts) Validate() error { - if o.EnvName != "" { + if o.name != "" { if err := o.validateEnvName(); err != nil { return err } @@ -113,12 +117,12 @@ func (o *deleteEnvOpts) Ask() error { return err } - if o.SkipConfirmation { + if o.skipConfirmation { return nil } - deleteConfirmed, err := o.prompt.Confirm(fmt.Sprintf(fmtDeleteEnvPrompt, o.EnvName, o.AppName()), "") + deleteConfirmed, err := o.prompt.Confirm(fmt.Sprintf(fmtDeleteEnvPrompt, o.name, o.appName), "") if err != nil { - return fmt.Errorf("confirm to delete environment %s: %w", o.EnvName, err) + return fmt.Errorf("confirm to delete environment %s: %w", o.name, err) } if !deleteConfirmed { return errEnvDeleteCancelled @@ -153,7 +157,7 @@ func (o *deleteEnvOpts) RecommendedActions() []string { } func (o *deleteEnvOpts) validateEnvName() error { - if _, err := o.store.GetEnvironment(o.AppName(), o.EnvName); err != nil { + if _, err := o.store.GetEnvironment(o.appName, o.name); err != nil { return err } return nil @@ -169,11 +173,11 @@ func (o *deleteEnvOpts) validateNoRunningServices() error { }, { Key: aws.String(deploy.EnvTagKey), - Values: []*string{aws.String(o.EnvName)}, + Values: []*string{aws.String(o.name)}, }, { Key: aws.String(deploy.AppTagKey), - Values: []*string{aws.String(o.AppName())}, + Values: []*string{aws.String(o.appName)}, }, }, }) @@ -190,25 +194,25 @@ func (o *deleteEnvOpts) validateNoRunningServices() error { svcNames = append(svcNames, *t.Value) } } - return fmt.Errorf("service '%s' still exist within the environment %s", strings.Join(svcNames, ", "), o.EnvName) + return fmt.Errorf("service '%s' still exist within the environment %s", strings.Join(svcNames, ", "), o.name) } return nil } func (o *deleteEnvOpts) askEnvName() error { - if o.EnvName != "" { + if o.name != "" { return nil } - env, err := o.sel.Environment(envDeleteNamePrompt, "", o.AppName()) + env, err := o.sel.Environment(envDeleteNamePrompt, "", o.appName) if err != nil { return fmt.Errorf("select environment to delete: %w", err) } - o.EnvName = env + o.name = env return nil } func (o *deleteEnvOpts) askProfile() error { - if o.EnvProfile != "" { + if o.profile != "" { return nil } @@ -217,44 +221,42 @@ func (o *deleteEnvOpts) askProfile() error { return errNamedProfilesNotFound } if len(names) == 1 { - o.EnvProfile = names[0] - log.Infof("Only found one profile, defaulting to: %s\n", color.HighlightUserInput(o.EnvProfile)) + o.profile = names[0] + log.Infof("Only found one profile, defaulting to: %s\n", color.HighlightUserInput(o.profile)) return nil } profile, err := o.prompt.SelectOne( - fmt.Sprintf(fmtEnvDeleteProfilePrompt, color.HighlightUserInput(o.EnvName)), + fmt.Sprintf(fmtEnvDeleteProfilePrompt, color.HighlightUserInput(o.name)), envDeleteProfileHelpPrompt, names) if err != nil { return fmt.Errorf("get the profile name: %w", err) } - o.EnvProfile = profile + o.profile = profile return nil } // deleteStack returns true if the stack was deleted successfully. Otherwise, returns false. func (o *deleteEnvOpts) deleteStack() bool { - o.prog.Start(fmt.Sprintf(fmtDeleteEnvStart, o.EnvName, o.AppName())) - if err := o.deployClient.DeleteEnvironment(o.AppName(), o.EnvName); err != nil { - o.prog.Stop(log.Serrorf(fmtDeleteEnvFailed, o.EnvName, o.AppName(), err)) + o.prog.Start(fmt.Sprintf(fmtDeleteEnvStart, o.name, o.appName)) + if err := o.deployClient.DeleteEnvironment(o.appName, o.name); err != nil { + o.prog.Stop(log.Serrorf(fmtDeleteEnvFailed, o.name, o.appName, err)) return false } - o.prog.Stop(log.Ssuccessf(fmtDeleteEnvComplete, o.EnvName, o.AppName())) + o.prog.Stop(log.Ssuccessf(fmtDeleteEnvComplete, o.name, o.appName)) return true } func (o *deleteEnvOpts) deleteFromStore() { - if err := o.store.DeleteEnvironment(o.AppName(), o.EnvName); err != nil { - log.Infof("Failed to remove environment %s from application %s store: %v\n", o.EnvName, o.AppName(), err) + if err := o.store.DeleteEnvironment(o.appName, o.name); err != nil { + log.Infof("Failed to remove environment %s from application %s store: %v\n", o.name, o.appName, err) } } -// BuildEnvDeleteCmd builds the command to delete environment(s). -func BuildEnvDeleteCmd() *cobra.Command { - vars := deleteEnvVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildEnvDeleteCmd builds the command to delete environment(s). +func buildEnvDeleteCmd() *cobra.Command { + vars := deleteEnvVars{} cmd := &cobra.Command{ Use: "delete", Short: "Deletes an environment from your application.", @@ -278,8 +280,9 @@ func BuildEnvDeleteCmd() *cobra.Command { return opts.Execute() }), } - cmd.Flags().StringVarP(&vars.EnvName, nameFlag, nameFlagShort, "", envFlagDescription) - cmd.Flags().StringVar(&vars.EnvProfile, profileFlag, "", profileFlagDescription) - cmd.Flags().BoolVar(&vars.SkipConfirmation, yesFlag, false, yesFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", envFlagDescription) + cmd.Flags().StringVar(&vars.profile, profileFlag, "", profileFlagDescription) + cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, yesFlagDescription) return cmd } diff --git a/internal/pkg/cli/env_delete_test.go b/internal/pkg/cli/env_delete_test.go index 4733b0ad09e..54569824d23 100644 --- a/internal/pkg/cli/env_delete_test.go +++ b/internal/pkg/cli/env_delete_test.go @@ -65,8 +65,8 @@ func TestDeleteEnvOpts_Validate(t *testing.T) { defer ctrl.Finish() opts := &deleteEnvOpts{ deleteEnvVars: deleteEnvVars{ - EnvName: tc.inEnv, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + name: tc.inEnv, + appName: tc.inAppName, }, store: tc.mockStore(ctrl), } @@ -116,7 +116,7 @@ func TestDeleteEnvOpts_Ask(t *testing.T) { o.sel = mockSelector o.profileConfig = mockCfg - o.GlobalOpts.prompt = mockPrompter + o.prompt = mockPrompter }, wantedEnvName: testEnv, wantedEnvProfile: testProfile1, @@ -134,7 +134,7 @@ func TestDeleteEnvOpts_Ask(t *testing.T) { o.sel = mockSelector o.profileConfig = mockCfg - o.GlobalOpts.prompt = mockPrompter + o.prompt = mockPrompter }, wantedEnvName: testEnv, wantedEnvProfile: testProfile1, @@ -148,7 +148,7 @@ func TestDeleteEnvOpts_Ask(t *testing.T) { mockPrompter := mocks.NewMockprompter(ctrl) mockPrompter.EXPECT().Confirm(fmt.Sprintf(fmtDeleteEnvPrompt, testEnv, testApp), gomock.Any()).Return(false, errors.New("some error")) - o.GlobalOpts.prompt = mockPrompter + o.prompt = mockPrompter }, wantedError: errors.New("confirm to delete environment test: some error"), @@ -164,7 +164,7 @@ func TestDeleteEnvOpts_Ask(t *testing.T) { mockPrompter.EXPECT().SelectOne(gomock.Any(), gomock.Any(), gomock.Any()).Return("", errors.New("some error")) o.profileConfig = mockCfg - o.GlobalOpts.prompt = mockPrompter + o.prompt = mockPrompter }, wantedError: errors.New("get the profile name: some error"), @@ -190,12 +190,10 @@ func TestDeleteEnvOpts_Ask(t *testing.T) { defer ctrl.Finish() opts := &deleteEnvOpts{ deleteEnvVars: deleteEnvVars{ - EnvName: tc.inEnvName, - EnvProfile: tc.inEnvProfile, - GlobalOpts: &GlobalOpts{ - appName: testApp, - }, - SkipConfirmation: tc.inSkipConfirmation, + name: tc.inEnvName, + profile: tc.inEnvProfile, + appName: testApp, + skipConfirmation: tc.inSkipConfirmation, }, } tc.mockDependencies(ctrl, opts) @@ -205,8 +203,8 @@ func TestDeleteEnvOpts_Ask(t *testing.T) { // THEN if tc.wantedError == nil { - require.Equal(t, tc.wantedEnvName, opts.EnvName) - require.Equal(t, tc.wantedEnvProfile, opts.EnvProfile) + require.Equal(t, tc.wantedEnvName, opts.name) + require.Equal(t, tc.wantedEnvProfile, opts.profile) require.NoError(t, err) } else { require.EqualError(t, err, tc.wantedError.Error()) @@ -334,10 +332,8 @@ func TestDeleteEnvOpts_Execute(t *testing.T) { defer ctrl.Finish() opts := deleteEnvOpts{ deleteEnvVars: deleteEnvVars{ - EnvName: testEnv, - GlobalOpts: &GlobalOpts{ - appName: testApp, - }, + name: testEnv, + appName: testApp, }, store: tc.mockStore(ctrl), deployClient: tc.mockDeploy(ctrl), diff --git a/internal/pkg/cli/env_init.go b/internal/pkg/cli/env_init.go index e81e1160d48..398d7dfe314 100644 --- a/internal/pkg/cli/env_init.go +++ b/internal/pkg/cli/env_init.go @@ -115,17 +115,17 @@ func (v tempCredsVars) isSet() bool { } type initEnvVars struct { - *GlobalOpts - Name string // Name for the environment. - Profile string // The named profile to use for credential retrieval. Mutually exclusive with TempCreds. - IsProduction bool // True means retain resources even after deletion. - DefaultConfig bool // True means using default environment configuration. + appName string + name string // Name for the environment. + profile string // The named profile to use for credential retrieval. Mutually exclusive with tempCreds. + isProduction bool // True means retain resources even after deletion. + defaultConfig bool // True means using default environment configuration. - ImportVPC importVPCVars // Existing VPC resources to use instead of creating new ones. - AdjustVPC adjustVPCVars // Configure parameters for VPC resources generated while initializing an environment. + importVPC importVPCVars // Existing VPC resources to use instead of creating new ones. + adjustVPC adjustVPCVars // Configure parameters for VPC resources generated while initializing an environment. - TempCreds tempCredsVars // Temporary credentials to initialize the environment. Mutually exclusive with the Profile. - Region string // The region to create the environment in. + tempCreds tempCredsVars // Temporary credentials to initialize the environment. Mutually exclusive with the profile. + region string // The region to create the environment in. } type initEnvOpts struct { @@ -140,6 +140,7 @@ type initEnvOpts struct { envIdentity identityService ec2Client ec2Client prog progress + prompt prompter selVPC ec2Selector selCreds credsSelector @@ -161,6 +162,7 @@ func newInitEnvOpts(vars initEnvVars) (*initEnvOpts, error) { return nil, fmt.Errorf("read named profiles: %w", err) } + prompter := prompt.New() return &initEnvOpts{ initEnvVars: vars, sessProvider: sessProvider, @@ -168,22 +170,23 @@ func newInitEnvOpts(vars initEnvVars) (*initEnvOpts, error) { appDeployer: deploycfn.New(defaultSession), identity: identity.New(defaultSession), prog: termprogress.NewSpinner(), + prompt: prompter, selCreds: &selector.CredsSelect{ Session: sessProvider, Profile: cfg, - Prompt: vars.prompt, + Prompt: prompter, }, }, nil } // Validate returns an error if the values passed by flags are invalid. func (o *initEnvOpts) Validate() error { - if o.Name != "" { - if err := validateEnvironmentName(o.Name); err != nil { + if o.name != "" { + if err := validateEnvironmentName(o.name); err != nil { return err } } - if o.AppName() == "" { + if o.appName == "" { return fmt.Errorf("no application found: run %s or %s into your workspace please", color.HighlightCode("app init"), color.HighlightCode("cd")) } if err := o.validateCustomizedResources(); err != nil { @@ -216,7 +219,7 @@ func (o *initEnvOpts) Execute() error { o.envDeployer = deploycfn.New(o.sess) } - app, err := o.store.GetApplication(o.AppName()) + app, err := o.store.GetApplication(o.appName) if err != nil { // Ensure the app actually exists before we do a deployment. return err @@ -233,11 +236,11 @@ func (o *initEnvOpts) Execute() error { } // 2. Get the environment - env, err := o.envDeployer.GetEnvironment(o.AppName(), o.Name) + env, err := o.envDeployer.GetEnvironment(o.appName, o.name) if err != nil { - return fmt.Errorf("get environment struct for %s: %w", o.Name, err) + return fmt.Errorf("get environment struct for %s: %w", o.name, err) } - env.Prod = o.IsProduction + env.Prod = o.isProduction // 3. Add the stack set instance to the app stackset. if err := o.addToStackset(app, env); err != nil { @@ -259,17 +262,17 @@ func (o *initEnvOpts) RecommendedActions() []string { } func (o *initEnvOpts) validateCustomizedResources() error { - if o.ImportVPC.isSet() && o.AdjustVPC.isSet() { + if o.importVPC.isSet() && o.adjustVPC.isSet() { return errors.New("cannot specify both import vpc flags and configure vpc flags") } - if (o.ImportVPC.isSet() || o.AdjustVPC.isSet()) && o.DefaultConfig { + if (o.importVPC.isSet() || o.adjustVPC.isSet()) && o.defaultConfig { return fmt.Errorf("cannot import or configure vpc if --%s is set", defaultConfigFlag) } return nil } func (o *initEnvOpts) askEnvName() error { - if o.Name != "" { + if o.name != "" { return nil } @@ -277,28 +280,28 @@ func (o *initEnvOpts) askEnvName() error { if err != nil { return fmt.Errorf("get environment name: %w", err) } - o.Name = envName + o.name = envName return nil } func (o *initEnvOpts) askEnvSession() error { - if o.Profile != "" { - sess, err := o.sessProvider.FromProfile(o.Profile) + if o.profile != "" { + sess, err := o.sessProvider.FromProfile(o.profile) if err != nil { - return fmt.Errorf("create session from profile %s: %w", o.Profile, err) + return fmt.Errorf("create session from profile %s: %w", o.profile, err) } o.sess = sess return nil } - if o.TempCreds.isSet() { - sess, err := o.sessProvider.FromStaticCreds(o.TempCreds.AccessKeyID, o.TempCreds.SecretAccessKey, o.TempCreds.SessionToken) + if o.tempCreds.isSet() { + sess, err := o.sessProvider.FromStaticCreds(o.tempCreds.AccessKeyID, o.tempCreds.SecretAccessKey, o.tempCreds.SessionToken) if err != nil { return err } o.sess = sess return nil } - sess, err := o.selCreds.Creds(fmt.Sprintf(fmtEnvInitCredsPrompt, color.HighlightUserInput(o.Name)), envInitCredsHelpPrompt) + sess, err := o.selCreds.Creds(fmt.Sprintf(fmtEnvInitCredsPrompt, color.HighlightUserInput(o.name)), envInitCredsHelpPrompt) if err != nil { return fmt.Errorf("select creds: %w", err) } @@ -308,8 +311,8 @@ func (o *initEnvOpts) askEnvSession() error { func (o *initEnvOpts) askEnvRegion() error { region := aws.StringValue(o.sess.Config.Region) - if o.Region != "" { - region = o.Region + if o.region != "" { + region = o.region } if region == "" { v, err := o.prompt.Get(envInitRegionPrompt, "", nil, prompt.WithDefaultInput(envInitDefaultRegionOption)) @@ -323,13 +326,13 @@ func (o *initEnvOpts) askEnvRegion() error { } func (o *initEnvOpts) askCustomizedResources() error { - if o.DefaultConfig { + if o.defaultConfig { return nil } - if o.ImportVPC.isSet() { + if o.importVPC.isSet() { return o.askImportResources() } - if o.AdjustVPC.isSet() { + if o.adjustVPC.isSet() { return o.askAdjustResources() } adjustOrImport, err := o.prompt.SelectOne( @@ -353,7 +356,7 @@ func (o *initEnvOpts) askImportResources() error { if o.selVPC == nil { o.selVPC = selector.NewEC2Select(o.prompt, ec2.New(o.sess)) } - if o.ImportVPC.ID == "" { + if o.importVPC.ID == "" { vpcID, err := o.selVPC.VPC(envInitVPCSelectPrompt, "") if err != nil { if err == selector.ErrVPCNotFound { @@ -364,51 +367,51 @@ func (o *initEnvOpts) askImportResources() error { } return fmt.Errorf("select VPC: %w", err) } - o.ImportVPC.ID = vpcID + o.importVPC.ID = vpcID } if o.ec2Client == nil { o.ec2Client = ec2.New(o.sess) } - dnsSupport, err := o.ec2Client.HasDNSSupport(o.ImportVPC.ID) + dnsSupport, err := o.ec2Client.HasDNSSupport(o.importVPC.ID) if err != nil { - return fmt.Errorf("check if VPC %s has DNS support enabled: %w", o.ImportVPC.ID, err) + return fmt.Errorf("check if VPC %s has DNS support enabled: %w", o.importVPC.ID, err) } if !dnsSupport { log.Errorln(`Looks like you're creating an environment using a VPC with DNS support *disabled*. Copilot cannot create services in VPCs without DNS support. We recommend enabling this property. To learn more about the issue: https://aws.amazon.com/premiumsupport/knowledge-center/ecs-pull-container-api-error-ecr/`) - return fmt.Errorf("VPC %s has no DNS support enabled", o.ImportVPC.ID) + return fmt.Errorf("VPC %s has no DNS support enabled", o.importVPC.ID) } - if o.ImportVPC.PublicSubnetIDs == nil { - publicSubnets, err := o.selVPC.PublicSubnets(envInitPublicSubnetsSelectPrompt, "", o.ImportVPC.ID) + if o.importVPC.PublicSubnetIDs == nil { + publicSubnets, err := o.selVPC.PublicSubnets(envInitPublicSubnetsSelectPrompt, "", o.importVPC.ID) if err != nil { if err == selector.ErrSubnetsNotFound { log.Errorf(`No existing public subnets were found in VPC %s. You can either: - Create new public subnets and then import them. -- Use the default Copilot environment configuration.`, o.ImportVPC.ID) +- Use the default Copilot environment configuration.`, o.importVPC.ID) } return fmt.Errorf("select public subnets: %w", err) } - o.ImportVPC.PublicSubnetIDs = publicSubnets + o.importVPC.PublicSubnetIDs = publicSubnets } - if o.ImportVPC.PrivateSubnetIDs == nil { - privateSubnets, err := o.selVPC.PrivateSubnets(envInitPrivateSubnetsSelectPrompt, "", o.ImportVPC.ID) + if o.importVPC.PrivateSubnetIDs == nil { + privateSubnets, err := o.selVPC.PrivateSubnets(envInitPrivateSubnetsSelectPrompt, "", o.importVPC.ID) if err != nil { if err == selector.ErrSubnetsNotFound { log.Errorf(`No existing private subnets were found in VPC %s. You can either: - Create new private subnets and then import them. -- Use the default Copilot environment configuration.`, o.ImportVPC.ID) +- Use the default Copilot environment configuration.`, o.importVPC.ID) } return fmt.Errorf("select private subnets: %w", err) } - o.ImportVPC.PrivateSubnetIDs = privateSubnets + o.importVPC.PrivateSubnetIDs = privateSubnets } return nil } func (o *initEnvOpts) askAdjustResources() error { - if o.AdjustVPC.CIDR.String() == emptyIPNet.String() { + if o.adjustVPC.CIDR.String() == emptyIPNet.String() { vpcCIDRString, err := o.prompt.Get(envInitVPCCIDRPrompt, envInitVPCCIDRPromptHelp, validateCIDR, prompt.WithDefaultInput(stack.DefaultVPCCIDR)) if err != nil { @@ -418,46 +421,46 @@ func (o *initEnvOpts) askAdjustResources() error { if err != nil { return fmt.Errorf("parse VPC CIDR: %w", err) } - o.AdjustVPC.CIDR = *vpcCIDR + o.adjustVPC.CIDR = *vpcCIDR } - if o.AdjustVPC.PublicSubnetCIDRs == nil { + if o.adjustVPC.PublicSubnetCIDRs == nil { publicCIDR, err := o.prompt.Get(envInitPublicCIDRPrompt, envInitPublicCIDRPromptHelp, validateCIDRSlice, prompt.WithDefaultInput(stack.DefaultPublicSubnetCIDRs)) if err != nil { return fmt.Errorf("get public subnet CIDRs: %w", err) } - o.AdjustVPC.PublicSubnetCIDRs = strings.Split(publicCIDR, ",") + o.adjustVPC.PublicSubnetCIDRs = strings.Split(publicCIDR, ",") } - if o.AdjustVPC.PrivateSubnetCIDRs == nil { + if o.adjustVPC.PrivateSubnetCIDRs == nil { privateCIDR, err := o.prompt.Get(envInitPrivateCIDRPrompt, envInitPrivateCIDRPromptHelp, validateCIDRSlice, prompt.WithDefaultInput(stack.DefaultPrivateSubnetCIDRs)) if err != nil { return fmt.Errorf("get private subnet CIDRs: %w", err) } - o.AdjustVPC.PrivateSubnetCIDRs = strings.Split(privateCIDR, ",") + o.adjustVPC.PrivateSubnetCIDRs = strings.Split(privateCIDR, ",") } return nil } func (o *initEnvOpts) importVPCConfig() *deploy.ImportVPCConfig { - if o.DefaultConfig || !o.ImportVPC.isSet() { + if o.defaultConfig || !o.importVPC.isSet() { return nil } return &deploy.ImportVPCConfig{ - ID: o.ImportVPC.ID, - PrivateSubnetIDs: o.ImportVPC.PrivateSubnetIDs, - PublicSubnetIDs: o.ImportVPC.PublicSubnetIDs, + ID: o.importVPC.ID, + PrivateSubnetIDs: o.importVPC.PrivateSubnetIDs, + PublicSubnetIDs: o.importVPC.PublicSubnetIDs, } } func (o *initEnvOpts) adjustVPCConfig() *deploy.AdjustVPCConfig { - if o.DefaultConfig || !o.AdjustVPC.isSet() { + if o.defaultConfig || !o.adjustVPC.isSet() { return nil } return &deploy.AdjustVPCConfig{ - CIDR: o.AdjustVPC.CIDR.String(), - PrivateSubnetCIDRs: o.AdjustVPC.PrivateSubnetCIDRs, - PublicSubnetCIDRs: o.AdjustVPC.PublicSubnetCIDRs, + CIDR: o.adjustVPC.CIDR.String(), + PrivateSubnetCIDRs: o.adjustVPC.PrivateSubnetCIDRs, + PublicSubnetCIDRs: o.adjustVPC.PublicSubnetCIDRs, } } @@ -467,9 +470,9 @@ func (o *initEnvOpts) deployEnv(app *config.Application) error { return fmt.Errorf("get identity: %w", err) } deployEnvInput := &deploy.CreateEnvironmentInput{ - Name: o.Name, - AppName: o.AppName(), - Prod: o.IsProduction, + Name: o.name, + AppName: o.appName, + Prod: o.isProduction, ToolsAccountPrincipalARN: caller.RootUserARN, AppDNSName: app.Domain, AdditionalTags: app.Tags, @@ -477,42 +480,42 @@ func (o *initEnvOpts) deployEnv(app *config.Application) error { ImportVPCConfig: o.importVPCConfig(), } - o.prog.Start(fmt.Sprintf(fmtDeployEnvStart, color.HighlightUserInput(o.Name))) + o.prog.Start(fmt.Sprintf(fmtDeployEnvStart, color.HighlightUserInput(o.name))) if err := o.envDeployer.DeployEnvironment(deployEnvInput); err != nil { var existsErr *cloudformation.ErrStackAlreadyExists if errors.As(err, &existsErr) { // Do nothing if the stack already exists. o.prog.Stop(log.Ssuccessf(fmtDeployEnvComplete, - color.HighlightUserInput(o.Name), color.HighlightUserInput(o.AppName()))) + color.HighlightUserInput(o.name), color.HighlightUserInput(o.appName))) return nil } - o.prog.Stop(log.Serrorf(fmtDeployEnvFailed, color.HighlightUserInput(o.Name))) + o.prog.Stop(log.Serrorf(fmtDeployEnvFailed, color.HighlightUserInput(o.name))) return err } // Display updates while the deployment is happening. - o.prog.Start(fmt.Sprintf(fmtStreamEnvStart, color.HighlightUserInput(o.Name))) + o.prog.Start(fmt.Sprintf(fmtStreamEnvStart, color.HighlightUserInput(o.name))) stackEvents, responses := o.envDeployer.StreamEnvironmentCreation(deployEnvInput) for stackEvent := range stackEvents { o.prog.Events(o.humanizeEnvironmentEvents(stackEvent)) } resp := <-responses if resp.Err != nil { - o.prog.Stop(log.Serrorf(fmtStreamEnvFailed, color.HighlightUserInput(o.Name))) + o.prog.Stop(log.Serrorf(fmtStreamEnvFailed, color.HighlightUserInput(o.name))) return resp.Err } - o.prog.Stop(log.Ssuccessf(fmtStreamEnvComplete, color.HighlightUserInput(o.Name))) + o.prog.Stop(log.Ssuccessf(fmtStreamEnvComplete, color.HighlightUserInput(o.name))) return nil } func (o *initEnvOpts) addToStackset(app *config.Application, env *config.Environment) error { - o.prog.Start(fmt.Sprintf(fmtAddEnvToAppStart, color.Emphasize(env.AccountID), color.Emphasize(env.Region), color.HighlightUserInput(o.AppName()))) + o.prog.Start(fmt.Sprintf(fmtAddEnvToAppStart, color.Emphasize(env.AccountID), color.Emphasize(env.Region), color.HighlightUserInput(o.appName))) if err := o.appDeployer.AddEnvToApp(app, env); err != nil { - o.prog.Stop(log.Serrorf(fmtAddEnvToAppFailed, color.Emphasize(env.AccountID), color.Emphasize(env.Region), color.HighlightUserInput(o.AppName()))) + o.prog.Stop(log.Serrorf(fmtAddEnvToAppFailed, color.Emphasize(env.AccountID), color.Emphasize(env.Region), color.HighlightUserInput(o.appName))) return fmt.Errorf("deploy env %s to application %s: %w", env.Name, app.Name, err) } - o.prog.Stop(log.Ssuccessf(fmtAddEnvToAppComplete, color.Emphasize(env.AccountID), color.Emphasize(env.Region), color.HighlightUserInput(o.AppName()))) + o.prog.Stop(log.Ssuccessf(fmtAddEnvToAppComplete, color.Emphasize(env.AccountID), color.Emphasize(env.Region), color.HighlightUserInput(o.appName))) return nil } @@ -569,7 +572,7 @@ func (o *initEnvOpts) humanizeEnvironmentEvents(resourceEvents []deploy.Resource } func (o *initEnvOpts) envProgressOrder() (order []termprogress.Text) { - if !o.ImportVPC.isSet() { + if !o.importVPC.isSet() { order = append(order, []termprogress.Text{textVPC, textInternetGateway, textPublicSubnets, textPrivateSubnets, textRouteTables}...) } order = append(order, []termprogress.Text{textECSCluster, textALB}...) @@ -577,23 +580,21 @@ func (o *initEnvOpts) envProgressOrder() (order []termprogress.Text) { } func (o *initEnvOpts) validateCredentials() error { - if o.Profile != "" && o.TempCreds.AccessKeyID != "" { + if o.profile != "" && o.tempCreds.AccessKeyID != "" { return fmt.Errorf("cannot specify both --%s and --%s", profileFlag, accessKeyIDFlag) } - if o.Profile != "" && o.TempCreds.SecretAccessKey != "" { + if o.profile != "" && o.tempCreds.SecretAccessKey != "" { return fmt.Errorf("cannot specify both --%s and --%s", profileFlag, secretAccessKeyFlag) } - if o.Profile != "" && o.TempCreds.SessionToken != "" { + if o.profile != "" && o.tempCreds.SessionToken != "" { return fmt.Errorf("cannot specify both --%s and --%s", profileFlag, sessionTokenFlag) } return nil } -// BuildEnvInitCmd builds the command for adding an environment. -func BuildEnvInitCmd() *cobra.Command { - vars := initEnvVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildEnvInitCmd builds the command for adding an environment. +func buildEnvInitCmd() *cobra.Command { + vars := initEnvVars{} cmd := &cobra.Command{ Use: "init", @@ -628,27 +629,28 @@ func BuildEnvInitCmd() *cobra.Command { return opts.Execute() }), } - cmd.Flags().StringVarP(&vars.Name, nameFlag, nameFlagShort, "", envFlagDescription) - - cmd.Flags().StringVar(&vars.Profile, profileFlag, "", profileFlagDescription) - cmd.Flags().StringVar(&vars.TempCreds.AccessKeyID, accessKeyIDFlag, "", accessKeyIDFlagDescription) - cmd.Flags().StringVar(&vars.TempCreds.SecretAccessKey, secretAccessKeyFlag, "", secretAccessKeyFlagDescription) - cmd.Flags().StringVar(&vars.TempCreds.SessionToken, sessionTokenFlag, "", sessionTokenFlagDescription) - cmd.Flags().StringVar(&vars.Region, regionFlag, "", envRegionTokenFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", envFlagDescription) + cmd.Flags().StringVar(&vars.profile, profileFlag, "", profileFlagDescription) + cmd.Flags().StringVar(&vars.tempCreds.AccessKeyID, accessKeyIDFlag, "", accessKeyIDFlagDescription) + cmd.Flags().StringVar(&vars.tempCreds.SecretAccessKey, secretAccessKeyFlag, "", secretAccessKeyFlagDescription) + cmd.Flags().StringVar(&vars.tempCreds.SessionToken, sessionTokenFlag, "", sessionTokenFlagDescription) + cmd.Flags().StringVar(&vars.region, regionFlag, "", envRegionTokenFlagDescription) - cmd.Flags().BoolVar(&vars.IsProduction, prodEnvFlag, false, prodEnvFlagDescription) + cmd.Flags().BoolVar(&vars.isProduction, prodEnvFlag, false, prodEnvFlagDescription) - cmd.Flags().StringVar(&vars.ImportVPC.ID, vpcIDFlag, "", vpcIDFlagDescription) - cmd.Flags().StringSliceVar(&vars.ImportVPC.PublicSubnetIDs, publicSubnetsFlag, nil, publicSubnetsFlagDescription) - cmd.Flags().StringSliceVar(&vars.ImportVPC.PrivateSubnetIDs, privateSubnetsFlag, nil, privateSubnetsFlagDescription) + cmd.Flags().StringVar(&vars.importVPC.ID, vpcIDFlag, "", vpcIDFlagDescription) + cmd.Flags().StringSliceVar(&vars.importVPC.PublicSubnetIDs, publicSubnetsFlag, nil, publicSubnetsFlagDescription) + cmd.Flags().StringSliceVar(&vars.importVPC.PrivateSubnetIDs, privateSubnetsFlag, nil, privateSubnetsFlagDescription) - cmd.Flags().IPNetVar(&vars.AdjustVPC.CIDR, vpcCIDRFlag, net.IPNet{}, vpcCIDRFlagDescription) + cmd.Flags().IPNetVar(&vars.adjustVPC.CIDR, vpcCIDRFlag, net.IPNet{}, vpcCIDRFlagDescription) // TODO: use IPNetSliceVar when it is available (https://github.com/spf13/pflag/issues/273). - cmd.Flags().StringSliceVar(&vars.AdjustVPC.PublicSubnetCIDRs, publicSubnetCIDRsFlag, nil, publicSubnetCIDRsFlagDescription) - cmd.Flags().StringSliceVar(&vars.AdjustVPC.PrivateSubnetCIDRs, privateSubnetCIDRsFlag, nil, privateSubnetCIDRsFlagDescription) - cmd.Flags().BoolVar(&vars.DefaultConfig, defaultConfigFlag, false, defaultConfigFlagDescription) + cmd.Flags().StringSliceVar(&vars.adjustVPC.PublicSubnetCIDRs, publicSubnetCIDRsFlag, nil, publicSubnetCIDRsFlagDescription) + cmd.Flags().StringSliceVar(&vars.adjustVPC.PrivateSubnetCIDRs, privateSubnetCIDRsFlag, nil, privateSubnetCIDRsFlagDescription) + cmd.Flags().BoolVar(&vars.defaultConfig, defaultConfigFlag, false, defaultConfigFlagDescription) flags := pflag.NewFlagSet("Common", pflag.ContinueOnError) + flags.AddFlag(cmd.Flags().Lookup(appFlag)) flags.AddFlag(cmd.Flags().Lookup(nameFlag)) flags.AddFlag(cmd.Flags().Lookup(profileFlag)) flags.AddFlag(cmd.Flags().Lookup(accessKeyIDFlag)) diff --git a/internal/pkg/cli/env_init_test.go b/internal/pkg/cli/env_init_test.go index 2652bab656e..1794deb7105 100644 --- a/internal/pkg/cli/env_init_test.go +++ b/internal/pkg/cli/env_init_test.go @@ -117,19 +117,19 @@ func TestInitEnvOpts_Validate(t *testing.T) { // GIVEN opts := &initEnvOpts{ initEnvVars: initEnvVars{ - Name: tc.inEnvName, - DefaultConfig: tc.inDefault, - AdjustVPC: adjustVPCVars{ + name: tc.inEnvName, + defaultConfig: tc.inDefault, + adjustVPC: adjustVPCVars{ PublicSubnetCIDRs: tc.inPublicCIDRs, CIDR: tc.inVPCCIDR, }, - ImportVPC: importVPCVars{ + importVPC: importVPCVars{ PublicSubnetIDs: tc.inPublicIDs, ID: tc.inVPCID, }, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, - Profile: tc.inProfileName, - TempCreds: tempCredsVars{ + appName: tc.inAppName, + profile: tc.inProfileName, + tempCreds: tempCredsVars{ AccessKeyID: tc.inAccessKeyID, SecretAccessKey: tc.inSecretAccessKey, SessionToken: tc.inSessionToken, @@ -456,21 +456,19 @@ func TestInitEnvOpts_Ask(t *testing.T) { // GIVEN addEnv := &initEnvOpts{ initEnvVars: initEnvVars{ - Name: tc.inEnv, - Profile: tc.inProfile, - TempCreds: tc.inTempCreds, - Region: tc.inRegion, - DefaultConfig: tc.inDefault, - AdjustVPC: tc.inAdjustVPCVars, - ImportVPC: tc.inImportVPCVars, - GlobalOpts: &GlobalOpts{ - prompt: mocks.prompt, - }, + name: tc.inEnv, + profile: tc.inProfile, + tempCreds: tc.inTempCreds, + region: tc.inRegion, + defaultConfig: tc.inDefault, + adjustVPC: tc.inAdjustVPCVars, + importVPC: tc.inImportVPCVars, }, sessProvider: mocks.sessProvider, selVPC: mocks.selVPC, selCreds: mocks.selCreds, ec2Client: mocks.ec2Client, + prompt: mocks.prompt, } // WHEN @@ -479,7 +477,7 @@ func TestInitEnvOpts_Ask(t *testing.T) { // THEN if tc.wantedError == nil { require.NoError(t, err) - require.Equal(t, mockEnv, addEnv.Name, "expected environment names to match") + require.Equal(t, mockEnv, addEnv.name, "expected environment names to match") } else { require.EqualError(t, err, tc.wantedError.Error()) } @@ -908,9 +906,9 @@ func TestInitEnvOpts_Execute(t *testing.T) { opts := &initEnvOpts{ initEnvVars: initEnvVars{ - Name: tc.inEnvName, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, - IsProduction: tc.inProd, + name: tc.inEnvName, + appName: tc.inAppName, + isProduction: tc.inProd, }, store: mockstore, envDeployer: mockDeployer, @@ -1032,7 +1030,7 @@ func TestInitEnvOpts_delegateDNSFromApp(t *testing.T) { } opts := &initEnvOpts{ initEnvVars: initEnvVars{ - GlobalOpts: &GlobalOpts{appName: tc.app.Name}, + appName: tc.app.Name, }, envIdentity: mockIdentity, appDeployer: mockDeployer, diff --git a/internal/pkg/cli/env_list.go b/internal/pkg/cli/env_list.go index b993bcee1c3..f7eaa734cce 100644 --- a/internal/pkg/cli/env_list.go +++ b/internal/pkg/cli/env_list.go @@ -12,6 +12,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/config" "github.com/aws/copilot-cli/internal/pkg/term/color" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/spf13/cobra" ) @@ -22,14 +23,15 @@ const ( ) type listEnvVars struct { - *GlobalOpts - ShouldOutputJSON bool + appName string + shouldOutputJSON bool } type listEnvOpts struct { listEnvVars - store store - sel configSelector + store store + prompt prompter + sel configSelector w io.Writer } @@ -40,17 +42,19 @@ func newListEnvOpts(vars listEnvVars) (*listEnvOpts, error) { return nil, err } + prompter := prompt.New() return &listEnvOpts{ listEnvVars: vars, store: store, - sel: selector.NewConfigSelect(vars.prompt, store), + sel: selector.NewConfigSelect(prompter, store), + prompt: prompter, w: os.Stdout, }, nil } // Ask asks for fields that are required but not passed in. func (o *listEnvOpts) Ask() error { - if o.AppName() != "" { + if o.appName != "" { return nil } app, err := o.sel.Application(envListAppNamePrompt, envListAppNameHelper) @@ -64,17 +68,17 @@ func (o *listEnvOpts) Ask() error { // Execute lists the environments through the prompt. func (o *listEnvOpts) Execute() error { // Ensure the application actually exists before we try to list its environments. - if _, err := o.store.GetApplication(o.AppName()); err != nil { + if _, err := o.store.GetApplication(o.appName); err != nil { return err } - envs, err := o.store.ListEnvironments(o.AppName()) + envs, err := o.store.ListEnvironments(o.appName) if err != nil { return err } var out string - if o.ShouldOutputJSON { + if o.shouldOutputJSON { data, err := o.jsonOutput(envs) if err != nil { return err @@ -111,11 +115,9 @@ func (o *listEnvOpts) jsonOutput(envs []*config.Environment) (string, error) { return fmt.Sprintf("%s\n", b), nil } -// BuildEnvListCmd builds the command for listing environments in an application. -func BuildEnvListCmd() *cobra.Command { - vars := listEnvVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildEnvListCmd builds the command for listing environments in an application. +func buildEnvListCmd() *cobra.Command { + vars := listEnvVars{} cmd := &cobra.Command{ Use: "ls", Short: "Lists all the environments in an application.", @@ -133,6 +135,7 @@ func BuildEnvListCmd() *cobra.Command { return opts.Execute() }), } - cmd.Flags().BoolVar(&vars.ShouldOutputJSON, jsonFlag, false, jsonFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().BoolVar(&vars.shouldOutputJSON, jsonFlag, false, jsonFlagDescription) return cmd } diff --git a/internal/pkg/cli/env_list_test.go b/internal/pkg/cli/env_list_test.go index 20c2c745bdb..ec374b2a912 100644 --- a/internal/pkg/cli/env_list_test.go +++ b/internal/pkg/cli/env_list_test.go @@ -54,9 +54,7 @@ func TestEnvList_Ask(t *testing.T) { listEnvs := &listEnvOpts{ listEnvVars: listEnvVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inputApp, - }, + appName: tc.inputApp, }, sel: mockSelector, } @@ -67,7 +65,7 @@ func TestEnvList_Ask(t *testing.T) { require.EqualError(t, err, tc.wantedErr.Error()) } else { require.NoError(t, err) - require.Equal(t, tc.wantedApp, listEnvs.AppName(), "expected app names to match") + require.Equal(t, tc.wantedApp, listEnvs.appName, "expected app names to match") } }) } @@ -87,10 +85,8 @@ func TestEnvList_Execute(t *testing.T) { "with json envs": { listOpts: listEnvOpts{ listEnvVars: listEnvVars{ - ShouldOutputJSON: true, - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + shouldOutputJSON: true, + appName: "coolapp", }, store: mockstore, }, @@ -111,9 +107,7 @@ func TestEnvList_Execute(t *testing.T) { "with envs": { listOpts: listEnvOpts{ listEnvVars: listEnvVars{ - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + appName: "coolapp", }, store: mockstore, }, @@ -135,9 +129,7 @@ func TestEnvList_Execute(t *testing.T) { expectedErr: mockError, listOpts: listEnvOpts{ listEnvVars: listEnvVars{ - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + appName: "coolapp", }, store: mockstore, }, @@ -156,9 +148,7 @@ func TestEnvList_Execute(t *testing.T) { expectedErr: mockError, listOpts: listEnvOpts{ listEnvVars: listEnvVars{ - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + appName: "coolapp", }, store: mockstore, }, @@ -176,9 +166,7 @@ func TestEnvList_Execute(t *testing.T) { "with production envs": { listOpts: listEnvOpts{ listEnvVars: listEnvVars{ - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + appName: "coolapp", }, store: mockstore, }, diff --git a/internal/pkg/cli/env_show.go b/internal/pkg/cli/env_show.go index a6f86198c66..a83ae08e4d5 100644 --- a/internal/pkg/cli/env_show.go +++ b/internal/pkg/cli/env_show.go @@ -12,6 +12,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/describe" "github.com/aws/copilot-cli/internal/pkg/term/color" "github.com/aws/copilot-cli/internal/pkg/term/log" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/spf13/cobra" ) @@ -24,10 +25,10 @@ const ( ) type showEnvVars struct { - *GlobalOpts + appName string + name string shouldOutputJSON bool shouldOutputResources bool - envName string } type showEnvOpts struct { @@ -54,18 +55,18 @@ func newShowEnvOpts(vars showEnvVars) (*showEnvOpts, error) { showEnvVars: vars, store: configStore, w: log.OutputWriter, - sel: selector.NewConfigSelect(vars.prompt, configStore), + sel: selector.NewConfigSelect(prompt.New(), configStore), } opts.initEnvDescriber = func() error { d, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ - App: opts.AppName(), - Env: opts.envName, + App: opts.appName, + Env: opts.name, ConfigStore: configStore, DeployStore: deployStore, EnableResources: opts.shouldOutputResources, }) if err != nil { - return fmt.Errorf("creating describer for environment %s in application %s: %w", opts.envName, opts.AppName(), err) + return fmt.Errorf("creating describer for environment %s in application %s: %w", opts.name, opts.appName, err) } opts.describer = d return nil @@ -75,13 +76,13 @@ func newShowEnvOpts(vars showEnvVars) (*showEnvOpts, error) { // Validate returns an error if the values provided by the user are invalid. func (o *showEnvOpts) Validate() error { - if o.AppName() != "" { - if _, err := o.store.GetApplication(o.AppName()); err != nil { + if o.appName != "" { + if _, err := o.store.GetApplication(o.appName); err != nil { return err } } - if o.envName != "" { - if _, err := o.store.GetEnvironment(o.AppName(), o.envName); err != nil { + if o.name != "" { + if _, err := o.store.GetEnvironment(o.appName, o.name); err != nil { return err } } @@ -104,7 +105,7 @@ func (o *showEnvOpts) Execute() error { } env, err := o.describer.Describe() if err != nil { - return fmt.Errorf("describe environment %s: %w", o.envName, err) + return fmt.Errorf("describe environment %s: %w", o.name, err) } if o.shouldOutputJSON { data, err := env.JSONString() @@ -120,7 +121,7 @@ func (o *showEnvOpts) Execute() error { } func (o *showEnvOpts) askApp() error { - if o.AppName() != "" { + if o.appName != "" { return nil } app, err := o.sel.Application(envShowAppNamePrompt, envShowAppNameHelpPrompt) @@ -133,23 +134,21 @@ func (o *showEnvOpts) askApp() error { func (o *showEnvOpts) askEnvName() error { //return if env name is set by flag - if o.envName != "" { + if o.name != "" { return nil } - env, err := o.sel.Environment(fmt.Sprintf(envShowNamePrompt, color.HighlightUserInput(o.AppName())), envShowHelpPrompt, o.AppName()) + env, err := o.sel.Environment(fmt.Sprintf(envShowNamePrompt, color.HighlightUserInput(o.appName)), envShowHelpPrompt, o.appName) if err != nil { - return fmt.Errorf("select environment for application %s: %w", o.AppName(), err) + return fmt.Errorf("select environment for application %s: %w", o.appName, err) } - o.envName = env + o.name = env return nil } -// BuildEnvShowCmd builds the command for showing environments in an application. -func BuildEnvShowCmd() *cobra.Command { - vars := showEnvVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildEnvShowCmd builds the command for showing environments in an application. +func buildEnvShowCmd() *cobra.Command { + vars := showEnvVars{} cmd := &cobra.Command{ Use: "show", Short: "Shows info about a deployed environment.", @@ -172,8 +171,8 @@ func BuildEnvShowCmd() *cobra.Command { return opts.Execute() }), } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) - cmd.Flags().StringVarP(&vars.envName, nameFlag, nameFlagShort, "", envFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", envFlagDescription) cmd.Flags().BoolVar(&vars.shouldOutputJSON, jsonFlag, false, jsonFlagDescription) cmd.Flags().BoolVar(&vars.shouldOutputResources, resourcesFlag, false, envResourcesFlagDescription) return cmd diff --git a/internal/pkg/cli/env_show_test.go b/internal/pkg/cli/env_show_test.go index 87a4394de90..4968d6d48eb 100644 --- a/internal/pkg/cli/env_show_test.go +++ b/internal/pkg/cli/env_show_test.go @@ -92,10 +92,8 @@ func TestEnvShow_Validate(t *testing.T) { showEnvs := &showEnvOpts{ showEnvVars: showEnvVars{ - envName: tc.inputEnvironment, - GlobalOpts: &GlobalOpts{ - appName: tc.inputApp, - }, + name: tc.inputEnvironment, + appName: tc.inputApp, }, store: mockStoreReader, } @@ -185,10 +183,8 @@ func TestEnvShow_Ask(t *testing.T) { showEnvs := &showEnvOpts{ showEnvVars: showEnvVars{ - envName: tc.inputEnv, - GlobalOpts: &GlobalOpts{ - appName: tc.inputApp, - }, + name: tc.inputEnv, + appName: tc.inputApp, }, sel: mockSelector, } @@ -199,8 +195,8 @@ func TestEnvShow_Ask(t *testing.T) { require.EqualError(t, err, tc.wantedError.Error()) } else { require.NoError(t, err) - require.Equal(t, tc.wantedApp, showEnvs.AppName(), "expected app name to match") - require.Equal(t, tc.wantedEnv, showEnvs.envName, "expected environment name to match") + require.Equal(t, tc.wantedApp, showEnvs.appName, "expected app name to match") + require.Equal(t, tc.wantedEnv, showEnvs.name, "expected environment name to match") } }) } @@ -321,7 +317,7 @@ func TestEnvShow_Execute(t *testing.T) { showEnvs := &showEnvOpts{ showEnvVars: showEnvVars{ - envName: tc.inputEnv, + name: tc.inputEnv, shouldOutputJSON: tc.shouldOutputJSON, }, store: mockStoreReader, diff --git a/internal/pkg/cli/env_upgrade.go b/internal/pkg/cli/env_upgrade.go index e15ba82382e..36ed5512be1 100644 --- a/internal/pkg/cli/env_upgrade.go +++ b/internal/pkg/cli/env_upgrade.go @@ -4,17 +4,14 @@ package cli import ( - "fmt" - "github.com/spf13/cobra" ) // envUpgradeVars holds flag values. type envUpgradeVars struct { - *GlobalOpts - - name string // Required. Name of the environment. - all bool // True means all environments should be upgraded. + appName string // Required. Name of the application. + name string // Required. Name of the environment. + all bool // True means all environments should be upgraded. } // envUpgradeOpts represents the env upgrade command and holds the necessary data @@ -42,15 +39,12 @@ func (o *envUpgradeVars) Ask() error { // Execute updates the cloudformation stack an environment to the specified version. // If the environment stack is busy updating, it spins and waits until the stack can be updated. func (o *envUpgradeOpts) Execute() error { - fmt.Printf("app name is %s\n", o.AppName()) return nil } -// BuildEnvUpgradeCmd builds the command to update the -func BuildEnvUpgradeCmd() *cobra.Command { - vars := envUpgradeVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildEnvUpgradeCmd builds the command to update the +func buildEnvUpgradeCmd() *cobra.Command { + vars := envUpgradeVars{} cmd := &cobra.Command{ Use: "upgrade", Short: "Upgrades the template of an environment to the latest version.", @@ -67,6 +61,7 @@ func BuildEnvUpgradeCmd() *cobra.Command { }), } cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", envFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) cmd.Flags().BoolVar(&vars.all, allFlag, false, upgradeAllEnvsDescription) return cmd } diff --git a/internal/pkg/cli/init.go b/internal/pkg/cli/init.go index 696f3394e7a..7089fc9ab63 100644 --- a/internal/pkg/cli/init.go +++ b/internal/pkg/cli/init.go @@ -24,7 +24,6 @@ import ( "github.com/aws/copilot-cli/internal/pkg/workspace" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/spf13/viper" ) const ( @@ -94,7 +93,7 @@ func newInitOpts(vars initVars) (*initOpts, error) { initAppCmd := &initAppOpts{ initAppVars: initAppVars{ - AppName: vars.appName, + name: vars.appName, }, store: ssm, ws: ws, @@ -105,30 +104,32 @@ func newInitOpts(vars initVars) (*initOpts, error) { } initSvcCmd := &initSvcOpts{ initSvcVars: initSvcVars{ - ServiceType: vars.svcType, - Name: vars.svcName, - DockerfilePath: vars.dockerfilePath, - Port: vars.port, - GlobalOpts: NewGlobalOpts(), + serviceType: vars.svcType, + name: vars.svcName, + dockerfilePath: vars.dockerfilePath, + port: vars.port, + appName: vars.appName, }, fs: &afero.Afero{Fs: afero.NewOsFs()}, ws: ws, store: ssm, appDeployer: deployer, prog: spin, + prompt: prompt, setupParser: func(o *initSvcOpts) { - o.df = dockerfile.New(o.fs, o.DockerfilePath) + o.df = dockerfile.New(o.fs, o.dockerfilePath) }, } initEnvCmd := &initEnvOpts{ initEnvVars: initEnvVars{ - GlobalOpts: NewGlobalOpts(), - Name: defaultEnvironmentName, - IsProduction: false, + appName: vars.appName, + name: defaultEnvironmentName, + isProduction: false, }, store: ssm, appDeployer: deployer, prog: spin, + prompt: prompt, identity: id, sess: defaultSess, @@ -136,12 +137,13 @@ func newInitOpts(vars initVars) (*initOpts, error) { deploySvcCmd := &deploySvcOpts{ deploySvcVars: deploySvcVars{ - EnvName: defaultEnvironmentName, - ImageTag: vars.imageTag, - GlobalOpts: NewGlobalOpts(), + envName: defaultEnvironmentName, + imageTag: vars.imageTag, + appName: vars.appName, }, store: ssm, + prompt: prompt, ws: ws, unmarshal: manifest.UnmarshalWorkload, sel: selector.NewWorkspaceSelect(prompt, ssm, ws), @@ -158,11 +160,11 @@ func newInitOpts(vars initVars) (*initOpts, error) { initEnvCmd: initEnvCmd, deploySvcCmd: deploySvcCmd, - appName: &initAppCmd.AppName, - svcType: &initSvcCmd.ServiceType, - svcName: &initSvcCmd.Name, - svcPort: &initSvcCmd.Port, - dockerfilePath: &initSvcCmd.DockerfilePath, + appName: &initAppCmd.name, + svcType: &initSvcCmd.serviceType, + svcName: &initSvcCmd.name, + svcPort: &initSvcCmd.port, + dockerfilePath: &initSvcCmd.dockerfilePath, prompt: prompt, }, nil @@ -209,12 +211,15 @@ func (o *initOpts) loadApp() error { if err := o.initAppCmd.Validate(); err != nil { return err } - // Write the application name to viper so that sub-commands can retrieve its value. - viper.Set(appFlag, o.appName) return nil } func (o *initOpts) loadSvc() error { + if initSvcOpts, ok := o.initSvcCmd.(*initSvcOpts); ok { + // Set the application name from app init to the service init command. + initSvcOpts.appName = *o.appName + } + if err := o.initSvcCmd.Ask(); err != nil { return fmt.Errorf("ask svc init: %w", err) } @@ -233,6 +238,10 @@ func (o *initOpts) deployEnv() error { // User chose not to deploy the service, exit. return nil } + if initEnvCmd, ok := o.initEnvCmd.(*initEnvOpts); ok { + // Set the application name from app init to the env init command. + initEnvCmd.appName = *o.appName + } log.Infoln() return o.initEnvCmd.Execute() @@ -243,8 +252,9 @@ func (o *initOpts) deploySvc() error { return nil } if deployOpts, ok := o.deploySvcCmd.(*deploySvcOpts); ok { - // Set the service's name to the deploy sub-command. - deployOpts.Name = *o.svcName + // Set the service's name and app name to the deploy sub-command. + deployOpts.name = *o.svcName + deployOpts.appName = *o.appName } if err := o.deploySvcCmd.Ask(); err != nil { @@ -289,7 +299,7 @@ func BuildInitCmd() *cobra.Command { return nil }), } - cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, "", appFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) cmd.Flags().StringVarP(&vars.svcName, svcFlag, svcFlagShort, "", svcFlagDescription) cmd.Flags().StringVarP(&vars.svcType, svcTypeFlag, svcTypeFlagShort, "", svcTypeFlagDescription) cmd.Flags().StringVarP(&vars.dockerfilePath, dockerFileFlag, dockerFileFlagShort, "", dockerFileFlagDescription) diff --git a/internal/pkg/cli/job.go b/internal/pkg/cli/job.go index 7ab86070156..12aea6b77be 100644 --- a/internal/pkg/cli/job.go +++ b/internal/pkg/cli/job.go @@ -7,7 +7,6 @@ import ( "github.com/aws/copilot-cli/cmd/copilot/template" "github.com/aws/copilot-cli/internal/pkg/cli/group" "github.com/spf13/cobra" - "github.com/spf13/viper" ) // BuildJobCmd is the top level command for jobs. @@ -19,13 +18,10 @@ Jobs are Amazon ECS tasks which run on a fixed schedule.`, Long: `Commands for scheduled jobs. Jobs are Amazon ECS tasks which run on a fixed schedule.`, } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) - cmd.PersistentFlags().StringP(appFlag, appFlagShort, "" /* default */, appFlagDescription) - _ = viper.BindPFlag(appFlag, cmd.PersistentFlags().Lookup(appFlag)) // Ignore err because the flag name is not empty. - cmd.AddCommand(BuildJobInitCmd()) + cmd.AddCommand(buildJobInitCmd()) // cmd.AddCommand(BuildJobPackageCmd()) - cmd.AddCommand(BuildJobDeployCmd()) + cmd.AddCommand(buildJobDeployCmd()) cmd.SetUsageTemplate(template.Usage) diff --git a/internal/pkg/cli/job_deploy.go b/internal/pkg/cli/job_deploy.go index 4158ef7dbbc..ad7ebbe489d 100644 --- a/internal/pkg/cli/job_deploy.go +++ b/internal/pkg/cli/job_deploy.go @@ -13,17 +13,18 @@ import ( "github.com/aws/copilot-cli/internal/pkg/manifest" "github.com/aws/copilot-cli/internal/pkg/term/command" termprogress "github.com/aws/copilot-cli/internal/pkg/term/progress" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/aws/copilot-cli/internal/pkg/workspace" "github.com/spf13/cobra" ) type deployJobVars struct { - *GlobalOpts - Name string - EnvName string - ImageTag string - ResourceTags map[string]string + appName string + name string + envName string + imageTag string + resourceTags map[string]string } type deployJobOpts struct { @@ -56,7 +57,7 @@ func newJobDeployOpts(vars deployJobVars) (*deployJobOpts, error) { ws: ws, unmarshal: manifest.UnmarshalWorkload, spinner: termprogress.NewSpinner(), - sel: selector.NewWorkspaceSelect(vars.prompt, store, ws), + sel: selector.NewWorkspaceSelect(prompt.New(), store, ws), cmd: command.New(), sessProvider: sessions.NewProvider(), }, nil @@ -64,15 +65,15 @@ func newJobDeployOpts(vars deployJobVars) (*deployJobOpts, error) { // Validate returns an error if the user inputs are invalid. func (o *deployJobOpts) Validate() error { - if o.AppName() == "" { + if o.appName == "" { return errNoAppInWorkspace } - if o.Name != "" { + if o.name != "" { if err := o.validateJobName(); err != nil { return err } } - if o.EnvName != "" { + if o.envName != "" { if err := o.validateEnvName(); err != nil { return err } @@ -101,26 +102,24 @@ func (o *deployJobOpts) validateJobName() error { return fmt.Errorf("list jobs in the workspace: %w", err) } for _, name := range names { - if o.Name == name { + if o.name == name { return nil } } - return fmt.Errorf("job %s not found in the workspace", color.HighlightUserInput(o.Name)) + return fmt.Errorf("job %s not found in the workspace", color.HighlightUserInput(o.name)) } func (o *deployJobOpts) validateEnvName() error { - _, err := o.store.GetEnvironment(o.AppName(), o.EnvName) + _, err := o.store.GetEnvironment(o.appName, o.envName) if err != nil { - return fmt.Errorf("get environment %s configuration: %w", o.EnvName, err) + return fmt.Errorf("get environment %s configuration: %w", o.envName, err) } return nil } -// BuildJobDeployCmd builds the `job deploy` subcommand. -func BuildJobDeployCmd() *cobra.Command { - vars := deployJobVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildJobDeployCmd builds the `job deploy` subcommand. +func buildJobDeployCmd() *cobra.Command { + vars := deployJobVars{} cmd := &cobra.Command{ Use: "deploy", Short: "Deploys a job to an environment.", @@ -147,10 +146,11 @@ func BuildJobDeployCmd() *cobra.Command { return nil }), } - cmd.Flags().StringVarP(&vars.Name, nameFlag, nameFlagShort, "", jobFlagDescription) - cmd.Flags().StringVarP(&vars.EnvName, envFlag, envFlagShort, "", envFlagDescription) - cmd.Flags().StringVar(&vars.ImageTag, imageTagFlag, "", imageTagFlagDescription) - cmd.Flags().StringToStringVar(&vars.ResourceTags, resourceTagsFlag, nil, resourceTagsFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", jobFlagDescription) + cmd.Flags().StringVarP(&vars.envName, envFlag, envFlagShort, "", envFlagDescription) + cmd.Flags().StringVar(&vars.imageTag, imageTagFlag, "", imageTagFlagDescription) + cmd.Flags().StringToStringVar(&vars.resourceTags, resourceTagsFlag, nil, resourceTagsFlagDescription) return cmd } diff --git a/internal/pkg/cli/job_deploy_test.go b/internal/pkg/cli/job_deploy_test.go index 4249972dc7f..c17a6fc27f2 100644 --- a/internal/pkg/cli/job_deploy_test.go +++ b/internal/pkg/cli/job_deploy_test.go @@ -87,11 +87,9 @@ func TestJobDeployOpts_Validate(t *testing.T) { tc.mockStore(mockStore) opts := deployJobOpts{ deployJobVars: deployJobVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - }, - Name: tc.inJobName, - EnvName: tc.inEnvName, + appName: tc.inAppName, + name: tc.inJobName, + envName: tc.inEnvName, }, ws: mockWs, store: mockStore, diff --git a/internal/pkg/cli/job_init.go b/internal/pkg/cli/job_init.go index 87dc479bde1..fa937d6787a 100644 --- a/internal/pkg/cli/job_init.go +++ b/internal/pkg/cli/job_init.go @@ -80,13 +80,13 @@ var presetSchedules = []string{ } type initJobVars struct { - *GlobalOpts - Name string - DockerfilePath string - Timeout string - Retries int - Schedule string - JobType string + appName string + name string + dockerfilePath string + timeout string + retries int + schedule string + jobType string } type initJobOpts struct { @@ -98,6 +98,7 @@ type initJobOpts struct { store store appDeployer appDeployer prog progress + prompt prompter // Outputs stored on successful actions. manifestPath string @@ -128,37 +129,38 @@ func newInitJobOpts(vars initJobVars) (*initJobOpts, error) { ws: ws, appDeployer: cloudformation.New(sess), prog: termprogress.NewSpinner(), + prompt: prompt.New(), }, nil } // Validate returns an error if the flag values passed by the user are invalid. func (o *initJobOpts) Validate() error { - if o.JobType != "" { - if err := validateJobType(o.JobType); err != nil { + if o.jobType != "" { + if err := validateJobType(o.jobType); err != nil { return err } } - if o.Name != "" { - if err := validateJobName(o.Name); err != nil { + if o.name != "" { + if err := validateJobName(o.name); err != nil { return err } } - if o.DockerfilePath != "" { - if _, err := o.fs.Stat(o.DockerfilePath); err != nil { + if o.dockerfilePath != "" { + if _, err := o.fs.Stat(o.dockerfilePath); err != nil { return err } } - if o.Schedule != "" { - if err := validateSchedule(o.Schedule); err != nil { + if o.schedule != "" { + if err := validateSchedule(o.schedule); err != nil { return err } } - if o.Timeout != "" { - if err := validateTimeout(o.Timeout); err != nil { + if o.timeout != "" { + if err := validateTimeout(o.timeout); err != nil { return err } } - if o.Retries < 0 { + if o.retries < 0 { return errors.New("number of retries must be non-negative") } return nil @@ -187,46 +189,46 @@ func (o *initJobOpts) Execute() error { } func (o *initJobOpts) askJobType() error { - if o.JobType != "" { + if o.jobType != "" { return nil } // short circuit since there's only one valid job type. - o.JobType = manifest.ScheduledJobType + o.jobType = manifest.ScheduledJobType return nil } func (o *initJobOpts) askJobName() error { - if o.Name != "" { + if o.name != "" { return nil } name, err := o.prompt.Get( - fmt.Sprintf(fmtWkldInitNamePrompt, color.Emphasize("name"), color.HighlightUserInput(o.JobType)), - fmt.Sprintf(fmtWkldInitNameHelpPrompt, job, o.AppName()), + fmt.Sprintf(fmtWkldInitNamePrompt, color.Emphasize("name"), color.HighlightUserInput(o.jobType)), + fmt.Sprintf(fmtWkldInitNameHelpPrompt, job, o.appName), validateSvcName, prompt.WithFinalMessage("Job name:"), ) if err != nil { return fmt.Errorf("get job name: %w", err) } - o.Name = name + o.name = name return nil } func (o *initJobOpts) askDockerfile() error { - if o.DockerfilePath != "" { + if o.dockerfilePath != "" { return nil } - df, err := askDockerfile(o.Name, o.fs, o.prompt) + df, err := askDockerfile(o.name, o.fs, o.prompt) if err != nil { return err } - o.DockerfilePath = df + o.dockerfilePath = df return nil } func (o *initJobOpts) askSchedule() error { - if o.Schedule != "" { + if o.schedule != "" { return nil } scheduleType, err := o.prompt.SelectOne( @@ -258,7 +260,7 @@ func (o *initJobOpts) askRate() error { if err != nil { return fmt.Errorf("get schedule rate: %w", err) } - o.Schedule = fmt.Sprintf("@every %s", rateInput) + o.schedule = fmt.Sprintf("@every %s", rateInput) return nil } @@ -273,7 +275,7 @@ func (o *initJobOpts) askCron() error { return fmt.Errorf("get preset schedule: %w", err) } if cronInput != custom { - o.Schedule = getPresetSchedule(cronInput) + o.schedule = getPresetSchedule(cronInput) return nil } var customSchedule, humanCron string @@ -317,7 +319,7 @@ func (o *initJobOpts) askCron() error { } } - o.Schedule = customSchedule + o.schedule = customSchedule return nil } @@ -330,16 +332,14 @@ func (o *initJobOpts) RecommendedActions() []string { return []string{ fmt.Sprintf("Update your manifest %s to change the defaults.", color.HighlightResource(o.manifestPath)), fmt.Sprintf("Run %s to deploy your job to a %s environment.", - color.HighlightCode(fmt.Sprintf("copilot job deploy --name %s --env %s", o.Name, defaultEnvironmentName)), + color.HighlightCode(fmt.Sprintf("copilot job deploy --name %s --env %s", o.name, defaultEnvironmentName)), defaultEnvironmentName), } } -// BuildJobInitCmd builds the command for creating a new job. -func BuildJobInitCmd() *cobra.Command { - vars := initJobVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildJobInitCmd builds the command for creating a new job. +func buildJobInitCmd() *cobra.Command { + vars := initJobVars{} cmd := &cobra.Command{ Use: "init", Short: "Creates a new scheduled job in an application.", @@ -370,12 +370,13 @@ func BuildJobInitCmd() *cobra.Command { return nil }), } - cmd.Flags().StringVarP(&vars.Name, nameFlag, nameFlagShort, "", jobFlagDescription) - cmd.Flags().StringVarP(&vars.JobType, jobTypeFlag, jobTypeFlagShort, "", jobTypeFlagDescription) - cmd.Flags().StringVarP(&vars.DockerfilePath, dockerFileFlag, dockerFileFlagShort, "", dockerFileFlagDescription) - cmd.Flags().StringVarP(&vars.Schedule, scheduleFlag, scheduleFlagShort, "", scheduleFlagDescription) - cmd.Flags().StringVar(&vars.Timeout, timeoutFlag, "", timeoutFlagDescription) - cmd.Flags().IntVar(&vars.Retries, retriesFlag, 0, retriesFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", jobFlagDescription) + cmd.Flags().StringVarP(&vars.jobType, jobTypeFlag, jobTypeFlagShort, "", jobTypeFlagDescription) + cmd.Flags().StringVarP(&vars.dockerfilePath, dockerFileFlag, dockerFileFlagShort, "", dockerFileFlagDescription) + cmd.Flags().StringVarP(&vars.schedule, scheduleFlag, scheduleFlagShort, "", scheduleFlagDescription) + cmd.Flags().StringVar(&vars.timeout, timeoutFlag, "", timeoutFlagDescription) + cmd.Flags().IntVar(&vars.retries, retriesFlag, 0, retriesFlagDescription) cmd.Annotations = map[string]string{ "group": group.Develop, diff --git a/internal/pkg/cli/job_init_test.go b/internal/pkg/cli/job_init_test.go index 2b0c06aa64b..dd884e64c1f 100644 --- a/internal/pkg/cli/job_init_test.go +++ b/internal/pkg/cli/job_init_test.go @@ -105,12 +105,11 @@ func TestJobInitOpts_Validate(t *testing.T) { t.Run(name, func(t *testing.T) { opts := initJobOpts{ initJobVars: initJobVars{ - Name: tc.inJobName, - DockerfilePath: tc.inDockerfilePath, - Timeout: tc.inTimeout, - Retries: tc.inRetries, - Schedule: tc.inSchedule, - GlobalOpts: &GlobalOpts{}, + name: tc.inJobName, + dockerfilePath: tc.inDockerfilePath, + timeout: tc.inTimeout, + retries: tc.inRetries, + schedule: tc.inSchedule, }, fs: &afero.Afero{Fs: afero.NewMemMapFs()}, } @@ -421,15 +420,13 @@ func TestJobInitOpts_Ask(t *testing.T) { mockPrompt := mocks.NewMockprompter(ctrl) opts := &initJobOpts{ initJobVars: initJobVars{ - JobType: tc.inJobType, - Name: tc.inJobName, - DockerfilePath: tc.inDockerfilePath, - Schedule: tc.inJobSchedule, - GlobalOpts: &GlobalOpts{ - prompt: mockPrompt, - }, + jobType: tc.inJobType, + name: tc.inJobName, + dockerfilePath: tc.inDockerfilePath, + schedule: tc.inJobSchedule, }, - fs: &afero.Afero{Fs: afero.NewMemMapFs()}, + fs: &afero.Afero{Fs: afero.NewMemMapFs()}, + prompt: mockPrompt, } tc.mockFileSystem(opts.fs) tc.mockPrompt(mockPrompt) @@ -443,10 +440,10 @@ func TestJobInitOpts_Ask(t *testing.T) { return } require.NoError(t, err) - require.Equal(t, wantedJobType, opts.JobType) - require.Equal(t, wantedJobName, opts.Name) - require.Equal(t, wantedDockerfilePath, opts.DockerfilePath) - require.Equal(t, tc.wantedSchedule, opts.Schedule) + require.Equal(t, wantedJobType, opts.jobType) + require.Equal(t, wantedJobName, opts.name) + require.Equal(t, wantedDockerfilePath, opts.dockerfilePath) + require.Equal(t, tc.wantedSchedule, opts.schedule) }) } diff --git a/internal/pkg/cli/pipeline.go b/internal/pkg/cli/pipeline.go index 9352b875f6a..fb308dbd508 100644 --- a/internal/pkg/cli/pipeline.go +++ b/internal/pkg/cli/pipeline.go @@ -19,11 +19,11 @@ Continuous delivery pipelines to release services.`, Continuous delivery pipelines to release services.`, } - cmd.AddCommand(BuildPipelineInitCmd()) - cmd.AddCommand(BuildPipelineUpdateCmd()) - cmd.AddCommand(BuildPipelineDeleteCmd()) - cmd.AddCommand(BuildPipelineShowCmd()) - cmd.AddCommand(BuildPipelineStatusCmd()) + cmd.AddCommand(buildPipelineInitCmd()) + cmd.AddCommand(buildPipelineUpdateCmd()) + cmd.AddCommand(buildPipelineDeleteCmd()) + cmd.AddCommand(buildPipelineShowCmd()) + cmd.AddCommand(buildPipelineStatusCmd()) cmd.SetUsageTemplate(template.Usage) cmd.Annotations = map[string]string{ diff --git a/internal/pkg/cli/pipeline_delete.go b/internal/pkg/cli/pipeline_delete.go index 6a79da71782..ec8b5bae4cf 100644 --- a/internal/pkg/cli/pipeline_delete.go +++ b/internal/pkg/cli/pipeline_delete.go @@ -13,6 +13,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/manifest" "github.com/aws/copilot-cli/internal/pkg/term/log" termprogress "github.com/aws/copilot-cli/internal/pkg/term/progress" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/workspace" "github.com/spf13/cobra" @@ -34,9 +35,9 @@ var ( ) type deletePipelineVars struct { - *GlobalOpts - SkipConfirmation bool - DeleteSecret bool + appName string + skipConfirmation bool + shouldDeleteSecret bool } type deletePipelineOpts struct { @@ -48,6 +49,7 @@ type deletePipelineOpts struct { // Interfaces to dependencies pipelineDeployer pipelineDeployer prog progress + prompt prompter secretsmanager secretsManager ws wsPipelineReader } @@ -71,6 +73,7 @@ func newDeletePipelineOpts(vars deletePipelineVars) (*deletePipelineOpts, error) opts := &deletePipelineOpts{ deletePipelineVars: vars, prog: termprogress.NewSpinner(), + prompt: prompt.New(), secretsmanager: secretsmanager, pipelineDeployer: cloudformation.New(defaultSess), ws: ws, @@ -81,7 +84,7 @@ func newDeletePipelineOpts(vars deletePipelineVars) (*deletePipelineOpts, error) // Validate returns an error if the flag values passed by the user are invalid. func (o *deletePipelineOpts) Validate() error { - if o.AppName() == "" { + if o.appName == "" { return errNoAppInWorkspace } @@ -94,12 +97,12 @@ func (o *deletePipelineOpts) Validate() error { // Ask prompts for fields that are required but not passed in. func (o *deletePipelineOpts) Ask() error { - if o.SkipConfirmation { + if o.skipConfirmation { return nil } deleteConfirmed, err := o.prompt.Confirm( - fmt.Sprintf(pipelineDeleteConfirmPrompt, o.PipelineName, o.AppName()), + fmt.Sprintf(pipelineDeleteConfirmPrompt, o.PipelineName, o.appName), pipelineDeleteConfirmHelp) if err != nil { @@ -150,7 +153,7 @@ func (o *deletePipelineOpts) readPipelineManifest() error { } func (o *deletePipelineOpts) deleteSecret() error { - if !o.DeleteSecret { + if !o.shouldDeleteSecret { confirmDeletion, err := o.prompt.Confirm( fmt.Sprintf(pipelineSecretDeleteConfirmPrompt, o.PipelineSecret, o.PipelineName), pipelineDeleteSecretConfirmHelp, @@ -175,12 +178,12 @@ func (o *deletePipelineOpts) deleteSecret() error { } func (o *deletePipelineOpts) deleteStack() error { - o.prog.Start(fmt.Sprintf(fmtDeletePipelineStart, o.PipelineName, o.AppName())) + o.prog.Start(fmt.Sprintf(fmtDeletePipelineStart, o.PipelineName, o.appName)) if err := o.pipelineDeployer.DeletePipeline(o.PipelineName); err != nil { - o.prog.Stop(log.Serrorf(fmtDeletePipelineFailed, o.PipelineName, o.AppName(), err)) + o.prog.Stop(log.Serrorf(fmtDeletePipelineFailed, o.PipelineName, o.appName, err)) return err } - o.prog.Stop(log.Ssuccessf(fmtDeletePipelineComplete, o.PipelineName, o.AppName())) + o.prog.Stop(log.Ssuccessf(fmtDeletePipelineComplete, o.PipelineName, o.appName)) return nil } @@ -203,11 +206,9 @@ func (o *deletePipelineOpts) Run() error { return nil } -// BuildPipelineDeleteCmd build the command for deleting an existing pipeline. -func BuildPipelineDeleteCmd() *cobra.Command { - vars := deletePipelineVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildPipelineDeleteCmd build the command for deleting an existing pipeline. +func buildPipelineDeleteCmd() *cobra.Command { + vars := deletePipelineVars{} cmd := &cobra.Command{ Use: "delete", Short: "Deletes the pipeline associated with your workspace.", @@ -223,7 +224,8 @@ func BuildPipelineDeleteCmd() *cobra.Command { return opts.Run() }), } - cmd.Flags().BoolVar(&vars.SkipConfirmation, yesFlag, false, yesFlagDescription) - cmd.Flags().BoolVar(&vars.DeleteSecret, deleteSecretFlag, false, deleteSecretFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, yesFlagDescription) + cmd.Flags().BoolVar(&vars.shouldDeleteSecret, deleteSecretFlag, false, deleteSecretFlagDescription) return cmd } diff --git a/internal/pkg/cli/pipeline_delete_test.go b/internal/pkg/cli/pipeline_delete_test.go index 23db852f350..09e73713084 100644 --- a/internal/pkg/cli/pipeline_delete_test.go +++ b/internal/pkg/cli/pipeline_delete_test.go @@ -95,9 +95,7 @@ stages: opts := &deletePipelineOpts{ deletePipelineVars: deletePipelineVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - }, + appName: tc.inAppName, }, ws: mockWorkspace, } @@ -163,13 +161,11 @@ func TestDeletePipelineOpts_Ask(t *testing.T) { opts := &deletePipelineOpts{ deletePipelineVars: deletePipelineVars{ - SkipConfirmation: tc.skipConfirmation, - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - prompt: mockPrompt, - }, + skipConfirmation: tc.skipConfirmation, + appName: tc.inAppName, }, PipelineName: tc.inPipelineName, + prompt: mockPrompt, } // WHEN @@ -299,11 +295,8 @@ func TestDeletePipelineOpts_Execute(t *testing.T) { opts := &deletePipelineOpts{ deletePipelineVars: deletePipelineVars{ - DeleteSecret: tc.deleteSecret, - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - prompt: mockPrompter, - }, + shouldDeleteSecret: tc.deleteSecret, + appName: tc.inAppName, }, PipelineName: tc.inPipelineName, PipelineSecret: tc.inPipelineSecret, @@ -311,6 +304,7 @@ func TestDeletePipelineOpts_Execute(t *testing.T) { pipelineDeployer: mockDeployer, ws: mockWorkspace, prog: mockProg, + prompt: mockPrompter, } // WHEN diff --git a/internal/pkg/cli/pipeline_init.go b/internal/pkg/cli/pipeline_init.go index 489b1530b4c..04c47dc264e 100644 --- a/internal/pkg/cli/pipeline_init.go +++ b/internal/pkg/cli/pipeline_init.go @@ -19,6 +19,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/term/color" "github.com/aws/copilot-cli/internal/pkg/term/command" "github.com/aws/copilot-cli/internal/pkg/term/log" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/version" "github.com/aws/copilot-cli/internal/pkg/workspace" "github.com/spf13/afero" @@ -54,13 +55,13 @@ var ( var errNoEnvsInApp = errors.New("there were no more environments found that can be added to your pipeline. Please run `copilot env init` to create a new environment") type initPipelineVars struct { - Environments []string - GitHubOwner string - GitHubRepo string - GitHubURL string - GitHubAccessToken string - GitBranch string - *GlobalOpts + appName string + environments []string + githubOwner string + githubRepo string + githubURL string + githubAccessToken string + gitBranch string } type initPipelineOpts struct { @@ -72,6 +73,7 @@ type initPipelineOpts struct { runner runner cfnClient appResourcesGetter store store + prompt prompter // Outputs stored on successful actions. secretName string @@ -142,13 +144,14 @@ func newInitPipelineOpts(vars initPipelineVars) (*initPipelineOpts, error) { } opts.cfnClient = cloudformation.New(defaultSession) + opts.prompt = prompt.New() return opts, nil } // Validate returns an error if the flag values passed by the user are invalid. func (o *initPipelineOpts) Validate() error { // TODO add validation for flags - if o.AppName() == "" { + if o.appName == "" { return errNoAppInWorkspace } @@ -158,29 +161,29 @@ func (o *initPipelineOpts) Validate() error { // Ask prompts for fields that are required but not passed in. func (o *initPipelineOpts) Ask() error { var err error - if len(o.Environments) == 0 { + if len(o.environments) == 0 { if err = o.selectEnvironments(); err != nil { return err } } - if o.GitHubURL == "" { + if o.githubURL == "" { if err = o.selectGitHubURL(); err != nil { return err } } - if o.GitHubOwner, o.GitHubRepo, err = o.parseOwnerRepoName(o.GitHubURL); err != nil { + if o.githubOwner, o.githubRepo, err = o.parseOwnerRepoName(o.githubURL); err != nil { return err } - if o.GitHubAccessToken == "" { + if o.githubAccessToken == "" { if err = o.getGitHubAccessToken(); err != nil { return err } } - if o.GitBranch == "" { - o.GitBranch = masterBranch + if o.gitBranch == "" { + o.gitBranch = masterBranch } return nil } @@ -188,14 +191,14 @@ func (o *initPipelineOpts) Ask() error { // Execute writes the pipeline manifest file. func (o *initPipelineOpts) Execute() error { secretName := o.createSecretName() - _, err := o.secretsmanager.CreateSecret(secretName, o.GitHubAccessToken) + _, err := o.secretsmanager.CreateSecret(secretName, o.githubAccessToken) if err != nil { var existsErr *secretsmanager.ErrSecretAlreadyExists if !errors.As(err, &existsErr) { return err } - log.Successf("Secret already exists for %s! Do nothing.\n", color.HighlightUserInput(o.GitHubRepo)) + log.Successf("Secret already exists for %s! Do nothing.\n", color.HighlightUserInput(o.githubRepo)) } else { log.Successf("Created the secret %s for pipeline source stage!\n", color.HighlightUserInput(secretName)) } @@ -230,17 +233,17 @@ func (o *initPipelineOpts) RecommendedActions() []string { } func (o *initPipelineOpts) createSecretName() string { - return fmt.Sprintf("github-token-%s-%s", o.appName, o.GitHubRepo) + return fmt.Sprintf("github-token-%s-%s", o.appName, o.githubRepo) } func (o *initPipelineOpts) createPipelineName() string { - return fmt.Sprintf("pipeline-%s-%s-%s", o.appName, o.GitHubOwner, o.GitHubRepo) + return fmt.Sprintf("pipeline-%s-%s-%s", o.appName, o.githubOwner, o.githubRepo) } func (o *initPipelineOpts) createPipelineProvider() (manifest.Provider, error) { config := &manifest.GitHubProperties{ - OwnerAndRepository: "https://" + githubURL + "/" + o.GitHubOwner + "/" + o.GitHubRepo, - Branch: o.GitBranch, + OwnerAndRepository: "https://" + githubURL + "/" + o.githubOwner + "/" + o.githubRepo, + Branch: o.gitBranch, GithubSecretIdKeyName: o.secretName, } return manifest.NewProvider(config) @@ -252,7 +255,7 @@ func (o *initPipelineOpts) getEnvConfig(environmentName string) (*config.Environ return env, nil } } - return nil, fmt.Errorf("environment %s in application %s is not found", environmentName, o.AppName()) + return nil, fmt.Errorf("environment %s in application %s is not found", environmentName, o.appName) } func (o *initPipelineOpts) createPipelineManifest() error { @@ -263,7 +266,7 @@ func (o *initPipelineOpts) createPipelineManifest() error { } var stages []manifest.PipelineStage - for _, environmentName := range o.Environments { + for _, environmentName := range o.environments { env, err := o.getEnvConfig(environmentName) if err != nil { return err @@ -300,7 +303,7 @@ func (o *initPipelineOpts) createPipelineManifest() error { if manifestExists { manifestMsgFmt = "Pipeline manifest file for %s already exists at %s, skipping writing it.\n" } - log.Successf(manifestMsgFmt, color.HighlightUserInput(o.GitHubRepo), color.HighlightResource(manifestPath)) + log.Successf(manifestMsgFmt, color.HighlightUserInput(o.githubRepo), color.HighlightResource(manifestPath)) log.Infoln("The manifest contains configurations for your CodePipeline resources, such as your pipeline stages and build steps.") return nil } @@ -347,9 +350,9 @@ func (o *initPipelineOpts) createBuildspec() error { } func (o *initPipelineOpts) artifactBuckets() ([]artifactBucket, error) { - app, err := o.store.GetApplication(o.AppName()) + app, err := o.store.GetApplication(o.appName) if err != nil { - return nil, fmt.Errorf("get application %s: %w", o.AppName(), err) + return nil, fmt.Errorf("get application %s: %w", o.appName, err) } regionalResources, err := o.cfnClient.GetRegionalAppResources(app) if err != nil { @@ -378,7 +381,7 @@ func (o *initPipelineOpts) selectEnvironments() error { for { promptMsg := pipelineInitAddEnvPrompt promptHelpMsg := pipelineInitAddEnvHelpPrompt - if len(o.Environments) > 0 { + if len(o.environments) > 0 { promptMsg = pipelineInitAddMoreEnvPrompt promptHelpMsg = pipelineInitAddMoreEnvHelpPrompt } @@ -413,7 +416,7 @@ func (o *initPipelineOpts) listAvailableEnvironments() []string { } func (o *initPipelineOpts) envCanBeAdded(selectedEnv string) bool { - for _, env := range o.Environments { + for _, env := range o.environments { if selectedEnv == env { return false } @@ -425,7 +428,7 @@ func (o *initPipelineOpts) envCanBeAdded(selectedEnv string) bool { func (o *initPipelineOpts) selectEnvironment() error { envs := o.listAvailableEnvironments() - if len(envs) == 0 && len(o.Environments) != 0 { + if len(envs) == 0 && len(o.environments) != 0 { log.Infoln("There are no more environments to add.") return nil } @@ -440,7 +443,7 @@ func (o *initPipelineOpts) selectEnvironment() error { return err } - o.Environments = append(o.Environments, env) + o.environments = append(o.environments, env) return nil } @@ -454,7 +457,7 @@ func (o *initPipelineOpts) selectGitHubURL() error { if err != nil { return fmt.Errorf("select GitHub URL: %w", err) } - o.GitHubURL = url + o.githubURL = url return nil } @@ -495,7 +498,7 @@ func (o *initPipelineOpts) parseGitRemoteResult(s string) ([]string, error) { func (o *initPipelineOpts) getGitHubAccessToken() error { token, err := o.prompt.GetSecret( - fmt.Sprintf("Please enter your GitHub Personal Access Token for your repository %s:", color.HighlightUserInput(o.GitHubRepo)), + fmt.Sprintf("Please enter your GitHub Personal Access Token for your repository %s:", color.HighlightUserInput(o.githubRepo)), `The personal access token for the GitHub repository linked to your workspace. For more information, please refer to: https://git.io/JfDFD.`, ) @@ -503,14 +506,14 @@ For more information, please refer to: https://git.io/JfDFD.`, if err != nil { return fmt.Errorf("get GitHub access token: %w", err) } - o.GitHubAccessToken = token + o.githubAccessToken = token return nil } func (o *initPipelineOpts) getEnvs() ([]*config.Environment, error) { - envs, err := o.store.ListEnvironments(o.AppName()) + envs, err := o.store.ListEnvironments(o.appName) if err != nil { - return nil, fmt.Errorf("list environments for application %s: %w", o.AppName(), err) + return nil, fmt.Errorf("list environments for application %s: %w", o.appName, err) } if len(envs) == 0 { return nil, errNoEnvsInApp @@ -518,11 +521,9 @@ func (o *initPipelineOpts) getEnvs() ([]*config.Environment, error) { return envs, nil } -// BuildPipelineInitCmd build the command for creating a new pipeline. -func BuildPipelineInitCmd() *cobra.Command { - vars := initPipelineVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildPipelineInitCmd build the command for creating a new pipeline. +func buildPipelineInitCmd() *cobra.Command { + vars := initPipelineVars{} cmd := &cobra.Command{ Use: "init", Short: "Creates a pipeline for the services in your workspace.", @@ -555,10 +556,11 @@ func BuildPipelineInitCmd() *cobra.Command { return nil }), } - cmd.Flags().StringVarP(&vars.GitHubURL, githubURLFlag, githubURLFlagShort, "", githubURLFlagDescription) - cmd.Flags().StringVarP(&vars.GitHubAccessToken, githubAccessTokenFlag, githubAccessTokenFlagShort, "", githubAccessTokenFlagDescription) - cmd.Flags().StringVarP(&vars.GitBranch, gitBranchFlag, gitBranchFlagShort, "", gitBranchFlagDescription) - cmd.Flags().StringSliceVarP(&vars.Environments, envsFlag, envsFlagShort, []string{}, pipelineEnvsFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.githubURL, githubURLFlag, githubURLFlagShort, "", githubURLFlagDescription) + cmd.Flags().StringVarP(&vars.githubAccessToken, githubAccessTokenFlag, githubAccessTokenFlagShort, "", githubAccessTokenFlagDescription) + cmd.Flags().StringVarP(&vars.gitBranch, gitBranchFlag, gitBranchFlagShort, "", gitBranchFlagDescription) + cmd.Flags().StringSliceVarP(&vars.environments, envsFlag, envsFlagShort, []string{}, pipelineEnvsFlagDescription) return cmd } diff --git a/internal/pkg/cli/pipeline_init_test.go b/internal/pkg/cli/pipeline_init_test.go index c8dc2a8aa37..8f23d7e52f5 100644 --- a/internal/pkg/cli/pipeline_init_test.go +++ b/internal/pkg/cli/pipeline_init_test.go @@ -224,17 +224,14 @@ func TestInitPipelineOpts_Ask(t *testing.T) { opts := &initPipelineOpts{ initPipelineVars: initPipelineVars{ - Environments: tc.inEnvironments, - GitHubOwner: tc.inGitHubOwner, - GitHubRepo: tc.inGitHubRepo, - GitHubAccessToken: tc.inGitHubAccessToken, - GlobalOpts: &GlobalOpts{ - prompt: mockPrompt, - }, + environments: tc.inEnvironments, + githubOwner: tc.inGitHubOwner, + githubRepo: tc.inGitHubRepo, + githubAccessToken: tc.inGitHubAccessToken, }, - envs: tc.inAppEnvs, repoURLs: tc.inURLs, + prompt: mockPrompt, } tc.mockPrompt(mockPrompt) @@ -247,10 +244,10 @@ func TestInitPipelineOpts_Ask(t *testing.T) { require.EqualError(t, err, tc.expectedError.Error()) } else { require.NoError(t, err) - require.Equal(t, tc.expectedGitHubOwner, opts.GitHubOwner) - require.Equal(t, tc.expectedGitHubRepo, opts.GitHubRepo) - require.Equal(t, tc.expectedGitHubAccessToken, opts.GitHubAccessToken) - require.ElementsMatch(t, tc.expectedEnvironments, opts.Environments) + require.Equal(t, tc.expectedGitHubOwner, opts.githubOwner) + require.Equal(t, tc.expectedGitHubRepo, opts.githubRepo) + require.Equal(t, tc.expectedGitHubAccessToken, opts.githubAccessToken) + require.ElementsMatch(t, tc.expectedEnvironments, opts.environments) } }) } @@ -276,7 +273,7 @@ func TestInitPipelineOpts_Validate(t *testing.T) { opts := &initPipelineOpts{ initPipelineVars: initPipelineVars{ - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + appName: tc.inAppName, }, } @@ -644,11 +641,11 @@ func TestInitPipelineOpts_Execute(t *testing.T) { opts := &initPipelineOpts{ initPipelineVars: initPipelineVars{ - Environments: tc.inEnvironments, - GitHubRepo: tc.inGitHubRepo, - GitHubAccessToken: tc.inGitHubToken, - GitBranch: tc.inGitBranch, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + environments: tc.inEnvironments, + githubRepo: tc.inGitHubRepo, + githubAccessToken: tc.inGitHubToken, + gitBranch: tc.inGitBranch, + appName: tc.inAppName, }, secretsmanager: mockSecretsManager, @@ -695,9 +692,9 @@ func TestInitPipelineOpts_createPipelineName(t *testing.T) { // GIVEN opts := &initPipelineOpts{ initPipelineVars: initPipelineVars{ - GitHubRepo: tc.inGitHubRepo, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, - GitHubOwner: tc.inAppOwner, + githubRepo: tc.inGitHubRepo, + appName: tc.inAppName, + githubOwner: tc.inAppOwner, }, } @@ -849,7 +846,7 @@ func TestInitPipelineOpts_getEnvConfig(t *testing.T) { // GIVEN opts := &initPipelineOpts{ initPipelineVars: initPipelineVars{ - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + appName: tc.inAppName, }, envs: tc.inEnvs, } diff --git a/internal/pkg/cli/pipeline_show.go b/internal/pkg/cli/pipeline_show.go index ac7b73734b2..8658d84c096 100644 --- a/internal/pkg/cli/pipeline_show.go +++ b/internal/pkg/cli/pipeline_show.go @@ -16,6 +16,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/manifest" "github.com/aws/copilot-cli/internal/pkg/term/color" "github.com/aws/copilot-cli/internal/pkg/term/log" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/aws/copilot-cli/internal/pkg/workspace" "github.com/spf13/cobra" @@ -29,7 +30,7 @@ const ( ) type showPipelineVars struct { - *GlobalOpts + appName string shouldOutputJSON bool shouldOutputResources bool pipelineName string @@ -46,6 +47,7 @@ type showPipelineOpts struct { describer describer initDescriber func(bool) error sel appSelector + prompt prompter } func newShowPipelineOpts(vars showPipelineVars) (*showPipelineOpts, error) { @@ -64,12 +66,14 @@ func newShowPipelineOpts(vars showPipelineVars) (*showPipelineOpts, error) { return nil, fmt.Errorf("default session: %w", err) } + prompter := prompt.New() opts := &showPipelineOpts{ showPipelineVars: vars, ws: ws, store: store, pipelineSvc: codepipeline.New(defaultSession), - sel: selector.NewSelect(vars.prompt, store), + sel: selector.NewSelect(prompter, store), + prompt: prompter, w: log.OutputWriter, } opts.initDescriber = func(enableResources bool) error { @@ -87,8 +91,8 @@ func newShowPipelineOpts(vars showPipelineVars) (*showPipelineOpts, error) { // Validate returns an error if the flag values passed by the user are invalid. func (o *showPipelineOpts) Validate() error { - if o.AppName() != "" { - if _, err := o.store.GetApplication(o.AppName()); err != nil { + if o.appName != "" { + if _, err := o.store.GetApplication(o.appName); err != nil { return err } } @@ -110,7 +114,7 @@ func (o *showPipelineOpts) Ask() error { } func (o *showPipelineOpts) askAppName() error { - if o.AppName() != "" { + if o.appName != "" { return nil } name, err := o.sel.Application(pipelineShowAppNamePrompt, pipelineShowAppNameHelpPrompt) @@ -135,7 +139,7 @@ func (o *showPipelineOpts) askPipelineName() error { } if errors.Is(err, workspace.ErrNoPipelineInWorkspace) { - log.Infof("No pipeline manifest in workspace for application %s, looking for deployed pipelines\n", color.HighlightUserInput(o.AppName())) + log.Infof("No pipeline manifest in workspace for application %s, looking for deployed pipelines\n", color.HighlightUserInput(o.appName)) } // find deployed pipelines @@ -145,7 +149,7 @@ func (o *showPipelineOpts) askPipelineName() error { } if len(pipelineNames) == 0 { - log.Infof("No pipelines found for application %s.\n", color.HighlightUserInput(o.AppName())) + log.Infof("No pipelines found for application %s.\n", color.HighlightUserInput(o.appName)) return nil } @@ -159,10 +163,10 @@ func (o *showPipelineOpts) askPipelineName() error { // select from list of deployed pipelines pipelineName, err = o.prompt.SelectOne( - fmt.Sprintf(fmtPipelineShowPipelineNamePrompt, color.HighlightUserInput(o.AppName())), pipelineShowPipelineNameHelpPrompt, pipelineNames, + fmt.Sprintf(fmtPipelineShowPipelineNamePrompt, color.HighlightUserInput(o.appName)), pipelineShowPipelineNameHelpPrompt, pipelineNames, ) if err != nil { - return fmt.Errorf("select pipeline for application %s: %w", o.AppName(), err) + return fmt.Errorf("select pipeline for application %s: %w", o.appName, err) } o.pipelineName = pipelineName return nil @@ -171,7 +175,7 @@ func (o *showPipelineOpts) askPipelineName() error { func (o *showPipelineOpts) retrieveAllPipelines() ([]string, error) { pipelines, err := o.pipelineSvc.ListPipelineNamesByTags(map[string]string{ - deploy.AppTagKey: o.AppName(), + deploy.AppTagKey: o.appName, }) if err != nil { return nil, fmt.Errorf("list pipelines: %w", err) @@ -221,11 +225,9 @@ func (o *showPipelineOpts) Execute() error { return nil } -// BuildPipelineShowCmd build the command for deploying a new pipeline or updating an existing pipeline. -func BuildPipelineShowCmd() *cobra.Command { - vars := showPipelineVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildPipelineShowCmd build the command for deploying a new pipeline or updating an existing pipeline. +func buildPipelineShowCmd() *cobra.Command { + vars := showPipelineVars{} cmd := &cobra.Command{ Use: "show", Short: "Shows info about a deployed pipeline for an application.", diff --git a/internal/pkg/cli/pipeline_show_test.go b/internal/pkg/cli/pipeline_show_test.go index 8d6b0157f87..26d83710313 100644 --- a/internal/pkg/cli/pipeline_show_test.go +++ b/internal/pkg/cli/pipeline_show_test.go @@ -96,9 +96,7 @@ func TestPipelineShow_Validate(t *testing.T) { opts := &showPipelineOpts{ showPipelineVars: showPipelineVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - }, + appName: tc.inAppName, pipelineName: tc.inPipelineName, }, store: mockStoreReader, @@ -274,16 +272,14 @@ stages: opts := &showPipelineOpts{ showPipelineVars: showPipelineVars{ - GlobalOpts: &GlobalOpts{ - prompt: mockPrompt, - appName: tc.inAppName, - }, + appName: tc.inAppName, pipelineName: tc.inPipelineName, }, store: mockStoreReader, ws: mockWorkspace, pipelineSvc: mockPipelineSvc, sel: mockSel, + prompt: mockPrompt, } // WHEN @@ -294,7 +290,7 @@ stages: require.EqualError(t, err, tc.expectedErr.Error()) } else { require.NoError(t, err) - require.Equal(t, tc.expectedApp, opts.AppName(), "expected application names to match") + require.Equal(t, tc.expectedApp, opts.appName, "expected application names to match") require.Equal(t, tc.expectedPipeline, opts.pipelineName, "expected pipeline name to match") } }) diff --git a/internal/pkg/cli/pipeline_status.go b/internal/pkg/cli/pipeline_status.go index 24d91058418..40538f53b8a 100644 --- a/internal/pkg/cli/pipeline_status.go +++ b/internal/pkg/cli/pipeline_status.go @@ -16,6 +16,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/manifest" "github.com/aws/copilot-cli/internal/pkg/term/color" "github.com/aws/copilot-cli/internal/pkg/term/log" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/aws/copilot-cli/internal/pkg/workspace" @@ -30,7 +31,7 @@ const ( ) type pipelineStatusVars struct { - *GlobalOpts + appName string shouldOutputJSON bool pipelineName string } @@ -44,6 +45,7 @@ type pipelineStatusOpts struct { pipelineSvc pipelineGetter describer describer sel appSelector + prompt prompter initDescriber func(opts *pipelineStatusOpts) error } @@ -63,13 +65,14 @@ func newPipelineStatusOpts(vars pipelineStatusVars) (*pipelineStatusOpts, error) return nil, fmt.Errorf("session: %w", err) } + prompter := prompt.New() return &pipelineStatusOpts{ w: log.OutputWriter, pipelineStatusVars: vars, ws: ws, store: store, pipelineSvc: codepipeline.New(session), - sel: selector.NewSelect(vars.prompt, store), + sel: selector.NewSelect(prompter, store), initDescriber: func(o *pipelineStatusOpts) error { d, err := describe.NewPipelineStatusDescriber(o.pipelineName) if err != nil { @@ -83,8 +86,8 @@ func newPipelineStatusOpts(vars pipelineStatusVars) (*pipelineStatusOpts, error) // Validate returns an error if the values provided by the user are invalid. func (o *pipelineStatusOpts) Validate() error { - if o.AppName() != "" { - if _, err := o.store.GetApplication(o.AppName()); err != nil { + if o.appName != "" { + if _, err := o.store.GetApplication(o.appName); err != nil { return err } } @@ -129,7 +132,7 @@ func (o *pipelineStatusOpts) Execute() error { } func (o *pipelineStatusOpts) askAppName() error { - if o.AppName() != "" { + if o.appName != "" { return nil } name, err := o.sel.Application(pipelineStatusAppNamePrompt, pipelineStatusAppNameHelpPrompt) @@ -154,7 +157,7 @@ func (o *pipelineStatusOpts) askPipelineName() error { } if errors.Is(err, workspace.ErrNoPipelineInWorkspace) { - log.Infof("No pipeline manifest in workspace for application %s, looking for deployed pipelines.\n", color.HighlightUserInput(o.AppName())) + log.Infof("No pipeline manifest in workspace for application %s, looking for deployed pipelines.\n", color.HighlightUserInput(o.appName)) } // find deployed pipelines @@ -164,7 +167,7 @@ func (o *pipelineStatusOpts) askPipelineName() error { } if len(pipelineNames) == 0 { - log.Infof("No pipelines found for application %s.\n", color.HighlightUserInput(o.AppName())) + log.Infof("No pipelines found for application %s.\n", color.HighlightUserInput(o.appName)) return nil } @@ -178,10 +181,10 @@ func (o *pipelineStatusOpts) askPipelineName() error { // select from list of deployed pipelines pipelineName, err = o.prompt.SelectOne( - fmt.Sprintf(fmtPipelineStatusPipelineNamePrompt, color.HighlightUserInput(o.AppName())), pipelineStatusPipelineNameHelpPrompt, pipelineNames, + fmt.Sprintf(fmtPipelineStatusPipelineNamePrompt, color.HighlightUserInput(o.appName)), pipelineStatusPipelineNameHelpPrompt, pipelineNames, ) if err != nil { - return fmt.Errorf("select pipeline for application %s: %w", o.AppName(), err) + return fmt.Errorf("select pipeline for application %s: %w", o.appName, err) } o.pipelineName = pipelineName return nil @@ -189,7 +192,7 @@ func (o *pipelineStatusOpts) askPipelineName() error { func (o *pipelineStatusOpts) retrieveAllPipelines() ([]string, error) { pipelines, err := o.pipelineSvc.ListPipelineNamesByTags(map[string]string{ - deploy.AppTagKey: o.AppName(), + deploy.AppTagKey: o.appName, }) if err != nil { return nil, fmt.Errorf("list pipelines: %w", err) @@ -211,11 +214,9 @@ func (o *pipelineStatusOpts) getPipelineNameFromManifest() (string, error) { return pipeline.Name, nil } -// BuildPipelineStatusCmd builds the command for showing the status of a deployed pipeline. -func BuildPipelineStatusCmd() *cobra.Command { - vars := pipelineStatusVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildPipelineStatusCmd builds the command for showing the status of a deployed pipeline. +func buildPipelineStatusCmd() *cobra.Command { + vars := pipelineStatusVars{} cmd := &cobra.Command{ Use: "status", Short: "Shows the status of a pipeline.", @@ -238,7 +239,6 @@ Shows status of the pipeline "pipeline-myapp-myrepo". return opts.Execute() }), } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) cmd.Flags().StringVarP(&vars.pipelineName, nameFlag, nameFlagShort, "", pipelineFlagDescription) cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, "", appFlagDescription) cmd.Flags().BoolVar(&vars.shouldOutputJSON, jsonFlag, false, jsonFlagDescription) diff --git a/internal/pkg/cli/pipeline_status_test.go b/internal/pkg/cli/pipeline_status_test.go index 014110f1a3c..72f9ba6bb57 100644 --- a/internal/pkg/cli/pipeline_status_test.go +++ b/internal/pkg/cli/pipeline_status_test.go @@ -88,9 +88,7 @@ func TestPipelineStatus_Validate(t *testing.T) { opts := &pipelineStatusOpts{ pipelineStatusVars: pipelineStatusVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.testAppName, - }, + appName: tc.testAppName, pipelineName: tc.testPipelineName, }, store: mockStoreReader, @@ -265,16 +263,14 @@ stages: opts := &pipelineStatusOpts{ pipelineStatusVars: pipelineStatusVars{ - GlobalOpts: &GlobalOpts{ - prompt: mockPrompt, - appName: tc.testAppName, - }, + appName: tc.testAppName, pipelineName: tc.testPipelineName, }, store: mockStore, ws: mockWS, pipelineSvc: mockPLSvc, sel: mockSel, + prompt: mockPrompt, } // WHEN @@ -285,7 +281,7 @@ stages: require.EqualError(t, err, tc.expectedErr.Error()) } else { require.NoError(t, err) - require.Equal(t, tc.expectedApp, opts.AppName(), "expected application names to match") + require.Equal(t, tc.expectedApp, opts.appName, "expected application names to match") require.Equal(t, tc.expectedPipeline, opts.pipelineName, "expected pipeline name to match") } }) diff --git a/internal/pkg/cli/pipeline_update.go b/internal/pkg/cli/pipeline_update.go index f5d7bec270a..9d61db953fa 100644 --- a/internal/pkg/cli/pipeline_update.go +++ b/internal/pkg/cli/pipeline_update.go @@ -15,6 +15,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/manifest" "github.com/aws/copilot-cli/internal/pkg/term/color" "github.com/aws/copilot-cli/internal/pkg/term/log" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/workspace" "github.com/aws/aws-sdk-go/aws" @@ -40,9 +41,9 @@ const ( ) type updatePipelineVars struct { - PipelineName string - SkipConfirmation bool - *GlobalOpts + appName string + pipelineName string + skipConfirmation bool } type updatePipelineOpts struct { @@ -51,6 +52,7 @@ type updatePipelineOpts struct { pipelineDeployer pipelineDeployer app *config.Application prog progress + prompt prompter region string envStore environmentStore ws wsPipelineReader @@ -62,9 +64,9 @@ func newUpdatePipelineOpts(vars updatePipelineVars) (*updatePipelineOpts, error) return nil, fmt.Errorf("new config store client: %w", err) } - app, err := store.GetApplication(vars.AppName()) + app, err := store.GetApplication(vars.appName) if err != nil { - return nil, fmt.Errorf("get application %s: %w", vars.AppName(), err) + return nil, fmt.Errorf("get application %s: %w", vars.appName, err) } defaultSession, err := sessions.NewProvider().Default() @@ -85,6 +87,7 @@ func newUpdatePipelineOpts(vars updatePipelineVars) (*updatePipelineOpts, error) envStore: store, ws: ws, prog: termprogress.NewSpinner(), + prompt: prompt.New(), }, nil } @@ -101,9 +104,9 @@ func (o *updatePipelineOpts) convertStages(manifestStages []manifest.PipelineSta } for _, stage := range manifestStages { - env, err := o.envStore.GetEnvironment(o.AppName(), stage.Name) + env, err := o.envStore.GetEnvironment(o.appName, stage.Name) if err != nil { - return nil, fmt.Errorf("get environment %s in application %s: %w", stage.Name, o.AppName(), err) + return nil, fmt.Errorf("get environment %s in application %s: %w", stage.Name, o.appName, err) } pipelineStage := deploy.PipelineStage{ @@ -141,11 +144,11 @@ func (o *updatePipelineOpts) getArtifactBuckets() ([]deploy.ArtifactBucket, erro } func (o *updatePipelineOpts) shouldUpdate() (bool, error) { - if o.SkipConfirmation { + if o.skipConfirmation { return true, nil } - shouldUpdate, err := o.prompt.Confirm(fmt.Sprintf(fmtPipelineUpdateExistPrompt, o.PipelineName), "") + shouldUpdate, err := o.prompt.Confirm(fmt.Sprintf(fmtPipelineUpdateExistPrompt, o.pipelineName), "") if err != nil { return false, fmt.Errorf("prompt for pipeline update: %w", err) } @@ -158,15 +161,15 @@ func (o *updatePipelineOpts) deployPipeline(in *deploy.CreatePipelineInput) erro return fmt.Errorf("check if pipeline exists: %w", err) } if !exist { - o.prog.Start(fmt.Sprintf(fmtPipelineUpdateStart, color.HighlightUserInput(o.PipelineName))) + o.prog.Start(fmt.Sprintf(fmtPipelineUpdateStart, color.HighlightUserInput(o.pipelineName))) if err := o.pipelineDeployer.CreatePipeline(in); err != nil { var alreadyExists *cloudformation.ErrStackAlreadyExists if !errors.As(err, &alreadyExists) { - o.prog.Stop(log.Serrorf(fmtPipelineUpdateFailed, color.HighlightUserInput(o.PipelineName))) + o.prog.Stop(log.Serrorf(fmtPipelineUpdateFailed, color.HighlightUserInput(o.pipelineName))) return fmt.Errorf("create pipeline: %w", err) } } - o.prog.Stop(log.Ssuccessf(fmtPipelineUpdateComplete, color.HighlightUserInput(o.PipelineName))) + o.prog.Stop(log.Ssuccessf(fmtPipelineUpdateComplete, color.HighlightUserInput(o.pipelineName))) return nil } @@ -178,25 +181,25 @@ func (o *updatePipelineOpts) deployPipeline(in *deploy.CreatePipelineInput) erro if !shouldUpdate { return nil } - o.prog.Start(fmt.Sprintf(fmtPipelineUpdateProposalStart, color.HighlightUserInput(o.PipelineName))) + o.prog.Start(fmt.Sprintf(fmtPipelineUpdateProposalStart, color.HighlightUserInput(o.pipelineName))) if err := o.pipelineDeployer.UpdatePipeline(in); err != nil { - o.prog.Stop(log.Serrorf(fmtPipelineUpdateProposalFailed, color.HighlightUserInput(o.PipelineName))) + o.prog.Stop(log.Serrorf(fmtPipelineUpdateProposalFailed, color.HighlightUserInput(o.pipelineName))) return fmt.Errorf("update pipeline: %w", err) } - o.prog.Stop(log.Ssuccessf(fmtPipelineUpdateProposalComplete, color.HighlightUserInput(o.PipelineName))) + o.prog.Stop(log.Ssuccessf(fmtPipelineUpdateProposalComplete, color.HighlightUserInput(o.pipelineName))) return nil } // Execute create a new pipeline or update the current pipeline if it already exists. func (o *updatePipelineOpts) Execute() error { // bootstrap pipeline resources - o.prog.Start(fmt.Sprintf(fmtPipelineUpdateResourcesStart, color.HighlightUserInput(o.AppName()))) + o.prog.Start(fmt.Sprintf(fmtPipelineUpdateResourcesStart, color.HighlightUserInput(o.appName))) err := o.pipelineDeployer.AddPipelineResourcesToApp(o.app, o.region) if err != nil { - o.prog.Stop(log.Serrorf(fmtPipelineUpdateResourcesFailed, color.HighlightUserInput(o.AppName()))) - return fmt.Errorf("add pipeline resources to application %s in %s: %w", o.AppName(), o.region, err) + o.prog.Stop(log.Serrorf(fmtPipelineUpdateResourcesFailed, color.HighlightUserInput(o.appName))) + return fmt.Errorf("add pipeline resources to application %s in %s: %w", o.appName, o.region, err) } - o.prog.Stop(log.Ssuccessf(fmtPipelineUpdateResourcesComplete, color.HighlightUserInput(o.AppName()))) + o.prog.Stop(log.Ssuccessf(fmtPipelineUpdateResourcesComplete, color.HighlightUserInput(o.appName))) // read pipeline manifest data, err := o.ws.ReadPipelineManifest() @@ -207,7 +210,7 @@ func (o *updatePipelineOpts) Execute() error { if err != nil { return fmt.Errorf("unmarshal pipeline manifest: %w", err) } - o.PipelineName = pipeline.Name + o.pipelineName = pipeline.Name source := &deploy.Source{ ProviderName: pipeline.Source.ProviderName, Properties: pipeline.Source.Properties, @@ -226,7 +229,7 @@ func (o *updatePipelineOpts) Execute() error { } deployPipelineInput := &deploy.CreatePipelineInput{ - AppName: o.AppName(), + AppName: o.appName, Name: pipeline.Name, Source: source, Stages: stages, @@ -242,10 +245,8 @@ func (o *updatePipelineOpts) Execute() error { } // BuildPipelineUpdateCmd build the command for deploying a new pipeline or updating an existing pipeline. -func BuildPipelineUpdateCmd() *cobra.Command { - vars := updatePipelineVars{ - GlobalOpts: NewGlobalOpts(), - } +func buildPipelineUpdateCmd() *cobra.Command { + vars := updatePipelineVars{} cmd := &cobra.Command{ Use: "update", Short: "Deploys a pipeline for the services in your workspace.", @@ -264,7 +265,7 @@ func BuildPipelineUpdateCmd() *cobra.Command { return opts.Execute() }), } - cmd.Flags().BoolVar(&vars.SkipConfirmation, yesFlag, false, yesFlagDescription) - + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, yesFlagDescription) return cmd } diff --git a/internal/pkg/cli/pipeline_update_test.go b/internal/pkg/cli/pipeline_update_test.go index 245f9dc64ae..63b85b02850 100644 --- a/internal/pkg/cli/pipeline_update_test.go +++ b/internal/pkg/cli/pipeline_update_test.go @@ -161,7 +161,7 @@ func TestUpdatePipelineOpts_convertStages(t *testing.T) { opts := &updatePipelineOpts{ updatePipelineVars: updatePipelineVars{ - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + appName: tc.inAppName, }, envStore: mockEnvStore, ws: mockWorkspace, @@ -601,11 +601,8 @@ stages: opts := &updatePipelineOpts{ updatePipelineVars: updatePipelineVars{ - PipelineName: tc.inPipelineName, - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - prompt: mockPrompt, - }, + pipelineName: tc.inPipelineName, + appName: tc.inAppName, }, pipelineDeployer: mockPipelineDeployer, ws: mockWorkspace, @@ -613,6 +610,7 @@ stages: region: tc.inRegion, envStore: mockEnvStore, prog: mockProgress, + prompt: mockPrompt, } // WHEN diff --git a/internal/pkg/cli/storage.go b/internal/pkg/cli/storage.go index 79bab6f809a..287657056a2 100644 --- a/internal/pkg/cli/storage.go +++ b/internal/pkg/cli/storage.go @@ -18,7 +18,7 @@ func BuildStorageCmd() *cobra.Command { Augment your services with S3 buckets, NoSQL and SQL databases.`, } - cmd.AddCommand(BuildStorageInitCmd()) + cmd.AddCommand(buildStorageInitCmd()) cmd.SetUsageTemplate(template.Usage) diff --git a/internal/pkg/cli/storage_init.go b/internal/pkg/cli/storage_init.go index ca88de6a785..30de718ad7f 100644 --- a/internal/pkg/cli/storage_init.go +++ b/internal/pkg/cli/storage_init.go @@ -89,7 +89,6 @@ var attributeTypes = []string{ } type initStorageVars struct { - *GlobalOpts storageType string storageName string storageSvc string @@ -104,12 +103,14 @@ type initStorageVars struct { type initStorageOpts struct { initStorageVars + appName string fs afero.Fs ws wsAddonManager store store - sel wsSelector + sel wsSelector + prompt prompter } func newStorageInitOpts(vars initStorageVars) (*initStorageOpts, error) { @@ -123,18 +124,21 @@ func newStorageInitOpts(vars initStorageVars) (*initStorageOpts, error) { return nil, fmt.Errorf("new workspace client: %w", err) } + prompter := prompt.New() return &initStorageOpts{ initStorageVars: vars, + appName: tryReadingAppName(), - fs: &afero.Afero{Fs: afero.NewOsFs()}, - store: store, - ws: ws, - sel: selector.NewWorkspaceSelect(vars.prompt, store, ws), + fs: &afero.Afero{Fs: afero.NewOsFs()}, + store: store, + ws: ws, + sel: selector.NewWorkspaceSelect(prompter, store, ws), + prompt: prompter, }, nil } func (o *initStorageOpts) Validate() error { - if o.AppName() == "" { + if o.appName == "" { return errNoAppInWorkspace } if o.storageSvc != "" { @@ -538,11 +542,9 @@ func (o *initStorageOpts) RecommendedActions() []string { } } -// BuildStorageInitCmd builds the command and adds it to the CLI. -func BuildStorageInitCmd() *cobra.Command { - vars := initStorageVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildStorageInitCmd builds the command and adds it to the CLI. +func buildStorageInitCmd() *cobra.Command { + vars := initStorageVars{} cmd := &cobra.Command{ Use: "init", Short: "Creates a new storage config file in a service's addons directory.", diff --git a/internal/pkg/cli/storage_init_test.go b/internal/pkg/cli/storage_init_test.go index 1f439b467a0..a000a02206e 100644 --- a/internal/pkg/cli/storage_init_test.go +++ b/internal/pkg/cli/storage_init_test.go @@ -173,9 +173,6 @@ func TestStorageInitOpts_Validate(t *testing.T) { tc.mockStore(mockStore) opts := initStorageOpts{ initStorageVars: initStorageVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - }, storageType: tc.inStorageType, storageName: tc.inStorageName, storageSvc: tc.inSvcName, @@ -185,8 +182,9 @@ func TestStorageInitOpts_Validate(t *testing.T) { noLSI: tc.inNoLSI, noSort: tc.inNoSort, }, - ws: mockWs, - store: mockStore, + appName: tc.inAppName, + ws: mockWs, + store: mockStore, } // WHEN @@ -559,9 +557,6 @@ func TestStorageInitOpts_Ask(t *testing.T) { mockCfg: func(m *mocks.MockwsSelector) {}, wantedVars: &initStorageVars{ - GlobalOpts: &GlobalOpts{ - appName: wantedAppName, - }, storageName: wantedTableName, storageSvc: wantedSvcName, storageType: dynamoDBStorageType, @@ -590,9 +585,6 @@ func TestStorageInitOpts_Ask(t *testing.T) { mockCfg: func(m *mocks.MockwsSelector) {}, wantedVars: &initStorageVars{ - GlobalOpts: &GlobalOpts{ - appName: wantedAppName, - }, storageName: wantedTableName, storageSvc: wantedSvcName, storageType: dynamoDBStorageType, @@ -619,9 +611,6 @@ func TestStorageInitOpts_Ask(t *testing.T) { mockCfg: func(m *mocks.MockwsSelector) {}, wantedVars: &initStorageVars{ - GlobalOpts: &GlobalOpts{ - appName: wantedAppName, - }, storageName: wantedTableName, storageSvc: wantedSvcName, storageType: dynamoDBStorageType, @@ -729,10 +718,6 @@ func TestStorageInitOpts_Ask(t *testing.T) { mockConfig := mocks.NewMockwsSelector(ctrl) opts := initStorageOpts{ initStorageVars: initStorageVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - prompt: mockPrompt, - }, storageType: tc.inStorageType, storageName: tc.inStorageName, storageSvc: tc.inSvcName, @@ -742,7 +727,9 @@ func TestStorageInitOpts_Ask(t *testing.T) { noLSI: tc.inNoLSI, noSort: tc.inNoSort, }, - sel: mockConfig, + appName: tc.inAppName, + sel: mockConfig, + prompt: mockPrompt, } tc.mockPrompt(mockPrompt) tc.mockCfg(mockConfig) @@ -756,7 +743,6 @@ func TestStorageInitOpts_Ask(t *testing.T) { require.NoError(t, err) } if tc.wantedVars != nil { - tc.wantedVars.prompt = opts.prompt require.Equal(t, *tc.wantedVars, opts.initStorageVars) } }) @@ -864,9 +850,6 @@ func TestStorageInitOpts_Execute(t *testing.T) { mockAddon := mocks.NewMockwsAddonManager(ctrl) opts := initStorageOpts{ initStorageVars: initStorageVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - }, storageType: tc.inStorageType, storageName: tc.inStorageName, storageSvc: tc.inSvcName, @@ -876,7 +859,8 @@ func TestStorageInitOpts_Execute(t *testing.T) { noLSI: tc.inNoLSI, noSort: tc.inNoSort, }, - ws: mockAddon, + appName: tc.inAppName, + ws: mockAddon, } tc.mockWs(mockAddon) // WHEN diff --git a/internal/pkg/cli/svc.go b/internal/pkg/cli/svc.go index ff4d544a167..6a6283358de 100644 --- a/internal/pkg/cli/svc.go +++ b/internal/pkg/cli/svc.go @@ -9,7 +9,6 @@ import ( "github.com/aws/copilot-cli/cmd/copilot/template" "github.com/aws/copilot-cli/internal/pkg/cli/group" "github.com/spf13/cobra" - "github.com/spf13/viper" ) var errNoAppInWorkspace = errors.New("could not find an application attached to this workspace, please run `app init` first") @@ -23,18 +22,15 @@ Services are long-running Amazon ECS services.`, Long: `Commands for services. Services are long-running Amazon ECS services.`, } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) - cmd.PersistentFlags().StringP(appFlag, appFlagShort, "" /* default */, appFlagDescription) - _ = viper.BindPFlag(appFlag, cmd.PersistentFlags().Lookup(appFlag)) // Ignore err because the flag name is not empty. - - cmd.AddCommand(BuildSvcInitCmd()) - cmd.AddCommand(BuildSvcListCmd()) - cmd.AddCommand(BuildSvcPackageCmd()) - cmd.AddCommand(BuildSvcDeployCmd()) - cmd.AddCommand(BuildSvcDeleteCmd()) - cmd.AddCommand(BuildSvcShowCmd()) - cmd.AddCommand(BuildSvcStatusCmd()) - cmd.AddCommand(BuildSvcLogsCmd()) + + cmd.AddCommand(buildSvcInitCmd()) + cmd.AddCommand(buildSvcListCmd()) + cmd.AddCommand(buildSvcPackageCmd()) + cmd.AddCommand(buildSvcDeployCmd()) + cmd.AddCommand(buildSvcDeleteCmd()) + cmd.AddCommand(buildSvcShowCmd()) + cmd.AddCommand(buildSvcStatusCmd()) + cmd.AddCommand(buildSvcLogsCmd()) cmd.SetUsageTemplate(template.Usage) diff --git a/internal/pkg/cli/svc_delete.go b/internal/pkg/cli/svc_delete.go index 839c59f7925..c2ea2449886 100644 --- a/internal/pkg/cli/svc_delete.go +++ b/internal/pkg/cli/svc_delete.go @@ -18,6 +18,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/term/color" "github.com/aws/copilot-cli/internal/pkg/term/log" termprogress "github.com/aws/copilot-cli/internal/pkg/term/progress" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/spf13/cobra" ) @@ -42,10 +43,10 @@ var ( ) type deleteSvcVars struct { - *GlobalOpts - SkipConfirmation bool - Name string - EnvName string + appName string + skipConfirmation bool + name string + envName string } type deleteSvcOpts struct { @@ -55,6 +56,7 @@ type deleteSvcOpts struct { store store sess sessionProvider spinner progress + prompt prompter appCFN svcRemoverFromApp getSvcCFN func(session *awssession.Session) svcDeleter getECR func(session *awssession.Session) imageRemover @@ -80,6 +82,7 @@ func newDeleteSvcOpts(vars deleteSvcVars) (*deleteSvcOpts, error) { store: store, spinner: termprogress.NewSpinner(), + prompt: prompt.New(), sess: provider, appCFN: cloudformation.New(defaultSession), getSvcCFN: func(session *awssession.Session) svcDeleter { @@ -93,15 +96,15 @@ func newDeleteSvcOpts(vars deleteSvcVars) (*deleteSvcOpts, error) { // Validate returns an error if the user inputs are invalid. func (o *deleteSvcOpts) Validate() error { - if o.AppName() == "" { + if o.appName == "" { return errNoAppInWorkspace } - if o.Name != "" { - if _, err := o.store.GetService(o.AppName(), o.Name); err != nil { + if o.name != "" { + if _, err := o.store.GetService(o.appName, o.name); err != nil { return err } } - if o.EnvName != "" { + if o.envName != "" { if err := o.validateEnvName(); err != nil { return err } @@ -115,20 +118,20 @@ func (o *deleteSvcOpts) Ask() error { return err } - if o.SkipConfirmation { + if o.skipConfirmation { return nil } // When there's no env name passed in, we'll completely // remove the service from the application. - deletePrompt := fmt.Sprintf(fmtSvcDeleteConfirmPrompt, o.Name, o.appName) + deletePrompt := fmt.Sprintf(fmtSvcDeleteConfirmPrompt, o.name, o.appName) deleteConfirmHelp := svcDeleteConfirmHelp - if o.EnvName != "" { + if o.envName != "" { // When a customer provides a particular environment, // we'll just delete the service from that environment - // but keep it in the app. - deletePrompt = fmt.Sprintf(fmtSvcDeleteFromEnvConfirmPrompt, o.Name, o.EnvName) - deleteConfirmHelp = fmt.Sprintf(svcDeleteFromEnvConfirmHelp, o.EnvName) + deletePrompt = fmt.Sprintf(fmtSvcDeleteFromEnvConfirmPrompt, o.name, o.envName) + deleteConfirmHelp = fmt.Sprintf(svcDeleteFromEnvConfirmHelp, o.envName) } deleteConfirmed, err := o.prompt.Confirm( @@ -172,7 +175,7 @@ func (o *deleteSvcOpts) Execute() error { return err } - log.Successf("Deleted service %s from application %s.\n", o.Name, o.appName) + log.Successf("Deleted service %s from application %s.\n", o.name, o.appName) return nil } @@ -189,19 +192,19 @@ func (o *deleteSvcOpts) needsAppCleanup() bool { // we're removing it from every environment. // If we're just removing the service from one // env, we keep the app configuration. - return o.EnvName == "" + return o.envName == "" } func (o *deleteSvcOpts) targetEnv() (*config.Environment, error) { - env, err := o.store.GetEnvironment(o.AppName(), o.EnvName) + env, err := o.store.GetEnvironment(o.appName, o.envName) if err != nil { - return nil, fmt.Errorf("get environment %s from config store: %w", o.EnvName, err) + return nil, fmt.Errorf("get environment %s from config store: %w", o.envName, err) } return env, nil } func (o *deleteSvcOpts) askSvcName() error { - if o.Name != "" { + if o.name != "" { return nil } @@ -210,25 +213,25 @@ func (o *deleteSvcOpts) askSvcName() error { return err } if len(names) == 0 { - return fmt.Errorf("couldn't find any services in the application %s", o.AppName()) + return fmt.Errorf("couldn't find any services in the application %s", o.appName) } if len(names) == 1 { - o.Name = names[0] - log.Infof("Only found one service, defaulting to: %s\n", color.HighlightUserInput(o.Name)) + o.name = names[0] + log.Infof("Only found one service, defaulting to: %s\n", color.HighlightUserInput(o.name)) return nil } name, err := o.prompt.SelectOne(svcDeleteNamePrompt, "", names) if err != nil { return fmt.Errorf("select service to delete: %w", err) } - o.Name = name + o.name = name return nil } func (o *deleteSvcOpts) serviceNames() ([]string, error) { - services, err := o.store.ListServices(o.AppName()) + services, err := o.store.ListServices(o.appName) if err != nil { - return nil, fmt.Errorf("list services for application %s: %w", o.AppName(), err) + return nil, fmt.Errorf("list services for application %s: %w", o.appName, err) } var names []string for _, svc := range services { @@ -238,14 +241,14 @@ func (o *deleteSvcOpts) serviceNames() ([]string, error) { } func (o *deleteSvcOpts) appEnvironments() error { - if o.EnvName != "" { + if o.envName != "" { env, err := o.targetEnv() if err != nil { return err } o.environments = append(o.environments, env) } else { - envs, err := o.store.ListEnvironments(o.AppName()) + envs, err := o.store.ListEnvironments(o.appName) if err != nil { return fmt.Errorf("list environments: %w", err) } @@ -262,16 +265,16 @@ func (o *deleteSvcOpts) deleteStacks() error { } cfClient := o.getSvcCFN(sess) - o.spinner.Start(fmt.Sprintf(fmtSvcDeleteStart, o.Name, env.Name)) + o.spinner.Start(fmt.Sprintf(fmtSvcDeleteStart, o.name, env.Name)) if err := cfClient.DeleteService(deploy.DeleteServiceInput{ - Name: o.Name, + Name: o.name, EnvName: env.Name, AppName: o.appName, }); err != nil { - o.spinner.Stop(log.Serrorf(fmtSvcDeleteFailed, o.Name, env.Name, err)) + o.spinner.Stop(log.Serrorf(fmtSvcDeleteFailed, o.name, env.Name, err)) return err } - o.spinner.Stop(log.Ssuccessf(fmtSvcDeleteComplete, o.Name, env.Name)) + o.spinner.Stop(log.Ssuccessf(fmtSvcDeleteComplete, o.name, env.Name)) } return nil } @@ -286,7 +289,7 @@ func (o *deleteSvcOpts) emptyECRRepos() error { } // TODO: centralized ECR repo name - repoName := fmt.Sprintf("%s/%s", o.appName, o.Name) + repoName := fmt.Sprintf("%s/%s", o.appName, o.name) for _, region := range uniqueRegions { sess, err := o.sess.DefaultWithRegion(region) if err != nil { @@ -306,20 +309,20 @@ func (o *deleteSvcOpts) removeSvcFromApp() error { return err } - o.spinner.Start(fmt.Sprintf(fmtSvcDeleteResourcesStart, o.Name, o.appName)) - if err := o.appCFN.RemoveServiceFromApp(proj, o.Name); err != nil { + o.spinner.Start(fmt.Sprintf(fmtSvcDeleteResourcesStart, o.name, o.appName)) + if err := o.appCFN.RemoveServiceFromApp(proj, o.name); err != nil { if !isStackSetNotExistsErr(err) { - o.spinner.Stop(log.Serrorf(fmtSvcDeleteResourcesStart, o.Name, o.appName)) + o.spinner.Stop(log.Serrorf(fmtSvcDeleteResourcesStart, o.name, o.appName)) return err } } - o.spinner.Stop(log.Ssuccessf(fmtSvcDeleteResourcesComplete, o.Name, o.appName)) + o.spinner.Stop(log.Ssuccessf(fmtSvcDeleteResourcesComplete, o.name, o.appName)) return nil } func (o *deleteSvcOpts) deleteSSMParam() error { - if err := o.store.DeleteService(o.appName, o.Name); err != nil { - return fmt.Errorf("delete service %s in application %s from config store: %w", o.Name, o.appName, err) + if err := o.store.DeleteService(o.appName, o.name); err != nil { + return fmt.Errorf("delete service %s in application %s from config store: %w", o.name, o.appName, err) } return nil @@ -333,11 +336,9 @@ func (o *deleteSvcOpts) RecommendedActions() []string { } } -// BuildSvcDeleteCmd builds the command to delete application(s). -func BuildSvcDeleteCmd() *cobra.Command { - vars := deleteSvcVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildSvcDeleteCmd builds the command to delete application(s). +func buildSvcDeleteCmd() *cobra.Command { + vars := deleteSvcVars{} cmd := &cobra.Command{ Use: "delete", Short: "Deletes a service from an application.", @@ -373,8 +374,9 @@ func BuildSvcDeleteCmd() *cobra.Command { }), } - cmd.Flags().StringVarP(&vars.Name, nameFlag, nameFlagShort, "", svcFlagDescription) - cmd.Flags().StringVarP(&vars.EnvName, envFlag, envFlagShort, "", envFlagDescription) - cmd.Flags().BoolVar(&vars.SkipConfirmation, yesFlag, false, yesFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", svcFlagDescription) + cmd.Flags().StringVarP(&vars.envName, envFlag, envFlagShort, "", envFlagDescription) + cmd.Flags().BoolVar(&vars.skipConfirmation, yesFlag, false, yesFlagDescription) return cmd } diff --git a/internal/pkg/cli/svc_delete_test.go b/internal/pkg/cli/svc_delete_test.go index 40291dcd040..0e10f6be78c 100644 --- a/internal/pkg/cli/svc_delete_test.go +++ b/internal/pkg/cli/svc_delete_test.go @@ -85,11 +85,9 @@ func TestDeleteSvcOpts_Validate(t *testing.T) { opts := deleteSvcOpts{ deleteSvcVars: deleteSvcVars{ - GlobalOpts: &GlobalOpts{ - appName: test.inAppName, - }, - Name: test.inName, - EnvName: test.inEnvName, + appName: test.inAppName, + name: test.inName, + envName: test.inEnvName, }, store: mockstore, } @@ -260,15 +258,13 @@ func TestDeleteSvcOpts_Ask(t *testing.T) { opts := deleteSvcOpts{ deleteSvcVars: deleteSvcVars{ - SkipConfirmation: test.skipConfirmation, - GlobalOpts: &GlobalOpts{ - appName: testAppName, - prompt: mockPrompter, - }, - Name: test.inName, - EnvName: test.envName, + skipConfirmation: test.skipConfirmation, + appName: testAppName, + name: test.inName, + envName: test.envName, }, - store: mockStore, + store: mockStore, + prompt: mockPrompter, } got := opts.Ask() @@ -276,7 +272,7 @@ func TestDeleteSvcOpts_Ask(t *testing.T) { if got != nil { require.Equal(t, test.wantedError, got) } else { - require.Equal(t, test.wantedName, opts.Name) + require.Equal(t, test.wantedName, opts.name) } }) } @@ -425,11 +421,9 @@ func TestDeleteSvcOpts_Execute(t *testing.T) { opts := deleteSvcOpts{ deleteSvcVars: deleteSvcVars{ - GlobalOpts: &GlobalOpts{ - appName: test.inAppName, - }, - Name: test.inSvcName, - EnvName: test.inEnvName, + appName: test.inAppName, + name: test.inSvcName, + envName: test.inEnvName, }, store: mockstore, sess: mockSession, diff --git a/internal/pkg/cli/svc_deploy.go b/internal/pkg/cli/svc_deploy.go index fe16fdf67b4..8ea8a1e93f2 100644 --- a/internal/pkg/cli/svc_deploy.go +++ b/internal/pkg/cli/svc_deploy.go @@ -37,11 +37,11 @@ const ( ) type deploySvcVars struct { - *GlobalOpts - Name string - EnvName string - ImageTag string - ResourceTags map[string]string + appName string + name string + envName string + imageTag string + resourceTags map[string]string } type deploySvcOpts struct { @@ -60,6 +60,7 @@ type deploySvcOpts struct { spinner progress sel wsSelector + prompt prompter // cached variables targetApp *config.Application @@ -77,6 +78,7 @@ func newSvcDeployOpts(vars deploySvcVars) (*deploySvcOpts, error) { if err != nil { return nil, fmt.Errorf("new workspace: %w", err) } + prompter := prompt.New() return &deploySvcOpts{ deploySvcVars: vars, @@ -84,7 +86,8 @@ func newSvcDeployOpts(vars deploySvcVars) (*deploySvcOpts, error) { ws: ws, unmarshal: manifest.UnmarshalWorkload, spinner: termprogress.NewSpinner(), - sel: selector.NewWorkspaceSelect(vars.prompt, store, ws), + sel: selector.NewWorkspaceSelect(prompter, store, ws), + prompt: prompter, cmd: command.New(), sessProvider: sessions.NewProvider(), }, nil @@ -92,15 +95,15 @@ func newSvcDeployOpts(vars deploySvcVars) (*deploySvcOpts, error) { // Validate returns an error if the user inputs are invalid. func (o *deploySvcOpts) Validate() error { - if o.AppName() == "" { + if o.appName == "" { return errNoAppInWorkspace } - if o.Name != "" { + if o.name != "" { if err := o.validateSvcName(); err != nil { return err } } - if o.EnvName != "" { + if o.envName != "" { if err := o.validateEnvName(); err != nil { return err } @@ -130,13 +133,13 @@ func (o *deploySvcOpts) Execute() error { } o.targetEnvironment = env - app, err := o.store.GetApplication(o.AppName()) + app, err := o.store.GetApplication(o.appName) if err != nil { return err } o.targetApp = app - svc, err := o.store.GetService(o.AppName(), o.Name) + svc, err := o.store.GetService(o.appName, o.name) if err != nil { return fmt.Errorf("get service configuration: %w", err) } @@ -174,11 +177,11 @@ func (o *deploySvcOpts) validateSvcName() error { return fmt.Errorf("list services in the workspace: %w", err) } for _, name := range names { - if o.Name == name { + if o.name == name { return nil } } - return fmt.Errorf("service %s not found in the workspace", color.HighlightUserInput(o.Name)) + return fmt.Errorf("service %s not found in the workspace", color.HighlightUserInput(o.name)) } func (o *deploySvcOpts) validateEnvName() error { @@ -189,15 +192,15 @@ func (o *deploySvcOpts) validateEnvName() error { } func (o *deploySvcOpts) targetEnv() (*config.Environment, error) { - env, err := o.store.GetEnvironment(o.AppName(), o.EnvName) + env, err := o.store.GetEnvironment(o.appName, o.envName) if err != nil { - return nil, fmt.Errorf("get environment %s configuration: %w", o.EnvName, err) + return nil, fmt.Errorf("get environment %s configuration: %w", o.envName, err) } return env, nil } func (o *deploySvcOpts) askSvcName() error { - if o.Name != "" { + if o.name != "" { return nil } @@ -205,32 +208,32 @@ func (o *deploySvcOpts) askSvcName() error { if err != nil { return fmt.Errorf("select service: %w", err) } - o.Name = name + o.name = name return nil } func (o *deploySvcOpts) askEnvName() error { - if o.EnvName != "" { + if o.envName != "" { return nil } - name, err := o.sel.Environment("Select an environment", "", o.AppName()) + name, err := o.sel.Environment("Select an environment", "", o.appName) if err != nil { return fmt.Errorf("select environment: %w", err) } - o.EnvName = name + o.envName = name return nil } func (o *deploySvcOpts) askImageTag() error { - if o.ImageTag != "" { + if o.imageTag != "" { return nil } tag, err := getVersionTag(o.cmd) if err == nil { - o.ImageTag = tag + o.imageTag = tag return nil } @@ -241,7 +244,7 @@ func (o *deploySvcOpts) askImageTag() error { if err != nil { return fmt.Errorf("prompt for image tag: %w", err) } - o.ImageTag = userInputTag + o.imageTag = userInputTag return nil } @@ -257,7 +260,7 @@ func (o *deploySvcOpts) configureClients() error { } // ECR client against tools account profile AND target environment region - repoName := fmt.Sprintf("%s/%s", o.appName, o.Name) + repoName := fmt.Sprintf("%s/%s", o.appName, o.name) registry := ecr.New(defaultSessEnvRegion) o.imageBuilderPusher, err = repository.New(repoName, registry) if err != nil { @@ -269,7 +272,7 @@ func (o *deploySvcOpts) configureClients() error { // CF client against env account profile AND target environment region o.svcCFN = cloudformation.New(envSession) - addonsSvc, err := addon.New(o.Name) + addonsSvc, err := addon.New(o.name) if err != nil { return fmt.Errorf("initiate addons service: %w", err) } @@ -303,17 +306,17 @@ func (o *deploySvcOpts) getBuildArgs() (*docker.BuildArguments, error) { BuildArgs(rootDirectory string) *manifest.DockerBuildArgs } - manifestBytes, err := o.ws.ReadWorkloadManifest(o.Name) + manifestBytes, err := o.ws.ReadWorkloadManifest(o.name) if err != nil { - return nil, fmt.Errorf("read manifest file %s: %w", o.Name, err) + return nil, fmt.Errorf("read manifest file %s: %w", o.name, err) } svc, err := o.unmarshal(manifestBytes) if err != nil { - return nil, fmt.Errorf("unmarshal service %s manifest: %w", o.Name, err) + return nil, fmt.Errorf("unmarshal service %s manifest: %w", o.name, err) } mf, ok := svc.(dfArgs) if !ok { - return nil, fmt.Errorf("service %s does not have required method Build()", o.Name) + return nil, fmt.Errorf("service %s does not have required method Build()", o.name) } copilotDir, err := o.ws.CopilotDirPath() if err != nil { @@ -326,7 +329,7 @@ func (o *deploySvcOpts) getBuildArgs() (*docker.BuildArguments, error) { Dockerfile: *args.Dockerfile, Context: *args.Context, Args: args.Args, - ImageTag: o.ImageTag, + ImageTag: o.imageTag, }, nil } @@ -349,7 +352,7 @@ func (o *deploySvcOpts) pushAddonsTemplateToS3Bucket() (string, error) { } reader := strings.NewReader(template) - url, err := o.s3.PutArtifact(resources.S3Bucket, fmt.Sprintf(config.AddonsCfnTemplateNameFormat, o.Name), reader) + url, err := o.s3.PutArtifact(resources.S3Bucket, fmt.Sprintf(config.AddonsCfnTemplateNameFormat, o.name), reader) if err != nil { return "", fmt.Errorf("put addons artifact to bucket %s: %w", resources.S3Bucket, err) } @@ -357,13 +360,13 @@ func (o *deploySvcOpts) pushAddonsTemplateToS3Bucket() (string, error) { } func (o *deploySvcOpts) manifest() (interface{}, error) { - raw, err := o.ws.ReadWorkloadManifest(o.Name) + raw, err := o.ws.ReadWorkloadManifest(o.name) if err != nil { - return nil, fmt.Errorf("read service %s manifest from workspace: %w", o.Name, err) + return nil, fmt.Errorf("read service %s manifest from workspace: %w", o.name, err) } mft, err := o.unmarshal(raw) if err != nil { - return nil, fmt.Errorf("unmarshal service %s manifest: %w", o.Name, err) + return nil, fmt.Errorf("unmarshal service %s manifest: %w", o.name, err) } return mft, nil } @@ -373,19 +376,19 @@ func (o *deploySvcOpts) runtimeConfig(addonsURL string) (*stack.RuntimeConfig, e if err != nil { return nil, fmt.Errorf("get application %s resources from region %s: %w", o.targetApp.Name, o.targetEnvironment.Region, err) } - repoURL, ok := resources.RepositoryURLs[o.Name] + repoURL, ok := resources.RepositoryURLs[o.name] if !ok { return nil, &errRepoNotFound{ - svcName: o.Name, + svcName: o.name, envRegion: o.targetEnvironment.Region, appAccountID: o.targetApp.AccountID, } } return &stack.RuntimeConfig{ ImageRepoURL: repoURL, - ImageTag: o.ImageTag, + ImageTag: o.imageTag, AddonsTemplateURL: addonsURL, - AdditionalTags: tags.Merge(o.targetApp.Tags, o.ResourceTags), + AdditionalTags: tags.Merge(o.targetApp.Tags, o.resourceTags), }, nil } @@ -424,7 +427,7 @@ func (o *deploySvcOpts) deploySvc(addonsURL string) error { } o.spinner.Start( fmt.Sprintf("Deploying %s to %s.", - fmt.Sprintf("%s:%s", color.HighlightUserInput(o.Name), color.HighlightUserInput(o.ImageTag)), + fmt.Sprintf("%s:%s", color.HighlightUserInput(o.name), color.HighlightUserInput(o.imageTag)), color.HighlightUserInput(o.targetEnvironment.Name))) if err := o.svcCFN.DeployService(conf, awscloudformation.WithRoleARN(o.targetEnvironment.ExecutionRoleARN)); err != nil { @@ -446,16 +449,16 @@ func (o *deploySvcOpts) showAppURI() error { case manifest.LoadBalancedWebServiceType: svcDescriber, err = describe.NewWebServiceDescriber(describe.NewWebServiceConfig{ NewServiceConfig: describe.NewServiceConfig{ - App: o.AppName(), - Svc: o.Name, + App: o.appName, + Svc: o.name, ConfigStore: o.store, }, }) case manifest.BackendServiceType: svcDescriber, err = describe.NewBackendServiceDescriber(describe.NewBackendServiceConfig{ NewServiceConfig: describe.NewServiceConfig{ - App: o.AppName(), - Svc: o.Name, + App: o.appName, + Svc: o.name, ConfigStore: o.store, }, }) @@ -472,18 +475,16 @@ func (o *deploySvcOpts) showAppURI() error { } switch o.targetSvc.Type { case manifest.BackendServiceType: - log.Successf("Deployed %s, its service discovery endpoint is %s.\n", color.HighlightUserInput(o.Name), color.HighlightResource(uri)) + log.Successf("Deployed %s, its service discovery endpoint is %s.\n", color.HighlightUserInput(o.name), color.HighlightResource(uri)) default: - log.Successf("Deployed %s, you can access it at %s.\n", color.HighlightUserInput(o.Name), color.HighlightResource(uri)) + log.Successf("Deployed %s, you can access it at %s.\n", color.HighlightUserInput(o.name), color.HighlightResource(uri)) } return nil } -// BuildSvcDeployCmd builds the `svc deploy` subcommand. -func BuildSvcDeployCmd() *cobra.Command { - vars := deploySvcVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildSvcDeployCmd builds the `svc deploy` subcommand. +func buildSvcDeployCmd() *cobra.Command { + vars := deploySvcVars{} cmd := &cobra.Command{ Use: "deploy", Short: "Deploys a service to an environment.", @@ -510,10 +511,11 @@ func BuildSvcDeployCmd() *cobra.Command { return nil }), } - cmd.Flags().StringVarP(&vars.Name, nameFlag, nameFlagShort, "", svcFlagDescription) - cmd.Flags().StringVarP(&vars.EnvName, envFlag, envFlagShort, "", envFlagDescription) - cmd.Flags().StringVar(&vars.ImageTag, imageTagFlag, "", imageTagFlagDescription) - cmd.Flags().StringToStringVar(&vars.ResourceTags, resourceTagsFlag, nil, resourceTagsFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", svcFlagDescription) + cmd.Flags().StringVarP(&vars.envName, envFlag, envFlagShort, "", envFlagDescription) + cmd.Flags().StringVar(&vars.imageTag, imageTagFlag, "", imageTagFlagDescription) + cmd.Flags().StringToStringVar(&vars.resourceTags, resourceTagsFlag, nil, resourceTagsFlagDescription) return cmd } diff --git a/internal/pkg/cli/svc_deploy_test.go b/internal/pkg/cli/svc_deploy_test.go index b0d804488de..de729e4b31e 100644 --- a/internal/pkg/cli/svc_deploy_test.go +++ b/internal/pkg/cli/svc_deploy_test.go @@ -94,11 +94,9 @@ func TestSvcDeployOpts_Validate(t *testing.T) { tc.mockStore(mockStore) opts := deploySvcOpts{ deploySvcVars: deploySvcVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - }, - Name: tc.inSvcName, - EnvName: tc.inEnvName, + appName: tc.inAppName, + name: tc.inSvcName, + envName: tc.inEnvName, }, ws: mockWs, store: mockStore, @@ -169,12 +167,10 @@ func TestSvcDeployOpts_Ask(t *testing.T) { tc.wantedCalls(mockSel) opts := deploySvcOpts{ deploySvcVars: deploySvcVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inAppName, - }, - Name: tc.inSvcName, - EnvName: tc.inEnvName, - ImageTag: tc.inImageTag, + appName: tc.inAppName, + name: tc.inSvcName, + envName: tc.inEnvName, + imageTag: tc.inImageTag, }, sel: mockSel, } @@ -185,9 +181,9 @@ func TestSvcDeployOpts_Ask(t *testing.T) { // THEN if tc.wantedError == nil { require.NoError(t, err) - require.Equal(t, tc.wantedSvcName, opts.Name) - require.Equal(t, tc.wantedEnvName, opts.EnvName) - require.Equal(t, tc.wantedImageTag, opts.ImageTag) + require.Equal(t, tc.wantedSvcName, opts.name) + require.Equal(t, tc.wantedEnvName, opts.envName) + require.Equal(t, tc.wantedImageTag, opts.imageTag) } else { require.EqualError(t, err, tc.wantedError.Error()) } @@ -303,7 +299,7 @@ image: } opts := deploySvcOpts{ deploySvcVars: deploySvcVars{ - Name: test.inputSvc, + name: test.inputSvc, }, ws: mockWorkspace, unmarshal: unmarshaler, @@ -455,7 +451,7 @@ func TestSvcDeployOpts_pushAddonsTemplateToS3Bucket(t *testing.T) { opts := deploySvcOpts{ deploySvcVars: deploySvcVars{ - Name: tc.inputSvc, + name: tc.inputSvc, }, store: mockProjectSvc, appCFN: mockProjectResourcesGetter, diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 41a73ed3098..9dbca041cb9 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -60,11 +60,11 @@ const ( ) type initSvcVars struct { - *GlobalOpts - ServiceType string - Name string - DockerfilePath string - Port uint16 + appName string + serviceType string + name string + dockerfilePath string + port uint16 } type initSvcOpts struct { @@ -76,6 +76,7 @@ type initSvcOpts struct { store store appDeployer appDeployer prog progress + prompt prompter df dockerfileParser // Outputs stored on successful actions. @@ -110,35 +111,36 @@ func newInitSvcOpts(vars initSvcVars) (*initSvcOpts, error) { ws: ws, appDeployer: cloudformation.New(sess), prog: termprogress.NewSpinner(), + prompt: prompt.New(), setupParser: func(o *initSvcOpts) { - o.df = dockerfile.New(o.fs, o.DockerfilePath) + o.df = dockerfile.New(o.fs, o.dockerfilePath) }, }, nil } // Validate returns an error if the flag values passed by the user are invalid. func (o *initSvcOpts) Validate() error { - if o.AppName() == "" { + if o.appName == "" { return errNoAppInWorkspace } - if o.ServiceType != "" { - if err := validateSvcType(o.ServiceType); err != nil { + if o.serviceType != "" { + if err := validateSvcType(o.serviceType); err != nil { return err } } - if o.Name != "" { - if err := validateSvcName(o.Name); err != nil { + if o.name != "" { + if err := validateSvcName(o.name); err != nil { return err } } - if o.DockerfilePath != "" { - if _, err := o.fs.Stat(o.DockerfilePath); err != nil { + if o.dockerfilePath != "" { + if _, err := o.fs.Stat(o.dockerfilePath); err != nil { return err } } - if o.Port != 0 { - if err := validateSvcPort(o.Port); err != nil { + if o.port != 0 { + if err := validateSvcPort(o.port); err != nil { return err } } @@ -165,9 +167,9 @@ func (o *initSvcOpts) Ask() error { // Execute writes the service's manifest file and stores the service in SSM. func (o *initSvcOpts) Execute() error { - app, err := o.store.GetApplication(o.AppName()) + app, err := o.store.GetApplication(o.appName) if err != nil { - return fmt.Errorf("get application %s: %w", o.AppName(), err) + return fmt.Errorf("get application %s: %w", o.appName, err) } manifestPath, err := o.createManifest() @@ -176,19 +178,19 @@ func (o *initSvcOpts) Execute() error { } o.manifestPath = manifestPath - o.prog.Start(fmt.Sprintf(fmtAddSvcToAppStart, o.Name)) - if err := o.appDeployer.AddServiceToApp(app, o.Name); err != nil { - o.prog.Stop(log.Serrorf(fmtAddSvcToAppFailed, o.Name)) - return fmt.Errorf("add service %s to application %s: %w", o.Name, o.AppName(), err) + o.prog.Start(fmt.Sprintf(fmtAddSvcToAppStart, o.name)) + if err := o.appDeployer.AddServiceToApp(app, o.name); err != nil { + o.prog.Stop(log.Serrorf(fmtAddSvcToAppFailed, o.name)) + return fmt.Errorf("add service %s to application %s: %w", o.name, o.appName, err) } - o.prog.Stop(log.Ssuccessf(fmtAddSvcToAppComplete, o.Name)) + o.prog.Stop(log.Ssuccessf(fmtAddSvcToAppComplete, o.name)) if err := o.store.CreateService(&config.Service{ - App: o.AppName(), - Name: o.Name, - Type: o.ServiceType, + App: o.appName, + Name: o.name, + Type: o.serviceType, }); err != nil { - return fmt.Errorf("saving service %s: %w", o.Name, err) + return fmt.Errorf("saving service %s: %w", o.name, err) } return nil } @@ -199,7 +201,7 @@ func (o *initSvcOpts) createManifest() (string, error) { return "", err } var manifestExists bool - manifestPath, err := o.ws.WriteWorkloadManifest(manifest, o.Name) + manifestPath, err := o.ws.WriteWorkloadManifest(manifest, o.name) if err != nil { e, ok := err.(*workspace.ErrFileExists) if !ok { @@ -217,21 +219,21 @@ func (o *initSvcOpts) createManifest() (string, error) { if manifestExists { manifestMsgFmt = "Manifest file for service %s already exists at %s, skipping writing it.\n" } - log.Successf(manifestMsgFmt, color.HighlightUserInput(o.Name), color.HighlightResource(manifestPath)) - log.Infoln(color.Help(fmt.Sprintf("Your manifest contains configurations like your container size and port (:%d).", o.Port))) + log.Successf(manifestMsgFmt, color.HighlightUserInput(o.name), color.HighlightResource(manifestPath)) + log.Infoln(color.Help(fmt.Sprintf("Your manifest contains configurations like your container size and port (:%d).", o.port))) log.Infoln() return manifestPath, nil } func (o *initSvcOpts) newManifest() (encoding.BinaryMarshaler, error) { - switch o.ServiceType { + switch o.serviceType { case manifest.LoadBalancedWebServiceType: return o.newLoadBalancedWebServiceManifest() case manifest.BackendServiceType: return o.newBackendServiceManifest() default: - return nil, fmt.Errorf("service type %s doesn't have a manifest", o.ServiceType) + return nil, fmt.Errorf("service type %s doesn't have a manifest", o.serviceType) } } @@ -242,21 +244,21 @@ func (o *initSvcOpts) newLoadBalancedWebServiceManifest() (*manifest.LoadBalance } props := &manifest.LoadBalancedWebServiceProps{ WorkloadProps: &manifest.WorkloadProps{ - Name: o.Name, + Name: o.name, Dockerfile: dfPath, }, - Port: o.Port, + Port: o.port, Path: "/", } - existingSvcs, err := o.store.ListServices(o.AppName()) + existingSvcs, err := o.store.ListServices(o.appName) if err != nil { return nil, err } // We default to "/" for the first service, but if there's another // Load Balanced Web Service, we use the svc name as the default, instead. for _, existingSvc := range existingSvcs { - if existingSvc.Type == manifest.LoadBalancedWebServiceType && existingSvc.Name != o.Name { - props.Path = o.Name + if existingSvc.Type == manifest.LoadBalancedWebServiceType && existingSvc.Name != o.name { + props.Path = o.name break } } @@ -274,16 +276,16 @@ func (o *initSvcOpts) newBackendServiceManifest() (*manifest.BackendService, err } return manifest.NewBackendService(manifest.BackendServiceProps{ WorkloadProps: manifest.WorkloadProps{ - Name: o.Name, + Name: o.name, Dockerfile: dfPath, }, - Port: o.Port, + Port: o.port, HealthCheck: hc, }), nil } func (o *initSvcOpts) askSvcType() error { - if o.ServiceType != "" { + if o.serviceType != "" { return nil } @@ -296,24 +298,24 @@ func (o *initSvcOpts) askSvcType() error { if err != nil { return fmt.Errorf("select service type: %w", err) } - o.ServiceType = t + o.serviceType = t return nil } func (o *initSvcOpts) askSvcName() error { - if o.Name != "" { + if o.name != "" { return nil } name, err := o.prompt.Get( - fmt.Sprintf(fmtWkldInitNamePrompt, color.Emphasize("name"), color.HighlightUserInput(o.ServiceType)), - fmt.Sprintf(fmtWkldInitNameHelpPrompt, service, o.AppName()), + fmt.Sprintf(fmtWkldInitNamePrompt, color.Emphasize("name"), color.HighlightUserInput(o.serviceType)), + fmt.Sprintf(fmtWkldInitNameHelpPrompt, service, o.appName), validateSvcName, prompt.WithFinalMessage("Service name:")) if err != nil { return fmt.Errorf("get service name: %w", err) } - o.Name = name + o.name = name return nil } @@ -354,21 +356,21 @@ func askDockerfile(wkldName string, fs afero.Fs, p prompter) (string, error) { // askDockerfile prompts for the Dockerfile by looking at sub-directories with a Dockerfile. func (o *initSvcOpts) askDockerfile() error { - if o.DockerfilePath != "" { + if o.dockerfilePath != "" { return nil } - df, err := askDockerfile(o.Name, o.fs, o.prompt) + df, err := askDockerfile(o.name, o.fs, o.prompt) if err != nil { return err } - o.DockerfilePath = df + o.dockerfilePath = df return nil } func (o *initSvcOpts) askSvcPort() error { // Use flag before anything else - if o.Port != 0 { + if o.port != 0 { return nil } @@ -384,7 +386,7 @@ func (o *initSvcOpts) askSvcPort() error { // There were no ports detected, keep the default port prompt. defaultPort = defaultSvcPortString case 1: - o.Port = ports[0] + o.port = ports[0] return nil default: defaultPort = strconv.Itoa(int(ports[0])) @@ -406,7 +408,7 @@ func (o *initSvcOpts) askSvcPort() error { return fmt.Errorf("parse port string: %w", err) } - o.Port = uint16(portUint) + o.port = uint16(portUint) return nil } @@ -417,7 +419,7 @@ func (o *initSvcOpts) getRelativePath() (string, error) { return "", fmt.Errorf("get copilot directory: %w", err) } wsRoot := filepath.Dir(copilotDirPath) - absDfPath, err := filepath.Abs(o.DockerfilePath) + absDfPath, err := filepath.Abs(o.dockerfilePath) if err != nil { return "", fmt.Errorf("get absolute path: %v", err) } @@ -435,7 +437,7 @@ func (o *initSvcOpts) parseHealthCheck() (*manifest.ContainerHealthCheck, error) o.setupParser(o) hc, err := o.df.GetHealthCheck() if err != nil { - return nil, fmt.Errorf("get healthcheck from Dockerfile: %s, %w", o.DockerfilePath, err) + return nil, fmt.Errorf("get healthcheck from Dockerfile: %s, %w", o.dockerfilePath, err) } if hc == nil { return nil, nil @@ -454,16 +456,14 @@ func (o *initSvcOpts) RecommendedActions() []string { return []string{ fmt.Sprintf("Update your manifest %s to change the defaults.", color.HighlightResource(o.manifestPath)), fmt.Sprintf("Run %s to deploy your service to a %s environment.", - color.HighlightCode(fmt.Sprintf("copilot svc deploy --name %s --env %s", o.Name, defaultEnvironmentName)), + color.HighlightCode(fmt.Sprintf("copilot svc deploy --name %s --env %s", o.name, defaultEnvironmentName)), defaultEnvironmentName), } } -// BuildSvcInitCmd build the command for creating a new service. -func BuildSvcInitCmd() *cobra.Command { - vars := initSvcVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildSvcInitCmd build the command for creating a new service. +func buildSvcInitCmd() *cobra.Command { + vars := initSvcVars{} cmd := &cobra.Command{ Use: "init", Short: "Creates a new service in an application.", @@ -497,10 +497,11 @@ This command is also run as part of "copilot init".`, return nil }), } - cmd.Flags().StringVarP(&vars.Name, nameFlag, nameFlagShort, "", svcFlagDescription) - cmd.Flags().StringVarP(&vars.ServiceType, svcTypeFlag, svcTypeFlagShort, "", svcTypeFlagDescription) - cmd.Flags().StringVarP(&vars.DockerfilePath, dockerFileFlag, dockerFileFlagShort, "", dockerFileFlagDescription) - cmd.Flags().Uint16Var(&vars.Port, svcPortFlag, 0, svcPortFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", svcFlagDescription) + cmd.Flags().StringVarP(&vars.serviceType, svcTypeFlag, svcTypeFlagShort, "", svcTypeFlagDescription) + cmd.Flags().StringVarP(&vars.dockerfilePath, dockerFileFlag, dockerFileFlagShort, "", dockerFileFlagDescription) + cmd.Flags().Uint16Var(&vars.port, svcPortFlag, 0, svcPortFlagDescription) // Bucket flags by service type. requiredFlags := pflag.NewFlagSet("Required Flags", pflag.ContinueOnError) diff --git a/internal/pkg/cli/svc_init_test.go b/internal/pkg/cli/svc_init_test.go index 06aa0b23b77..ed8516ee386 100644 --- a/internal/pkg/cli/svc_init_test.go +++ b/internal/pkg/cli/svc_init_test.go @@ -69,11 +69,11 @@ func TestSvcInitOpts_Validate(t *testing.T) { t.Run(name, func(t *testing.T) { opts := initSvcOpts{ initSvcVars: initSvcVars{ - ServiceType: tc.inSvcType, - Name: tc.inSvcName, - DockerfilePath: tc.inDockerfilePath, - Port: tc.inSvcPort, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + serviceType: tc.inSvcType, + name: tc.inSvcName, + dockerfilePath: tc.inDockerfilePath, + port: tc.inSvcPort, + appName: tc.inAppName, }, fs: &afero.Afero{Fs: afero.NewMemMapFs()}, } @@ -331,17 +331,15 @@ func TestSvcInitOpts_Ask(t *testing.T) { mockDockerfile := mocks.NewMockdockerfileParser(ctrl) opts := &initSvcOpts{ initSvcVars: initSvcVars{ - ServiceType: tc.inSvcType, - Name: tc.inSvcName, - Port: tc.inSvcPort, - DockerfilePath: tc.inDockerfilePath, - GlobalOpts: &GlobalOpts{ - prompt: mockPrompt, - }, + serviceType: tc.inSvcType, + name: tc.inSvcName, + port: tc.inSvcPort, + dockerfilePath: tc.inDockerfilePath, }, fs: &afero.Afero{Fs: afero.NewMemMapFs()}, setupParser: func(o *initSvcOpts) {}, df: mockDockerfile, + prompt: mockPrompt, } tc.mockFileSystem(opts.fs) tc.mockPrompt(mockPrompt) @@ -355,9 +353,9 @@ func TestSvcInitOpts_Ask(t *testing.T) { require.EqualError(t, err, tc.wantedErr.Error()) } else { require.NoError(t, err) - require.Equal(t, wantedSvcType, opts.ServiceType) - require.Equal(t, wantedSvcName, opts.Name) - require.Equal(t, wantedDockerfilePath, opts.DockerfilePath) + require.Equal(t, wantedSvcType, opts.serviceType) + require.Equal(t, wantedSvcName, opts.name) + require.Equal(t, wantedDockerfilePath, opts.dockerfilePath) } }) } @@ -637,11 +635,11 @@ func TestAppInitOpts_Execute(t *testing.T) { } opts := initSvcOpts{ initSvcVars: initSvcVars{ - ServiceType: tc.inSvcType, - Name: tc.inSvcName, - Port: tc.inSvcPort, - DockerfilePath: tc.inDockerfilePath, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + serviceType: tc.inSvcType, + name: tc.inSvcName, + port: tc.inSvcPort, + dockerfilePath: tc.inDockerfilePath, + appName: tc.inAppName, }, setupParser: func(o *initSvcOpts) {}, ws: mockWriter, @@ -770,11 +768,11 @@ func TestAppInitOpts_createLoadBalancedAppManifest(t *testing.T) { } opts := initSvcOpts{ initSvcVars: initSvcVars{ - ServiceType: manifest.LoadBalancedWebServiceType, - Name: tc.inSvcName, - Port: tc.inSvcPort, - DockerfilePath: tc.inDockerfilePath, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + serviceType: manifest.LoadBalancedWebServiceType, + name: tc.inSvcName, + port: tc.inSvcPort, + dockerfilePath: tc.inDockerfilePath, + appName: tc.inAppName, }, ws: mockWriter, store: mockstore, diff --git a/internal/pkg/cli/svc_list.go b/internal/pkg/cli/svc_list.go index d8ab9a6859f..c04461a2e83 100644 --- a/internal/pkg/cli/svc_list.go +++ b/internal/pkg/cli/svc_list.go @@ -13,6 +13,7 @@ import ( "text/tabwriter" "github.com/aws/copilot-cli/internal/pkg/config" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/aws/copilot-cli/internal/pkg/workspace" "github.com/spf13/cobra" @@ -31,9 +32,9 @@ const ( ) type listSvcVars struct { - *GlobalOpts - ShouldOutputJSON bool - ShouldShowLocalServices bool + appName string + shouldOutputJSON bool + shouldShowLocalServices bool } type listSvcOpts struct { @@ -61,13 +62,13 @@ func newListSvcOpts(vars listSvcVars) (*listSvcOpts, error) { store: store, ws: ws, w: os.Stdout, - sel: selector.NewSelect(vars.prompt, store), + sel: selector.NewSelect(prompt.New(), store), }, nil } // Ask asks for fields that are required but not passed in. func (o *listSvcOpts) Ask() error { - if o.AppName() != "" { + if o.appName != "" { return nil } @@ -82,16 +83,16 @@ func (o *listSvcOpts) Ask() error { // Execute lists the services through the prompt. func (o *listSvcOpts) Execute() error { // Ensure the application actually exists before we try to list its services. - if _, err := o.store.GetApplication(o.AppName()); err != nil { + if _, err := o.store.GetApplication(o.appName); err != nil { return fmt.Errorf("get application: %w", err) } - svcs, err := o.store.ListServices(o.AppName()) + svcs, err := o.store.ListServices(o.appName) if err != nil { return err } - if o.ShouldShowLocalServices { + if o.shouldShowLocalServices { localNames, err := o.ws.ServiceNames() if err != nil { return fmt.Errorf("get local services names: %w", err) @@ -100,7 +101,7 @@ func (o *listSvcOpts) Execute() error { } var out string - if o.ShouldOutputJSON { + if o.shouldOutputJSON { data, err := o.jsonOutput(svcs) if err != nil { return err @@ -156,11 +157,9 @@ func filterSvcsByName(svcs []*config.Service, wantedNames []string) []*config.Se return filtered } -// BuildSvcListCmd builds the command for listing services in an appication. -func BuildSvcListCmd() *cobra.Command { - vars := listSvcVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildSvcListCmd builds the command for listing services in an appication. +func buildSvcListCmd() *cobra.Command { + vars := listSvcVars{} cmd := &cobra.Command{ Use: "ls", Short: "Lists all the services in an application.", @@ -178,8 +177,8 @@ func BuildSvcListCmd() *cobra.Command { return opts.Execute() }), } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) - cmd.Flags().BoolVar(&vars.ShouldOutputJSON, jsonFlag, false, jsonFlagDescription) - cmd.Flags().BoolVar(&vars.ShouldShowLocalServices, localFlag, false, localSvcFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().BoolVar(&vars.shouldOutputJSON, jsonFlag, false, jsonFlagDescription) + cmd.Flags().BoolVar(&vars.shouldShowLocalServices, localFlag, false, localSvcFlagDescription) return cmd } diff --git a/internal/pkg/cli/svc_list_test.go b/internal/pkg/cli/svc_list_test.go index 84151e3d1dd..e5f5da91239 100644 --- a/internal/pkg/cli/svc_list_test.go +++ b/internal/pkg/cli/svc_list_test.go @@ -30,10 +30,8 @@ func TestListSvcOpts_Execute(t *testing.T) { "with json outputs": { opts: listSvcOpts{ listSvcVars: listSvcVars{ - ShouldOutputJSON: true, - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + shouldOutputJSON: true, + appName: "coolapp", }, store: mockstore, }, @@ -54,9 +52,7 @@ func TestListSvcOpts_Execute(t *testing.T) { "with human outputs": { opts: listSvcOpts{ listSvcVars: listSvcVars{ - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + appName: "coolapp", }, store: mockstore, }, @@ -78,9 +74,7 @@ func TestListSvcOpts_Execute(t *testing.T) { expectedErr: fmt.Errorf("get application: %w", mockError), opts: listSvcOpts{ listSvcVars: listSvcVars{ - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + appName: "coolapp", }, store: mockstore, }, @@ -99,9 +93,7 @@ func TestListSvcOpts_Execute(t *testing.T) { expectedErr: mockError, opts: listSvcOpts{ listSvcVars: listSvcVars{ - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + appName: "coolapp", }, store: mockstore, }, @@ -120,10 +112,8 @@ func TestListSvcOpts_Execute(t *testing.T) { expectedErr: nil, opts: listSvcOpts{ listSvcVars: listSvcVars{ - ShouldShowLocalServices: true, - GlobalOpts: &GlobalOpts{ - appName: "coolapp", - }, + shouldShowLocalServices: true, + appName: "coolapp", }, store: mockstore, ws: mockWorkspace, @@ -195,9 +185,7 @@ func TestListSvcOpts_Ask(t *testing.T) { listApps := &listSvcOpts{ listSvcVars: listSvcVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.inApp, - }, + appName: tc.inApp, }, sel: mockSel, } @@ -205,7 +193,7 @@ func TestListSvcOpts_Ask(t *testing.T) { err := listApps.Ask() require.NoError(t, err) - require.Equal(t, tc.wantedApp, listApps.AppName(), "expected application names to match") + require.Equal(t, tc.wantedApp, listApps.appName, "expected application names to match") }) } } diff --git a/internal/pkg/cli/svc_logs.go b/internal/pkg/cli/svc_logs.go index e5addc7f6bc..85c37351f20 100644 --- a/internal/pkg/cli/svc_logs.go +++ b/internal/pkg/cli/svc_logs.go @@ -15,6 +15,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/deploy" "github.com/aws/copilot-cli/internal/pkg/ecslogging" "github.com/aws/copilot-cli/internal/pkg/term/log" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/spf13/cobra" ) @@ -35,11 +36,11 @@ type svcLogsVars struct { limit int svcName string envName string + appName string humanStartTime string humanEndTime string taskIDs []string since time.Duration - *GlobalOpts } type svcLogsOpts struct { @@ -71,14 +72,14 @@ func newSvcLogOpts(vars svcLogsVars) (*svcLogsOpts, error) { w: log.OutputWriter, configStore: configStore, deployStore: deployStore, - sel: selector.NewDeploySelect(vars.prompt, configStore, deployStore), + sel: selector.NewDeploySelect(prompt.New(), configStore, deployStore), } opts.initLogsSvc = func() error { configStore, err := config.NewStore() if err != nil { return fmt.Errorf("connect to environment config store: %w", err) } - env, err := configStore.GetEnvironment(opts.AppName(), opts.envName) + env, err := configStore.GetEnvironment(opts.appName, opts.envName) if err != nil { return fmt.Errorf("get environment: %w", err) } @@ -86,7 +87,7 @@ func newSvcLogOpts(vars svcLogsVars) (*svcLogsOpts, error) { if err != nil { return err } - opts.logsSvc = ecslogging.NewServiceClient(sess, opts.AppName(), opts.envName, opts.svcName) + opts.logsSvc = ecslogging.NewServiceClient(sess, opts.appName, opts.envName, opts.svcName) return nil } return opts, nil @@ -94,8 +95,8 @@ func newSvcLogOpts(vars svcLogsVars) (*svcLogsOpts, error) { // Validate returns an error if the values provided by flags are invalid. func (o *svcLogsOpts) Validate() error { - if o.AppName() != "" { - _, err := o.configStore.GetApplication(o.AppName()) + if o.appName != "" { + _, err := o.configStore.GetApplication(o.appName) if err != nil { return err } @@ -172,7 +173,7 @@ func (o *svcLogsOpts) Execute() error { } func (o *svcLogsOpts) askApp() error { - if o.AppName() != "" { + if o.appName != "" { return nil } app, err := o.sel.Application(svcLogAppNamePrompt, svcLogAppNameHelpPrompt) @@ -184,9 +185,9 @@ func (o *svcLogsOpts) askApp() error { } func (o *svcLogsOpts) askSvcEnvName() error { - deployedService, err := o.sel.DeployedService(svcLogNamePrompt, svcLogNameHelpPrompt, o.AppName(), selector.WithEnv(o.envName), selector.WithSvc(o.svcName)) + deployedService, err := o.sel.DeployedService(svcLogNamePrompt, svcLogNameHelpPrompt, o.appName, selector.WithEnv(o.envName), selector.WithSvc(o.svcName)) if err != nil { - return fmt.Errorf("select deployed services for application %s: %w", o.AppName(), err) + return fmt.Errorf("select deployed services for application %s: %w", o.appName, err) } o.svcName = deployedService.Svc o.envName = deployedService.Env @@ -207,11 +208,9 @@ func (o *svcLogsOpts) parseRFC3339(timeStr string) (int64, error) { return startTimeTmp.Unix() * 1000, nil } -// BuildSvcLogsCmd builds the command for displaying service logs in an application. -func BuildSvcLogsCmd() *cobra.Command { - vars := svcLogsVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildSvcLogsCmd builds the command for displaying service logs in an application. +func buildSvcLogsCmd() *cobra.Command { + vars := svcLogsVars{} cmd := &cobra.Command{ Use: "logs", Short: "Displays logs of a deployed service.", @@ -241,9 +240,9 @@ func BuildSvcLogsCmd() *cobra.Command { return opts.Execute() }), } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) cmd.Flags().StringVarP(&vars.svcName, nameFlag, nameFlagShort, "", svcFlagDescription) cmd.Flags().StringVarP(&vars.envName, envFlag, envFlagShort, "", envFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) cmd.Flags().StringVar(&vars.humanStartTime, startTimeFlag, "", startTimeFlagDescription) cmd.Flags().StringVar(&vars.humanEndTime, endTimeFlag, "", endTimeFlagDescription) cmd.Flags().BoolVar(&vars.shouldOutputJSON, jsonFlag, false, jsonFlagDescription) diff --git a/internal/pkg/cli/svc_logs_test.go b/internal/pkg/cli/svc_logs_test.go index 2d855f5d409..d9e5e473f4b 100644 --- a/internal/pkg/cli/svc_logs_test.go +++ b/internal/pkg/cli/svc_logs_test.go @@ -24,7 +24,6 @@ type svcLogsMock struct { func TestSvcLogs_Validate(t *testing.T) { const ( - mockLimit = 3 mockSince = 1 * time.Minute mockStartTime = "1970-01-01T01:01:01+00:00" mockBadStartTime = "badStartTime" @@ -132,9 +131,7 @@ func TestSvcLogs_Validate(t *testing.T) { humanEndTime: tc.inputEndTime, since: tc.inputSince, svcName: tc.inputSvc, - GlobalOpts: &GlobalOpts{ - appName: tc.inputApp, - }, + appName: tc.inputApp, }, configStore: mockstore, } @@ -237,9 +234,7 @@ func TestSvcLogs_Ask(t *testing.T) { svcLogsVars: svcLogsVars{ envName: tc.inputEnvName, svcName: tc.inputSvc, - GlobalOpts: &GlobalOpts{ - appName: tc.inputApp, - }, + appName: tc.inputApp, }, configStore: mockstore, sel: mockSel, diff --git a/internal/pkg/cli/svc_package.go b/internal/pkg/cli/svc_package.go index 57414a5a11f..417111c12ef 100644 --- a/internal/pkg/cli/svc_package.go +++ b/internal/pkg/cli/svc_package.go @@ -31,7 +31,7 @@ const ( ) var initPackageAddonsSvc = func(o *packageSvcOpts) error { - addonsSvc, err := addon.New(o.Name) + addonsSvc, err := addon.New(o.name) if err != nil { return fmt.Errorf("initiate addons service: %w", err) } @@ -40,11 +40,11 @@ var initPackageAddonsSvc = func(o *packageSvcOpts) error { } type packageSvcVars struct { - *GlobalOpts - Name string - EnvName string - Tag string - OutputDir string + name string + envName string + appName string + tag string + outputDir string } type packageSvcOpts struct { @@ -62,6 +62,7 @@ type packageSvcOpts struct { fs afero.Fs runner runner sel wsSelector + prompt prompter stackSerializer func(mft interface{}, env *config.Environment, app *config.Application, rc stack.RuntimeConfig) (stackSerializer, error) } @@ -80,6 +81,7 @@ func newPackageSvcOpts(vars packageSvcVars) (*packageSvcOpts, error) { return nil, fmt.Errorf("retrieve default session: %w", err) } + prompter := prompt.New() opts := &packageSvcOpts{ packageSvcVars: vars, initAddonsSvc: initPackageAddonsSvc, @@ -87,7 +89,8 @@ func newPackageSvcOpts(vars packageSvcVars) (*packageSvcOpts, error) { store: store, appCFN: cloudformation.New(sess), runner: command.New(), - sel: selector.NewWorkspaceSelect(vars.prompt, store, ws), + sel: selector.NewWorkspaceSelect(prompter, store, ws), + prompt: prompter, stackWriter: os.Stdout, paramsWriter: ioutil.Discard, addonsWriter: ioutil.Discard, @@ -124,20 +127,20 @@ func newPackageSvcOpts(vars packageSvcVars) (*packageSvcOpts, error) { // Validate returns an error if the values provided by the user are invalid. func (o *packageSvcOpts) Validate() error { - if o.AppName() == "" { + if o.appName == "" { return errNoAppInWorkspace } - if o.Name != "" { + if o.name != "" { names, err := o.ws.ServiceNames() if err != nil { return fmt.Errorf("list services in the workspace: %w", err) } - if !contains(o.Name, names) { - return fmt.Errorf("service '%s' does not exist in the workspace", o.Name) + if !contains(o.name, names) { + return fmt.Errorf("service '%s' does not exist in the workspace", o.name) } } - if o.EnvName != "" { - if _, err := o.store.GetEnvironment(o.AppName(), o.EnvName); err != nil { + if o.envName != "" { + if _, err := o.store.GetEnvironment(o.appName, o.envName); err != nil { return err } } @@ -157,12 +160,12 @@ func (o *packageSvcOpts) Ask() error { // Execute prints the CloudFormation template of the application for the environment. func (o *packageSvcOpts) Execute() error { - env, err := o.store.GetEnvironment(o.AppName(), o.EnvName) + env, err := o.store.GetEnvironment(o.appName, o.envName) if err != nil { return err } - if o.OutputDir != "" { + if o.outputDir != "" { if err := o.setOutputFileWriters(); err != nil { return err } @@ -190,7 +193,7 @@ func (o *packageSvcOpts) Execute() error { } // Addons template won't show up without setting --output-dir flag. - if o.OutputDir != "" { + if o.outputDir != "" { if err := o.setAddonsFileWriter(); err != nil { return err } @@ -201,7 +204,7 @@ func (o *packageSvcOpts) Execute() error { } func (o *packageSvcOpts) askAppName() error { - if o.Name != "" { + if o.name != "" { return nil } @@ -209,25 +212,25 @@ func (o *packageSvcOpts) askAppName() error { if err != nil { return fmt.Errorf("select service: %w", err) } - o.Name = name + o.name = name return nil } func (o *packageSvcOpts) askEnvName() error { - if o.EnvName != "" { + if o.envName != "" { return nil } - name, err := o.sel.Environment(svcPackageEnvNamePrompt, "", o.AppName()) + name, err := o.sel.Environment(svcPackageEnvNamePrompt, "", o.appName) if err != nil { return fmt.Errorf("select environment: %w", err) } - o.EnvName = name + o.envName = name return nil } func (o *packageSvcOpts) askTag() error { - if o.Tag != "" { + if o.tag != "" { return nil } @@ -239,7 +242,7 @@ func (o *packageSvcOpts) askTag() error { return fmt.Errorf("prompt get image tag: %w", err) } } - o.Tag = tag + o.tag = tag return nil } @@ -257,7 +260,7 @@ type svcCfnTemplates struct { // getSvcTemplates returns the CloudFormation stack's template and its parameters for the service. func (o *packageSvcOpts) getSvcTemplates(env *config.Environment) (*svcCfnTemplates, error) { - raw, err := o.ws.ReadWorkloadManifest(o.Name) + raw, err := o.ws.ReadWorkloadManifest(o.name) if err != nil { return nil, err } @@ -266,7 +269,7 @@ func (o *packageSvcOpts) getSvcTemplates(env *config.Environment) (*svcCfnTempla return nil, err } - app, err := o.store.GetApplication(o.AppName()) + app, err := o.store.GetApplication(o.appName) if err != nil { return nil, err } @@ -275,17 +278,17 @@ func (o *packageSvcOpts) getSvcTemplates(env *config.Environment) (*svcCfnTempla return nil, err } - repoURL, ok := resources.RepositoryURLs[o.Name] + repoURL, ok := resources.RepositoryURLs[o.name] if !ok { return nil, &errRepoNotFound{ - svcName: o.Name, + svcName: o.name, envRegion: env.Region, appAccountID: app.AccountID, } } serializer, err := o.stackSerializer(mft, env, app, stack.RuntimeConfig{ ImageRepoURL: repoURL, - ImageTag: o.Tag, + ImageTag: o.tag, AdditionalTags: app.Tags, }) if err != nil { @@ -304,20 +307,20 @@ func (o *packageSvcOpts) getSvcTemplates(env *config.Environment) (*svcCfnTempla // setOutputFileWriters creates the output directory, and updates the template and param writers to file writers in the directory. func (o *packageSvcOpts) setOutputFileWriters() error { - if err := o.fs.MkdirAll(o.OutputDir, 0755); err != nil { - return fmt.Errorf("create directory %s: %w", o.OutputDir, err) + if err := o.fs.MkdirAll(o.outputDir, 0755); err != nil { + return fmt.Errorf("create directory %s: %w", o.outputDir, err) } - templatePath := filepath.Join(o.OutputDir, - fmt.Sprintf(config.ServiceCfnTemplateNameFormat, o.Name)) + templatePath := filepath.Join(o.outputDir, + fmt.Sprintf(config.ServiceCfnTemplateNameFormat, o.name)) templateFile, err := o.fs.Create(templatePath) if err != nil { return fmt.Errorf("create file %s: %w", templatePath, err) } o.stackWriter = templateFile - paramsPath := filepath.Join(o.OutputDir, - fmt.Sprintf(config.ServiceCfnTemplateConfigurationNameFormat, o.Name, o.EnvName)) + paramsPath := filepath.Join(o.outputDir, + fmt.Sprintf(config.ServiceCfnTemplateConfigurationNameFormat, o.name, o.envName)) paramsFile, err := o.fs.Create(paramsPath) if err != nil { return fmt.Errorf("create file %s: %w", paramsPath, err) @@ -328,8 +331,8 @@ func (o *packageSvcOpts) setOutputFileWriters() error { } func (o *packageSvcOpts) setAddonsFileWriter() error { - addonsPath := filepath.Join(o.OutputDir, - fmt.Sprintf(config.AddonsCfnTemplateNameFormat, o.Name)) + addonsPath := filepath.Join(o.outputDir, + fmt.Sprintf(config.AddonsCfnTemplateNameFormat, o.name)) addonsFile, err := o.fs.Create(addonsPath) if err != nil { return fmt.Errorf("create file %s: %w", addonsPath, err) @@ -368,11 +371,9 @@ func (e *errRepoNotFound) Is(target error) bool { e.appAccountID == t.appAccountID } -// BuildSvcPackageCmd builds the command for printing a service's CloudFormation template. -func BuildSvcPackageCmd() *cobra.Command { - vars := packageSvcVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildSvcPackageCmd builds the command for printing a service's CloudFormation template. +func buildSvcPackageCmd() *cobra.Command { + vars := packageSvcVars{} cmd := &cobra.Command{ Use: "package", Short: "Prints the AWS CloudFormation template of a service.", @@ -400,10 +401,10 @@ func BuildSvcPackageCmd() *cobra.Command { return opts.Execute() }), } - // Set the defaults to opts.{Field} otherwise cobra overrides the values set by the constructor. - cmd.Flags().StringVarP(&vars.Name, nameFlag, nameFlagShort, "", svcFlagDescription) - cmd.Flags().StringVarP(&vars.EnvName, envFlag, envFlagShort, "", envFlagDescription) - cmd.Flags().StringVar(&vars.Tag, imageTagFlag, "", imageTagFlagDescription) - cmd.Flags().StringVar(&vars.OutputDir, stackOutputDirFlag, "", stackOutputDirFlagDescription) + cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", svcFlagDescription) + cmd.Flags().StringVarP(&vars.envName, envFlag, envFlagShort, "", envFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) + cmd.Flags().StringVar(&vars.tag, imageTagFlag, "", imageTagFlagDescription) + cmd.Flags().StringVar(&vars.outputDir, stackOutputDirFlag, "", stackOutputDirFlagDescription) return cmd } diff --git a/internal/pkg/cli/svc_package_test.go b/internal/pkg/cli/svc_package_test.go index 33109afa95a..ffc71fc7a16 100644 --- a/internal/pkg/cli/svc_package_test.go +++ b/internal/pkg/cli/svc_package_test.go @@ -90,9 +90,9 @@ func TestPackageSvcOpts_Validate(t *testing.T) { opts := &packageSvcOpts{ packageSvcVars: packageSvcVars{ - Name: tc.inSvcName, - EnvName: tc.inEnvName, - GlobalOpts: &GlobalOpts{appName: tc.inAppName}, + name: tc.inSvcName, + envName: tc.inEnvName, + appName: tc.inAppName, }, ws: mockWorkspace, store: mockStore, @@ -213,15 +213,13 @@ func TestPackageSvcOpts_Ask(t *testing.T) { opts := &packageSvcOpts{ packageSvcVars: packageSvcVars{ - Name: tc.inSvcName, - EnvName: tc.inEnvName, - Tag: tc.inTag, - GlobalOpts: &GlobalOpts{ - appName: testAppName, - prompt: mockPrompt, - }, + name: tc.inSvcName, + envName: tc.inEnvName, + tag: tc.inTag, + appName: testAppName, }, sel: mockSelector, + prompt: mockPrompt, runner: mockRunner, } @@ -229,9 +227,9 @@ func TestPackageSvcOpts_Ask(t *testing.T) { err := opts.Ask() // THEN - require.Equal(t, tc.wantedSvcName, opts.Name) - require.Equal(t, tc.wantedEnvName, opts.EnvName) - require.Equal(t, tc.wantedTag, opts.Tag) + require.Equal(t, tc.wantedSvcName, opts.name) + require.Equal(t, tc.wantedEnvName, opts.envName) + require.Equal(t, tc.wantedTag, opts.tag) if tc.wantedErrorS != "" { require.EqualError(t, err, tc.wantedErrorS) @@ -255,12 +253,10 @@ func TestPackageSvcOpts_Execute(t *testing.T) { }{ "writes service template without addons": { inVars: packageSvcVars{ - GlobalOpts: &GlobalOpts{ - appName: "ecs-kudos", - }, - Name: "api", - EnvName: "test", - Tag: "1234", + appName: "ecs-kudos", + name: "api", + envName: "test", + tag: "1234", }, mockDependencies: func(ctrl *gomock.Controller, opts *packageSvcOpts) { mockStore := mocks.NewMockstore(ctrl) diff --git a/internal/pkg/cli/svc_show.go b/internal/pkg/cli/svc_show.go index 50c4afe2dea..5be09424908 100644 --- a/internal/pkg/cli/svc_show.go +++ b/internal/pkg/cli/svc_show.go @@ -13,6 +13,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/manifest" "github.com/aws/copilot-cli/internal/pkg/term/color" "github.com/aws/copilot-cli/internal/pkg/term/log" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/spf13/cobra" ) @@ -25,9 +26,9 @@ const ( ) type showSvcVars struct { - *GlobalOpts shouldOutputJSON bool shouldOutputResources bool + appName string svcName string } @@ -55,11 +56,11 @@ func newShowSvcOpts(vars showSvcVars) (*showSvcOpts, error) { showSvcVars: vars, store: ssmStore, w: log.OutputWriter, - sel: selector.NewConfigSelect(vars.prompt, ssmStore), + sel: selector.NewConfigSelect(prompt.New(), ssmStore), } opts.initDescriber = func() error { var d describer - svc, err := opts.store.GetService(opts.AppName(), opts.svcName) + svc, err := opts.store.GetService(opts.appName, opts.svcName) if err != nil { return err } @@ -67,7 +68,7 @@ func newShowSvcOpts(vars showSvcVars) (*showSvcOpts, error) { case manifest.LoadBalancedWebServiceType: d, err = describe.NewWebServiceDescriber(describe.NewWebServiceConfig{ NewServiceConfig: describe.NewServiceConfig{ - App: opts.AppName(), + App: opts.appName, Svc: opts.svcName, ConfigStore: ssmStore, }, @@ -77,7 +78,7 @@ func newShowSvcOpts(vars showSvcVars) (*showSvcOpts, error) { case manifest.BackendServiceType: d, err = describe.NewBackendServiceDescriber(describe.NewBackendServiceConfig{ NewServiceConfig: describe.NewServiceConfig{ - App: opts.AppName(), + App: opts.appName, Svc: opts.svcName, ConfigStore: ssmStore, }, @@ -89,7 +90,7 @@ func newShowSvcOpts(vars showSvcVars) (*showSvcOpts, error) { } if err != nil { - return fmt.Errorf("creating describer for service %s in application %s: %w", opts.svcName, opts.AppName(), err) + return fmt.Errorf("creating describer for service %s in application %s: %w", opts.svcName, opts.appName, err) } opts.describer = d return nil @@ -99,13 +100,13 @@ func newShowSvcOpts(vars showSvcVars) (*showSvcOpts, error) { // Validate returns an error if the values provided by the user are invalid. func (o *showSvcOpts) Validate() error { - if o.AppName() != "" { - if _, err := o.store.GetApplication(o.AppName()); err != nil { + if o.appName != "" { + if _, err := o.store.GetApplication(o.appName); err != nil { return err } } if o.svcName != "" { - if _, err := o.store.GetService(o.AppName(), o.svcName); err != nil { + if _, err := o.store.GetService(o.appName, o.svcName); err != nil { return err } } @@ -148,7 +149,7 @@ func (o *showSvcOpts) Execute() error { } func (o *showSvcOpts) askApp() error { - if o.AppName() != "" { + if o.appName != "" { return nil } appName, err := o.sel.Application(svcShowAppNamePrompt, svcShowAppNameHelpPrompt) @@ -164,21 +165,19 @@ func (o *showSvcOpts) askSvcName() error { if o.svcName != "" { return nil } - svcName, err := o.sel.Service(fmt.Sprintf(svcShowSvcNamePrompt, color.HighlightUserInput(o.AppName())), - svcShowSvcNameHelpPrompt, o.AppName()) + svcName, err := o.sel.Service(fmt.Sprintf(svcShowSvcNamePrompt, color.HighlightUserInput(o.appName)), + svcShowSvcNameHelpPrompt, o.appName) if err != nil { - return fmt.Errorf("select service for application %s: %w", o.AppName(), err) + return fmt.Errorf("select service for application %s: %w", o.appName, err) } o.svcName = svcName return nil } -// BuildSvcShowCmd builds the command for showing services in an application. -func BuildSvcShowCmd() *cobra.Command { - vars := showSvcVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildSvcShowCmd builds the command for showing services in an application. +func buildSvcShowCmd() *cobra.Command { + vars := showSvcVars{} cmd := &cobra.Command{ Use: "show", Short: "Shows info about a deployed service per environment.", @@ -201,7 +200,7 @@ func BuildSvcShowCmd() *cobra.Command { return opts.Execute() }), } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) cmd.Flags().StringVarP(&vars.svcName, nameFlag, nameFlagShort, "", svcFlagDescription) cmd.Flags().BoolVar(&vars.shouldOutputJSON, jsonFlag, false, jsonFlagDescription) cmd.Flags().BoolVar(&vars.shouldOutputResources, resourcesFlag, false, svcResourcesFlagDescription) diff --git a/internal/pkg/cli/svc_show_test.go b/internal/pkg/cli/svc_show_test.go index 50b1133f1da..9d866e98a74 100644 --- a/internal/pkg/cli/svc_show_test.go +++ b/internal/pkg/cli/svc_show_test.go @@ -17,7 +17,6 @@ import ( type showSvcMocks struct { storeSvc *mocks.Mockstore - prompt *mocks.Mockprompter describer *mocks.Mockdescriber ws *mocks.MockwsSvcReader sel *mocks.MockconfigSelector @@ -104,9 +103,7 @@ func TestSvcShow_Validate(t *testing.T) { showSvcs := &showSvcOpts{ showSvcVars: showSvcVars{ svcName: tc.inputSvc, - GlobalOpts: &GlobalOpts{ - appName: tc.inputApp, - }, + appName: tc.inputApp, }, store: mockStoreReader, } @@ -190,13 +187,11 @@ func TestSvcShow_Ask(t *testing.T) { defer ctrl.Finish() mockStoreReader := mocks.NewMockstore(ctrl) - mockPrompter := mocks.NewMockprompter(ctrl) mockWorkspace := mocks.NewMockwsSvcReader(ctrl) mockSelector := mocks.NewMockconfigSelector(ctrl) mocks := showSvcMocks{ storeSvc: mockStoreReader, - prompt: mockPrompter, ws: mockWorkspace, sel: mockSelector, } @@ -206,10 +201,7 @@ func TestSvcShow_Ask(t *testing.T) { showSvcs := &showSvcOpts{ showSvcVars: showSvcVars{ svcName: tc.inputSvc, - GlobalOpts: &GlobalOpts{ - prompt: mockPrompter, - appName: tc.inputApp, - }, + appName: tc.inputApp, }, store: mockStoreReader, sel: mockSelector, @@ -223,7 +215,7 @@ func TestSvcShow_Ask(t *testing.T) { require.EqualError(t, err, tc.wantedError.Error()) } else { require.NoError(t, err) - require.Equal(t, tc.wantedApp, showSvcs.AppName(), "expected app name to match") + require.Equal(t, tc.wantedApp, showSvcs.appName, "expected app name to match") require.Equal(t, tc.wantedSvc, showSvcs.svcName, "expected service name to match") } }) @@ -304,9 +296,7 @@ func TestSvcShow_Execute(t *testing.T) { showSvcVars: showSvcVars{ svcName: tc.inputSvc, shouldOutputJSON: tc.shouldOutputJSON, - GlobalOpts: &GlobalOpts{ - appName: appName, - }, + appName: appName, }, describer: mockSvcDescriber, initDescriber: func() error { return nil }, diff --git a/internal/pkg/cli/svc_status.go b/internal/pkg/cli/svc_status.go index c504c071ee3..9731214f84c 100644 --- a/internal/pkg/cli/svc_status.go +++ b/internal/pkg/cli/svc_status.go @@ -11,6 +11,7 @@ import ( "github.com/aws/copilot-cli/internal/pkg/deploy" "github.com/aws/copilot-cli/internal/pkg/describe" "github.com/aws/copilot-cli/internal/pkg/term/log" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/term/selector" "github.com/spf13/cobra" ) @@ -23,10 +24,10 @@ const ( ) type svcStatusVars struct { - *GlobalOpts shouldOutputJSON bool svcName string envName string + appName string } type svcStatusOpts struct { @@ -52,16 +53,16 @@ func newSvcStatusOpts(vars svcStatusVars) (*svcStatusOpts, error) { svcStatusVars: vars, store: configStore, w: log.OutputWriter, - sel: selector.NewDeploySelect(vars.prompt, configStore, deployStore), + sel: selector.NewDeploySelect(prompt.New(), configStore, deployStore), initStatusDescriber: func(o *svcStatusOpts) error { d, err := describe.NewServiceStatus(&describe.NewServiceStatusConfig{ - App: o.AppName(), + App: o.appName, Env: o.envName, Svc: o.svcName, ConfigStore: configStore, }) if err != nil { - return fmt.Errorf("creating status describer for service %s in application %s: %w", o.svcName, o.AppName(), err) + return fmt.Errorf("creating status describer for service %s in application %s: %w", o.svcName, o.appName, err) } o.statusDescriber = d return nil @@ -71,18 +72,18 @@ func newSvcStatusOpts(vars svcStatusVars) (*svcStatusOpts, error) { // Validate returns an error if the values provided by the user are invalid. func (o *svcStatusOpts) Validate() error { - if o.AppName() != "" { - if _, err := o.store.GetApplication(o.AppName()); err != nil { + if o.appName != "" { + if _, err := o.store.GetApplication(o.appName); err != nil { return err } } if o.svcName != "" { - if _, err := o.store.GetService(o.AppName(), o.svcName); err != nil { + if _, err := o.store.GetService(o.appName, o.svcName); err != nil { return err } } if o.envName != "" { - if _, err := o.store.GetEnvironment(o.AppName(), o.envName); err != nil { + if _, err := o.store.GetEnvironment(o.appName, o.envName); err != nil { return err } } @@ -121,7 +122,7 @@ func (o *svcStatusOpts) Execute() error { } func (o *svcStatusOpts) askApp() error { - if o.AppName() != "" { + if o.appName != "" { return nil } app, err := o.sel.Application(svcStatusAppNamePrompt, svcStatusAppNameHelpPrompt) @@ -133,20 +134,18 @@ func (o *svcStatusOpts) askApp() error { } func (o *svcStatusOpts) askSvcEnvName() error { - deployedService, err := o.sel.DeployedService(svcStatusNamePrompt, svcStatusNameHelpPrompt, o.AppName(), selector.WithEnv(o.envName), selector.WithSvc(o.svcName)) + deployedService, err := o.sel.DeployedService(svcStatusNamePrompt, svcStatusNameHelpPrompt, o.appName, selector.WithEnv(o.envName), selector.WithSvc(o.svcName)) if err != nil { - return fmt.Errorf("select deployed services for application %s: %w", o.AppName(), err) + return fmt.Errorf("select deployed services for application %s: %w", o.appName, err) } o.svcName = deployedService.Svc o.envName = deployedService.Env return nil } -// BuildSvcStatusCmd builds the command for showing the status of a deployed service. -func BuildSvcStatusCmd() *cobra.Command { - vars := svcStatusVars{ - GlobalOpts: NewGlobalOpts(), - } +// buildSvcStatusCmd builds the command for showing the status of a deployed service. +func buildSvcStatusCmd() *cobra.Command { + vars := svcStatusVars{} cmd := &cobra.Command{ Use: "status", Short: "Shows status of a deployed service.", @@ -169,9 +168,9 @@ func BuildSvcStatusCmd() *cobra.Command { return opts.Execute() }), } - // The flags bound by viper are available to all sub-commands through viper.GetString({flagName}) cmd.Flags().StringVarP(&vars.svcName, nameFlag, nameFlagShort, "", svcFlagDescription) cmd.Flags().StringVarP(&vars.envName, envFlag, envFlagShort, "", envFlagDescription) + cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription) cmd.Flags().BoolVar(&vars.shouldOutputJSON, jsonFlag, false, jsonFlagDescription) return cmd } diff --git a/internal/pkg/cli/svc_status_test.go b/internal/pkg/cli/svc_status_test.go index 9a49d398f81..57d433355b4 100644 --- a/internal/pkg/cli/svc_status_test.go +++ b/internal/pkg/cli/svc_status_test.go @@ -92,9 +92,7 @@ func TestSvcStatus_Validate(t *testing.T) { svcStatusVars: svcStatusVars{ svcName: tc.inputSvc, envName: tc.inputEnvironment, - GlobalOpts: &GlobalOpts{ - appName: tc.inputApp, - }, + appName: tc.inputApp, }, store: mockStoreReader, } @@ -166,9 +164,7 @@ func TestSvcStatus_Ask(t *testing.T) { svcStatusVars: svcStatusVars{ svcName: tc.inputSvc, envName: tc.inputEnvironment, - GlobalOpts: &GlobalOpts{ - appName: tc.inputApp, - }, + appName: tc.inputApp, }, sel: mockSelector, } @@ -228,9 +224,7 @@ func TestSvcStatus_Execute(t *testing.T) { svcName: "mockSvc", envName: "mockEnv", shouldOutputJSON: tc.shouldOutputJSON, - GlobalOpts: &GlobalOpts{ - appName: "mockApp", - }, + appName: "mockApp", }, statusDescriber: mockStatusDescriber, initStatusDescriber: func(*svcStatusOpts) error { return nil }, diff --git a/internal/pkg/cli/task_run.go b/internal/pkg/cli/task_run.go index c98fb1302d3..44acbaac16e 100644 --- a/internal/pkg/cli/task_run.go +++ b/internal/pkg/cli/task_run.go @@ -11,6 +11,7 @@ import ( awscloudformation "github.com/aws/copilot-cli/internal/pkg/aws/cloudformation" "github.com/aws/copilot-cli/internal/pkg/ecslogging" + "github.com/aws/copilot-cli/internal/pkg/term/prompt" "github.com/aws/copilot-cli/internal/pkg/aws/ec2" "github.com/aws/copilot-cli/internal/pkg/aws/ecr" @@ -63,7 +64,6 @@ Select %s to run the task in your default VPC instead of any existing environmen ) type runTaskVars struct { - *GlobalOpts count int cpu int memory int @@ -80,6 +80,7 @@ type runTaskVars struct { subnets []string securityGroups []string env string + appName string useDefaultSubnets bool envVars map[string]string @@ -127,7 +128,7 @@ func newTaskRunOpts(vars runTaskVars) (*runTaskOpts, error) { fs: &afero.Afero{Fs: afero.NewOsFs()}, store: store, - sel: selector.NewSelect(vars.prompt, store), + sel: selector.NewSelect(prompt.New(), store), spinner: termprogress.NewSpinner(), } @@ -164,7 +165,7 @@ func (o *runTaskOpts) configureRunner() taskRunner { Count: o.count, GroupName: o.groupName, - App: o.AppName(), + App: o.appName, Env: o.env, VPCGetter: vpcGetter, @@ -494,7 +495,7 @@ func (o *runTaskOpts) deploy() error { ExecutionRole: o.executionRole, Command: o.command, EnvVars: o.envVars, - App: o.AppName(), + App: o.appName, Env: o.env, AdditionalTags: o.resourceTags, } @@ -509,7 +510,7 @@ func (o *runTaskOpts) validateAppName() error { } func (o *runTaskOpts) validateEnvName() error { - if o.AppName() != "" { + if o.appName != "" { if _, err := o.targetEnv(); err != nil { return err } @@ -521,7 +522,7 @@ func (o *runTaskOpts) validateEnvName() error { } func (o *runTaskOpts) askAppName() error { - if o.AppName() != "" { + if o.appName != "" { return nil } @@ -545,11 +546,11 @@ func (o *runTaskOpts) askEnvName() error { } // If the application is empty then the user wants to run in the default VPC. Do not prompt for an environment name. - if o.AppName() == "" || o.subnets != nil { + if o.appName == "" || o.subnets != nil { return nil } - env, err := o.sel.Environment(taskRunEnvPrompt, taskRunEnvPromptHelp, o.AppName(), appEnvOptionNone) + env, err := o.sel.Environment(taskRunEnvPrompt, taskRunEnvPromptHelp, o.appName, appEnvOptionNone) if err != nil { return fmt.Errorf("ask for environment: %w", err) } @@ -563,7 +564,7 @@ func (o *runTaskOpts) askEnvName() error { } func (o *runTaskOpts) targetEnv() (*config.Environment, error) { - env, err := o.store.GetEnvironment(o.AppName(), o.env) + env, err := o.store.GetEnvironment(o.appName, o.env) if err != nil { return nil, fmt.Errorf("get environment %s config: %w", o.env, err) } @@ -572,9 +573,7 @@ func (o *runTaskOpts) targetEnv() (*config.Environment, error) { // BuildTaskRunCmd build the command for running a new task func BuildTaskRunCmd() *cobra.Command { - vars := runTaskVars{ - GlobalOpts: NewGlobalOpts(), - } + vars := runTaskVars{} cmd := &cobra.Command{ Use: "run", Short: "Run a one-off task on Amazon ECS.", diff --git a/internal/pkg/cli/task_run_test.go b/internal/pkg/cli/task_run_test.go index 1e409b09d45..41e2cdd1c26 100644 --- a/internal/pkg/cli/task_run_test.go +++ b/internal/pkg/cli/task_run_test.go @@ -285,9 +285,7 @@ func TestTaskRunOpts_Validate(t *testing.T) { opts := runTaskOpts{ runTaskVars: runTaskVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.appName, - }, + appName: tc.appName, count: tc.inCount, cpu: tc.inCPU, memory: tc.inMemory, @@ -525,10 +523,7 @@ func TestTaskRunOpts_Ask(t *testing.T) { opts := runTaskOpts{ runTaskVars: runTaskVars{ - GlobalOpts: &GlobalOpts{ - appName: tc.appName, - prompt: mockPrompter, - }, + appName: tc.appName, groupName: tc.inName, env: tc.inEnv, useDefaultSubnets: tc.inDefault, @@ -543,7 +538,7 @@ func TestTaskRunOpts_Ask(t *testing.T) { if tc.wantedError == nil { require.NoError(t, err) require.Equal(t, tc.wantedEnv, opts.env) - require.Equal(t, tc.wantedApp, opts.AppName()) + require.Equal(t, tc.wantedApp, opts.appName) if tc.wantedName != "" { require.Equal(t, tc.wantedName, opts.groupName) } @@ -752,8 +747,7 @@ func TestTaskRunOpts_Execute(t *testing.T) { opts := &runTaskOpts{ runTaskVars: runTaskVars{ - GlobalOpts: &GlobalOpts{}, - groupName: inGroupName, + groupName: inGroupName, image: tc.inImage, imageTag: tc.inTag,