Resource Definitions

Overview

A Resource Definition defines how and when a Resource should be provisioned. It is made up of the Resource Type, Resource Drivers and its inputs, along with a set of Matching Criteria to define when to select the Resource Definition. Resource Definitions are generally created and managed by the Platform Team.

Several pre-defined default Resource Definitions are available by default in all Organizations. These definitions are managed by Humanitec but can be enabled or disabled as needed.

A Resource Definition holds the following:

Parameter Description
Resource Type The type of resource that the Resource Definition provisions
Resource Driver The ID of the Resource Driver to use when provisioning the resource
Driver Inputs Driver specific inputs that are taken by the Driver alongside the Resource inputs
Matching Criteria The criteria used to select which Resource Definition of a specific Resource Type

Example

This example shows the core elements of a Resource Definition:

apiVersion: core.api.humanitec.io/v1
kind: Definition
metadata:
  id: stg-redis

entity:
  name: stg-redis
  type: redis
  driver_type: humanitec/terraform
  driver_account: aws-stg-infra
  driver_inputs:
    values:
      source:
        url: https://my-git-host.com/my-project/redis-elasticache-example.git
        rev: refs/tags/v.1.3.5
      variables:
        # Redacted for brevity

criteria:
  - env_type: staging
  • The type specifies the Resource Type as redis
  • The driver_type specifies the Driver to use. Here, it is the Terraform Driver
  • The driver_account references a Cloud Account for providing access to the target infrastructure for the Driver
  • The driver_inputs specify the inputs the specific Driver needs to operate. In this case, the Resource Definition provides data on finding the Terraform code to be executed, and potentially more inputs which are redacted for brevity
  • The criteria specify the matching criteria , i.e. in which context to use this exact Resource Definition. Here, it is for Deployments into Environments of type staging

Working with inputs and outputs

A Resource Definition may receive inputs to parameterize the Resource to be provisioned. It configures and calls a Driver internally to perform the actual provisioning, and may produce outputs when done. More technical details follow in the sections below. These general principles apply:

  1. Within Score, only inputs and outputs defined by the schema of the respective Resource Type can be used.

  2. All params passed in to a resource in Score become available as Resource inputs in the Resource Definition used to provision that Resource. The restriction for the Resource Type input schema applies, see 1.

Score file snippet using resource inputs and outputs
...
containers:
  demo:
    variables:
      # Use the output "host" which is part of the "dns" Resource Type's output schema
      MY_HOSTNAME: ${resources.my-dns.host}
...
resources:
  my-dns:
    type: dns
  users-route:
    type: route
      # Provide resource inputs via "params" according to the "route" Resource Type's input schema
      params:
        # Use the output "host" which is part of the "dns" Resource Type's output schema
        host: ${resources.api-dns.host}
        path: /users
        port: 80
  1. A Resource Definition passes Driver inputs to the Driver it uses, following that Driver’s input schema.

  2. A Resource Definition may produce any output regardless of its Resource Type. In the Resource Graph , i.e. between Resources, a Resource Definition has access to any output another referenced Resource may produce.

Resource Definition snippet showing the use of a custom output from a referenced Resource
...
  driver_type: template
  driver_inputs:
    values:
      templates:
        init: |
          # Reading the output "region" from a Resource of type "config"
          region: ${resources.config.outputs.region}
...

Note: the config Resource Type does not need to have region in its output schema, and it does not.

  1. However, only the outputs defined by the Resource Type’s output schema will be available in Score (see 1.).

  2. A Resource Definition cannot push inputs to another referenced Resource in the Resource Graph. It can only pull data from other Resources through the Graph by querying their outputs.

Schematically, inputs and outputs can flow like this:

%%{ init: { 'flowchart': { 'curve': 'linear' } } }%%
flowchart LR
  subgraph resourceGraph[Resource Graph]
    direction LR
    workload(Workload) --> rgResource1(Resource 1)
    workload --> rgResource2(Resource 2)  -->|references| rgResource3(Resource 3)
    rgResource3 -.->|May use any output<br/>regardless of output schema| rgResource2
  end
  score -.->|Provide Resource inputs<br/>via Score &quot;params:&quot; following<br/>Resource Type input schemas<br/>of Resource 1 and 2| resourceGraph
  subgraph score[Score file]
    direction TB
    subgraph scoreContainers[&quot;containers:&quot; section]
      scoreContainer1(container)
    end
    scoreContainers ~~~ scoreResources
    subgraph scoreResources[&quot;resources:&quot; section]
      direction TB
      scoreResource2 -.->|"May use outputs from<br/>Resource Type output schema<br/>of Resource 2<br/>(and vice versa)"| scoreResource1
      scoreResource1(Resource 1) ~~~ scoreResource2(Resource 2)
    end
  end
  scoreResources -.->|May use outputs from<br/>Resource Type output schemas<br/>of Resource 1 and 2<br/>as container variables| scoreContainer1
  resourceGraph -.->|Provide outputs following<br/>Resource Type output schemas<br/>of Resource 1 and 2| score

  classDef invisible fill:#00000000,stroke:#00000000
  class scoreContainers,scoreResources nested

