Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for using jobspec2 parser #398

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion command/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ General Options:
Used in conjunction with the -job-file will deploy a templated job to your
Nomad cluster. You can repeat this flag multiple times to supply multiple var-files.
[default: levant.(json|yaml|yml|tf)]

-hcl2
Use HCL2 jopspec parser.
`
return strings.TrimSpace(helpText)
}
Expand Down Expand Up @@ -125,6 +128,7 @@ func (c *DeployCommand) Run(args []string) int {
flags.StringVar(&format, "log-format", "HUMAN", "")
flags.StringVar(&config.Deploy.VaultToken, "vault-token", "", "")
flags.BoolVar(&config.Deploy.EnvVault, "vault", false, "")
flags.BoolVar(&config.Template.HCL2, "hcl2", false, "")

flags.Var((*helper.FlagStringSlice)(&config.Template.VariableFiles), "var-file", "")

Expand Down Expand Up @@ -159,7 +163,7 @@ func (c *DeployCommand) Run(args []string) int {
}

config.Template.Job, err = template.RenderJob(config.Template.TemplateFile,
config.Template.VariableFiles, config.Client.ConsulAddr, &c.Meta.flagVars)
config.Template.VariableFiles, config.Client.ConsulAddr, &c.Meta.flagVars, config.Template.HCL2)
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, []string{}, "", &fVars)
job, err := template.RenderJob(c.File, []string{}, "", &fVars, false)
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, []string{}, "", &fVars)
job, err := template.RenderJob(c.File, []string{}, "", &fVars, false)
if err != nil {
t.Fatalf("case %d failed: %v", i, err)
}
Expand Down
4 changes: 2 additions & 2 deletions command/dispatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Usage: levant dispatch [options] <parameterized job> [input source]
Dispatch creates an instance of a parameterized job. A data payload to the
dispatched instance can be provided via stdin by using "-" or by specifying a
path to a file. Metadata can be supplied by using the meta flag one or more
times.
times.

General Options:

Expand All @@ -46,7 +46,7 @@ Dispatch Options:
-meta <key>=<value>
Meta takes a key/value pair separated by "=". The metadata key will be
merged into the job's metadata. The job may define a default value for the
key which is overridden when dispatching. The flag can be provided more
key which is overridden when dispatching. The flag can be provided more
than once to inject multiple metadata key/value pairs. Arbitrary keys are
not allowed. The parameterized job must allow the key to be merged.
`
Expand Down
8 changes: 6 additions & 2 deletions command/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ General Options:

-allow-stale
Allow stale consistency mode for requests into nomad.

-consul-address=<addr>
The Consul host and port to use when making Consul KeyValue lookups for
template rendering.
Expand All @@ -68,6 +68,9 @@ General Options:
Used in conjunction with the -job-file will plan a templated job against your
Nomad cluster. You can repeat this flag multiple times to supply multiple var-files.
[default: levant.(json|yaml|yml|tf)]

