Skip to content

Commit

Permalink
Merge pull request #172 from jrasell/f_gh_124
Browse files Browse the repository at this point in the history
Addition of scale-out and scale-in commands.
  • Loading branch information
jrasell committed May 17, 2018
2 parents 20f24e4 + 82f70dc commit 71cbe30
Show file tree
Hide file tree
Showing 11 changed files with 569 additions and 29 deletions.
21 changes: 21 additions & 0 deletions client/nomad.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package client

import (
nomad "github.com/hashicorp/nomad/api"
)

// NewNomadClient is used to create a new client to interact with Nomad.
func NewNomadClient(addr string) (*nomad.Client, error) {
config := nomad.DefaultConfig()

if addr != "" {
config.Address = addr
}

c, err := nomad.NewClient(config)
if err != nil {
return nil, err
}

return c, nil
}
2 changes: 1 addition & 1 deletion command/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func (c *DeployCommand) Run(args []string) int {
}
}

success := levant.TriggerDeployment(config)
success := levant.TriggerDeployment(config, nil)
if !success {
return 1
}
Expand Down
118 changes: 118 additions & 0 deletions command/scale_in.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package command

import (
"strings"

"github.com/jrasell/levant/levant/structs"
"github.com/jrasell/levant/logging"
"github.com/jrasell/levant/scale"
)

// ScaleInCommand is the command implementation that allows users to scale a
// Nomad job out.
type ScaleInCommand struct {
Meta
}

