Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/dadav/helm-schema

Generate jsonschemas from helm charts.
https://github.com/dadav/helm-schema

hacktoberfest helm jsonschema

Last synced: about 1 month ago
JSON representation

Generate jsonschemas from helm charts.

Awesome Lists containing this project

README

        

# helm-schema





Latest Release
Go Docs
Build Status
MIT LICENSE
pre-commit
Go Report

This tool tries to help you to easily create some nice JSON schema for your helm chart.

By default it will traverse the current directory and look for `Chart.yaml` files.
For every file, helm-schema will try to find one of the given value filenames.
The first files found will be read and a jsonschema will be created.
For every dependency defined in the `Chart.yaml` file, a reference to the dependencies JSON schema
will be created.

> [!NOTE]
> The tool uses `jsonschema` Draft 7, because the library helm uses only supports that version.

## Installation

Via `go` install:

```sh
go install github.com/dadav/helm-schema/cmd/helm-schema@latest
```

From `aur`:

```sh
paru -S helm-schema
```

Via `podman/docker`:

```sh
podman run --rm -v $PWD:/home/helm-schema ghcr.io/dadav/helm-schema:latest
```

## Usage

### Pre-commit hook

If you want to automatically generate a new `values.schema.json` if you change the `values.yaml`
file, you can do the following:

