Chapter 7. Sharing Data with Workspaces


Workspaces are shared volumes used to transfer data between the various steps of a task.


Types of volume sources:

  • emptyDir
  • ConfigMap
  • Secret


Using your first workspace


$ cat << 'EOF' | kubectl apply -f -
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: clone-and-list
spec:
  params:
    - name: repo
      type: string
      description: Git repository to be cloned
      default: https://github.com/joellord/handson-tekton
  workspaces:
    - name: source
  steps:
    - name: clone
      image: alpine/git
      workingDir: $(workspaces.source.path)
      command:
        - /bin/sh
      args:
        - '-c'
        - git clone -v $(params.repo) ./source
    - name: list
      image: alpine
      workingDir: $(workspaces.source.path)
      command:
        - /bin/sh
      args:
        - "-c"
        - ls ./source
EOF


$ tkn task start clone-and-list -w name=source,emptyDir="" --showlog


Using workspaces with task runs


$ vi ~/tmp/clone-and-list-tr.yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
    generateName: git-clone-tr-
spec:
    workspaces:
        - name: source
          emptyDir: {}
    taskRef:
        name: clone-and-list


$ kubectl create -f ~/tmp/clone-and-list-tr.yaml


$ tkn taskrun logs git-clone-tr-c22wd


Adding a workspace to a pipeline


$ cat << 'EOF' | kubectl apply -f -
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: clone
spec:
  params:
    - name: repo
      type: string
      description: Git repository to be cloned
      default: https://github.com/joellord/handson-tekton
  workspaces:
    - name: source
  steps:
    - name: clone
      image: alpine/git
      workingDir: $(workspaces.source.path)
      command:
        - /bin/sh
      args:
        - '-c'
        - git clone -v $(params.repo) ./source
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: list
spec:
  workspaces:
    - name: source
  steps:
    - name: list
      image: alpine
      workingDir: $(workspaces.source.path)
      command:
        - /bin/sh
      args:
        - "-c"
        - ls ./source
EOF


$ cat << 'EOF' | kubectl apply -f -
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: clone-and-list
spec:
  workspaces:
    - name: codebase
  tasks:
    - name: clone
      taskRef:
        name: clone
      workspaces:
        - name: source
          workspace: codebase
    - name: list
      taskRef:
        name: list
      workspaces:
        - name: source
          workspace: codebase
      runAfter:
        - clone
EOF


$ tkn pipeline start clone-and-list --showlog


? Name for the workspace : codebase
? Value of the Sub Path :
? Type of the Workspace : emptyDir
? Type of EmptyDir :


result:


[list : list] ls: ./source: No such file or directory

failed to get logs for task list : container step-list has failed  : [{"key":"StartedAt","value":"2021-10-02T21:09:56.952Z","type":3}]


To share the content across tasks, you will need to use a persistent volume, which you will do in the next section.


Persisting data within a pipeline


$ cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolume
metadata:
  name: tekton-pv
spec:
  storageClassName: manual
  capacity:
    storage: 2Gi
  accessModes:
    - ReadWriteMany
    - ReadWriteOnce
    - ReadOnlyMany
  hostPath:
    path: "/mnt/data"
EOF


$ cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: tekton-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
EOF


$ tkn pipeline start clone-and-list \
  -w name=codebase,claimName=tekton-pvc \
  --showlog


Если выполнить второй раз - будет ошибка, т.к. данные из pvc не были удалены.


Cleaning up with finally


$ cat << 'EOF' | kubectl apply -f -
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: cleanup
spec:
  workspaces:
    - name: source
  steps:
    - name: remove-source
      image: registry.access.redhat.com/ubi8/ubi
      command:
        - /bin/bash
      args:
        - "-c"
        - "rm -rf $(workspaces.source.path)/source"
    - name: message
      image: registry.access.redhat.com/ubi8/ubi
      command:
        - /bin/bash
      args:
        - "-c"
        - echo All files were deleted
EOF


$ cat << 'EOF' | kubectl apply -f -
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: clone-and-list
spec:
  workspaces:
    - name: codebase
  tasks:
    - name: clone
      taskRef:
        name: clone
      workspaces:
        - name: source
          workspace: codebase
    - name: list
      taskRef:
        name: list
      workspaces:
        - name: source
          workspace: codebase
      runAfter:
        - clone
  finally:
    - name: clean
      taskRef:
        name: cleanup
      workspaces:
        - name: source
          workspace: codebase
