diff --git a/cloud/aws/dynamo/config.go b/cloud/aws/dynamo/config.go index 3f52af2..1ff3bb0 100644 --- a/cloud/aws/dynamo/config.go +++ b/cloud/aws/dynamo/config.go @@ -1,11 +1,12 @@ package dynamo import ( + "context" "fmt" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/dynamodb" - "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" + + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" "github.com/pkg/errors" ) @@ -15,19 +16,21 @@ const keyColumnName = "key" type Config struct{} func (c Config) Query(key string) (interface{}, error) { - sess := session.Must(session.NewSessionWithOptions(session.Options{ - SharedConfigState: session.SharedConfigEnable, - })) - connection := dynamodb.New(sess) + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + return nil, errors.Wrap(err, "could not load aws config") + } + connection := dynamodb.NewFromConfig(cfg) - dbKey, err := dynamodbattribute.MarshalMap(map[string]interface{}{keyColumnName: key}) + dbKey, err := attributevalue.MarshalMap(map[string]interface{}{keyColumnName: key}) if err != nil { return nil, errors.Wrap(err, "could not marshal key for dynamo config table") } - item, err := connection.GetItem(&dynamodb.GetItemInput{ + table := tableName + item, err := connection.GetItem(context.TODO(), &dynamodb.GetItemInput{ Key: dbKey, - TableName: aws.String(tableName), + TableName: &table, }) if err != nil { return nil, errors.Wrap(err, "could query dynamo config table") @@ -38,7 +41,7 @@ func (c Config) Query(key string) (interface{}, error) { } var value interface{} - err = dynamodbattribute.Unmarshal(item.Item["value"], &value) + err = attributevalue.Unmarshal(item.Item["value"], &value) return value, errors.Wrap(err, "Could not unmarshal dynamo config value") } diff --git a/cloud/aws/dynamo/dynamo.go b/cloud/aws/dynamo/dynamo.go index 1633106..c301650 100644 --- a/cloud/aws/dynamo/dynamo.go +++ b/cloud/aws/dynamo/dynamo.go @@ -1,13 +1,12 @@ package dynamo import ( + "context" "fmt" "github.com/Direct-Debit/go-commons/cloud" "github.com/Direct-Debit/go-commons/concurrency" "github.com/Direct-Debit/go-commons/stdext" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute" - "github.com/pkg/errors" + "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" "math" "math/rand" "strings" @@ -15,34 +14,36 @@ import ( "time" "github.com/Direct-Debit/go-commons/errlib" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + dynamotypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" log "github.com/sirupsen/logrus" ) -var connection *dynamodb.DynamoDB +var connection *dynamodb.Client -type Item map[string]*dynamodb.AttributeValue +type Item map[string]dynamotypes.AttributeValue -func Connect() *dynamodb.DynamoDB { - if connection != (*dynamodb.DynamoDB)(nil) { +func Connect() *dynamodb.Client { + if connection != nil { return connection } - sess := session.Must(session.NewSessionWithOptions(session.Options{ - SharedConfigState: session.SharedConfigEnable, - })) - connection = dynamodb.New(sess) + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + connection = dynamodb.NewFromConfig(cfg) return connection } func TableExists(tableName *string) bool { db := Connect() - descOutput, err := db.DescribeTable(&dynamodb.DescribeTableInput{TableName: tableName}) + descOutput, err := db.DescribeTable(context.TODO(), &dynamodb.DescribeTableInput{TableName: tableName}) if err != nil { return false } - return strings.ToLower(*descOutput.Table.TableStatus) == "active" + return strings.ToLower(string(descOutput.Table.TableStatus)) == "active" } func PutItems(items []Item, table *string, delete bool) { @@ -54,15 +55,15 @@ func PutItems(items []Item, table *string, delete bool) { } submitItems := items[i:max] - writeRequests := make([]*dynamodb.WriteRequest, len(submitItems)) + writeRequests := make([]dynamotypes.WriteRequest, len(submitItems)) for idx, item := range submitItems { if delete { - writeRequests[idx] = &dynamodb.WriteRequest{ - DeleteRequest: &dynamodb.DeleteRequest{Key: item}, + writeRequests[idx] = dynamotypes.WriteRequest{ + DeleteRequest: &dynamotypes.DeleteRequest{Key: item}, } } else { - writeRequests[idx] = &dynamodb.WriteRequest{ - PutRequest: &dynamodb.PutRequest{Item: item}, + writeRequests[idx] = dynamotypes.WriteRequest{ + PutRequest: &dynamotypes.PutRequest{Item: item}, } } } @@ -72,21 +73,21 @@ func PutItems(items []Item, table *string, delete bool) { go func() { defer wg.Done() putItemsWithBackoff( - map[string][]*dynamodb.WriteRequest{*table: writeRequests}, + map[string][]dynamotypes.WriteRequest{*table: writeRequests}, 1) }() } wg.Wait() } -func putItemsWithBackoff(items map[string][]*dynamodb.WriteRequest, backoff int) { +func putItemsWithBackoff(items map[string][]dynamotypes.WriteRequest, backoff int) { db := Connect() if backoff < 1 { backoff = 1 } - out, err := db.BatchWriteItem(&dynamodb.BatchWriteItemInput{ + out, err := db.BatchWriteItem(context.TODO(), &dynamodb.BatchWriteItemInput{ RequestItems: items, }) errlib.PanicError(err, "Couldn't write batch") @@ -126,9 +127,61 @@ func GetItemsLambda(keys []Item, table *string, batchSize int) []Item { return res } +// toV1AttributeValue converts a V2 dynamotypes.AttributeValue into the JSON +// shape produced by the old aws-sdk-go v1 dynamodb.AttributeValue struct +// (e.g. {"S": "foo"}, {"N": "123"}, {"M": {...}}), since the cmn-dynamo-get-items +// lambda still expects that wire format and cannot be changed. +func toV1AttributeValue(av dynamotypes.AttributeValue) map[string]interface{} { + switch v := av.(type) { + case *dynamotypes.AttributeValueMemberS: + return map[string]interface{}{"S": v.Value} + case *dynamotypes.AttributeValueMemberN: + return map[string]interface{}{"N": v.Value} + case *dynamotypes.AttributeValueMemberB: + return map[string]interface{}{"B": v.Value} + case *dynamotypes.AttributeValueMemberSS: + return map[string]interface{}{"SS": v.Value} + case *dynamotypes.AttributeValueMemberNS: + return map[string]interface{}{"NS": v.Value} + case *dynamotypes.AttributeValueMemberBS: + return map[string]interface{}{"BS": v.Value} + case *dynamotypes.AttributeValueMemberBOOL: + return map[string]interface{}{"BOOL": v.Value} + case *dynamotypes.AttributeValueMemberNULL: + return map[string]interface{}{"NULL": v.Value} + case *dynamotypes.AttributeValueMemberL: + list := make([]interface{}, len(v.Value)) + for i, elem := range v.Value { + list[i] = toV1AttributeValue(elem) + } + return map[string]interface{}{"L": list} + case *dynamotypes.AttributeValueMemberM: + m := make(map[string]interface{}, len(v.Value)) + for key, elem := range v.Value { + m[key] = toV1AttributeValue(elem) + } + return map[string]interface{}{"M": m} + default: + return nil + } +} + +func toV1Item(item Item) map[string]interface{} { + v1Item := make(map[string]interface{}, len(item)) + for key, av := range item { + v1Item[key] = toV1AttributeValue(av) + } + return v1Item +} + func invokeGetItemsLambda(items []Item, table *string, output chan []Item) { + v1Items := make([]map[string]interface{}, len(items)) + for i, item := range items { + v1Items[i] = toV1Item(item) + } + input := map[string]interface{}{ - "items": items, + "items": v1Items, "table": *table, } result, err := cloud.CallFunc("cmn-dynamo-get-items", input) @@ -136,7 +189,7 @@ func invokeGetItemsLambda(items []Item, table *string, output chan []Item) { var resp []Item for _, item := range result["items"].([]interface{}) { - dynamoItem, err := dynamodbattribute.MarshalMap(item) + dynamoItem, err := attributevalue.MarshalMap(item) if errlib.ErrorError(err, "failed to marshal attribute values") { continue } @@ -153,18 +206,18 @@ func GetItems(items []Item, table *string) []Item { max = len(items) } - getItems := make([]map[string]*dynamodb.AttributeValue, 0, max-i) + getItems := make([]map[string]dynamotypes.AttributeValue, 0, max-i) for item := i; item < max; item++ { getItems = append(getItems, items[item]) } - keysAndAttr := &dynamodb.KeysAndAttributes{ + keysAndAttr := dynamotypes.KeysAndAttributes{ Keys: getItems, } log.Debugf("Getting %d items from dynamo table %s", len(getItems), *table) subRes := getItemsWithBackoff( - map[string]*dynamodb.KeysAndAttributes{*table: keysAndAttr}, 1, + map[string]dynamotypes.KeysAndAttributes{*table: keysAndAttr}, 1, ) res = append(res, subRes[*table]...) @@ -183,17 +236,17 @@ func GetItemsConcurrent(workers int, items []Item, table *string) []Item { }() keys := stdext.Map(chunk, - func(item Item) map[string]*dynamodb.AttributeValue { + func(item Item) map[string]dynamotypes.AttributeValue { return item }, ) - keysAndAttr := &dynamodb.KeysAndAttributes{ + keysAndAttr := dynamotypes.KeysAndAttributes{ Keys: keys, } log.Debugf("Getting %d items from dynamo table %s", len(chunk), *table) itemsReturned := getItemsWithBackoff( - map[string]*dynamodb.KeysAndAttributes{*table: keysAndAttr}, 1, + map[string]dynamotypes.KeysAndAttributes{*table: keysAndAttr}, 1, ) ret, success = itemsReturned[*table], true return @@ -202,14 +255,14 @@ func GetItemsConcurrent(workers int, items []Item, table *string) []Item { return stdext.Flatten(returnedChunks) } -func getItemsWithBackoff(items map[string]*dynamodb.KeysAndAttributes, backoff int) map[string][]Item { +func getItemsWithBackoff(items map[string]dynamotypes.KeysAndAttributes, backoff int) map[string][]Item { db := Connect() if backoff < 1 { backoff = 1 } - out, err := db.BatchGetItem(&dynamodb.BatchGetItemInput{ + out, err := db.BatchGetItem(context.TODO(), &dynamodb.BatchGetItemInput{ RequestItems: items, }) errlib.PanicError(err, "Couldn't get batch") @@ -243,7 +296,7 @@ func QueryAll(initialInput *dynamodb.QueryInput) ([]Item, error) { queryDone := false db := Connect() for !queryDone { - result, err := db.Query(initialInput) + result, err := db.Query(context.TODO(), initialInput) if err != nil { return res, err } @@ -264,21 +317,18 @@ type CountOutput struct { } func CountAll(initialInput *dynamodb.QueryInput) (CountOutput, error) { - initialInput.Select = aws.String(dynamodb.SelectCount) + initialInput.Select = dynamotypes.SelectCount res := CountOutput{} queryDone := false db := Connect() for !queryDone { - result, err := db.Query(initialInput) + result, err := db.Query(context.TODO(), initialInput) if err != nil { return res, err } - if result.Count == (*int64)(nil) || result.ScannedCount == (*int64)(nil) { - return CountOutput{}, errors.New("count or scanned count is nil") - } - res.Count += *result.Count - res.ScannedCount += *result.ScannedCount + res.Count += int64(result.Count) + res.ScannedCount += int64(result.ScannedCount) queryDone = len(result.LastEvaluatedKey) == 0 initialInput.ExclusiveStartKey = result.LastEvaluatedKey @@ -293,7 +343,7 @@ func ScanAll(initialInput *dynamodb.ScanInput) ([]Item, error) { scanDone := false db := Connect() for !scanDone { - result, err := db.Scan(initialInput) + result, err := db.Scan(context.TODO(), initialInput) if err != nil { return res, err } diff --git a/cloud/aws/lambda/lambda.go b/cloud/aws/lambda/lambda.go index b1a0ae8..eccc5e0 100644 --- a/cloud/aws/lambda/lambda.go +++ b/cloud/aws/lambda/lambda.go @@ -5,46 +5,30 @@ import ( "encoding/json" "github.com/Direct-Debit/go-commons/errlib" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/lambda" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/lambda" + lambdatypes "github.com/aws/aws-sdk-go-v2/service/lambda/types" log "github.com/sirupsen/logrus" ) type Client struct { - client *lambda.Lambda + client *lambda.Client } func NewClient() Client { log.Trace("Setting up lambda client") - sess := session.Must(session.NewSessionWithOptions(session.Options{ - SharedConfigState: session.SharedConfigEnable, - })) + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } return Client{ - client: lambda.New(sess), + client: lambda.NewFromConfig(cfg), } } func (l Client) General(fName string, in interface{}) (out map[string]interface{}, err error) { - payload, err := json.Marshal(in) - if err != nil { - return nil, err - } - - log.Infof("Invoking %s", fName) - response, err := l.client.Invoke(&lambda.InvokeInput{ - FunctionName: &fName, - Payload: payload, - }) - if err != nil { - return nil, err - } - - var result map[string]interface{} - log.Tracef("%s response: %s", fName, string(response.Payload)) - err = json.Unmarshal(response.Payload, &result) - return result, err + return l.GeneralWithContext(context.TODO(), fName, in) } func (l Client) GeneralWithContext(ctx context.Context, fName string, in interface{}) (out map[string]interface{}, err error) { @@ -54,7 +38,7 @@ func (l Client) GeneralWithContext(ctx context.Context, fName string, in interfa } log.Infof("Invoking %s", fName) - response, err := l.client.InvokeWithContext(ctx, &lambda.InvokeInput{ + response, err := l.client.Invoke(ctx, &lambda.InvokeInput{ FunctionName: &fName, Payload: payload, }) @@ -75,10 +59,10 @@ func (l Client) GeneralAsync(fName string, in interface{}) (err error) { } log.Infof("Invoking %s asynchronously", fName) - _, err = l.client.Invoke(&lambda.InvokeInput{ + _, err = l.client.Invoke(context.TODO(), &lambda.InvokeInput{ FunctionName: &fName, Payload: payload, - InvocationType: aws.String(lambda.InvocationTypeEvent), + InvocationType: lambdatypes.InvocationTypeEvent, }) return } diff --git a/cloud/aws/sns/sns.go b/cloud/aws/sns/sns.go index ba7ee73..26d322a 100644 --- a/cloud/aws/sns/sns.go +++ b/cloud/aws/sns/sns.go @@ -1,42 +1,48 @@ package sns import ( - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/sns" + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/sns" + snstypes "github.com/aws/aws-sdk-go-v2/service/sns/types" log "github.com/sirupsen/logrus" ) type Client struct { - snsClient *sns.SNS + snsClient *sns.Client Topic string Environment string // dev, stage, prod etc... } func NewClient(topic string, env string) Client { log.Trace("Setting up sns client") - sess := session.Must(session.NewSessionWithOptions(session.Options{ - SharedConfigState: session.SharedConfigEnable, - })) + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } return Client{ - snsClient: sns.New(sess), + snsClient: sns.NewFromConfig(cfg), Topic: topic, Environment: env, } } -func (s Client) attributes() map[string]*sns.MessageAttributeValue { - var env sns.MessageAttributeValue - env.SetStringValue(s.Environment) - env.SetDataType("String") - - return map[string]*sns.MessageAttributeValue{"env": &env} +func (s Client) attributes() map[string]snstypes.MessageAttributeValue { + return map[string]snstypes.MessageAttributeValue{ + "env": { + DataType: aws.String("String"), + StringValue: aws.String(s.Environment), + }, + } } func (s Client) Publish(subject string, message string) error { log.Debugf("Publishing %s to %s", subject, s.Topic) - _, err := s.snsClient.Publish(&sns.PublishInput{ + _, err := s.snsClient.Publish(context.TODO(), &sns.PublishInput{ Message: &message, MessageAttributes: s.attributes(), Subject: &subject, diff --git a/cloud/aws/sqs/sqs.go b/cloud/aws/sqs/sqs.go index 2839edd..0e8e930 100644 --- a/cloud/aws/sqs/sqs.go +++ b/cloud/aws/sqs/sqs.go @@ -1,32 +1,35 @@ package sqs import ( + "context" "fmt" "strings" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/sqs" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/sqs" + sqstypes "github.com/aws/aws-sdk-go-v2/service/sqs/types" "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) type Client struct { - sqsClient *sqs.SQS + sqsClient *sqs.Client Queues map[string]*string Environment string // dev, stage, prod etc... } -type Attributes map[string]*sqs.MessageAttributeValue +type Attributes map[string]sqstypes.MessageAttributeValue func NewClient(env string) Client { log.Trace("Setting up sqs client") - sess := session.Must(session.NewSessionWithOptions(session.Options{ - SharedConfigState: session.SharedConfigEnable, - })) + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } return Client{ - sqsClient: sqs.New(sess), + sqsClient: sqs.NewFromConfig(cfg), Queues: make(map[string]*string), Environment: env, } @@ -36,7 +39,7 @@ func (c Client) getQueueURL(queue string) (*string, error) { if queueUrl, ok := c.Queues[queue]; ok { return queueUrl, nil } else { - result, err := c.sqsClient.GetQueueUrl(&sqs.GetQueueUrlInput{ + result, err := c.sqsClient.GetQueueUrl(context.TODO(), &sqs.GetQueueUrlInput{ QueueName: &queue, }) if err != nil { @@ -62,15 +65,15 @@ func (c Client) SendMessage(queue string, message string, delay int, attr Attrib attr = make(Attributes) } - attr["Env"] = &sqs.MessageAttributeValue{ + attr["Env"] = sqstypes.MessageAttributeValue{ DataType: aws.String("String"), StringValue: aws.String(c.Environment), } - _, err = c.sqsClient.SendMessage(&sqs.SendMessageInput{ + _, err = c.sqsClient.SendMessage(context.TODO(), &sqs.SendMessageInput{ MessageAttributes: attr, MessageBody: aws.String(message), - DelaySeconds: aws.Int64(int64(delay)), + DelaySeconds: int32(delay), QueueUrl: queueUrl, }) return errors.Wrapf(err, "failed to send message to SQS queue %v", queue) @@ -82,14 +85,14 @@ func (c Client) DeleteMessage(queue string, receiptHandle string) error { return errors.Wrapf(err, "failed to get SQS queue url for %v", queue) } - _, err = c.sqsClient.DeleteMessage(&sqs.DeleteMessageInput{ + _, err = c.sqsClient.DeleteMessage(context.TODO(), &sqs.DeleteMessageInput{ ReceiptHandle: &receiptHandle, QueueUrl: queueUrl, }) return errors.Wrapf(err, "failed to delete message from %v", queue) } -func (c Client) Listen(queue string, waitTime int, msgs chan *sqs.Message) error { +func (c Client) Listen(queue string, waitTime int, msgs chan sqstypes.Message) error { queueUrl, err := c.getQueueURL(queue) if err != nil { return errors.Wrapf(err, "failed to get SQS queue url for %v", queue) @@ -102,10 +105,10 @@ func (c Client) Listen(queue string, waitTime int, msgs chan *sqs.Message) error } for { - output, err := c.sqsClient.ReceiveMessage(&sqs.ReceiveMessageInput{ - MaxNumberOfMessages: aws.Int64(int64(10)), + output, err := c.sqsClient.ReceiveMessage(context.TODO(), &sqs.ReceiveMessageInput{ + MaxNumberOfMessages: int32(10), QueueUrl: queueUrl, - WaitTimeSeconds: aws.Int64(int64(waitTime)), + WaitTimeSeconds: int32(waitTime), }) if err != nil { diff --git a/fileio/s3-store.go b/fileio/s3-store.go index 513dca1..8d1168e 100644 --- a/fileio/s3-store.go +++ b/fileio/s3-store.go @@ -2,6 +2,7 @@ package fileio import ( "bytes" + "context" "fmt" "io" "strings" @@ -9,14 +10,15 @@ import ( "github.com/Direct-Debit/go-commons/errlib" "github.com/Direct-Debit/go-commons/stdext" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/s3" + s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" log "github.com/sirupsen/logrus" ) type S3Store struct { - s3 *s3.S3 // AWS S3 client + s3 *s3.Client // AWS S3 client Bucket *string // Name of the S3 bucket PresignDuration time.Duration // Duration for which the presigned URL is valid } @@ -25,10 +27,11 @@ type S3Store struct { // It initializes the AWS session and S3 client. // The PresignDuration is set to 24 hours by default. func NewS3Store(bucket string) S3Store { - sess := session.Must(session.NewSessionWithOptions(session.Options{ - SharedConfigState: session.SharedConfigEnable, - })) - return S3Store{s3: s3.New(sess), Bucket: &bucket, PresignDuration: 24 * time.Hour} + cfg, err := config.LoadDefaultConfig(context.TODO()) + if err != nil { + panic(err) + } + return S3Store{s3: s3.NewFromConfig(cfg), Bucket: &bucket, PresignDuration: 24 * time.Hour} } func (s S3Store) Save(path string, content string) error { @@ -36,7 +39,7 @@ func (s S3Store) Save(path string, content string) error { } func (s S3Store) SaveStream(path string, content io.ReadSeeker) error { - _, err := s.s3.PutObject(&s3.PutObjectInput{ + _, err := s.s3.PutObject(context.TODO(), &s3.PutObjectInput{ Body: content, Bucket: s.Bucket, Key: aws.String(path), @@ -49,7 +52,7 @@ func (s S3Store) SaveStream(path string, content io.ReadSeeker) error { func (s S3Store) Load(path string) (content string, err error) { log.Trace(fmt.Sprintf("Downloading s3://%s/%s", *s.Bucket, path)) - output, err := s.s3.GetObject(&s3.GetObjectInput{ + output, err := s.s3.GetObject(context.TODO(), &s3.GetObjectInput{ Bucket: s.Bucket, Key: &path, }) @@ -80,7 +83,7 @@ func (s S3Store) Move(path string, targetDir string) error { return nil } - _, err := s.s3.CopyObject(&s3.CopyObjectInput{ + _, err := s.s3.CopyObject(context.TODO(), &s3.CopyObjectInput{ Bucket: s.Bucket, CopySource: aws.String(*s.Bucket + "/" + path), Key: aws.String(targetDir + name), @@ -93,7 +96,7 @@ func (s S3Store) Move(path string, targetDir string) error { } func (s S3Store) Delete(path string) error { - _, err := s.s3.DeleteObject(&s3.DeleteObjectInput{ + _, err := s.s3.DeleteObject(context.TODO(), &s3.DeleteObjectInput{ Bucket: s.Bucket, Key: &path, }) @@ -107,15 +110,14 @@ func (s S3Store) List(path string) (subPaths []FileInfo, err error) { Prefix: &path, } - var content []*s3.Object - err = s.s3.ListObjectsV2Pages(params, - func(page *s3.ListObjectsV2Output, lastPage bool) bool { - content = append(content, page.Contents...) - return true - }, - ) - if err != nil { - return nil, err + var content []s3types.Object + paginator := s3.NewListObjectsV2Paginator(s.s3, params) + for paginator.HasMorePages() { + page, err := paginator.NextPage(context.TODO()) + if err != nil { + return nil, err + } + content = append(content, page.Contents...) } subPaths = make([]FileInfo, 0, len(content)) @@ -136,7 +138,7 @@ func (s S3Store) List(path string) (subPaths []FileInfo, err error) { } func (s S3Store) GetInfo(path string) (info FileInfo, err error) { - output, err := s.s3.HeadObject(&s3.HeadObjectInput{ + output, err := s.s3.HeadObject(context.TODO(), &s3.HeadObjectInput{ Bucket: s.Bucket, Key: &path, }) @@ -164,14 +166,14 @@ func (s S3Store) Split(path string) (directory string, filename string) { } func (s S3Store) GenerateDownloadLink(filePath string) (string, error) { - req, _ := s.s3.GetObjectRequest(&s3.GetObjectInput{ + presignClient := s3.NewPresignClient(s.s3) + req, err := presignClient.PresignGetObject(context.TODO(), &s3.GetObjectInput{ Bucket: s.Bucket, Key: &filePath, - }) - - if req.Error != nil { - return "", stdext.WrapError(req.Error, "Couldn't create S3 presigned URL") + }, s3.WithPresignExpires(s.PresignDuration)) + if err != nil { + return "", stdext.WrapError(err, "Couldn't create S3 presigned URL") } - return req.Presign(s.PresignDuration) + return req.URL, nil } diff --git a/go.mod b/go.mod index eeca74d..618d677 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,14 @@ toolchain go1.24.12 require ( github.com/PagerDuty/go-pagerduty v1.4.0 - github.com/aws/aws-sdk-go v1.36.23 + github.com/aws/aws-sdk-go-v2 v1.42.1 + github.com/aws/aws-sdk-go-v2/config v1.32.29 + github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.20.51 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.60.0 + github.com/aws/aws-sdk-go-v2/service/lambda v1.97.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0 + github.com/aws/aws-sdk-go-v2/service/sns v1.41.0 + github.com/aws/aws-sdk-go-v2/service/sqs v1.45.0 github.com/labstack/echo/v4 v4.9.0 github.com/pelletier/go-toml v1.8.1 github.com/pkg/errors v0.9.1 @@ -18,9 +25,25 @@ require ( ) require ( + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.19.28 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31 // indirect + github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.35.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.23 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.12.7 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.4.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.32.0 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.37.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.44.0 // indirect + github.com/aws/smithy-go v1.27.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/go-querystring v1.0.0 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/labstack/gommon v0.3.1 // indirect github.com/mattn/go-colorable v0.1.11 // indirect diff --git a/go.sum b/go.sum index c54c3be..c75baff 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,56 @@ github.com/PagerDuty/go-pagerduty v1.4.0 h1:1ju+FZt47dLsm9iDU6eMymbr1TeqHmpSRU27pJ+LwQ4= github.com/PagerDuty/go-pagerduty v1.4.0/go.mod h1:W5hSIIPrzSgAkNBDiuymWN5g9yQVzimL7BUBL44f3RY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go v1.36.23 h1:umM44ptMKImsUWLtjGBv/4Ut7Nd99DfqoZDkO0j0/Kc= -github.com/aws/aws-sdk-go v1.36.23/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go-v2 v1.42.1 h1:9eOTgu1z/dVtYpNZ3/8/XbbaX0x/BqE3HUzAzs6K0ek= +github.com/aws/aws-sdk-go-v2 v1.42.1/go.mod h1:5pKeft2eJj+gElQ38Jqg4ibCqh+/AK33/0X3hip7IjM= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14 h1:3IZY0XAJquT3aHzbkHfPzy4ACPcEjVG0x87KOwtpqGY= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14/go.mod h1:zwM6veDkhGgQFqkBy+uT28AAYpLu+uFMlPl+rCg/73E= +github.com/aws/aws-sdk-go-v2/config v1.32.29 h1:BcMHHnpiWKogf+gGfpj3K1w+Sktz29XDo/cPSAPO3FU= +github.com/aws/aws-sdk-go-v2/config v1.32.29/go.mod h1:+Kbhn8Es4kPUph3F/0W7avykytc+Jh2Ld9/msv9ljV4= +github.com/aws/aws-sdk-go-v2/credentials v1.19.28 h1:zTXJSsNcoO91/mTXsZoYf0AK8dvNPiA58/VtyGXR+wM= +github.com/aws/aws-sdk-go-v2/credentials v1.19.28/go.mod h1:Kd9E0JzDBW/q1xbsHFrev/GnbAf5J0Ng8xoyc7HZ91Q= +github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.20.51 h1:pVJCWZVlf77zOEuxPnR69pLX97Se+d2zPIQoGnbBFJo= +github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.20.51/go.mod h1:OiabLK2FGV8RWdXw7eZzfyHkAsI9KaFSYg5OwyDQju8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30 h1:/hi1JADLEW9YYryEz1w4GQu0EtP23pP553Cf9KgsDV4= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30/go.mod h1:/3AOgy4K17Dm4ucMZVC/MJkzy5kmfKUcINRHZyo0koQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30 h1:xM/Is9cKMHa8Jj8zkvWhvrFkZsXJV9E+BB4g0HW0duQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30/go.mod h1:WueJeNDZvK1fMYEWJIkcivBfEzUkTpBhzlrUKKY8EuA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30 h1:jn46zC9LdsVR/ZpMIJqMqb8hHv31BlLx3ulVqNspUOk= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.30/go.mod h1:1hTMsAgbdS/AtUi4bw8+gUuh1pceo+eXRLfpSuSQj3M= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31 h1:3GUprIsfmGcC5SACIyB0e7E0BM1O1b3Erl5CePYIAeQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.31/go.mod h1:7PuV1yl5e2xnUbm+RqvVg5i2iBM8EyijZNoI9wsOoOc= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.60.0 h1:wnyC01GGkqvRvVI73+xRTr8qDyIZMzqjHlsFqWUUVSo= +github.com/aws/aws-sdk-go-v2/service/dynamodb v1.60.0/go.mod h1:HnWoC3m6VmjUSg+kBL6OgQsXdyRAGzBYWb7B3J2f+JM= +github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.35.0 h1:v6och6VKfZ/N8Kr5EyWKIu1JOfqjv4xpDAAyCxEuLlw= +github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.35.0/go.mod h1:2hgy3N0+CUZxbEKTNCTDWLevN+5pV7lY3B7hMl+oEQ4= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13 h1:mbRIur/BiHK6SKPjoBIXSE/hJ6g6JGRLuxQy1jGjlN4= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13/go.mod h1:ITg9em2KbJx1s0y4aqRX5OYWG6HBZ5TVR//OdpEZ2CQ= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.23 h1:9Fjh6fi/U5JEStVZijmaMpUwE/gvBJj7x2B/PjbO9To= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.23/go.mod h1:iMoT2f1tClxrWAAnKCXjZQ6LOmfLrMG14wmnWpM+F14= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.12.7 h1:uqsKxr7kJp9DXVj2m8KbVeZcYMuwsNEwvoVrYl2Vpf8= +github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.12.7/go.mod h1:Js/P8Zbwe1mRejnD+OpFLyQiJ8ioQlo3GMAg7Dfxk7w= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30 h1:/Z5jmNrKsSD7EmDjzAPsm/3L9IuOkzaynklJZ1qX7S4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30/go.mod h1:lEzEZnOosE7zi8Z6royW1cFJTD9fpab4Ul1SBrllewk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31 h1:uao4A3QZ5UmB326V6KF+qRpv9Tjz7IlnlnTbbANntlU= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31/go.mod h1:I/1+z0VwL1GhQyLgkoHDlygpUZ+iTAwOQ/NsftiUL2I= +github.com/aws/aws-sdk-go-v2/service/lambda v1.97.0 h1:k/rVQdLBLbfoNfp1pdTO4+RGGt0DhZM/KZ7lgviPA6I= +github.com/aws/aws-sdk-go-v2/service/lambda v1.97.0/go.mod h1:gKWVtxlMTgoLU9m6FDw7z6FAEFh8u8CoaPJx0zWk5J8= +github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0 h1:XptwLL+UHXgafYMIHTy59IRovLbhz3znkxY2uS/pbXU= +github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0/go.mod h1:zdmCoFO/dSI7GlrwsPqFJI+WlFnSU4Tc8TJnlXrM1Do= +github.com/aws/aws-sdk-go-v2/service/signin v1.4.0 h1:sLzmJGCMv+C8KqiJgEqDLB6vxaJGmobRh4rr//ZpA3w= +github.com/aws/aws-sdk-go-v2/service/signin v1.4.0/go.mod h1:mxC0nT/C8wMMS97DemZPzvUZxvIt+2Iq+eS3JdFZGgg= +github.com/aws/aws-sdk-go-v2/service/sns v1.41.0 h1:GT6QdvVfByxl1/AJQe7PNbLtQDj0kmFTgx0eU2tLrKo= +github.com/aws/aws-sdk-go-v2/service/sns v1.41.0/go.mod h1:5EnTxMpMVeiY0vcjjN/a958FFaHrS6XfXcyRBzDKDCE= +github.com/aws/aws-sdk-go-v2/service/sqs v1.45.0 h1:k1aaG71RTEqSWNy1LWkKtSRT2G36x2/HbU+nu54uXpc= +github.com/aws/aws-sdk-go-v2/service/sqs v1.45.0/go.mod h1:JISE0m3JPVhirZEVIAUyK4C62n87tU4BZmUa9Ozc2to= +github.com/aws/aws-sdk-go-v2/service/sso v1.32.0 h1:qjMmry/cBDee1E/2gyvel0uRYCi3mwRZ2hf6N+GAodo= +github.com/aws/aws-sdk-go-v2/service/sso v1.32.0/go.mod h1:u8af9Nqkmqnr96f7v9nHqzZT9XBwbXEkTiqT4ROuJSE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.37.0 h1:fpOlDPI55HdszaxapEGk6HsGosOUaM2YPWJpjMgp8UI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.37.0/go.mod h1:DMPWJBjYs6+3+f/qhBFEFPPlQ6NlhWjai3dJNvipJ84= +github.com/aws/aws-sdk-go-v2/service/sts v1.44.0 h1:bLZ0PolJ8J+HkJHztcXORUpHXBye2U8298lCEMi6ZCU= +github.com/aws/aws-sdk-go-v2/service/sts v1.44.0/go.mod h1:9gdl4RrflIdpDb2TlXshWgR1F9TeCkvqDx77Vpr4Z/Q= +github.com/aws/smithy-go v1.27.3 h1:F3Zb497UhhskkfpJmfkXswyo+t0sh9OTBnIHjogWbVY= +github.com/aws/smithy-go v1.27.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -12,10 +60,6 @@ github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASu github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= @@ -62,20 +106,16 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -87,16 +127,12 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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=