github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/website/content/docs/job-specification/hcl2/expressions.mdx (about)

     1  ---
     2  layout: docs
     3  page_title: Expressions - Configuration Language
     4  description: |-
     5    HCL allows the use of expressions to access data exported
     6    by sources and to transform and combine that data to produce other values.
     7  ---
     8  
     9  # Expressions
    10  
    11  _Expressions_ are used to refer to or compute values within a configuration.
    12  The simplest expressions are just literal values, like `"hello"` or `5`, but
    13  HCL also allows more complex expressions such as arithmetic, conditional
    14  evaluation, and a number of built-in functions.
    15  
    16  Expressions can be used in a number of places in HCL, particularly as attribute
    17  values. Attribute value expressions must adhere to the attribute type. Block
    18  labels must be string literals without any interpolation. Each language
    19  feature's documentation describes any restrictions it places on expressions.
    20  
    21  The rest of this page describes all of the features of Nomad's
    22  expression syntax.
    23  
    24  ## Types and Values
    25  
    26  The result of an expression is a _value_. All values have a _type_, which
    27  dictates where that value can be used and what transformations can be
    28  applied to it.
    29  
    30  HCL uses the following types for its values:
    31  
    32  - `string`: a sequence of Unicode characters representing some text, like
    33    `"hello"`.
    34  - `number`: a numeric value. The `number` type can represent both whole
    35    numbers like `15` and fractional values like `6.283185`.
    36  - `bool`: either `true` or `false`. `bool` values can be used in conditional
    37    logic.
    38  - `list` (or `tuple`): a sequence of values, like
    39    `["us-west-1a", "us-west-1c"]`. Elements in a list or tuple are identified by
    40    consecutive whole numbers, starting with zero.
    41  - `map` (or `object`): a group of values identified by named labels, like
    42    `{name = "Mabel", age = 52}`.
    43  
    44  Strings, numbers, and bools are sometimes called _primitive types._
    45  Lists/tuples and maps/objects are sometimes called _complex types,_ _structural
    46  types,_ or _collection types._
    47  
    48  Finally, there is one special value that has _no_ type:
    49  
    50  - `null`: a value that represents _absence_ or _omission._ If you set an
    51    argument to `null`, Nomad behaves as though you
    52    had completely omitted it — it will use the argument's default value if it has
    53    one, or raise an error if the argument is mandatory. `null` is most useful in
    54    conditional expressions, so you can dynamically omit an argument if a
    55    condition isn't met.
    56  
    57  ### Advanced Type Details
    58  
    59  In most situations, lists and tuples behave identically, as do maps and objects.
    60  Whenever the distinction isn't relevant, the Nomad documentation uses each
    61  pair of terms interchangeably (with a historical preference for "list" and
    62  "map").
    63  
    64  ### Type Conversion
    65  
    66  Expressions are most often used to set values for arguments. In these cases,
    67  the argument has an expected type and the given expression must produce a value
    68  of that type.
    69  
    70  Where possible, Nomad automatically converts values from one type to
    71  another in order to produce the expected type. If this isn't possible, Nomad
    72  will produce a type mismatch error and you must update the configuration with a
    73  more suitable expression.
    74  
    75  Nomad automatically converts number and bool values to strings when needed.
    76  It also converts strings to numbers or bools, as long as the string contains a
    77  valid representation of a number or bool value.
    78  
    79  - `true` converts to `"true"`, and vice-versa
    80  - `false` converts to `"false"`, and vice-versa
    81  - `15` converts to `"15"`, and vice-versa
    82  
    83  ## Literal Expressions
    84  
    85  A _literal expression_ is an expression that directly represents a particular
    86  constant value. Nomad has a literal expression syntax for each of the value
    87  types described above:
    88  
    89  - Strings are usually represented by a double-quoted sequence of Unicode
    90    characters, `"like this"`. There is also a "heredoc" syntax for more complex
    91    strings. String literals are the most complex kind of literal expression in
    92    Nomad, and have additional documentation on this page:
    93    - See [String Literals](#string-literals) below for information about escape
    94      sequences and the heredoc syntax.
    95    - See [String Templates](#string-templates) below for information about
    96      interpolation and template directives.
    97  - Numbers are represented by unquoted sequences of digits with or without a
    98    decimal point, like `15` or `6.283185`.
    99  - Bools are represented by the unquoted symbols `true` and `false`.
   100  - The null value is represented by the unquoted symbol `null`.
   101  - Lists/tuples are represented by a pair of square brackets containing a
   102    comma-separated sequence of values, like `["a", 15, true]`.
   103  
   104    List literals can be split into multiple lines for readability, but always
   105    require a comma between values. A comma after the final value is allowed,
   106    but not required. Values in a list can be arbitrary expressions.
   107  
   108  - Maps/objects are represented by a pair of curly braces containing a series of
   109    `<KEY> = <VALUE>` pairs:
   110  
   111    ```hcl
   112    {
   113      name = "John"
   114      age  = 52
   115    }
   116    ```
   117  
   118    Key/value pairs can be separated by either a comma or a line break. Values
   119    can be arbitrary expressions. Keys are strings; they can be left unquoted if
   120    they are a valid [identifier](/docs/job-specification/hcl2/syntax#identifiers), but must be quoted
   121    otherwise. You can use a non-literal expression as a key by wrapping it in
   122    parentheses, like `(var.business_unit_tag_name) = "SRE"`.
   123  
   124  ### Available Functions
   125  
   126  For a full list of available functions, see [the function
   127  reference](/docs/job-specification/hcl2/functions).
   128  
   129  ## `for` Expressions
   130  
   131  A _`for` expression_ creates a complex type value by transforming
   132  another complex type value. Each element in the input value
   133  can correspond to either one or zero values in the result, and an arbitrary
   134  expression can be used to transform each input element into an output element.
   135  
   136  For example, if `var.list` is a list of strings, then the following expression
   137  produces a list of strings with all-uppercase letters:
   138  
   139  ```hcl
   140  [for s in var.list : upper(s)]
   141  ```
   142  
   143  This `for` expression iterates over each element of `var.list`, and then
   144  evaluates the expression `upper(s)` with `s` set to each respective element.
   145  It then builds a new tuple value with all of the results of executing that
   146  expression in the same order.
   147  
   148  The type of brackets around the `for` expression decide what type of result
   149  it produces. The above example uses `[` and `]`, which produces a tuple. If
   150  `{` and `}` are used instead, the result is an object, and two result
   151  expressions must be provided separated by the `=>` symbol:
   152  
   153  ```hcl
   154  {for s in var.list : s => upper(s)}
   155  ```
   156  
   157  This expression produces an object whose attributes are the original elements
   158  from `var.list` and their corresponding values are the uppercase versions.
   159  
   160  A `for` expression can also include an optional `if` clause to filter elements
   161  from the source collection, which can produce a value with fewer elements than
   162  the source:
   163  
   164  ```text
   165  [for s in var.list : upper(s) if s != ""]
   166  ```
   167  
   168  The source value can also be an object or map value, in which case two
   169  temporary variable names can be provided to access the keys and values
   170  respectively:
   171  
   172  ```text
   173  [for k, v in var.map : length(k) + length(v)]
   174  ```
   175  
   176  Finally, if the result type is an object (using `{` and `}` delimiters) then
   177  the value result expression can be followed by the `...` symbol to group
   178  together results that have a common key:
   179  
   180  ```text
   181  {for s in var.list : substr(s, 0, 1) => s... if s != ""}
   182  ```
   183  
   184  <!---
   185  ## TODO: revamp this section
   186  
   187  ## Splat Expressions
   188  
   189  A _splat expression_ provides a more concise way to express a common operation
   190  that could otherwise be performed with a `for` expression.
   191  
   192  If `var.list` is a list of objects that all have an attribute `id`, then a list
   193  of the ids could be produced with the following `for` expression:
   194  
   195  ```hcl
   196  [for o in var.list : o.id]
   197  ```
   198  
   199  This is equivalent to the following _splat expression:_
   200  
   201  ```hcl
   202  var.list[*].id
   203  ```
   204  
   205  The special `[*]` symbol iterates over all of the elements of the list given to
   206  its left and accesses from each one the attribute name given on its right. A
   207  splat expression can also be used to access attributes and indexes from lists
   208  of complex types by extending the sequence of operations to the right of the
   209  symbol:
   210  
   211  ```hcl
   212  var.list[*].interfaces[0].name
   213  ```
   214  
   215  The above expression is equivalent to the following `for` expression:
   216  
   217  ```hcl
   218  [for o in var.list : o.interfaces[0].name]
   219  ```
   220  
   221  Splat expressions are for lists only (and thus cannot be used [to reference
   222  resources created with
   223  `for_each`](https://www.terraform.io/docs/configuration/resources.html#referring-to-instances), which
   224  are represented as maps). However, if a splat expression is applied to a value
   225  that is _not_ a list or tuple then the value is automatically wrapped in a
   226  single-element list before processing.
   227  
   228  For example, `var.single_object[*].id` is equivalent to
   229  `[var.single_object][*].id`, or effectively `[var.single_object.id]`. This
   230  behavior is not interesting in most cases, but it is particularly useful when
   231  referring to resources that may or may not have `count` set, and thus may or
   232  may not produce a tuple value:
   233  
   234  ```hcl
   235  aws_instance.example[*].id
   236  ```
   237  
   238  The above will produce a list of ids whether `aws_instance.example` has `count`
   239  set or not, avoiding the need to revise various other expressions in the
   240  configuration when a particular resource switches to and from having `count`
   241  set.
   242  
   243  --->
   244  
   245  ## `dynamic` blocks
   246  
   247  Within top-level block constructs like sources, expressions can usually be used
   248  only when assigning a value to an argument using the `name = expression` or
   249  `key = expression` form. This covers many uses, but some source types include
   250  repeatable _nested blocks_ in their arguments, which do not accept expressions:
   251  
   252  ```hcl
   253  network {
   254    mode = "host" # can use expressions here
   255  
   256    port "label" {
   257      # but the "port" block is always a literal block
   258    }
   259  }
   260  ```
   261  
   262  You can dynamically construct repeatable nested blocks like `port` using a
   263  special `dynamic` block type, which is supported anywhere, example:
   264  
   265  ```hcl
   266  locals {
   267    ports = [
   268      {
   269        port_label = "api"
   270        port       = 80
   271      },
   272      {
   273        port_label = "ui"
   274        port       = 8080
   275      }
   276    ]
   277  }
   278  
   279  job "example" {
   280    datacenters = ["dc1"]
   281  
   282    group "cache" {
   283      network {
   284  
   285        mode = "host"
   286  
   287        dynamic "port" {
   288          for_each = local.ports
   289          labels   = [port.value.port_label]
   290  
   291          content {
   292            to = port.value.port
   293          }
   294        }
   295      }
   296      ...
   297  ```
   298  
   299  A `dynamic` block acts much like a `for` expression, but produces nested blocks
   300  instead of a complex typed value. It iterates over a given complex value, and
   301  generates a nested block for each element of that complex value.
   302  
   303  - The label of the dynamic block (`"port"` in the example above) specifies
   304    what kind of nested block to generate.
   305  - The `for_each` argument provides the complex value to iterate over.
   306  - The `iterator` argument (optional) sets the name of a temporary variable
   307    that represents the current element of the complex value. If omitted, the name
   308    of the variable defaults to the label of the `dynamic` block (`"port"` in
   309    the example above).
   310  - The `labels` argument (optional) is a list of strings that specifies the block
   311    labels, in order, to use for each generated block. You can use the temporary
   312    iterator variable in this value. Nomad currently only has blocks that support
   313    a single label such as `port`.
   314  - The nested `content` block defines the body of each generated block. You can
   315    use the temporary iterator variable inside this block.
   316  
   317  Since the `for_each` argument accepts any collection or structural value,
   318  you can use a `for` expression or splat expression to transform an existing
   319  collection.
   320  
   321  The iterator object (`port` in the example above) has two attributes:
   322  
   323  - `key` is the map key or list element index for the current element. If the
   324    `for_each` expression produces a _set_ value then `key` is identical to
   325    `value` and should not be used.
   326  - `value` is the value of the current element.
   327  
   328  The `for_each` value must be a map or set with one element per desired nested
   329  block. If you need to declare resource instances based on a nested data
   330  structure or combinations of elements from multiple data structures you can use
   331  expressions and functions to derive a suitable value. For some common examples
   332  of such situations, see the
   333  [`flatten`](/docs/job-specification/hcl2/functions/collection/flatten) and
   334  [`setproduct`](/docs/job-specification/hcl2/functions/collection/setproduct)
   335  functions.
   336  
   337  ### Best Practices for `dynamic` Blocks
   338  
   339  Overuse of `dynamic` blocks can make configuration hard to read and maintain,
   340  so we recommend using them only when you need to hide details in order to build
   341  a clean user interface for a re-usable code. Always write nested blocks out
   342  literally where possible.
   343  
   344  ## String Literals
   345  
   346  HCL has two different syntaxes for string literals. The
   347  most common is to delimit the string with quote characters (`"`), like
   348  `"hello"`. In quoted strings, the backslash character serves as an escape
   349  sequence, with the following characters selecting the escape behavior:
   350  
   351  | Sequence     | Replacement                                                                   |
   352  | ------------ | ----------------------------------------------------------------------------- |
   353  | `\n`         | Newline                                                                       |
   354  | `\r`         | Carriage Return                                                               |
   355  | `\t`         | Tab                                                                           |
   356  | `\"`         | Literal quote (without terminating the string)                                |
   357  | `\\`         | Literal backslash                                                             |
   358  | `\uNNNN`     | Unicode character from the basic multilingual plane (NNNN is four hex digits) |
   359  | `\UNNNNNNNN` | Unicode character from supplementary planes (NNNNNNNN is eight hex digits)    |
   360  
   361  The alternative syntax for string literals is the so-called Here Documents or
   362  "heredoc" style, inspired by Unix shell languages. This style allows multi-line
   363  strings to be expressed more clearly by using a custom delimiter word on a line
   364  of its own to close the string:
   365  
   366  ```hcl
   367  <<EOF
   368  hello
   369  world
   370  EOF
   371  ```
   372  
   373  The `<<` marker followed by any identifier at the end of a line introduces the
   374  sequence. Nomad then processes the following lines until it finds one that
   375  consists entirely of the identifier given in the introducer. In the above
   376  example, `EOF` is the identifier selected. Any identifier is allowed, but
   377  conventionally this identifier is in all-uppercase and begins with `EO`, meaning
   378  "end of". `EOF` in this case stands for "end of text".
   379  
   380  The "heredoc" form shown above requires that the lines following be flush with
   381  the left margin, which can be awkward when an expression is inside an indented
   382  block:
   383  
   384  ```hcl
   385  block {
   386    value = <<EOF
   387  hello
   388  world
   389  EOF
   390  }
   391  ```
   392  
   393  To improve on this, Nomad also accepts an _indented_ heredoc string variant
   394  that is introduced by the `<<-` sequence:
   395  
   396  ```hcl
   397  block {
   398    value = <<-EOF
   399    hello
   400      world
   401    EOF
   402  }
   403  ```
   404  
   405  In this case, Nomad analyses the lines in the sequence to find the one
   406  with the smallest number of leading spaces, and then trims that many spaces
   407  from the beginning of all of the lines, leading to the following result:
   408  
   409  ```text
   410  hello
   411    world
   412  ```
   413  
   414  Backslash sequences are not interpreted in a heredoc string expression.
   415  Instead, the backslash character is interpreted literally.
   416  
   417  In both quoted and heredoc string expressions, Nomad supports template
   418  sequences that begin with `${` and `%{`. These are described in more detail
   419  in the following section. To include these sequences _literally_ without
   420  beginning a template sequence, double the leading character: `$${` or `%%{`.
   421  
   422  ## String Templates
   423  
   424  Within quoted and heredoc string expressions, the sequences `${` and `%{` begin
   425  _template sequences_. Templates let you directly embed expressions into a string
   426  literal, to dynamically construct strings from other values.