Bundle Runtime
While Timoni Bundles offer a way to specify the config values in declarative manner, not all the configuration values of an application are known ahead of time. Some values may be available at runtime, in the Kubernetes clusters where the Bundle is applied.
For example, the API token for some backend service that your app consumes is stored in a Kubernetes Secret in-cluster. When installing the application with Timoni, you may want to fetch the token from the Kubernetes Secret and use it as a config value.
Timoni Runtime API offers a way to define values which are fetched from the Kubernetes API and mapped to fields inside a Bundle.
Example
The following is an example of a Runtime definition that extracts values from the cluster.
runtime: {
apiVersion: "v1alpha1"
name: "production"
clusters: {
// using the cluster set in kubeconfig current context
}
values: [
{
query: "k8s:v1:ConfigMap:infra:aws-info"
for: {
"ACCOUNT_ID": "obj.data.account_id"
"REGION": "obj.data.region"
}
},
{
query: "k8s:v1:Secret:infra:redis-auth"
for: {
"REDIS_PASS": "obj.data.password"
"REDIS_CA": "obj.data.\"ca.crt\""
}
},
]
}
The values from the Runtime, can be mapped to fields in a Bundle using @timoni()
attributes.
bundle: {
_id: string @timoni(runtime:string:ACCOUNT_ID)
_reg: string @timoni(runtime:string:REGION)
_pass: string @timoni(runtime:string:REDIS_PASS)
apiVersion: "v1alpha1"
name: "podinfo"
instances: {
podinfo: {
module: url: "oci://\(_id).dkr.ecr.\(_reg).amazonaws.com/modules/podinfo"
namespace: "podinfo"
values: caching: {
enabled: true
redisURL: "tcp://:\(_pass)@redis.infra:6379"
}
}
}
}
Assuming the ConfigMaps and Secrets are in the cluster,
and the Runtime file is runtime.cue
and the Bundle file is bundle.cue
.
Build the runtime to see which values are present on the cluster:
timoni runtime build -f runtime.cue
ACCOUNT_ID: 1234567890
REGION: us-west-2
REDIS_PASS: password
REDIS_CA:
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl
cm5ldGVzMB4XDTIzMDgxMDE1MTA1MFoXDTMzMDgwNzE1MTA1MFowFTETMBEGA1UE
AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJgA
mOniTayGyXG51eYeb1oJGCszRlZv4ci2kzLC0H4YCph6TXDEawrsvyuzGe7fwp4t
Jq2fQiJgKbibOjHcOwhYqp1lJDu37p6WsLvx/RxbkHoVX8X2n7n1BVS5MbQ81on9
BdXkff71g8n3HmJo8ZN1abEW434tHFwjjGMDzFoqxTnNM+qMKegxAtTPVbjV4X+5
4/95L7T0XNd97XnunSUAEJRroMTRuaBAcYz81N0ix3Mc0T+G16aV127R0ZFkPlS8
bzjA1NGgbiveX7i7n9mNG4Gy7iElP0iidtdrA6loxWYLW1jmEH5/pWQnki7s3OrK
/94xrQ2MaDpXUzcoc0cCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFJ7f9QzKWA/BQJSRinKne4FZNzScMBUGA1UdEQQO
MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAAhGgYFB90Rkexgh56q8
n8uC4XQF6OTJJf/4A9VXNbiZhG+PUYkMUyqeOSOq+IztJVOffuG9ZutYQtJYCX7g
TUQmZxeKAw1MAMEWAELmNVdbG6Y6YZ14v8SI+TADqeqcIkmDEkACygfeHKdEFOfV
LoRAc3Zn32byP5M6EVjTXWvb+UbVt7umoBOCbMo3BEF71TEDI5Oww6y3YxDM9/Y7
MCTu+0XHZNGDgprmci6fzLv8PV0C6eiuUrrBM237WOfk4BYL6pmCDcCWo6aP2cH7
Y5zcVBxKUs/h5bZfLJFuwXJC5zWudNLOAtNtFhctMwDoNaKYq720g/GmEroq4wgA
tBI=
-----END CERTIFICATE-----
Apply the Bundle using the values from the Runtime:
timoni bundle apply -f bundle.cue --runtime runtime.cue
applying instance podinfo
pulling oci://1234567890.dkr.ecr.us-west-2.amazonaws.com/modules/podinfo
using module timoni.sh/podinfo version 6.3.5
installing podinfo in namespace podinfo
ServiceAccount/podinfo/podinfo created
Service/podinfo/podinfo created
Deployment/podinfo/podinfo created
waiting for 3 resource(s) to become ready...
resources are ready
Writing a Runtime spec
A Runtime file must contain a definition that matches the following schema:
#Runtime: {
apiVersion: string
name: string
clusters?: [string]: {
group!: string
kubeContext!: string
}
values?: [...#RuntimeValue]
}
#RuntimeValue: {
query: string
for: {[string]: string}
optional: *false | bool
}
API version
The apiVersion
is a required field that specifies the version of the Runtime schema.
Currently, the only supported value is v1alpha1
.
Name
The name
is a required field used to identify the Runtime.
Clusters
The clusters
field is for defining the target clusters and
environments (group of clusters) where a Bundle is applied.
A cluster entry must specify the group
and kubeContext
fields.
The kubeContext
value must match a context name from the .kube/config
file.
Default cluster
When no clusters are defined in the Runtime, Timoni will use the
current context from the kubeconfig, unless the context is specifed
using the --kube-context
flag.
Example:
runtime: {
apiVersion: "v1alpha1"
name: "fleet"
clusters: {
"preview-us-1": {
group: "staging"
kubeContext: "eks-us-west-2"
}
"prod-us-1": {
group: "production"
kubeContext: "eks-us-west-1"
}
"prod-eu-1": {
group: "production"
kubeContext: "eks-eu-west-1"
}
}
}
The clusters name and group, can be mapped to fields in a Bundle using @timoni()
attributes.
bundle: {
_cluster: string @timoni(runtime:string:TIMONI_CLUSTER_NAME)
_env: string @timoni(runtime:string:TIMONI_CLUSTER_GROUP)
apiVersion: "v1alpha1"
name: "apps"
instances: {
app: {
module: url: "oci://ghcr.io/stefanprodan/modules/podinfo"
namespace: "apps"
values: {
ui: message: "Hosted by \(_cluster)"
if _env == "staging" {
replicas: 1
}
if _env == "production" {
replicas: 2
}
}
}
}
}
When applying the above Bundle, Timoni will deploy the app instances to all the clusters, in the order defined in the Runtime. If the apply fails on a staging cluster, Timoni will stop the execution and not continue with production.
For more details please see the multi-cluster deployments guide.
Values
The values
array is for specifying
the list of Kubernetes resources and the fields to be extracted.
Query
The values.query
is a required field that specifies the Kubernetes resource.
The query
field must be in the format k8s:<apiVersion>:<kind>:<namespace>:<name>
.
Example:
runtime: {
apiVersion: "v1alpha1"
name: "production"
values: [
{
query: "k8s:v1:Secret:infra:redis-auth"
for: {
"REDIS_PASS": "obj.data.password"
}
},
]
}
If the Kubernetes resource is global, the query
format is k8s:<apiVersion>:<kind>:<name>
.
Example:
runtime: {
apiVersion: "v1alpha1"
name: "production"
values: [
{
query: "k8s:cert-manager.io/v1:ClusterIssuer:letsencrypt"
for: {
"ISSUER_EMAIL": "obj.spec.acme.email"
}
},
]
}
For
The values.for
is a required map that specifies which fields to be extracted from the Kubernetes resource.
The for
map must contain pairs of name and CUE expression.
Example:
runtime: {
apiVersion: "v1alpha1"
name: "production"
values: [
{
query: "k8s:source.toolkit.fluxcd.io/v1:GitRepository:flux-system:cluster"
for: {
"GIT_REVISION": "obj.status.artifact.revision"
"GIT_STATUS": "[for c in obj.status.conditions if c.type == \"Ready\" {c.status}][0]"
}
},
]
}
The CUE expression must result in a concrete value of type string
, number
or bool
.
Optional
The optional
field can be set to true
and Timoni will skip
not found Kubernetes resources instead of throwing an error.
Using values from Kubernetes API
The values defined in a Runtime can be referred in Bundles using CUE attributes.
The @timoni(runtime:[string|number|bool]:[VAR_NAME])
CUE attribute can be placed next
to a field to set its value from the runtime.
bundle: {
apiVersion: "v1alpha1"
name: "app"
instances: {
app: {
module: url: "oci://localhost:5000/modules/app"
namespace: "apps"
values: {
host: string @timoni(runtime:string:HOST)
enabled: bool @timoni(runtime:bool:ENABLED)
score: int @timoni(runtime:number:SCORE)
}
}
}
}
If a runtime value is optional, you can provide defaults which will be used only if the runtime doesn't contain the referenced value:
values: {
host: "example.com" @timoni(runtime:string:HOST)
enabled: true @timoni(runtime:bool:ENABLED)
score: 1 @timoni(runtime:number:SCORE)
}
To load the runtime values at apply time, use the --runtime
flag:
timoni bundle apply -f bundle.cue --runtime runtime.cue
At apply time, Timoni injects the fields values from the runtime,
if a specified runtime var is not found and if a default is not provided,
the apply with fail with an incomplete value
error.
Using values from environment variables
To use values from environment variables, the environment must contain variables which match the CUE attributes.
Example:
bundle: {
apiVersion: "v1alpha1"
name: "app"
instances: {
app: {
module: url: "oci://localhost:5000/modules/app"
namespace: "apps"
values: {
sshKey: string @timoni(runtime:string:SSH_KEY)
isAdmin: false @timoni(runtime:bool:IS_ADMIN)
}
}
}
}
Export the env vars and run the timoni bundle apply --runtime-from-env
command.
EXPORT SSH_KEY=$(cat .ssh/id_ecdsa.pub)
EXPORT IS_ADMIN="true"
timoni bundle apply -f bundle.cue --runtime-from-env
Mixing values from the Runtime and Environment
When using timoni bundle apply --runtime runtime.cue --runtime-from-env
,
the values coming from the Runtime take precedence over the Environment.