Resource inputs

Resource inputs are provided via Score file through the params property of a Score resource. Allowable resource inputs are defined through the input schema of the Resource Type, e.g. for the route Resource Type :

...
resources:
  users-route:
    type: route
      params:
        host: ${resources.api-dns.host}
        path: /users
        port: 80

The mechanism for referencing Resource inputs within the Resource Definition depends on the Driver:

In the Template Driver , use the format .resource.<input>:

driver_type: humanitec/template
driver_inputs:
  values:
    templates:
      init: |
        host: {{ .resource.host | quote }}

In the Terraform Driver , declare a Terraform variable named like the Resource input and use in your code as required:

  driver_type: humanitec/terraform
  driver_inputs:
    values:
      # Using an inline Terraform script for simplicity, same approach for external code in Git repo
      script: |
        variable "host" {}

        # use as var.host in your code

In the Container Driver , the container receives the Resource Inputs as a JSON object stored in the file at the path specified by the Driver in the RESOURCE_INPUTS_FILE environment variable:

{
  "host": "my-host.my-domain.com"
}

Driver inputs

Driver inputs are the inputs a Resource Definition provides to the Driver that performs the provisioning of a Resource. Allowable Driver inputs are defined through the input schema of the respective Driver. Use the driver_inputs property of the Resource Definition to set their values.

You can find the input schema on the Driver’s page within the Drivers section or, if no dedicated page is available, by querying the API and inspecting the inputs_schema property for the Driver of choice:

# Obtain the input schema for the "dns-cloudflare" Driver
humctl get driver -o yaml \
  | yq '.[] | select(.metadata.id == "dns-cloudflare") | .entity.inputs_schema'

Different Drivers used for the same Resource Type may require different inputs based on how they work, i.e. they may have different input schemas.

For example, provisioning a dns resource with the humanitec/dns-cloudflare Driver requires the Cloudflare Zone ID and the parent domain to be specified as Driver inputs:

...
entity:
  type: dns
  driver_type: humanitec/dns-cloudflare
  driver_inputs:
    values:
      zone_id: some-zone-id
      domain: my-domain.com
...

Whereas the humanitec/dns-wildcard Driver only requires the parent domain as a Driver input:

...
entity:
  type: dns
  driver_type: humanitec/dns-wildcard
  driver_inputs:
    values:
      domain: staging.example.com
...

The Driver inputs can make use of any Placeholder marked with driver_inputs to insert parts of the deployment context at deploy time:

...
  driver_inputs:
    values:
      domain: ${context.app.id}.example.com
...

Some Drivers have flexible capabilities for working with Driver inputs.

The Echo Driver will accept any top level Driver input and return is as an output.

Example

redis-secret-refs.yaml ( view on GitHub ) :

apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: redis-echo
entity:
  name: redis-echo
  type: redis
  driver_type: humanitec/echo
  driver_inputs:
    values:
      host: 0.0.0.0
      port: 6379
    secret_refs:
      password:
        store: my-gsm
        ref: redis-password
      username:
        store: my-gsm
        ref: redis-user
  criteria:
    - {}

The Template Driver lets you set any additional top level Driver input and then reference it in the templates as .driver.values.<input> or .driver.secrets.<input>.

Example

config.yaml ( view on GitHub ) :

