diff --git a/tools/reaper/README.md b/tools/reaper/README.md index c46541c..2947430 100644 --- a/tools/reaper/README.md +++ b/tools/reaper/README.md @@ -13,6 +13,7 @@ For listing the resources, readonly access to all the resources is needed. - AWS: Use the builtin `AWSResourceGroupsReadOnlyAccess` IAM policy . - Azure: Use the builtin `Reader` IAM role. - GCP: Use the builtin `Cloud Asset Viewer` IAM role. +- aws-nuke: See below for an AWS IAM policy document. For deleting the resources, grant the delete permission for the individual resources. @@ -25,6 +26,74 @@ account with the following permissions to delete integration test resources: - `artifactregistry.repositories.get` - `artifactregistry.repositories.delete` +For aws-nuke, a new deleter IAM policy can be created and assigned to the reaper +IAM principal with the following policy document: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "reaper", + "Effect": "Allow", + "Action": [ + "iam:ListPolicies", + "iam:ListRoles", + "iam:ListOpenIDConnectProviders", + "iam:ListAttachedRolePolicies", + "iam:ListAccountAliases", + "iam:ListRolePolicies", + "iam:GetRole", + "iam:GetPolicy", + "iam:GetOpenIDConnectProvider", + "ec2:DescribeAddresses", + "ec2:DescribeInstances", + "ec2:DescribeLaunchTemplates", + "ec2:DescribeNatGateways", + "ec2:DescribeSecurityGroups", + "ec2:DescribeInternetGateways", + "ec2:DescribeNetworkInterfaces", + "ec2:DescribeVpcs", + "ec2:DescribeVolumes", + "ec2:DescribeSubnets", + "ec2:DescribeRouteTables", + "autoscaling:DescribeAutoScalingGroups", + "eks:ListClusters", + "eks:ListNodegroups", + "eks:DescribeCluster", + "eks:DescribeNodegroup", + "ecr:ListTagsForResource", + "ecr:DescribeRepositories", + "ec2:DeleteSubnet", + "ec2:DeleteRouteTable", + "ec2:DeleteVolume", + "ec2:DeleteInternetGateway", + "ec2:DetachInternetGateway", + "ec2:RevokeSecurityGroupEgress", + "ec2:RevokeSecurityGroupIngress", + "ec2:DeleteSecurityGroup", + "ec2:DeleteNatGateway", + "ec2:DeleteVpc", + "ec2:ReleaseAddress", + "ec2:DeleteLaunchTemplate", + "ec2:TerminateInstances", + "eks:DeleteCluster", + "eks:DeleteNodegroup", + "iam:ListPolicyVersions", + "iam:DeletePolicyVersion", + "iam:DeletePolicy", + "iam:DeleteRolePolicy", + "iam:DetachRolePolicy", + "iam:DeleteOpenIDConnectProvider", + "iam:DeleteRole", + "ecr:DeleteRepository" + ], + "Resource": "*" + } + ] +} +``` + ## Usage Query the resources by providing the cloud provider name(`provider`) and the @@ -68,16 +137,9 @@ The above command would list the resources that are older than 3 days. In order to delete these resources, pass the `-delete` flag. -**NOTE:** Deleting resources is fully supported in Azure and GCP. Due to the -complexity of deleting the resources created in AWS, it's not implemented yet. -The test infrastructure for AWS involves a lot of individual components that -have to be managed independently, compared of Azure and GCP where resources -related to a cluster are related to one another and can be deleted all together. -If and when the complexity of the AWS test infrastructure is simplified, -deleting the resources can be easily implemented similar to the other providers. -Another issue that contributes to it is the stale resources that are reported -when listing resources via the Resource Groups Tagging API which makes it hard -to find out if a resource still exists or has been deleted without describing -the individual resource and checking their status. +**NOTE:** For AWS, unlike the other providers, a third party tool, `aws-nuke`, +is used. The `aws` provider may be removed in the future. It works in a very +limited manner using the Resource Groups Tagging API. The replacement, +`aws-nuke` provider, is capable of listing and deleting the resources properly. Use the `-h` flag to list all the available options. diff --git a/tools/reaper/aws-nuke.go b/tools/reaper/aws-nuke.go new file mode 100644 index 0000000..d8afa9b --- /dev/null +++ b/tools/reaper/aws-nuke.go @@ -0,0 +1,218 @@ +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/aws/aws-sdk-go/aws/endpoints" + "github.com/rebuy-de/aws-nuke/v2/cmd" + "github.com/rebuy-de/aws-nuke/v2/pkg/awsutil" + "github.com/rebuy-de/aws-nuke/v2/pkg/config" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" + awsresources "github.com/rebuy-de/aws-nuke/v2/resources" + + "github.com/fluxcd/test-infra/tftestenv" + "github.com/fluxcd/test-infra/tools/reaper/internal/awsnukemod" +) + +// getAWSAccountID returns the AWS account ID of the target aws account. +func getAWSAccountID(ctx context.Context, cliPath string) (string, error) { + output, err := tftestenv.RunCommandWithOutput(ctx, "./", + fmt.Sprintf(`%s sts get-caller-identity --query "Account" --output text`, cliPath), + tftestenv.RunCommandOptions{StdoutOnly: true}, + ) + if err != nil { + return "", err + } + id := strings.TrimSpace(string(output)) + if len(id) == 0 { + return "", fmt.Errorf("could not get aws account ID") + } + return id, nil +} + +// getAWSNukeConfig returns the aws-nuke configuration to be used against flux +// test-infra. +func getAWSNukeConfig(accountID string, regions []string) *config.Nuke { + nukeRegions := []string{"global"} + nukeRegions = append(nukeRegions, regions...) + + tagFilter := config.Filter{ + Property: fmt.Sprintf("tag:%s", tagKey), + Value: tagVal, + Invert: "true", + } + tagRoleFilter := config.Filter{ + Property: fmt.Sprintf("tag:role:%s", tagKey), + Value: tagVal, + Invert: "true", + } + tagIGWFilter := config.Filter{ + Property: fmt.Sprintf("tag:igw:%s", tagKey), + Value: tagVal, + Invert: "true", + } + + return &config.Nuke{ + Regions: nukeRegions, + // Set a fake account in the blocklist to suppress this validation + // https://github.com/rebuy-de/aws-nuke/blob/v2.25.0/pkg/config/config.go#L121-L125. + // It is a requirement to set a production account ID in blocklist. + AccountBlocklist: []string{"999999999999"}, + Accounts: map[string]config.Account{ + accountID: { + ResourceTypes: config.ResourceTypes{ + Targets: types.Collection{ + "EC2VPC", + "EC2SecurityGroup", + "EC2LaunchTemplate", + "EC2RouteTable", + "EC2NetworkInterface", + "ECRRepository", + "EC2Volume", + "EKSNodegroups", + "EC2Subnet", + "AutoScalingGroup", + "EC2Address", + "EKSCluster", + "EC2InternetGatewayAttachment", + "EC2InternetGateway", + "EC2Instance", + "EC2NATGateway", + "IAMRole", + "IAMRolePolicy", + "IAMRolePolicyAttachment", + "IAMPolicy", + "IAMOpenIDConnectProvider", + }, + }, + Filters: config.Filters{ + "EC2VPC": []config.Filter{tagFilter}, + "EC2SecurityGroup": []config.Filter{tagFilter}, + "EC2LaunchTemplate": []config.Filter{tagFilter}, + "EC2RouteTable": []config.Filter{tagFilter}, + "EC2NetworkInterface": []config.Filter{tagFilter}, + "ECRRepository": []config.Filter{tagFilter}, + "EC2Volume": []config.Filter{tagFilter}, + "EKSNodegroups": []config.Filter{tagFilter}, + "EC2Subnet": []config.Filter{tagFilter}, + "AutoScalingGroup": []config.Filter{tagFilter}, + "EC2Address": []config.Filter{tagFilter}, + "EKSCluster": []config.Filter{tagFilter}, + "EC2InternetGatewayAttachment": []config.Filter{tagIGWFilter}, + "EC2InternetGateway": []config.Filter{tagFilter}, + "EC2Instance": []config.Filter{tagFilter}, + "EC2NATGateway": []config.Filter{tagFilter}, + "IAMRole": []config.Filter{tagFilter}, + "IAMRolePolicy": []config.Filter{tagFilter}, + "IAMRolePolicyAttachment": []config.Filter{tagRoleFilter}, + "IAMPolicy": []config.Filter{tagFilter}, + "IAMOpenIDConnectProvider": []config.Filter{tagFilter}, + }, + }, + }, + } +} + +// awsnukeScan configures and scans the target aws account with aws-nuke, and +// returns an instance of aws-nuke. +func awsnukeScan(accountID string) (*awsnukemod.Nuke, error) { + // Parse the regions and set the default region. + regions := strings.Split(*awsRegions, ",") + // Use the first region as the default. + defaultRegion := regions[0] + + var creds awsutil.Credentials + + // Read aws credentials from the environment and validate. + creds.AccessKeyID = os.Getenv("AWS_ACCESS_KEY_ID") + creds.SecretAccessKey = os.Getenv("AWS_SECRET_ACCESS_KEY") + creds.Profile = os.Getenv("AWS_PROFILE") + creds.SessionToken = os.Getenv("AWS_SESSION_TOKEN") + creds.AssumeRoleArn = os.Getenv("AWS_ROLE_ARN") + if creds.HasProfile() && creds.HasKeys() { + return nil, fmt.Errorf("please provide either AWS_PROFILE or " + + "AWS_ACCESS_KEY_ID with AWS_SECRET_ACCESS_KEY and optionally " + + "AWS_SESSION_TOKEN environment variables") + } + + nukeCfg := getAWSNukeConfig(accountID, regions) + + if defaultRegion != "" { + awsutil.DefaultRegionID = defaultRegion + switch defaultRegion { + case endpoints.UsEast1RegionID, endpoints.UsEast2RegionID, endpoints.UsWest1RegionID, endpoints.UsWest2RegionID: + awsutil.DefaultAWSPartitionID = endpoints.AwsPartitionID + case endpoints.UsGovEast1RegionID, endpoints.UsGovWest1RegionID: + awsutil.DefaultAWSPartitionID = endpoints.AwsUsGovPartitionID + case endpoints.CnNorth1RegionID, endpoints.CnNorthwest1RegionID: + awsutil.DefaultAWSPartitionID = endpoints.AwsCnPartitionID + default: + if nukeCfg.CustomEndpoints.GetRegion(defaultRegion) == nil { + err := fmt.Errorf("the custom region '%s' must be specified in the configuration 'endpoints'", defaultRegion) + return nil, err + } + } + } + + account, err := awsutil.NewAccount(creds, nukeCfg.CustomEndpoints) + if err != nil { + return nil, err + } + params := cmd.NukeParameters{ + Quiet: true, + } + n := awsnukemod.NewNuke(params, *account) + n.Config = nukeCfg + + err = n.GatherResources() + return n, err +} + +// awsnukeItemsToResources converts the items which would be removed to resource +// type. +func awsnukeItemsToResources(items cmd.Queue) []resource { + resources := []resource{} + + for _, item := range items { + // Only consider items that would be removed. + if item.State != cmd.ItemStateNew { + continue + } + + r := resource{} + rString, ok := item.Resource.(awsresources.LegacyStringer) + if ok { + r.Name = rString.String() + } + r.Location = item.Region.Name + r.Type = item.Type + r.Tags = map[string]string{} + rProp, ok := item.Resource.(awsresources.ResourcePropertyGetter) + if ok { + r.Tags = rProp.Properties() + } + + resources = append(resources, r) + } + + return resources +} diff --git a/tools/reaper/aws-nuke_test.go b/tools/reaper/aws-nuke_test.go new file mode 100644 index 0000000..f65a475 --- /dev/null +++ b/tools/reaper/aws-nuke_test.go @@ -0,0 +1,107 @@ +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "testing" + + . "github.com/onsi/gomega" + "github.com/rebuy-de/aws-nuke/v2/cmd" + + "github.com/fluxcd/test-infra/tools/reaper/internal/awsnukemod" +) + +func Test_awsnukeItemsToResources(t *testing.T) { + fakeRegion1 := &cmd.Region{Name: "aa"} + fakeRegion2 := &cmd.Region{Name: "bb"} + fakeResourceType := "Foo" + + tests := []struct { + name string + items cmd.Queue + want []resource + }{ + { + name: "only converts the items to be deleted", + items: []*cmd.Item{ + { + Resource: awsnukemod.MockResource{ARN: "a1"}, + State: cmd.ItemStateFailed, + Region: fakeRegion1, + Type: fakeResourceType, + }, + { + Resource: awsnukemod.MockResource{ARN: "a2"}, + State: cmd.ItemStateNew, + Region: fakeRegion1, + Type: fakeResourceType, + }, + { + Resource: awsnukemod.MockResourceWithTags("a3", map[string]string{"o": "p"}), + State: cmd.ItemStateFiltered, + Region: fakeRegion1, + Type: fakeResourceType, + }, + { + Resource: awsnukemod.MockResource{ARN: "a4"}, + State: cmd.ItemStateFinished, + Region: fakeRegion1, + Type: fakeResourceType, + }, + { + Resource: awsnukemod.MockResource{ARN: "a5"}, + State: cmd.ItemStatePending, + Region: fakeRegion1, + Type: fakeResourceType, + }, + { + Resource: awsnukemod.MockResource{ARN: "a6"}, + State: cmd.ItemStatePending, + Region: fakeRegion1, + Type: fakeResourceType, + }, + { + Resource: awsnukemod.MockResourceWithTags("a7", map[string]string{"m": "n"}), + State: cmd.ItemStateNew, + Region: fakeRegion2, + Type: fakeResourceType, + }, + }, + want: []resource{ + { + Name: "a2", + Type: fakeResourceType, + Location: fakeRegion1.Name, + Tags: nil, + }, + { + Name: "a7", + Type: fakeResourceType, + Location: fakeRegion2.Name, + Tags: map[string]string{"m": "n"}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + got := awsnukeItemsToResources(tt.items) + g.Expect(got).To(Equal(tt.want)) + }) + } +} diff --git a/tools/reaper/go.mod b/tools/reaper/go.mod index 8a0513d..7d549c4 100644 --- a/tools/reaper/go.mod +++ b/tools/reaper/go.mod @@ -1,13 +1,20 @@ module github.com/fluxcd/test-infra/tools/reaper -go 1.20 +go 1.22 replace github.com/fluxcd/test-infra/tftestenv => ../../tftestenv +// Refer https://github.com/rebuy-de/aws-nuke/issues/955. Use Optum's forked +// patch for now. +replace github.com/rebuy-de/aws-nuke/v2 => github.com/Optum/aws-nuke/v2 v2.25.1 + require ( + github.com/aws/aws-sdk-go v1.50.6 github.com/fluxcd/test-infra/tftestenv v0.0.0 github.com/k1LoW/duration v1.2.0 github.com/onsi/gomega v1.18.1 + github.com/rebuy-de/aws-nuke/v2 v2.25.0 + github.com/sirupsen/logrus v1.9.3 ) require ( @@ -22,6 +29,7 @@ require ( github.com/docker/docker-credential-helpers v0.6.4 // indirect github.com/emicklei/go-restful v2.9.5+incompatible // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect + github.com/fatih/color v1.16.0 // indirect github.com/go-logr/logr v1.2.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect @@ -31,6 +39,7 @@ require ( github.com/google/gnostic v0.5.7-v3refs // indirect github.com/google/go-containerregistry v0.11.0 // indirect github.com/google/gofuzz v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -39,10 +48,15 @@ require ( github.com/hashicorp/terraform-exec v0.18.1 // indirect github.com/hashicorp/terraform-json v0.15.0 // indirect github.com/imdario/mergo v0.3.12 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.8 // indirect github.com/mailru/easyjson v0.7.6 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mb0/glob v0.0.0-20160210091149-1eb79d2de6c4 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -50,24 +64,24 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/vbatts/tar-split v0.11.2 // indirect github.com/zclconf/go-cty v1.13.0 // indirect - golang.org/x/crypto v0.5.0 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.5.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/mod v0.9.0 // indirect + golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 // indirect - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/term v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.24.1 // indirect k8s.io/apimachinery v0.24.1 // indirect k8s.io/client-go v0.24.1 // indirect diff --git a/tools/reaper/go.sum b/tools/reaper/go.sum index b8be87b..faa096e 100644 --- a/tools/reaper/go.sum +++ b/tools/reaper/go.sum @@ -73,9 +73,12 @@ github.com/Masterminds/sprig/v3 v3.2.1/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFP github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= +github.com/Optum/aws-nuke/v2 v2.25.1 h1:TC+RJqPHpJsG/64xvKQVp6AdC/nD/NxDV21z6jfPEPQ= +github.com/Optum/aws-nuke/v2 v2.25.1/go.mod h1:RhL1bOdkONsSLmRF6hIVVWr0xEVUcqXLgWMyIi0uRls= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -111,6 +114,8 @@ github.com/ashanbrown/makezero v0.0.0-20210520155254-b6261585ddde/go.mod h1:oG9D github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.36.30/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.50.6 h1:FaXvNwHG3Ri1paUEW16Ahk9zLVqSAdqa1M3phjZR35Q= +github.com/aws/aws-sdk-go v1.50.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -127,6 +132,7 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.9/go.mod h1:SSbRIBVfMjCi/kEB6K65XEA83D6prSM8ap1UCpNKtgg= github.com/chavacava/garif v0.0.0-20210405164556-e8a0a408d6af/go.mod h1:Qjyv4H3//PWVzTeCezG2b9IRn6myJxJSr4TD/xo6ojU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -152,6 +158,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/daixiang0/gci v0.2.9/go.mod h1:+4dZ7TISfSmqfAGv59ePaHfNzgGtIkHAhhdKggP1JAc= @@ -196,6 +203,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= @@ -230,6 +239,7 @@ github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTg github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk= +github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -281,6 +291,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -365,6 +376,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -456,6 +469,8 @@ github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= @@ -464,7 +479,9 @@ github.com/jhump/protoreflect v1.6.1/go.mod h1:RZQ/lnuN+zqeRVpQigTwO6o0AJUkxbnSn github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +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= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -542,6 +559,8 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -549,6 +568,9 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= @@ -557,6 +579,9 @@ github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOq github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mb0/glob v0.0.0-20160210091149-1eb79d2de6c4 h1:NK3O7S5FRD/wj7ORQ5C3Mx1STpyEMuFe+/F0Lakd1Nk= +github.com/mb0/glob v0.0.0-20160210091149-1eb79d2de6c4/go.mod h1:FqD3ES5hx6zpzDainDaHgkTIqrPaI9uX4CVWqYZoQjY= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= github.com/mgechev/dots v0.0.0-20210922191527-e955255bf517/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg= github.com/mgechev/revive v1.1.2/go.mod h1:bnXsMr+ZTH09V5rssEI+jHAZ4z+ZdyhgO/zsy3EhK+0= @@ -623,6 +648,7 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -664,6 +690,7 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -672,11 +699,13 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA= github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= @@ -695,6 +724,7 @@ github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.2.3/go.mod h1:rYbA/4Tg5c54mV1sv4sQTP5WOPBcoLtnBZ7/TEhXAbg= github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0KCtEdgEkHwDbigdA= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -705,6 +735,7 @@ github.com/sebdah/goldie v1.0.0/go.mod h1:jXP4hmWywNEwZzhMuv2ccnqTSFpuq8iyQhtQdk github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shirou/gopsutil/v3 v3.21.10/go.mod h1:t75NhzCZ/dYyPQjyQmrAYP6c8+LCdFANeBMdLPCNnew= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -717,8 +748,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/tenv v1.4.7/go.mod h1:5nF+bITvkebQVanjU6IuMbvIot/7ReNsUV7I5NbprB0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -735,6 +766,8 @@ github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -755,8 +788,9 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/sylvia7788/contextcheck v1.0.4/go.mod h1:vuPKJMQ7MQ91ZTqfdyreNKwZjyUg6KO+IebVyQDedZQ= github.com/tdakkota/asciicheck v0.0.0-20200416200610-e657995f937b/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM= @@ -843,6 +877,7 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -865,8 +900,9 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -905,8 +941,9 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -962,8 +999,9 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -995,8 +1033,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1082,13 +1121,17 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1098,8 +1141,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1215,6 +1259,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= +gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1391,9 +1436,11 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1405,6 +1452,7 @@ honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= k8s.io/api v0.24.1 h1:BjCMRDcyEYz03joa3K1+rbshwh1Ay6oB53+iUx2H8UY= k8s.io/api v0.24.1/go.mod h1:JhoOvNiLXKTPQ60zh2g0ewpA+bnEYf5q44Flhquh4vQ= k8s.io/apiextensions-apiserver v0.24.0 h1:JfgFqbA8gKJ/uDT++feAqk9jBIwNnL9YGdQvaI9DLtY= +k8s.io/apiextensions-apiserver v0.24.0/go.mod h1:iuVe4aEpe6827lvO6yWQVxiPSpPoSKVjkq+MIdg84cM= k8s.io/apimachinery v0.24.1 h1:ShD4aDxTQKN5zNf8K1RQ2u98ELLdIW7jEnlO9uAMX/I= k8s.io/apimachinery v0.24.1/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/client-go v0.24.1 h1:w1hNdI9PFrzu3OlovVeTnf4oHDt+FJLd9Ndluvnb42E= diff --git a/tools/reaper/internal/awsnukemod/doc.go b/tools/reaper/internal/awsnukemod/doc.go new file mode 100644 index 0000000..def9281 --- /dev/null +++ b/tools/reaper/internal/awsnukemod/doc.go @@ -0,0 +1,32 @@ +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package awsnukemod contains copies of code from the aws-nuke project +// https://github.com/rebuy-de/aws-nuke and modifications to it. In order to +// integrate with reaper, the resources observed by aws-nuke needed to be +// converted into the resource data type of the reaper, so that the list of +// resources can be printed in a coherent manner across all the different +// providers. For this, the Nuke.Run() command, which combines scan and delete, +// to be split into separate steps. Hence, the mods.go adds GatherResources() +// and Delete() to Nuke. +// The Nuke.Scan() function prints all the scanned resources. This breaks the +// reaper interface. Scan() is modified to not print the resources. +// To support the retain-period feature of reaper, aws-nuke needs to understand +// the custom timestamp that test-env uses. Since the default aws-nuke filters +// can't be appended without copying and modifying more code, +// ApplyRetentionFilter() is introduced on Nuke. This allows applying the filter +// on the items after gathering all the resources and before deleting them. +package awsnukemod diff --git a/tools/reaper/internal/awsnukemod/mock.go b/tools/reaper/internal/awsnukemod/mock.go new file mode 100644 index 0000000..9f9e197 --- /dev/null +++ b/tools/reaper/internal/awsnukemod/mock.go @@ -0,0 +1,36 @@ +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package awsnukemod + +import "github.com/rebuy-de/aws-nuke/v2/pkg/types" + +type MockResource struct { + ARN string + Tags types.Properties + RemoveError error +} + +func (mr MockResource) Remove() error { return mr.RemoveError } +func (mr MockResource) String() string { return mr.ARN } +func (mr MockResource) Properties() types.Properties { return mr.Tags } + +func MockResourceWithTags(arn string, props map[string]string) MockResource { + return MockResource{ + ARN: arn, + Tags: types.Properties(props), + } +} diff --git a/tools/reaper/internal/awsnukemod/mods.go b/tools/reaper/internal/awsnukemod/mods.go new file mode 100644 index 0000000..522afc4 --- /dev/null +++ b/tools/reaper/internal/awsnukemod/mods.go @@ -0,0 +1,163 @@ +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code in this file extends the Nuke data type defined in nuke.go. +package awsnukemod + +import ( + "fmt" + "log" + "time" + + "github.com/k1LoW/duration" + "github.com/rebuy-de/aws-nuke/v2/cmd" + + "github.com/fluxcd/test-infra/tftestenv" +) + +// TODO: Combine this and the createdat constant in filter.go. +const createdat = "createdat" + +// Items returns the resource items that Nuke has observed. +func (n *Nuke) Items() cmd.Queue { + return n.items +} + +// ApplyRetentionFilter applies the retention filter on the aws-nuke items that +// are to be removed. It only alters the items that were already selected for +// removal by checking if the retention period applies to them. If an item is to +// be removed but don't contain the createdat tag, it is filtered to not be +// removed. This function reduces the number of items to be removed or keeps +// them the same as before. It never increases the items to be deleted. +func (n *Nuke) ApplyRetentionFilter(period string) error { + p, err := duration.Parse(period) + if err != nil { + return err + } + // Subtract period from now in UTC. + now := time.Now().UTC() + date := now.Add(-p) + + // Read the createdat tag from resources and check if they were created + // before date. + for _, item := range n.items { + if item.State != cmd.ItemStateNew { + continue + } + + prop, err := getCreatedAt(item) + // TODO: Maybe return the error if encountered? + if err != nil || prop == "" { + // The item will be removed but don't contain the createdat tag. + // Update the item state to filtered to avoid deleting them. + item.State = cmd.ItemStateFiltered + item.Reason = "filtered by retention-period" + continue + } + + createdat, err := tftestenv.ParseCreatedAtTime(prop) + if err != nil { + return err + } + // If the item was not created before the retention period, filter it + // out to not be removed. + if !createdat.Before(date) { + item.State = cmd.ItemStateFiltered + item.Reason = "filtered by retention-period" + } + } + + return nil +} + +// getCreatedAt returns the value of createdat tag if present on the given +// resource item. Some resources have variation in the tag key, it attempts to +// handle them and get the createdat tag value. +func getCreatedAt(item *cmd.Item) (string, error) { + // TODO: Maybe base this on the type of resource with switch case. + propPrefix := []string{"tag", "tag:role", "tag:igw"} + for _, pp := range propPrefix { + prop, err := item.GetProperty(fmt.Sprintf("%s:%s", pp, createdat)) + if err != nil { + return "", err + } + if prop != "" { + return prop, nil + } + } + return "", nil +} + +// GatherResources runs aws-nuke scan to gather all the resource details. +func (n *Nuke) GatherResources() error { + err := n.Config.ValidateAccount(n.Account.ID(), n.Account.Aliases()) + if err != nil { + return err + } + + return n.Scan() +} + +// Delete deletes the resources. This is based on the upstream Nuke.Run() which +// deletes the resources at the end. +func (n *Nuke) Delete() error { + failCount := 0 + waitingCount := 0 + + for { + n.HandleQueue() + + if n.items.Count(cmd.ItemStatePending, cmd.ItemStateWaiting, cmd.ItemStateNew) == 0 && n.items.Count(cmd.ItemStateFailed) > 0 { + if failCount >= 2 { + log.Println("There are resources in failed state, but none are ready for deletion, anymore.") + fmt.Println() + + for _, item := range n.items { + if item.State != cmd.ItemStateFailed { + continue + } + + item.Print() + log.Println(item.Reason) + } + + return fmt.Errorf("failed") + } + + failCount = failCount + 1 + } else { + failCount = 0 + } + if n.Parameters.MaxWaitRetries != 0 && n.items.Count(cmd.ItemStateWaiting, cmd.ItemStatePending) > 0 && n.items.Count(cmd.ItemStateNew) == 0 { + if waitingCount >= n.Parameters.MaxWaitRetries { + return fmt.Errorf("max wait retries of %d exceeded", n.Parameters.MaxWaitRetries) + } + waitingCount = waitingCount + 1 + } else { + waitingCount = 0 + } + if n.items.Count(cmd.ItemStateNew, cmd.ItemStatePending, cmd.ItemStateFailed, cmd.ItemStateWaiting) == 0 { + break + } + + time.Sleep(5 * time.Second) + } + + fmt.Printf("Nuke complete: %d failed, %d skipped, %d finished.\n\n", + n.items.Count(cmd.ItemStateFailed), n.items.Count(cmd.ItemStateFiltered), n.items.Count(cmd.ItemStateFinished)) + + return nil +} diff --git a/tools/reaper/internal/awsnukemod/mods_test.go b/tools/reaper/internal/awsnukemod/mods_test.go new file mode 100644 index 0000000..9143b9a --- /dev/null +++ b/tools/reaper/internal/awsnukemod/mods_test.go @@ -0,0 +1,137 @@ +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package awsnukemod + +import ( + "testing" + "time" + + . "github.com/onsi/gomega" + "github.com/rebuy-de/aws-nuke/v2/cmd" + + "github.com/fluxcd/test-infra/tftestenv" +) + +func TestNuke_ApplyRetentionFilter(t *testing.T) { + // Construct relative time to be used in the test cases. + now := time.Now().UTC() + period := "2h" + beforePeriod := now.Add(-time.Hour * (2 + 1)) + afterPeriod := now.Add(-time.Hour) + + fakeRegion := cmd.Region{Name: "zz"} + + tests := []struct { + name string + inputPeriod string + items cmd.Queue + wantErr bool + wantItemStates []cmd.ItemState + }{ + { + name: "old, new, without createdat, filtered, waiting states", + inputPeriod: period, + items: []*cmd.Item{ + { + Resource: MockResourceWithTags("a1", map[string]string{"tag:" + createdat: beforePeriod.Format(tftestenv.CreatedAtTimeLayout)}), + State: cmd.ItemStateNew, + Region: &fakeRegion, + }, + { + Resource: MockResourceWithTags("a2", map[string]string{"tag:" + createdat: afterPeriod.Format(tftestenv.CreatedAtTimeLayout)}), + State: cmd.ItemStateNew, + Region: &fakeRegion, + }, + { + Resource: MockResource{ARN: "a3"}, + State: cmd.ItemStateNew, + Region: &fakeRegion, + }, + { + Resource: MockResourceWithTags("a4", map[string]string{"tag:" + createdat: beforePeriod.Format(tftestenv.CreatedAtTimeLayout)}), + State: cmd.ItemStateFiltered, + Region: &fakeRegion, + }, + { + Resource: MockResourceWithTags("a5", map[string]string{"tag:" + createdat: beforePeriod.Format(tftestenv.CreatedAtTimeLayout)}), + State: cmd.ItemStateWaiting, + Region: &fakeRegion, + }, + { + Resource: MockResourceWithTags("a6", map[string]string{"tag:" + createdat: ""}), + State: cmd.ItemStateNew, + Region: &fakeRegion, + }, + { + Resource: MockResourceWithTags("a7", map[string]string{"tag:role:" + createdat: beforePeriod.Format(tftestenv.CreatedAtTimeLayout)}), + State: cmd.ItemStateNew, + Region: &fakeRegion, + }, + { + Resource: MockResourceWithTags("a8", map[string]string{"tag:igw:" + createdat: beforePeriod.Format(tftestenv.CreatedAtTimeLayout)}), + State: cmd.ItemStateNew, + Region: &fakeRegion, + }, + }, + wantItemStates: []cmd.ItemState{ + cmd.ItemStateNew, + cmd.ItemStateFiltered, + cmd.ItemStateFiltered, + cmd.ItemStateFiltered, + cmd.ItemStateWaiting, + cmd.ItemStateFiltered, + cmd.ItemStateNew, + cmd.ItemStateNew, + }, + }, + { + name: "invalid created at", + inputPeriod: period, + items: []*cmd.Item{ + { + Resource: MockResourceWithTags("a2", map[string]string{"tag:" + createdat: "222222"}), + State: cmd.ItemStateNew, + Region: &fakeRegion, + }, + }, + wantErr: true, + }, + { + name: "invalid period", + inputPeriod: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + + n := &Nuke{ + items: tt.items, + } + err := n.ApplyRetentionFilter(tt.inputPeriod) + if (err != nil) != tt.wantErr { + t.Fatalf("Nuke.ApplyRetentionFilter() error = %v, wantErr %v", err, tt.wantErr) + } + if err == nil { + for i, item := range n.Items() { + g.Expect(item.State).To(Equal(tt.wantItemStates[i])) + } + } + }) + } +} diff --git a/tools/reaper/internal/awsnukemod/nuke.go b/tools/reaper/internal/awsnukemod/nuke.go new file mode 100644 index 0000000..fcd434c --- /dev/null +++ b/tools/reaper/internal/awsnukemod/nuke.go @@ -0,0 +1,260 @@ +/* +Copyright 2024 The Flux authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +The MIT License (MIT) + +Copyright (c) 2016 reBuy reCommerce GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +This file is copied from the source at +https://github.com/rebuy-de/aws-nuke/blob/v2.25.0/cmd/nuke.go, modified to keep +only the functions we need. Function modification is stated in the function +comment explicitly. The extension of the data type defined in this file exist in +mods.go. +*/ + +package awsnukemod + +import ( + "fmt" + + "github.com/rebuy-de/aws-nuke/v2/cmd" + "github.com/rebuy-de/aws-nuke/v2/pkg/awsutil" + "github.com/rebuy-de/aws-nuke/v2/pkg/config" + "github.com/rebuy-de/aws-nuke/v2/pkg/types" + "github.com/rebuy-de/aws-nuke/v2/resources" + "github.com/sirupsen/logrus" +) + +type Nuke struct { + Parameters cmd.NukeParameters + Account awsutil.Account + Config *config.Nuke + + ResourceTypes types.Collection + + items cmd.Queue +} + +func NewNuke(params cmd.NukeParameters, account awsutil.Account) *Nuke { + n := Nuke{ + Parameters: params, + Account: account, + } + + return &n +} + +// Most of this code is covered by the MIT License except for the commented out +// the print statements at the bottom, which are covered by the Apache License. +func (n *Nuke) Scan() error { + accountConfig := n.Config.Accounts[n.Account.ID()] + + resourceTypes := cmd.ResolveResourceTypes( + resources.GetListerNames(), + resources.GetCloudControlMapping(), + []types.Collection{ + n.Parameters.Targets, + n.Config.ResourceTypes.Targets, + accountConfig.ResourceTypes.Targets, + }, + []types.Collection{ + n.Parameters.Excludes, + n.Config.ResourceTypes.Excludes, + accountConfig.ResourceTypes.Excludes, + }, + []types.Collection{ + n.Parameters.CloudControl, + n.Config.ResourceTypes.CloudControl, + accountConfig.ResourceTypes.CloudControl, + }, + ) + + queue := make(cmd.Queue, 0) + + for _, regionName := range n.Config.Regions { + region := cmd.NewRegion(regionName, n.Account.ResourceTypeToServiceType, n.Account.NewSession) + + items := cmd.Scan(region, resourceTypes) + for item := range items { + ffGetter, ok := item.Resource.(resources.FeatureFlagGetter) + if ok { + ffGetter.FeatureFlags(n.Config.FeatureFlags) + } + + queue = append(queue, item) + err := n.Filter(item) + if err != nil { + return err + } + + // if item.State != cmd.ItemStateFiltered || !n.Parameters.Quiet { + // item.Print() + // } + } + } + + // fmt.Printf("Scan complete: %d total, %d nukeable, %d filtered.\n\n", + // queue.CountTotal(), queue.Count(cmd.ItemStateNew), queue.Count(cmd.ItemStateFiltered)) + + n.items = queue + + return nil +} + +func (n *Nuke) Filter(item *cmd.Item) error { + + checker, ok := item.Resource.(resources.Filter) + if ok { + err := checker.Filter() + if err != nil { + item.State = cmd.ItemStateFiltered + item.Reason = err.Error() + + // Not returning the error, since it could be because of a failed + // request to the API. We do not want to block the whole nuking, + // because of an issue on AWS side. + return nil + } + } + + accountFilters, err := n.Config.Filters(n.Account.ID()) + if err != nil { + return err + } + + itemFilters, ok := accountFilters[item.Type] + if !ok { + return nil + } + + for _, filter := range itemFilters { + prop, err := item.GetProperty(filter.Property) + if err != nil { + logrus.Warnf(err.Error()) + continue + } + match, err := filter.Match(prop) + if err != nil { + return err + } + + if cmd.IsTrue(filter.Invert) { + match = !match + } + + if match { + item.State = cmd.ItemStateFiltered + item.Reason = "filtered by config" + return nil + } + } + + return nil +} + +func (n *Nuke) HandleQueue() { + listCache := make(map[string]map[string][]resources.Resource) + + for _, item := range n.items { + switch item.State { + case cmd.ItemStateNew: + n.HandleRemove(item) + item.Print() + case cmd.ItemStateFailed: + n.HandleRemove(item) + n.HandleWait(item, listCache) + item.Print() + case cmd.ItemStatePending: + n.HandleWait(item, listCache) + item.State = cmd.ItemStateWaiting + item.Print() + case cmd.ItemStateWaiting: + n.HandleWait(item, listCache) + item.Print() + } + + } + + fmt.Println() + fmt.Printf("Removal requested: %d waiting, %d failed, %d skipped, %d finished\n\n", + n.items.Count(cmd.ItemStateWaiting, cmd.ItemStatePending), n.items.Count(cmd.ItemStateFailed), + n.items.Count(cmd.ItemStateFiltered), n.items.Count(cmd.ItemStateFinished)) +} + +func (n *Nuke) HandleRemove(item *cmd.Item) { + err := item.Resource.Remove() + if err != nil { + item.State = cmd.ItemStateFailed + item.Reason = err.Error() + return + } + + item.State = cmd.ItemStatePending + item.Reason = "" +} + +func (n *Nuke) HandleWait(item *cmd.Item, cache map[string]map[string][]resources.Resource) { + var err error + region := item.Region.Name + _, ok := cache[region] + if !ok { + cache[region] = map[string][]resources.Resource{} + } + left, ok := cache[region][item.Type] + if !ok { + left, err = item.List() + if err != nil { + item.State = cmd.ItemStateFailed + item.Reason = err.Error() + return + } + cache[region][item.Type] = left + } + + for _, r := range left { + if item.Equals(r) { + checker, ok := r.(resources.Filter) + if ok { + err := checker.Filter() + if err != nil { + break + } + } + + return + } + } + + item.State = cmd.ItemStateFinished + item.Reason = "" +} diff --git a/tools/reaper/main.go b/tools/reaper/main.go index 3c26b10..4461779 100644 --- a/tools/reaper/main.go +++ b/tools/reaper/main.go @@ -26,12 +26,17 @@ import ( "os/exec" "strings" "time" + + "github.com/rebuy-de/aws-nuke/v2/cmd" + + "github.com/fluxcd/test-infra/tools/reaper/internal/awsnukemod" ) const ( - aws = "aws" - azure = "azure" - gcp = "gcp" + aws = "aws" + azure = "azure" + gcp = "gcp" + awsnuke = "aws-nuke" ) // registryTypes maps the registry type resources with their resource.Type value @@ -63,9 +68,10 @@ type resource struct { } var ( - supportedProviders = []string{aws, azure, gcp} + supportedProviders = []string{aws, azure, gcp, awsnuke} targetProvider = flag.String("provider", "", fmt.Sprintf("name of the provider %v", supportedProviders)) gcpProject = flag.String("gcpproject", "", "GCP project name") + awsRegions = flag.String("awsregions", "", "Comma separated list of aws regions for aws-nuke (e.g.: us-east-1,us-east-2). The first entry is used as the default region") tags = flag.String("tags", "", "key-value pair of tag to query with. Only single pair supported at present ('environment=dev')") retentionPeriod = flag.String("retention-period", "", "period for which the resources should be retained (e.g.: 1d, 1h)") jsonoutput = flag.Bool("ojson", false, "JSON output") @@ -84,6 +90,7 @@ func main() { // Paths of the cloud provider CLI binaries. var awsPath, azPath, gcloudPath string + var awsNuker *awsnukemod.Nuke t, err := time.ParseDuration(*timeout) if err != nil { @@ -150,19 +157,53 @@ func main() { *gcpProject = p } resources, queryErr = getGCPResources(ctx, gcloudPath, jqBinPath) + case awsnuke: + // Get the account ID of the IAM principal using AWS CLI. Since aws-nuke + // can work on multiple accounts, it explicitly needs the target account + // ID. + awsPath, err = exec.LookPath("aws") + if err != nil { + log.Fatalln(err) + } + awsAccountID, err := getAWSAccountID(ctx, awsPath) + if err != nil { + log.Fatalln(err) + } + if *awsRegions == "" { + log.Fatalf("-awsregions flag unset. AWS regions must be set for aws-nuke") + } + + // Query aws resources using aws-nuke. + awsNuker, queryErr = awsnukeScan(awsAccountID) + if queryErr == nil { + resources = awsnukeItemsToResources(awsNuker.Items()) + } } if queryErr != nil { log.Fatalf("Query error: %v", queryErr) } - // Print only the result to stdout. + // Apply the retention period filter. if *retentionPeriod != "" && len(resources) > 0 { - resources, err = applyRetentionFilter(resources, *retentionPeriod) - if err != nil { - log.Fatalf("Failed to filter resources with retention-period: %v", err) + switch *targetProvider { + case awsnuke: + if err := awsNuker.ApplyRetentionFilter(*retentionPeriod); err != nil { + log.Fatalf("Failed to filter resources with retention-period: %v", err) + } + // Update the resources if the number of items to be removed has + // changed. + if awsNuker.Items().Count(cmd.ItemStateNew) != len(resources) { + resources = awsnukeItemsToResources(awsNuker.Items()) + } + default: + resources, err = applyRetentionFilter(resources, *retentionPeriod) + if err != nil { + log.Fatalf("Failed to filter resources with retention-period: %v", err) + } } } + // Print only the result to stdout. if *jsonoutput { out, err := json.MarshalIndent(resources, "", " ") if err != nil { @@ -204,6 +245,10 @@ func main() { log.Fatalf("Failed to delete cluster: %v", err) } } + case awsnuke: + if err := awsNuker.Delete(); err != nil { + log.Fatalf("Failed to delete resources: %v", err) + } } } else if !*delete && len(resources) > 0 { // Exit with non-zero exit code when resources are found but not