-hcl2
Use HCL2 jopspec parser.
`
return strings.TrimSpace(helpText)
}
Expand Down Expand Up @@ -97,6 +100,7 @@ func (c *PlanCommand) Run(args []string) int {
flags.BoolVar(&config.Plan.IgnoreNoChanges, "ignore-no-changes", false, "")
flags.StringVar(&level, "log-level", "INFO", "")
flags.StringVar(&format, "log-format", "HUMAN", "")
flags.BoolVar(&config.Template.HCL2, "hcl2", false, "")
flags.Var((*helper.FlagStringSlice)(&config.Template.VariableFiles), "var-file", "")

if err = flags.Parse(args); err != nil {
Expand Down Expand Up @@ -124,7 +128,7 @@ func (c *PlanCommand) Run(args []string) int {
}

config.Template.Job, err = template.RenderJob(config.Template.TemplateFile,
config.Template.VariableFiles, config.Client.ConsulAddr, &c.Meta.flagVars)
config.Template.VariableFiles, config.Client.ConsulAddr, &c.Meta.flagVars, config.Template.HCL2)

if err != nil {
c.UI.Error(fmt.Sprintf("[ERROR] levant/command: %v", err))
Expand Down
4 changes: 2 additions & 2 deletions command/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ func (c *RenderCommand) Help() string {
Usage: levant render [options] [TEMPLATE]

Render a Nomad job template, useful for debugging. Like deploy, the render
command also supports passing variables individually on the command line.
Multiple vars can be passed in the format of -var 'key=value'. Variables
command also supports passing variables individually on the command line.
Multiple vars can be passed in the format of -var 'key=value'. Variables
passed via the command line take precedence over the same variable declared
within a passed variable file.

Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ github.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible/go.mod h1:LDQHRZy
github.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=
Expand Down Expand Up @@ -123,6 +124,7 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmatcuk/doublestar v1.1.5 h1:2bNwBOmhyFEFcoB3tGvTD5xanq+4kyOZlB8wFYbMjkk=
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
Expand Down Expand Up @@ -356,6 +358,7 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-connlimit v0.2.0/go.mod h1:OUj9FGL1tPIhl/2RCfzYHrIiWj+VVPGNyVPnUX8AqS0=
github.com/hashicorp/go-connlimit v0.3.0/go.mod h1:OUj9FGL1tPIhl/2RCfzYHrIiWj+VVPGNyVPnUX8AqS0=
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 h1:kgvybwEeu0SXktbB2y3uLHX9lklLo+nzUwh59A3jzQc=
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
github.com/hashicorp/go-discover v0.0.0-20191202160150-7ec2cfbda7a2/go.mod h1:NnH5X4UCBEBdTuK2L8s4e4ilJm3UmGX0bANHCz0HSs0=
github.com/hashicorp/go-discover v0.0.0-20200812215701-c4b85f6ed31f/go.mod h1:D4eo8/CN92vm9/9UDG+ldX1/fMFa4kpl8qzyTolus8o=
Expand Down Expand Up @@ -794,6 +797,7 @@ github.com/zclconf/go-cty v1.5.1/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0
github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zclconf/go-cty-yaml v1.0.2 h1:dNyg4QLTrv2IfJpm7Wtxi55ed5gLGOlPrZ6kMd51hY0=
github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
Expand Down
3 changes: 3 additions & 0 deletions levant/structs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ type TemplateConfig struct {
// VariableFiles contains the variables which will be substituted into the
// templateFile before deployment.
VariableFiles []string

// HCL2 is a boolean flag that enables using jobspec2 parser
HCL2 bool
}

// ScaleConfig contains all the scaling specific configuration options.
Expand Down
12 changes: 11 additions & 1 deletion template/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/hashicorp/levant/helper"
nomad "github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/jobspec"
"github.com/hashicorp/nomad/jobspec2"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/hcl2shim"
"github.com/rs/zerolog/log"
Expand All @@ -19,13 +20,22 @@ import (

// RenderJob takes in a template and variables performing a render of the
// template followed by Nomad jobspec parse.
func RenderJob(templateFile string, variableFiles []string, addr string, flagVars *map[string]interface{}) (job *nomad.Job, err error) {
func RenderJob(templateFile string, variableFiles []string, addr string, flagVars *map[string]interface{}, hcl2 bool) (job *nomad.Job, err error) {
var tpl *bytes.Buffer
tpl, err = RenderTemplate(templateFile, variableFiles, addr, flagVars)
if err != nil {
return
}

if hcl2 {
alexdulin marked this conversation as resolved.
Show resolved Hide resolved
return jobspec2.ParseWithConfig(&jobspec2.ParseConfig{
Path: templateFile,
Body: tpl.Bytes(),
AllowFS: false,
Strict: true,
})
}

return jobspec.Parse(tpl)
}

Expand Down
32 changes: 24 additions & 8 deletions template/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestTemplater_RenderTemplate(t *testing.T) {
fVars := make(map[string]interface{})

// Test basic TF template render.
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.tf"}, "", &fVars)
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.tf"}, "", &fVars, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -37,7 +37,7 @@ func TestTemplater_RenderTemplate(t *testing.T) {
}

// Test basic YAML template render.
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.yaml"}, "", &fVars)
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.yaml"}, "", &fVars, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -49,7 +49,7 @@ func TestTemplater_RenderTemplate(t *testing.T) {
}

// Test multiple var-files
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.yaml", "test-fixtures/test-overwrite.yaml"}, "", &fVars)
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.yaml", "test-fixtures/test-overwrite.yaml"}, "", &fVars, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -58,7 +58,7 @@ func TestTemplater_RenderTemplate(t *testing.T) {
}

// Test multiple var-files of different types
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.tf", "test-fixtures/test-overwrite.yaml"}, "", &fVars)
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.tf", "test-fixtures/test-overwrite.yaml"}, "", &fVars, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -68,7 +68,7 @@ func TestTemplater_RenderTemplate(t *testing.T) {

// Test multiple var-files with var-args
fVars["job_name"] = testJobNameOverwrite2
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.tf", "test-fixtures/test-overwrite.yaml"}, "", &fVars)
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{"test-fixtures/test.tf", "test-fixtures/test-overwrite.yaml"}, "", &fVars, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -77,7 +77,7 @@ func TestTemplater_RenderTemplate(t *testing.T) {
}

// Test empty var-args and empty variable file render.
job, err = RenderJob("test-fixtures/none_templated.nomad", []string{}, "", &fVars)
job, err = RenderJob("test-fixtures/none_templated.nomad", []string{}, "", &fVars, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -87,7 +87,7 @@ func TestTemplater_RenderTemplate(t *testing.T) {

// Test var-args only render.
fVars = map[string]interface{}{"job_name": testJobName, "task_resource_cpu": "1313"}
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{}, "", &fVars)
job, err = RenderJob("test-fixtures/single_templated.nomad", []string{}, "", &fVars, false)
if err != nil {
t.Fatal(err)
}
Expand All @@ -98,11 +98,27 @@ func TestTemplater_RenderTemplate(t *testing.T) {
t.Fatalf("expected CPU resource %v but got %v", 1313, *job.TaskGroups[0].Tasks[0].Resources.CPU)
}

// Test var-args only render with HCL2 spec
fVars = map[string]interface{}{"job_name": testJobName, "task_resource_cpu": "1313", "upstream_datacenter": "dc2"}
job, err = RenderJob("test-fixtures/single_templated_connect.nomad", []string{}, "", &fVars, true)
if err != nil {
t.Fatal(err)
}
if *job.Name != testJobName {
t.Fatalf("expected %s but got %v", testJobName, *job.Name)
}
if *job.TaskGroups[0].Tasks[0].Resources.CPU != 1313 {
t.Fatalf("expected CPU resource %v but got %v", 1313, *job.TaskGroups[0].Tasks[0].Resources.CPU)
}
if job.TaskGroups[0].Services[0].Connect.SidecarService.Proxy.Upstreams[0].Datacenter != "dc2" {
t.Fatalf("expected connect upstream datacenter %v but got %v", "dc2", job.TaskGroups[0].Services[0].Connect.SidecarService.Proxy.Upstreams[0].Datacenter)
}

// Test var-args and variables file render.
delete(fVars, "job_name")
fVars["datacentre"] = testDCName
os.Setenv(testEnvName, testEnvValue)
job, err = RenderJob("test-fixtures/multi_templated.nomad", []string{"test-fixtures/test.yaml"}, "", &fVars)
job, err = RenderJob("test-fixtures/multi_templated.nomad", []string{"test-fixtures/test.yaml"}, "", &fVars, false)
if err != nil {
t.Fatal(err)
}
Expand Down
72 changes: 72 additions & 0 deletions template/test-fixtures/single_templated_connect.nomad
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
job "[[.job_name]]" {
datacenters = ["dc1"]
type = "service"
update {
max_parallel = 1
min_healthy_time = "10s"
healthy_deadline = "1m"
auto_revert = true
}

group "cache" {
count = 1
restart {
attempts = 10
interval = "5m"
delay = "25s"
mode = "delay"
}
ephemeral_disk {
size = 300
}
network {
mode = "bridge"
}
service {
name = "global-redis-check"
tags = ["global", "cache"]
port = "6379"

connect {
sidecar_service {
proxy {
upstreams {
destination_name = "foobar"
local_bind_port = 9200
datacenter = "[[ .upstream_datacenter ]]"
}
}
}
}

check {
name = "alive"
type = "tcp"
interval = "10s"
timeout = "2s"
}
}

task "redis" {
template {
data = <<EOH
APP_ENV={{ key "config/app/env" }}
APP_DEBUG={{ key "config/app/debug" }}
APP_KEY={{ secret "secret/key" }}
APP_URL={{ key "config/app/url" }}
EOH
destination = "core/.env"
change_mode = "noop"
}

driver = "docker"
config {
image = "redis:3.2"
}
resources {
cpu = [[.task_resource_cpu]]
memory = 256
}
}
}
}
2 changes: 1 addition & 1 deletion test/acctest/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (c DeployTestStepRunner) Run(s *TestState) error {
}
c.Vars["job_name"] = s.JobName

job, err := template.RenderJob("fixtures/"+c.FixtureName, []string{}, "", &c.Vars)
job, err := template.RenderJob("fixtures/"+c.FixtureName, []string{}, "", &c.Vars, false)
if err != nil {
return fmt.Errorf("error rendering template: %s", err)
}
Expand Down