# This Resource Definition pulls credentials for a container image registry from a secret store
# and creates a Kubernetes Secret of kubernetes.io/dockerconfigjson type
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: regcred-config
entity:
  driver_type: humanitec/template
  name: regcred-config
  type: config
  criteria:
  - class: default
    # This res_id must be used from a referencing Resource Definition to request this config Resource
    res_id: regcred
  driver_inputs:
    # These secret references read the credentials from a secret store
    secret_refs:
      password:
        ref: regcred-password
        # Replace this value with the secret store id that's supplying the password
        store: FIXME
      username:
        ref: regcred-username
        # Replace this value with the secret store id that's supplying the username
        store: FIXME
    values:
      secret_name: regcred
      # Replace this value with the servername of your registry
      server: FIXME
      templates:
        # The init template is used to prepare the "dockerConfigJson" content
        init: |
          dockerConfigJson:
            auths:
              {{ .driver.values.server | quote }}:
                username: {{ .driver.secrets.username | toRawJson }}
                password: {{ .driver.secrets.password | toRawJson }}
        manifests:
          # The manifests template creates the Kubernetes Secret
          # which can then be used in the workload "imagePullSecrets"
          regcred-secret.yaml:
            data: |
              apiVersion: v1
              kind: Secret
              metadata:
                name: {{ .driver.values.secret_name }}
              data:
                .dockerconfigjson: {{ .init.dockerConfigJson | toRawJson | b64enc }}
              type: kubernetes.io/dockerconfigjson
            location: namespace
        outputs: |
          secret_name: {{ .driver.values.secret_name }}

Outputs

Every Resource Type has an output schema. A Resource Definition for that type should at least produce the outputs defined by that schema. It may produce any additional outputs.

All outputs may be used within the Resource Graph, i.e. between one Resource and another. However, only the outputs defined by the Resource Type output schema can be used in Score.

Drivers do not have an output schema.

Resource output used in Score

score.yaml ( view on GitHub ) :

# Example Score file for a Workload that has three routes from one DNS name
apiVersion: score.dev/v1b1
metadata:
  name: routes-example
service:
  ports:
    www:
      port: 80
      targetPort: 8080
containers:
  webserver:
    image: my-webservice:latest
resources:
  api-dns:
    type: dns
  users-route:
    type: route
    params:
      host: ${resources.api-dns.host}
      path: /users
      port: 80
  products-route:
    type: route
    params:
      host: ${resources.api-dns.host}
      path: /products
      port: 80
  checkout-route:
    type: route
    params:
      host: ${resources.api-dns.host}
      path: /checkout
      port: 80

Within the Resource Graph, any outputs can be used. For example, this Resource Definition of type config produces an output that is being used in another Resource Definition via a Resource Reference .

Resource Definitions producing and consuming an output

Resource Definition producing an output cost_center_id:


config-labels.yaml ( view on GitHub ) :

# This "config" type Resource Definition provides the value for the sample label
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: app-config
entity:
  name: app-config
  type: config
  driver_type: humanitec/template
  driver_inputs:
    values:
      templates:
        # Returns a sample output named "cost_center_id" to be used as a label
        outputs: |
          cost_center_id: my-example-id
  # Match the resource ID "app-config" so that it can be requested via that ID
  criteria:
    - res_id: app-config

Resource Definition consuming the output:


custom-namespace-with-dynamic-labels.yaml ( view on GitHub ) :

# This Resource Definition references the "config" resource to use its output as a label
# and adds another label taken from the Deployment context
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: custom-namespace-with-label
entity:
  name: custom-namespace-with-label
  type: k8s-namespace
  driver_type: humanitec/template
  driver_inputs:
    values:
      templates:
        init: |
          name: ${context.app.id}-${context.env.id}
        manifests: |
          namespace.yaml:
            location: cluster
            data:
              apiVersion: v1
              kind: Namespace
              metadata:
                labels:
                  env_id: ${context.env.id}
                  cost_center_id: ${resources['config.default#app-config'].outputs.cost_center_id}
                name: {{ .init.name }}
        outputs: |
          namespace: {{ .init.name }}
  # Set matching criteria as required
  criteria:
    - {}

A Resource output is either secret or non-secret. A secret output of a Resource can only be used as a secret input to another Resource.

All non-secret outputs of an Active Resource can be observed in the Humanitec Portal as the Resource “Values”, or queried programmatically. Visit Active Resources for details. For the shell commands shown on that page, check the resource property in the generated response to see the outputs.

The mechanism for producing outputs within the Resource Definition depends on the Driver.

For the Echo Driver , all Driver inputs automatically become outputs.

This Resource Definition produces the non-secret outputs host and port, and the secret output password.

...
entity:
  driver_type: humanitec/echo
  driver_inputs:
    # Non-secret values
    values:
      host: my-hostname.example.com
      port: 6379
    # Secret value using a secret reference
    secret_refs:
      password:
        store: my-store
        ref: my-password