EOF


$ tkn pipeline start clone-and-list \
  -w name=codebase,claimName=tekton-pvc \
  --showlog


Using workspaces in pipeline runs


$ vi ~/tmp/pipelinerun.yaml


apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
    generateName: clone-and-ls-pr-
spec:
    pipelineRef:
        name: clone-and-list
    workspaces:
        - name: codebase
          persistentVolumeClaim:
              claimName: tekton-pvc


$ kubectl create -f ~/tmp/pipelinerun.yaml


$ tkn pr logs clone-and-ls-pr-hsqfz -f


Using volume claim templates

Instead of specifying a persistent volume claim directly, you can also ask Tekton to create a temporary one for you. This can be useful when you don’t need to persist data outside of your pipelines.


$ vi ~/tmp/pvc-template.yaml


apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
    generateName: clone-and-ls-pr-
spec:
    pipelineSpec:
        workspaces:
            - name: codebase
        tasks:
            - name: clone
              taskRef:
                  name: clone
              workspaces:
                  - name: source
                    workspace: codebase
            - name: list
              taskRef:
                  name: list
              workspaces:
                  - name: source
                    workspace: codebase
              runAfter:
                  - clone
    workspaces:
        - name: codebase
          volumeClaimTemplate:
              spec:
                  accessModes:
                      - ReadWriteOnce
                  resources:
                      requests:
                          storage: 1Gi


$ kubectl create -f ~/tmp/pvc-template.yaml


$ tkn pr logs -f clone-and-ls-pr-xwsnd


This volume claim template creates a new PVC for each pipeline execution.


Assessments


Write and read


Create a task that uses a workspace to share information across its two steps. The first step will write a message, specified in a parameter, to a file in the workspace. The second step will output the content of the file in the logs.


$ cat << 'EOF' | kubectl apply -f -
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: write-read-workspace
spec:
  workspaces:
    - name: data
  params:
    - name: message
      default: "Hello World"
      type: string
      description: "Message to write in the workspace"
  steps:
    - name: write
      image: registry.access.redhat.com/ubi8/ubi
      command:
        - /bin/bash
      args:
        - -c
        - echo "$(params.message)" > $(workspaces.data.path)/message.txt
    - name: read
      image: registry.access.redhat.com/ubi8/ubi
      command:
        - /bin/bash
      args:
        - -c
        - cat $(workspaces.data.path)/message.txt
EOF


$ tkn task start write-read-workspace --showlog
? Value for param `message` of type `string`? (Default is `Hello World`) Hello World
Please give specifications for the workspace: data
? Name for the workspace : data
? Value of the Sub Path :
? Type of the Workspace : emptyDir
? Type of EmptyDir :
TaskRun started: write-read-workspace-run-sg2b5
Waiting for logs to be available...

[read] Hello World


Pick a card

Using the Deck of Cards API available at http://deckofcardsapi.com/, create a pipeline that will generate a new deck of cards and then pick a single card from it. The first call will generate a deck identifier (ID) that you can then use in the next task to pick a card. Output the card value and suit in the second task.


$ cat << 'EOF' | kubectl apply -f -
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: deck-api-create
spec:
  workspaces:
    - name: deck
  steps:
    - name: create-deck
      image: registry.access.redhat.com/ubi8/ubi
      script: |
        curl https://deckofcardsapi.com/api/deck/new/shuffle/ -o $(workspaces.deck.path)/deck-id.txt
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: deck-api-draw
spec:
  workspaces:
    - name: deck
  steps:
    - name: draw
      image: node:14
      script: |
        #!/usr/bin/env node
        const fs = require("fs");
        const https = require("https");
        const deck = fs.readFileSync("$(workspaces.deck.path)/deck-id.txt");
        const deckId = JSON.parse(deck).deck_id;
        const URL = `https://deckofcardsapi.com/api/deck/${deckId}/draw/`;
        console.log(URL);
        https.get(URL, response => {
          response.on("data", data => {
            let card = JSON.parse(data).cards[0];
            console.log("Card was drawn from the deck");
            console.log(`${card.value} of ${card.suit}`);
          })
        });
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: pick-a-card
spec:
  workspaces:
    - name: api-data
  tasks:
    - name: create-deck
      taskRef:
        name: deck-api-create
      workspaces:
        - name: deck
          workspace: api-data
    - name: pick-card
      taskRef:
        name: deck-api-draw
      workspaces:
        - name: deck
          workspace: api-data
      runAfter:
        - create-deck
