Skip to content

Commit

Permalink
Merge pull request #175 from jrasell/f_gh_58
Browse files Browse the repository at this point in the history
Addition of template functions allow Consul and Time lookups.
  • Loading branch information
jrasell committed May 21, 2018
2 parents 14d10be + 9502d0f commit ecb9b0b
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 40 deletions.
21 changes: 21 additions & 0 deletions client/consul.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package client

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

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

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

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

return c, nil
}
8 changes: 7 additions & 1 deletion command/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ General Options:
-canary-auto-promote=<seconds>
The time in seconds, after which Levant will auto-promote a canary job
if all canaries within the deployment are healthy.
-consul-address=<addr>
The Consul host and port to use when making Consul KeyValue lookups for
template rendering.
-force-batch
Forces a new instance of the periodic job. A new instance will be created
Expand Down Expand Up @@ -73,13 +77,15 @@ func (c *DeployCommand) Synopsis() string {
func (c *DeployCommand) Run(args []string) int {

var err error
var addr string
config := &structs.Config{}

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

flags.StringVar(&config.Addr, "address", "", "")
flags.IntVar(&config.Canary, "canary-auto-promote", 0, "")
flags.StringVar(&addr, "consul-address", "", "")
flags.BoolVar(&config.ForceBatch, "force-batch", false, "")
flags.BoolVar(&config.ForceCount, "force-count", false, "")
flags.StringVar(&config.LogLevel, "log-level", "INFO", "")
Expand Down Expand Up @@ -110,7 +116,7 @@ func (c *DeployCommand) Run(args []string) int {
return 1
}

config.Job, err = template.RenderJob(config.TemplateFile, config.VaiableFile, &c.Meta.flagVars)
config.Job, err = template.RenderJob(config.TemplateFile, config.VaiableFile, addr, &c.Meta.flagVars)
if err != nil {
c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
return 1
Expand Down
4 changes: 2 additions & 2 deletions command/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func TestDeploy_checkCanaryAutoPromote(t *testing.T) {
}

for i, c := range cases {
job, err := template.RenderJob(c.File, "", &fVars)
job, err := template.RenderJob(c.File, "", "", &fVars)
if err != nil {
t.Fatalf("case %d failed: %v", i, err)
}
Expand Down Expand Up @@ -61,7 +61,7 @@ func TestDeploy_checkForceBatch(t *testing.T) {
}

for i, c := range cases {
job, err := template.RenderJob(c.File, "", &fVars)
job, err := template.RenderJob(c.File, "", "", &fVars)
if err != nil {
t.Fatalf("case %d failed: %v", i, err)
}
Expand Down
9 changes: 7 additions & 2 deletions command/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ Arguments:
If no argument is given we look for a single *.nomad file
General Options:
-consul-address=<addr>
The Consul host and port to use when making Consul KeyValue lookups for
template rendering.
-out=<file>
Specify the path to write the rendered template out to, if a file exists at
Expand All @@ -49,13 +53,14 @@ func (c *RenderCommand) Synopsis() string {
// Run triggers a run of the Levant template functions.
func (c *RenderCommand) Run(args []string) int {

var variables, outPath, templateFile string
var addr, variables, outPath, templateFile string
var err error
var tpl *bytes.Buffer

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

flags.StringVar(&addr, "consul-address", "", "")
flags.StringVar(&variables, "var-file", "", "")
flags.StringVar(&outPath, "out", "", "")

Expand All @@ -78,7 +83,7 @@ func (c *RenderCommand) Run(args []string) int {
return 1
}

tpl, err = template.RenderTemplate(templateFile, variables, &c.Meta.flagVars)
tpl, err = template.RenderTemplate(templateFile, variables, addr, &c.Meta.flagVars)
if err != nil {
c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
return 1
Expand Down
119 changes: 119 additions & 0 deletions template/funcs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package template

import (
"errors"
"text/template"
"time"

consul "github.com/hashicorp/consul/api"
"github.com/rs/zerolog/log"
)

// funcMap builds the template functions and passes the consulClient where this
// is required.
func funcMap(consulClient *consul.Client) template.FuncMap {
return template.FuncMap{
"consulKey": consulKeyFunc(consulClient),
"consulKeyExists": consulKeyExistsFunc(consulClient),
"consulKeyOrDefault": consulKeyOrDefaultFunc(consulClient),
"timeNow": timeNowFunc,
"timeNowUTC": timeNowUTCFunc,
"timeNowTimezone": timeNowTimezoneFunc(),
}
}

func consulKeyFunc(consulClient *consul.Client) func(string) (string, error) {
return func(s string) (string, error) {

if len(s) == 0 {
return "", nil
}

kv, _, err := consulClient.KV().Get(s, nil)
if err != nil {
return "", err
}

if kv == nil {
return "", errors.New("Consul KV not found")
}

v := string(kv.Value[:])
log.Info().Msgf("template/funcs: using Consul KV variable with key %s and value %s",
s, v)

return v, nil
}
}

func consulKeyExistsFunc(consulClient *consul.Client) func(string) (bool, error) {
return func(s string) (bool, error) {

if len(s) == 0 {
return false, nil
}

kv, _, err := consulClient.KV().Get(s, nil)
if err != nil {
return false, err
}

if kv == nil {
return false, nil
}

log.Info().Msgf("template/funcs: found Consul KV variable with key %s", s)

return true, nil
}
}

func consulKeyOrDefaultFunc(consulClient *consul.Client) func(string, string) (string, error) {
return func(s, d string) (string, error) {

if len(s) == 0 {
log.Info().Msgf("template/funcs: using default Consul KV variable with value %s", d)
return d, nil
}

kv, _, err := consulClient.KV().Get(s, nil)
if err != nil {
return "", err
}

if kv == nil {
log.Info().Msgf("template/funcs: using default Consul KV variable with value %s", d)
return d, nil
}

v := string(kv.Value[:])
log.Info().Msgf("template/funcs: using Consul KV variable with key %s and value %s",
s, v)

return v, nil
}
}

func timeNowFunc() string {
return time.Now().Format("2006-01-02T15:04:05Z07:00")
}

func timeNowUTCFunc() string {
return time.Now().UTC().Format("2006-01-02T15:04:05Z07:00")
}

func timeNowTimezoneFunc() func(string) (string, error) {
return func(t string) (string, error) {

if t == "" {
return "", nil
}

loc, err := time.LoadLocation(t)
if err != nil {
return "", err
}

return time.Now().In(loc).Format("2006-01-02T15:04:05Z07:00"), nil
}
}
Loading

0 comments on commit ecb9b0b

Please sign in to comment.