04/06/2023 - AWS, GO
Bu örnekte, AWS S3 dosya yükleme ve indirme için önceden imzalanmış bir URL kullanacağız. URL, varsayılan olarak 15 dakika boyunca geçerli olacaktır.
S3 grubum başka bir hesapta, dolayısıyla STS istemcisi kullanıyorum.
package aws
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
func NewCrossAccountConfigWithRole(ctx context.Context, roleARN string) (aws.Config, error) {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return aws.Config{}, err
}
stsClient := sts.NewFromConfig(cfg)
stsCreds := stscreds.NewAssumeRoleProvider(stsClient, roleARN)
cfg.Credentials = aws.NewCredentialsCache(stsCreds)
return cfg, nil
}
package aws
import "time"
type PresignedURLArgs struct {
Bucket string
Key string
Expiry time.Duration
}
package aws
import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/s3"
)
type S3 struct {
client *s3.Client
signer *s3.PresignClient
}
func NewS3(cfg aws.Config) S3 {
client := s3.NewFromConfig(cfg)
return S3{
client: client,
signer: s3.NewPresignClient(client),
}
}
// PresignedUploadURL creates a presigned request URL that can be used to upload
// an object in a bucket. The URL is valid for the specified number of seconds.
func (s S3) PresignedUploadURL(ctx context.Context, args PresignedURLArgs) (string, error) {
input := s3.PutObjectInput{
Bucket: aws.String(args.Bucket),
Key: aws.String(args.Key),
}
expiry := func(opts *s3.PresignOptions) {
opts.Expires = args.Expiry
}
req, err := s.signer.PresignPutObject(ctx, &input, expiry)
if err != nil {
return "", fmt.Errorf("failed to create request: %w", err)
}
return req.URL, nil
}
// PresignedDownloadURL creates a presigned request URL that can be used to download
// an object from a bucket. The URL is valid for the specified number of seconds.
func (s S3) PresignedDownloadURL(ctx context.Context, args PresignedURLArgs) (string, error) {
input := s3.GetObjectInput{
Bucket: aws.String(args.Bucket),
Key: aws.String(args.Key),
}
expiry := func(opts *s3.PresignOptions) {
opts.Expires = args.Expiry
}
req, err := s.signer.PresignGetObject(ctx, &input, expiry)
if err != nil {
return "", fmt.Errorf("failed to create request: %w", err)
}
return req.URL, nil
}
package api
import (
"context"
"fmt"
"net/http"
"strings"
)
func Upload(ctx context.Context, url string) error {
req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, strings.NewReader(`{"users":["a,b,c"]}`))
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
res, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
if res.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected response: %s", res.Status)
}
return nil
}
package api
import (
"context"
"fmt"
"io"
"net/http"
"os"
)
func Download(ctx context.Context, url, path string) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
res, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
return fmt.Errorf("failed to send request: %w", err)
}
if res.StatusCode != http.StatusOK {
return fmt.Errorf("unexpected response: %s", res.Status)
}
defer res.Body.Close()
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
return fmt.Errorf("failed to open file: %w", err)
}
defer file.Close()
// In a memory efficient manner, read maximum 32KB at a time from input and
// write it to output then repeat until the EOL.
if _, err = io.Copy(file, res.Body); err != nil {
return fmt.Errorf("failed to write file: %w", err)
}
return nil
}
package main
import (
"aws/api"
"aws/aws"
"context"
"log"
"time"
)
var (
bucket = "inanzzz-development"
key = "users.json"
expiry = time.Second * 10
)
func main() {
ctx := context.Background()
awsConfig, err := aws.NewCrossAccountConfigWithRole(ctx, "arn:aws:iam::1234567890:role/services")
if err != nil {
log.Fatal(err)
}
s3 := aws.NewS3(awsConfig)
// UPLOAD ------------------------------------------------------------------
uploadArgs := aws.PresignedURLArgs{
Bucket: bucket,
Key: key,
Expiry: expiry,
}
uploadURL, err := s3.PresignedUploadURL(ctx, uploadArgs)
if err != nil {
log.Fatalln(err)
}
if err := api.Upload(ctx, uploadURL); err != nil {
log.Fatalln(err)
}
// DOWNLOAD ----------------------------------------------------------------
downloadArgs := aws.PresignedURLArgs{
Bucket: bucket,
Key: key,
Expiry: expiry,
}
downloadURL, err := s3.PresignedDownloadURL(ctx, downloadArgs)
if err != nil {
log.Fatalln(err)
}
if err := api.Download(ctx, downloadURL, "downloaded-users.json"); err != nil {
log.Fatalln(err)
}
}