Skip to content

Commit

Permalink
Resolves #386 - Fixes help screen crash and improves start up perform…
Browse files Browse the repository at this point in the history
…ance by deferring runbook validation to actual execution
  • Loading branch information
steve-r-west committed Oct 7, 2023
1 parent 44a7371 commit 91354b4
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 88 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ The following is a summary of the main commands, in general you can type `epcc h
| `epcc test-json [KEY] [VAL] [KEY] [VAL] ...` | Render a JSON document based on the supplied key and value pairs |

#### Power User Commands
| Command | Description |
|-----------------------------------------|--------------------------------------------------------------|
| `epcc reset-store <STORE_ID>` | Reset the store to an initial state (on a best effort basis) |
| `epcc runbooks show <RUNBOOK> <ACTION>` | Show a specific runbook (script) |
| `epcc runbooks run <RUNBOOK> <ACTION>` | Run a specific runbook (script) |
| Command | Description |
|-----------------------------------------|----------------------------------------------------------------------------|
| `epcc reset-store <STORE_ID>` | Reset the store to an initial state (on a best effort basis) |
| `epcc runbooks show <RUNBOOK> <ACTION>` | Show a specific runbook (script) |
| `epcc runbooks validate` | Validates all runbooks (built in and user supplied, outputting any errors) |
| `epcc runbooks run <RUNBOOK> <ACTION>` | Run a specific runbook (script) |

#### Tuning Runbooks

Expand Down
54 changes: 30 additions & 24 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ var profileNameFromCommandLine = ""
func InitializeCmd() {

os.Args = misc.AddImplicitDoubleDash(os.Args)
if os.Args[1] == "__complete" {
if len(os.Args) > 1 && os.Args[1] == "__complete" {
DisableLongOutput = true
DisableExampleOutput = true
}
Expand Down Expand Up @@ -98,6 +98,7 @@ func InitializeCmd() {

Logs.AddCommand(LogsList, LogsShow, LogsClear)

testJson.ResetFlags()
testJson.Flags().BoolVarP(&noWrapping, "no-wrapping", "", false, "if set, we won't wrap the output the json in a data tag")
testJson.Flags().BoolVarP(&compliant, "compliant", "", false, "if set, we wrap most keys in an attributes tags automatically.")

Expand All @@ -115,6 +116,7 @@ func InitializeCmd() {
RootCmd.PersistentFlags().Uint16VarP(&statisticsFrequency, "statistics-frequency", "", 15, "How often to print runtime statistics (0 turns them off)")

RootCmd.PersistentFlags().BoolVarP(&aliases.SkipAliasProcessing, "skip-alias-processing", "", false, "if set, we don't process the response for aliases")
ResetStore.ResetFlags()
ResetStore.PersistentFlags().BoolVarP(&DeleteApplicationKeys, "delete-application-keys", "", false, "if set, we delete application keys as well")

aliasesCmd.AddCommand(aliasListCmd, aliasClearCmd)
Expand Down Expand Up @@ -170,10 +172,13 @@ func AddRootPreRunFunc(f func(cmd *cobra.Command, args []string) error) {
persistentPreRunFuncs = append(persistentPreRunFuncs, f)
}

var RootCmd = &cobra.Command{
Use: os.Args[0],
Short: "A command line interface for interacting with the Elastic Path Commerce Cloud API",
Long: `The EPCC CLI tool provides a powerful command line interface for interacting with the Elastic Path Commerce Cloud API.
var RootCmd = GetRootCommand()

func GetRootCommand() *cobra.Command {
return &cobra.Command{
Use: os.Args[0],
Short: "A command line interface for interacting with the Elastic Path Commerce Cloud API",
Long: `The EPCC CLI tool provides a powerful command line interface for interacting with the Elastic Path Commerce Cloud API.
The EPCC CLI tool uses environment variables for configuration and in particular a tool like https://direnv.net/ which
auto populates your shell with environment variables when you switch directories. This allows you to store a context in a folder,
Expand All @@ -189,30 +194,31 @@ Environment Variables
- EPCC_PROFILE - The name of the profile we will use (isolates namespace, credentials, etc...)
`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
log.SetLevel(logger.Loglevel)

env := config.GetEnv()
if env.EPCC_RATE_LIMIT != 0 {
rateLimit = env.EPCC_RATE_LIMIT
}
log.Debugf("Rate limit set to %d request per second, printing statistics every %d seconds ", rateLimit, statisticsFrequency)
httpclient.Initialize(rateLimit, requestTimeout, int(statisticsFrequency))
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
log.SetLevel(logger.Loglevel)

for _, runFunc := range persistentPreRunFuncs {
err := runFunc(cmd, args)
if err != nil {
return err
env := config.GetEnv()
if env.EPCC_RATE_LIMIT != 0 {
rateLimit = env.EPCC_RATE_LIMIT
}
log.Debugf("Rate limit set to %d request per second, printing statistics every %d seconds ", rateLimit, statisticsFrequency)
httpclient.Initialize(rateLimit, requestTimeout, int(statisticsFrequency))

for _, runFunc := range persistentPreRunFuncs {
err := runFunc(cmd, args)
if err != nil {
return err
}
}
}

version.CheckVersionChangeAndLogWarning()
version.CheckVersionChangeAndLogWarning()

return nil
},
return nil
},

SilenceUsage: true,
Version: fmt.Sprintf("%s (Commit %s)", version.Version, version.Commit),
SilenceUsage: true,
Version: fmt.Sprintf("%s (Commit %s)", version.Version, version.Commit),
}
}

