Skip to content

Commit

Permalink
refactor(describe): apply new store apis on env show (#1142)
Browse files Browse the repository at this point in the history
<!-- Provide summary of changes -->
Last piece of #1069.
<!-- Issue number, if available. E.g. "Fixes #31", "Addresses #42, 77" -->

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
  • Loading branch information
iamhopaul123 authored Jul 15, 2020
1 parent aa6718a commit e3fa88d
Show file tree
Hide file tree
Showing 18 changed files with 227 additions and 359 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ coverage.out
node_modules/
.DS_Store
site/node_modules/
site/themes/docsy
site/themes/docsy
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,12 @@ gen-mocks: tools
${GOBIN}/mockgen -source=./internal/pkg/cli/progress.go -package=mocks -destination=./internal/pkg/cli/mocks/mock_progress.go
${GOBIN}/mockgen -source=./internal/pkg/cli/prompter.go -package=mocks -destination=./internal/pkg/cli/mocks/mock_prompter.go
${GOBIN}/mockgen -source=./internal/pkg/cli/interfaces.go -package=mocks -destination=./internal/pkg/cli/mocks/mock_interfaces.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/cli/selector/mocks/mock_selector.go -source=./internal/pkg/cli/selector/selector.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/term/selector/mocks/mock_selector.go -source=./internal/pkg/term/selector/selector.go
${GOBIN}/mockgen -source=./internal/pkg/cli/completion.go -package=mocks -destination=./internal/pkg/cli/mocks/mock_completion.go
${GOBIN}/mockgen -source=./internal/pkg/cli/identity.go -package=mocks -destination=./internal/pkg/cli/mocks/mock_identity.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_lb_web_service.go -source=./internal/pkg/describe/lb_web_service.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_service.go -source=./internal/pkg/describe/service.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_describe.go -source=./internal/pkg/describe/describe.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_env.go -source=./internal/pkg/describe/env.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_stack.go -source=./internal/pkg/describe/stack.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_status.go -source=./internal/pkg/describe/status.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/describe/mocks/mock_pipeline.go -source=./internal/pkg/describe/pipeline.go
Expand Down
24 changes: 15 additions & 9 deletions internal/pkg/cli/env_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io"

"github.com/aws/copilot-cli/internal/pkg/config"
"github.com/aws/copilot-cli/internal/pkg/deploy"
"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"
Expand Down Expand Up @@ -40,24 +41,29 @@ type showEnvOpts struct {
}

func newShowEnvOpts(vars showEnvVars) (*showEnvOpts, error) {
store, err := config.NewStore()
configStore, err := config.NewStore()
if err != nil {
return nil, fmt.Errorf("connect to copilot config store: %w", err)
}
deployStore, err := deploy.NewStore(configStore)
if err != nil {
return nil, fmt.Errorf("connect to copilot deploy store: %w", err)
}

opts := &showEnvOpts{
showEnvVars: vars,
store: store,
store: configStore,
w: log.OutputWriter,
sel: selector.NewConfigSelect(vars.prompt, store),
sel: selector.NewConfigSelect(vars.prompt, configStore),
}
opts.initEnvDescriber = func() error {
var d envDescriber
if !vars.shouldOutputResources {
d, err = describe.NewEnvDescriber(opts.AppName(), opts.envName)
} else {
d, err = describe.NewEnvDescriberWithResources(opts.AppName(), opts.envName)
}
d, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{
App: opts.AppName(),
Env: opts.envName,
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)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/pkg/describe/backend_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type BackendServiceDescriber struct {
svc string
enableResources bool

store deployStoreSvc
store DeployedEnvServicesLister
svcDescriber map[string]svcDescriber
initServiceDescriber func(string) error
}
Expand All @@ -30,7 +30,7 @@ type BackendServiceDescriber struct {
type NewBackendServiceConfig struct {
NewServiceConfig
EnableResources bool
DeployStore deployStoreSvc
DeployStore DeployedEnvServicesLister
}

// NewBackendServiceDescriber instantiates a backend service describer.
Expand Down
4 changes: 2 additions & 2 deletions internal/pkg/describe/backend_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

type backendSvcDescriberMocks struct {
storeSvc *mocks.MockdeployStoreSvc
storeSvc *mocks.MockDeployedEnvServicesLister
svcDescriber *mocks.MocksvcDescriber
}

Expand Down Expand Up @@ -174,7 +174,7 @@ func TestBackendServiceDescriber_Describe(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockStore := mocks.NewMockdeployStoreSvc(ctrl)
mockStore := mocks.NewMockDeployedEnvServicesLister(ctrl)
mockSvcDescriber := mocks.NewMocksvcDescriber(ctrl)
mocks := backendSvcDescriberMocks{
storeSvc: mockStore,
Expand Down
16 changes: 15 additions & 1 deletion internal/pkg/describe/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ package describe

import (
"fmt"
"io"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/dustin/go-humanize"
"io"
)

const (
Expand Down Expand Up @@ -48,6 +49,19 @@ func flattenResources(stackResources []*cloudformation.StackResource) []*CfnReso
return resources
}

func flattenEnvVars(envName string, m map[string]string) []*EnvVars {
var envVarList []*EnvVars
for k, v := range m {
envVarList = append(envVarList, &EnvVars{
Environment: envName,
Name: k,
Value: v,
})
}
return envVarList
}

// HumanString returns the stringified CfnResource struct with human readable format.
func (c CfnResource) HumanString() string {
return fmt.Sprintf(" %s\t%s\n", c.Type, c.PhysicalID)
}
Expand Down
129 changes: 47 additions & 82 deletions internal/pkg/describe/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,19 @@ import (
"encoding/json"
"fmt"
"sort"
"strings"
"text/tabwriter"

"github.com/aws/aws-sdk-go/aws/arn"
"github.com/aws/copilot-cli/internal/pkg/config"
"github.com/aws/copilot-cli/internal/pkg/deploy"
"github.com/aws/copilot-cli/internal/pkg/deploy/cloudformation/stack"
"github.com/aws/copilot-cli/internal/pkg/term/color"

rg "github.com/aws/copilot-cli/internal/pkg/aws/resourcegroups"
"github.com/aws/copilot-cli/internal/pkg/aws/session"
)

const (
cloudformationResourceType = "cloudformation:stack"
)

type resourceGroupsClient interface {
GetResourcesByTags(resourceType string, tags map[string]string) ([]*rg.Resource, error)
}

// EnvDescription contains the information about an environment.
type EnvDescription struct {
Environment *config.Environment `json:"environment"`
Expand All @@ -39,83 +31,63 @@ type EnvDescription struct {

// EnvDescriber retrieves information about an environment.
type EnvDescriber struct {
app *config.Application
app string
env *config.Environment
svcs []*config.Service
enableResources bool

store configStoreSvc
rgClient resourceGroupsClient
configStore ConfigStoreSvc
deployStore DeployedEnvServicesLister
stackDescriber stackAndResourcesDescriber
}

// NewEnvDescriber instantiates an environment describer.
func NewEnvDescriber(appName, envName string) (*EnvDescriber, error) {
store, err := config.NewStore()
// NewEnvDescriberConfig contains fields that initiates EnvDescriber struct.
type NewEnvDescriberConfig struct {
App string
Env string
EnableResources bool
ConfigStore ConfigStoreSvc
DeployStore DeployedEnvServicesLister
}

if err != nil {
return nil, fmt.Errorf("connect to store: %w", err)
}
env, err := store.GetEnvironment(appName, envName)
// NewEnvDescriber instantiates an environment describer.
func NewEnvDescriber(opt NewEnvDescriberConfig) (*EnvDescriber, error) {
env, err := opt.ConfigStore.GetEnvironment(opt.App, opt.Env)
if err != nil {
return nil, fmt.Errorf("get environment: %w", err)
}
app, err := store.GetApplication(appName)
if err != nil {
return nil, fmt.Errorf("get application %s: %w", appName, err)
}
svcs, err := store.ListServices(appName)
if err != nil {
return nil, err
}
sess, err := session.NewProvider().FromRole(env.ManagerRoleARN, env.Region)
if err != nil {
return nil, fmt.Errorf("assuming role for environment %s: %w", env.ManagerRoleARN, err)
return nil, fmt.Errorf("assume role for environment %s: %w", env.ManagerRoleARN, err)
}
d := newStackDescriber(sess)
return &EnvDescriber{
app: app,
app: opt.App,
env: env,
svcs: svcs,
enableResources: false,
enableResources: opt.EnableResources,

store: store,
rgClient: rg.New(sess),
configStore: opt.ConfigStore,
deployStore: opt.DeployStore,
stackDescriber: stackAndResourcesDescriber(d),
}, nil
}

// NewEnvDescriberWithResources instantiates an environment describer with stack resources.
func NewEnvDescriberWithResources(appName, envName string) (*EnvDescriber, error) {
d, err := NewEnvDescriber(appName, envName)
if err != nil {
return nil, err
}
d.enableResources = true
return d, nil
}

// Describe returns info about an application's environment.
func (e *EnvDescriber) Describe() (*EnvDescription, error) {
svcs, err := e.filterSvcsForEnv()
svcs, err := e.filterDeployedSvcs()
if err != nil {
return nil, err
}

tags := make(map[string]string)
envStack, err := e.stackDescriber.Stack(stack.NameForEnv(e.app.Name, e.env.Name))
tags, err := e.stackTags()
if err != nil {
return nil, err
}
for _, tag := range envStack.Tags {
tags[*tag.Key] = *tag.Value
return nil, fmt.Errorf("retrieve environment tags: %w", err)
}

var stackResources []*CfnResource
if e.enableResources {
stackResources, err = e.envOutputs()
if err != nil {
return nil, err
return nil, fmt.Errorf("retrieve environment resources: %w", err)
}
}

Expand All @@ -127,49 +99,42 @@ func (e *EnvDescriber) Describe() (*EnvDescription, error) {
}, nil
}

func (e *EnvDescriber) filterSvcsForEnv() ([]*config.Service, error) {
tags := map[string]string{
deploy.EnvTagKey: e.env.Name,
}
resources, err := e.rgClient.GetResourcesByTags(cloudformationResourceType, tags)
func (e *EnvDescriber) stackTags() (map[string]string, error) {
tags := make(map[string]string)
envStack, err := e.stackDescriber.Stack(stack.NameForEnv(e.app, e.env.Name))
if err != nil {
return nil, fmt.Errorf("get %s resources for env %s: %w", cloudformationResourceType, e.env.Name, err)
}

stacksOfEnvironment := make(map[string]bool)
for _, resource := range resources {
stack, err := e.getStackName(resource.ARN)
if err != nil {
return nil, err
}
stacksOfEnvironment[stack] = true
return nil, err
}
var svcs []*config.Service
for _, svc := range e.svcs {
stackName := stack.NameForService(e.app.Name, e.env.Name, svc.Name)
if stacksOfEnvironment[stackName] {
svcs = append(svcs, svc)
}
for _, tag := range envStack.Tags {
tags[*tag.Key] = *tag.Value
}
return svcs, nil
return tags, nil
}

func (e *EnvDescriber) getStackName(resourceArn string) (string, error) {
parsedArn, err := arn.Parse(resourceArn)
func (e *EnvDescriber) filterDeployedSvcs() ([]*config.Service, error) {
allSvcs, err := e.configStore.ListServices(e.app)
if err != nil {
return "", fmt.Errorf("parse ARN %s: %w", resourceArn, err)
return nil, fmt.Errorf("list services for app %s: %w", e.app, err)
}
stack := strings.Split(parsedArn.Resource, "/")
if len(stack) < 2 {
return "", fmt.Errorf("invalid ARN resource format %s. Ex: arn:partition:service:region:account-id:resource-type/resource-id", parsedArn.Resource)
svcs := make(map[string]*config.Service)
for _, svc := range allSvcs {
svcs[svc.Name] = svc
}
return stack[1], nil
deployedSvcNames, err := e.deployStore.ListDeployedServices(e.app, e.env.Name)
if err != nil {
return nil, fmt.Errorf("list deployed services in env %s: %w", e.env.Name, err)
}
var deployedSvcs []*config.Service
for _, deployedSvcName := range deployedSvcNames {
deployedSvcs = append(deployedSvcs, svcs[deployedSvcName])
}
return deployedSvcs, nil
}

func (e *EnvDescriber) envOutputs() ([]*CfnResource, error) {
envStack, err := e.stackDescriber.StackResources(stack.NameForEnv(e.app.Name, e.env.Name))
envStack, err := e.stackDescriber.StackResources(stack.NameForEnv(e.app, e.env.Name))
if err != nil {
return nil, fmt.Errorf("retrieve environment resources: %w", err)
return nil, err
}
outputs := flattenResources(envStack)
return outputs, nil
Expand Down
Loading

0 comments on commit e3fa88d

Please sign in to comment.