github.com/letsencrypt/boulder@v0.20251208.0/docs/config-validation.md (about)

     1  # Configuration Validation
     2  
     3  We use a fork of https://github.com/go-playground/validator which can be found
     4  at https://github.com/letsencrypt/validator. 
     5  
     6  ## Usage
     7  
     8  By default Boulder validates config files for all components with a registered
     9  validator. Validating a config file for a given component is as simple as
    10  running the component directly:
    11  
    12  ```shell
    13  $ ./bin/boulder-observer -config test/config-next/observer.yml
    14  Error validating config file "test/config-next/observer.yml": Key: 'ObsConf.MonConfs[1].Kind' Error:Field validation for 'Kind' failed on the 'oneof' tag
    15  ```
    16  
    17  or by running the `boulder` binary and passing the component name as a
    18  subcommand:
    19  
    20  ```shell
    21  $ ./bin/boulder boulder-observer -config test/config-next/observer.yml
    22  Error validating config file "test/config-next/observer.yml": Key: 'ObsConf.MonConfs[1].Kind' Error:Field validation for 'Kind' failed on the 'oneof' tag
    23  ```
    24  
    25  ## Struct Tag Tips
    26  
    27  You can find the full list of struct tags supported by the validator [here]
    28  (https://pkg.go.dev/github.com/go-playground/validator/v10#section-documentation).
    29  The following are some tips for struct tags that are commonly used in our
    30  configuration files.
    31  
    32  ### `required`
    33  
    34  The required tag means that the field is not allowed to take its zero value, or
    35  equivalently, is not allowed to be omitted. Note that this does not validate
    36  that slices or maps have contents, it simply guarantees that they are not nil.
    37  For fields of those types, you should use min=1 or similar to ensure they are
    38  not empty.
    39  
    40  There are also "conditional" required tags, such as `required_with`,
    41  `required_with_all`, `required_without`, `required_without_all`, and
    42  `required_unless`. These behave exactly like the basic required tag, but only if
    43  their conditional (usually the presence or absence of one or more other named
    44  fields) is met.
    45  
    46  ### `omitempty`
    47  
    48  The omitempty tag allows a field to be empty, or equivalently, to take its zero
    49  value. If the field is omitted, none of the other validation tags on the field
    50  will be enforced. This can be useful for tags like validate="omitempty,url", for
    51  a field which is optional, but must be a URL if it is present.
    52  
    53  The omitempty tag can be "overruled" by the various conditional required tags.
    54  For example, a field with tag `validate="omitempty,url,required_with=Foo"` is
    55  allowed to be empty when field Foo is not present, but if field Foo is present,
    56  then this field must be present and must be a URL.
    57  
    58  ### `-`
    59  
    60  Normally, config validation descends into all struct-type fields, recursively
    61  validating their fields all the way down. Sometimes this can pose a problem,
    62  when a nested struct declares one of its fields as required, but a parent struct
    63  wants to treat the whole nested struct as optional. The "-" tag tells the
    64  validation not to recurse, marking the tagged field as optional, and therefore
    65  making all of its sub-fields optional as well. We use this tag for many config
    66  duration and password file struct valued fields which are optional in some
    67  configs but required in others.
    68  
    69  ### `structonly`
    70  
    71  The structonly tag allows a struct valued field to be empty, or equivalently, to
    72  take its zero value, if it's not "overruled" by various conditional tags. If the
    73  field is omitted the recursive validation of the structs fields will be skipped.
    74  This can be useful for tags like `validate:"required_without=Foo,structonly"`
    75  for a struct valued field which is only required, and thus should only be
    76  validated, if field `Foo` is not present.
    77  
    78  ### `min=1`, `gte=1`
    79  
    80  These validate that the value of integer valued field is greater than zero and
    81  that the length of the slice or map is greater than zero.
    82  
    83  For instance, the following would be valid config for a slice valued field
    84  tagged with `required`.
    85  ```json
    86  {
    87    "foo": [],
    88  }
    89  ```
    90  
    91  But, only the following would be valid config for a slice valued field tagged
    92  with `min=1`.
    93  ```json
    94  {
    95    "foo": ["bar"],
    96  }
    97  ```
    98  
    99  ### `len`
   100  
   101  Same as `eq` (equal to) but can also be used to validate the length of the
   102  strings.
   103  
   104  ### `hostname_port`
   105  
   106  The
   107  [docs](https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-HostPort)
   108  for this tag are scant with detail, but it validates that the value is a valid
   109  RFC 1123 hostname and port. It is used to validate many of the
   110  `ListenAddress` and `DebugAddr` fields of our components.
   111  
   112  #### Future Work
   113  
   114  This tag is compatible with IPv4 addresses, but not IPv6 addresses. We should
   115  consider fixing this in our fork of the validator.
   116  
   117  ### `dive`
   118  
   119  This tag is used to validate the values of a slice or map. For instance, the
   120  following would be valid config for a slice valued field (`[]string`) tagged
   121  with `min=1,dive,oneof=bar baz`.
   122  
   123  ```json
   124  {
   125    "foo": ["bar", "baz"],
   126  }
   127  ```
   128  
   129  Note that the `dive` tag introduces an order-dependence in writing tags: tags
   130  that come before `dive` apply to the current field, while tags that come after
   131  `dive` apply to the current field's child values. In the example above: `min=1`
   132  applies to the length of the slice (`[]string`), while `oneof=bar baz` applies
   133  to the value of each string in the slice.
   134  
   135  We can also use `dive` to validate the values of a map. For instance, the
   136  following would be valid config for a map valued field (`map[string]string`)
   137  tagged with `min=1,dive,oneof=one two`.
   138  
   139  ```json
   140  {
   141    "foo": {
   142      "bar": "one",
   143      "baz": "two"
   144    },
   145  }
   146  ```
   147  
   148  `dive` can also be invoked multiple times to validate the values of nested
   149  slices or maps. For instance, the following would be valid config for a slice of
   150  slice valued field (`[][]string`) tagged with `min=1,dive,min=2,dive,oneof=bar
   151  baz`.
   152  
   153  ```json
   154  {
   155    "foo": [
   156      ["bar", "baz"],
   157      ["baz", "bar"],
   158    ],
   159  }
   160  ```
   161  
   162  - `min=1` will be applied to the outer slice (`[]`).
   163  - `min=2` will be applied to inner slice (`[]string`).
   164  - `oneof=bar baz` will be applied to each string in the inner slice.
   165  
   166  ### `keys` and `endkeys`
   167  
   168  These tags are used to validate the keys of a map. For instance, the following
   169  would be valid config for a map valued field (`map[string]string`) tagged with
   170  `min=1,dive,keys,eq=1|eq=2,endkeys,required`.
   171  
   172  ```json
   173  {
   174    "foo": {
   175      "1": "bar",
   176      "2": "baz",
   177    },
   178  }
   179  ```
   180  
   181  - `min=1` will be applied to the map itself
   182  - `eq=1|eq=2` will be applied to the map keys
   183  - `required` will be applied to map values