Skip to content

Commit

Permalink
cmd: exit 1 if cdk, cloudformation or terraform fail
Browse files Browse the repository at this point in the history
If the diff or deploy fail then we want to ensure that telophase exits
with a non-zero status code.

This makes it easier to use in a script with set -e to show up as red
when telophasecli is used in CI
  • Loading branch information
dschofie committed May 8, 2024
1 parent 9bc3d8d commit 8e133b3
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 15 deletions.
22 changes: 17 additions & 5 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package cmd

import (
"fmt"
"log"
"os"
"strings"

"github.com/santiago-labs/telophasecli/cmd/runner"
"github.com/santiago-labs/telophasecli/resourceoperation"
"golang.org/x/sync/errgroup"

"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -34,20 +37,29 @@ var deployCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {

if err := validateTargets(); err != nil {
fmt.Println(err)
return
log.Fatal("error validating targets err:", err)
}
var consoleUI runner.ConsoleUI
parsedTargets := filterEmptyStrings(strings.Split(targets, ","))
var g errgroup.Group

if useTUI {
consoleUI = runner.NewTUI()
go ProcessOrgEndToEnd(consoleUI, resourceoperation.Deploy, parsedTargets)
g.Go(func() error {
return ProcessOrgEndToEnd(consoleUI, resourceoperation.Deploy, parsedTargets)
})
} else {
consoleUI = runner.NewSTDOut()
ProcessOrgEndToEnd(consoleUI, resourceoperation.Deploy, parsedTargets)
if err := ProcessOrgEndToEnd(consoleUI, resourceoperation.Deploy, parsedTargets); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

consoleUI.Start()

if err := g.Wait(); err != nil {
fmt.Println(err)
os.Exit(1)
}
},
}
20 changes: 16 additions & 4 deletions cmd/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package cmd

import (
"fmt"
"log"
"os"
"strings"

"github.com/santiago-labs/telophasecli/cmd/runner"
"github.com/santiago-labs/telophasecli/resourceoperation"
"golang.org/x/sync/errgroup"

"github.com/spf13/cobra"
)
Expand All @@ -25,20 +28,29 @@ var diffCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {

if err := validateTargets(); err != nil {
fmt.Println(err)
return
log.Fatal("error validating targets err:", err)
}
var consoleUI runner.ConsoleUI
parsedTargets := filterEmptyStrings(strings.Split(targets, ","))

var g errgroup.Group

if useTUI {
consoleUI = runner.NewTUI()
go ProcessOrgEndToEnd(consoleUI, resourceoperation.Diff, parsedTargets)
g.Go(func() error {
return ProcessOrgEndToEnd(consoleUI, resourceoperation.Diff, parsedTargets)
})
} else {
consoleUI = runner.NewSTDOut()
ProcessOrgEndToEnd(consoleUI, resourceoperation.Diff, parsedTargets)
if err := ProcessOrgEndToEnd(consoleUI, resourceoperation.Diff, parsedTargets); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

consoleUI.Start()
if err := g.Wait(); err != nil {
os.Exit(1)
}
},
}
10 changes: 9 additions & 1 deletion cmd/iac.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ func runIAC(
consoleUI runner.ConsoleUI,
cmd int,
accts []resource.Account,
) {
) error {
var wg sync.WaitGroup

var once sync.Once
var retError error

for i := range accts {
wg.Add(1)
go func(acct resource.Account) {
Expand All @@ -40,6 +43,9 @@ func runIAC(

for _, op := range ops {
if err := op.Call(ctx); err != nil {
once.Do(func() {
retError = err
})
consoleUI.Print(fmt.Sprintf("%v", err), acct)
return
}
Expand All @@ -48,6 +54,8 @@ func runIAC(
}

wg.Wait()

return retError
}
func contains(e string, s []string) bool {
for _, a := range s {
Expand Down
27 changes: 22 additions & 5 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,28 @@ func Execute() {
}
}

func ProcessOrgEndToEnd(consoleUI runner.ConsoleUI, cmd int, targets []string) {
func setOpsError() error {
return fmt.Errorf("error running operations")
}

func ProcessOrgEndToEnd(consoleUI runner.ConsoleUI, cmd int, targets []string) error {
ctx := context.Background()
orgClient := awsorgs.New()
rootAWSOU, err := ymlparser.NewParser(orgClient).ParseOrganization(ctx, orgFile)
if err != nil {
consoleUI.Print(fmt.Sprintf("error: %s", err), resource.Account{AccountID: "error", AccountName: "error"})
return
return oops.Wrapf(err, "ParseOrg")
}

if rootAWSOU == nil {
consoleUI.Print("Could not parse AWS Organization", resource.Account{AccountID: "error", AccountName: "error"})
return
return oops.Errorf("No root AWS OU")
}

mgmtAcct, err := resolveMgmtAcct(ctx, orgClient, rootAWSOU)
if err != nil {
consoleUI.Print(fmt.Sprintf("Could not fetch AWS Management Account: %s", err), resource.Account{AccountID: "error", AccountName: "error"})
return
return oops.Wrapf(err, "resolveMgmtAcct")
}

var deployStacks bool
Expand All @@ -72,6 +76,11 @@ func ProcessOrgEndToEnd(consoleUI runner.ConsoleUI, cmd int, targets []string) {
}
}

// opsError is the error we return eventually. We want to allow partially
// applied operations across organizations, IaC, and SCPs so we only return
// this error in the end.
var opsError error

if len(targets) == 0 || deployOrganization {
orgOps := resourceoperation.CollectOrganizationUnitOps(
ctx, consoleUI, orgClient, mgmtAcct, rootAWSOU, cmd,
Expand All @@ -88,6 +97,7 @@ func ProcessOrgEndToEnd(consoleUI runner.ConsoleUI, cmd int, targets []string) {
err := op.Call(ctx)
if err != nil {
consoleUI.Print(fmt.Sprintf("Error on AWS Organization Operation: %v", err), *mgmtAcct)
opsError = setOpsError()
}
}
}
Expand All @@ -105,7 +115,11 @@ func ProcessOrgEndToEnd(consoleUI runner.ConsoleUI, cmd int, targets []string) {
consoleUI.Print("No accounts to deploy.", *mgmtAcct)
}

runIAC(ctx, consoleUI, cmd, accountsToApply)
err := runIAC(ctx, consoleUI, cmd, accountsToApply)
if err != nil {
consoleUI.Print("No accounts to deploy.", *mgmtAcct)
opsError = setOpsError()
}
}

if len(targets) == 0 || deploySCP {
Expand All @@ -124,14 +138,17 @@ func ProcessOrgEndToEnd(consoleUI runner.ConsoleUI, cmd int, targets []string) {
err := op.Call(ctx)
if err != nil {
consoleUI.Print(fmt.Sprintf("Error on SCP Operation: %v", err), *scpAdmin)
opsError = setOpsError()
}
}

if len(scpOps) == 0 {
consoleUI.Print("No Service Control Policies to deploy.", *scpAdmin)
}
}

consoleUI.Print("Done.\n", *mgmtAcct)
return opsError
}

func validateTargets() error {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/samsarahq/go/oops v0.0.0-20220211150445-4b291d6feac4
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
gopkg.in/yaml.v3 v3.0.1
)

Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
Expand Down

0 comments on commit 8e133b3

Please sign in to comment.