diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..3804775 --- /dev/null +++ b/app/app.go @@ -0,0 +1,30 @@ +package app + +import ( + "github.com/Trapesys/aws-commander/aws" + "github.com/Trapesys/aws-commander/conf" + "github.com/Trapesys/aws-commander/logger" + "go.uber.org/fx" + "os" +) + +func Run() { + fx.New( + fx.Provide( + conf.New, + logger.New, + aws.New, + ), + fx.Invoke(mainApp), + fx.NopLogger, + ).Run() +} + +func mainApp(log logger.Logger, awss aws.Aws) { + if err := awss.Run(); err != nil { + log.Error("Run command error", "err", err) + os.Exit(1) + } + + os.Exit(0) +} diff --git a/aws/aws.go b/aws/aws.go new file mode 100644 index 0000000..f0c2dc5 --- /dev/null +++ b/aws/aws.go @@ -0,0 +1,81 @@ +package aws + +import ( + "github.com/Trapesys/aws-commander/aws/ssm" + "github.com/Trapesys/aws-commander/conf" + "github.com/Trapesys/aws-commander/logger" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/pkg/errors" +) + +type mode string + +const ( + bash mode = "bash" + ansible mode = "ansible" +) + +type modeHandler func() error + +type modesFactory map[mode]modeHandler + +var ( + ErrModeNotSupported = errors.New("selected mode not supported") +) + +type SSM interface { + RunBash() error + RunAnsible() error +} + +type Aws struct { + conf conf.Config + ssm SSM + modes modesFactory +} + +func New(conf conf.Config, log logger.Logger) Aws { + sess, err := provideSesson(conf) + if err != nil { + log.Fatalln("Could not create AWS session", "err", err.Error()) + } + + localssm := ssm.New(log, conf, sess) + + return Aws{ + conf: conf, + ssm: localssm, + modes: modesFactory{ + bash: localssm.RunBash, + ansible: localssm.RunAnsible, + }, + } +} + +func (a *Aws) Run() error { + modeHn, ok := a.modes[mode(a.conf.Mode)] + if !ok { + return ErrModeNotSupported + } + + return modeHn() +} + +func provideSesson(conf conf.Config) (*session.Session, error) { + sessOpt := session.Options{} + sessConf := aws.Config{} + + if conf.AWSRegion != "" { + sessConf.Region = &conf.AWSRegion + } + + if conf.AWSProfile != "" { + sessOpt.Profile = conf.AWSProfile + } + + sessOpt.Config = sessConf + sessOpt.SharedConfigState = session.SharedConfigEnable + + return session.NewSessionWithOptions(sessOpt) +} diff --git a/aws/ssm/ssm.go b/aws/ssm/ssm.go new file mode 100644 index 0000000..ceacc3e --- /dev/null +++ b/aws/ssm/ssm.go @@ -0,0 +1,165 @@ +package ssm + +import ( + "bytes" + "fmt" + "github.com/Trapesys/aws-commander/conf" + "github.com/Trapesys/aws-commander/logger" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" + "github.com/davecgh/go-spew/spew" + "os" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws/session" + assm "github.com/aws/aws-sdk-go/service/ssm" +) + +type ssm struct { + log logger.Logger + conf conf.Config + + cl *assm.SSM +} + +func (s ssm) RunBash() error { + s.log.Info("Running ssm bash command") + + command, err := s.cl.SendCommand(&assm.SendCommandInput{ + DocumentName: aws.String("AWS-RunShellScript"), + DocumentVersion: aws.String("$LATEST"), + InstanceIds: s.provideInstanceIDs(), + Parameters: s.provideBashCommands(), + TimeoutSeconds: aws.Int64(300), + }) + if err != nil { + return err + } + + s.log.Info("Command deployed successfully") + s.log.Info("Waiting for results") + + var instIdsSuccess = make([]*string, 0) + + for _, instId := range command.Command.InstanceIds { + if werr := s.waitForCmdExecutionComplete(command.Command.CommandId, instId); werr != nil { + s.log.Error("Error waiting for command execution", "err", err.Error(), "instance_id", *instId) + } else { + instIdsSuccess = append(instIdsSuccess, instId) + } + } + + for _, id := range instIdsSuccess { + out, err := s.cl.GetCommandInvocation(&assm.GetCommandInvocationInput{ + CommandId: command.Command.CommandId, + InstanceId: id, + }) + if err != nil { + s.log.Error("Could not get command output", "err", "instance_id", *id) + } else { + displayResults(id, out) + } + } + + return nil +} + +func (s ssm) RunAnsible() error { + s.log.Info("Running ssm ansible command") + // TODO: implement + return nil +} + +func New(log logger.Logger, conf conf.Config, session *session.Session) *ssm { + return &ssm{ + log: log.Named("ssm"), + conf: conf, + cl: assm.New(session), + } +} + +func (s ssm) provideInstanceIDs() []*string { + var instIDs []*string + + ids := strings.Split(strings.TrimSpace(s.conf.AWSInstanceIDs), ",") + for _, i := range ids { + trimed := strings.TrimSpace(i) + instIDs = append(instIDs, &trimed) + } + + s.log.Debug("Instance ids", "ids", spew.Sdump(instIDs)) + + return instIDs +} + +func (s ssm) provideBashCommands() map[string][]*string { + var ( + resp = map[string][]*string{} + shebang = "#!/bin/bash" + ) + + if s.conf.BashOneLiner != "" { + resp["commands"] = append(resp["commands"], &shebang) + resp["commands"] = append(resp["commands"], &s.conf.BashOneLiner) + } else if s.conf.BashFile != "" { + cmds, err := s.readBashFileAndProvideCommands() + if err != nil { + s.log.Fatalln("Could not provide bash commands", "err", err.Error()) + } + + for _, c := range cmds { + resp["commands"] = append(resp["commands"], c) + } + } else { + s.log.Fatalln("Bash command or bash script not specified") + } + + s.log.Debug("Parsed commands from bash script", "cmds", spew.Sdump(resp)) + return resp +} + +func (s ssm) readBashFileAndProvideCommands() ([]*string, error) { + var cmds []*string + + fileBytes, err := os.ReadFile(s.conf.BashFile) + if err != nil { + return nil, err + } + + for _, cmdLine := range strings.Split(string(fileBytes), "\n") { + cmds = append(cmds, &cmdLine) + } + + return cmds, nil +} + +func (s ssm) waitForCmdExecutionComplete(cmdId *string, instId *string) error { + return s.cl.WaitUntilCommandExecutedWithContext(aws.BackgroundContext(), &assm.GetCommandInvocationInput{ + CommandId: cmdId, + InstanceId: instId, + }, func(waiter *request.Waiter) { + waiter.Delay = request.ConstantWaiterDelay(time.Second * time.Duration(10)) + }) +} + +func displayResults(instanceId *string, data *assm.GetCommandInvocationOutput) { + buff := bytes.Buffer{} + + buff.WriteString(fmt.Sprintf("==== INSTANCE ID - %s =====\n", *instanceId)) + + if *data.StandardOutputContent != "" { + buff.WriteString("[COMMAND OUTPUT]\n") + buff.WriteString(*data.StandardOutputContent) + buff.WriteString("\n") + } + + if *data.StandardErrorContent != "" { + buff.WriteString("[COMMAND ERROR]\n") + buff.WriteString(*data.StandardErrorContent) + } + + buff.WriteString("====================\n\n") + + fmt.Print(buff.String()) +} diff --git a/conf/conf.go b/conf/conf.go new file mode 100644 index 0000000..99f085e --- /dev/null +++ b/conf/conf.go @@ -0,0 +1,92 @@ +package conf + +import ( + "errors" + "flag" + "log" +) + +var ( + ErrNoBashCMDOrScriptProvided = errors.New("bash cmd or script not provided") + ErrAnsiblePlaybookNotProvided = errors.New("ansible playbook not provided") + ErrEC2TagsOrIDsNotSpecified = errors.New("ec2 instance ids or tags not specified") + ErrEC2TagsAndIDsAreMutuallyExclusive = errors.New("ec2 instance ids and tags are mutually exclusive") +) + +type Config struct { + LogLevel string + Mode string + + BashOneLiner string + BashFile string + + AnsiblePlaybook string + + AWSProfile string + AWSRegion string + + AWSInstanceIDs string + AWSInstanceTags string + + CommandMaxWait int +} + +func New() Config { + conf := DefaultConfig() + + conf.processFlags() + if err := conf.validateFlags(); err != nil { + log.Fatalln(err) + } + + return conf +} + +func DefaultConfig() Config { + return Config{ + LogLevel: "info", + Mode: "bash", + BashOneLiner: "", + BashFile: "", + AnsiblePlaybook: "", + AWSProfile: "", + AWSRegion: "", + AWSInstanceIDs: "", + AWSInstanceTags: "", + CommandMaxWait: 30, + } +} + +func (c *Config) processFlags() { + flag.StringVar(&c.LogLevel, "log-level", c.LogLevel, "log output level") + flag.StringVar(&c.Mode, "mode", c.Mode, "running mode") + flag.StringVar(&c.BashOneLiner, "cmd", c.BashOneLiner, "bash command to run") + flag.StringVar(&c.BashFile, "script", c.BashFile, "bash script to run") + flag.StringVar(&c.AnsiblePlaybook, "playbook", c.AnsiblePlaybook, "ansible playbook to run") + flag.StringVar(&c.AWSProfile, "profile", c.AWSProfile, "aws profile") + flag.StringVar(&c.AWSRegion, "region", c.AWSRegion, "aws region") + flag.StringVar(&c.AWSInstanceIDs, "ids", c.AWSInstanceIDs, "comma delimited list of aws ec2 ids") + flag.StringVar(&c.AWSInstanceTags, "tags", c.AWSInstanceTags, "comma delimited list of ec2 tags") + flag.IntVar(&c.CommandMaxWait, "max-wait", c.CommandMaxWait, "maximum wait time in seconds for command execution") + flag.Parse() +} + +func (c *Config) validateFlags() error { + if c.Mode == "bash" && c.BashFile == "" && c.BashOneLiner == "" { + return ErrNoBashCMDOrScriptProvided + } + + if c.Mode == "ansible" && c.AnsiblePlaybook == "" { + return ErrAnsiblePlaybookNotProvided + } + + if c.AWSInstanceIDs == "" && c.AWSInstanceTags == "" { + return ErrEC2TagsOrIDsNotSpecified + } + + if c.AWSInstanceTags != "" && c.AWSInstanceIDs != "" { + return ErrEC2TagsAndIDsAreMutuallyExclusive + } + + return nil +} diff --git a/go.mod b/go.mod index 724b698..291aa68 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,10 @@ go 1.17 require ( github.com/aws/aws-sdk-go v1.44.79 + github.com/davecgh/go-spew v1.1.1 github.com/hashicorp/go-hclog v1.2.2 + github.com/pkg/errors v0.9.1 + go.uber.org/fx v1.20.1 ) require ( @@ -12,5 +15,9 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/dig v1.17.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.23.0 // indirect golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect ) diff --git a/go.sum b/go.sum index 9ac632c..f95afd6 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,8 @@ github.com/aws/aws-sdk-go v1.44.79 h1:IZCtfBq9VlJ1Eu34I+2Y76q+XkvTtZYbEwaoVM1gzoA= github.com/aws/aws-sdk-go v1.44.79/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -11,32 +14,82 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y 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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/dig v1.17.0 h1:5Chju+tUvcC+N7N6EV08BJz41UZuO3BmHcN4A287ZLI= +go.uber.org/dig v1.17.0/go.mod h1:rTxpf7l5I0eBTlE6/9RL+lDybC7WFwY2QH55ZSjy1mU= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/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-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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/logger/hclog.go b/logger/hclog.go new file mode 100644 index 0000000..8638642 --- /dev/null +++ b/logger/hclog.go @@ -0,0 +1,72 @@ +package logger + +import ( + "github.com/Trapesys/aws-commander/conf" + "github.com/hashicorp/go-hclog" + "os" +) + +type hlog struct { + log hclog.Logger +} + +func (h *hlog) Error(msg string, args ...interface{}) { + if len(args) == 0 { + h.log.Error(msg) + return + } + + h.log.Error(msg, args...) +} + +func (h *hlog) Warn(msg string, args ...interface{}) { + if len(args) == 0 { + h.log.Warn(msg) + return + } + + h.log.Warn(msg, args...) +} + +func (h *hlog) Info(msg string, args ...interface{}) { + if len(args) == 0 { + h.log.Info(msg) + return + } + + h.log.Info(msg, args...) +} + +func (h *hlog) Debug(msg string, args ...interface{}) { + if len(args) == 0 { + h.log.Debug(msg) + return + } + + h.log.Debug(msg, args...) +} + +func (h *hlog) Fatalln(msg string, args ...interface{}) { + if len(args) == 0 { + h.log.Error(msg) + } else { + h.log.Error(msg, args...) + } + + os.Exit(1) +} + +func (h *hlog) Named(name string) Logger { + h.log = h.log.Named(name) + return h +} + +func New(conf conf.Config) Logger { + return &hlog{ + log: hclog.New(&hclog.LoggerOptions{ + Name: "aws-commander", + Level: hclog.LevelFromString(conf.LogLevel), + Color: hclog.AutoColor, + }), + } +} diff --git a/logger/logger.go b/logger/logger.go new file mode 100644 index 0000000..16fcc56 --- /dev/null +++ b/logger/logger.go @@ -0,0 +1,10 @@ +package logger + +type Logger interface { + Error(msg string, args ...interface{}) + Warn(msg string, args ...interface{}) + Info(msg string, args ...interface{}) + Debug(msg string, args ...interface{}) + Fatalln(msg string, args ...interface{}) + Named(name string) Logger +} diff --git a/main.go b/main.go index e29c898..05c9d4b 100644 --- a/main.go +++ b/main.go @@ -1,34 +1,7 @@ package main -import ( - "os" - - "github.com/Trapesys/aws-commander/framework/adapters/left/cmd" - "github.com/Trapesys/aws-commander/framework/adapters/left/localfs" - "github.com/Trapesys/aws-commander/framework/adapters/right/ssm" - "github.com/Trapesys/aws-commander/internal/adapters/app" - "github.com/Trapesys/aws-commander/internal/adapters/core" - "github.com/hashicorp/go-hclog" -) +import "github.com/Trapesys/aws-commander/app" func main() { - // init logger instance - logger := hclog.New(&hclog.LoggerOptions{ - Name: "aws-commander", - Level: hclog.NoLevel, - }) - - // inject adapters into App - commander := app.NewAdapter( - core.NewAdapter(), - localfs.NewAdapter(), - ssm.NewAdapter(), - cmd.NewAdapter(), - ).WithLogger(logger).Init() - - // run command and check for error - if err := commander.RunCommand(); err != nil { - logger.Error("could not run command: ", "err", err.Error()) - os.Exit(1) - } + app.Run() }