-
Notifications
You must be signed in to change notification settings - Fork 31
ConvergeHCL ProposedChanges
This proposal is open for community discussion in Issue #605. Please comment there. This wiki page will be updated to reflect any changes that have been made based on the discussion.
We propose to add a new method of including modules using the full, imported, or qualified name of an HCL file. We will also add a new mechanism for specifying module parameters. The existing method of module imports will still be supported.
We propose a new method of including modules, dubbed "resource-style imports", that allow HCL modules to be included with syntax similar to nnative resources.
With resource style imports the resource name is the path of the resource, starting from any location or qualified name identifier in the import path, and without the trailing `.hcl`.
import "/usr/include/converge/stdlib/homebrew" {
as = "brew"
}
brew.install "zsh" {}
module "/usr/include/converge/stdlib/homebrew/install.hcl" "zsh" {}
We propose an improvement to the param syntax for imported modules. In place of specifying an explict param map, each param may be set by name as with native resources. The two examples below demonstrate the improved and traditional syntax for param definition:
stdlib.file "testfile" {
destination = "/tmp/file"
contents = "testdata"
uid = 1
gid = 1
}
stdlib.file "testfile" {
params = {
"destination" = "/tmp/file"
"contents" = "testdata"
"uid" = 1
"gid" = 1
}
}
We propose a new keyword, export
which will allow a module to export
a value or resource so that it will be available from an importing
module.
An export block will allow a module to export values that can be referenced by including modules. Exported values may be any valid HCL datatype, or may reference an existing resource accessible to the module. Examples are given below.
Modules will be able to export a value in an export block. The type
of the exported value is inferred using the same rules as param
. In
the example below we export a static string, as well as a templated
string. Any templated values will be rendered automatically.
export {
constant = "constant string"
lookedup = "{{lookup `file.content.foo.destination`}}"
}
Modules may also re-export any resources that they have
defined. Re-exported fields are given the name of the field, and
support an optional field, as
, allowing the re-exported field to be
qualified. In the example below we re-export a pair of task.query
resources, one qualifed as q
, and the other unqualified.
export {
"task.query.foo" { }
"task.query.bar" { as = "q" }
}
task.query "foo" {
query = "echo foo"
}
task.query "bar" {
query = "echo bar"
}
Exported values will be available through calls to lookup
as with
any values exported from native resources.
examples.exported "values" { }
file.content "exported-values" {
destination = "/tmp/exported-values"
content = <<EOF
constant: {{lookup `examples.exported.values.foo`}}
lookedup: {{lookup `examples.exported.values.lookedup`}}
foo-stdout: {{lookup `examples.exported.values.task.query.foo.status.stdout`}}
bar-stdout: {{lookup `examples.exported.values.q.status.stdout`}}
EOF
}
Exported values will adhere to the following semantics:
- Any number of export blocks are allowed. The values in all exported blocks will be exported.
- No field may be exported more than once with different values 1
We propose a mechanism to allow users to extract data and resources from switch statements. The following changes will be made to facilitate this:
- A new value will be exported from switch and case fields to identify which branch was executed
- Data may now be exported from switch statements as long as it is well defined under all branches
Switch and Case statements will now support new fields through
lookup
. Switch statements will gain map[string]bool branches
, a
mapping of branch names to a bool indicating whether or not they were
evaluated, and string evaluated
, a string that will represent the
evaluated branch. Case statements will gain a boolean field,
evaluated
, that will be set to true if the branch was evaluated.
The following example demonstrates how to use the branches and evaluated fields for switches and cases:
switch "foo" {
case "{{eq `1` `0`}}" "a" { }
case "{{eq `1` `1`}}" "b" { }
case "{{eq `1` `2`}}" "c" { }
}
file.content "results" {
destination = "results"
content = <<EOF
Evaluated Branch (switch): {{lookup `switch.foo.evaluated`}}
Known Conditionals:
{{range $name, $eval := {{lookup `switch.foo.branches`}}}}
$name = $eval
{{end}}
Evaluated (a): {{lookup `switch.foo.case.a.evaluated`}}
Evaluated (b): {{lookup `switch.foo.case.b.evaluated`}}
Evaluated (c): {{lookup `switch.foo.case.c.evaluated`}}
EOF
}
Switch statements support exporting both fields and templated or constant values. To export a field from a switch statement, all branches must have an export block with the same exported field names and types. Resources may be exported provided that they have the same real or qualified name, and are of the same type. When exporting fields a default branch is required to ensure that there will never be undefined values for exported fields.
switch "exports" {
case "{{eq `1` `0`}}" "a" {
export {
"file.content.a" = {as = "f"}
dest = "{{file.content.a.destination}}"
}
file.content "a" {
destination = "branch-a"
content = "branch-a"
}
}
case "{{eq `1` `1`}}" "b" {
export {
"file.content.b" = {as = "f"}
dest = "{{file.content.c.destination}}"
}
file.content "b" {
destination = "branch-b"
content = "branch-b"
}
}
case "{{eq `1` `2`}}" "c" {
export {
"file.content.c" = {as = "f"}
dest = "{{file.content.c.destination}}"
}
file.content "c" {
destination = "branch-c"
content = "branch-c"
}
}
default {
export {
"file.content.default" = {as = "f"}
dest = "{{file.content.a.destination}}"
}
file.content "default" {
destination = ""
content = ""
}
}
}
Fields that have been exported from switch blocks are accessible in the same way as all other exported fields. The example below demonstrates looking up the exported fields that we've just defined:
examples.exported.switch {}
file.content "output" {
destination = "output"
content = <<EOF
Content: {{lookup `examples.exported.switch.f.content`}}
Dest: {{lookup `examples.exported.switch.dest`}}
EOF
}
1 except for in the case of switch statements.