Skip to content

Commit a944b79

Browse files
authored
chore(cli): build env deploy into the cli (#3684)
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the Apache 2.0 License.
1 parent 1d7b57d commit a944b79

File tree

9 files changed

+142
-19
lines changed

9 files changed

+142
-19
lines changed

internal/pkg/cli/deploy/env.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ import (
1414

1515
"github.com/aws/copilot-cli/internal/pkg/aws/cloudformation"
1616
"github.com/aws/copilot-cli/internal/pkg/aws/s3"
17+
"github.com/aws/copilot-cli/internal/pkg/aws/sessions"
1718
"github.com/aws/copilot-cli/internal/pkg/config"
19+
deploycfn "github.com/aws/copilot-cli/internal/pkg/deploy/cloudformation"
1820
"github.com/aws/copilot-cli/internal/pkg/deploy/cloudformation/stack"
1921
"github.com/aws/copilot-cli/internal/pkg/manifest"
2022

@@ -23,6 +25,10 @@ import (
2325
termprogress "github.com/aws/copilot-cli/internal/pkg/term/progress"
2426
)
2527

28+
type customResourcesUploader interface {
29+
UploadEnvironmentCustomResources(upload s3.CompressAndUploadFunc) (map[string]string, error)
30+
}
31+
2632
type appResourcesGetter interface {
2733
GetAppResourcesByRegion(app *config.Application, region string) (*stack.AppRegionalResources, error)
2834
}
@@ -38,7 +44,7 @@ type envDeployer struct {
3844
// Dependencies.
3945
appCFN appResourcesGetter
4046
// Dependencies to upload artifacts.
41-
uploader customResourcesUploader
47+
uploader customResourcesUploader // Deprecated: after legacy is removed.
4248
templateFS template.Reader
4349
s3 uploader
4450
// Dependencies to deploy an environment.
@@ -51,6 +57,40 @@ type envDeployer struct {
5157
uploadCustomResourceFlag bool
5258
}
5359

60+
// NewEnvDeployerInput contains information needd to construct an environment deployer.
61+
type NewEnvDeployerInput struct {
62+
App *config.Application
63+
Env *config.Environment
64+
SessionProvider *sessions.Provider
65+
}
66+
67+
// NewEnvDeployer constructs an environment deployer.
68+
func NewEnvDeployer(in *NewEnvDeployerInput) (*envDeployer, error) {
69+
defaultSession, err := in.SessionProvider.Default()
70+
if err != nil {
71+
return nil, fmt.Errorf("get default session: %w", err)
72+
}
73+
envRegionSession, err := in.SessionProvider.DefaultWithRegion(in.Env.Region)
74+
if err != nil {
75+
return nil, fmt.Errorf("get default session in env region %s: %w", in.Env.Region, err)
76+
}
77+
envManagerSession, err := in.SessionProvider.FromRole(in.Env.ManagerRoleARN, in.Env.Region)
78+
if err != nil {
79+
return nil, fmt.Errorf("get env session: %w", err)
80+
}
81+
return &envDeployer{
82+
app: in.App,
83+
env: in.Env,
84+
85+
appCFN: deploycfn.New(defaultSession),
86+
templateFS: template.New(),
87+
uploader: template.New(),
88+
s3: s3.New(envRegionSession),
89+
90+
envDeployer: deploycfn.New(envManagerSession),
91+
}, nil
92+
}
93+
5494
// UploadArtifacts uploads the deployment artifacts for the environment.
5595
func (d *envDeployer) UploadArtifacts() (map[string]string, error) {
5696
resources, err := d.getAppRegionalResources()

internal/pkg/cli/deploy/deploy.go renamed to internal/pkg/cli/deploy/svc.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,6 @@ type publicCIDRBlocksGetter interface {
121121
PublicCIDRBlocks() ([]string, error)
122122
}
123123

124-
type customResourcesUploader interface {
125-
UploadEnvironmentCustomResources(upload s3.CompressAndUploadFunc) (map[string]string, error)
126-
UploadRequestDrivenWebServiceCustomResources(upload s3.CompressAndUploadFunc) (map[string]string, error)
127-
UploadNetworkLoadBalancedWebServiceCustomResources(upload s3.CompressAndUploadFunc) (map[string]string, error)
128-
}
129-
130124
type snsTopicsLister interface {
131125
ListSNSTopics(appName string, envName string) ([]deploy.Topic, error)
132126
}

internal/pkg/cli/env.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Environments are deployment stages shared between services.`,
2424
cmd.AddCommand(buildEnvDeleteCmd())
2525
cmd.AddCommand(buildEnvShowCmd())
2626
cmd.AddCommand(buildEnvUpgradeCmd())
27+
cmd.AddCommand(buildEnvDeployCmd())
2728
cmd.SetUsageTemplate(template.Usage)
2829
cmd.Annotations = map[string]string{
2930
"group": group.Develop,

internal/pkg/cli/env_deploy.go

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@ package cli
66
import (
77
"fmt"
88

9+
"github.com/aws/aws-sdk-go/aws"
10+
"github.com/aws/aws-sdk-go/service/ssm"
11+
"github.com/aws/copilot-cli/internal/pkg/aws/identity"
12+
"github.com/aws/copilot-cli/internal/pkg/aws/sessions"
913
"github.com/aws/copilot-cli/internal/pkg/cli/deploy"
1014
"github.com/aws/copilot-cli/internal/pkg/config"
1115
"github.com/aws/copilot-cli/internal/pkg/manifest"
1216
"github.com/aws/copilot-cli/internal/pkg/term/color"
1317
"github.com/aws/copilot-cli/internal/pkg/term/log"
18+
"github.com/aws/copilot-cli/internal/pkg/term/prompt"
19+
"github.com/aws/copilot-cli/internal/pkg/term/selector"
20+
"github.com/aws/copilot-cli/internal/pkg/workspace"
21+
"github.com/spf13/cobra"
1422
)
1523

1624
type deployEnvVars struct {
@@ -29,10 +37,10 @@ type deployEnvOpts struct {
2937
sel wsEnvironmentSelector
3038

3139
// Dependencies to execute.
32-
ws wsEnvironmentReader
33-
deployer envDeployer
34-
identity identityService
35-
interpolator interpolator
40+
ws wsEnvironmentReader
41+
identity identityService
42+
interpolator interpolator
43+
newEnvDeployer func() (envDeployer, error)
3644

3745
// Cached variables.
3846
targetApp *config.Application
@@ -42,6 +50,47 @@ type deployEnvOpts struct {
4250
unmarshalManifest func(in []byte) (*manifest.Environment, error)
4351
}
4452

53+
func newEnvDeployOpts(vars deployEnvVars) (*deployEnvOpts, error) {
54+
sessProvider := sessions.ImmutableProvider(sessions.UserAgentExtras("env deploy"))
55+
defaultSess, err := sessProvider.Default()
56+
if err != nil {
57+
return nil, err
58+
}
59+
store := config.NewSSMStore(identity.New(defaultSess), ssm.New(defaultSess), aws.StringValue(defaultSess.Config.Region))
60+
ws, err := workspace.New()
61+
if err != nil {
62+
return nil, fmt.Errorf("new workspace: %w", err)
63+
}
64+
opts := &deployEnvOpts{
65+
deployEnvVars: vars,
66+
67+
store: store,
68+
sel: selector.NewLocalEnvironmentSelector(prompt.New(), store, ws),
69+
70+
ws: ws,
71+
identity: identity.New(defaultSess),
72+
interpolator: manifest.NewInterpolator(vars.appName, vars.name),
73+
74+
unmarshalManifest: manifest.UnmarshalEnvironment,
75+
}
76+
opts.newEnvDeployer = func() (envDeployer, error) {
77+
app, err := opts.cachedTargetApp()
78+
if err != nil {
79+
return nil, err
80+
}
81+
env, err := opts.cachedTargetEnv()
82+
if err != nil {
83+
return nil, err
84+
}
85+
return deploy.NewEnvDeployer(&deploy.NewEnvDeployerInput{
86+
App: app,
87+
Env: env,
88+
SessionProvider: sessProvider,
89+
})
90+
}
91+
return opts, nil
92+
}
93+
4594
// Validate is a no-op for this command.
4695
func (o *deployEnvOpts) Validate() error {
4796
return nil
@@ -69,11 +118,15 @@ func (o *deployEnvOpts) Execute() error {
69118
if err != nil {
70119
return fmt.Errorf("get identity: %w", err)
71120
}
72-
urls, err := o.deployer.UploadArtifacts()
121+
deployer, err := o.newEnvDeployer()
122+
if err != nil {
123+
return err
124+
}
125+
urls, err := deployer.UploadArtifacts()
73126
if err != nil {
74127
return fmt.Errorf("upload artifacts for environment %s: %w", o.name, err)
75128
}
76-
if err := o.deployer.DeployEnvironment(&deploy.DeployEnvironmentInput{
129+
if err := deployer.DeployEnvironment(&deploy.DeployEnvironmentInput{
77130
RootUserARN: caller.RootUserARN,
78131
CustomResourcesURLs: urls,
79132
Manifest: mft,
@@ -144,3 +197,27 @@ func (o *deployEnvOpts) cachedTargetApp() (*config.Application, error) {
144197
}
145198
return o.targetApp, nil
146199
}
200+
201+
// buildEnvDeployCmd builds the command for deploying an environment given a manifest.
202+
func buildEnvDeployCmd() *cobra.Command {
203+
vars := deployEnvVars{}
204+
cmd := &cobra.Command{
205+
Use: "deploy",
206+
Short: "Deploys an environment to an application.",
207+
Long: "Deploys an environment to an application.",
208+
Example: `
209+
Deploy an environment named "test".
210+
/code $copilot env deploy --name test`,
211+
Hidden: true,
212+
RunE: runCmdE(func(cmd *cobra.Command, args []string) error {
213+
opts, err := newEnvDeployOpts(vars)
214+
if err != nil {
215+
return err
216+
}
217+
return run(opts)
218+
}),
219+
}
220+
cmd.Flags().StringVarP(&vars.appName, appFlag, appFlagShort, tryReadingAppName(), appFlagDescription)
221+
cmd.Flags().StringVarP(&vars.name, nameFlag, nameFlagShort, "", envFlagDescription)
222+
return cmd
223+
}

internal/pkg/cli/env_deploy_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,11 @@ func TestDeployEnvOpts_Execute(t *testing.T) {
225225
name: "mockEnv",
226226
},
227227
ws: m.ws,
228-
deployer: m.deployer,
229228
identity: m.identity,
230229
interpolator: m.interpolator,
230+
newEnvDeployer: func() (envDeployer, error) {
231+
return m.deployer, nil
232+
},
231233
targetEnv: &config.Environment{
232234
Name: "mockEnv",
233235
},

internal/pkg/deploy/cloudformation/stack/env.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ func (e *EnvStackConfig) Template() (string, error) {
7878
return "", err
7979
}
8080

81+
// TODO(Lou1415926): remove all these after we are able to migrate to the new upload workflow.
8182
bucket, dnsCertValidator, err := s3.ParseURL(e.in.CustomResourcesURLs[template.DNSCertValidatorFileName])
8283
if err != nil {
8384
return "", err
@@ -234,12 +235,11 @@ func (e *EnvStackConfig) internalALBSubnets() []string {
234235
// Parameters returns the parameters to be passed into an environment CloudFormation template.
235236
func (e *EnvStackConfig) Parameters() ([]*cloudformation.Parameter, error) {
236237
httpsListener := "false"
237-
if len(e.in.ImportCertARNs) != 0 || e.in.App.Domain != "" {
238+
if len(e.importPublicCertARNs()) != 0 || e.in.App.Domain != "" {
238239
httpsListener = "true"
239240
}
240241
internalHTTPSListener := "false"
241-
if len(e.in.ImportCertARNs) != 0 && e.in.ImportVPCConfig != nil &&
242-
len(e.in.ImportVPCConfig.PublicSubnetIDs) == 0 {
242+
if len(e.importPrivateCertARNs()) != 0 {
243243
internalHTTPSListener = "true"
244244
}
245245
return []*cloudformation.Parameter{

internal/pkg/deploy/cloudformation/stack/env_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func TestEnv_Parameters(t *testing.T) {
215215
},
216216
},
217217
},
218-
"with private DNS": {
218+
"with private DNS only": {
219219
input: deploymentInputWithPrivateDNS,
220220
want: []*cloudformation.Parameter{
221221
{
@@ -264,7 +264,7 @@ func TestEnv_Parameters(t *testing.T) {
264264
},
265265
{
266266
ParameterKey: aws.String(envParamCreateHTTPSListenerKey),
267-
ParameterValue: aws.String("true"),
267+
ParameterValue: aws.String("false"),
268268
},
269269
{
270270
ParameterKey: aws.String(envParamCreateInternalHTTPSListenerKey),

internal/pkg/term/selector/selector.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,15 @@ func NewLocalWorkloadSelector(prompt prompter, store configLister, ws workspaceR
292292
}
293293
}
294294

295+
// NewLocalEnvironmentSelector returns a new selector that chooses applications from the config store, but an environment
296+
// from the local workspace.
297+
func NewLocalEnvironmentSelector(prompt prompter, store configLister, ws workspaceRetriever) *LocalEnvironmentSelector {
298+
return &LocalEnvironmentSelector{
299+
AppEnvSelector: NewAppEnvSelector(prompt, store),
300+
ws: ws,
301+
}
302+
}
303+
295304
// NewWorkspaceSelector returns a new selector that prompts for local information.
296305
func NewWorkspaceSelector(prompt prompter, ws workspaceRetriever) *WorkspaceSelector {
297306
return &WorkspaceSelector{

0 commit comments

Comments
 (0)