Deployment Tasks with Keptn
A KeptnTaskDefinition resource defines one or more "executables" (functions, programs, scripts, etc.) that Keptn runs as part of the pre- and post-deployment phases of a KeptnApp or KeptnWorkload.
- pre-deployment (before the pod is scheduled)
- post-deployment (after the pod is scheduled)
These KeptnTask
resources and the
KeptnEvaluation
resources (discussed in
Evaluations)
are part of the Keptn Release Lifecycle Management.
A
KeptnTask
executes as a runner in an application
container,
which runs as part of a Kubernetes
job.
A KeptnTaskDefinition
includes calls to executables to be run.
To implement a KeptnTask
:
- Define a KeptnTaskDefinition resource that defines the runner to use for the container and the executables to be run pre- and post-deployment
- Apply basic-annotations to your workloads.
- Generate the required KeptnApp resources following the instructions in Auto app discovery.
- Annotate your workload YAML files
to associate your
KeptnTaskDefinition
with the pre-/post-deployment tasks that should be run. - Create the appropriate
KeptnAppContext
resource to associateKeptnTaskDefinition
resources to the generatedKeptnApp
.
This page provides information to help you create your tasks:
- Code your task in an appropriate runner
- How to control the
execution order
of functions, programs, and scripts
since all
KeptnTask
resources at the same level run in parallel - Understand how to use Context that contains a Kubernetes cluster, a user, a namespace, the application name, workload name, and version.
- Use parameterized functions if your task requires input parameters
- Create secret text and pass secrets to a function if necessary.
Runners and containers
Each KeptnTaskDefinition
can use exactly one container with one runner.
The runner you use determines the language you can use
to define the task.
The spec
section of the KeptnTaskDefinition
defines the runner to use for the container:
Keptn provides a general Kubernetes container runtime that you can configure to do almost anything you want:
- The
container-runtime
runner provides a pure custom Kubernetes application container that you define to includes a runtime, an application and its runtime dependencies. This gives you the greatest flexibility to define tasks using the language and facilities of your choice
Keptn also includes two "pre-defined" runners:
- Use the
deno-runtime
runner to define tasks using Deno scripts, which use JavaScript/Typescript syntax with a few limitations. You can use this to specify simple actions without having to define a container. - Use the
python-runtime
runner to define your task using Python 3.
For the pre-defined runners (deno-runtime
and python-runtime
),
the actual code to be executed
can be configured in one of four different ways:
- inline
- referring to an HTTP script
- referring to another
KeptnTaskDefinition
- referring to a ConfigMap resource that is populated with the function to execute
See the KeptnTaskDefinition reference page for the synopsis and examples for each runner.
Run a task associated with your workload deployment
To define pre-/post-deployment tasks, you must manually edit the YAML files to add annotations for your tasks to the appropriate workload YAML file.
Specify one of the following annotations/labels for each task you want to execute:
The value of each annotation corresponds
to the value of the name
field of the
KeptnTaskDefinition
resource.
Run a task associated with your entire KeptnApp
To execute pre-/post-deployment tasks for a KeptnApp
,
create a KeptnAppContext
with the same name and in the same namespace
as the KeptnApp
.
The KeptnAppContext
resource contains a list of
pre-/post-deployment tasks
that should be executed before and after the
workloads within the KeptnApp
are deployed.
See the Getting started guide
for more information on how to configure a KeptnAppContext
resource
to execute pre-/post-deployment checks.
KeptnAppContext
is also used to collect user defined
metadata information to use during your Task execution.
As explained later in this guide.
(See the context section)
Example of pre/post-deployment actions
A comprehensive example of pre-/post-deployment evaluations and tasks can be found in our examples folder, where we use Podtato-Head to run some simple pre-deployment checks. Check out the readme to learn how to test this example on your machine.
Executing sequential tasks
All KeptnTask
resources that are defined by
KeptnTaskDefinition
resources at the same level
(either pre-deployment or post-deployment) execute in parallel.
This is by design, because Keptn is not a pipeline engine.
Task sequences that are not part of the lifecycle workflow
should not be handled by Keptn
but should instead be handled by the pipeline engine tools being used
such as Jenkins, Argo Workflows, Flux, and Tekton.
If your lifecycle workflow includes
a sequence of executables that need to be run in order,
you can put them all in one KeptnTaskDefinition
resource,
which can execute a virtually unlimited number
of programs, scripts, and functions,
as long as they are all using the same runner.
You have the following options:
-
Encode all your steps in the language of your choice and build an image that Keptn executes in a
container-runtime
runner. This is often the best solution if you need to execute complex sequences because it gives you the most flexibility. -
Use the
inline
syntax for one of the Keptn pre-defined runners (eitherdeno-runtime
orpython-runtime
) to code the actual calls inline in theKeptnTaskDefinition
resource. See Fields for pre-defined containers for more information. -
Create a script that calls the functions, programs, and scripts that need to execute sequentially and install this on a remote webserver that Keptn can access. Then use the
httpRef
syntax for one of the pre-defined runners to call this script from yourKeptnTaskDefinition
, which can set parameters for the script if appropriate.
For more details about implementing these options, see the KeptnTaskDefinition page.
Context
The Keptn task context includes details about the current deployment, application name, version, object type and other user-defined metadata. Keptn populates this metadata while running tasks before and after deployments, to provide the necessary context associated with each task.
This contrasts with the Kubernetes context, which is a set of access parameters that defines the specific cluster, user and namespace with which you interact. For more information, see Configure Access to Multiple Clusters.
For Tasks generated for applications running in Kubernetes,
Keptn populates a KEPTN_CONTEXT
environment variable containing a set
of parameters that correlate a task
to a specific application/workload,
information about the phase in which the task is being executed,
as well as any metadata that has been attached to
the related application/workload
You can use this context information
in the function
code in your
KeptnTaskDefinition
resource.
KEPTN_CONTEXT
is encoded as JSON and by default, contains:
- "appName"
- "appVersion"
- "workloadName"
- "workloadVersion"
- "taskType"
- "objectType"
- "traceparent"
- "metadata"
A Job created by a KeptnTask
with KEPTN_CONTEXT
, may look like the following
apiVersion: batch/v1
kind: Job
spec:
template:
spec:
containers:
- name: "my-task-container"
env:
- name: KEPTN_CONTEXT
value: '{
"workloadName":"waiter-waiter",
"appName":"waiter",
"appVersion":"",
"workloadVersion":"0.4",
"taskType":"pre",
"objectType":"Workload",
"metadata":{
"commit-id":"1234",
"stage":"dev",
"test-metadata":"test-metadata",
"traceparent": "00-traceid-spanid-01"
}
}'
- name: SCRIPT
value: /var/data/function.ts
You can customize the metadata field to hold any key-value pair of interest to share among
your workloads and tasks in a KeptnApp
(for instance a commit ID value).
To do so, the metadata needs to be specified for the workload or for the application.
Follow our guide on Context and Metadata here.
Note
For an example of how to access the KEPTN_CONTEXT
, follow our reference page examples
for deno
and for python.
Parameterized functions
KeptnTaskDefinition
s can use input parameters.
Simple parameters are passed as a single map of key values,
while the secret
parameters refer to a single Kubernetes secret
.
Consider the following example:
apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
name: slack-notification-dev
spec:
deno:
functionRef:
name: slack-notification
parameters:
map:
textMessage: "This is my configuration"
secureParameters:
secret: slack-token
Note the following about using parameters with functions:
- Keptn passes the values
defined inside the
map
field as a JSON object. - Multi-level maps are not currently supported.
- The JSON object can be read through the environment variable
DATA
usingDeno.env.get("DATA");
. - Currently only one secret can be passed.
The secret must have a
key
calledSECURE_DATA
. It can be accessed via the environment variableDeno.env.get("SECURE_DATA")
.
Working with secrets
A special case of parameterized functions is to pass secrets that may be required to access data that your task requires.
Create secret text
To create a secret to use in a KeptnTaskDefinition
,
execute this command:
apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
name: dummy-task
namespace: "default"
spec:
deno:
secureParameters:
secret: my-secret
inline:
code: |
let secret_text = Deno.env.get("SECURE_DATA");
// secret_text = "foo"
To pass multiple variables you can create a Kubernetes secret using a JSON string:
kubectl create secret generic my-secret \
--from-literal=SECURE_DATA="{\"foo\": \"bar\", \"foo2\": \"bar2\"}"
apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
name: dummy-task
namespace: "default"
spec:
deno:
secureParameters:
secret: my-secret
inline:
code: |
let secret_text = Deno.env.get("SECURE_DATA");
let secret_text_obj = JSON.parse(secret_text);
// secret_text_obj["foo"] = "bar"
// secret_text_obj["foo2"] = "bar2"
Pass secrets to a function
Kubernetes secrets
can be passed to the function
using the secureParameters
field.
Here, the secret
value is the name of the Kubernetes secret,
which contains a field with the key SECURE_DATA
.
The value of that field is then available to the function's runtime
via an environment variable called SECURE_DATA
.
For example, if you have a task function that should make use of secret data,
you must first ensure that the secret containing the SECURE_DATA
key exists
For example:
apiVersion: v1
kind: Secret
metadata:
name: deno-demo-secret
namespace: default
type: Opaque
data:
SECURE_DATA: YmFyCg== # base64 encoded string, e.g. 'bar'
Then, you can make use of that secret as follows:
apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
name: deployment-hello
namespace: "default"
spec:
deno:
secureParameters:
secret: deno-demo-secret
inline:
code: |
console.log("Deployment Hello Task has been executed");
let foo = Deno.env.get('SECURE_DATA');
console.log(foo);
Deno.exit(0);