Skip to content

KeptnTaskDefinition

A KeptnTaskDefinition defines tasks that Keptn runs as part of the pre- and post-deployment phases of a KeptnApp or KeptnWorkload.

A Keptn task executes as a runner in an application container, which runs as part of a Kubernetes job.

Each KeptnTaskDefinition can use exactly one container with one runner. which is one of the following, differentiated by the spec section:

  • The container-runtime runner provides functionality to run a standard Kubernetes container inside a Kubernetes job. You define the container image, and the arbitrary application inside it. This gives you the flexibility to define tasks using the language and facilities of your choice, although it is more complicated that using one of the pre-defined runtimes. See Synopsis for container-runtime and Example for a container-runtime runner.

  • Pre-defined containers

    • Use the pre-defined deno-runtime runner to define tasks using Deno scripts, which use JavaScript with a few limitations. You can use this to specify simple actions without having to define a full container. See runtime examples
    • Use the pre-defined python-runtime runner to define your task using Python 3. See runtime examples for practical usage of the pre-defined containers.

Synopsis for all runners

The KeptnTaskDefinition Yaml files for all runners include the same lines at the top. These are described here.

apiVersion: lifecycle.keptn.sh/v?alpha?
kind: KeptnTaskDefinition
metadata:
  name: <task-name>
spec: |
  deno | python | container
  ...
  retries: <integer>
  timeout: <duration>

Fields used for all containers

  • apiVersion -- API version being used.

  • kind -- Resource type. Must be set to KeptnTaskDefinition

  • metadata

    • name -- Unique name of this task or container. This is the name used to insert this task or container into the preDeployment or postDeployment list. Names must comply with the Kubernetes Object Names and IDs specification.
  • spec

    • deno | python | container (required) -- Define the container type to use for this task. Each task can use one type of runner, identified by this field:

      • deno -- Use a deno-runtime runner and code the functionality in Deno script, which is similar to JavaScript and Typescript. See Synopsis for deno.
      • python -- Use a python-runtime function and code the functionality in Python 3. See Synopsis for python.
      • container -- Use the runner defined for the container-runtime container. This is a standard Kubernetes container for which you define the image, runner, runtime parameters, etc. and code the functionality to match the container you define. See Synopsis for container-runtime container.
    • retries -- specifies the number of times a job executing the KeptnTaskDefinition should be restarted if an attempt is unsuccessful.

    • timeout -- specifies the maximum time to wait for the task to be completed successfully. The value supplied should specify the unit of measurement; for example, 5s indicates 5 seconds and 5m indicates 5 minutes. If the task does not complete successfully within this time frame, it is considered to be failed.

Synopsis for container-runtime

Use the container-runtime to specify your own Kubernetes container and define the task you want to execute.

Task sequences that are not part of the lifecycle workflow and should be handled by the pipeline engine tools being used such as Jenkins, Argo Workflows, Flux, and Tekton.

If you are migrating from Keptn v1, you can use a container-runtime to execute almost anything that you implemented with JES for Keptn v1.

apiVersion: lifecycle.keptn.sh/v?alpha?
kind: KeptnTaskDefinition
metadata:
  name: <task-name>
spec:
  container: |
    name: <container-name>
    image: <image-name>
    <other fields>

Fields used only for container-runtime

  • spec
    • container -- Container definition.
      • name -- Name of the container that will run, which is not the same as the metadata.name field that is used in the KeptnTaskDefinition resource.
      • image -- name of the image you defined according to image reference and image concepts and pushed to a registry
      • other fields -- The full list of valid fields is available at ContainerSpec, with additional information in the Kubernetes Container spec documentation.

Synopsis for predefined containers

The predefined containers allow you to easily define a task using either Deno or Python syntax. You do not need to specify the image, volumes, and so forth. Instead, just provide either a Deno or Python script and Keptn sets up the container and runs the script as part of the task.

