# HULL - Helm Uniform Layer Library


This repository contains the HULL Helm library chart. It is designed to ease building, maintaining and configuring Kubernetes objects in Helm charts and can be added to any Helm chart as an addon to enhance functionality without any risk of breaking existing Helm chart configurations.

The chart itself and all documentation related to it can be found in the [`hull`](https://github.com/vidispine/hull/blob/main/hull) folder which is the root folder of the HULL library helm chart.

The Kubernetes API JSON Schemas are stored in the [`kubernetes-json-schema`](https://github.com/vidispine/hull/blob/main/kubernetes-json-schema) folder.

## Introduction

One major design aspect of [Helm](https://helm.sh/) is that it forces the user to create individual abstractions of the Kubernetes configuration of applications. For each individual Helm Chart that is realized in form of YAML templates in a [Helm charts](https://helm.sh/docs/topics/charts/) `/templates` folder. These template files, containing boilerplate Kubernetes YAML code blocks on the one hand and custom configuration mappings utilizing Go Templating expressions on the other hand, provide the glue between the configuration of the application via the central `values.yaml` configuration file and the desired Kubernetes YAML output. Arguably this approach of per-application abstraction is suited well to create tailormade configuration packages for even the most specialized applications but comes at a cost of having a large overhead for simpler, recurring and off-the-shelf application packaging use cases. Creating, maintaining and (often) understanding the abstractions introduced by Helm Charts - especially when facing a high number of individual Helm charts from various sources - can become tedious and challenging.

The primary feature of the HULL library is the ability to remove customized YAML template files entirely from Helm chart workflows and thereby allowing to remove a level of abstraction. Using the HULL library chart, Kubernetes objects including all their properties can be completely and transparently specified in the `values.yaml`. The HULL library chart itself provides the uniform layer to streamline specification, configuration and rendering of Helm charts to achieve this. You can also think of it as a thin layer on top of the Kubernetes API to avoid the middleman between Helm Chart and Kubernetes API object configuration, yet providing flexibility when it is required to customize individual configuration options instead of requiring you to add each configuration switch manually to the templates. JSON schema validation based on the [Helm JSON validation feature](https://helm.sh/docs/topics/charts/#schema-files) (via `values.schema.json`) aids in writing Kubernetes API conforming objects right from the beginning when [using an IDE that supports live JSON schema validation](https://github.com/vidispine/hull/blob/main/hull/doc/json_schema_validation.md). Additional benefits (uniform inheritable object metadata, simplified inclusion of ConfigMaps/Secrets, cross-referencing values within the `values.yaml`, ...) are available with HULL which you can read about below in the **Key Features Overview**. But maybe most importantly, the HULL library can be added as a dependency to any existing Helm chart and be used side-by-side without breaking any existing Helm charts functionalities, see [adding the HULL library chart to a Helm chart](https://github.com/vidispine/hull/blob/main/hull/doc/setup.md) for more information. And lastly, by being a library chart itself, everything works 100% within the functionality that plain Helm offers - no additional tooling is introduced or involved.

**Your feedback on this project is valued, hence please comment or start a discussion in the** `Issues` section or create feature wishes and bug reports. Thank you!

The HULL library chart idea is partly inspired by the [common](https://github.com/helm/charts/tree/master/incubator/common) Helm chart concept and for testing

[![Gauge Badge](https://camo.githubusercontent.com/2e30aa9636dd5990c8a4af2dacbfd357732f4bb7e4cac1c7843be3f4688da96c/68747470733a2f2f67617567652e6f72672f47617567655f42616467652e737667 align="left")](https://gauge.org/)

.

[![Build Status](https://camo.githubusercontent.com/255f0827e6c7ced49ea3ad5003f3bb2854e86304ea2e98441006e3c90808ae09/68747470733a2f2f6465762e617a7572652e636f6d2f61727661746f2d73797374656d732d646d6d2f56504d533325323043726f737343757474696e672f5f617069732f6275696c642f7374617475732f766964697370696e652e68756c6c3f6272616e63684e616d653d6d61696e align="left")](https://dev.azure.com/arvato-systems-dmm/VPMS3%20CrossCutting/_build/latest?definitionId=589&branchName=main)

## Quick Start - the `hull-demo` chart

Before diving into the details of HULL, here is a first glimpse at how it works. You can simply download the latest version of the `hull-demo` Helm chart from the Releases section of this page, it has everything bootstrapped for testing out HULL or setting up a new Helm Chart based on HULL with minimal effort.

The `hull-demo` chart wraps a fictional application `myapp` with a `frontend` and `backend` deployment and service pair. There is a config file for the server configuration that is mounted to the `backend` pods. The `frontend` pods need to know about the `backend` service address via environment variables. Moreover, the setup should by default be easily switchable from a `debug` setup (using a NodePort for accessing the frontend) to a production-like setup (using a ClusterIP service and an ingress).

A bare default structure to capture these aspects may look like this (with added line comments for explanation):

```plaintext
hull: # HULL is configured via subchart key 'hull'
  config: # chart setup takes place here for everything besides object definitions
    specific: # central place for shared values specific to this chart
      debug: true # a switch influencing creation of objects in this chart
      application_version: v23.1 # a shared image tag for multiple container
      myapp: # some exemplary configuration settings for the app, exposed here for transparency
        rate_limit: 100
        max_connections: 5     
  objects: # all objects to create are defined here
    deployment: # create deployments
      myapp-frontend: # the base part of the object name for frontend deployment
        pod: # configure pod-related aspects
          containers: # non-init containers
            main: # one main container
              image: # provide image reference
                repository: mycompany/myapp-frontend # repository
                tag: _HT*hull.config.specific.application_version # reference to central tag value above
              ports: # exposed ports
                http: # port name is http
                  containerPort: 80 # the port number
              env: # environment variables
                SERVER_HOSTNAME: # name of variable
                  value: _HT^myapp-backend # value is dynamically rendered reference to myapp-backend service name
                SERVER_PORT: # name of variable
                  value: "8080" # backend service port
      myapp-backend: # the base part of the object name for backend deployment
        pod: # configure pod-related aspects
          containers: # non-init containers
            main: # one main container
              image: # image reference
                repository: mycompany/myapp-backend # repository
                tag: _HT*hull.config.specific.application_version # reference to central tag value above
              ports: # exposed ports
                http: # port name is http
                  containerPort: 8080 # the port number
              volumeMounts: # mounts of the container
                appconfig: # context key is appconfig
                  name: myappconfig # the name needs to match a volume
                  mountPath: /etc/config/appconfig.json # mountPath
                  subPath: backend-appconfig.json # subPath
          volumes: # volumes that may be mounted
            myappconfig: # key matching a volumeMounts name
              configMap: # configmap reference
                name: myappconfig # the configmap to load, simply referenced by key name   
    configmap: # create configmaps
      myappconfig: # the backend configuration
        data: # data section
          backend-appconfig.json: # key name is file name
            inline: |- # define the contents of the file, using templating logic and references
              {
                "rate-limit": {{ .Values.hull.config.specific.myapp.rate_limit }}, 
                "max-connections": {{ .Values.hull.config.specific.myapp.max_connections }}, 
                "debug-log": {{ if .Values.hull.config.specific.debug }}true{{ else }}false{{ end }}
              }
    service: # create services
      myapp-frontend: # frontend service, automatically matches pods with identical parent object's key name
        type: |-  # dynamically switch type based on hull.config.specific.debug setting
          _HT!
            {{- if (index . "$").Values.hull.config.specific.debug -}}
            NodePort
            {{- else -}}
            ClusterIP
            {{- end -}}
        ports: # definition of service ports
          http: # http port for type=ClusterIP
            enabled: _HT?not (index . "$").Values.hull.config.specific.debug # bind rendering to debug: false condition
            port: 80 # regular port 
            targetPort: http # targetPort setting
          http_nodeport: # http port for type=NodePort
            enabled: _HT?(index . "$").Values.hull.config.specific.debug # bind rendering to debug: true condition
            port: 80 # regular port 
            nodePort: 31111 # the node port
            targetPort: http # targetPort setting
      myapp-backend: # backend service, automatically matches pods with identical parent object's key name
        type: ClusterIP # in cluster service
        ports: # definition of service ports
          http: # http port
            port: 8080 # regular port 
            targetPort: http # targetPort setting
    ingress: # create ingresses
      myapp: # the central frontend ingress
        enabled: _HT?not (index . "$").Values.hull.config.specific.debug # rendering bound to debug: false
        rules: # the ingress rules
          myapp: # key-value dictionary of rules
            host: SET_HOSTNAME_HERE # change the host at deployment time to actual one
            http: # http settings
              paths: # paths definition
                standard: # a standard path definition
                  path: / # could be changed at deployment time
                  pathType: ImplementationSpecific # path type
                  backend: # backend config
                    service: # service targeted
                      name: myapp-frontend # key name suffices to reference service created in this chart
                      port: # target port
                        name: http # target port name
```

This is the example constituting as `hull-demo`'s `values.yaml`, if you download the latest `hull-demo` release and execute:

```plaintext
helm template hull-demo-<version>.tgz
```

it renders out a set of objects based on above `values.yaml` containing:

* a deployment for `myapp-frontend` that has a centrally configured image `tag` set (by default `v23.1`), and environment variables pointing to the `myapp-backend`'s service in-cluster address
    
* a deployment for `myapp-backend` that has a centrally configured image `tag` set (by default `v23.1`) and a configuration mounted from the `myappconfig` ConfigMap
    
* a `myappconfig` ConfigMap with a JSON file that is dynamically built by incorporating templating expressions and referencing values defined elsewhere in `values.yaml`
    
* a simple ClusterIP Service fronting `myapp-backend` Deployment
    
* a service fronting `myapp-frontend` deployment whose type and port configuration is dependend on the central `debug` switch - either type `NodePort` in a `debug` setup mode or type `ClusterIP` in combination with a `myapp` ingress in non-debug setups
    
* an ingress object `myapp` which is only rendered/created in case the `debug: false` value is set
    

Every aspect of this configuration can be changed or overwritten at deployment time using additional `values.yaml` overlay files, for example:

* switching the overall configuration from and to `debug` mode by settings `debug: true` or `debug: false`
    
* adding resource definitions to the deployments
    
* setting hostname and path for the ingress
    
* add further environment variables to pods
    
* change `myapp` ConfigMaps source values (`rate_limit` and `max_connections`) or overwrite it completely
    
* ...
    

All objects and logic was created with under a hundred lines of overall configuration code in the `hull-demo`'s `values.yaml`. You can test all of the above mentioned aspects or simply experiment by adding additional `values.yaml` overlay files to the `helm template` command above. For bootstrapping your own Helm chart, just empty the `values.yaml` configuration, rename the charts folder and `name` in `Chart.yaml` to whatever you want and you are ready to go.

This is a first demo of how HULL could be used but there is a lot more functionality and supported use-cases. Check the key features and the detailed documentation for more information.

## Key Features Overview

As highlighted above, when included in a Helm chart the HULL library chart can take over the job of dynamically rendering Kubernetes objects from their given specifications from the `values.yaml` file alone. With YAML object construction deferred to the HULL library's Go Templating functions instead of custom YAML templates in the `/templates` folder you can centrally enforce best practices:

* Concentrate on what is needed to specify Kubernetes objects without having to add individual boilerplate YAML templates to your chart. This removes a common source of errors and maintenance from the regular Helm workflow. **To have the HULL rendered output conform to the Kubernetes API specification, a large number of unit tests validate the HULL rendered output against the Kubernetes API JSON schema.**
    
    For more details refer to the documentation on [JSON Schema Validation](https://github.com/vidispine/hull/blob/main/hull/doc/json_schema_validation.md).
    
* For all Kubernetes object types supported by HULL, **full configurational access to the Kubernetes object types properties is directly available**. This relieves chart maintainers from having to add missing configuration options one by one and the Helm chart users from forking the Helm chart to add just the properties they need for their configuration. Only updating the HULL chart to a newer version with matching Kubernetes API version is required to enable configuration of properties added to Kubernetes objects meanwhile in newer API versions. The HULL charts are versioned to reflect the minimal Kubernetes API versions supported by them.
    
    For more details refer to the documentation on [Architecture Overview](https://github.com/vidispine/hull/blob/main/hull/doc/architecture.md).
    
* The single interface of the HULL library is used to both create and configure objects in charts for deployment. This fosters the mutual understanding of chart creators/maintainers and consumers of how the chart actually works and what it contains. Digging into the `/templates` folder to understand the Helm charts implications is not required anymore. To avoid any misconfiguration, the interface to the library - the `values.yaml` of the HULL library - is fully JSON validated. **When using an IDE supporting live JSON schema validation (e.g. VSCode) you can get IDE guidance when creating the HULL objects. Before rendering, JSON schema conformance is validated by the HULL library.**
    
    For more details refer to the documentation on [JSON Schema Validation](https://github.com/vidispine/hull/blob/main/hull/doc/json_schema_validation.md).
    
* **Uniform and rich metadata is automatically attached to all objects created by the HULL library.**
    
    * Kubernetes standard labels as defined for [Kubernetes](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/) and [Helm](https://helm.sh/docs/chart_best_practices/labels/#standard-labels) are added to all objects metadata automatically.
        
    * Additional custom labels and annotations metadata can be set hierarchically for:
        
        * all created Kubernetes objects or
            
        * all created Kubernetes objects of a given type or
            
        * any individual Kubernetes object.
            
    
    For more details on metadata overwriting refer to the advanced example below.
    
* Flexible handling of ConfigMap and Secret input by choosing between inline specification of contents in `values.yaml` or import from external files for contents of larger sizes. When importing data from files the data can be either run through the templating engine or imported un-templated 'as is' if it already contains templating expressions that shall be passed on to the consuming application. With a focus on convenient handling of standard scenarios, you can also define file contents as a regular YAML structure in the `values.yaml` and have HULL serialize it automatically to JSON or YAML by the file extension or explicily to any representation of your choice. HULL takes care of the Base64 encoding of Secrets so writing ConfigMaps and Secrets works the exact same way and **adding ConfigMaps or Secrets to your deployment requires only a few lines of code.**
    
    For more details refer to the documentation on [ConfigMaps and Secrets](https://github.com/vidispine/hull/blob/main/hull/doc/objects_configmaps_secrets.md).
    
* Extensive defaulting capabilities for instantiating object instances. Whether you want to have all your object instances or groups of instances share certain aspects such as labels or annotations, container environment variables or mounted volumes, HULL provides support to efficiently define default values for object instance fields avoiding unnecessary configuration repetitions.
    
    For more details refer to the [Chart Design](https://github.com/vidispine/hull/blob/main/hull/doc/chart_design.md) advices.
    
* For more complex scenarios where actual values in the target YAML are subject to configurations in the `values.yaml`, there is **support to dynamically populate values by injecting Go Templating expressions defined in place of the value in the** `values.yaml`. For example, if your concrete container arguments depend on various other settings in `values.yaml` you can inject the conditions into the calculation of the arguments or simply reference other fields in the `values.yaml`.
    
    For more details refer to the documentation on [Transformations](https://github.com/vidispine/hull/blob/main/hull/doc/transformations.md).
    
* Enable automatic hashing of referenced ConfigMaps and Secrets to facilitate pod restarts on changes of configuration when required.
    
    For more details refer to the documentation on [Pods](https://github.com/vidispine/hull/blob/main/hull/doc/objects_pod.md).
    

To learn more about the general architecture and features of the HULL library see the [Architecture Overview](https://github.com/vidispine/hull/blob/main/hull/doc/architecture.md)

## Important information

Some important things to mention first before looking at the library in more detail:

⚠️ **While there may be several benefits to rendering YAML via the HULL library please take note that it is a non-breaking addition to your Helm charts. The regular Helm workflow involving rendering of YAML templates in the** `/templates` folder is completely unaffected by integration of the HULL library chart. Sometimes you might have very specific requirements on your configuration or object specification which the HULL library does not meet so you can use the regular Helm workflow for them and the HULL library for your more standard needs - easily in parallel in the same Helm chart. ⚠️

⚠️ **Note that a single static file, the** `hull.yaml`, must be copied 'as-is' without any modification from an embedded HULL charts root folder to the parent charts `/templates` folder to be able to render any YAML via HULL. It contains the code that initiates the HULL rendering pipeline, see [adding the HULL library chart to a Helm chart](https://github.com/vidispine/hull/blob/main/hull/doc/setup.md) for more details! ⚠️

⚠️ **At this time HULL releases are tested against all existing non-beta and non-alpha Helm 3 CLI versions. Note that Helm CLI versions** `3.0.x` are not compatible with HULL, all other currently existing non-beta and non-alpha versions are compatible. ⚠️

⚠️ **It is intended to support the latest 3 major Kubernetes releases with corresponding HULL releases. At this time Kubernetes versions** `1.29` and `1.30` and `1.31` have a matching and maintained HULL release. ⚠️

## NEW! The HULL Tutorials

If you like a hands on approach you are invited to take a look at the [new HULL tutorials series at dev.to](https://dev.to/gre9ory/series/18319)! The eigth part tutorial will start from the very beginning of setting up Helm and creating a HULL based chart to finalizing a real life HULL based Helm Chart step by step. To highlight the differences to the regular Helm chart workflow the tutorials take the popular [`kubernetes-dashboard`](https://artifacthub.io/packages/helm/k8s-dashboard/kubernetes-dashboard) Helm chart as a source and transport it to a functionally equivalent HULL based Helm chart. In the end it shows that reducing the lines of configuration to create and maintain can be reduced by more than 50% when using a HULL based approach instead of the regular Helm style of writing charts!

## Creating and configuring a HULL based chart

The tasks of creating and configuring a HULL based helm chart can be considered as two sides of the same coin. Both sides interact with the same interface (the HULL library) to specify the objects that should be created. The task from a creators/maintainers perspective is foremost to provide the ground structure for the objects that make up the particular application which is to be wrapped in a Helm chart. The consumer of the chart is tasked with appropriately adding his system specific context to the ground structure wherein he has the freedom to change and even add or delete objects as needed to achieve his goals. At deploy time the creators base structure is merged with the consumers system-specific yaml file to build the complete configuration. Interacting via the same library interface fosters common understanding of how to work with the library on both sides and can eliminate most of the tedious copy&paste creation and examination heavy configuration processes.

So all that is needed to create a helm chart based on HULL is a standard scaffolded helm chart directory structure. Add the HULL library chart as a sub-chart, copy the `hull.yaml` from the HULL library chart to your parent Helm charts `/templates` folder. Then just configure the default objects to deploy via the `values.yaml` and you are done. There is no limit as to how many objects of which type you create for your deployment package.

But besides allowing to define more complex applications with HULL you could also use it to wrap simple Kubernetes Objects you would otherwise either deploy via kubectl (being out-of-line from the management perspective with helm releases) or have to write a significant amount of Helm boilerplate templates to achieve this.

The base structure of the `values.yaml` understood by HULL is given here in the next section. This essentially forms the single interface for producing and consuming HULL based charts. Any object is only created in case it is defined and enabled in the `values.yaml`, this means you might want to pre-configure objects for consumers that would just need to enable them if they want to use them.

At the top level of the YAML structure, HULL distinguishes between `config` and `objects`. While the `config` sub-configuration is intended to deal with chart specific settings such as metadata and product settings, the concrete Kubernetes objects to be rendered are specified under the `objects` key. An additional third top level key named `version` is allowed as well, when this is being set to the HULL charts version for example during the parent Helm Charts release pipeline it will automatically populate the label `vidispine.hull/version`on all objects indicating the HULL version that was used to render the objects.

### *The* `config` section

Within the `config` section you can configure general settings for your Helm chart. It is divided into two subsections, `config.general` and `config.specific`.

### *The* `config.general` section

In contrast to the `config.specific` section, which should be populated with arbitrary data that is specific only to a single helm chart, the `config.general` section should be used to define everything that is not particular to a unique application. On the one hand it holds configuration options which are relevant for all HULL based charts but also leaves room under the `config.general.data` entry to define your own data fields which ideally are modeled the same way in other helm charts. For example, if several applications in a product suite depend on the same endpoints, you could model these endpoints uniformly under the `general.data` property in all relevant charts and thereby having your helm charts interface in the same way with e.g. a continuous deployment pipeline.

  
  
`config.general` has only the following sub-fields:  
  
`nameOverride`  
`fullnameOverride`  
`namespaceOverride`  
`noObjectNamePrefixes`  
`createImagePullSecretsFromRegistries`  
`globalImageRegistryServer`  
`globalImageRegistryToFirstRegistrySecretServer`  
`debug`  
`rbac`  
`data`  
`serialization`  
`postRender`  
`errorChecks`  
`metadata`

| Parameter | Description | Default | Example |
| --- | --- | --- | --- |
| `nameOverride` | The name override is applied to values of metadata label `app.kubernetes.io/name`. If set this effectively replaces the chart name here. |  |  |
| `fullnameOverride` | If set to a value, the fullname override is applied as a prefix to all object names and replaces the standard `<release>-<chart>` prefix pattern in object names. |  | `myapp` |
| `namespaceOverride` | If set to a value, the namespace of all created objects is set to this value. If this is not defined, the namespace of all object instances defaults to the release namespace provided to the respective helm command. |  | `my-namespace` |
| `noObjectNamePrefixes` | If set, the object instance keys directly serve as the names for the Kubernetes objects created and are never prefixed. This is technically equivalent to setting `staticName` true on each object. Note that by setting this to `true` the value of `config.general.fullnameOverride` becomes irrelevant. | `false` | `true` |
| `createImagePullSecretsFromRegistries` | If true, image pull secrets are created from all registries defined in this Helm chart and are added to all pods. | `true` | `false` |
| `globalImageRegistryServer` | If not empty the `registry` field of all container `image` fields is set to the value given here. The setting of `config.general.globalImageRegistryToFirstRegistrySecretServer` is ignored if this field is non-empty. All defined explicit `registry` settings for an `image` are overwritten with this value.  
  
Intended usage of this is to conveniently have all images pulled from a central docker registry in case of air-gap like deployment scenarios.  
  
Contrary to setting `globalImageRegistryToFirstRegistrySecretServer` to `true`, in this case the registry secret is typically defined outside of this helm chart and the registry secret's server is referenced by its name directly. If you use this feature and define the Docker registry secret outside of this Helm chart you may additionally need to add `imagePullSecrets` to your pods in case the referenced Docker registry is not insecure. | `""` | `mycompany.docker-registry.io` |
| `globalImageRegistryToFirstRegistrySecretServer` | If true and `globalImageRegistryServer` is empty, the `registry` field of all container `image` fields is set to the `server` field of the first found `registry` object. Note that this is the registry with the lowest alphanumeric key name if you provide multiple `registry` obejcts. Should normally be used together with setting `createImagePullSecretsFromRegistries` to `true` to benefit from autopopulated `imagePullSecrets` and accordingly set `registry`. Explicit `registry` settings for an `image` are overwritten with this value.  
  
Intended usage of this setting is to conveniently have all images pulled from a central docker registry in case of air-gap like deployment scenarios. | `false` | `true` |
| `errorChecks` | Options that determine in which cases HULL will generate an error on `helm install` or `helm template`. For more details see also the detailed documentation on [configuring error checks](https://github.com/vidispine/hull/blob/main/hull/doc/error_checking.md)  
  
Has only the following sub-fields:  
  
`objectYamlValid`  
`hullGetTransformationReferenceValid`  
`containerImageValid`  
`virtualFolderDataPathExists`  
`virtualFolderDataInlineValid` |  |  |
| `errorChecks.objectYamlValid` | Validate that no broken YAML is rendered | `true` |  |
| `errorChecks.hullGetTransformationReferenceValid` | Validate that all `_HT*` references point to an existing key in the `values.yaml` | `true` |  |
| `errorChecks.containerImageValid` | Validate that all `pod`'s `containers` and `initContainers` `image` sections exist and have at least a `repository` set | `true` |  |
| `errorChecks.virtualFolderDataPathExists` | Validate that all files being refered to in a ConfigMap and Secret's `data` `path` field do physically exist | `true` |  |
| `errorChecks.virtualFolderDataInlineValid` | Validate that no `null` values or missing values (which are converted to empty strings) are set for ConfigMap and Secret's `data` `inline` fields | `false` |  |
| `debug` | Options that can help with debugging chart problems. Mostly obsolete and replaced by speaking default error messages configured under `errorChecks`.  
  
Has only the following sub-fields:  
  
`renderBrokenHullGetTransformationReferences`  
`renderNilWhenInlineIsNil`  
`renderPathMissingWhenPathIsNonExistent` |  |  |
| `debug.renderBrokenHullGetTransformationReferences` | Global switch which if enabled will print out a string:  
  
`HULL failed with error BROKEN-HULL-GET-TRANSFORMATION-REFERENCE: Element <y> in path <x.y.z> was not found`  
  
including the `_HT*/hull.util.transformation.get` reference (`x.y.z`) and the missing key (`y`) if the transformation references a non existing dictionary key. This is useful to debug chart rendering and reduces searching for broken references because normally the installation aborts with an error on broken references (which may make it hard to pin point the problematic reference(s)).  
  
**NOTE:  
  
By now any broken get reference will be signaled by a speaking helm error by default so this switch is mostly obsolete for debugging broken references. It is recomended to disable this option and fail hard on broken get references instead and analyze problems directly from the error message.** | `false` | `true` |
| `debug.renderNilWhenInlineIsNil` | Global switch which if enabled will print out a string:  
  
`<nil>`  
  
as a `data` fields value when an `inline` spec references a `nil` pointer in a ConfigMap or Secret. If set to false, the `nil` value will be printed as an empty string in the ConfigMap or Secret `data` field.  
  
**NOTE:  
  
By now any invalid inline fields will be signaled by a speaking helm error by default (meaning** `hull.config.general.errorChecks.virtualFolderDataInlineValid` is `true`). Enabling this switch is mostly obsolete for debugging and it is recomended to disable this option and fail hard on invalid inline fields. | `false` | `true` |
| `debug.renderPathMissingWhenPathIsNonExistent` | Global switch which if enabled will print out a string:  
  
`<path missing: the_missing_path>`  
  
in a ConfigMap or Secret `data` fields value including the `the_missing_path` value which does not resolve to a file. If false, the `data` fields value will resolve to an empty string.  
  
**NOTE:  
  
By now any non-existent file referenced in a path field will be signaled by a speaking helm error by default (meaning** `hull.config.general.errorChecks.virtualFolderDataPathExists` is `true`). Enabling this switch is mostly obsolete for debugging and it is recomended to disable this option and fail hard on non-existing file path references. | `false` | `true` |
| `render` | Options to influence how HULL renders out objects as YAML.  
  
Has only the following sub-fields:  
  
`emptyAnnotations`  
`emptyLabels`  
`emptyHullObjects` |  |  |
| `render.emptyAnnotations` | If `true`, HULL renders out `annotations: {}` if no annotations exist for an object, if `false` the `annotations` key is omitted. | `false` | `true` |
| `render.emptyLabels` | If `true`, HULL renders out `labels: {}` if no labels exist for an object, if `false` the `labels` key is omitted. | `false` | `true` |
| `render.emptyTemplateAnnotations` | If `true`, HULL renders out `annotations: {}` in the `template` of a pod if no annotations exist for an object, if `false` the `annotations` key is omitted. | `false` | `true` |
| `render.emptyTemplateLabels` | If `true`, HULL renders out `labels: {}` in the `template` of pods `if no labels exist for an object, if`false`the`labels\` key is omitted. | `false` | `true` |
| `render.emptyHullObjects` | If `true`, HULL renders out arrays as empty arrays if no elements exist for some fields processed by HULL. If false, the key-value pair is ommited.  
  
This affects fields which are mapped from a dictionary in HULL configuration to a Kubernetes array in the rendered YAML. The following is a list of affected fields in HULL's object configuration:  
  
`data` in `secret` and `configmap` objects`initContainers`, `containers`, `volumes` and `imagePullSecrets` in `cronjob`,`daemonset`, `deployment`, `job` and `statefulset` `pod` objects`ports`, `env`, `envFrom` and `volumeMounts` in `initContainers`, `containers` and `volumes` in `cronjob`,`daemonset`, `deployment`, `job` and `statefulset` `pod` objects`ports` in `service` objects`rules` and `tls` in `ingress` objects`http.paths` in `rules` in `ingress` objects`webhooks` in `validatingwebhookconfiguration` and `mutatingwebhookconfigurationrules` in `clusterrole` and `role` | `false` | `true` |
| `postRender` | After HULL has fully rendered an object it is possible to manipulate the resulting YAML string. Possibilities to do so are provided as `postRender` actions here.  
  
**WARNING: Use with caution as this may corrupt the YAML structure!** |  |  |
| `postRender.globalStringReplacements` | A dictionary of replacement possibilities that may be applied to the rendered object's YAML. The main use case for this is in combination with extensive defaulting in `_HULL_OBJECT_TYPE_DEFAULT_` and `sources` object instances where it allows to inject instance specific strings into the defaulted YAML. The preconfigured mappings provided may be `enabled: true` on demand. Each mapping consists of following fields:`enabled`: execute mapping if `truestring`: the exact string part to be replaced`replacement`: the type of value inserted instead of `string`. Not a free string but one of the following values: `OBJECT_INSTANCE_KEY`, `OBJECT_INSTANCE_KEY_RESOLVED` or `OBJECT_INSTANCE_NAME` |  |  |
| `postRender.globalStringReplacements.instanceKey` | If `enabled`, the `string` value will be replaced with the actual object's `instance_key` as in `hull.objects.<object_type>.<instance_key>`. The value of `replacement` is `OBJECT_INSTANCE_KEY` for this mapping. | `instanceKey:`  
  `enabled:` `false`  
  `string:` `_HULL_OBJECT_TYPE_DEFAULT_`  
  `replacement:` `OBJECT_INSTANCE_KEY` |  |
| `postRender.globalStringReplacements.instanceKeyResolved` | If `enabled`, the `string` value will be replaced with the actual object's `instance_key` as in `hull.objects.<object_type>.<instance_key>` or by `hull.objects.<object_type>.<instance_key>.metadataNameOverride` if this is defined. The value of `replacement` is `OBJECT_INSTANCE_KEY_RESOLVED` for this mapping. | `instanceKeyResolved:`  
  `enabled:` `false`  
  `string:` `_HULL_OBJECT_TYPE_DEFAULT_`  
  `replacement:` `OBJECT_INSTANCE_KEY_RESOLVED` |  |
| `postRender.globalStringReplacements.instanceName` | If `enabled`, the `string` value will be replaced with the actual object's rendered `metadata.name`. The value of `replacement` is `OBJECT_INSTANCE_NAME` for this mapping. | `instanceName:`  
  `enabled:` `false`  
  `string:` `_HULL_OBJECT_TYPE_DEFAULT_`  
  `replacement:` `OBJECT_INSTANCE_NAME` |  |
| `serialization` | General serialization options. |  |  |
| `serialization.configmap.enabled` | If `enabled`, the mapped file extensions under `fileExtensions` are serialized with the given serialization method by default. If the `data` key ends with one of the mapped extensions the serialization method in the value is used to write the content to string. A specific `serialization` field on a configmaps `data` entry overwrites any default settings. | `true` |  |
| `serialization.configmap.fileExtensions` | A dictionary of mappings from file extensions to serialization methods. | `fileExtensions:`  
  `json:` `toPrettyJson`  
  `yaml:` `toYaml`  
  `yml:` `toYaml` |  |
| `serialization.secret.enabled` | If `enabled`, the mapped file extensions under `fileExtensions` are serialized with the given serialization method by default. If the `data` key ends with one of the mapped extensions the serialization method in the value is used to write the content to string. A specific `serialization` field on a secrets `data` entry overwrites any default settings. | `true` |  |
| `serialization.secret.fileExtensions` | A dictionary of mappings from file extensions to serialization methods. | `fileExtensions:`  
  `json:` `toPrettyJson`  
  `yaml:` `toYaml`  
  `yml:` `toYaml` |  |
| `config.general.rbac` | Global switch which enables RBAC objects for installation.  
  
If `true` all enabled RBAC objects are deployed to the cluster, if `false` no RBAC objects are created at all.  
  
RBAC objects that are deployable are:  
`roles`  
`rolebindings`  
`clusterroles`  
`clusterrolebindings` | `true` | `false` |
| `config.general.data` | Free form field whereas subfields of this field should have a clearly defined meaning in the context of your product suite.  
  
For example, assume all of your products or microservices (each coming as a separate helm chart) depends on the same given endpoints (authentication, configuration, ...). You might have a shared Kubernetes job executed by each helm chart which targets those endpoints. Now you could specify an external HULL `values.yaml` containing the job specification and the endpoint definition here in a way you see fit and construct an overlay `values.yaml` rendered on top of each deployment and have a unified mechanism in place. | `{}` |  |
| `config.general.metadata` | Defined metadata fields here will be automatically added to all objects metadata.  
  
Has only the following sub-fields:  
  
`labels`  
`annotations` |  |  |
| `config.general.metadata.labels` | Labels that are added to all objects. The `common` labels refer to the Kubernetes and Helm common labels and `custom` labels can be freely specified.  
  
Has only the following sub-fields:  
  
`common`  
`custom` |  |  |
| `config.general.metadata.labels.common` | Common labels specification as defined in [https://helm.sh/docs/chart\_best\_practices/labels/](https://helm.sh/docs/chart_best_practices/labels/) and [https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/](https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/).  
  
Unless specifically overwritten with empty values (`''`) all metadata labels are automatically added to all objects according to their default definition. It should be considered to set a value for `config.general.metadata.labels.common.'app.kubernetes.io/part-of'` if the helm chart is part-of a product suite. |  |  |
| `config.general.metadata.labels.common.'app.kubernetes.io/managed-by'` | Managed by metadata. | `{{ .Release.Service }}` |  |
| `config.general.metadata.labels.common.'app.kubernetes.io/version'` | Version metadata. | `{{ .Chart.AppVersion }}` |  |
| `config.general.metadata.labels.common.'app.kubernetes.io/part-of'` | Part-of metadata. | `"unspecified"` |  |
| `config.general.metadata.labels.common.'app.kubernetes.io/name'` | Name metadata. | `{{ printf "%s-%s" .ChartName <hullObjectKey> }}` |  |
| `config.general.metadata.labels.common.'app.kubernetes.io/instance'` | Instance metadata. | `{{ .Release.Name }}` |  |
| `config.general.metadata.labels.common.'app.kubernetes.io/component'` | Component metadata. | `<hullObjectKey>` |  |
| `config.general.metadata.labels.common.'helm.sh/chart'` | Helm metadata. | \`{{ (printf "%s-%s" .Chart.Name .Chart.Version) | replace "+" "\_" }}\` |
| `config.general.metadata.labels.custom` | All specified custom labels are automatically added to all objects of this helm chart. | `{}` |  |
| `config.general.metadata.annotations` | Annotations that are added to all objects. The `custom` labels can be freely specified.  
  
Has only the following sub-fields:  
  
`custom`. |  |  |
| `config.general.metadata.annotations.custom` | All specified custom annotations are automatically added to all objects of this helm chart. | `{}` |  |
| `config.specific` | Free form field that holds configuration options that are specific to the specific product contained in the helm chart. Typically the values specified here ought to be used to populate the contents of configuration files that a particular applications read their configuration from at startup. Hence the `config.specific` fields are typically being consumed in ConfigMaps or Secrets. | `{}` | `maxDatepickerRange:` `50`  
`defaultPoolColor:` `"#FB6350"`  
`updateInterval:` `60000` |

### *The* `objects` section

The top-level object types beneath `hull.objects` represent the supported Kubernetes object types you might want to create instances from. Each object type is a dictionary where the entries values are the objects properties and each object has it's own key which is unique to the object type it belongs to. Further K8S object types can be added as needed to the library so it can easily be extended.

#### *Keys of object instances*

One important aspect is that for all top-level object types, instances of a particular type are always identified by a key which is unique to the instance and object type combination. The same key can however be used for instances of different object types.

By having keys that identify instances you can:

* do multi-layered merging of object properties by stacking `values.yaml` files on top of each other. You might start with defining the default object structure of the application or micro service defined in the given helm chart. Then you might add a `values.yaml` layer for a particular environment like staging or production. Then you might add a `values.yaml` layer for credentials. And so on. By uniquely identifying the instances of a particular K8s object type it becomes easy to adjust the objects properties through a multitude of layers.
    
* use the key of an instance for naming the instance. All instance names are constructed by the following ground rule: `{{ printf "%s-%s-%s" .Release.Name .Chart.Name key }}`. This generates unique, dynamic names per object type and release + instance key combination.
    
    For example, assuming the parent Helm chart is named `my_webservice` and the release named `staging` and given this specification in `values.yaml`:
    
    ```plaintext
    hull:
      objects:
        deployment:
          nginx:
            pod:
              containers:
                nginx:
                  repository: nginx
                  tag: 1.14.2
    ```
    
    a Kubernetes deployment object with the following `metadata.name` is created:
    
    `my_webservice-staging-nginx`
    
    > Note that you can opt to define a static name for instances you create by adding a property `staticName: true` to your objects definition. If you do so the objects name will exactly match the key name you chose.
    
* each particular instance can have an `enabled` sub-field set to `true` or `false`. This way you can predefine instances of object types in your helm charts `values.yaml` but not deploy them in a default scenario. Or enable them by default and refrain from deploying them in a particular environment by disabling them in an superimposed system specific `values.yaml`. Note that unless you explicitly specify `enabled: false` each instance you define will be created by default, a missing `enabled` key is equivalent to `enabled: true`.
    
* cross-referencing objects within a helm chart by the instance key is a useful feature of the HULL library. This is possible in these contexts:
    
    * when a reference to a ConfigMap or Secret comes into play you can just use the key of the targeted instance and the dynamic name will be rendered in the output. This is possible for referencing
        
    * a ConfigMap or Secret behind a Volume or
        
    * a Secret behind an Ingress' TLS specification or
        
    * a ConfigMap or Secret behind an environment value added to a container spec.
        
    * when referencing Services in the backend of an ingress' host you can specify the key to reference the backend service.
        
    
    > Note that you can in these cases opt to refer to a static name instead too. Adding a property `staticName: true` to the dictionary with your reference will force the referenced objects name to exactly match the name you entered.
    

#### *Values of object instances*

The values of object instance keys reflects the Kubernetes objects to create for the chart. To specify these objects efficiently, the available properties for configuration can be split into three groups:

1. Basic HULL object configuration with [hull.ObjectBase.v1](https://github.com/vidispine/hull/blob/main/hull/doc/object_base.md) whose properties are available for all object types and instances. These are `enabled`, `staticName`, `annotations` and `labels`.
    
    Given the example of a `deployment` named `nginx` you can add the following properties of [hull.ObjectBase.v1](https://github.com/vidispine/hull/blob/main/hull/doc/object_base.md) to the object instance:
    
    ```plaintext
    hull:
      objects:
        deployment:
          nginx: # unique key/identifier of the deployment to create
            staticName: true # property of hull.ObjectBase.v1
                             # forces the metadata.name to be just the <KEY> 'nginx' 
                             # and not a dynamic name '<CHART>-<RELEASE>-<KEY>' which 
                             # would be the better default behavior of creating 
                             # unique object names for all objects.
            enabled: true    # property of hull.ObjectBase.v1
                             # this deployment will be rendered to a YAML object if enabled
            labels:
              demo_label: "demo" # property of hull.ObjectBase.v1
                                 # add all labels here that shall be added 
                                 # to the object instance metadata section
            annotations:
              demo_annotation: "demo" # property of hull.ObjectBase.v1
                                      # add all annotations here that shall be added 
                                      # to the object instance metadata section
            pod: 
              ... # Here would come the hull.PodTemplate.v1 definition
                  # see below for details
    ```
    
2. Specialized HULL object properties for some object types. Below is a reference of which object type supports which special properties in addition to the basic object configuration.
    
    Again given the example of a `deployment` named `nginx` you would want to add properties of the HULL [**hull.PodTemplate.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_pod.md) to the instance. With them you set the `pod` property to define the pod template (initContainers, containers, volumes, ...) and can add `templateLabels` and `templateAnnotations` just to the pods created `metadata` and not the deployment objects `metadata` section:
    
    ```plaintext
    hull:
      objects:
        deployment:
          nginx: 
            staticName: true 
            enabled: true 
            labels: 
              demo_label: "demo" 
            annotations: 
              demo_annotation: "demo" 
            templateLabels: # property of hull.PodTemplate.v1 to define 
                            # labels only added to the pod
              demo_pod_label: "demo pod" 
            templateAnnotations: # property of hull.PodTemplate.v1 to define 
                            # annotations only added to the pod
              demo_pod_annotation: "demo pod"
            pod: # property of hull.PodTemplate.v1 to define the pod template
              containers:
                nginx: # all containers of a pod template are also referenced by a 
                      # unique key to make manipulating them easy.
                  image:
                    repository: nginx # specify repository and tag
                                      # separately with HULL for easier composability
                    tag: 1.14.2
                  ... # further properties (volumeMounts, affinities, ...)
    ```
    
3. Kubernetes object properties. For each object type it is basically possible to specify all existing Kubernetes properties. In case a HULL property overwrites a identically named Kubernetes property the HULL property has precedence. Even if a HULL property overrides a Kubernetes property it is intended to provide the same complete configuration options, even if sometimes handled differently by HULL.
    
    Some of the typical top-level Kubernetes object properties and fields don't require setting them with HULL based objects because they can be deducted automatically:
    
    * the `apiVersion` and `kind` are determined by the HULL object type and Kubernetes API version and don't require to be explicitly set (except for objects of type `customresource`).
        
    * the top-level `metadata` dictionary on objects is handled by HULL via the `annotations` and `labels` fields and the naming rules explained above. So the `metadata` field does not require configuration and is hence not configurable for any object.
        
    
    Some lower level structures are also converted from the Kubernetes API array form to a dictionary form or are modified to improve working with them. This also enables more sophisticated merging of layers since arrays don't merge well, they only can be overwritten completely. Overwriting arrays however can make it hard to forget about elements that are contained in the default form of the array (you would need to know that they existed in the first place). In short, for a layered configuration approach without an endless amount of elements the dictionary is preferable for representing data since it offers a much better merging support.
    
    So again using the example of a `deployment` named `nginx` you can add the remaining available Kubernetes properties to the object instance which are not handled by HULL as shown below. For a `deployment` specifically you can add all the remaining properties defined in the `deploymentspec` API schema from [**deploymentspec-v1-apps**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#deploymentspec-v1-apps) which are `minReadySeconds`, `paused`, `progressDeadlineSeconds`, `replicas`, `revisionHistoryLimit` and `strategy`. If properties are marked as mandatory in the Kubernetes JSON schema you must provide them otherwise the rendering process will fail:
    
    ```plaintext
    hull:
      objects:
        deployment:
          nginx: 
            staticName: true 
            enabled: true 
            labels: 
              demo_label: "demo" 
            annotations: 
              demo_annotation: "demo" 
            pod:
              ... # Here would come the hull.PodTemplate.v1 definition
                  # see above for details 
            replicas: 3 # property from the Kubernetes API deploymentspec
            strategy: # property from the Kubernetes API deploymentspec
              type: Recreate
            ... # further Kubernetes API deploymentspec options
    ```
    

#### *Composing objects with HULL*

Here is an overview of which top level properties are available for which object type in HULL. The HULL properties are grouped by the respective HULL JSON schema group they belong to. A detailed description of these groups and their properties is found in the documentation of this helm chart and the respective linked documents.

[**Workloads APIs**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#-strong-workloads-apis-strong-)

| HULL  
Object Type | HULL  
Properties | Kubernetes/External  
Properties |
| --- | --- | --- |
| `deployment` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.PodTemplate.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_pod.md)  
`templateAnnotations`  
`templateLabels`  
`pod` | [**deploymentspec-v1-apps**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#deploymentspec-v1-apps)  
`minReadySeconds`  
`paused`  
`progressDeadlineSeconds`  
`replicas`  
`revisionHistoryLimit`  
`strategy` |
| `job` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.PodTemplate.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_pod.md)  
`templateAnnotations`  
`templateLabels`  
`pod` | [**jobspec-v1-batch**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#jobspec-v1-batch)  
`activeDeadlineSeconds`  
`backoffLimit`  
`completionMode`  
`completions`  
`manualSelector`  
`parallelism`  
`selector`  
`suspend`  
`ttlSecondsAfterFinished` |
| `daemonset` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.PodTemplate.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_pod.md)  
`templateAnnotations`  
`templateLabels`  
`pod` | [**daemonsetspec-v1-apps**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#daemonsetspec-v1-apps)  
`minReadySeconds`  
`revisionHistoryLimit`  
`updateStrategy` |
| `statefulset` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.PodTemplate.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_pod.md)  
`templateAnnotations`  
`templateLabels`  
`pod` | [**statefulsetspec-v1-apps**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#statefulsetspec-v1-apps)  
`podManagementPolicy`  
`replicas`  
`revisionHistoryLimit`  
`serviceName`  
`updateStrategy`  
`serviceName`  
`volumeClaimTemplates` |
| `cronjob` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.Job.v1**](https://github.com/vidispine/hull/blob/main/hull/README.md)  
`job` | [**cronjobspec-v1-batch**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#cronjobspec-v1-batch)  
`concurrencyPolicy`  
`failedJobsHistoryLimit`  
`schedule`  
`startingDeadlineSeconds`  
`successfulJobsHistoryLimit`  
`suspend` |

[**Service APIs**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#-strong-service-apis-strong-)

| HULL  
Object Type | HULL  
Properties | Kubernetes/External  
Properties |
| --- | --- | --- |
| `endpoints` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**endpoints-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#endpoints-v1-core)  
`subsets` |
| `endpointslice` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**endpointslice-v1-discovery-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#endpointslice-v1-discovery-k8s-io)  
`addressType`  
`endpoints`  
`ports` |
| `service` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.Service.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_service.md)  
`ports` | [**servicespec-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#servicespec-v1-core)  
`allocateLoadBalancerNodePorts`  
`clusterIP`  
`clusterIPs`  
`externalIPs`  
`externalName`  
`externalTrafficPolicy`  
`healthCheckNodePort`  
`internalTrafficPolicy`  
`ipFamilies`  
`ipFamilyPolicy`  
`loadBalancerClass`  
`loadBalancerIP`  
`loadBalancerSourceRanges`  
`publishNotReadyAddresses`  
`selector`  
`sessionAffinity`  
`sessionAffinityConfig`  
`topologyKeys`  
`type` |
| `ingress` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.Ingress.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_ingress.md)  
`tls`  
`rules` | [**ingressspec-v1-networking-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#ingressspec-v1-networking-k8s-io)  
`defaultBackend`  
`ingressClassName` |
| `ingressclass` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**ingressclassspec-v1-networking-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#ingressclassspec-v1-networking-k8s-io)  
`controller`  
`parameters` |

[**Config and Storage APIs**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#-strong-config-and-storage-apis-strong-)

| HULL  
Object Type | HULL  
Properties | Kubernetes/External  
Properties |
| --- | --- | --- |
| `configmap` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.VirtualFolder.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_configmaps_secrets.md)  
`data` | [**configmap-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#configmap-v1-core)  
`binaryData`  
`immutable` |
| `secret` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.VirtualFolder.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_configmaps_secrets.md)  
`data` | [**secret-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#secret-v1-core)  
`immutable`  
`stringData`  
`type` |
| `registry` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.Registry.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_registry.md)  
`server`  
`username`  
`password` | [**secret-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#secret-v1-core) |
| `persistentvolumeclaim` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**persistentvolumeclaimspec-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#persistentvolumeclaimspec-v1-core)  
`accessModes`  
`dataSource`  
`resources`  
`selector`  
`storageClassName`  
`volumeMode`  
`volumeName` |
| `storageclass` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**storageclass-v1-storage-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#storageclass-v1-storage-k8s-io)  
`allowVolumeExpansion`  
`allowedTopologies`  
`mountOptions`  
`parameters`  
`provisioner`  
`reclaimPolicy`  
`volumeBindingMode` |

[**Metadata APIs**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#-strong-metadata-apis-strong-)

| HULL  
Object Type | HULL  
Properties | Kubernetes/External  
Properties |
| --- | --- | --- |
| `customresource` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.CustomResource.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_customresource.md)  
`apiVersion`  
`kind`  
`spec` |  |
| `limitrange` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**limitrange-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#limitrange-v1-core)  
`limits` |
| `horizontalpodautoscaler` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.HorizontalPodAutoscaler.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_horizontalpodautoscaler.md)  
`scaleTargetRef` | [**horizontalpodautoscalerspec-v2-autoscaling**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#horizontalpodautoscalerspec-v2-autoscaling)  
`behavior`  
`maxReplicas`  
`metrics`  
`minReplicas` |
| `mutatingwebhookconfiguration` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.MutatingWebhook.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base_webhook.md)  
`webhooks` |  |
| `poddisruptionbudget` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**poddisruptionbudgetspec-v1-policy**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#poddisruptionbudgetspec-v1-policy)  
`maxUnavailable`  
`minAvailable`  
`selector` |
| `validatingwebhookconfiguration` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.ValidatingWebhook.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base_webhook.md)  
`webhooks` |  |
| `priorityclass` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**priorityclass-v1-scheduling-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#priorityclass-v1-scheduling-k8s-io)  
`description`  
`globalDefault`  
`preemptionPolicy`  
`value` |

[**Cluster APIs**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#-strong-cluster-apis-strong-)

| HULL  
Object Type | HULL  
Properties | Kubernetes/External  
Properties |
| --- | --- | --- |
| `clusterrole` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.Rule.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_role.md)  
`rules` | [**clusterrole-v1-rbac-authorization-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#clusterrole-v1-rbac-authorization-k8s-io)  
`aggregationRule` |
| `clusterrolebinding` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**clusterrolebinding-v1-rbac-authorization-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#clusterrolebinding-v1-rbac-authorization-k8s-io)  
`roleRef`  
`subjects` |
| `namespace` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**namespace-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#namespace-v1-core)  
`spec`  
`status` |
| `persistentvolume` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**persistentvolumespec-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#persistentvolumespec-v1-core)  
`accessModes`  
`awsElasticBlockStore`  
`azureDisk`  
`azureFile`  
`capacity`  
`cephfs`  
`cinder`  
`claimRef`  
`csi`  
`fc`  
`flexVolume`  
`flocker`  
`gcePersistentDisk`  
`glusterfs`  
`hostPath`  
`iscsi`  
`local`  
`mountOptions`  
`nfs`  
`nodeAffinity`  
`persistentVolumeReclaimPolicy`  
`photonPersistentDisk`  
`portworxVolume`  
`quobyte`  
`rbd`  
`scaleIO`  
`storageClassName`  
`storageos`  
`volumeMode`  
`vsphereVolume` |
| `role` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName`  
  
[**hull.Rule.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_role.md)  
`rules` | [**role-v1-rbac-authorization-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#role-v1-rbac-authorization-k8s-io) |
| `rolebinding` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**rolebinding-v1-rbac-authorization-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#rolebinding-v1-rbac-authorization-k8s-io)  
`roleRef`  
`subjects` |
| `serviceaccount` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**serviceaccount-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#serviceaccount-v1-core)  
`automountServiceAccountToken`  
`imagePullSecrets`  
`secrets` |
| `resourcequota` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**resourcequotaspec-v1-core**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#resourcequotaspec-v1-core)  
`hard`  
`scopeSelector`  
`scopes` |
| `networkpolicy` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**networkpolicyspec-v1-networking-k8s-io**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#networkpolicyspec-v1-networking-k8s-io)  
`egress`  
`ingress`  
`podSelector`  
`policyTypes` |

**Other APIs**

| HULL  
Object Type | HULL  
Properties | Kubernetes/External  
Properties |
| --- | --- | --- |
| `servicemonitor` | [**hull.ObjectBase.v1**](https://github.com/vidispine/hull/blob/main/hull/doc/objects_base.md)  
`enabled`  
`annotations`  
`labels`  
`staticName` | [**ServiceMonitor CRD**](https://github.com/prometheus-community/helm-charts/blob/main/charts/kube-prometheus-stack/crds/crd-servicemonitors.yaml)  
`spec` |

## Testing and installing a HULL based chart

To test or install a chart based on HULL the standard Helm v3 tooling is usable. See also the Helm documentation at the [Helm website](https://helm.sh/).

### Testing a HULL based chart

To inspect the outcome of a specific `values.yaml` configuration you can simply render the templates which would be deployed to Kubernetes and inspect them with the below command adapted to your needs:

`<PATH_TO_HELM_V3_BINARY> template --debug --namespace <CHART_RELEASE_NAMESPACE> --kubeconfig <PATH_TO_K8S_CLUSTER_KUBECONFIG> -f <PATH_TO_SYSTEM_SPECIFIC_VALUES_YAML> --output-dir <PATH_TO_OUTPUT_DIRECTORY> <PATH_TO_CHART_DIRECTORY>`

### Install or upgrade a release:

Installing or upgrading a chart using HULL follows the standard procedures for every Helm chart:

`<PATH_TO_HELM_V3_BINARY> upgrade --install --debug --create-namespace --atomic --namespace <CHART_RELEASE_NAMESPACE> --kubeconfig <PATH_TO_K8S_CLUSTER_KUBECONFIG> -f <PATH_TO_SYSTEM_SPECIFIC_VALUES_YAML> <RELEASE_NAME> <PATH_TO_CHART_DIRECTORY>`

## First Examples

Using the nginx deployment example from the Kubernetes documentation [https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#creating-a-deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#creating-a-deployment) as something we want to create with our HULL based Helm chart:

```plaintext
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
```

To render this analogously using the HULL library your chart needs to be [setup for using HULL](https://github.com/vidispine/hull/blob/main/hull/doc/setup.md). In the following section we assume the parent Helm chart is named `hull-test` and we use the `helm template` command to test render the `values.yaml`'s.

### Minimal Example

A minimal example of creating the expected result from above would be to create a `values.yaml` like below in your parent chart (commented with some explanations). Note that some default features of HULL such as RBAC and dynamic naming are explicitly disabled here to obtain the output matching the above example closely:

```plaintext
hull:
  config:
    general:
      rbac: false # Don't render RBAC objects. By default HULL would provide 
                  # a 'default' Role and 'default' RoleBinding associated with 
                  # a 'default' ServiceAccount to use for all pods.
                  # You can modify this as needed. Here we turn it off to not 
                  # render the default RBAC objects.
  objects:
    serviceaccount:
      default:
        enabled: false # The release specific 'default' ServiceAccount created
                       # for a release by default is disabled here. In this case 
                       # it will not be rendered out and automatically used as 
                       # 'serviceAccountName' in the pod templates. 
    deployment:
      nginx: # all object instances have a key used for naming the objects and 
             # allowing to overwrite properties in multiple values.yaml layers
        staticName: true # forces the metadata.name to be just the <KEY> 'nginx' 
                         # and not a dynamic name '<CHART>-<RELEASE>-<KEY>' which 
                         # would be the better default behavior of creating 
                         # unique object names for all objects.
        replicas: 3
        pod:
          containers:
            nginx: # all containers of a pod template are also referenced by a 
                   # unique key to make manipulating them easy.
              image:
                repository: nginx
                tag: 1.14.2
              ports:
                http: # unique key per container here too. All key-value structures
                      # which are finally arrays in the K8S objects are converted to 
                      # arrays on rendering the chart.
                  containerPort: 80
```

This produces the following rendered deployment when running the `helm template` command (commented with some brief explanations):

```plaintext
apiVersion: apps/v1 # derived from object type 'deployment'
kind: Deployment # derived from object type 'deployment'
metadata: 
  annotations: {}
  labels: # standard Kubernetes metadata is created always automatically.
    app.kubernetes.io/component: nginx 
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: hull-test
    app.kubernetes.io/part-of: undefined
    app.kubernetes.io/version: 1.31.0
    helm.sh/chart: hull-test-1.31.0
  name: nginx # default name would be 'release-name-hull-test-nginx' 
              # but with staticName: true in the HULL spec it is just the key name
spec:
  replicas: 3
  selector: # selector is auto-created to match the unique metadata combination 
            # found also in the in the object's metadata labels.
    matchLabels:
      app.kubernetes.io/component: nginx
      app.kubernetes.io/instance: release-name
      app.kubernetes.io/name: hull-test
  template:
    metadata:
      annotations: {}
      labels: # auto-created metadata is added to pod template 
        app.kubernetes.io/component: nginx
        app.kubernetes.io/instance: release-name
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/name: hull-test
        app.kubernetes.io/part-of: undefined
        app.kubernetes.io/version: 1.31.0
        helm.sh/chart: hull-test-1.31.0
    spec:
      containers:
      - env: []
        envFrom: []
        image: nginx:1.14.2
        name: nginx
        ports:
        - containerPort: 80
          name: http # name 'http' derived from the key of the port 
                     # object defined in the values.yaml
        volumeMounts: []
      imagePullSecrets: {}
      initContainers: []
      volumes: []
```

### Advanced Example

Now to render the nginx deployment example showcasing extra features of the HULL library you can could create the below `values.yaml` file in your parent chart. Note that this is a very advanced example of what is possible using this library chart.

This example highlights:

* hierarchical metadata handling
    
* default RBAC setup of objects
    
* dynamic naming mechanism
    
* transformations
    
* easy inclusion of ConfigMaps and/or Secrets
    

```plaintext
hull:
  config:
    general:  # This time we are not setting rbac: false 
              # so RBAC default objects are created. 
              # If the default objects don't match the use-case
              # you can tweak all aspects individually if needed
      metadata:
        labels:         
          custom: # Additional labels added to all K8S objects
            general_custom_label_1: General Custom Label 1
            general_custom_label_2: General Custom Label 2
            general_custom_label_3: General Custom Label 3
        annotations: 
          custom: # Additional annotations added to all K8S objects
            general_custom_annotation_1: General Custom Annotation 1
            general_custom_annotation_2: General Custom Annotation 2
            general_custom_annotation_3: General Custom Annotation 3
    specific: # Put configuration options specific to this chart here
      nginx_tag: 1.14.2 # You can also use entries here to globally 
                        # define values that are referenced in multiple
                        # places in your chart. See how this field 
                        # is accessed below in the deployment.

  objects:
    deployment:
      _HULL_OBJECT_TYPE_DEFAULT_: # A special object key available for
                                  # all object types allowing defining 
                                  # defaults for properties of all object 
                                  # type instances created.
        annotations:  
          default_annotation_1: Default Annotation 1
          default_annotation_2: Default Annotation 2
          general_custom_annotation_2:  Default Annotation 2  # overwriting this 
                                                              # general annotation for
                                                              # all deployments
          
        labels:
          default_label_1: Default Label 1
          default_label_2: Default Label 2
          general_custom_label_2:  Default Label 2 # overwriting this 
                                                   # general label for
                                                   # all deployments
          
      nginx: # specify the nginx deployment under key 'nginx'
        # This time we're not setting the metadata.name to be static so 
        # name will be created dynamically and will be unique
        annotations:
          general_custom_annotation_3: Specific Object Annotation 3 # overwrite a
                                                                    # general annotation
          default_annotation_2: Specific Object Annotation 2 # overwrite a default annotation
          specific_annotation_1: Specific Object Annotation 1 # add a specific annotation 
                                                              # to the all this object's metadata
        labels: 
          general_custom_label_3: Specific Object Label 3 # overwrite a
                                                          # general label
          default_label_2: Specific Object Label 2 # overwrite a default label
          specific_label_1: Specific Object Label 1 # add a specific label 
                                                    # to the all this object's metadata
        templateAnnotations:
          specific_annotation_2: Specific Template Annotation 2 # this annotation will only appear 
                                                                # in the pod template metadata
        templateLabels:
          specific_label_2: Specific Template Label 2 # this label will only appear 
                                                      # in the pod template metadata
        replicas: 3
        pod:
          containers:
            nginx: # all containers of a pod template are also referenced by a 
                   # unique key to make manipulating them easy.
              image:
                repository: nginx
                tag: _HT!{{ (index . "$").Values.hull.config.specific.nginx_tag }}
                  # Applies a tpl transformation allowing to inject dynamic data based
                  # on values in this values.yaml into the resulting field (here the tag
                  # field of this container).
                  # _HT! is the short form of the transformation that applies tpl to
                  # a given value. This example just references the value of the field 
                  # which is specified further above in the values.yaml and will 
                  # produce 'image: nginx:1.14.2' when rendered in the resulting 
                  # deployment YAML but complex conditional Go templating logic is 
                  # applicable too. 
                  # There are some limitations to using this approach which are 
                  # detailed in the transformation.md in the doc section.
              ports:
                http: # unique key per container here too. All key-value structures
                      # which are array in the K8S objects are converted to arrays
                      # on rendering the chart.
                  containerPort: 80
    configmap: # this is to highlight the secret/configmap inclusion feature
      nginx_configmap: # configmap objects have keys too
        data: # specify for which contents a data entry shall be created
              # within only a few lines of configuration. Contents can come from ...
          an_inline_configmap.txt: # ... an inline specified content or ...
            inline: |- 
              Top secret contents
              spread over 
              multiple lines...
          contents_from_an_external_file.txt: # ... something from an external file.
            path: files/my_secret.txt 
```

This produces the following rendered objects when running the `helm template` command (commented with some brief explanations):

```plaintext
---
# Source: hull-test/templates/hull.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    general_custom_annotation_1: General Custom Annotation 1 # All objects share the general_custom_annotations
    general_custom_annotation_2: General Custom Annotation 2 # if they are not overwritten for the object type's
    general_custom_annotation_3: General Custom Annotation 3 # default or specific instance
  labels:
    app.kubernetes.io/component: default
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: hull-test
    app.kubernetes.io/part-of: undefined
    app.kubernetes.io/version: 1.31.0
    general_custom_label_1: General Custom Label 1 # All objects share the general_custom_labels
    general_custom_label_2: General Custom Label 2 # if they are not overwritten for the object type's
    general_custom_label_3: General Custom Label 3 # default or specific instance
    helm.sh/chart: hull-test-1.31.0
  name: release-name-hull-test-default # This is the default ServiceAccount created for this chart.
                                       # As all object instances by default it will be assigned a 
                                       # dynamically created unique name in context of this object type.
                                       # In the simple example we disabled this rendering by 
                                       # setting enabled: false for this object's key.
---
# Source: hull-test/templates/hull.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  annotations:
    general_custom_annotation_1: General Custom Annotation 1
    general_custom_annotation_2: General Custom Annotation 2
    general_custom_annotation_3: General Custom Annotation 3
  labels:
    app.kubernetes.io/component: default
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: hull-test
    app.kubernetes.io/part-of: undefined
    app.kubernetes.io/version: 1.31.0
    general_custom_label_1: General Custom Label 1
    general_custom_label_2: General Custom Label 2
    general_custom_label_3: General Custom Label 3
    helm.sh/chart: hull-test-1.31.0
  name: release-name-hull-test-default # A default Role for RBAC. 
rules: []
---
# Source: hull-test/templates/hull.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  annotations:
    general_custom_annotation_1: General Custom Annotation 1
    general_custom_annotation_2: General Custom Annotation 2
    general_custom_annotation_3: General Custom Annotation 3
  labels:
    app.kubernetes.io/component: default
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: hull-test
    app.kubernetes.io/part-of: undefined
    app.kubernetes.io/version: 1.31.0
    general_custom_label_1: General Custom Label 1
    general_custom_label_2: General Custom Label 2
    general_custom_label_3: General Custom Label 3
    helm.sh/chart: hull-test-1.31.0
  name: release-name-hull-test-default
roleRef:
  apiGroup: rbac.authorization.k8s.io/v1
  kind: Role
  name: release-name-hull-test-default
subjects:
- apiGroup: rbac.authorization.k8s.io/v1
  kind: ServiceAccount
  name: release-name-hull-test-default # A default RoleBinding for RBAC. It connects the 
                                       # default ServiceAccount with the default Role.
                                       # By default RBAC is enabled in charts.
---
# Source: hull-test/templates/hull.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    default_annotation_1: Default Annotation 1 # non-overwritten default_annotation
    default_annotation_2: Specific Object Annotation 2 # overwritten default_annotation by instance
    general_custom_annotation_1: General Custom Annotation 1 # non-overwritten general_custom_annotation
    general_custom_annotation_2: Default Annotation 2 # overwritten general_custom_annotation 
                                                      # by default_annotation
    general_custom_annotation_3: Specific Object Annotation 3 # overwritten general_custom_annotation 
                                                              # by specific_annotation
    specific_annotation_1: Specific Object Annotation 1 # added annotation for instance metadata only
  labels:
    app.kubernetes.io/component: nginx
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: hull-test
    app.kubernetes.io/part-of: undefined
    app.kubernetes.io/version: 1.31.0
    default_label_1: Default Label 1 # non-overwritten default_label
    default_label_2: Specific Object Label 2 # overwritten default_label by instance
    general_custom_label_1: General Custom Label 1 # non-overwritten general_custom_label
    general_custom_label_2: Default Label 2 # overwritten general_custom_label by default_label
    general_custom_label_3: Specific Object Label 3 # overwritten general_custom_label 
                                                    # by specific_label
    helm.sh/chart: hull-test-1.31.0
    specific_label_1: Specific Object Label 1 # added label for instance metadata only
  name: release-name-hull-test-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/component: nginx
      app.kubernetes.io/instance: release-name
      app.kubernetes.io/name: hull-test
  template:
    metadata:
      annotations:
        default_annotation_1: Default Annotation 1
        default_annotation_2: Specific Object Annotation 2
        general_custom_annotation_1: General Custom Annotation 1
        general_custom_annotation_2: Default Annotation 2
        general_custom_annotation_3: Specific Object Annotation 3
        specific_annotation_1: Specific Object Annotation 1
        specific_annotation_2: Specific Template Annotation 2 # this annotation was added only 
                                                              # for the pod template's metadata
      labels:
        app.kubernetes.io/component: nginx
        app.kubernetes.io/instance: release-name
        app.kubernetes.io/managed-by: Helm
        app.kubernetes.io/name: hull-test
        app.kubernetes.io/part-of: undefined
        app.kubernetes.io/version: 1.31.0
        default_label_1: Default Label 1
        default_label_2: Specific Object Label 2
        general_custom_label_1: General Custom Label 1
        general_custom_label_2: Default Label 2
        general_custom_label_3: Specific Object Label 3
        helm.sh/chart: hull-test-1.31.0
        specific_label_1: Specific Object Label 1
        specific_label_2: Specific Template Label 2 # this label was added only 
                                                    # for the pod template's metadata
    spec:
      containers:
      - env: []
        envFrom: []
        image: nginx:1.14.2
        name: nginx
        ports:
        - containerPort: 80
          name: http
        volumeMounts: []
      imagePullSecrets: {}
      initContainers: []
      serviceAccountName: release-name-hull-test-default # the dynamically created name
      volumes: []
---
# Source: hull-test/templates/hull.yaml
apiVersion: v1
data:
  an_inline_configmap.txt: "Top secret contents\nspread over \nmultiple lines..."
  contents_from_an_external_file.txt: "Whatever was in this file ..."  
kind: ConfigMap
metadata:
  annotations:
    general_custom_annotation_1: General Custom Annotation 1 # All objects share the general_custom_annotations
    general_custom_annotation_2: General Custom Annotation 2 # if they are not overwritten for the object type's
    general_custom_annotation_3: General Custom Annotation 3 # default or specific instance
  labels:
    app.kubernetes.io/component: nginx_configmap
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: hull-test
    app.kubernetes.io/part-of: undefined
    app.kubernetes.io/version: 1.31.0
    general_custom_label_1: General Custom Label 1 # All objects share the general_custom_labels
    general_custom_label_2: General Custom Label 2 # if they are not overwritten for the object type's
    general_custom_label_3: General Custom Label 3 # default or specific instance
    helm.sh/chart: hull-test-1.31.0
  name: release-name-hull-test-nginx_configmap
```

Read the additional documentation in the [documentation folder](https://github.com/vidispine/hull/blob/main/hull/doc) on how to utilize the features of the HULL library to the full effect.