1. Install [`pre-commit`](https://pre-commit.com/#install)
2. Copy the [`.pre-commit-config.yaml`](./.pre-commit-config.yaml) to your helm chart repository.
3. Then run these commands:

```sh
pre-commit install
pre-commit install-hooks
```

### Running the binary directly

You can also just run the binary yourself:

```sh
helm-schema
```

### Options

The binary has the following options:

```sh
Flags:
-r, --add-schema-reference "add reference to schema in values.yaml if not found"
-a, --append-newline append newline to generated jsonschema at the end of the file
-c, --chart-search-root string "directory to search recursively within for charts (default ".")"
-x, --dont-strip-helm-docs-prefix "disable the removal of the helm-docs prefix (--)"
-d, --dry-run "don't actually create files just print to stdout passed"
-h, --help "help for helm-schema"
-s, --keep-full-comment "keep the whole leading comment (default: cut at empty line)"
-l, --log-level string "level of logs that should printed, one of (panic, fatal, error, warning, info, debug, trace) (default "info")"
-n, --no-dependencies "don't analyze dependencies"
-o, --output-file string "jsonschema file path relative to each chart directory to which jsonschema will be written (default 'values.schema.json')"
-f, --value-files strings "filenames to check for chart values (default [values.yaml])"
-k, --skip-auto-generation strings "skip the auto generation for these fields (default [])"
-u, --uncomment "consider yaml which is commented out"
-v, --version "version for helm-schema"
```

## Annotations

The `jsonschema` must be between two entries of `# @schema` :

```yaml
# @schema
# my: annotation
# @schema
# you can add comment here as well
foo: bar
```

> [!WARNING]
> It must be written just above the key you want to annotate.

> [!NOTE]
> If you don't use the `properties` option on hashes/objects or don't use `items` on arrays, it will be parsed from the values and their annotations instead.

### Available annotations

| Key| Description | Values |
|-|-|-|
| [`type`](#type) | Defines the [jsonschema-type](https://json-schema.org/understanding-json-schema/reference/type.html) of the object. Multiple values are supported (e.g. `[string, integer]`) as a shortcut to `anyOf` | `object`, `array`, `string`, `number`, `integer`, `boolean` or `null` |
| [`title`](#title) | Defines the [title field](https://json-schema.org/understanding-json-schema/reference/generic.html?highlight=title) of the object | Defaults to the key itself |
| [`description`](#description) | Defines the [description field](https://json-schema.org/understanding-json-schema/reference/generic.html?highlight=description) of the object. | Defaults to the comments just above or below the `@schema` annotations block |
| [`default`](#default) | Sets the default value and will be displayed first on the users IDE| Takes a `string` |
| [`properties`](#properties) | Contains a map with keys as property names and values as schema | Takes an `object` |
| [`pattern`](#pattern) | Regex pattern to test the value | Takes an `string` |
| [`format`](#format) | The [format keyword](https://json-schema.org/understanding-json-schema/reference/string.html#format) allows for basic semantic identification of certain kinds of string values | Takes a [keyword](https://json-schema.org/understanding-json-schema/reference/string.html#format) |
| [`required`](#required) | Adds the key to the required items | `true` or `false` or `array` |
| [`deprecated`](#deprecated) | Marks the option as deprecated | `true` or `false` |
| [`items`](#items) | Contains the schema that describes the possible array items | Takes an `object` |
| [`enum`](#enum) | Multiple allowed values. Accepts an array of `string` | Takes an `array` |
| [`const`](#const) | Single allowed value | Takes a `string`|
| [`examples`](#examples) | Some examples you can provide for the end user | Takes an `array` |
| [`minimum`](#minimum) | Minimum value. Can't be used with `exclusiveMinimum` | Takes an `integer`. Must be smaller than `maximum` or `exclusiveMaximum` (if used) |
| [`exclusiveMinimum`](#exclusiveminimum) | Exclusive minimum. Can't be used with `minimum` | Takes an `integer`. Must be smaller than `maximum` or `exclusiveMaximum` (if used) |
| [`maximum`](#maximum) | Maximum value. Can't be used with `exclusiveMaximum` | Takes an `integer`. Must be bigger than `minimum` or `exclusiveMinimum` (if used) |
| [`exclusiveMaximum`](#exclusivemaximum) | Exclusive maximum value. Can't be used with `maximum` | Takes an `integer`. Must be bigger than `minimum` or `exclusiveMinimum` (if used) |
| [`multipleOf`](#multipleof) | The yaml-value must be a multiple of. For example: If you set this to 10, allowed values would be 0, 10, 20, 30... | Takes an `integer` |
| [`additionalProperties`](#additionalproperties) | Allow additional keys in maps. Useful if you want to use for example `additionalAnnotations`, which will be filled with keys that the `jsonschema` can't know| Defaults to `false` if the map is not an empty map. Takes a schema or boolean value |
| [`patternProperties`](#patternproperties) | Contains a map which maps schemas to pattern. If properties match the patterns, the given schema is applied| Takes an `object` |
| [`anyOf`](#anyof) | Accepts an array of schemas. None or one must apply | Takes an `array` |
| [`oneOf`](#oneof) | Accepts an array of schemas. One or more must apply | Takes an `array` |
| [`allOf`](#allof) | Accepts an array of schemas. All must apply| Takes an `array` |
| [`not`](#not) | A schema that must not be matched. | Takes an `object` |
| [`if/then/else`](#ifthenelse) | `if` the given schema applies, `then` also apply the given schema or `else` the other schema| Takes an `object` |
| [`$ref`](#ref) | Accepts an URI to a valid `jsonschema`. Extend the schema for the current key | Takes an URI (or relative file) |
| [`minLength`](#minlength) | Minimum string length. | Takes an `integer`. Must be smaller or equal than `maxLength` (if used) |
| [`maxLength`](#maxlength) | Maximum string length. | Takes an `integer`. Must be greater or equal than `minLength` (if used) |

## Validation & completion

To take advantage of the generated `values.schema.json`, you can use it within your IDE through a plugin supporting the `yaml-language-server` annotation (e.g. [VSCode - YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml))

You'll have to place this line at the top of your `values.yaml` (`$schema=`) :

```yaml
# vim: set ft=yaml:
# yaml-language-server: $schema=values.schema.json

# @schema
# required: true
# @schema
# -- This is an example description
foo: bar
```

You can use the `-r` flag to make sure this line exists.

> [!NOTE]
> You can also point to an online available schema, if you upload a version of yours and want other to be able to implement it.
>
> `yaml-language-server: $schema=https://example.org/my-json-schema.json`
>
> e.g. from github `https://raw.githubusercontent.com///main/values.schema.json`

### helm-docs

If you're using [`helm-docs`](https://github.com/norwoodj/helm-docs), then you can combine both annotations and use both pre-commit hooks to automatically generate your documentation (e.g. `README.md`) alongside your `values.schema.json`.

If not provided, `title` will be the key and the `description` will be parsed from the `helm-docs` formatted comment.

```yaml
# @schema
# type: array
# @schema
# -- helm-docs description here
foo: []
```

> [!NOTE]
> Make sure to place the `@schema` annotations **before** the actual key description to avoid having it in your `helm-docs` generated table

## Dependencies

Per default, `helm-schema` will try to also create the schemas for the dependencies in their respective chart directory. These schemas will be merged as properties in the main schema, but the `requiredProperties` field will be nullified, otherwise you would have to always overwrite all the required fields.

If you don't want to generate `jsonschema` for chart dependencies, you can use the `-n, --no-dependencies` option to only generate the `values.schema.json` for your parent chart(s)

## Limitations

You can't change the `jsonschema` for dependencies by using `@schema` annotations on dependency config values. For example:

```yaml
# foo is a dependency chart
foo:
# You can't change the schema here, this has no effect.
# @schema
# type: number
# @schema
bar: 1
```

## Examples

Some annotation examples you may want to use, to help you get started!

> [!NOTE]
> See how the schema behaves with live examples : [values.yaml](./examples/values.yaml)

Below a snippet to test it out, with the current options `helm-schema` will not analyze dependencies (`-n`) and will omit the `additionalProperties` (`-k`, when not explicitly defined) in the generated schema. It will start looking for `Chart.yaml` and `values.yaml` files in `examples/` (`-c`)

```sh
cd examples
helm-schema -n -k additionalProperties

# or

helm-schema -c examples -n -k additionalProperties
```

If you'd like to use `helm-schema` on your chart dependencies as well, you have to build and unpack them before. You'll avoid the "missing dependency" error message.

```sh
# go where your Chart.lock/yaml is located
cd

# build dependencies and untar them
helm dep build
ls charts/*.tgz |xargs -n1 tar -C charts/ -xzf
```

#### `type`

If `type` isn't specified, current value type will be used.

```yaml
# Will be parsed as 'string'
# @schema
# title: Some title
# description: Some description
# @schema
name: foo

# Will be parsed as 'boolean'
# @schema
# type: boolean
# @schema
enabled: true

# You can define multiple types as an array.
# @schema
# type: [string, integer]
# minimum: 0
# @schema
cpu: 1
```

#### `title`

By default, the `title` will be parsed from the key name. If the key is `foo`, then `title: foo`.

```yaml
# Define a custom title for the key
# @schema
# title: My custom title for 'foo'
# @schema
bar: foo
```

#### `description`

You can provide the `description` through its property or let it be parsed from your comments. If `description` is provided, the comments will not be parsed as description.

If you're implementing it alongside `helm-docs`, read [this](#helm-docs) to do it correctly.

```yaml
# This text will be used as description.
# @schema
# type: integer
# minimum: 1
# @schema
replica: 1

# @schema
# type: integer
# minimum: 1
# @schema
# This text will be used as description.
replica: 1

# @schema
# type: integer
# minimum: 1
# description: This text will be used as description.
# @schema
# And not this one
replica: 1
```

#### `default`

Help users when using their IDE to quickly retrieve the `default` value, for example through CTRL+SPACE.

```yaml
# @schema
# default: standalone
# enum: [standalone,cluster]
# @schema
architecture: ""

# @schema
# type: boolean
# default: true
# @schema
enabled: true
```

#### `properties`

Allows user to define valid keys without defining them yet. Give the user an insight of the possible properties, their types and description.

By default, `title` for the keys defined under `properties` will inherit from the main key (e.g. here `title: env`). You need to provide `title` explicitly if you want to change it.

```yaml
# @schema
# properties:
# CONFIG_PATH:
# title: CONFIG_PATH
# type: string
# description: The local path to the service configuration file
# ADMIN_EMAIL:
# title: ADMIN_EMAIL
# type: string
# format: idn-email
# API_URL:
# type: string
# format: idn-hostname
# description: Title will be 'env' as we do not specify it here
# @schema
# -- Environment variables. If you want to provide auto-completion to the user
env: {}
```

#### `pattern`

Pattern that'll be used to test the value.

```yaml
# @schema
# pattern: ^api-key
# @schema
# The value have to start with the 'api-key-' prefix
apiKey: "api-key-xxxxx"
```

#### `format`

Known formats that the value must match. Formats available at [JSON Schema - Formats](https://json-schema.org/understanding-json-schema/reference/string.html#format).

```yaml
# @schema
# format: idn-email
# @schema
# Requires a valid email format
email: [email protected]
```

#### `required`

By default every property is a required property, you can disable this with `required: false` for a single key. You can also invert this behaviour with the option `helm-schema -k required`, now every property is an optional one.

```yaml
# @schema
# required: false
# @schema
altName: foo
```

It's also possible to define an array of required properties on the parent.

```yaml
# @schema
# required: [foo]
# @schema
altName:
foo: bar
```

#### `deprecated`

Let the user know if the key is deprecated, hence should be avoided.

```yaml
# @schema
# deprecated: true
# @schema
secret: foo
```

#### `items`

If you want to specify a schema for possible array values without using a default value. E.g. to define the structure of the hosts definition in an k8s ingress resource.

```yaml
# @schema
# type: array
# items:
# type: object
# properties:
# host:
# type: object
# properties:
# url:
# type: string
# format: idn-hostname
# @schema
# Will give auto-completion for the below structure
# hosts:
# - name:
# url: my.example.org
hosts: []
```

#### `enum`

Allows user to define available values for a given key. Validation will fail and error shown if you try to put another value.

```yaml
# @schema
# enum:
# - application
# - controller
# - api
# @schema
# Only those three values are accepted
type: application

# @schema
# type: array
# items:
# enum: [api,frontend,backend,microservice,teamA,teamB,us-west-1,us-west-2]
# @schema
# For each array index, only one of those values are accepted
tags:
- "api"
- "teamA"
- "us-west-2"
```

#### `const`

Defines a constant value which shouldn't be changed.

```yaml
# @schema
# const: [email protected]
# @schema
maintainer: [email protected]
```

#### `examples`

Provides example values to the user when hovering the key in IDE, or by auto-completion mechanism.

```yaml
# @schema
# format: ipv4
# examples: [192.168.0.1]
# @schema
clusterIP: ""

# @schema
# properties:
# CONFIG_PATH:
# type: string
# description: The local path to the service configuration file
# examples: [/path/to/config]
# ADMIN_EMAIL:
# type: string
# format: idn-email
# examples: [[email protected]]
# API_URL:
# type: string
# format: idn-hostname
# examples: [https://api.example.org]
# @schema
# -- Provide auto-completion and examples to the user
env: {}
```

#### `minimum`

The value have to be above or equal the given `integer`.

```yaml
# @schema
# minimum: 1
# @schema
replica: ""
```

#### `exclusiveMinimum`

The value have to be strictly above the given `integer`.

```yaml
# @schema
# exclusiveMinimum: 0
# @schema
replica: ""
```

#### `maximum`

The value have to be below or equal the given `integer`.

```yaml
# @schema
# maximum: 10
# @schema
replica: ""
```

#### `exclusiveMaximum`

The value have to be strictly below the given `integer`.

```yaml
# @schema
# exclusiveMaximum: 5
# @schema
cpu: ""
```

#### `multipleOf`

The value have to be a multiple of the given `integer`.

```yaml
# @schema
# multipleOf: 1024
# @schema
storageCapacity: 2048
```

#### `additionalProperties`

By default, `additionalProperties` is set to `false` unless you use the `-k additionalProperties` option. Useful when you don't know what nested keys you'll have.

```yaml
# @schema
# additionalProperties: true
# @schema
# You'll be able to add as many keys below `env:` as you want without invalidating the schema
env:
LONG: foo
LIST: bar
OF: baz
VARIABLES: bat

# @schema
# additionalProperties: true
# properties:
# REQUIRED_VAR:
# type: string
# @schema
env:
REQUIRED_VAR: foo
OPTIONAL_VAR: bar
```

#### `patternProperties`

Mapping schemas to key name patterns. If properties match the patterns, the given schema is applied.

Useful when you work with a long list of keys and want to define a common schema for a group of them, for example.

E.g. `patternProperties."^API_.*"` key defines the pattern whose schema will be applied on any user provided key that match that pattern.

```yaml
# @schema
# type: object
# patternProperties:
# "^API_.*":
# type: string
# pattern: ^api-key
# "^EMAIL_.*":
# type: string
# format: idn-email
# @schema
env:
API_PROVIDER_ONE: api-key-xxxxx
API_PROVIDER_TWO: api-key-xxxxx
EMAIL_ADMIN: [email protected]
EMAIL_DEFAULT_USER: [email protected]
```

#### `anyOf`

Allows user to define multiple schema fo a single key. Key can be `anyOf` the given schemas or none of them.

```yaml
# Accepts multiple types
# @schema
# anyOf:
# - type: string
# - type: integer
# minimum: 0
# @schema
foot: 1

# The above can be simplified with `type:`
# @schema
# type: [string, integer]
# minimum: 0
# @schema
fool: 1

# A pattern is also possible.
# In this case null or some string starting with foo.
# @schema
# anyOf:
# - type: "null"
# - pattern: ^foo
# @schema
bar:
```

#### `oneOf`

Allows user to define multiple schema fo a single key. Key must match `oneOf` the given schemas.

```yaml
# @schema
# oneOf:
# - type: integer
# - pattern: Gib$
# - pattern: gib$
# @schema
storage: 30Gib
```

#### `allOf`

Allows user to define multiple schema for a single key. Key must match `oneOf` the given schemas.

```yaml
# @schema
# allOf:
# - type: string
# pattern: Gib$
# - enum: [5Gib,10Gib,15Gib]
# @schema
storage: 10Gib
```

#### `not`

Allows to define a schema that must not be matched.

```yaml
# @schema
# not:
# type: string
# @schema
foo: bar
```

#### `if/then/else`

Conditional schema settings with `if`/`then`/`else`

```yaml
# @schema
# anyOf:
# - type: "null"
# - type: string
# if:
# type: "null"
# then:
# description: It's a null value
# else:
# description: It's a string
# @schema
unknown: foo
```

#### `minLength`

The value must be an integer greater or equal to zero and defines the minimum length of a string value.

```yaml
# @schema
# minLength: 1
# @schema
namespace: foo
```

#### `maxLength`

The value must be an integer greater than zero and defines the maximum length of a string value.

```yaml
# @schema
# maxLength: 3
# @schema
namespace: foo
```

#### `$ref`

The value must be an URI or relative file.

Relative files are imported on creation time. If you update the referenced file, you need
to run helm-schema again.

**foo.json:**

```json
{
"foo": {
"type": "string",
"minLength": 10
}
}
```

```yaml
# @schema
# $ref: foo.json#/foo
# @schema
namespace: foo
```

is the same as

```yaml
# @schema
# type: string
# minLength: 10
# @schema
namespace: foo
```

## License

[MIT](https://github.com/dadav/helm-schema/blob/main/LICENSE)