Resource Classes

Overview

Resource Classes provide a way of specializing Resource Types. Developers can set the class of a Resource alongside the type in their Score File. Platform teams can create and describe the class of a Resource and match it via Matching Criteria.

An Internal Developer Platform (IDP) often offers many “flavors” of a particular Resource Type. For example, there might be multiple flavors of S3 bucket available:

  • An external S3 bucket might be open to the public internet, e.g. to allow for downloading of assets.
  • A sensitive S3 bucket might be configured to be encrypted and only accessible via a limited set of roles, e.g. for storing confidential data.
  • A volatile S3 bucket where retention time is less than 24 hours, e.g. for holding intermediate data from other processes.

In all cases, an S3 bucket will be provisioned but they’ll be configured differently. Each of these flavors is represented by a “class”.

Every Resource always has a class. If the developer does not choose a class, the class default is used. Classes can also be set explicitly within the Platform Orchestrator, providing developers with more context about the available resource types.

Using classes in a Score file

A developer can select the class of a Resource by adding a class property to the Resource dependency in their Score file.

...
resources:
  confidential-bucket:
    type: s3
    class: sensitive

Matching a Resource Definition to classes

Resource Definitions can be matched to classes using Matching Criteria. See the Matching Criteria documentation for more details on how this works.

Matching Criteria allow for a single Resource Definition to be matched to multiple classes. This can be very useful to model some scenarios.

  • Use a single Resource Definition for all classes in development environments, and only specialize in higher environments.

    For example, it might not make sense to actually make an external S3 bucket externally accessible in development. Instead the same S3 bucket Resource Definition could cover both the internal and external classes.

    This could be modeled with three Resource Definitions:

# Dev version that matches both external and internal
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: dev-s3-bucket
entity:
  ...
  criteria:
    - env_type: development
      class: internal
    - env_type: development
      class: external
# Production version that matches just internal
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: prod-internal-s3-bucket
entity:
  ...
  criteria:
    - env_type: production
      class: internal
# Production version that matches just external
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: prod-external-s3-bucket
entity:
  ...
  criteria:
    - env_type: production
      class: external
  • Using the class placeholder to programmatically handle the class in a single Resource Definition.

    For example, one of the inputs to a Resource Definition using the Terraform Driver could be ${context.res.class}. This could then be used to specialize the Resource within the Terraform code.

Choosing Resource classes

Classes are a powerful construct. Care should be taken to carefully choose classes that allow for maximum flexibility as the platform and use cases develop.

We recommend following these best practice guidelines:

  • Classes should be descriptive, not prescriptive

    A class should describe the purpose of the specialization rather than how the specialization is implemented.

    For example, rather than defining the machine requirements for a postgres database with a class such as 4cpu-32Gb, describe the usage such as high-volume.

    This means the class can be satisfied differently in different contexts. high-volume in a development environment is much lower volume than high-volume in production. Whereas being prescriptive about the machine cannot be varied across environments.

  • Classes should be general rather than specific

    It might be tempting to choose classes for all possible combinations of configurations of a Resource type. However, remember that every class is an additional flavor of a Resource that needs to be maintained.

    It’s always possible to further specialize a Resource Definition for a particular app later. This means that a small number of more general classes can often suffice for developers needing to get started. By the time the workload makes it to production, there may be deviations from the standard Resource Definitions.

  • Class names should follow a consistent format

    Class names often have to perform double duty. Most commonly, this means covering both the intention of the Resource and access requirements. Examples for an S3 Resource Type could include ro-sensitive and rw-sensitive.

    It’s important to be consistent across your organization so that class names are predictable and self-descriptive.

Class notation in Resource Descriptors

Resource Descriptors are used when working with the Resource Graph. They are used in Resource References, Co-provisioning, Resource Selectors, and in error messages.

When using Resource References, co-provisioning, or selectors, you can optionally specify the class alongside the type and id. This involves adding a . after the type, followed by the class name.

As with the ID in Resource Descriptors, if the class is not specified, the class of the Resource that includes the descriptor is used. This allows for classes to be passed through the Resource Graph.

For example:

  • s3.sensitive
  • postgres.high-volume
  • dns.default

Creating a Resource Class

Resource classes don’t have to be created explicitly; you can start using them in Matching Criteria and it will work as well. However, to harness the full potential of the orchestrator, it is beneficial to create them. As the orchestrator resolves all classes during deployment, it’s impossible to calculate them earlier. Therefore, setting these explicitly allows for better validation and provides more context about the available resources for developers.

To ensure backwards compatibility, the connection between resource classes and matching criteria is kept minimal, allowing the use of classes in matching criteria without explicitly creating them. However, if you choose to create resource classes, tools like humctl can validate their presence. It is essential to create all classes, excluding default ones, to successfully validate the Score file.

humctl api post "/orgs/${HUMANITEC_ORG}/resources/types/${RESOURCE_TYPE}/classes" \
  -d '{
  "id": "sensitive",
  "description": "Encrypted bucket for storing confidential data"
}'

Where the following environment variables are set:

Variable Example Description
HUMANITEC_ORG my-org The Humanitec organization the Application is in.
RESOURCE_TYPE s3 The Humanitec resource type.

curl "https://api.humanitec.io/orgs/${HUMANITEC_ORG}/resources/types/${RESOURCE_TYPE}/classes" \
  -X POST \
  -H "Authorization: Bearer ${HUMANITEC_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
  "id": "sensitive",
  "description": "Encrypted bucket for storing confidential data"
}'

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_TYPE s3 The Humanitec resource type.

resource "humanitec_resource_class" "resource_class" {
  id            = "sensitive"
  resource_type = "s3"
  description   = "Encrypted bucket for storing confidential data"
}

Next steps

Top