Skip to content

Commit

Permalink
added full ansible support
Browse files Browse the repository at this point in the history
  • Loading branch information
ZeljkoBenovic committed Nov 18, 2023
1 parent 2ed5779 commit 310d864
Show file tree
Hide file tree
Showing 22 changed files with 157 additions and 958 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:

- uses: actions/setup-go@v3
with:
go-version: 1.17.x
go-version: 1.21.x
cache: true

- uses: goreleaser/goreleaser-action@v2
Expand Down
3 changes: 0 additions & 3 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
hooks:
# You may remove this if you don't use go modules.
- go mod tidy
builds:
- env:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.PHONY: lint
lint:
golangci-lint run -E whitespace -E wsl -E wastedassign -E unconvert -E tparallel -E thelper -E stylecheck -E prealloc \
golangci-lint run --fix -E whitespace -E wsl -E wastedassign -E unconvert -E tparallel -E thelper -E stylecheck -E prealloc \
-E predeclared -E nlreturn -E misspell -E makezero -E lll -E importas -E gosec -E gofmt -E goconst \
-E forcetypeassert -E dogsled -E dupl -E errname -E errorlint -E nolintlint --timeout 2m
3 changes: 2 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package app

import (
"os"

"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() {
Expand Down
74 changes: 58 additions & 16 deletions aws/ssm/ansible.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package ssm

import (
"os"
"strings"

"github.com/aws/aws-sdk-go/aws"
assm "github.com/aws/aws-sdk-go/service/ssm"
"os"
"github.com/davecgh/go-spew/spew"
)

func (s ssm) RunAnsible() error {
Expand All @@ -20,30 +23,69 @@ func (s ssm) RunAnsible() error {
return err
}

s.log.Info("Command deployed successfully")
s.log.Info("Waiting for results")
s.log.Info("Ansible playbook deployed successfully")
s.log.Info("Waiting for results...")

s.waitForCmdExecAndDisplayCmdOutput(command)

return nil
}

func (s ssm) provideAnsibleCommands() map[string][]*string {
var resp = map[string][]*string{}
checkStr := "False"
var (
trueStr = "True"
falseStr = "False"
resp = map[string][]*string{}
check = map[bool]*string{
true: &trueStr,
false: &falseStr,
}
)

playbookStr, err := os.ReadFile(s.conf.AnsiblePlaybook)
if err != nil {
s.log.Fatalln("Could not read ansible playbook", "err", err.Error())
resp["check"] = []*string{check[s.conf.AnsibleDryRun]}

if s.conf.AnsiblePlaybook != "" {
playbookStr, err := os.ReadFile(s.conf.AnsiblePlaybook)
if err != nil {
s.log.Fatalln("Could not read ansible playbook", "err", err.Error())
}

playbook := string(playbookStr)

resp["playbook"] = []*string{&playbook}
}

if s.conf.AnsibleURL != "" {
resp["playbookurl"] = []*string{&s.conf.AnsibleURL}
}
playbook := string(playbookStr)
resp["playbook"] = []*string{&playbook}

if s.conf.AnsibleDryRun {
checkStr = "True"
if s.conf.AnsibleExtraVars != "" {
resp["extravars"] = []*string{s.processExtraVars()}
}
resp["check"] = []*string{&checkStr}

// TODO: implement "ploybookurl" and "extravars"
resp["playbookurl"] = []*string{}
resp["extravars"] = []*string{}
s.log.Debug("Ansible params", "prams", spew.Sdump(resp))

return resp
}

func (s ssm) processExtraVars() *string {
var (
trimmedVars = make([]string, 0)
processedVars string
)

vars := strings.Split(strings.TrimSpace(s.conf.AnsibleExtraVars), ",")
for _, v := range vars {
trimmedVars = append(trimmedVars, strings.TrimSpace(v))
}

for _, tv := range trimmedVars {
processedVars += tv + " "
}

processedVars = processedVars[:len(processedVars)-1] // trim last space char

s.log.Debug("Processed extra vars", "vars", processedVars)

return &processedVars
}
34 changes: 8 additions & 26 deletions aws/ssm/bash.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package ssm

import (
"os"
"strings"

"github.com/aws/aws-sdk-go/aws"
assm "github.com/aws/aws-sdk-go/service/ssm"
"github.com/davecgh/go-spew/spew"
"os"
"strings"
)

func (s ssm) RunBash() error {
Expand All @@ -22,30 +23,10 @@ func (s ssm) RunBash() error {
return err
}

s.log.Info("Command deployed successfully")
s.log.Info("Waiting for results")
s.log.Info("Bash 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)
}
}
s.waitForCmdExecAndDisplayCmdOutput(command)

return nil
}
Expand Down Expand Up @@ -73,11 +54,12 @@ func (s ssm) provideBashCommands() map[string][]*string {
}

s.log.Debug("Parsed commands from bash script", "cmds", spew.Sdump(resp))

return resp
}

func (s ssm) readBashFileAndProvideCommands() ([]*string, error) {
var cmds []*string
var cmds = make([]*string, 0)

fileBytes, err := os.ReadFile(s.conf.BashFile)
if err != nil {
Expand Down
38 changes: 31 additions & 7 deletions aws/ssm/ssm.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func New(log logger.Logger, conf conf.Config, session *session.Session) *ssm {
}

func (s ssm) provideInstanceIDs() []*string {
var instIDs []*string
var instIDs = make([]*string, 0)

ids := strings.Split(strings.TrimSpace(s.conf.AWSInstanceIDs), ",")
for _, i := range ids {
Expand All @@ -45,19 +45,43 @@ func (s ssm) provideInstanceIDs() []*string {
return instIDs
}

func (s ssm) waitForCmdExecutionComplete(cmdId *string, instId *string) error {
func (s ssm) waitForCmdExecutionComplete(cmdID *string, instID *string) error {
return s.cl.WaitUntilCommandExecutedWithContext(aws.BackgroundContext(), &assm.GetCommandInvocationInput{
CommandId: cmdId,
InstanceId: instId,
CommandId: cmdID,
InstanceId: instID,
}, func(waiter *request.Waiter) {
waiter.Delay = request.ConstantWaiterDelay(time.Second * time.Duration(s.conf.CommandResultMaxWait))
})
}

func displayResults(instanceId *string, data *assm.GetCommandInvocationOutput) {
func (s ssm) waitForCmdExecAndDisplayCmdOutput(command *assm.SendCommandOutput) {
var instIdsSuccess = make([]*string, 0)

for _, instID := range command.Command.InstanceIds {
if err := s.waitForCmdExecutionComplete(command.Command.CommandId, instID); err != 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)
}
}
}

func displayResults(instanceID *string, data *assm.GetCommandInvocationOutput) {
buff := bytes.Buffer{}

buff.WriteString(fmt.Sprintf("==== INSTANCE ID - %s =====\n", *instanceId))
buff.WriteString(fmt.Sprintf("==== INSTANCE ID - %s =====\n", *instanceID))

if *data.StandardOutputContent != "" {
buff.WriteString("[COMMAND OUTPUT]\n")
Expand All @@ -73,7 +97,7 @@ func displayResults(instanceId *string, data *assm.GetCommandInvocationOutput) {
buff.WriteString("NO CONTENT TO SHOW\n")
}

buff.WriteString("====================\n")
buff.WriteString("====================\n\n")

fmt.Print(buff.String())
}
63 changes: 49 additions & 14 deletions conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ type Config struct {
BashOneLiner string
BashFile string

AnsiblePlaybook string
AnsibleDryRun bool
AnsiblePlaybook string
AnsibleURL string
AnsibleExtraVars string
AnsibleDryRun bool

AWSProfile string
AWSRegion string
Expand All @@ -37,6 +39,7 @@ func New() Config {
conf := DefaultConfig()

conf.processFlags()

if err := conf.validateFlags(); err != nil {
log.Fatalln(err)
}
Expand All @@ -51,29 +54,61 @@ func DefaultConfig() Config {
BashOneLiner: "",
BashFile: "",
AnsiblePlaybook: "",
AnsibleURL: "",
AWSProfile: "",
AWSRegion: "",
AWSInstanceIDs: "",
AWSInstanceTags: "",
AnsibleExtraVars: "",
CommandResultMaxWait: 30,
CommandExecMaxWait: 300,
AnsibleDryRun: false,
}
}

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.CommandResultMaxWait, "max-wait", c.CommandResultMaxWait, "maximum wait time in seconds for command execution")
flag.Int64Var(&c.CommandExecMaxWait, "max-exec", c.CommandExecMaxWait, "maximum command execution time in seconds")
flag.BoolVar(&c.AnsibleDryRun, "dry-run", c.AnsibleDryRun, "run ansible in dry-run mode")
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.AnsibleURL, "ansible-url", c.AnsibleURL,
"ansible url where the playbook can be read from",
)
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.CommandResultMaxWait, "max-wait", c.CommandResultMaxWait,
"maximum wait time in seconds for command execution",
)
flag.Int64Var(&c.CommandExecMaxWait, "max-exec", c.CommandExecMaxWait,
"maximum command execution time in seconds",
)
flag.BoolVar(&c.AnsibleDryRun, "dry-run", c.AnsibleDryRun,
"run ansible in dry-run mode",
)
flag.StringVar(&c.AnsibleExtraVars, "extra-vars", c.AnsibleExtraVars,
"comma separated key value pairs for extra vars (foo=bar,fus=baz)",
)
flag.Parse()
}

Expand Down
Loading

0 comments on commit 310d864

Please sign in to comment.