From be3f5bb147ada0a0bb400ed46d3152436e8eef0b Mon Sep 17 00:00:00 2001 From: Taliesin Millhouse Date: Thu, 26 May 2022 16:10:13 +1000 Subject: [PATCH] feat: add kinesis stream support --- README.md | 41 ++++++------ aws/aws.go | 16 +++++ aws/kinesis_stream.go | 122 ++++++++++++++++++++++++++++++++++ aws/kinesis_stream_test.go | 126 ++++++++++++++++++++++++++++++++++++ aws/kinesis_stream_types.go | 38 +++++++++++ config/config.go | 1 + config/config_test.go | 1 + go.mod | 2 - go.sum | 15 +---- 9 files changed, 329 insertions(+), 33 deletions(-) create mode 100644 aws/kinesis_stream.go create mode 100644 aws/kinesis_stream_test.go create mode 100644 aws/kinesis_stream_types.go diff --git a/README.md b/README.md index bd8ccec4..7b44a61b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This repo contains a CLI tool to delete all resources in an AWS account. cloud-nuke was created for situations when you might have an account you use for testing and need to clean up leftover resources so you're not charged for them. Also great for cleaning out accounts with redundant resources. Also great for removing unnecessary defaults like default VPCs and permissive ingress/egress rules in default security groups. In addition, cloud-nuke offers non-destructive inspecting functionality that can either be called via the command-line interface, or consumed as library methods, for scripting purposes. - + The currently supported functionality includes: ## AWS @@ -43,6 +43,7 @@ The currently supported functionality includes: - Inspecting and deleting all CloudWatch Log Groups in an AWS Account - Inspecting and deleting all GuardDuty Detectors in an AWS Account - Inspecting and deleting all Macie member accounts in an AWS account - as long as those accounts were created by Invitation - and not via AWS Organizations +- Inspecting and deleting all Kinesis Streams in an AWS account ### BEWARE! @@ -81,13 +82,13 @@ When using `cloud-nuke aws`, or `cloud-nuke inspect-aws`, you can use the `--reg cloud-nuke aws --region ap-south-1 --region ap-south-2 ``` -Similarly, the following command will inspect resources only in `us-east-1` +Similarly, the following command will inspect resources only in `us-east-1` ```shell cloud-nuke inspect-aws --region us-east-1 ``` -Including regions is available within: -- `cloud-nuke aws` +Including regions is available within: +- `cloud-nuke aws` - `cloud-nuke defaults-aws` - `cloud-nuke inspect-aws` @@ -107,8 +108,8 @@ cloud-nuke inspect-aws --exclude-region us-west-1 `--region` and `--exclude-region` flags cannot be specified together i.e. they are mutually exclusive. -Excluding regions is available within: -- `cloud-nuke aws` +Excluding regions is available within: +- `cloud-nuke aws` - `cloud-nuke defaults-aws` - `cloud-nuke inspect-aws` @@ -120,8 +121,8 @@ You can use the `--older-than` flag to only nuke resources that were created bef cloud-nuke aws --older-than 24h ``` -Excluding resources by age is available within: -- `cloud-nuke aws` +Excluding resources by age is available within: +- `cloud-nuke aws` - `cloud-nuke inspect-aws` @@ -133,8 +134,8 @@ You can use the `--list-resource-types` flag to list resource types whose termin cloud-nuke aws --list-resource-types ``` -Listing supported resource types is available within: -- `cloud-nuke aws` +Listing supported resource types is available within: +- `cloud-nuke aws` - `cloud-nuke inspect-aws` @@ -151,14 +152,14 @@ will search and target only `ec2` and `ami` resources. The specified resource ty i.e. it should be present in the `--list-resource-types` output. Using `--resource-type` also speeds up search because we are searching only for specific resource types. -Similarly, the following command will inspect only ec2 instances: +Similarly, the following command will inspect only ec2 instances: ```shell cloud-nuke inspect-aws --resource-type ec2 ``` -Specifying target resource types is available within: -- `cloud-nuke aws` +Specifying target resource types is available within: +- `cloud-nuke aws` - `cloud-nuke inspect-aws` ### Exclude terminating specific resource types @@ -174,8 +175,8 @@ This will terminate all resource types other than S3 and EC2. `--resource-type` and `--exclude-resource-type` flags cannot be specified together i.e. they are mutually exclusive. -Specifying resource types to exclude is available within: -- `cloud-nuke aws` +Specifying resource types to exclude is available within: +- `cloud-nuke aws` - `cloud-nuke inspect-aws` ### Dry run mode @@ -187,14 +188,14 @@ If you want to check what resources are going to be targeted without actually te cloud-nuke aws --resource-type ec2 --dry-run ``` -Dry run mode is only available within: +Dry run mode is only available within: - `cloud-nuke aws` ### Using cloud-nuke as a library -You can import cloud-nuke into other projects and use it as a library for programmatically inspecting and counting resources. +You can import cloud-nuke into other projects and use it as a library for programmatically inspecting and counting resources. -```golang +```golang package main @@ -348,6 +349,9 @@ The following resources support the Config file: - EKS Clusters - Resource type: `ekscluster` - Config key: `EKSCluster` +- Kinesis Streams + - Resource type: `kinesis-stream` + - Config key: `KinesisStream` #### Example @@ -456,6 +460,7 @@ To find out what we options are supported in the config file today, consult this | eip | none | ✅ | none | none | | ec2 | none | ✅ | none | none | | eks | none | ✅ | none | none | +| kinesis-stream | none | ✅ | none | none | | acmpca | none | none | none | none | | iam role | none | none | none | none | | ... (more to come) | none | none | none | none | diff --git a/aws/aws.go b/aws/aws.go index c14941a0..6677ab7d 100644 --- a/aws/aws.go +++ b/aws/aws.go @@ -717,6 +717,7 @@ func GetAllResources(targetRegions []string, excludeAfter time.Time, resourceTyp } // End GuardDuty detectors + // Macie member accounts macieAccounts := MacieMember{} if IsNukeable(macieAccounts.ResourceName(), resourceTypes) { @@ -733,6 +734,20 @@ func GetAllResources(targetRegions []string, excludeAfter time.Time, resourceTyp } // End Macie member accounts + // Kinesis Streams + kinesisStreams := KinesisStreams{} + if IsNukeable(kinesisStreams.ResourceName(), resourceTypes) { + streams, err := getAllKinesisStreams(cloudNukeSession, configObj) + if err != nil { + return nil, errors.WithStackTrace(err) + } + if len(streams) > 0 { + kinesisStreams.Names = awsgo.StringValueSlice(streams) + resourcesInRegion.Resources = append(resourcesInRegion.Resources, kinesisStreams) + } + } + // End Kinesis Streams + if len(resourcesInRegion.Resources) > 0 { account.Resources[region] = resourcesInRegion } @@ -831,6 +846,7 @@ func ListResourceTypes() []string { CloudWatchLogGroups{}.ResourceName(), GuardDuty{}.ResourceName(), MacieMember{}.ResourceName(), + KinesisStreams{}.ResourceName(), } sort.Strings(resourceTypes) return resourceTypes diff --git a/aws/kinesis_stream.go b/aws/kinesis_stream.go new file mode 100644 index 00000000..a8c6c382 --- /dev/null +++ b/aws/kinesis_stream.go @@ -0,0 +1,122 @@ +package aws + +import ( + "sync" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/kinesis" + "github.com/gruntwork-io/cloud-nuke/config" + "github.com/gruntwork-io/cloud-nuke/logging" + "github.com/gruntwork-io/go-commons/errors" + "github.com/hashicorp/go-multierror" +) + +func getAllKinesisStreams(session *session.Session, configObj config.Config) ([]*string, error) { + svc := kinesis.New(session) + + allStreams := []*string{} + err := svc.ListStreamsPages( + &kinesis.ListStreamsInput{}, + func(page *kinesis.ListStreamsOutput, lastPage bool) bool { + for _, streamName := range page.StreamNames { + if shouldIncludeKinesisStream(streamName, configObj) { + allStreams = append(allStreams, streamName) + } + } + return !lastPage + }, + ) + if err != nil { + return nil, errors.WithStackTrace(err) + } + return allStreams, nil +} + +func shouldIncludeKinesisStream(streamName *string, configObj config.Config) bool { + if streamName == nil { + return false + } + + return config.ShouldInclude( + aws.StringValue(streamName), + configObj.KinesisStream.IncludeRule.NamesRegExp, + configObj.KinesisStream.ExcludeRule.NamesRegExp, + ) +} + +func nukeAllKinesisStreams(session *session.Session, identifiers []*string) error { + region := aws.StringValue(session.Config.Region) + svc := kinesis.New(session) + + if len(identifiers) == 0 { + logging.Logger.Infof("No Kinesis Streams to nuke in region: %s", region) + } + + // NOTE: we don't need to do pagination here, because the pagination is handled by the caller to this function, + // based on KinesisStream.MaxBatchSize, however we add a guard here to warn users when the batching fails and + // has a chance of throttling AWS. Since we concurrently make one call for each identifier, we pick 100 for the + // limit here because many APIs in AWS have a limit of 100 requests per second. + if len(identifiers) > 100 { + logging.Logger.Errorf("Nuking too many Kinesis Streams at once (100): halting to avoid hitting AWS API rate limiting") + return TooManyStreamsErr{} + } + + // There is no bulk delete Kinesis Stream API, so we delete the batch of Kinesis Streams concurrently + // using go routines. + logging.Logger.Infof("Deleting Kinesis Streams in region: %s", region) + wg := new(sync.WaitGroup) + wg.Add(len(identifiers)) + errChans := make([]chan error, len(identifiers)) + for i, streamName := range identifiers { + errChans[i] = make(chan error, 1) + go deleteKinesisStreamAsync(wg, errChans[i], svc, streamName, region) + } + wg.Wait() + + // Collect all the errors from the async delete calls into a single error struct. + // NOTE: We ignore OperationAbortedException which is thrown when there is an eventual consistency issue, where + // cloud-nuke picks up a Stream that is already requested to be deleted. + var allErrs *multierror.Error + for _, errChan := range errChans { + if err := <-errChan; err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() != "OperationAbortedException" { + allErrs = multierror.Append(allErrs, err) + } + } + } + finalErr := allErrs.ErrorOrNil() + if finalErr != nil { + return errors.WithStackTrace(finalErr) + } + return nil +} + +func deleteKinesisStreamAsync( + wg *sync.WaitGroup, + errChan chan error, + svc *kinesis.Kinesis, + streamName *string, + region string, +) { + defer wg.Done() + input := &kinesis.DeleteStreamInput{StreamName: streamName} + _, err := svc.DeleteStream(input) + errChan <- err + + streamNameStr := aws.StringValue(streamName) + if err == nil { + logging.Logger.Infof("[OK] Kinesis Stream %s delete in %s", streamNameStr, region) + } else { + logging.Logger.Errorf("[Failed] Error deleting Kinesis Stream %s in %s: %s", streamNameStr, region, err) + } +} + +// Custom errors + +type TooManyStreamsErr struct{} + +func (err TooManyStreamsErr) Error() string { + return "Too many Streams requested at once." +} diff --git a/aws/kinesis_stream_test.go b/aws/kinesis_stream_test.go new file mode 100644 index 00000000..c6bdcab6 --- /dev/null +++ b/aws/kinesis_stream_test.go @@ -0,0 +1,126 @@ +package aws + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/kinesis" + "github.com/gruntwork-io/cloud-nuke/config" + "github.com/gruntwork-io/cloud-nuke/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestListKinesisStreams(t *testing.T) { + t.Parallel() + + region, err := getRandomRegion() + require.NoError(t, err) + + session, err := session.NewSession(&aws.Config{Region: aws.String(region)}) + require.NoError(t, err) + svc := kinesis.New(session) + + sName := createKinesisStream(t, svc) + defer deleteKinesisStream(t, svc, sName, true) + + sNames, err := getAllKinesisStreams(session, config.Config{}) + require.NoError(t, err) + assert.Contains(t, aws.StringValueSlice(sNames), aws.StringValue(sName)) +} + +func TestNukeKinesisStreamOne(t *testing.T) { + t.Parallel() + + region, err := getRandomRegion() + require.NoError(t, err) + + session, err := session.NewSession(&aws.Config{Region: aws.String(region)}) + require.NoError(t, err) + svc := kinesis.New(session) + + // We ignore errors in the delete call here, because it is intended to be a stop gap in case there is a bug in nuke. + sName := createKinesisStream(t, svc) + defer deleteKinesisStream(t, svc, sName, true) + identifiers := []*string{sName} + + require.NoError( + t, + nukeAllKinesisStreams(session, identifiers), + ) + + assertKinesisStreamsDeleted(t, svc, identifiers) +} + +func TestNukeKinesisStreamMoreThanOne(t *testing.T) { + t.Parallel() + + region, err := getRandomRegion() + require.NoError(t, err) + + session, err := session.NewSession(&aws.Config{Region: aws.String(region)}) + require.NoError(t, err) + svc := kinesis.New(session) + + sNames := []*string{} + for i := 0; i < 3; i++ { + // We ignore errors in the delete call here, because it is intended to be a stop gap in case there is a bug in nuke. + sName := createKinesisStream(t, svc) + defer deleteKinesisStream(t, svc, sName, true) + sNames = append(sNames, sName) + } + + require.NoError( + t, + nukeAllKinesisStreams(session, sNames), + ) + + assertKinesisStreamsDeleted(t, svc, sNames) +} + +func createKinesisStream(t *testing.T, svc *kinesis.Kinesis) *string { + uniqueID := util.UniqueID() + name := fmt.Sprintf("cloud-nuke-test-%s", strings.ToLower(uniqueID)) + + _, err := svc.CreateStream(&kinesis.CreateStreamInput{ + ShardCount: aws.Int64(1), + StreamName: aws.String(name), + }) + require.NoError(t, err) + + // Add an arbitrary sleep to account for eventual consistency + time.Sleep(15 * time.Second) + return &name +} + +func deleteKinesisStream(t *testing.T, svc *kinesis.Kinesis, name *string, checkErr bool) { + _, err := svc.DeleteStream(&kinesis.DeleteStreamInput{ + StreamName: name, + }) + if checkErr { + require.NoError(t, err) + } +} + +func assertKinesisStreamsDeleted(t *testing.T, svc *kinesis.Kinesis, identifiers []*string) { + for _, name := range identifiers { + stream, err := svc.DescribeStream(&kinesis.DescribeStreamInput{ + StreamName: name, + }) + + // There is an error returned, assert it's because the Stream cannot be found because it's + // been deleted. Otherwise assert that the stream status is DELETING. + if err != nil { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() != "ResourceNotFoundException" { + t.Fatalf("Stream %s is not deleted", aws.StringValue(name)) + } + } else { + require.Equal(t, "DELETING", *stream.StreamDescription.StreamStatus) + } + } +} diff --git a/aws/kinesis_stream_types.go b/aws/kinesis_stream_types.go new file mode 100644 index 00000000..7c61946a --- /dev/null +++ b/aws/kinesis_stream_types.go @@ -0,0 +1,38 @@ +package aws + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/gruntwork-io/go-commons/errors" +) + +// KinesisStreams - represents all Kinesis streams +type KinesisStreams struct { + Names []string +} + +// ResourceName - The simple name of the AWS resource +func (k KinesisStreams) ResourceName() string { + return "kinesis-stream" +} + +// ResourceIdentifiers - The names of the Kinesis Streams +func (k KinesisStreams) ResourceIdentifiers() []string { + return k.Names +} + +func (k KinesisStreams) MaxBatchSize() int { + // Tentative batch size to ensure AWS doesn't throttle. Note that Kinesis Streams does not support bulk delete, so + // we will be deleting this many in parallel using go routines. We pick 35 here, which is half of what the AWS web + // console will do. We pick a conservative number here to avoid hitting AWS API rate limits. + return 35 +} + +// Nuke - nuke 'em all!!! +func (k KinesisStreams) Nuke(session *session.Session, identifiers []string) error { + if err := nukeAllKinesisStreams(session, aws.StringSlice(identifiers)); err != nil { + return errors.WithStackTrace(err) + } + + return nil +} diff --git a/config/config.go b/config/config.go index a52d77c0..3ce9c55f 100644 --- a/config/config.go +++ b/config/config.go @@ -33,6 +33,7 @@ type Config struct { CloudWatchLogGroup ResourceType `yaml:"CloudWatchLogGroup"` KMSCustomerKeys ResourceType `yaml:"KMSCustomerKeys"` EKSCluster ResourceType `yaml:"EKSCluster"` + KinesisStream ResourceType `yaml:"KinesisStream"` } type ResourceType struct { diff --git a/config/config_test.go b/config/config_test.go index c6927b27..321f92a1 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -34,6 +34,7 @@ func emptyConfig() *Config { ResourceType{FilterRule{}, FilterRule{}}, ResourceType{FilterRule{}, FilterRule{}}, ResourceType{FilterRule{}, FilterRule{}}, + ResourceType{FilterRule{}, FilterRule{}}, } } diff --git a/go.mod b/go.mod index eb4e031d..517f75ee 100644 --- a/go.mod +++ b/go.mod @@ -12,11 +12,9 @@ require ( github.com/hashicorp/go-multierror v1.1.0 github.com/pquerna/otp v1.3.0 github.com/sirupsen/logrus v1.6.0 - github.com/stretchr/objx v0.4.0 // indirect github.com/stretchr/testify v1.7.1 github.com/urfave/cli v1.22.4 golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 - golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect gopkg.in/yaml.v2 v2.2.8 gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index bcbc7ee7..56b30443 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,6 @@ github.com/aws/aws-sdk-go v1.27.1/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN github.com/aws/aws-sdk-go v1.30.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.34.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.38.28/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.42.4 h1:L3gadqlmmdWCDE7aD52l3A5TKVG9jPBHZG1/65x9GVw= -github.com/aws/aws-sdk-go v1.42.4/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go v1.44.46 h1:BsKENvu24eXg7CWQ2wJAjKbDFkGP+hBtxKJIR3UdcB8= github.com/aws/aws-sdk-go v1.44.46/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -404,14 +402,11 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -514,8 +509,6 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -568,23 +561,20 @@ golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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= @@ -689,7 +679,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=