Factories¶
A factory source describes the stack family you want to compile into one executable.
A small factory has inputs, imports, resources, and outputs:
factory: {
description: 'Writes a file.'
inputs: {
path: { type: string }
content: { type: string }
}
imports: { std: 'github.com/cloudboss/unobin-library-std' }
resources: {
file: std.fs-file {
path: input.path
content: input.content
}
}
outputs: {
path: { value: resource.file.path }
}
}
Inputs and locals¶
Inputs are values passed to the factory from the stack file. Locals are file-scoped expressions computed from inputs, resources, data sources, actions, and other locals where dependencies permit it.
Input constraints¶
Use constraints: to reject invalid combinations of factory inputs when a stack is planned. The compiler checks constraint syntax and verifies that fields: entries name declared inputs. The compiled factory evaluates the rules after stack inputs and defaults are combined, before it plans resources.
constraints: [
{ kind: exactly-one-of, fields: [input.image, input.build] },
{ kind: required-with, fields: [input.image, input.registry] },
{ kind: required-together, fields: [input.replicas[*].cert, input.replicas[*].key] },
{
kind: predicate
when: input.tier == 'prod'
require: @core.length(input.replicas) >= 2
message: 'prod needs at least two replicas'
},
{
kind: predicate
@for-each: input.replicas
when: @each.value.tls == true
require: @each.value.cert != null
message: 'tls replicas need certs'
},
]
Field-based constraints use a fields: list of input references. The set kinds are exactly-one-of, at-least-one-of, at-most-one-of, required-together, required-with, and forbidden-with. A field can point into an object or list item, such as input.code.inline or input.listeners[0].cert. A [*] field checks each list element; all splatted fields in one set rule must refer to the same list.
Predicate constraints evaluate when: first. If it is false, the rule passes. If it is true, require: must be true. A message: value replaces the default failure text.
A predicate can iterate over a list or map:
{
kind: predicate
@for-each: input.replicas
when: @each.value.tls == true
require: @each.value.cert != null
}
For nested iteration, use a list of binding objects:
{
kind: predicate
@for-each: [
{ @rule: input.rules },
{ @transition: @rule.value.transitions },
]
when: true
require: @transition.value.days != null
}
Factory constraints may read input.* values only. They may call @core functions and imported Go functions. Use them for the factory's input contract; Go library constraints still check each resource, data source, or action body wherever that library kind is called.
Resources, data sources, and actions¶
Resource, data source, and action bodies call imported library kinds:
actions: {
read-back: std.exec-command {
@trigger: resource.hello.sha256
argv: ['cat', input.path]
}
}
data-sources: {
github: aws.iam-openid-connect-provider {
url: input.oidc-provider-url
}
}
resources: {
app: std.fs-file {
path: local.path
content: input.content
}
}
State moves¶
State moves rename entries in state without recreating the external object:
Library configs¶
If an imported Go library declares a configuration schema, bind the import alias to the configuration in library-configs:
inputs: {
cloud-config: {
@sensitive: true
type: library-config('github.com/example/cloud')
default: {}
}
}
imports: { cloud: 'github.com/example/cloud' }
library-configs: {
cloud: input.cloud-config
}
Inputs may use library-config('...') to define the configuration type, passing in the library path. Thus, ordinary inputs are used to configure libraries; there is no separate configuration mechanism like Terraform uses for providers.
To configure multiple library instances with different configurations, import the library again under a different alias, and bind the configurations separately:
inputs: {
cloud-config: {
@sensitive: true
type: library-config('github.com/example/cloud')
default: {}
}
}
locals: {
cloud-config-east: @core.merge(input.cloud-config, { region: 'east' })
}
imports: {
cloud: 'github.com/example/cloud'
cloud-east: 'github.com/example/cloud'
}
library-configs: {
cloud: input.cloud-config
cloud-east: local.cloud-config-east
}
Compiling¶
Compile from the source root:
Alternatively, pass the path to the source root:
The resulting executable is the factory.