In the last article we covered how resources are provisioned. We explored how Resource Definitions describe how to produce a resource of a particular type. If we want to enable Dynamic Environments - we need a way of choosing the correct resource definition for a new environment. This is the other half of the resource provisioning problem: when to provision the resource.

Resources have a context

In Humanitec, resources always exist in a context. The resource exists in an Environment which in turn belongs to an Application which is held in an Organization. In addition, each resource that is requested by a developer has its own ID. This resource ID is actually the identifier used in the placeholders discussed in the first article.

This means that the full context of a resource is made up of the following data:

  • Application ID
  • Environment ID
  • Resource ID

This allows us to discriminate between different active instances of the same resources in different environments. For example, we might have a dns resource to access the public API of our application. The developer has created a dependency on a shared resource called api-dns. This makes its fully qualified resource ID shared.api-dns. If there are three environments (development, staging and production) then there will be three instances of this resource - one in each environment. Each active resource will have the same Application ID and Resource ID, but they will have different Environment IDs. Therefore they each have a unique context.

Application ID awesome-app awesome-app awesome-app
Environment ID development staging production
Resource ID shared.api-dns shared.api-dns shared.api-dns

Matching resource definitions

In the previous example, the developer defined a dependency on a DNS resource. We definitely need a different DNS name for each environment otherwise we cannot access them separately. Maybe we want the following three DNS names for our environments:

Development test-api.dev.awesome.com
Staging api.staging.awesome.com
Production api.awesome.com

This would mean that we need 3 resource definitions - one for each DNS name that we need. How do we match the resource definitions to the right environment?

Each Resource Definition has a set of "Matching Criteria" associated with it. When a resource needs to be provisioned, Humanitec checks all the resource definitions of the required type for their Matching Criteria. The resource definition with the closest matching criteria is used to create the resource!

For the above example, the matching criteria for the Resource Definition for each environment could look like:

dev-dns staging-dns prod-dns
Application ID awesome-app awesome-app awesome-app
Environment ID development staging production
Resource ID shared.api-dns shared.api-dns shared.api-dns

As you can see, the matching criteria is just the context!

Generalising matching for dynamic environments

Earlier we said: "The resource definition with the closest matching criteria is used to create the resource". If the matching criteria is just the context - isn't the match always clear? And why do we have such a complicated structure for linking resource definitions to environments?

What if we want to have dynamic development environments? We want Humanitec to be able to spin up new environments on demand - for example whenever a developer creates a PR. To do this, we would need a new DNS name for each environment that was spun up. We could use a DNS driver such as humanitec/dns-cloudflare to create new DNS names for us by generating unique subdomains. We would want to match this driver to any environment that does not have a static DNS name already defined.

Humanitec allows you to do this by omitting parts of the matching criteria. In this case, we would omit the Environment ID. The matching criteria would look like this:

generated-dns
Application ID awesome-app
Environment ID
Resource ID shared.api-dns

How does this work? Well, Humanitec finds "the resource definition with the closest matching criteria". Our original environments are unaffected. If we need a shared.api-dns in the staging environment in awesome-app, the best match is still the 'staging-dns' resource definition. But, if we create a new environment in that application - called pr-cool-feature it would not match any of our original matching criteria. It would match the generated-dns matching criteria though!

Keep your resources separate with Environment Types

Being able to match a Resource Definition to a dynamically generated environment is really powerful. A common problem though is that you might want to provision resources differently in different types of environments. For example, the QA team might want dynamic environments to make their testing more efficient. What if they also need to provision a resource dynamically but it needs to be done differently to the way the development environments are provisioned?

In Humanitec, every environment has a Type. For example, all your development environments might have a type of dev while the environments used by the QA team have a type of test. This would be a useful thing to be able to match on when choosing a Resource Definition!

Well, it turns out that Humanitec Matching Criteria also include Environment Type. As every environment has a type, it is an unambiguous thing to match on. Matching on Environment type is very powerful. It lets you partition resources between different classes of environment.

For example, we could extend our matching criteria from earlier as follows:

dev-dns staging-dns prod-dns generated-dns
Evironment Type dev stg prod dev
Application ID awesome-app awesome-app awesome-app awesome-app
Environment ID development staging production
Resource ID shared.api-dns shared.api-dns shared.api-dns shared.api-dns

Now there is no way to accidentally have auto-generated DNS names pointing to something in a production environment! It might be low stakes with DNS names, but consider what this allows for databases. You can ensure that dev databases stay connected to dev and production databases stay connected to production.

Summary

  • Each resource exists in a context made up of the application ID, environment ID and resource ID.
  • Resource Definitions are linked to the environment they are needed in by Matching Criteria.
  • The Matching Criteria that best matches the context of a to-be provisioned resource is used to choose the Resource Definition.
  • Parts of the Matching Criteria can be left blank to use the same resource definition across many contexts.
  • Environment Types can be used in the Matching Criteria to partition resources.