When using the deno-runtime runner to define a task, the executables are coded in Deno-script, (which is mostly the same as JavaScript and TypeScript) and executed in the deno-runtime runner, which is a lightweight runtime environment that executes in your namespace. Note that Deno has tighter restrictions for permissions and importing data so a script that works properly elsewhere may not function out of the box when run in the deno-runtime runner. In this case you may want to use a custom container instead.

apiVersion: lifecycle.keptn.sh/v?alpha?
kind: KeptnTaskDefinition
metadata:
  name: <task-name>
spec:
  deno: |
    inline | httpRef | functionRef | ConfigMapRef
    parameters: |
      map:
        textMessage: "This is my configuration"
    secureParameters:
      secret: <secret-name>

When using the python-runtime runner to define a task, the executables are coded in python3. The runner enables the following packages: requests, json, git, yaml.

Example
apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: python-inline
spec:
  python:
    inlineRef:
      code: |
        import sys
        import json
        import yaml
        print("Hello, World!")
        dct = yaml.safe_load('''
        name: John
        age: 30
        automobiles:
          - brand: Honda
            type: Odyssey
            year: 2018
          - brand: Toyota
            type: Sienna
            year: 2015
        ''')
        assert dct['name'] == 'John'
        assert dct['age'] == 30
        assert len(dct["automobiles"]) == 2
        assert dct["automobiles"][0]["brand"] == "Honda"
        assert dct["automobiles"][1]["year"] == 2015
        # some JSON:
        x =  '{ "name":"John", "age":30, "city":"New York"}'
        # parse x:
        y = json.loads(x)
        # the result is a Python dictionary:
        print(y["age"])

Note that other libraries may not function out of the box in the python-runtime runner. In this case you may want to use a custom container instead.

apiVersion: lifecycle.keptn.sh/v?alpha?
kind: KeptnTaskDefinition
metadata:
  name: <task-name>
spec:
  python: |
    inline | httpRef | functionRef | ConfigMapRef
    parameters: |
      map:
        textMessage: "This is my configuration"
    secureParameters:
      secret: <secret-name>

Fields for predefined containers

  • spec -- choose either deno or python
    • deno | python
      • deno -- Specify that the task uses the deno-runtime and is expressed as a Deno script. Refer to deno runtime for more information about this runner.
      • python -- Identifies this as a Python runner.

        • inline | httpRef | functionRef | ConfigMapRef -- choose the syntax used to call the executables. Only one of these can be specified per KeptnTaskDefinition resource:

          • inline -- Include the actual executable code to execute. You can code a sequence of executables here that need to be run in order as long as they are executables that are part of the lifecycle workflow. Task sequences that are not part of the lifecycle workflow should be handled by the pipeline engine tools being used such as Jenkins, Argo Workflows, Flux, and Tekton. See examples of usage for deno and for python.

          • httpRef -- Specify a script to be executed at runtime from the remote webserver that is specified. This syntax allows you to call a general function that is used in multiple places, possibly with different parameters that are provided in the calling KeptnTaskDefinition resource. Another KeptnTaskDefinition resource could call this same script but with different parameters. Only one script can be executed. Any other scripts listed here are silently ignored. See examples of usage for deno and for python.

          • functionRef -- Execute another KeptnTaskDefinition resources. Populate this field with the value(s) of the metadata.name field for each KeptnDefinitionTask to be called. Like the httpRef syntax,this is commonly used to call a general function that is used in multiple places, possibly with different parameters that are set in the calling KeptnTaskDefinition resource. To be able to run the pre-/post-deployment task, you must create the KeptnAppContext resource and link the KeptnTaskDefinition in the pre-/post-deployment section of KeptnAppContext. The KeptnTaskDefinition called with functionref is the parent task whose runner is used for the execution even if it is not the same runner defined in the calling KeptnTaskDefinition. Only one KeptnTaskDefinition resources can be listed with the functionRef syntax although that KeptnTaskDefinition can call multiple executables (programs, functions, and scripts). Any calls to additional KeptnTaskDefinition resources are silently ignored. See examples of usage for deno and python.

          • ConfigMapRef -- Specify the name of a ConfigMap resource that contains the function to be executed. See examples of usage for deno and for python.
        • parameters -- An optional field to supply input parameters to a function. Keptn passes the values defined inside the map field as a JSON object. See Parameterized functions for more information. Also see examples for deno and python.

        • secureParameters -- An optional field used to pass a Kubernetes secret. The secret value is the Kubernetes secret name that is mounted into the runtime and made available to functions using the SECURE_DATA environment variable.

          Note that, currently, only one secret can be passed per KeptnTaskDefinition resource.

          See Create secret text for details. Also see examples on secret usage in tasks runner for deno and python.