EOF


$ tkn pipeline start pick-a-card --showlog
Please give specifications for the workspace: api-data
? Name for the workspace : api-data
? Value of the Sub Path :
? Type of the Workspace : emptyDir
? Type of EmptyDir :
PipelineRun started: pick-a-card-run-5kmmq
Waiting for logs to be available...
[create-deck : create-deck] + curl https://deckofcardsapi.com/api/deck/new/shuffle/ -o /workspace/deck/deck-id.txt
[create-deck : create-deck]   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
[create-deck : create-deck]                                  Dload  Upload   Total   Spent    Left  Speed
100    79  100    79    0     0    292      0 --:--:-- --:--:-- --:--:--   292

[pick-card : draw] internal/fs/utils.js:332
[pick-card : draw]     throw err;
[pick-card : draw]     ^
[pick-card : draw]
[pick-card : draw] Error: ENOENT: no such file or directory, open '/workspace/deck/deck-id.txt'
[pick-card : draw]     at Object.openSync (fs.js:497:3)
[pick-card : draw]     at Object.readFileSync (fs.js:393:35)
[pick-card : draw]     at Object.<anonymous> (/tekton/scripts/script-0-mg6rh:4:17)
[pick-card : draw]     at Module._compile (internal/modules/cjs/loader.js:1085:14)
[pick-card : draw]     at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
[pick-card : draw]     at Module.load (internal/modules/cjs/loader.js:950:32)
[pick-card : draw]     at Function.Module._load (internal/modules/cjs/loader.js:790:12)
[pick-card : draw]     at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12)
[pick-card : draw]     at internal/main/run_main_module.js:17:47 {
[pick-card : draw]   errno: -2,
[pick-card : draw]   syscall: 'open',
[pick-card : draw]   code: 'ENOENT',
[pick-card : draw]   path: '/workspace/deck/deck-id.txt'
[pick-card : draw] }

failed to get logs for task pick-card : container step-draw has failed  : [{"key":"StartedAt","value":"2021-10-24T12:47:27.107Z","type":3}]


$ tkn pr logs -f pick-a-card-run-5kmmq


Hello admin

Build a pipeline that will return a different greeting, whether the username passed as a parameter is admin or something else. This pipeline should have two tasks. The first task will verify the username and output the role ( admin or user ) in the result. The second task will pick up this role and display the appropriate message from a ConfigMap mounted as a workspace.


$ cat << 'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
  name: messages
data:
  admin-welcome: Welcome master.
  user-welcome: Hello user.
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: get-role
spec:
  results:
    - name: role
  params:
    - name: user
      type: string
  steps:
    - name: check-username
      image: registry.access.redhat.com/ubi8/ubi
      script: |
        #!/usr/bin/env bash
        if [ "$(params.user)" == "admin" ]; then
          echo "admin" > $(results.role.path)
        else
          echo "user" > $(results.role.path)
        fi
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: role-based-greet
spec:
  params:
    - name: role
      type: string
  workspaces:
    - name: messages
  steps:
    - name: greet
      image: registry.access.redhat.com/ubi8/ubi
      script: |
        ROLE=$(params.role)
        cat $(workspaces.messages.path)/$ROLE-welcome
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: admin-or-not
spec:
  params:
    - name: username
      default: user
      type: string
  workspaces:
    - name: message-map
  tasks:
    - name: validate-admin
      taskRef:
        name: get-role
      params:
        - name: user
          value: $(params.username)
    - name: greetings
      taskRef:
        name: role-based-greet
      params:
        - name: role
          value: $(tasks.validate-admin.results.role)
      workspaces:
        - name: messages
          workspace: message-map
      runAfter:
        - validate-admin
EOF


$ tkn pipeline start admin-or-not --showlog
? Value for param `username` of type `string`? (Default is `user`) user
Please give specifications for the workspace: message-map
? Name for the workspace : message-map
? Value of the Sub Path :
? Type of the Workspace : emptyDir
? Type of EmptyDir :
PipelineRun started: admin-or-not-run-rcjzn
Waiting for logs to be available...

[greetings : greet] + ROLE=user
[greetings : greet] + cat /workspace/messages/user-welcome
[greetings : greet] cat: /workspace/messages/user-welcome: No such file or directory

failed to get logs for task greetings : container step-greet has failed  : [{"key":"StartedAt","value":"2021-10-24T12:58:28.817Z","type":3}]