// Help provides the help information for the scale-in command.
func (c *ScaleInCommand) Help() string {
helpText := `
Usage: levant scale-in [options] <job-id>
Scale a Nomad job and optional task group out.
General Options:
-address=<http_address>
The Nomad HTTP API address including port which Levant will use to make
calls.
-log-level=<level>
Specify the verbosity level of Levant's logs. Valid values include DEBUG,
INFO, and WARN, in decreasing order of verbosity. The default is INFO.
-log-format=<format>
Specify the format of Levant's logs. Valid values are HUMAN or JSON. The
default is HUMAN.
Scale In Options:
-count=<num>
The count by which the job and task groups should be scaled in by. Only
one of count or percent can be passed.
-percent=<num>
A percentage value by which the job and task groups should be scaled in
by. Counts will be rounded up, to ensure required capacity is met. Only
one of count or percent can be passed.
-task-group=<name>
The name of the task group you wish to target for scaling. Is this is not
speicified all task groups within the job will be scaled.
`
return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the scale-in command.
func (c *ScaleInCommand) Synopsis() string {
return "Scale in a Nomad job"
}

// Run triggers a run of the Levant scale-in functions.
func (c *ScaleInCommand) Run(args []string) int {

var err error
var logL, logF string

config := &structs.ScalingConfig{}
config.Direction = structs.ScalingDirectionIn

flags := c.Meta.FlagSet("scale-in", FlagSetVars)
flags.Usage = func() { c.UI.Output(c.Help()) }

flags.StringVar(&config.Addr, "address", "", "")
flags.StringVar(&logL, "log-level", "INFO", "")
flags.StringVar(&logF, "log-format", "HUMAN", "")
flags.IntVar(&config.Count, "count", 0, "")
flags.IntVar(&config.Percent, "percent", 0, "")
flags.StringVar(&config.TaskGroup, "task-group", "", "")

if err = flags.Parse(args); err != nil {
return 1
}

args = flags.Args()

if len(args) != 1 {
c.UI.Error("This command takes one argument: <job-name>")
return 1
}

config.JobID = args[0]

if config.Count == 0 && config.Percent == 0 || config.Count > 0 && config.Percent > 0 ||
config.Count < 0 || config.Percent < 0 {
c.UI.Error("You must set either -count or -percent flag to scale-in")
return 1
}

if config.Count > 0 {
config.DirectionType = structs.ScalingDirectionTypeCount
}

if config.Percent > 0 {
config.DirectionType = structs.ScalingDirectionTypePercent
}

if err = logging.SetupLogger(logL, logF); err != nil {
c.UI.Error(err.Error())
return 1
}

success := scale.TriggerScalingEvent(config)
if !success {
return 1
}

return 0
}
117 changes: 117 additions & 0 deletions command/scale_out.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package command

import (
"strings"

"github.com/jrasell/levant/levant/structs"
"github.com/jrasell/levant/logging"
"github.com/jrasell/levant/scale"
)

// ScaleOutCommand is the command implementation that allows users to scale a
// Nomad job out.
type ScaleOutCommand struct {
Meta
}

// Help provides the help information for the scale-out command.
func (c *ScaleOutCommand) Help() string {
helpText := `
Usage: levant scale-out [options] <job-id>
Scale a Nomad job and optional task group out.
General Options:
-address=<http_address>
The Nomad HTTP API address including port which Levant will use to make
calls.
-log-level=<level>
Specify the verbosity level of Levant's logs. Valid values include DEBUG,
INFO, and WARN, in decreasing order of verbosity. The default is INFO.
-log-format=<format>
Specify the format of Levant's logs. Valid values are HUMAN or JSON. The
default is HUMAN.
Scale Out Options:
-count=<num>
The count by which the job and task groups should be scaled out by. Only
one of count or percent can be passed.
-percent=<num>
A percentage value by which the job and task groups should be scaled out
by. Counts will be rounded up, to ensure required capacity is met. Only
one of count or percent can be passed.
-task-group=<name>
The name of the task group you wish to target for scaling. Is this is not
speicified all task groups within the job will be scaled.
`
return strings.TrimSpace(helpText)
}

// Synopsis is provides a brief summary of the scale-out command.
func (c *ScaleOutCommand) Synopsis() string {
return "Scale out a Nomad job"
}

// Run triggers a run of the Levant scale-out functions.
func (c *ScaleOutCommand) Run(args []string) int {

var err error
var logL, logF string

config := &structs.ScalingConfig{}
config.Direction = structs.ScalingDirectionOut

flags := c.Meta.FlagSet("scale-out", FlagSetVars)
flags.Usage = func() { c.UI.Output(c.Help()) }

flags.StringVar(&config.Addr, "address", "", "")
flags.StringVar(&logL, "log-level", "INFO", "")
flags.StringVar(&logF, "log-format", "HUMAN", "")
flags.IntVar(&config.Count, "count", 0, "")
flags.IntVar(&config.Percent, "percent", 0, "")
flags.StringVar(&config.TaskGroup, "task-group", "", "")

if err = flags.Parse(args); err != nil {
return 1
}

args = flags.Args()

if len(args) != 1 {
c.UI.Error("This command takes one argument: <job-name>")
return 1
}

config.JobID = args[0]

if config.Count == 0 && config.Percent == 0 || config.Count > 0 && config.Percent > 0 {
c.UI.Error("You must set either -count or -percent flag to scale-out")
return 1
}

if config.Count > 0 {
config.DirectionType = structs.ScalingDirectionTypeCount
}

if config.Percent > 0 {
config.DirectionType = structs.ScalingDirectionTypePercent
}

if err = logging.SetupLogger(logL, logF); err != nil {
c.UI.Error(err.Error())
return 1
}

success := scale.TriggerScalingEvent(config)
if !success {
return 1
}

return 0
}
10 changes: 10 additions & 0 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
Meta: meta,
}, nil
},
"scale-in": func() (cli.Command, error) {
return &command.ScaleInCommand{
Meta: meta,
}, nil
},
"scale-out": func() (cli.Command, error) {
return &command.ScaleOutCommand{
Meta: meta,
}, nil
},
"version": func() (cli.Command, error) {
ver := version.Version
rel := version.VersionPrerelease
Expand Down
39 changes: 12 additions & 27 deletions levant/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,63 +7,48 @@ import (

nomad "github.com/hashicorp/nomad/api"
nomadStructs "github.com/hashicorp/nomad/nomad/structs"
"github.com/jrasell/levant/client"
"github.com/jrasell/levant/levant/structs"
"github.com/rs/zerolog/log"
)

const (
jobIDContextField = "job_id"
)

// levantDeployment is the all deployment related objects for this Levant
// deployment invoction.
type levantDeployment struct {
nomad *nomad.Client
config *structs.Config
}

// newNomadClient is used to create a new client to interact with Nomad.
func newNomadClient(addr string) (*nomad.Client, error) {
config := nomad.DefaultConfig()

if addr != "" {
config.Address = addr
}

c, err := nomad.NewClient(config)
if err != nil {
return nil, err
}

return c, nil
}

// newLevantDeployment sets up the Levant deployment object and Nomad client
// to interact with the Nomad API.
func newLevantDeployment(config *structs.Config) (*levantDeployment, error) {
func newLevantDeployment(config *structs.Config, nomadClient *nomad.Client) (*levantDeployment, error) {

var err error

dep := &levantDeployment{}
dep.config = config

dep.nomad, err = newNomadClient(config.Addr)
if err != nil {
return nil, err
if nomadClient == nil {
dep.nomad, err = client.NewNomadClient(config.Addr)
if err != nil {
return nil, err
}
} else {
dep.nomad = nomadClient
}

// Add the JobID as a log context field.
log.Logger = log.With().Str(jobIDContextField, *config.Job.ID).Logger()
log.Logger = log.With().Str(structs.JobIDContextField, *config.Job.ID).Logger()

return dep, nil
}

// TriggerDeployment provides the main entry point into a Levant deployment and
// is used to setup the clients before triggering the deployment process.
func TriggerDeployment(config *structs.Config) bool {
func TriggerDeployment(config *structs.Config, nomadClient *nomad.Client) bool {

// Create our new deployment object.
levantDep, err := newLevantDeployment(config)
levantDep, err := newLevantDeployment(config, nomadClient)
if err != nil {
log.Error().Err(err).Msg("levant/deploy: unable to setup Levant deployment")
return false
Expand Down
3 changes: 2 additions & 1 deletion levant/dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package levant

import (
nomad "github.com/hashicorp/nomad/api"
"github.com/jrasell/levant/client"
"github.com/jrasell/levant/levant/structs"
"github.com/rs/zerolog/log"
)
Expand All @@ -10,7 +11,7 @@ import (
// is used to setup the clients before triggering the dispatch process.
func TriggerDispatch(job string, metaMap map[string]string, payload []byte, address string) bool {

client, err := newNomadClient(address)
client, err := client.NewNomadClient(address)
if err != nil {
log.Error().Msgf("levant/dispatch: unable to setup Levant dispatch: %v", err)
return false
Expand Down
6 changes: 6 additions & 0 deletions levant/structs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ package structs

import nomad "github.com/hashicorp/nomad/api"

const (
// JobIDContextField is the logging context feild added when interacting
// with jobs.
JobIDContextField = "job_id"
)

// Config is the main struct used to configure and run a Levant deployment on
// a given target job.
type Config struct {
Expand Down
Loading

0 comments on commit 71cbe30

Please sign in to comment.