Usage

A Task executes the TaskDefinition of a KeptnApp or a KeptnWorkload. The execution is done by spawning a Kubernetes Job to handle a single Task. In its state, it tracks the current status of this Kubernetes Job.

When using a container runtime that includes a volume, an EmptyDir volume is created with the same name as is specified the container volumeMount. Note that, if more volumeMounts are specified, only one volume is created with the name of the first volumeMount. By default, the size of this volume is 1GB. If the memory limit for the container is set, the size of the volume is 50% of the memory allocated for the node.

A task can be executed either pre-deployment or post-deployment as specified in the pod template specs of your Workloads (Deployments, StatefulSets, DaemonSets, and ReplicaSets) and in the KeptnApp resource. See Annotations to KeptnApp for details. Note that the annotation identifies the task by name. This means that you can modify the function code in the resource definition and the revised code is picked up without additional changes.

All KeptnTaskDefinition resources specified to the KeptnAppContext resource at the same stage (either pre- or post-deployment) run in parallel. You can run multiple executables sequentially either by using the inline syntax for a predefined container image or by creating your own image and running it in the Keptn container-runtime runner. See Executing sequential tasks for more information.

Example for a container-runtime runner

For an example of a KeptnTaskDefinition that defines a custom container. see container-task.yaml. This is a trivial example that just runs busybox, then spawns a shell and runs the sleep 30 command:

apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: container-sleep
  namespace: podtato-kubectl
spec:
  container:
    name: testy-test
    image: busybox:1.36.1
    command:
      - 'sh'
      - '-c'
      - 'sleep 30'

This task is then referenced in the appcontext.yaml file.

Examples for deno-runtime and python-runtime runners

Inline scripts

This example defines a full-fledged Deno script within the KeptnTaskDefinition YAML file:

apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: hello-keptn-inline
spec:
  deno:
    inline:
      code: |
        let text = Deno.env.get("DATA");
        let data;
        let name;
        data = JSON.parse(text);

        name = data.name
        console.log("Hello, " + name + " new"); 

You can embed python code directly in the task definition. This example prints data stored in the parameters map:

apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: scheduled-deployment-inline
spec:
  python:
    parameters:
      map:
        mydata: "my-user-defined"
    inline:
      code: |
        # Get environment variables
        data = os.getenv('DATA')
        print(data)
HttpRef

This example fetches the Deno script from a remote webserver at runtime:

apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: hello-keptn-http
spec:
  deno:
    httpRef:
      url: "https://www.example.com/yourscript.js"

For other examples, see the sample-app. and sample-app/version-1 PodtatoHead example for a more complete example.

apiVersion: lifecycle.keptn.sh/v1alpha3
kind: KeptnTaskDefinition
metadata:
  name: hello-py
spec:
  python:
    httpRef:
      url: https://raw.githubusercontent.com/keptn/lifecycle-toolkit/main/runtimes/python-runtime/samples/hellopy.py
FunctionRef

This example calls another defined task, illustrating how one KeptnTaskDefinition can build on top of other KeptnTaskDefinitions. In this case, it calls slack-notification-dev, passing parameters and secureParameters to that other task:

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 

You can refer to an existing KeptnTaskDefinition. this example calls the inline example but overrides the data printed with what is specified in the task:

apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: scheduled-deployment-2
spec:
  python:
    parameters:
      map:
        mydata: "my-other-data"
    functionRef:
      name: scheduled-deployment-inline
