In this guide we will use the Kuard application image which was created by the authors of Kubernetes Up and Ready book, and is able to render a list of its environment variables:
- Kuard repository
- Kuard image: gcr.io/kuar-demo/kuard-amd64:blue
A very generic playground kuard CRD can be found at Scoby repo, containing combination of nested elements, arrays, object references and full status support for Scoby.
A flattened version of that CRD spec
contents would look like this:
spec.variable1
spec.variable2
spec.group.variable3
spec.group.variable4
spec.array[]
spec.reftoSecret.secretName
spec.reftoSecret.secretKey
spec.refToConfigMap.configName
spec.refToConfigMap.configKey
spec.refToAddress.uri
spec.refToAddress.ref.apiVersion
spec.refToAddress.ref.kind
spec.refToAddress.ref.name
spec.refToAddress.ref.namespace
For this guide we will create the CRD once, and use it with different registrations, resulting in different workloads settings for each of them:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/01.kuard-crd.yaml
The Kuard CRD created above needs to be managed by the Scoby controller. An aggregated ClusterRole
provides the mechanism to grant those permissions by tagging a ClusterRole
with the scoby.triggermesh.io/crdregistration: true
label.
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/02.kuard-clusterrole.yaml
The results of each registration at this guide can be check by port forwarding the generated workload and navigating kuard, or by inspecting the generated pod's environment variables.
- Navigating kuard: forward the generated service for a deployment. In the case of a Knative Serving service, use the external address to access the UI.
kubectl port-forward svc/my-kuard-extension 8888:80
- Inspecting pod's environment variables: replace the pod name with the Scoby rendered pod.
kubectl get po my-rendered-pod -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
We will stick to the pod inspecting method but for the first example, where we will use both.
The deployment registration is going to be used for most examples at this guide due to not requiring any added software compared with the Knative Service registration. The form factor at this example is configured to create one pod and a service that listens on 80 and forward requests to the pod's 8080, where the kuard application is listening:
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
You can also find in the YAML snippet above the reference to the CRD and image that will remain constant throughout all examples.
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/01.deployment/01.kuard-registration.yaml
Scoby spins up a controller that will manage kuard01
objects. Let's create an instance, and to get started with the default rendering behavior, let's fill some elements in it:
apiVersion: extensions.triggermesh.io/v1
kind: Kuard
metadata:
name: my-kuard-extension
spec:
variable1: value 1
variable2: value 2
group:
variable3: false
variable4: 42
array:
- alpha
- beta
- gamma
The spec above matches a subset of the CRD structure. Only these fields will be converted into environment variables when applied.
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/01.deployment/02.kuard-instance.yaml
A deployment and a service must have been generated:
kubectl get deployment,svc -l app.kubernetes.io/name=kuard
Retrieve the pod's environment variables.
Note: using the label selector return a list, we expect a single pod to match it at items[0]
:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
The result shows Scoby rendering each informed element as environment variables whose name is a capitalized concatenation of the element hierarchy:
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "VARIABLE1",
"value": "value 1"
},
{
"name": "VARIABLE2",
"value": "value 2"
}
]
Exploring kuard's interface we can also find these environment variables:
kubectl port-forward svc/my-kuard-extension 8888:80
If the registered CRD constains a status.address.url
element, and it renders a Kubernetes service or Knative service, the internal address is populated at the aforementioned element.
kubectl get kuard my-kuard-extension -ojsonpath='{.status}' | jq .
{
"address": {
"url": "http://my-kuard-extension.default.svc.cluster.local"
},
"conditions": [
{
"lastTransitionTime": "2023-03-23T13:09:44Z",
"message": "",
"reason": "MinimumReplicasAvailable",
"status": "True",
"type": "DeploymentReady"
},
{
"lastTransitionTime": "2023-03-23T13:09:44Z",
"message": "",
"reason": "CONDITIONSOK",
"status": "True",
"type": "Ready"
},
{
"lastTransitionTime": "2023-03-23T13:09:44Z",
"message": "",
"reason": "ServiceExist",
"status": "True",
"type": "ServiceReady"
}
],
"observedGeneration": 1
}
Let's remove the kuard instance and registration before proceeding with the next one:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
This will remove the kuard instance and its generated components, and the Scoby registration.
The Knative Service registration requires Knative Serving to be installed. Follow the instructions at the Knative site to install it. The form factor at this example is configured to create a public service that scales between 1 and 3 instances. You can set the minScale
parameter to 0 to enable scale to 0
feature:
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
knativeService:
minScale: 1
maxScale: 3
visibility: public
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/02.knative-service/01.kuard-registration.yaml
Now create the same kuard instance we created for the deployment registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/02.knative-service/02.kuard-instance.yaml
The service generates a pod whose environment variables can be inspected using a Knative Service version of the label filter that we used for the deployment:
kubectl get po -l serving.knative.dev/service=my-kuard-extension -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
You can find some Knative Serving variables being added, and the same Scoby variables we got at the Deployment form factor example.
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "VARIABLE1",
"value": "value 1"
},
{
"name": "VARIABLE2",
"value": "value 2"
},
{
"name": "PORT",
"value": "8080"
},
{
"name": "K_REVISION",
"value": "my-kuard-extension-00001"
},
{
"name": "K_CONFIGURATION",
"value": "my-kuard-extension"
},
{
"name": "K_SERVICE",
"value": "my-kuard-extension"
}
]
Let's clean up the example.
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
When an element in the spec is not meant to generate an environment variable, the rendering can be skipped via a configuration parameter.
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
parameterConfiguration:
fromSpec:
# Skip variable2 from generating a parameter for the workload
skip:
- path: spec.variable2
The spec.workload.parameterConfiguration.fromSpec[].skip
boolean indicates whether the environment variable for the element should be generated.
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/03.param.skip/01.kuard-registration.yaml
Create the same instance we have created so far:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/03.param.skip/02.kuard-instance.yaml
Inspect generated environment variables:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
Look at the result:
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "VARIABLE1",
"value": "value 1"
}
]
Rendering skipped .spec.variable2
rendering.
Clean up the example:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
Most often expected environment variables at the container do not match Scoby's automatic rendering. All generated environment variables can be renamed using spec.workload.parameterConfiguration.fromSpec[].toEnv.name
.
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
parameterConfiguration:
fromSpec:
toEnv:
# Rename variable2
- path: spec.variable2
name: KUARD_VARIABLE_TWO
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/04.param.rename/01.kuard-registration.yaml
Create the same instance we have created so far:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/04.param.rename/02.kuard-instance.yaml
Inspect generated environment variables:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
Look at the result:
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "VARIABLE1",
"value": "value 1"
},
{
"name": "KUARD_VARIABLE_TWO",
"value": "value 2"
}
]
Note the variable at .spec.variable2
renamed as KUARD_VARIABLE_TWO
.
Clean up the example:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
The value for an environment variable can be set to a default value using spec.workload.parameterConfiguration.fromSpec[].toEnv.defaultValue
.
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
parameterConfiguration:
fromSpec:
toEnv:
# Override variable2 value
- path: spec.variable2
defaultValue: new variable2 value
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/05.param.value/01.kuard-registration.yaml
Create a mutation fo the instance we have created so far that doesn't inform spec.value2
:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/05.param.value/02.kuard-instance.yaml
Inspect generated environment variables:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
Look at the result:
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "VARIABLE1",
"value": "value 1"
},
{
"name": "VARIABLE2",
"value": "new variable2 value"
}
]
Note the variable at .spec.variable2
value has been defaulted.
Clean up the example:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
The value for an environment variable can reference a Secret through the spec.workload.parameterConfiguration.fromSpec[].toEnv.valueFromSecret
customization option, that needs the name
and key
subelements to be set. In this example we will also be setting the variable name.
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
parameterConfiguration:
fromSpec:
toEnv:
# Reference a secret
- path: spec.refToSecret
name: FOO_CREDENTIALS
valueFrom:
secretPath:
name: spec.refToSecret.secretName
key: spec.refToSecret.secretKey
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/06.param.secret/01.kuard-registration.yaml
Create the same instance we have created so far plus the Secret it references:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/06.param.secret/02.kuard-instance.yaml
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/06.param.secret/03.secret.yaml
Inspect generated environment variables:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
Look at the result:
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "FOO_CREDENTIALS",
"valueFrom": {
"secretKeyRef": {
"key": "kuard-key",
"name": "kuard-secret"
}
}
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "VARIABLE1",
"value": "value 1"
},
{
"name": "VARIABLE2",
"value": "value 2"
}
]
Note the variable at .spec.refToSecret
is rendered with name FOO_CREDENTIALS
as a reference to a secret.
Clean up the example:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
kubectl delete secret kuard-secret
The value for an environment variable can reference a ConfigMap through the spec.workload.parameterConfiguration.fromSpec[].toEnv.valueFrom.configMapPath
customization option, that needs the name
and key
subelements to be set.
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
parameterConfiguration:
fromSpec:
toEnv:
# Reference a ConfigMap
- path: spec.refToConfigMap
valueFrom:
configMapPath:
name: spec.refToConfigMap.configName
key: spec.refToConfigMap.configKey
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/07.param.configmap/01.kuard-registration.yaml
Create the same instance we have created so far plus the ConfigMap it references:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/07.param.configmap/02.kuard-instance.yaml
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/07.param.configmap/03.configmap.yaml
Inspect generated environment variables:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
Look at the result:
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "REFTOCONFIGMAP",
"valueFrom": {
"configMapKeyRef": {
"key": "kuard-key",
"name": "kuard-configmap"
}
}
},
{
"name": "VARIABLE1",
"value": "value 1"
},
{
"name": "VARIABLE2",
"value": "value 2"
}
]
Note the variable at .spec.refToConfigMap
is rendered with name REFTOCONFIGMAP
as a reference to a ConfigMap.
Clean up the example:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
kubectl delete configmap kuard-configmap
If part of a spec uses a Destination duck type to express a location, just like Knative Sinks do, the registration can be used to resolve it and use the result as an environment variable.
A destination duck type informs either an URI, a Kubernetes service, or a Kubernetes object that contains a URL at status.address.url
.
destination:
ref:
apiVersion: <version>
kind: <kind>
namespace: <namespace - optional>
name: <name>
uri: <uri>
Use the built-in function spec.workload.parameterConfiguration.fromSpec[].toEnv.valueFrom.builtInFunc.resolveAddress
on the element that contains the Destination type. As an added feature this example also updates an status element with the resolved address.
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
parameterConfiguration:
fromSpec:
toEnv:
# Resolve an address
- path: spec.refToAddress
name: FOO_SINK
valueFrom:
builtInFunc:
name: resolveAddress
statusConfiguration:
add:
# Add the result to an status element
- path: status.sinkUri
valueFrom:
path: spec.refToAddress
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/08.param.addressable/01.kuard-registration.yaml
Create a service or addressable and reference it from a kuard instance:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/08.param.addressable/02.kuard-instance.yaml
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/08.param.addressable/03.service.yaml
Inspect generated environment variables:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
Look at the result:
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "FOO_SINK",
"value": "http://my-service.default.svc.cluster.local"
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "VARIABLE1",
"value": "value 1"
},
{
"name": "VARIABLE2",
"value": "value 2"
}
]
Note the variable at .spec.refToAddress
is rendered with name FOO_SINK
containing the DNS address for the service.
Also check the status:
kubectl get kuard my-kuard-extension -ojsonpath='{.status}' | jq .
The status.sinkUri
element has been filled with the value from the resolved address above.
{
"address": {
"url": "http://my-kuard-extension.default.svc.cluster.local"
},
"conditions": [
{
"lastTransitionTime": "2023-03-21T09:40:38Z",
"message": "",
"reason": "MinimumReplicasAvailable",
"status": "True",
"type": "DeploymentReady"
},
{
"lastTransitionTime": "2023-03-21T09:40:38Z",
"message": "",
"reason": "CONDITIONSOK",
"status": "True",
"type": "Ready"
},
{
"lastTransitionTime": "2023-03-21T09:40:38Z",
"message": "",
"reason": "ServiceExist",
"status": "True",
"type": "ServiceReady"
}
],
"observedGeneration": 1,
"sinkUri": "http://my-service.default.svc.cluster.local"
}
Clean up the example:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
kubectl delete service my-service
In scenarios where parameters unrelated to the instance .spec
data needs to be added, the spec.workload.parameterConfiguration.add.Envs[]
is used. An array of EnvVar can be provided referencing literal values, ConfigMaps, Secrets or the Downward API.
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
parameterConfiguration:
# A new variable will be created using a reference to the object's field.
add:
toEnv:
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/09.param.add.metadata/01.kuard-registration.yaml
Create a kuard instance:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/09.param.add.metadata/02.kuard-instance.yaml
Inspect generated environment variables:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
Look at the result:
[
{
"name": "ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "GROUP_VARIABLE3",
"value": "false"
},
{
"name": "GROUP_VARIABLE4",
"value": "42"
},
{
"name": "MY_POD_NAME",
"valueFrom": {
"fieldRef": {
"apiVersion": "v1",
"fieldPath": "metadata.name"
}
}
},
{
"name": "VARIABLE1",
"value": "value 1"
},
{
"name": "VARIABLE2",
"value": "value 2"
}
]
Note the variable named MY_POD_NAME
using the Downward API.
Clean up the example:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
Generated environment variables names can be added a prefix by using the spec.workload.parameterConfiguration.global.defaultPrefix
element. All generated names will use the prefix but for those that contain extra configuration that set a key name.
apiVersion: scoby.triggermesh.io/v1alpha1
kind: CRDRegistration
metadata:
name: kuard
spec:
crd: kuards.extensions.triggermesh.io
workload:
formFactor:
deployment:
replicas: 1
service:
port: 80
targetPort: 8080
fromImage:
repo: gcr.io/kuar-demo/kuard-amd64:blue
parameterConfiguration:
# Use a prefix for all generated variables.
global:
defaultPrefix: KUARD_
Create the registration:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/10.param.prefix/01.kuard-registration.yaml
Create a kuard instance:
kubectl apply -f https://raw.githubusercontent.com/triggermesh/scoby/main/docs/samples/01.kuard/10.param.prefix/02.kuard-instance.yaml
Inspect generated environment variables:
kubectl get po -l app.kubernetes.io/name=kuard -ojsonpath='{.items[0].spec.containers[0].env}' | jq .
Look at the result:
[
{
"name": "KUARD_ARRAY",
"value": "alpha,beta,gamma"
},
{
"name": "KUARD_GROUP_VARIABLE3",
"value": "false"
},
{
"name": "KUARD_GROUP_VARIABLE4",
"value": "42"
},
{
"name": "KUARD_VARIABLE1",
"value": "value 1"
},
{
"name": "KUARD_VARIABLE2",
"value": "value 2"
}
]
Note that each key has been prefixed with KUARD_
.
Clean up the example:
kubectl delete kuard my-kuard-extension
kubectl delete crdregistration kuard
Remove the registered CRD and ClusterRole.
kubectl delete clusterrole crd-registrations-scoby-kuard
kubectl delete crd kuards.extensions.triggermesh.io
Important Note: Due to limitations at controller-runtime, removing a CRD that has been watched will lead to logging errors at the still existing informer. That can be solved restarting the informer and will be solved after this issue is solved.