From 583e542aeadf8a44d05d4a7c4e4535517663e6ba Mon Sep 17 00:00:00 2001 From: psihachina Date: Wed, 12 Oct 2022 16:15:51 +0300 Subject: [PATCH] added multiple deploy apps\stacks --- internal/commands/up.go | 35 +++++----- internal/config/project.go | 93 ++++++++++++++++++------- internal/config/project_test.go | 117 ++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 45 deletions(-) create mode 100644 internal/config/project_test.go diff --git a/internal/commands/up.go b/internal/commands/up.go index be27886b..f1f5ba04 100644 --- a/internal/commands/up.go +++ b/internal/commands/up.go @@ -15,11 +15,11 @@ import ( type UpOptions struct { Config *config.Project - AppName string SkipBuildAndPush bool SkipGen bool AutoApprove bool UI terminal.UI + Apps []string } type Apps map[string]*interface{} @@ -60,7 +60,6 @@ func NewCmdUp(project *config.Project) *cobra.Command { Example: upExample, Short: "Bring full application up from the bottom to the top.", Long: upLongDesc, - Args: cobra.MaximumNArgs(1), ValidArgsFunction: config.GetApps, RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true @@ -139,7 +138,7 @@ func (o *UpOptions) Complete(cmd *cobra.Command, args []string) error { } } - o.AppName = cmd.Flags().Args()[0] + o.Apps = cmd.Flags().Args() } o.UI = terminal.ConsoleUI(context.Background(), o.Config.PlainText) @@ -148,13 +147,13 @@ func (o *UpOptions) Complete(cmd *cobra.Command, args []string) error { } func (o *UpOptions) Validate() error { - if o.AppName == "" { - err := o.validateAll() + if len(o.Apps) > 0 { + err := o.validate() if err != nil { return err } } else { - err := o.validate() + err := o.validateAll() if err != nil { return err } @@ -165,20 +164,22 @@ func (o *UpOptions) Validate() error { func (o *UpOptions) Run() error { ui := o.UI - if o.AppName == "" { - err := deployAll(ui, o) + if len(o.Apps) > 0 { + err := manager.InDependencyOrder(aws.BackgroundContext(), o.Config.GetStates(o.Apps...), func(c context.Context, name string) error { + return deployInfra(name, ui, o.Config, o.SkipGen) + }) if err != nil { return err } - } else { - if _, ok := o.Config.Terraform[o.AppName]; ok { - err := deployInfra(o.AppName, ui, o.Config, o.SkipGen) - if err != nil { - return err - } + err = manager.InDependencyOrder(aws.BackgroundContext(), o.Config.GetApps(o.Apps...), func(c context.Context, name string) error { + return deployApp(name, ui, o.Config) + }) + if err != nil { + return err } - err := deployApp(o.AppName, ui, o.Config) + } else { + err := deployAll(ui, o) if err != nil { return err } @@ -196,10 +197,6 @@ func (o *UpOptions) validate() error { return fmt.Errorf("can't validate options: namespace must be specified") } - if len(o.AppName) == 0 { - return fmt.Errorf("can't validate options: app name must be specified") - } - return nil } diff --git a/internal/config/project.go b/internal/config/project.go index 2d76430f..ed2dde16 100644 --- a/internal/config/project.go +++ b/internal/config/project.go @@ -134,48 +134,89 @@ func (p *Project) SettingAWSClient(sess *session.Session) { ) } -func (p *Project) GetApps() map[string]*interface{} { +func (p *Project) GetApps(names ...string) map[string]*interface{} { apps := map[string]*interface{}{} - for name, body := range p.Ecs { - var v interface{} - v = map[string]interface{}{ - "depends_on": body.DependsOn, + if len(names) > 0 { + for _, name := range names { + if s, ok := p.Ecs[name]; ok { + var v interface{} + v = map[string]interface{}{ + "depends_on": s.DependsOn, + } + apps[name] = &v + } + if s, ok := p.Serverless[name]; ok { + var v interface{} + v = map[string]interface{}{ + "depends_on": s.DependsOn, + } + apps[name] = &v + } + if s, ok := p.Alias[name]; ok { + var v interface{} + v = map[string]interface{}{ + "depends_on": s.DependsOn, + } + apps[name] = &v + } + } + } else { + for name, body := range p.Ecs { + var v interface{} + v = map[string]interface{}{ + "depends_on": body.DependsOn, + } + apps[name] = &v } - apps[name] = &v - } - for name, body := range p.Serverless { - var v interface{} - v = map[string]interface{}{ - "depends_on": body.DependsOn, + for name, body := range p.Serverless { + var v interface{} + v = map[string]interface{}{ + "depends_on": body.DependsOn, + } + apps[name] = &v } - apps[name] = &v - } - for name, body := range p.Alias { - var v interface{} - v = map[string]interface{}{ - "depends_on": body.DependsOn, + for name, body := range p.Alias { + var v interface{} + v = map[string]interface{}{ + "depends_on": body.DependsOn, + } + apps[name] = &v } - apps[name] = &v } return apps } -func (p *Project) GetStates() map[string]*interface{} { +func (p *Project) GetStates(stacks ...string) map[string]*interface{} { states := map[string]*interface{}{} - for name, body := range p.Terraform { - if name == "infra" { - continue + if len(stacks) > 0 { + for _, stack := range stacks { + if stack == "infra" { + continue + } + if s, ok := p.Terraform[stack]; ok { + var v interface{} + v = map[string]interface{}{ + "depends_on": s.DependsOn, + } + states[stack] = &v + } } - var v interface{} - v = map[string]interface{}{ - "depends_on": body.DependsOn, + } else { + for name, body := range p.Terraform { + if name == "infra" { + continue + } + var v interface{} + v = map[string]interface{}{ + "depends_on": body.DependsOn, + } + states[name] = &v } - states[name] = &v } return states diff --git a/internal/config/project_test.go b/internal/config/project_test.go new file mode 100644 index 00000000..3a05123e --- /dev/null +++ b/internal/config/project_test.go @@ -0,0 +1,117 @@ +package config + +import ( + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" + "testing" +) + +func TestProject_GetStates(t *testing.T) { + type fields struct { + Terraform map[string]*Terraform + } + type args struct { + stacks []string + } + tests := []struct { + name string + fields fields + args args + want map[string]*interface{} + }{ + {name: "success", fields: fields{ + Terraform: map[string]*Terraform{ + "test": {}, + "test1": {}, + "test2": {}, + "test3": {}, + }, + }, args: args{}, want: map[string]*interface{}{"test": nil, "test1": nil, "test2": nil, "test3": nil}}, + {name: "success", fields: fields{ + Terraform: map[string]*Terraform{ + "test": {}, + "test1": {}, + "test2": {}, + "test3": {}, + }, + }, args: args{stacks: []string{"test", "test2"}}, want: map[string]*interface{}{"test": nil, "test2": nil}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Project{ + Terraform: tt.fields.Terraform, + } + got := p.GetStates(tt.args.stacks...) + gotKeys := maps.Keys(got) + wantKeys := maps.Keys(tt.want) + slices.Sort(gotKeys) + slices.Sort(wantKeys) + if !slices.Equal(gotKeys, wantKeys) { + t.Errorf("GetStates() = %v, want %v", maps.Keys(got), maps.Keys(tt.want)) + } + }) + } +} + +func TestProject_GetApps(t *testing.T) { + type fields struct { + Serverless map[string]*Serverless + Ecs map[string]*Ecs + Alias map[string]*Alias + } + type args struct { + names []string + } + tests := []struct { + name string + fields fields + args args + want map[string]*interface{} + }{ + {name: "success", fields: fields{ + Serverless: map[string]*Serverless{ + "testSls1": {}, + "testSls2": {}, + }, + Ecs: map[string]*Ecs{ + "testEcs1": {}, + "testEcs2": {}, + }, + Alias: map[string]*Alias{ + "testAlias1": {}, + "testAlias2": {}, + }, + }, args: args{}, want: map[string]*interface{}{"testAlias1": nil, "testAlias2": nil, "testEcs1": nil, "testEcs2": nil, "testSls2": nil, "testSls1": nil}}, + {name: "success", fields: fields{ + Serverless: map[string]*Serverless{ + "testSls1": {}, + "testSls2": {}, + }, + Ecs: map[string]*Ecs{ + "testEcs1": {}, + "testEcs2": {}, + }, + Alias: map[string]*Alias{ + "testAlias1": {}, + "testAlias2": {}, + }, + }, args: args{names: []string{"testSls1", "testAlias1"}}, want: map[string]*interface{}{"testSls1": nil, "testAlias1": nil}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p := &Project{ + Ecs: tt.fields.Ecs, + Serverless: tt.fields.Serverless, + Alias: tt.fields.Alias, + } + got := p.GetApps(tt.args.names...) + gotKeys := maps.Keys(got) + wantKeys := maps.Keys(tt.want) + slices.Sort(gotKeys) + slices.Sort(wantKeys) + if !slices.Equal(gotKeys, wantKeys) { + t.Errorf("GetStates() = %v, want %v", maps.Keys(got), maps.Keys(tt.want)) + } + }) + } +}