Skip to content

Commit

Permalink
[cli] Add flags to change exit codes on plan command (#236)
Browse files Browse the repository at this point in the history
  • Loading branch information
angrycub authored Apr 12, 2022
1 parent c1b2b50 commit 0d97c6d
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ IMPROVEMENTS:
* cli: Add flags to configure Nomad API client [[GH-213](https://github.com/hashicorp/nomad-pack/pull/213)]
* template: Add support for custom Spew configurations. [[GH-220](https://github.com/hashicorp/nomad-pack/pull/220)]
* template: Create a `my` alias for the current pack [[GH-221](https://github.com/hashicorp/nomad-pack/pull/221)]
* cli: Add flags to override exit codes on `plan` command [[GH-236](https://github.com/hashicorp/nomad-pack/pull/236)]
* cli: Add environment variables to configure Nomad API client [[GH-230](https://github.com/hashicorp/nomad-pack/pull/230)]


## 0.0.1-techpreview2 (February 07, 2022)

FEATURES:
Expand Down
81 changes: 81 additions & 0 deletions internal/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,75 @@ func TestJobPlanConflictingNonPackJob(t *testing.T) {
})
}

func TestJobPlanOverrideExitCodes(t *testing.T) {
httpTest(t, WithDefaultConfig(), func(s *agent.TestAgent) {
// Plan against empty - should be makes-changes
result := runTestPackCmd(t, s, []string{
"plan",
"--exit-code-makes-changes=91",
"--exit-code-no-changes=90",
"--exit-code-error=92",
getTestPackPath(testPack),
})
require.Empty(t, result.cmdErr.String(), "cmdErr should be empty, but was %q", result.cmdErr.String())
require.Contains(t, result.cmdOut.String(), "Plan succeeded\n")
require.Equal(t, 91, result.exitCode) // Should return exit-code-makes-changes

// Register non pack job
err := NomadRun(s, getTestNomadJobPath(testPack))
require.NoError(t, err)

// Now try to register the pack, should make error
result = runTestPackCmd(t, s, []string{
"plan",
"--exit-code-makes-changes=91",
"--exit-code-no-changes=90",
"--exit-code-error=92",
getTestPackPath(testPack),
})
require.Empty(t, result.cmdErr.String(), "cmdErr should be empty, but was %q", result.cmdErr.String())
require.Contains(t, result.cmdOut.String(), job.ErrExistsNonPack{JobID: testPack}.Error())
require.Equal(t, 92, result.exitCode) // Should exit-code-error

err = NomadPurge(s, testPack)
require.NoError(t, err)

isGone := func() bool {
_, err = NomadJobStatus(s, testPack)
if err != nil {
return err.Error() == "job not found"
}
return false
}
require.Eventually(t, isGone, 10*time.Second, 500*time.Millisecond)

result = runTestPackCmd(t, s, []string{"run", getTestPackPath(testPack)})
require.Empty(t, result.cmdErr.String(), "cmdErr should be empty, but was %q", result.cmdErr.String())
require.Contains(t, result.cmdOut.String(), "")
require.Equal(t, 0, result.exitCode) // Should return 0
isStarted := func() bool {
j, err := NomadJobStatus(s, testPack)
if err != nil {
return false
}
return j.GetStatus() == "running"
}
require.Eventually(t, isStarted, 10*time.Second, 500*time.Millisecond)

// Plan against deployed - should be no-changes
result = runTestPackCmd(t, s, []string{
"plan",
"--exit-code-makes-changes=91",
"--exit-code-no-changes=90",
"--exit-code-error=92",
getTestPackPath(testPack),
})
require.Empty(t, result.cmdErr.String(), "cmdErr should be empty, but was %q", result.cmdErr.String())
require.Contains(t, result.cmdOut.String(), "Plan succeeded\n")
require.Equal(t, 90, result.exitCode) // Should return exit-code-no-changes
})
}

func TestJobStop(t *testing.T) {
httpTestParallel(t, WithDefaultConfig(), func(s *agent.TestAgent) {
expectGoodPackDeploy(t, runTestPackCmd(t, s, []string{"run", getTestPackPath(testPack)}))
Expand Down Expand Up @@ -906,6 +975,18 @@ func NomadRun(s *agent.TestAgent, path string, opts ...v1.ClientOption) error {
return nil
}

func NomadJobStatus(s *agent.TestAgent, jobname string, opts ...v1.ClientOption) (*client.Job, error) {
c, err := NewTestClient(s, opts...)
if err != nil {
return nil, err
}
resp, _, err := c.Jobs().GetJob(c.QueryOpts().Ctx(), jobname)
if err != nil {
return nil, err
}
return resp, nil
}

func NomadStop(s *agent.TestAgent, jobname string, opts ...v1.ClientOption) error {
c, err := NewTestClient(s, opts...)

Expand Down
60 changes: 49 additions & 11 deletions internal/cli/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ import (

type PlanCommand struct {
*baseCommand
packConfig *cache.PackConfig
jobConfig *job.CLIConfig
packConfig *cache.PackConfig
jobConfig *job.CLIConfig
exitCodeNoChanges int
exitCodeChanges int
exitCodeError int
}

func (c *PlanCommand) Run(args []string) int {
c.exitCodeNoChanges = 0
c.exitCodeChanges = 1
c.exitCodeError = 255

c.cmdKey = "plan" // Add cmdKey here to print out helpUsageMessage on Init error
// Initialize. If we fail, we just exit since Init handles the UI.
if err := c.Init(
Expand All @@ -35,7 +42,7 @@ func (c *PlanCommand) Run(args []string) int {

// verify packs exist before planning jobs
if err := cache.VerifyPackExists(c.packConfig, errorContext, c.ui); err != nil {
return 255
return c.exitCodeError
}

// If no deploymentName set default to pack@ref
Expand All @@ -45,22 +52,22 @@ func (c *PlanCommand) Run(args []string) int {
client, err := c.getAPIClient()
if err != nil {
c.ui.ErrorWithContext(err, "failed to initialize client", errorContext.GetAll()...)
return 255
return c.exitCodeError
}

packManager := generatePackManager(c.baseCommand, client, c.packConfig)

// load pack
r, err := renderPack(packManager, c.baseCommand.ui, errorContext)
if err != nil {
return 255
return c.exitCodeError
}

// Commands that render templates are required to render at least one
// parent template.
if r.LenParentRenders() < 1 {
c.ui.ErrorWithContext(errors.ErrNoTemplatesRendered, "no templates rendered", errorContext.GetAll()...)
return 255
return c.exitCodeError
}

depConfig := runner.Config{
Expand All @@ -75,7 +82,7 @@ func (c *PlanCommand) Run(args []string) int {
jobRunner, err := generateRunner(client, "job", c.jobConfig, &depConfig)
if err != nil {
c.ui.ErrorWithContext(err, "failed to generate deployer", errorContext.GetAll()...)
return 255
return c.exitCodeError
}

// Set the rendered templates on the job deployer.
Expand All @@ -87,22 +94,22 @@ func (c *PlanCommand) Run(args []string) int {
validateErr.Context.Append(errorContext)
c.ui.ErrorWithContext(validateErr.Err, validateErr.Subject, validateErr.Context.GetAll()...)
}
return 255
return c.exitCodeError
}

if canonicalizeErrs := jobRunner.CanonicalizeTemplates(); canonicalizeErrs != nil {
for _, canonicalizeErr := range canonicalizeErrs {
canonicalizeErr.Context.Append(errorContext)
c.ui.ErrorWithContext(canonicalizeErr.Err, canonicalizeErr.Subject, canonicalizeErr.Context.GetAll()...)
}
return 1
return c.exitCodeError
}

if conflictErrs := jobRunner.CheckForConflicts(errorContext); conflictErrs != nil {
for _, conflictErr := range conflictErrs {
c.ui.ErrorWithContext(conflictErr.Err, conflictErr.Subject, conflictErr.Context.GetAll()...)
}
return 255
return c.exitCodeError
}

planExitCode, planErrs := jobRunner.PlanDeployment(c.ui, errorContext)
Expand All @@ -113,7 +120,18 @@ func (c *PlanCommand) Run(args []string) int {
if planExitCode < 2 {
c.ui.Success("Plan succeeded")
}
return planExitCode

// Map planExitCode to replacement values.
switch planExitCode {
case 0:
return c.exitCodeNoChanges
case 1:
return c.exitCodeChanges
case 255:
return c.exitCodeError
default: // protect from unexpected new exit codes.
return planExitCode
}
}

func (c *PlanCommand) Flags() *flag.Sets {
Expand Down Expand Up @@ -177,6 +195,26 @@ func (c *PlanCommand) Flags() *flag.Sets {
},
Shorthand: "v",
})
f.IntVar(&flag.IntVar{
Name: "exit-code-no-changes",
Target: &c.exitCodeNoChanges,
Default: 0,
Usage: `Override exit code returned when the plan shown no changes.`,
})

f.IntVar(&flag.IntVar{
Name: "exit-code-makes-changes",
Target: &c.exitCodeChanges,
Default: 1,
Usage: `Override exit code returned when the plan shows changes.`,
})

f.IntVar(&flag.IntVar{
Name: "exit-code-error",
Target: &c.exitCodeError,
Default: 255,
Usage: `Override exit code returned when there is an error.`,
})
})
}

Expand Down

0 comments on commit 0d97c6d

Please sign in to comment.