package tools import ( "context" "fmt" "io" "os" "path/filepath" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/cloudformation" cftypes "github.com/aws/aws-sdk-go-v2/service/cloudformation/types" "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/s3" ) func AWS() *awstool { ctx := context.TODO() accessKeyID := os.Getenv("AWS_ACCESS_KEY_ID") secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY") sessionToken := os.Getenv("AWS_SESSION_TOKEN") staticProvider := credentials.NewStaticCredentialsProvider(accessKeyID, secretAccessKey, sessionToken) cfg, err := config.LoadDefaultConfig(ctx, config.WithCredentialsProvider(staticProvider), config.WithRegion(os.Getenv("AWS_REGION")), ) if err != nil { log.Write([]byte(fmt.Sprintf("unable to load SDK config, %v", err))) } return &awstool{ cfg: cfg, } } type ( awstool struct { cfg aws.Config } awss3 struct { aws *awstool } awsec2 struct { aws *awstool } awscloudformation struct { aws *awstool } awsimagebuilder struct { aws *awstool } awsecs struct { aws *awstool } awseks struct { aws *awstool } ) func (t *awstool) S3() *awss3 { return &awss3{aws: t} } func (t *awstool) EC2() *awsec2 { return &awsec2{aws: t} } func (t *awstool) CloudFormation() *awscloudformation { return &awscloudformation{aws: t} } func (t *awstool) ImageBuilder() *awsimagebuilder { return &awsimagebuilder{aws: t} } func (t *awstool) ECS() *awsecs { return &awsecs{aws: t} } func (t *awstool) EKS() *awseks { return &awseks{aws: t} } func (t *awstool) Filter(filters map[string][]string) []types.Filter { filter := []types.Filter{} for k, v := range filters { filter = append(filter, types.Filter{ Name: aws.String(k), Values: v, }) } return filter } func (t *awss3) Put(src, bucket, key string) error { api := s3.NewFromConfig(t.aws.cfg) f, err := os.Open(src) if err != nil { return err } defer f.Close() _, err = api.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), Body: f, }) if err != nil { return err } return nil } func (t *awss3) PutDir(path, bucket, s3Prefix string) error { api := s3.NewFromConfig(t.aws.cfg) err := filepath.WalkDir(path, func(p string, d os.DirEntry, err error) error { if !d.IsDir() { relPath, _ := filepath.Rel(path, p) s3Key := filepath.ToSlash(filepath.Join(s3Prefix, relPath)) file, err := os.Open(p) if err == nil { return err } defer file.Close() api.PutObject(context.TODO(), &s3.PutObjectInput{ Bucket: aws.String(bucket), Key: aws.String(s3Key), Body: file, }) } return nil }) if err != nil { return err } return nil } func (t *awss3) Get(bucket, key, dst string) error { api := s3.NewFromConfig(t.aws.cfg) output, err := api.GetObject(context.TODO(), &s3.GetObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), }) if err != nil { return err } f, err := os.Create(dst) if err != nil { return err } _, err = io.Copy(f, output.Body) return err } func (t *awss3) Read(bucket, key string) ([]byte, error) { api := s3.NewFromConfig(t.aws.cfg) output, err := api.GetObject(context.TODO(), &s3.GetObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), }) if err != nil { return nil, err } return io.ReadAll(output.Body) } func (t *awss3) ListObjects(bucket, path string) error { return nil } func (t *awss3) ListBuckets(prefix string) ([]string, error) { api := s3.NewFromConfig(t.aws.cfg) output, err := api.ListBuckets(context.TODO(), &s3.ListBucketsInput{ Prefix: &prefix, }) if err != nil { return nil, err } buckets := []string{} for _, bucket := range output.Buckets { buckets = append(buckets, *bucket.Name) } return buckets, nil } func (t *awsec2) ListIAMRoles(filter []types.Filter) error { return nil } func (t *awsec2) FindLatestAMI(filter []types.Filter) error { return nil } func (t *awsec2) ListSubnets(filter []types.Filter) error { return nil } func (t *awsec2) ListSecurityGroups(filter []types.Filter) error { return nil } func (t *awsec2) ListVPCs(filter []types.Filter) error { return nil } func (t *awsec2) ListKeypairs(filter []types.Filter) error { return nil } func (t *awsec2) List(filter []types.Filter) error { return nil } func (t *awsec2) Create() error { return nil } func (t *awsec2) Stop() error { return nil } func (t *awsec2) Start() error { return nil } func (t *awsec2) Terminate() error { return nil } func (t *awscloudformation) ListStacks(filter []types.Filter) {} func (t *awscloudformation) CreateStack(stackName, templatePath string, params, tags map[string]string) error { ctx := context.TODO() client := cloudformation.NewFromConfig(t.aws.cfg) templateBody, err := os.ReadFile(templatePath) if err != nil { return fmt.Errorf("unable to read template file %s, %v", templatePath, err) } aparams := []cftypes.Parameter{} for k, v := range params { aparams = append(aparams, cftypes.Parameter{ ParameterKey: aws.String(k), ParameterValue: aws.String(v), }) } atags := []cftypes.Tag{} for k, v := range tags { atags = append(atags, cftypes.Tag{ Key: aws.String(k), Value: aws.String(v), }) } input := &cloudformation.CreateStackInput{ StackName: aws.String(stackName), TemplateBody: aws.String(string(templateBody)), Parameters: aparams, Capabilities: []cftypes.Capability{ cftypes.CapabilityCapabilityNamedIam, }, Tags: atags, } result, err := client.CreateStack(ctx, input) if err != nil { return fmt.Errorf("failed to create stack %s, %v", stackName, err) } log.Write([]byte(fmt.Sprintf("Stack creation initiated. Stack ID: %s\n", aws.ToString(result.StackId)))) return nil } func (t *awscloudformation) UpdateStack(stackName, templatePath string, params, tags map[string]string) error { ctx := context.TODO() client := cloudformation.NewFromConfig(t.aws.cfg) templateBody, err := os.ReadFile(templatePath) if err != nil { return fmt.Errorf("unable to read template file %s, %v", templatePath, err) } aparams := []cftypes.Parameter{} for k, v := range params { aparams = append(aparams, cftypes.Parameter{ ParameterKey: aws.String(k), ParameterValue: aws.String(v), }) } atags := []cftypes.Tag{} for k, v := range tags { atags = append(atags, cftypes.Tag{ Key: aws.String(k), Value: aws.String(v), }) } input := &cloudformation.UpdateStackInput{ StackName: aws.String(stackName), TemplateBody: aws.String(string(templateBody)), Parameters: aparams, Capabilities: []cftypes.Capability{ cftypes.CapabilityCapabilityNamedIam, }, Tags: atags, } result, err := client.UpdateStack(ctx, input) if err != nil { return fmt.Errorf("failed to update stack %s, %v", stackName, err) } log.Write([]byte(fmt.Sprintf("Stack update initiated. Stack ID: %s\n", aws.ToString(result.StackId)))) return nil } func (t *awscloudformation) DeleteStack(stackName string) error { ctx := context.TODO() client := cloudformation.NewFromConfig(t.aws.cfg) input := &cloudformation.DeleteStackInput{ StackName: aws.String(stackName), } _, err := client.DeleteStack(ctx, input) if err != nil { return fmt.Errorf("failed to delete stack %s, %v", stackName, err) } return nil } func (t *awsimagebuilder) Component() error { return nil } func (t *awsimagebuilder) Recipe() error { return nil } func (t *awsimagebuilder) Pipeline() error { return nil } func (t *awsimagebuilder) RunPipeline() error { return nil } func (t *awsecs) Cluster() error { return nil } func (t *awsecs) Task() error { return nil } func (t *awseks) UpgradeCluster() error { return nil }