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

Cache results of verified resources. #515

Merged
merged 8 commits into from
Mar 3, 2023
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
11 changes: 11 additions & 0 deletions cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ var cliTests = []struct {
subOption: &ecspresso.VerifyOption{
GetSecrets: ptr(true),
PutLogs: ptr(true),
Cache: ptr(true),
},
},
{
Expand All @@ -515,6 +516,16 @@ var cliTests = []struct {
subOption: &ecspresso.VerifyOption{
GetSecrets: ptr(false),
PutLogs: ptr(false),
Cache: ptr(true),
},
},
{
args: []string{"verify", "--no-get-secrets", "--no-put-logs", "--no-cache"},
sub: "verify",
subOption: &ecspresso.VerifyOption{
GetSecrets: ptr(false),
PutLogs: ptr(false),
Cache: ptr(false),
},
},
{
Expand Down
2 changes: 2 additions & 0 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ var (
NewLogFilter = newLogFilter
NewConfigLoader = newConfigLoader
ArnToName = arnToName
InitVerifyState = initVerifyState
VerifyResource = verifyResource
)

func (d *App) SetLogger(logger *log.Logger) {
Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
github.com/aws/smithy-go v1.13.5
github.com/fatih/color v1.13.0
github.com/fujiwara/cfn-lookup v1.0.0
github.com/fujiwara/ecsta v0.1.3
github.com/fujiwara/ecsta v0.2.0
github.com/fujiwara/logutils v1.1.0
github.com/fujiwara/tfstate-lookup v1.0.0
github.com/goccy/go-yaml v1.9.5
Expand All @@ -40,7 +40,7 @@ require (
github.com/samber/lo v1.36.0
github.com/schollz/progressbar/v3 v3.11.0
github.com/shogo82148/go-retry v1.1.1
golang.org/x/sys v0.1.0
golang.org/x/sys v0.2.0
)

require (
Expand Down Expand Up @@ -91,12 +91,12 @@ require (
github.com/hashicorp/go-slug v0.10.0 // indirect
github.com/hashicorp/go-tfe v1.10.0 // indirect
github.com/hashicorp/jsonapi v0.0.0-20210826224640-ee7dae0fb22d // indirect
github.com/itchyny/gojq v0.12.9 // indirect
github.com/itchyny/timefmt-go v0.1.4 // indirect
github.com/itchyny/gojq v0.12.11 // indirect
github.com/itchyny/timefmt-go v0.1.5 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-ieproxy v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
Expand Down
18 changes: 10 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fujiwara/cfn-lookup v1.0.0 h1:oDhBXaEvT27B0dFB4z3zbjuTzA3E6m8t8Sge9mffAzU=
github.com/fujiwara/cfn-lookup v1.0.0/go.mod h1:HI8F4hlW+nN9KRTWNzdwvnOUQFrN8jX9puyfhtAVgwU=
github.com/fujiwara/ecsta v0.1.3 h1:sPh2N/HHrbMSNV0zzcYksne7qLtX99S9ndrnsiJ858o=
github.com/fujiwara/ecsta v0.1.3/go.mod h1:dizMf4vttV1G+X8aHST112P7RTpNUE1+Ka1e34segdk=
github.com/fujiwara/ecsta v0.2.0 h1:plUDvsA09PGH/hpYq8PdcYiaproLxZlcWJ0xubS5ilM=
github.com/fujiwara/ecsta v0.2.0/go.mod h1:BZMnz+fIatuerKO0MxaowUf+zXS1sS14TVai06hT4Y0=
github.com/fujiwara/logutils v1.1.0 h1:JAYmqW40d/ZjzouB01sfZiaTxwNe4hwmB6lLajZqm1s=
github.com/fujiwara/logutils v1.1.0/go.mod h1:pdb/Uk70rjQWEmFm/OvYH7OG8meZt1fEIqC0qZbvro4=
github.com/fujiwara/tfstate-lookup v1.0.0 h1:Ii2F3Y1KASnLcMf+rod7HVn1VuiY6mTKoHgKPNnK31A=
Expand Down Expand Up @@ -371,10 +371,10 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/itchyny/gojq v0.12.9 h1:biKpbKwMxVYhCU1d6mR7qMr3f0Hn9F5k5YykCVb3gmM=
github.com/itchyny/gojq v0.12.9/go.mod h1:T4Ip7AETUXeGpD+436m+UEl3m3tokRgajd5pRfsR5oE=
github.com/itchyny/timefmt-go v0.1.4 h1:hFEfWVdwsEi+CY8xY2FtgWHGQaBaC3JeHd+cve0ynVM=
github.com/itchyny/timefmt-go v0.1.4/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
github.com/itchyny/gojq v0.12.11 h1:YhLueoHhHiN4mkfM+3AyJV6EPcCxKZsOnYf+aVSwaQw=
github.com/itchyny/gojq v0.12.11/go.mod h1:o3FT8Gkbg/geT4pLI0tF3hvip5F3Y/uskjRz9OYa38g=
github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
Expand Down Expand Up @@ -406,8 +406,9 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
Expand Down Expand Up @@ -654,8 +655,9 @@ golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
Expand Down
8 changes: 8 additions & 0 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,11 @@ func parseTags(s string) ([]types.Tag, error) {
}
return tags, nil
}

func map2str(m map[string]string) string {
var p []string
for k, v := range m {
p = append(p, fmt.Sprintf("%s=%s", k, v))
}
return strings.Join(p, ",")
}
18 changes: 18 additions & 0 deletions util_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ecspresso_test

import (
"bytes"
"io"
"os"
"testing"

"github.com/aws/aws-sdk-go-v2/aws"
Expand Down Expand Up @@ -120,3 +123,18 @@ func TestParseTags(t *testing.T) {
}
}
}

func extractStdout(t *testing.T, fn func()) []byte {
t.Helper()
org := os.Stdout
defer func() {
os.Stdout = org
}()
r, w, _ := os.Pipe()
os.Stdout = w
fn()
w.Close()
var buf bytes.Buffer
io.Copy(&buf, r)
return buf.Bytes()
}
81 changes: 59 additions & 22 deletions verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,15 @@ func (d *App) newAssumedVerifier(ctx context.Context, cfg aws.Config, executionR
type VerifyOption struct {
GetSecrets *bool `help:"get secrets from ParameterStore or SecretsManager" default:"true" negatable:""`
PutLogs *bool `help:"put logs to CloudWatchLogs" default:"true" negatable:""`
Cache *bool `help:"use cache" default:"true" negatable:""`
}

type verifyResourceFunc func(context.Context) error

// Verify verifies service / task definitions related resources are valid.
func (d *App) Verify(ctx context.Context, opt VerifyOption) error {
initVerifyState(aws.ToBool(opt.Cache))

td, err := d.LoadTaskDefinition(d.config.TaskDefinitionPath)
if err != nil {
return err
Expand All @@ -173,34 +176,67 @@ func (d *App) Verify(ctx context.Context, opt VerifyOption) error {
{name: "Cluster", fn: d.verifyCluster},
}
for _, r := range resources {
if err := d.verifyResource(ctx, r.name, r.fn); err != nil {
if err := verifyResource(ctx, r.name, r.fn); err != nil {
return err
}
}
d.Log("Verify OK!")
return nil
}

var verifyResourceNestLevel = 0
var verifyState = struct {
cache verifyCache
level int
}{
cache: nil,
level: 0,
}

func initVerifyState(cache bool) {
if cache {
verifyState.cache = make(verifyCache, 100)
} else {
verifyState.cache = verifyCache(nil)
}
verifyState.level = 0
}

type verifyCache map[string]error

func (d *App) verifyResource(ctx context.Context, resourceType string, verifyFunc func(context.Context) error) error {
verifyResourceNestLevel++
defer func() { verifyResourceNestLevel-- }()
indent := strings.Repeat(" ", verifyResourceNestLevel)
func (v verifyCache) Do(ctx context.Context, name string, fn verifyResourceFunc) (error, bool) {
if v == nil {
return fn(ctx), false
}
if err, ok := v[name]; ok {
return err, true
}
err := fn(ctx)
v[name] = err
return err, false
}

func verifyResource(ctx context.Context, name string, verifyFunc func(context.Context) error) error {
verifyState.level++
defer func() { verifyState.level-- }()
indent := strings.Repeat(" ", verifyState.level)
print := func(f string, args ...interface{}) {
fmt.Printf(indent+f+"\n", args...)
}
print("%s", resourceType)
err := verifyFunc(ctx)
if err != nil {
if errors.As(err, &errSkipVerify) {
print("--> %s [%s] %s", resourceType, color.CyanString("SKIP"), color.CyanString(err.Error()))
print("%s", name)
var cached string
verifyErr, hit := verifyState.cache.Do(ctx, name, verifyFunc)
if hit {
cached = color.CyanString("(cached)")
}
if verifyErr != nil {
if errors.As(verifyErr, &errSkipVerify) {
print("--> [%s]%s %s", color.CyanString("SKIP"), cached, color.CyanString(verifyErr.Error()))
return nil
}
print("--> %s [%s] %s", resourceType, color.RedString("NG"), color.RedString(err.Error()))
return fmt.Errorf("verify %s failed: %w", resourceType, err)
print("--> [%s]%s %s", color.RedString("NG"), cached, color.RedString(verifyErr.Error()))
return fmt.Errorf("verify %s failed: %w", name, verifyErr)
}
print("--> [%s]", color.GreenString("OK"))
print("--> [%s]%s", color.GreenString("OK"), cached)
return nil
}

Expand Down Expand Up @@ -242,7 +278,7 @@ func (d *App) verifyServiceDefinition(ctx context.Context) error {
// LB
for i, lb := range sv.LoadBalancers {
name := fmt.Sprintf("LoadBalancer[%d]", i)
err := d.verifyResource(ctx, name, func(context.Context) error {
err := verifyResource(ctx, name, func(context.Context) error {
out, err := d.elbv2.DescribeTargetGroups(ctx, &elasticloadbalancingv2.DescribeTargetGroupsInput{
TargetGroupArns: []string{*lb.TargetGroupArn},
})
Expand Down Expand Up @@ -291,7 +327,7 @@ func (d *App) verifyTaskDefinition(ctx context.Context) error {

if execRole := td.ExecutionRoleArn; execRole != nil {
name := fmt.Sprintf("ExecutionRole[%s]", *execRole)
err := d.verifyResource(ctx, name, func(ctx context.Context) error {
err := verifyResource(ctx, name, func(ctx context.Context) error {
return d.verifyRole(ctx, *execRole)
})
if err != nil {
Expand All @@ -300,7 +336,7 @@ func (d *App) verifyTaskDefinition(ctx context.Context) error {
}
if taskRole := td.TaskRoleArn; taskRole != nil {
name := fmt.Sprintf("TaskRole[%s]", *taskRole)
err := d.verifyResource(ctx, name, func(ctx context.Context) error {
err := verifyResource(ctx, name, func(ctx context.Context) error {
return d.verifyRole(ctx, *taskRole)
})
if err != nil {
Expand All @@ -310,7 +346,7 @@ func (d *App) verifyTaskDefinition(ctx context.Context) error {

for _, c := range td.ContainerDefinitions {
name := fmt.Sprintf("ContainerDefinition[%s]", aws.ToString(c.Name))
err := d.verifyResource(ctx, name, func(ctx context.Context) error {
err := verifyResource(ctx, name, func(ctx context.Context) error {
return d.verifyContainer(ctx, &c, td)
})
if err != nil {
Expand Down Expand Up @@ -446,23 +482,24 @@ func (d *App) verifyImage(ctx context.Context, image string) error {
func (d *App) verifyContainer(ctx context.Context, c *types.ContainerDefinition, td *ecs.RegisterTaskDefinitionInput) error {
image := aws.ToString(c.Image)
name := fmt.Sprintf("Image[%s]", image)
err := d.verifyResource(ctx, name, func(ctx context.Context) error {
err := verifyResource(ctx, name, func(ctx context.Context) error {
return d.verifyImage(ctx, image)
})
if err != nil {
return err
}
for _, secret := range c.Secrets {
name := fmt.Sprintf("Secret %s[%s]", *secret.Name, *secret.ValueFrom)
err := d.verifyResource(ctx, name, func(ctx context.Context) error {
err := verifyResource(ctx, name, func(ctx context.Context) error {
return d.verifier.existsSecretValue(ctx, *secret.ValueFrom)
})
if err != nil {
return err
}
}
if c.LogConfiguration != nil && c.LogConfiguration.LogDriver == types.LogDriverAwslogs {
err := d.verifyResource(ctx, "LogConfiguration[awslogs]", func(ctx context.Context) error {
name := fmt.Sprintf("LogConfiguration[%s]", map2str(c.LogConfiguration.Options))
err := verifyResource(ctx, name, func(ctx context.Context) error {
return d.verifyLogConfiguration(ctx, c)
})
if err != nil {
Expand All @@ -471,7 +508,7 @@ func (d *App) verifyContainer(ctx context.Context, c *types.ContainerDefinition,
}
for _, envFile := range c.EnvironmentFiles {
name := fmt.Sprintf("EnvironmentFile[%s %s]", envFile.Type, aws.ToString(envFile.Value))
if err := d.verifyResource(ctx, name, func(ctx context.Context) error {
if err := verifyResource(ctx, name, func(ctx context.Context) error {
return d.verifier.existsEnvironmentFile(ctx, envFile)
}); err != nil {
return err
Expand Down
Loading