...

For the Template Driver , use the values.templates.outputs and values.templates.secrets properties to generate outputs.

This Resource Definition produces the non-secret output referencedVal and the secret output password.

...
entity:
  driver_type: humanitec/template
  driver_inputs:
    # Using a secret reference to read from a secret store
    secret_refs:
      password:
        ref: my-password
        store: my-store
    values:
      referencedVal: ${resources.config.outputs.someVal}
      templates:
        # Non-secret outputs
        outputs: |
          referencedVal: {{ .driver.values.referencedVal }}
        # Secret outputs
        secrets: |
          password: {{ .driver.secrets.password }}
...

For the Terraform Driver , declare output values in your Terraform code.

This Resource Definition produces the non-secret output some-output and the secret output some-secret-output.

...
entity:
  driver_type: humanitec/terraform
  driver_inputs:
    values:
      # Using an inline Terraform script for simplicity
      # Showing a non-secret and a secret output
      script: |
        output "some-output" {
          value = some_tf_resource.example.id
        }
        output "some-secret-output" {
          value     = some_tf_resource.example.password
          sensitive = true
        }
...

In the Container Driver , the container should store non-sensitive and sensitive outputs as a JSON object in two files whose path is specified by the Driver in the environment variable OUTPUTS_FILE and SECRET_OUTPUTS_FILE environment variables, respectively:

{
  "host": "my-hostname.example.com",
  "port": 6379
}

A Custom Driver may add any output to a Resource using whichever technology is used to implement the Driver, as long as the output data is compliant with the Driver API Specification .

Matching criteria

Matching Criteria provide a way for Humanitec to select a Resource Definition for provisioning a resource in a given Resource Context. Each Resource Definition can have 0 or more Matching Criteria associated with it. Each of the Matching Criteria defines 0, 1 or more parts of the context that must match in order for a Resource Definition to be selected. When selecting with Matching Criteria, the most specific one is selected. In general, this means if all the Matching Criteria fully matching the context, the Matching Criteria Rule with the most specific element filled is chosen. If there is a tie, the next most specific elements are compared and so on until one is chosen.

A Resource Definition with no Matching Criteria will not be considered.

Criteria specificity

In general, the parts of the matching criteria are in order from least specific to most specific as follows:

Environment Type < Application ID < Environment ID < Resource ID < Resource Class

This means that Matching Criteria containing a Resource ID will always be more specific than any Matching Criteria that does not contain a Resource ID.

Mathematically, the specificity of Matching Criteria can be calculated by summing together weights for each part of the Matching Criteria:

Part Weight
Environment Type 1
Application ID 2
Environment ID 4
Resource ID 8
Resource Class 16

An empty Matching Criteria would score 0, one containing just an Environment ID would score 4 and one containing an Application ID and Environment Type would score 3.

The Matching Criteria with the highest total weight are the most specific.

Example

Consider an Application called awesome-app with multiple Environments of type development. There is a shared environment called dev used by all developers and multiple “PR” environments which are created automatically when a PR is created in GitHub. The dev environment should be accessible via a stable DNS name of dev-api.awesome.app while the preview environments should be available on randomly generated subdomains such as fjs92.awesome.app or 4ij76s.awesome.app.

Two Resource Definitions of type dns are created:

  • static-dev-dns - always returns the same DNS name of dev-api.awesome.app
  • random-dev-dns - generates a random subdomain

They have the following Matching Criteria:

Resource Definition Environment Type Application ID Environment ID Resource ID Resource Class
static-dev-dns awesome-app dev shared.api-dns default
random-dev-dns development awesome-app shared.api-dns default

When a deployment occurs in the dev environment, Humanitec will attempt to provision a dns resource with ID shared-api-dns. Looking at the Matching Criteria for Resource Definitions of type dns it will return static-dev-dns and random-dev-dns. In this case, the static-dev-dns would be chosen because its Matching Criteria are more specific.

When a deployment occurs in a PR environment, only the random-dev-dns Resource Definition would be returned.

Manage Resource Definitions

Resource Definitions can be managed via the UI, CLI, API and using the Humanitec Terraform provider.