ConfigMap and ConfigMapRef

This example references a ConfigMap by the name of dev-configmap that contains the code for the function to be executed.

apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: scheduled-deployment
spec:
  deno:
    configMapRef:
      name: scheduled-deployment-cm-1
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: scheduled-deployment-1
data:
  code: |
    let text = Deno.env.get("DATA");
    let data;
    if (text != "") {
        data = JSON.parse(text);
    }
    let targetDate = new Date(data.targetDate)
    let dateTime = new Date();
    if(targetDate < dateTime) {
        console.log("Date has passed - ok");
        Deno.exit(0);
    } else {
        console.log("It's too early - failing");
        Deno.exit(1);
    }
    console.log(targetDate); 

In this example the python runner refers to an existing configMap called python-test-cm

apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: scheduled-deployment
spec:
  python:
    configMapRef:
      name: python-test-cm
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: python-test-cm
data:
  code: |
    print("Hello, World!")
Accessing KEPTN_CONTEXT environment variable

For Tasks triggered as pre- and post- deployment of applications on Kubernetes, Keptn populates an environment variable called KEPTN_CONTEXT. As all environment variables, this can be accessed using language specific methods.

let context = Deno.env.get("KEPTN_CONTEXT");
console.log(context);
import os
import yaml
data = os.getenv('KEPTN_CONTEXT')
dct = yaml.safe_load(data)
meta= dct['metadata']
print(meta)
Passing secrets, environment variables and modifying the runner command

The following example shows how to pass data inside the parameter map, and how to load a secret in your code. The deno command does not takes modifiers so filling the cmdParameters will do nothing.

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  SECURE_DATA: dG9rZW46IG15dG9rZW4=
---
apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: pre-deployment-hello
  annotations: ## accessible via "KEPTN_CONTEXT"
    my: test
spec:
  deno:
    parameters: ## accessible via "DATA"
      map:
        user: "myuser"
    secureParameters: ## accessible via "SECURE_DATA"
      secret: mysecret
    inline:
      code: |
        const data = Deno.env.get("DATA")!;
        const secret = Deno.env.get("SECURE_DATA")!;
        const context = Deno.env.get("KEPTN_CONTEXT")!;
        console.log(data);
        console.log(secret);
        console.log(context);

The following example shows how to pass data inside the parameter map, how to load a secret in your code, and how to modify the python command. In this case the container runs with the -h option, which prints the help message for the python3 interpreter:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  SECURE_DATA: dG9rZW46IG15dG9rZW4=
---
apiVersion: lifecycle.keptn.sh/v1
kind: KeptnTaskDefinition
metadata:
  name: pre-deployment-hello
  annotations:
    python: test
spec:
  python:
    parameters:
      map:
        user: "myuser"
    secureParameters:
      secret: mysecret
    cmdParameters: "-h"
    inline:
      code: |
        import os
        import yaml
        data = os.getenv('DATA')
        dct = yaml.safe_load(data)
        USER= dct['user']
        PASSWORD = os.environ.get('SECURE_DATA')
        print(USER,PASSWORD)

More examples

See the lifecycle-operator/config/samples directory for more example KeptnTaskDefinition YAML files.

Files

API Reference:

Differences between versions

The KeptnTaskDefinition support for the container-runtime and python-runtime is introduced in v0.8.0. This modifies the synopsis in the following ways:

  • Add the spec.container field.
  • Add the python descriptor for the python-runtime runner.
  • Add the container descriptor for the container-runtime runner.
  • Add the deno descriptor to replace function for the deno-runtime runner. The function identifier for the deno-runtime runner is deprecated; it still works for v 0.8.0 but will be dropped from future releases.
  • The spec.function field is changed to be a pointer receiver. This aligns it with the spec.container field, which must be a pointer, and enables KeptnTask to omit it when it is empty, which it must be when spec.container is populated.

Limitations

  • Only one runtime is allowed per KeptnTaskDefinition resource.

  • Only one secret can be passed per KeptnTaskDefinition resource.

See also

Comments