Working with Keptn tasks
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.
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 to integrate your task with Kubernetes
- Annotate the appropriate
KeptnApp
resource to associate your
KeptnTaskDefinition
with the pre- and post-deployment tasks that should run it; see Pre- and post-deployment tasks and checks for more information.
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 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.
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 all need the same runner, such as Python.
Another option is to encode all your steps in the language of your choice and build a container that Keptn executes. This is often the best solution if you need to execute complex sequences.
If you use either the deno-runtime
or python-runtime
runner,
you can specify the actions to take by coding the actual calls
inline in the manifest,
by calling scripts from a remote webserver,
or by calling other KeptnTaskDefinition
resources you have defined.
This provides great flexibility in
how you define your KeptnTaskDefinition
resources,
allowing you to define the ideal mix of executables that run sequentially
and executables (or sets of executables) that run in parallel.
As an example, let’s say you need to run a set of integration tests, a set of performance tests, and a set of regression tests.
-
You can create one
KeptnTaskDefinition
that calls all the tasks, in order, either by putting the actual calls in theKeptnTaskDefinition
resource (inline
syntax) or by calling scripts from a remote webserver (httpRef
syntax), or by providing a container to run. -
You can create separate
KeptnTaskDefinition
resources for integration tests, performance tests, and regression tests.-
If you annotate the
KeptnApp
resource to call each of theseKeptnTask
resources, the three sets of tests run in parallel. -
You can create a “parent”
KeptnTaskDefinition
resource that uses thefunctionref
syntax to call theKeptnTaskDefinition
resources for integration tests, performance tests, and regression tests. If you annotate theKeptnApp
resource to run this parentKeptnTask
resource, all tests run sequentially.This approach also allows you to run the test sequence if, for example, the integration tests require the
deno-runtime
runner but the performance and regression tests require thepython-runtime
runner. The parentKeptnTaskDefinition
can runKeptnTask
resources that use different runners, although the “parent” definition runtime is used for the container that runs all the tests. In other words, the parentKeptnTaskDefinition
resources is not a merge of otherKeptnTaskDefinition
resources but rather the code/container of the parent runner.
-
-
If you need to test your deployment for different platforms (such as Linux, MacOS, and Windows) or for different software versions (such as Java 11, Java 17, and Java 21), parallel testing can improve performance. In this case, you can construct different
KeptnTaskDefinition
resources for each platform, perhaps defining different input parameters (such as different secrets or environment variables). -
Define one
KeptnTaskDefinition
resource that runs integration tests, then regression test, then performance tests in order.- You could use the
functionRef
syntax and code the calling sequences for all tests inside yourKeptnTaskDefinition
resource or you could use thehttpRef
syntax to call scripts from an external webserver. - You could code separate
KeptnTaskDefinition
resources for integration tests, regression tests, and performance tests. These three test sets could then run in parallel. - You could create a
KeptnTaskDefinition
resource that uses thefunctionRef
syntax to call theKeptnTaskDefinition
resources for each type of testing. Executing that resource would execute the three types of tests in sequential order.
- You could use the
If you define a container-runtime
runner container
for your KeptnTaskDefinition
,
you can do anything allowed by the configuration you define for that container.
Context
A Kubernetes context is a set of access parameters that contains a Kubernetes cluster, a user, a namespace, the application name, workload name, and version. For more information, see Configure Access to Multiple Clusters.
You may need to include context information in the function
code
included in the YAML file that defines a
KeptnTaskDefinition
resource.
For an example of how to do this, see the
keptn-tasks.yaml
file.
A context environment variable is available via Deno.env.get("CONTEXT")
.
It can be used like this:
let context = Deno.env.get("CONTEXT");
if (context.objectType == "Application") {
let application_name = contextdata.appName;
let application_version = contextdata.appVersion;
}
if (context.objectType == "Workload") {
let application_name = contextdata.appName;
let workload_name = contextdata.workloadName;
let workload_version = contextdata.workloadVersion;
}
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/v1alpha2
kind: KeptnTaskDefinition
metadata:
name: slack-notification-dev
spec:
function:
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:
kubectl create secret generic my-secret --from-literal=SECURE_DATA=foo
apiVersion: lifecycle.keptn.sh/v1alpha3
kind: KeptnTaskDefinition
metadata:
name: dummy-task
namespace: "default"
spec:
function:
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/v1alpha3
kind: KeptnTaskDefinition
metadata:
name: dummy-task
namespace: "default"
spec:
function:
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/v1alpha3
kind: KeptnTaskDefinition
metadata:
name: deployment-hello
namespace: "default"
spec:
function:
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);