Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ls subcommand #9

Merged
merged 2 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion app/di/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
type S3App struct {
// S3BucketCreator is the usecase for creating a new S3 bucket.
S3BucketCreator usecase.S3BucketCreator
// S3BucketLister is the usecase for listing S3 buckets.
S3BucketLister usecase.S3BucketLister
}

// NewS3App creates a new S3App.
Expand All @@ -26,14 +28,18 @@ func NewS3App(ctx context.Context, profile model.AWSProfile, region model.Region
model.NewAWSConfig,
external.NewS3Client,
external.S3BucketCreatorSet,
external.S3BucketListerSet,
external.S3BucketLocationGetterSet,
interactor.S3bucketCreatorSet,
interactor.S3bucketListerSet,
newS3App,
)
return nil, nil
}

func newS3App(s3bucketCreator usecase.S3BucketCreator) *S3App {
func newS3App(s3bucketCreator usecase.S3BucketCreator, s3bucketLister usecase.S3BucketLister) *S3App {
return &S3App{
S3BucketCreator: s3bucketCreator,
S3BucketLister: s3bucketLister,
}
}
10 changes: 8 additions & 2 deletions app/di/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions app/domain/model/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package model
import (
"fmt"
"strings"
"time"

"github.com/nao1215/rainbow/utils/errfmt"
"github.com/nao1215/rainbow/utils/xregex"
Expand Down Expand Up @@ -219,3 +220,27 @@ func (b Bucket) validateCharSequence() error {
}
return nil
}

// BucketSets is the set of the BucketSet.
type BucketSets []BucketSet

// BucketSet is the set of the Bucket and the Region.
type BucketSet struct {
// Bucket is the name of the S3 bucket.
Bucket Bucket
// Region is the name of the AWS region.
Region Region
// CreationDate is date the bucket was created.
// This date can change when making changes to your bucket, such as editing its bucket policy.
CreationDate time.Time
}

// Len returns the length of the BucketSets.
func (b BucketSets) Len() int {
return len(b)
}

// Empty returns true if the BucketSets is empty.
func (b BucketSets) Empty() bool {
return b.Len() == 0
}
30 changes: 30 additions & 0 deletions app/domain/service/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,33 @@ type S3BucketCreatorOutput struct{}
type S3BucketCreator interface {
CreateBucket(ctx context.Context, input *S3BucketCreatorInput) (*S3BucketCreatorOutput, error)
}

// S3BucketListerInput is the input of the ListBuckets method.
type S3BucketListerInput struct{}

// S3BucketListerOutput is the output of the ListBuckets method.
type S3BucketListerOutput struct {
// Buckets is the list of the buckets.
Buckets model.BucketSets
}

// S3BucketLister is the interface that wraps the basic ListBuckets method.
type S3BucketLister interface {
ListBuckets(ctx context.Context, input *S3BucketListerInput) (*S3BucketListerOutput, error)
}

// S3BucketLocationGetterInput is the input of the GetBucketLocation method.
type S3BucketLocationGetterInput struct {
Bucket model.Bucket
}

// S3BucketLocationGetterOutput is the output of the GetBucketLocation method.
type S3BucketLocationGetterOutput struct {
// Region is the region of the bucket.
Region model.Region
}

// S3BucketLocationGetter is the interface that wraps the basic GetBucketLocation method.
type S3BucketLocationGetter interface {
GetBucketLocation(ctx context.Context, input *S3BucketLocationGetterInput) (*S3BucketLocationGetterOutput, error)
}
76 changes: 76 additions & 0 deletions app/external/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,79 @@ func (c *S3BucketCreator) CreateBucket(ctx context.Context, input *service.S3Buc
}
return &service.S3BucketCreatorOutput{}, nil
}

// S3BucketLister implements the S3BucketLister interface.
type S3BucketLister struct {
client *s3.Client
}

// S3BucketListerSet is a provider set for S3BucketLister.
//
//nolint:gochecknoglobals
var S3BucketListerSet = wire.NewSet(
NewS3BucketLister,
wire.Bind(new(service.S3BucketLister), new(*S3BucketLister)),
)

var _ service.S3BucketLister = (*S3BucketLister)(nil)

// NewS3BucketLister creates a new S3BucketLister.
func NewS3BucketLister(client *s3.Client) *S3BucketLister {
return &S3BucketLister{client: client}
}

// ListBuckets lists the buckets.
func (c *S3BucketLister) ListBuckets(ctx context.Context, _ *service.S3BucketListerInput) (*service.S3BucketListerOutput, error) {
out, err := c.client.ListBuckets(ctx, &s3.ListBucketsInput{})
if err != nil {
return nil, err
}

var buckets model.BucketSets
for _, b := range out.Buckets {
buckets = append(buckets, model.BucketSet{
Bucket: model.Bucket(*b.Name),
CreationDate: *b.CreationDate,
})
}
return &service.S3BucketListerOutput{Buckets: buckets}, nil
}

// S3BucketLocationGetter implements the S3BucketLocationGetter interface.
type S3BucketLocationGetter struct {
client *s3.Client
}

// S3BucketLocationGetterSet is a provider set for S3BucketLocationGetter.
//
//nolint:gochecknoglobals
var S3BucketLocationGetterSet = wire.NewSet(
NewS3BucketLocationGetter,
wire.Bind(new(service.S3BucketLocationGetter), new(*S3BucketLocationGetter)),
)

var _ service.S3BucketLocationGetter = (*S3BucketLocationGetter)(nil)

// NewS3BucketLocationGetter creates a new S3BucketLocationGetter.
func NewS3BucketLocationGetter(client *s3.Client) *S3BucketLocationGetter {
return &S3BucketLocationGetter{client: client}
}

