Skip to main content

Resource hooks

One of the most powerful features of datacenters is the ability for developers to request cloud resources that get fulfilled uniquely by the datacenter. These resources represent a contract between Components and Datacenters that allows them both to work together to create and deploy cloud software.

When Components get deployed, Architect extracts all the relevant cloud resources and queues them up for the datacenter to fulfill with its own modules. The way the datacenter "hooks" into these resource claims to fulfill them is with declarations inside the environment block of the datacenter configuration.

In doing so, the modules have full access to the resource inputs, but are required to also provide back the resource "outputs". By providing the relevant output values, datacenters don't need to have any knowledge of the relationship between the resources, allowing the process of fulfilling the requests to be as simple as possible. If a deployment expects one of its environment variables to be populated with the address of a peer service, the datacenter simply needs to cite the url as an output of the service hook and doesn't actually care what other resources are going to consume the value. Instead, Architect will ensure the relevant resources get the correct output values.

Supported resource types

The resources Architect supports are limited only to resources relevant to applications. Infrastructure resources like VPCs, kubernetes clusters, DNS zones, etc. are only relevant to applications if they end up being used to fulfill core resource claims. For example, a service can be fulfilled by a kubernetes cluster or a separate service mesh - the application doesn't care which so long as it gets a resolvable URL at the end.

Below is a full list of the supported resource types, along with the relevant input/output specs:

database

The database resource represents a request for a specific type/version of database. These requests can be fulfilled by a shared database cluster just as easily as they can be by individual database clusters. It's up to the datacenter to decide the specifics.

Example

environment {
database {
when = node.inputs.databaseType == "postgres"

module "dbCluster" {
source = "./db/cluster"
inputs = {
# ...
}
}

outputs = {
host = module.dbCluster.host
port = module.dbCluster.port
username = module.dbCluster.username
}
}
}

Inputs

Loading...

Outputs

Loading...

databaseUser

The databaseUser resource type represents a request for a unique set of credentials. Architect generates these requests on behalf of components whenever it detects that different applications are asking for access to the same database.

Example

environment {
databaseUser {
when = node.inputs.databaseType == "postgres"

module "pgUser" {
source = "./pg/user"
inputs = {
rootUser = node.inputs.username
rootPass = node.inputs.password
}
}

outputs = {
username = module.dbCluster.username
password = module.dbCluster.password
}
}
}

Inputs

Loading...

Outputs

Loading...

deployment

The deployment resource represents a request to deploy and maintain one or more replicas of an application. For those familiar with Kubernetes, this is a near identical concept to the Deployment resource.

Example

environment {
deployment {
module "deployment" {
build = "./k8s-deployment"
inputs = merge(node.inputs, {
namespace = module.namespace.id
kubeconfig = module.k8s.kubeconfig
})
}
}
}

Inputs

Loading...

Outputs

Loading...

service

The service resource represents an application's need to expose an application for internal network traffic. These are common for internal APIs.

Example

environment {
service {
module "service" {
build = "./k8s-service"
inputs = merge(node.inputs, {
name = "${node.component}--${node.name}"
namespace = module.namespace.id
kubeconfig = module.k8s.kubeconfig
})
}

outputs = {
protocol = node.inputs.protocol || "http"
host = module.service.host
port = module.service.port
url = "${nodes.inputs.protocol}://${module.service.host}:${module.service.port}"
}
}
}

Inputs

Loading...

Outputs

Loading...

ingress

Ingress resources represent an application's need to expose an application for external traffic. This is common for web applications and external APIs.

Example

environment {
ingress {
module "ingressRule" {
build = "./ingressRule"
inputs = merge(node.inputs, {
name = "${node.component}--${node.name}"
namespace = module.namespace.id
kubeconfig = module.k8s.kubeconfig
dns_zone = variable.dns_zone
ingress_class_name = module.nginxController.ingress_class_name
})
}

module "dnsRecord" {
build = "./dns-record"
environment = {
DIGITALOCEAN_TOKEN = variable.do_token
}
inputs = {
domain = variable.dns_zone
type = "A"
value = module.ingressRule.load_balancer_ip
subdomain = node.inputs.subdomain
}
}

outputs = {
protocol = module.ingressRule.protocol
host = module.ingressRule.host
port = module.ingressRule.port
username = module.ingressRule.username
password = module.ingressRule.password
url = module.ingressRule.url
path = module.ingressRule.path
subdomain = node.inputs.subdomain
dns_zone = node.inputs.dns_zone
}
}
}

Inputs

Loading...

Outputs

Loading...

cronjob

Cronjobs represent a request to run a script on a schedule.

Example

environment {
cronjob {
module "cronjob" {
build = "./cronjob"
inputs = merge(node.inputs, {
namespace = module.namespace.id
kubeconfig = module.k8s.kubeconfig
})
}

outputs = {
data = node.inputs.data
}
}
}

Inputs

Loading...

Outputs

Loading...

secret

Secret resources represent an application's need to store sensitive data for their applications. These are often component inputs or outputs.

Example

environment {
secret {
module "secret" {
build = "./secret"
plugin = "opentofu"
inputs = {
filename = "${var.secretsDir}--${environment.name}--${node.component}--${node.name}.json"
content = node.inputs.data
}
}

outputs = {
data = node.inputs.data
}
}
}

Inputs

Loading...

Outputs

Loading...

dockerBuild

This resource type is only used when deploying components from source. Each node represents a job that builds a docker image from the component source. This is common for ephemeral and preview environments.

Example

environment {
dockerBuild {
module "build" {
build = "./docker-build"

environment = {
DOCKER_HOST = "unix:///var/run/docker.sock"
}

volume {
host_path = "/var/run/docker.sock"
mount_path = "/var/run/docker.sock"
}

inputs = {
image = "${node.component}-${node.name}"
context = node.inputs.context
dockerfile = node.inputs.dockerfile
args = node.inputs.args
target = node.inputs.target
}
}

outputs = {
image = module.build.image
}
}
}

Inputs

Loading...

Outputs

Loading...