func Execute() {
Expand Down
16 changes: 16 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cmd

import (
"github.com/elasticpath/epcc-cli/external/resources"
"github.com/elasticpath/epcc-cli/external/runbooks"
"testing"
)

func BenchmarkRootCommandBootstrap(b *testing.B) {
for i := 0; i < b.N; i++ {
runbooks.Reset()
RootCmd = GetRootCommand()
resources.PublicInit()
InitializeCmd()
}
}
54 changes: 54 additions & 0 deletions cmd/runbooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,41 @@ func initRunbookCommands() {
runbookGlobalCmd.AddCommand(initRunbookShowCommands())
runbookGlobalCmd.AddCommand(initRunbookRunCommands())
runbookGlobalCmd.AddCommand(initRunbookDevCommands())
runbookGlobalCmd.AddCommand(initRunbookValidateCommands())
}

var AbortRunbookExecution = atomic.Bool{}

func initRunbookValidateCommands() *cobra.Command {
// epcc runbook validate
runbookValidateCommand := &cobra.Command{
Use: "validate",
Short: "Validates all runbooks",
RunE: func(cmd *cobra.Command, args []string) error {
errMsg := ""

for _, runbook := range runbooks.GetRunbooks() {
log.Debugf("Validating runbook %s", runbook.Name)
err := runbooks.ValidateRunbook(&runbook)

if err != nil {
newErr := fmt.Errorf("validation of runbook '%s' failed: %v", runbook.Name, err)
errMsg += newErr.Error() + "\n"
}
}

if errMsg == "" {
log.Infof("All runbooks are valid!")
return nil
} else {
return fmt.Errorf("one or more runbooks failed validation\n:%s", errMsg)
}
},
}

return runbookValidateCommand
}

func initRunbookShowCommands() *cobra.Command {

// epcc runbook show
Expand All @@ -56,6 +87,18 @@ func initRunbookShowCommands() *cobra.Command {
Long: runbook.Description.Long,
Short: runbook.Description.Short,
}

runbookShowRunbookCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
log.Debugf("Validating runbook %s", runbook.Name)
err := runbooks.ValidateRunbook(&runbook)

if err != nil {
return fmt.Errorf("validation of runbook '%s' failed: %v", runbook.Name, err)
}

return nil
}

runbookShowCommand.AddCommand(runbookShowRunbookCmd)

for _, runbookAction := range runbook.RunbookActions {
Expand All @@ -70,6 +113,7 @@ func initRunbookShowCommands() *cobra.Command {
Long: runbookAction.Description.Long,
Short: runbookAction.Description.Short,
RunE: func(cmd *cobra.Command, args []string) error {

for stepIdx, cmd := range runbookAction.RawCommands {
templateName := fmt.Sprintf("Runbook: %s Action: %s Step: %d", runbook.Name, runbookAction.Name, stepIdx)

Expand Down Expand Up @@ -128,6 +172,16 @@ func initRunbookRunCommands() *cobra.Command {
Short: runbook.Description.Short,
}

runbookRunRunbookCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
err := runbooks.ValidateRunbook(&runbook)

if err != nil {
return fmt.Errorf("validation of runbook '%s' failed: %v", runbook.Name, err)
}

return nil
}

runbookRunCommand.AddCommand(runbookRunRunbookCmd)

for _, runbookAction := range runbook.RunbookActions {
Expand Down
4 changes: 4 additions & 0 deletions external/resources/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ func AppendResourceData(newResources map[string]Resource) {
}

func init() {
PublicInit()
}

func PublicInit() {

resourceData, err := GenerateResourceMetadataFromYaml(resourceMetaData)

Expand Down
2 changes: 0 additions & 2 deletions external/runbooks/hello-world.epcc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,3 @@ actions:
epcc delete customer [email protected]
epcc delete customer [email protected]
epcc delete customer [email protected]
3 changes: 3 additions & 0 deletions external/runbooks/run-all-runbooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
set -e
set -x

#Let's test that epcc command works after an embarrassing bug that caused it to panic :(
epcc

epcc reset-store .+

echo "Starting Misc Runbook"
Expand Down
4 changes: 2 additions & 2 deletions external/runbooks/runbook_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"strings"
)

func validateRunbook(runbook *Runbook) error {
func ValidateRunbook(runbook *Runbook) error {

if len(runbook.Name) == 0 {
return fmt.Errorf("Runbook has no name")
Expand All @@ -19,7 +19,7 @@ func validateRunbook(runbook *Runbook) error {

for _, runbookAction := range runbook.RunbookActions {
if (len(runbookAction.RawCommands)) == 0 {
return fmt.Errorf("number of commands in action %s is zero", runbookAction.Name)
return fmt.Errorf("number of commands in action '%s' is zero", runbookAction.Name)
}

argumentsWithDefaults := CreateMapForRunbookArgumentPointers(runbookAction)
Expand Down
Loading

0 comments on commit 91354b4

Please sign in to comment.