All actions involving Resource Definitions require the user to have the Administrator role in the Organization.

  1. Click the Resource Management menu item from the left-hand navigation to open the Resource Management screen.

  2. Select + Add resource definition, on the top left of the screen.

  3. Select the Resource Type for the Resource Definition you want to create in the dialog.

  4. Select the appropriate Driver from the list.

  5. Supply the Driver ID any additional inputs that are required by the Driver.

  6. Select Add, in the bottom right of the dialog.

  1. Create a file defining the Resource definition you want to create:
cat << EOF > my-def.yaml
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: example-ns-def
entity:
  type: k8s-namespace
  name: k8s-namespace
  driver_type: humanitec/echo
  driver_inputs:
    values:
      namespace: \${context.app.id}-\${context.env.id}
EOF
  1. Use the humctl create command to create the resource definition in the organization defined by your configured context:
humctl create -f my-def.yaml

curl "https://api.humanitec.io/orgs/${HUMANITEC_ORG}/resources/defs" \
  -X POST \
  -H "Authorization: Bearer ${HUMANITEC_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
  "id": "example-ns-def",
  "type": "k8s-namespace",
  "driver_type": "humanitec/echo",
  "driver_inputs": {
    "values": {
      "namespace": "${context.app.id}-${context.env.id}"
    }
  }
}'

Where the following environment variables are set:

Variable Example Description
HUMANITEC_TOKEN lsakdjhcals A Humanitec token of a user with at least Developer permission on the Application.
HUMANITEC_ORG my-org The Humanitec organization the Application is in.

resource "humanitec_resource_definition" "example-ns-def" {
  driver_type = "humanitec/echo"
  id          = "example-ns-def"
  name        = "example-ns-def"
  type        = "k8s-namespace"

  driver_inputs = {
    values = {
      "namespace" = "$${context.app.id}-$${context.env.id}"
    }
  }
}

Manage Resource Definitions by selecting Resources from the main navigation menu.

Create definitions for Resources

  1. On the Resources Management screen, assuming the Resource Type you’re looking for is not already in the hot-list, click Show all resources.
  2. From the grid, select the Resource Type that you need to create a Definition for.
  3. In the modal dialog, fill out the id field and select the appropriate Driver from the list.
  4. Supply any additional inputs that are required by the Driver.
  5. Select Create.

Delete definitions for Resources

  1. On the Resources Management screen, select the .. at the end of the row of the Resource Definition that you wish to delete.
  2. Select Delete.
  3. In the modal dialog, choose Delete to confirm.

Using secret references

You can use secret references to read secrets from secret stores, and pass them to Drivers as driver_inputs. Go here to see examples.

Default Resource Definitions

The Humanitec Platform Orchestrator provides a collection of default Resource Definitions for commonly provisioned resources. These are available by default in all organizations and do not require explicit matching criteria as they will match any supported resources that don’t have a custom Resource Definition and matching criteria.

Default Resource Definitions are available for:

  • base-env - No special behavior.
  • workload - No special behavior.
  • k8s-namespace - Generates a random namespace name.
  • ingress - Provisions a Kubernetes networking.k8s.io/v1 Ingress using the Ingress driver.
  • logging - Defaults to Kubernetes logging.
  • route - See Routes for more information on how Route resources are handled.
  • dns - Provisions a new random DNS subdomain of the newapp.io domain using the newapp-io-dns Driver .
  • tls-cert - Provides a default TLS certificate secret for the newapp.io domain using the newapp-io-tls Driver .
  • (Deprecated) k8s-cluster - Provisions an ephemeral and temporary Kubernetes cluster credential.
  • (Deprecated) postgres - Provisions an ephemeral and temporary Postgres database.

Default Resource Definitions can be disabled through either the Web UI or CLI. Note that this will cause any existing active resources that use this definition to be re-provisioned on the next deployment.

  1. Click the Resource Management menu item from the left-hand navigation to open the Resource Management screen.

  2. Select the “Default Humanitec” resource definition that you wish to disable.

  3. Select the Matching Criteria tab.

  4. Toggle the Active toggle to Inactive. Or the opposite to enable the definition again.

  1. Use the humctl api command to disable the default definition by clearing its matching criteria:
humctl api PUT /orgs/${HUMANITEC_ORG}/resources/defs/default-humanitec-dns/criteria --data '[]'
  1. Or re-enable the default definition by adding a catch-all matching criteria.
humctl api PUT /orgs/${HUMANITEC_ORG}/resources/defs/default-humanitec-dns/criteria --data '[{"class": "default"}]'

Where the following environment variables are set:

Variable Example Description
HUMANITEC_ORG my-org The Humanitec organization to act on.

Examples

Top