Surfacing information via Status
This is Part 4 of a series illustrating how to write Kratix Promises.
ππΎ Previous: Accessing secrets and storing state
ππΎ Next: Writing Compound Promises
In the previous tutorial, you learned how to access secrets and states from a Pipeline. In this section, you will learn how to surface information to users via the status of a Resource.
You will:
- Learn about the Metadata directory in Kratix Workflows
- Learn how to expose information via the Resource Status
Conveying information back to the application developersβ
What we've focused on so far is how to define a Kratix Promise in a compelling way so that platform users choose to use the platform. A Kratix-powered platform enables users to ask for the services they need, on-demand, without having to know unnecessary business and service lifecycle requirements.
What platform users need, though, is the end-to-end experience of making a simple request, understanding what's happening with the request, then ultimately making use of the service created by the request. So how do you communicate information about a request back to platform users, and how do users use the services that the platform creates?
There are actually a number of ways you can communicate the status of a service to the platform, and the choice comes down to the Promise and Promise Workflow author.
One approach is to generate notifications for internal systems like Slack or Teams from the Promise Workflow's Pipeline container.
Another approach, which is what we'll choose today, is to follow convention and leverage the status
field on Kubernetes resources. The Kratix Workflow's Pipeline has the ability to write information back to the status of the Resource.
In the context of your Promise, an example of what you might want to convey back is the configuration of the Resource (e.g. default configuration), and how to access the running Resources (e.g. a URL or connection string).
Statusβ
As we saw in Promise Workflows, within the Pipeline container file system, Kratix mounts a /kratix/metadata
directory to manage important configuration that is independent of the Resources definitions for your State Store.
In this section, you will focus on one of the files in the metadata
directory: status.yaml
.
The status.yaml
file can contain arbitrary key-value pairs. Anything you write in this file will be persistent in the resource, and visible when the user inspects it.
The message
key is a special key that is displayed as part of the kubectl get resource
command.
With that in-mind, you can update your existing resource-configure
script to surface some information about the Resource in the status.yaml
file. One of the things you may want to surface is, for example, the URL of the running application.
Add the following lines to the end of your resource-configure
script:
# Set the resource status
cat <<EOF > /kratix/metadata/status.yaml
message: "${name}.local.gd:31338"
EOF
Next, run the tests:
./scripts/test-pipeline resource-configure
You should see the following in your local directory:
π app-promise
...
βββ test
Β Β βββ input
Β Β βΒ Β βββ object.yaml
Β Β βββ metadata
Β Β βΒ Β βββ status.yaml
Β Β βββ output
Β Β βββ deployment.yaml
Β Β βββ ingress.yaml
Β Β βββ service.yaml
If you inspect the test/metadata/status.yaml
file, you should see something like the following:
message: "my-app.local.gd:31338"
Excellent. With that file in place, Kratix will automatically persist that information to the status of the Resource.
The status that persist to your resource will also be viewable the next time
the pipeline runs by inspecting the /kratix/input/object.yaml
. For example after
the very first pipeline has run, all future pipelines would have an input object
like the following:
apiVersion: workshop.kratix.io/v1
kind: App
metadata:
name: my-app
namespace: default
spec:
image: example/image:v1.0.0
service:
port: 9000
status:
message: "my-app.local.gd:31338"
Great! Now you have a way to persist information about your Resource.
This time, you haven't changed the Promise at all, but rather updated the underlying pipeline image. The existing resources will automatically get the new status information on the next reconciliation.
Just like last time, you can force a reconciliation by adding the kratix.io/manual-reconciliation = true
label:
kubectl --context $PLATFORM label apps.workshop.kratix.io todo kratix.io/manual-reconciliation=true
Once the pipeline completes, you should see the new status information on the Resource:
kubectl --context $PLATFORM describe apps.workshop.kratix.io todo
The above command outputs something similar to the following:
Name: todo
Namespace: default
Labels: <none>
Annotations: <none>
API Version: workshop.kratix.io/v1
Kind: App
Metadata:
Creation Timestamp: 2024-01-31T15:27:43Z
Finalizers:
kratix.io/work-cleanup
kratix.io/workflows-cleanup
kratix.io/delete-workflows
Generation: 1
Resource Version: 814088
UID: 19266d15-6362-4a54-85fb-b4965af65c24
Spec:
Image: syntasso/sample-todo:v0.1.0
Service:
Port: 8080
Status:
Conditions:
- lastTransitionTime: "2024-10-24T09:36:04Z"
message: Pipelines completed
reason: PipelinesExecutedSuccessfully
status: "True"
type: ConfigureWorkflowCompleted
Message: todo.local.gd:31338
Events: <none>
Note how the status
field is now populated with the information you added to the status.yaml
file.
You may also notice that there's a condition called ConfigureWorkflowCompleted
. This is
a special condition that is automatically added to the status of the Resource, and set to
True
when all Pipelines in the Configure workflow have completed successfully.
Furthermore, the value for the message
key should also be displayed as part of the get
command:
kubectl --context $PLATFORM get apps.workshop.kratix.io
The above command should output the following:
NAME STATUS
todo todo.local.gd:31338
Bonus Challengeβ
We just learned how to persist information about your Resource via using Status. As a bonus challenge, let's take a look at how to ensure that the information in Status is not overwritten if it already exists.
Kubernetes automatically adds a metadata.creationTimestamp
field to all resources. The date you will add to the status is only to illustrate how to add dynamic information to the status.
Update your resource-configure
script so it sets a new field createdAt
:
createdAt="$(date)"
# Set the resource status
cat <<EOF > /kratix/metadata/status.yaml
createdAt: ${createdAt}
message: "${name}.local.gd:31338"
EOF
Next, run the tests:
./scripts/test-pipeline resource-configure
If you inspect the test/metadata/status.yaml
file, you should see that there is a new field createdAt
added:
createdAt: "Wed Jan 1 15:00:00 UTC 2023"
message: "my-app.local.gd:31338"
If you run the pipeline again, you will see that the createdAt
field is updated to the current date. This is because our pipeline is not idempotent. You can fix this by checking the status on the resource, and only updating the status if it is not already set.
Update your workflow/resource-configure
script to the following:
createdAt=$(yq '.status.createdAt // ""' /kratix/input/object.yaml)
if [ -z "$createdAt" ]; then
createdAt="$(date)"
fi
# Set the resource status
cat <<EOF > /kratix/metadata/status.yaml
createdAt: "${createdAt}"
message: "${name}.local.gd:31338"
EOF
Now test your changes:
./scripts/test-pipeline resource-configure
Inspect the test/metadata/status.yaml
file. Since your input does not contain the status
field, you should see today's date in the createdAt
field. Now, to test that the status is not updated if it already exists, change your test input to look like the following:
apiVersion: workshop.kratix.io/v1
kind: App
metadata:
name: my-app
namespace: default
spec:
image: example/image:v1.0.0
service:
port: 9000
status:
createdAt: "Thu Jan 28 15:00:00 UTC 2021"
message: "my-app.local.gd:31338"
Re-run the tests:
./scripts/test-pipeline resource-configure
If you inspect the test/metadata/status.yaml
file now, you should see the following:
createdAt: "Thu Jan 28 15:00:00 UTC 2021"
message: "my-app.local.gd:31338"
Awesome! You now have a way to ensure that the information is not overwritten if it already exists.
Let's force a reconciliation by adding the kratix.io/manual-reconciliation = true
label:
kubectl --context $PLATFORM label apps.workshop.kratix.io todo kratix.io/manual-reconciliation=true
Once the pipeline completes, you should see the new status information on the Resource:
kubectl --context $PLATFORM describe apps.workshop.kratix.io todo
The above command outputs something similar to the following:
Name: todo
Namespace: default
API Version: workshop.kratix.io/v1
Kind: App
Metadata:
Creation Timestamp: 2024-01-31T15:27:43Z
...
Spec:
Image: syntasso/sample-todo:v0.1.0
Service:
Port: 8080
Status:
...
Created At: Wed Jan 1 15:01:01 UTC 2023
Message: todo.local.gd:31338
Events: <none>
Note how the status
field is now populated with the information you added to the status.yaml
file.
Try retriggering the reconciliation by adding the label again. You should notice that the CreatedAt
field is not updated, just like you observed in the test.
π Β Congratulations!β
You successfully surfaced information back to the user by writing to the resource status! You Promise is getting more and more useful.
To recap what you achieved:
- β Β Β Learned how to use metadata to set a custom Resource status
- β Β Β Learned how to make writing to status idempotent
ππΎΒ Β Next, let's add support for stateful applications.