hashicorp / levant

An open source templating and deployment tool for HashiCorp Nomad jobs
Mozilla Public License 2.0
829 stars 125 forks source link

Recursively render fileContents #305

Open rorylshanks opened 5 years ago

rorylshanks commented 5 years ago

Hi there!

I noticed that when we include a file using the fileContents tag, and that file has levant templating in it, the included template is not recursively rendered. This would be a very useful feature to have allowing for large numbers of config files to be included in the job without putting them all in the one file, while also allowing for a single templating tool to manage them all.

Thanks!

gaspo53 commented 4 years ago

Would be very useful to have this!

bjornicus commented 3 years ago

it would be ok, IMHO to have a separate templateFileContents or some such function to do this, or a parameter to fileContents to control it.

in the meantime, for simple things I have found that something like this can be used: [[fileContents "my.config" | replace "$ENV" (env "ENV")]]

but would love to have the full levant templating power in those included files.

foozmeat commented 3 years ago

I'm resorting to this 🤢 :

while grep '\[\[' job.nomad; do
  echo "*****************************************"
  levant render \
  -log-level=WARN \
  -out=job.nomad \
  job.nomad
done
milesrichardson commented 2 years ago

The solution from @foozmeat is pretty much the only option AFAICT, pretty disgusting :D

If your job spec is simple enough, and you only need to render it twice, then you can use file redirection like this:

levant render -var-file config.json <(levant render -var-file config.json job-1.hcl)

which works:

❯ levant render -var-file config.json <(levant render -var-file config.json job-1.hcl)
2022-10-17T20:09:02Z |INFO| helper/variable: using variable with key foo and value map[bar:map[bazz:map[buzz:1]]] from file
2022-10-17T20:09:02Z |INFO| helper/variable: using variable with key foo and value map[bar:map[bazz:map[buzz:1]]] from file
job_2_stanza {
  config = "eyJiYXp6Ijp7ImJ1enoiOjF9fQ==
}

job_1_stanza {
  config = "eyJiYXp6Ijp7ImJ1enoiOjF9fQ==
}

Personally I'm just gonna accept the non-DRY config


How to reproduce the bug (Levant v0.3.0)

For anyone coming from google search who just wants to see whether this works, here's a minimal reproduction of the current bug (Levant v0.3.0).

Given example files job-1.hcl and job-2.hcl which both include a Levant template referencing variables in config.json:

tee config.json << EOC
{
  "foo": {
    "bar": {
      "bazz": {
        "buzz": 1
      }
    }
  }
}
EOC

tee job-1.hcl << EOJ
[[ fileContents "./job-2.hcl" ]]

job_1_stanza {
  config = "[[ .foo.bar | toJson | b64enc ]]
}
EOJ

tee job-2.hcl << EOK
job_2_stanza {
  config = "[[ .foo.bar | toJson | b64enc ]]
}
EOK

job-2.hcl renders its template When rendered individually:

#❯ levant render -var-file config.json job-2.hcl
#2022-10-17T19:58:35Z |INFO| helper/variable: using variable with key foo and value map[bar:map[bazz:map[buzz:1]]] from file
job_2_stanza {
  config = "eyJiYXp6Ijp7ImJ1enoiOjF9fQ==
}

but not when rendered via fileContents as part of job-1.hcl:

#❯ levant render -var-file config.json job-1.hcl
#2022-10-17T20:04:47Z |INFO| helper/variable: using variable with key foo and value map[bar:map[bazz:map[buzz:1]]] from file
job_2_stanza {
  config = "[[ .foo.bar | toJson | b64enc ]]
}

job_1_stanza {
  config = "eyJiYXp6Ijp7ImJ1enoiOjF9fQ==
}