Helm Tricks: Input Validation with values.schema.json

Earlier, I published a post about performing input validation in Helm using the required and fail functions. These functions provide a simple way to perform inline validation within your chart templates and can be used to ensure that expected values are provided and fit within specific constraints.

Imagine, however, that you could map out the structure of your Helm values in a single schema file. Since the release of Helm 3, you can create a file called values.schema.json which allows you to easily set value requirements and constraints in a single location. This file provides similar capabilities as the required and fail functions but can be used to provide more robust Helm input validation, with the additional benefit of enforcing value restrictions from a single schema file.

The values.schema.json file is written using the JSON Schema vocabulary. According to json-schema.org, JSON Schema is “…a vocabulary that allows you to annotate and validate JSON documents”. Below shows an example values.schema.json file.

{
   "$schema": "http://json-schema.org/draft-07/schema",
   "required": [
       "image",
       "serviceType",
       "port"
   ],
   "properties": {
       "image": {
           "type": "object",
           "required": [
               "repository",
               "tag"
           ],
           "properties": {
               "repository": {
                   "type": "string"
               },
               "tag": {
                   "type": "string"
               }
           }
       },
       "serviceType": {
           "type": "string",
           "enum": ["ClusterIP", "LoadBalancer"]
       },
       "port": {
           "type": "integer",
           "minimum": 8080
       }
   }
}

This example provides a schema for the values “image.repository”, “image.tag”, “serviceType”, and “port”. This can be seen by reviewing the keys under the “properties” keyword.

This example also uses the required, type, enum, and minimum keywords. Let’s dive deeper into each of these by first exploring “required”.

Requiring User Input with “required”

The required keyword is used to fail chart rendering if specific values are not provided. The example from above contained two instances of the “required” keyword. The first was at the top level and looked like this:

   "required": [
       "image",
       "serviceType",
       "port"
   ],

The values listed (“image”, “serviceType”, and “port”) must be provided in order to install or upgrade the chart. If you were to try installing this chart without any values, you would see the following error:

$ helm template validation-test test-chart
Error: values don't meet the specifications of the schema(s) in the following chart(s):
test-chart:
- (root): image is required
- (root): serviceType is required
- (root): port is required

This example used another “required” keyword inside the nested image “properties” that looked like this:

           "required": [
               "repository",
               "tag"
           ],

This indicates that the values under the image object (image.repository and image.tag) must also be provided. You didn’t see an error for these previously because the “image” was not provided, so it threw an error for “image” first. But, imagine you tried to render this chart with an invalid image value:

$ helm template validation-test test-chart \
  --set image.invalidValue=aaa

In this case, you would see the following error:

Error: values don't meet the specifications of the schema(s) in the following chart(s):
test-chart:
- (root): serviceType is required
- (root): port is required
- image: repository is required
- image: tag is required

The serviceType and port values returned errors for the same reason as before – they weren’t provided. But this time, you’ll see new errors indicating that the repository and tag values must be provided under the “image” object.

There’s one other interesting thing to point out about validating nested values with a values.schema.json file. Before, we saw a requirement error when we set an incorrect value called “image.invalidValue”, but imagine you tried to render the chart by providing an empty image object.

$ helm template validation-test test-chart \
  --set image=’’

Here, you would receive this output:

Error: values don't meet the specifications of the schema(s) in the following chart(s):
test-chart:
- (root): serviceType is required
- (root): port is required
- image: Invalid type. Expected: object, given: string

Perhaps unexpectedly, the error is not about requiring the image.repository and image.tag values. Instead, the error states that an invalid type was given to “image”. The presence of nested values are only checked if at least one nested value is provided, correct or not. Since only an empty string was provided, we instead received a type-based error, which brings us to our next topic – validating provided input using type, enum, minimum, and other keywords.

Validating User Input against Constraints

JSON Schema provides several different keywords which can be used for validating user inputs against specific constraints. One common keyword is type, which can be used to ensure that the input is a String, Integer, Boolean, Object, or another type. Recall the Invalid Type error encountered previously:

- image: Invalid type. Expected: object, given: string

This error occurred because we passed an empty string to “image”, but the expected type is an object (or a map containing the nested values). Here is the snippet from the values.schema.json file that restricts the “image” type:

       "image": {
           "type": "object",

You would expect to also see type validation against “serviceType” (which must be a string):

       "serviceType": {
           "type": "string",

And you would expect to see type validation against “port” (which must be an integer):

       "port": {
           "type": "integer",

Another useful keyword to validate user input is enum. This keyword is excellent for validating against a list of supported inputs. In the values.schema.json example, enum is being used to restrict the serviceType to “ClusterIP” and “LoadBalancer”:

       "serviceType": {
           "type": "string",
           "enum": ["ClusterIP", "LoadBalancer"]
       },

If the wrong serviceType is provided, you’ll see the following error (other errors unrelated to serviceType were omitted):

$ helm template validation-test test-chart --set serviceType=NodePort
Error: values don't meet the specifications of the schema(s) in the following chart(s):
test-chart:
- serviceType: serviceType must be one of the following: "ClusterIP", "LoadBalancer"

The last keyword I’ll dive into is minimum. This keyword is a good way to ensure that integer values are not set below a certain threshold. The below example shows how you can set a minimum value for a port number:

       "port": {
           "type": "integer",
           "minimum": 8080
       }

If you attempt to install the chart with a port number smaller than 8080, you’ll see the following error (errors unrelated to port were omitted):

$ helm template validation-test test-chart --set port=8000
Error: values don't meet the specifications of the schema(s) in the following chart(s):
test-chart:
- port: Must be greater than or equal to 8080/1

As an aside, the reason the error says “8080/1” instead of simply “8080” is because, as of writing, the underlying library that is being used is interpreting this as a big.Rat instead of a big.Int, so the number will be displayed as a fraction.

There are other keywords which can be used for validating input, including (but not limited to):

  • maximum: Set a maximum threshold for a numeric value
  • pattern: Set a regex that a value must conform to
  • additionalProperties: Determines if the user can provide values that are not defined in the values.schema.json file. By default, users can provide any additional value without restriction, but this would not impact chart rendering if the additional value isn’t used in your templates.

The following links from json-schema.org provide a complete list of validation keywords:

Further Reading

Check out the Helm documentation’s page on the values.schema.json file for more information.

For help creating a values.schema.json file, check out the website https://jsonschema.net. This website will help you bootstrap a schema that can be used to validate your chart’s values (and is the site I used to help create the example schema for this post).

Thank You!

Thanks for reading! The values.schema.json file is an excellent way of performing Helm input validation in one convenient schema file. Hopefully, this post helps you validate user input in your Helm charts!

The title photo is by Joseph Barrientos on Unsplash.

Austin Dewey

Austin Dewey is a DevOps engineer focused on delivering a streamlined developer experience on cloud and container technologies. Austin started his career with Red Hat’s consulting organization, where he helped drive success at many different Fortune 500 companies by automating deployments on Red Hat’s Kubernetes-based PaaS, OpenShift Container Platform. Currently, Austin works at fintech startup Prime Trust, building automation to scale financial infrastructure and support developers on Kubernetes and AWS. Austin is the author of "Learn Helm", a book focused on packaging and delivering applications to Kubernetes, and he enjoys writing about open source technologies at his blog in his free time, austindewey.com.

Leave a Reply