// GetBucketLocation gets the location of the bucket.
func (c *S3BucketLocationGetter) GetBucketLocation(ctx context.Context, input *service.S3BucketLocationGetterInput) (*service.S3BucketLocationGetterOutput, error) {
out, err := c.client.GetBucketLocation(ctx, &s3.GetBucketLocationInput{
Bucket: aws.String(input.Bucket.String()),
})
if err != nil {
return nil, err
}

region := model.Region(out.LocationConstraint)
if region == "" {
region = model.RegionUSEast1
}

return &service.S3BucketLocationGetterOutput{
Region: region,
}, nil
}
47 changes: 47 additions & 0 deletions app/interactor/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,50 @@ func (s *S3BucketCreator) CreateBucket(ctx context.Context, input *usecase.S3Buc
}
return &usecase.S3BucketCreatorOutput{}, nil
}

// S3bucketListerSet is a provider set for S3BucketLister.
//
//nolint:gochecknoglobals
var S3bucketListerSet = wire.NewSet(
NewS3BucketLister,
wire.Bind(new(usecase.S3BucketLister), new(*S3BucketLister)),
)

var _ usecase.S3BucketLister = (*S3BucketLister)(nil)

// S3BucketLister implements the S3BucketLister interface.
type S3BucketLister struct {
service.S3BucketLister
service.S3BucketLocationGetter
}

// NewS3BucketLister creates a new S3BucketLister.
func NewS3BucketLister(l service.S3BucketLister, g service.S3BucketLocationGetter) *S3BucketLister {
return &S3BucketLister{
S3BucketLister: l,
S3BucketLocationGetter: g,
}
}

// ListBuckets lists the buckets.
func (s *S3BucketLister) ListBuckets(ctx context.Context, _ *usecase.S3BucketListerInput) (*usecase.S3BucketListerOutput, error) {
out, err := s.S3BucketLister.ListBuckets(ctx, &service.S3BucketListerInput{})
if err != nil {
return nil, err
}

for i, b := range out.Buckets {
in := service.S3BucketLocationGetterInput{
Bucket: b.Bucket,
}
o, err := s.S3BucketLocationGetter.GetBucketLocation(ctx, &in)
if err != nil {
return nil, err
}
out.Buckets[i].Region = o.Region
}

return &usecase.S3BucketListerOutput{
Buckets: out.Buckets,
}, nil
}
14 changes: 14 additions & 0 deletions app/usecase/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,17 @@ type S3BucketCreatorOutput struct{}
type S3BucketCreator interface {
CreateBucket(ctx context.Context, input *S3BucketCreatorInput) (*S3BucketCreatorOutput, error)
}

// S3BucketListerInput is the input of the ListBuckets method.
type S3BucketListerInput struct{}

// S3BucketListerOutput is the output of the ListBuckets method.
type S3BucketListerOutput struct {
// Buckets is the list of the buckets.
Buckets model.BucketSets
}

// S3BucketLister is the interface that wraps the basic ListBuckets method.
type S3BucketLister interface {
ListBuckets(ctx context.Context, input *S3BucketListerInput) (*S3BucketListerOutput, error)
}
22 changes: 22 additions & 0 deletions cmd/subcmd/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package subcmd

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [golangci] reported by reviewdog 🐶
ST1000: at least one file in a package should have a package comment (stylecheck)


import "github.com/spf13/cobra"

// Doer is an interface that represents the behavior of a command.
type Doer interface {
Do() error
}

// SubCommand is an interface that represents the behavior of a command.
type SubCommand interface {
Parse(cmd *cobra.Command, args []string) error
Doer
}

// Run runs the subcommand.
func Run(cmd *cobra.Command, args []string, subCmd SubCommand) error {
if err := subCmd.Parse(cmd, args); err != nil {
return err
}
return subCmd.Do()
}
75 changes: 75 additions & 0 deletions cmd/subcmd/s3hub/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package s3hub

import (
"context"

"github.com/nao1215/rainbow/app/di"
"github.com/nao1215/rainbow/app/domain/model"
"github.com/nao1215/rainbow/utils/errfmt"
"github.com/spf13/cobra"
)

// s3hub have common fields and methods for s3hub commands.
type s3hub struct {
// S3App is the application service for S3.
*di.S3App
// command is the cobra command.
command *cobra.Command
// ctx is the context of s3hub command.
ctx context.Context
// profile is the AWS profile name.
profile model.AWSProfile
// region is the AWS region name.
region model.Region
}

// newS3hub returns a new s3hub.
func newS3hub() *s3hub {
return &s3hub{}
}

// parse parses command line arguments.
func (s *s3hub) parse(cmd *cobra.Command) error {
s.command = cmd
s.ctx = context.Background()

p, err := cmd.Flags().GetString("profile")
if err != nil {
return err
}
s.profile = model.NewAWSProfile(p)

r, err := cmd.Flags().GetString("region")
if err != nil {
return err
}
s.region = model.Region(r)

cfg, err := model.NewAWSConfig(s.ctx, s.profile, s.region)
if err != nil {
return errfmt.Wrap(err, "can not get aws config")
}
if s.region == "" {
if cfg.Config.Region == "" {
s.region = model.RegionUSEast1
} else {
s.region = model.Region(cfg.Config.Region)
}
}

s.S3App, err = di.NewS3App(s.ctx, s.profile, s.region)
if err != nil {
return errfmt.Wrap(err, "can not create s3 application service")
}
return nil
}

// printf prints a formatted string.
func (s *s3hub) printf(format string, a ...interface{}) {
s.command.Printf(format, a...)
}

// commandName returns the s3hub command name.
func commandName() string {
return "s3hub"
}
Loading