Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(manifest): add from_tag for workload #3727

Merged
merged 4 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ gen-mocks: tools
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/template/mocks/mock_template.go -source=./internal/pkg/template/template.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/task/mocks/mock_task.go -source=./internal/pkg/task/task.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/repository/mocks/mock_repository.go -source=./internal/pkg/repository/repository.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/logging/mocks/mock_service.go -source=./internal/pkg/logging/service.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/logging/mocks/mock_workload.go -source=./internal/pkg/logging/workload.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/logging/mocks/mock_task.go -source=./internal/pkg/logging/task.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/cli/list/mocks/mock_list.go -source=./internal/pkg/cli/list/list.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/cli/deploy/mocks/mock_svc.go -source=./internal/pkg/cli/deploy/svc.go
Expand All @@ -211,3 +211,4 @@ gen-mocks: tools
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/apprunner/mocks/mock_apprunner.go -source=./internal/pkg/apprunner/apprunner.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/ecs/mocks/mock_run_task_request.go -source=./internal/pkg/ecs/run_task_request.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/runner/jobrunner/mocks/mock.go -source=./internal/pkg/runner/jobrunner/jobrunner.go
${GOBIN}/mockgen -package=mocks -destination=./internal/pkg/manifest/mocks/mock.go -source=./internal/pkg/manifest/loader.go
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ require (
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/tools v0.1.1 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,7 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
21 changes: 17 additions & 4 deletions internal/pkg/aws/ec2/ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ const (
internetGatewayIDPrefix = "igw-"
cloudFrontPrefixListName = "com.amazonaws.global.cloudfront.origin-facing"

// TagFilterName is the filter name format for tag filters
TagFilterName = "tag:%s"
// FmtTagFilter is the filter name format for tag filters
FmtTagFilter = "tag:%s"
tagKeyFilter = "tag-key"
)

var (
Expand Down Expand Up @@ -50,6 +51,17 @@ type Filter struct {
Values []string
}

// FilterForTags takes a key and optional values to construct an EC2 filter.
func FilterForTags(key string, values ...string) Filter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this is a really clean function signature 👍 😍

if len(values) == 0 {
return Filter{Name: tagKeyFilter, Values: []string{key}}
}
return Filter{
Name: fmt.Sprintf(FmtTagFilter, key),
Values: values,
}
}

// EC2 wraps an AWS EC2 client.
type EC2 struct {
client api
Expand Down Expand Up @@ -322,7 +334,6 @@ func (c *EC2) subnets(filters ...Filter) ([]*ec2.Subnet, error) {
return nil, fmt.Errorf("describe subnets: %w", err)
}
subnets = append(subnets, response.Subnets...)

for response.NextToken != nil {
response, err = c.client.DescribeSubnets(&ec2.DescribeSubnetsInput{
Filters: inputFilters,
Expand All @@ -333,7 +344,9 @@ func (c *EC2) subnets(filters ...Filter) ([]*ec2.Subnet, error) {
}
subnets = append(subnets, response.Subnets...)
}

if len(subnets) == 0 {
return nil, fmt.Errorf("cannot find any subnets")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return nil, fmt.Errorf("cannot find any subnets")
return nil, errors.New("cannot find any subnets")

}
return subnets, nil
}

Expand Down
43 changes: 40 additions & 3 deletions internal/pkg/aws/ec2/ec2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,18 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/copilot-cli/internal/pkg/aws/ec2/mocks"
"github.com/aws/copilot-cli/internal/pkg/deploy"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
)

var (
inAppEnvFilters = []Filter{
{
Name: fmt.Sprintf(TagFilterName, deploy.AppTagKey),
Name: fmt.Sprintf(FmtTagFilter, "copilot-application"),
Values: []string{"my-app"},
},
{
Name: fmt.Sprintf(TagFilterName, deploy.EnvTagKey),
Name: fmt.Sprintf(FmtTagFilter, "copilot-environment"),
Values: []string{"my-env"},
},
}
Expand All @@ -42,6 +41,33 @@ var (
}
)

func TestEC2_FilterForTags(t *testing.T) {
testCases := map[string]struct {
inValues []string
wanted Filter
}{
"with no values": {
wanted: Filter{
Name: "tag-key",
Values: []string{"mockKey"},
},
},
"with values": {
inValues: []string{"foo", "bar"},
wanted: Filter{
Name: "tag:mockKey",
Values: []string{"foo", "bar"},
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
filter := FilterForTags("mockKey", tc.inValues...)
require.Equal(t, tc.wanted, filter)
})
}
}

func TestEC2_extractResource(t *testing.T) {
testCases := map[string]struct {
displayString string
Expand Down Expand Up @@ -802,6 +828,17 @@ func TestEC2_SubnetIDs(t *testing.T) {
},
wantedError: fmt.Errorf("describe subnets: error describing subnets"),
},
"cannot get any subnets": {
inFilter: inAppEnvFilters,
mockEC2Client: func(m *mocks.Mockapi) {
m.EXPECT().DescribeSubnets(&ec2.DescribeSubnetsInput{
Filters: toEC2Filter(inAppEnvFilters),
}).Return(&ec2.DescribeSubnetsOutput{
Subnets: []*ec2.Subnet{},
}, nil)
},
wantedError: fmt.Errorf("cannot find any subnets"),
},
"successfully get subnets": {
inFilter: inAppEnvFilters,
mockEC2Client: func(m *mocks.Mockapi) {
Expand Down
8 changes: 8 additions & 0 deletions internal/pkg/cli/job_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/aws/copilot-cli/internal/pkg/describe"

Expand Down Expand Up @@ -44,6 +45,7 @@ type deployJobOpts struct {
// cached variables
targetApp *config.Application
targetEnv *config.Environment
envSess *session.Session
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😰 were we not using the correct session for job deploy this entire time>>>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is actually used here

appliedManifest interface{}
rootUserARN string
}
Expand Down Expand Up @@ -149,6 +151,7 @@ func (o *deployJobOpts) Execute() error {
interpolator: o.newInterpolator(o.appName, o.envName),
ws: o.ws,
unmarshal: o.unmarshal,
sess: o.envSess,
})
if err != nil {
return err
Expand Down Expand Up @@ -220,6 +223,11 @@ func (o *deployJobOpts) configureClients() error {
if err != nil {
return fmt.Errorf("create default session: %w", err)
}
envSess, err := o.sessProvider.FromRole(env.ManagerRoleARN, env.Region)
if err != nil {
return err
}
o.envSess = envSess

// client to retrieve caller identity.
caller, err := identity.New(defaultSess).Get()
Expand Down
16 changes: 14 additions & 2 deletions internal/pkg/cli/svc_deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/aws/copilot-cli/internal/pkg/aws/identity"
"github.com/aws/copilot-cli/internal/pkg/aws/tags"
Expand Down Expand Up @@ -63,6 +64,7 @@ type deploySvcOpts struct {
// cached variables
targetApp *config.Application
targetEnv *config.Environment
envSess *session.Session
svcType string
appliedManifest interface{}
rootUserARN string
Expand Down Expand Up @@ -185,6 +187,7 @@ func (o *deploySvcOpts) Execute() error {
interpolator: o.newInterpolator(o.appName, o.envName),
ws: o.ws,
unmarshal: o.unmarshal,
sess: o.envSess,
})
if err != nil {
return err
Expand Down Expand Up @@ -327,6 +330,11 @@ func (o *deploySvcOpts) configureClients() error {
if err != nil {
return fmt.Errorf("create default session: %w", err)
}
envSess, err := o.sessProvider.FromRole(env.ManagerRoleARN, env.Region)
if err != nil {
return err
}
o.envSess = envSess

// client to retrieve caller identity.
caller, err := identity.New(defaultSess).Get()
Expand All @@ -353,6 +361,7 @@ type workloadManifestInput struct {
envName string
ws wsWlDirReader
interpolator interpolator
sess *session.Session
unmarshal func([]byte) (manifest.WorkloadManifest, error)
}

Expand All @@ -371,10 +380,13 @@ func workloadManifest(in *workloadManifestInput) (manifest.WorkloadManifest, err
}
envMft, err := mft.ApplyEnv(in.envName)
if err != nil {
return nil, fmt.Errorf("apply environment %s override: %s", in.envName, err)
return nil, fmt.Errorf("apply environment %s override: %w", in.envName, err)
}
if err := envMft.Validate(); err != nil {
return nil, fmt.Errorf("validate manifest against environment %s: %s", in.envName, err)
return nil, fmt.Errorf("validate manifest against environment %q: %w", in.envName, err)
}
if err := envMft.Load(in.sess); err != nil {
return nil, fmt.Errorf("load dynamic content: %w", err)
}
return envMft, nil
}
Expand Down
9 changes: 9 additions & 0 deletions internal/pkg/cli/svc_deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/copilot-cli/internal/pkg/manifest"
"github.com/aws/copilot-cli/internal/pkg/template"
"github.com/golang/mock/gomock"
Expand Down Expand Up @@ -348,6 +349,14 @@ func (m *mockWorkloadMft) Validate() error {
return nil
}

func (m *mockWorkloadMft) Load(sess *session.Session) error {
return nil
}

func (m *mockWorkloadMft) Manifest() interface{} {
return nil
}

func (m *mockWorkloadMft) RequiredEnvironmentFeatures() []string {
return m.mockRequiredEnvironmentFeatures()
}
16 changes: 14 additions & 2 deletions internal/pkg/cli/svc_package.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"path/filepath"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ssm"
clideploy "github.com/aws/copilot-cli/internal/pkg/cli/deploy"
"github.com/aws/copilot-cli/internal/pkg/deploy"
Expand Down Expand Up @@ -78,6 +79,7 @@ type packageSvcOpts struct {
// cached variables
targetApp *config.Application
targetEnv *config.Environment
envSess *session.Session
appliedManifest manifest.WorkloadManifest
rootUserARN string
}
Expand Down Expand Up @@ -136,10 +138,10 @@ func newWkldTplGenerator(o *packageSvcOpts) (workloadTemplateGenerator, error) {
App: targetApp,
Env: targetEnv,
ImageTag: o.tag,
Mft: o.appliedManifest,
Mft: o.appliedManifest.Manifest(),
RawMft: raw,
}
switch t := o.appliedManifest.(type) {
switch t := o.appliedManifest.Manifest().(type) {
case *manifest.LoadBalancedWebService:
deployer, err = clideploy.NewLBWSDeployer(&in)
case *manifest.BackendService:
Expand Down Expand Up @@ -275,6 +277,15 @@ func (o *packageSvcOpts) configureClients() error {
if err != nil {
return fmt.Errorf("create default session: %w", err)
}
targetEnv, err := o.getTargetEnv()
if err != nil {
return err
}
envSess, err := o.sessProvider.FromRole(targetEnv.ManagerRoleARN, targetEnv.Region)
if err != nil {
return err
}
o.envSess = envSess
// client to retrieve caller identity.
caller, err := identity.New(defaultSess).Get()
if err != nil {
Expand Down Expand Up @@ -308,6 +319,7 @@ func (o *packageSvcOpts) getSvcTemplates(env *config.Environment) (*wkldCfnTempl
interpolator: o.newInterpolator(o.appName, o.envName),
ws: o.ws,
unmarshal: o.unmarshal,
sess: o.envSess,
})
if err != nil {
return nil, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -542,9 +542,10 @@ func TestBackendService_TemplateAndParamsGeneration(t *testing.T) {
paramsBytes, err := ioutil.ReadFile(tc.ParamsPath)
require.NoError(t, err)

mft, err := manifest.UnmarshalWorkload([]byte(manifestBytes))
dynamicMft, err := manifest.UnmarshalWorkload([]byte(manifestBytes))
require.NoError(t, err)
require.NoError(t, mft.Validate())
require.NoError(t, dynamicMft.Validate())
mft := dynamicMft.Manifest()

envConfig := &manifest.Environment{
Workload: manifest.Workload{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//go:build localintegration
// +build localintegration
//go:build integration || localintegration

// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
Expand Down Expand Up @@ -48,11 +47,11 @@ func TestGrpcLoadBalancedWebService_Template(t *testing.T) {
require.NoError(t, err)
envMft, err := mft.ApplyEnv(tc.envName)
require.NoError(t, err)

err = envMft.Validate()
require.NoError(t, err)
content := envMft.Manifest()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you thing we also want to add the .Load step to these local integration test? I feel like otherwise they wouldn't be testing the right thing because in real executions their output template would be one that has gone through .Load.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we add it it is not local anymore lol

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not local only if it has subnet.from_tags and not subnet.ids right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Load is supposed to load dynamic contents that requires API calls. If the test itself is supposed to be run locally then it doesn't make sense to me to add .Load step 🤔

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the only reason why these tests have all these Validate, ApplyEnv calls is because we didn't have a better way to mimic how cli package would have manipulate the manifest, than to just hardcoding-ly repeating the codes in cli. Without calling Load, we are not effectively "mimicking" it.

Another way that I think about it is that I think these integration tests were meant to test that "given a manifest input, do I eventually get an expected template". Without calling Load like cli does, we can't be confident with our test result.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's true. IMO we need to

  1. remove some of our existing integ tests because we have e2e tests now
  2. make current local integ tests to be integ tests because we can't generate expected template without making internet.


v, ok := envMft.(*manifest.LoadBalancedWebService)
v, ok := content.(*manifest.LoadBalancedWebService)
require.True(t, ok)

envConfig := &manifest.Environment{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ func TestNetworkLoadBalancedWebService_Template(t *testing.T) {
require.NoError(t, err)
envMft, err := mft.ApplyEnv(tc.envName)
require.NoError(t, err)

err = envMft.Validate()
require.NoError(t, err)
content := envMft.Manifest()

v, ok := envMft.(*manifest.LoadBalancedWebService)
v, ok := content.(*manifest.LoadBalancedWebService)
require.True(t, ok)

svcDiscoveryEndpointName := fmt.Sprintf("%s.%s.local", tc.envName, appName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ func TestLoadBalancedWebService_Template(t *testing.T) {
require.NoError(t, err)
envMft, err := mft.ApplyEnv(tc.envName)
require.NoError(t, err)

err = envMft.Validate()
require.NoError(t, err)
content := envMft.Manifest()

v, ok := envMft.(*manifest.LoadBalancedWebService)
v, ok := content.(*manifest.LoadBalancedWebService)
require.True(t, ok)

svcDiscoveryEndpointName := fmt.Sprintf("%s.%s.local", tc.envName, appName)
Expand Down
Loading