├── examples ├── basics │ ├── .gitignore │ ├── test │ │ ├── .gitignore │ │ ├── set.simple.file │ │ ├── handle-event.respond.sh │ │ ├── handle-event.trigger-pipeline.sh │ │ ├── handle-event.disable.sh │ │ ├── handle-event.set-values.sh │ │ ├── load.sh │ │ ├── handle-event.manual-choice.sh │ │ ├── handle-event.notify.sh │ │ ├── test_basics.sh │ │ ├── init-new.sh │ │ ├── generate.sh │ │ ├── get.sh │ │ └── set.sh │ ├── set.simple.file │ ├── localdata │ │ └── cicdContextData.id │ ├── set.simple.json │ ├── set.simple.yaml │ ├── requirements.txt │ ├── secrets.yaml │ ├── set.manual-choice-gen-data.json │ ├── bases │ │ └── base1.yaml │ ├── README.md │ ├── LOAD.md │ ├── GENERATE.md │ ├── GET.md │ ├── SET.md │ ├── app.yaml │ └── config.yaml ├── tekton │ ├── core │ │ ├── .gitignore │ │ ├── tekton-service-accounts.yaml │ │ ├── dashboard-ingress.yaml │ │ ├── redis.yaml │ │ ├── registry-aliases-config.yaml │ │ ├── image-registry-cache.yaml │ │ ├── trivy-server.yaml │ │ ├── tekton-dashboard-mods.yaml │ │ ├── slack-payload-handler.yaml │ │ ├── tekton-pipelines-mods.yaml │ │ ├── tekton-triggers-rbac.yaml │ │ └── install.sh │ ├── img │ │ ├── pattern.png │ │ └── tekton.png │ ├── pipelines │ │ ├── confs │ │ │ ├── cicdstatemgr-secrets.yaml │ │ │ └── cicdstatemgr-config.yaml │ │ ├── README.md │ │ ├── common │ │ │ ├── README.md │ │ │ ├── conditions │ │ │ │ └── conditions.yaml │ │ │ └── tasks │ │ │ │ ├── handle-event.yaml │ │ │ │ └── init.yaml │ │ ├── install.sh │ │ ├── install-confs.sh │ │ ├── test │ │ │ ├── tasks │ │ │ │ └── test-invoke.yaml │ │ │ ├── triggers.yaml │ │ │ └── pipeline.yaml │ │ ├── build │ │ │ ├── tasks │ │ │ │ └── build.yaml │ │ │ ├── triggers.yaml │ │ │ └── pipeline.yaml │ │ ├── validate │ │ │ ├── triggers.yaml │ │ │ ├── tasks │ │ │ │ └── trivy-scan.yaml │ │ │ └── pipeline.yaml │ │ ├── deploy │ │ │ ├── tasks │ │ │ │ └── deploy.yaml │ │ │ ├── triggers.yaml │ │ │ └── pipeline.yaml │ │ └── start │ │ │ ├── pipeline.yaml │ │ │ ├── triggers.yaml │ │ │ └── tasks │ │ │ └── start.yaml │ ├── .gitignore │ ├── remove.sh │ └── install.sh ├── simple │ ├── secrets.yaml │ ├── app.yaml │ └── config.yaml └── README.md ├── tests └── test_cicdstatemgr.py ├── requirements.txt ├── imgs ├── init-new.png ├── overview.png └── slashcmd.png ├── cicdstatemgr ├── __main__.py ├── __init__.py ├── datasources │ ├── idfile.py │ ├── jsonfile.py │ ├── yamlfile.py │ ├── redis.py │ ├── shellfile.py │ └── __init__.py ├── jinja2util.py └── utils.py ├── NOTES.md ├── hooks └── build ├── Dockerfile ├── setup.py ├── LICENSE ├── .gitignore ├── CHANGELOG.md └── .travis.yml /examples/basics/.gitignore: -------------------------------------------------------------------------------- 1 | venv -------------------------------------------------------------------------------- /examples/basics/test/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/basics/set.simple.file: -------------------------------------------------------------------------------- 1 | simple body contents 2 | -------------------------------------------------------------------------------- /examples/basics/localdata/cicdContextData.id: -------------------------------------------------------------------------------- 1 | context-data-id-1 -------------------------------------------------------------------------------- /examples/basics/test/set.simple.file: -------------------------------------------------------------------------------- 1 | simple body contents 2 | -------------------------------------------------------------------------------- /examples/tekton/core/.gitignore: -------------------------------------------------------------------------------- 1 | minikube-helpers 2 | secrets/* -------------------------------------------------------------------------------- /tests/test_cicdstatemgr.py: -------------------------------------------------------------------------------- 1 | def test_dummy(): 2 | assert True -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | jinja2 2 | pyyaml 3 | redis 4 | jsonpath-ng 5 | requests -------------------------------------------------------------------------------- /imgs/init-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitsofinfo/cicdstatemgr/HEAD/imgs/init-new.png -------------------------------------------------------------------------------- /imgs/overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitsofinfo/cicdstatemgr/HEAD/imgs/overview.png -------------------------------------------------------------------------------- /imgs/slashcmd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitsofinfo/cicdstatemgr/HEAD/imgs/slashcmd.png -------------------------------------------------------------------------------- /examples/basics/set.simple.json: -------------------------------------------------------------------------------- 1 | {"dog":"beagle", "bark":{"quality":"high","volume":"loud"}} 2 | -------------------------------------------------------------------------------- /examples/basics/set.simple.yaml: -------------------------------------------------------------------------------- 1 | dog: beagle 2 | bark: 3 | quality: high 4 | volume: loud 5 | -------------------------------------------------------------------------------- /examples/tekton/img/pattern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitsofinfo/cicdstatemgr/HEAD/examples/tekton/img/pattern.png -------------------------------------------------------------------------------- /examples/tekton/img/tekton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitsofinfo/cicdstatemgr/HEAD/examples/tekton/img/tekton.png -------------------------------------------------------------------------------- /cicdstatemgr/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | from .cli import main 8 | 9 | main() -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | # notes 2 | 3 | ``` 4 | python3 -m pip install --user --upgrade twine 5 | python3 setup.py sdist bdist_wheel 6 | python3 -m twine upload dist/* 7 | ``` -------------------------------------------------------------------------------- /examples/basics/requirements.txt: -------------------------------------------------------------------------------- 1 | # for local dev 2 | # source venv/bin/activate 3 | # pip install --requirement requirements.txt 4 | ../../../cicdstatemgr 5 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/confs/cicdstatemgr-secrets.yaml: -------------------------------------------------------------------------------- 1 | cicdstatemgr: 2 | slack: 3 | token: "@SLACK_BEARER_TOKEN@" 4 | trigger: 5 | token: "3ecret1!" 6 | 7 | -------------------------------------------------------------------------------- /examples/tekton/.gitignore: -------------------------------------------------------------------------------- 1 | catalog 2 | ngrok 3 | minikube-helpers 4 | *.tmp 5 | 6 | **/secrets/* 7 | 8 | .ngrok.pid 9 | 10 | 11 | 12 | .DS_Store 13 | 14 | !build 15 | -------------------------------------------------------------------------------- /examples/simple/secrets.yaml: -------------------------------------------------------------------------------- 1 | # See: examples/basics for a more complete example 2 | # this is an ultra minimalistic example 3 | cicdstatemgr: 4 | slack: 5 | token: "FAKE_TOKEN" 6 | 7 | -------------------------------------------------------------------------------- /examples/tekton/core/tekton-service-accounts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: v1 4 | kind: ServiceAccount 5 | metadata: 6 | name: cicd-tekton 7 | #secrets: 8 | # - name: cicd-secrets 9 | 10 | --- -------------------------------------------------------------------------------- /examples/tekton/remove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | NGROK_PID=$(cat .ngrok.pid) 5 | 6 | echo "Killing (-TERM) NGROK at pid: $NGROK_PID" 7 | kill -TERM `cat .ngrok.pid` 8 | 9 | echo "Issuing minikube delete" 10 | minikube delete -------------------------------------------------------------------------------- /hooks/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # https://github.com/docker/hub-feedback/issues/508#issuecomment-222520720 3 | 4 | GIT_TAG=$DOCKER_TAG 5 | 6 | if [ "$GIT_TAG" = "latest" ]; then 7 | GIT_TAG=master 8 | fi 9 | 10 | docker build --build-arg GIT_TAG=$GIT_TAG -t $IMAGE_NAME . 11 | -------------------------------------------------------------------------------- /examples/basics/secrets.yaml: -------------------------------------------------------------------------------- 1 | cicdstatemgr: 2 | 3 | datasources: 4 | redis: 5 | username: cicdstatemgr 6 | password: "123$aBcZ" 7 | 8 | someSuperSecret: mySecretValue 9 | 10 | slack: 11 | token: "FAKE_TOKEN" 12 | trigger: 13 | token: "3ecret1!" 14 | 15 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/README.md: -------------------------------------------------------------------------------- 1 | # pipelines 2 | 3 | Within this directory contains the manifests for the underlying Tekton pipelines. All of the flows that are orchestrated by these pipelines in conjunction with `cicdstatemgr` follow a general pattern that looks like this: 4 | 5 | ![Diagram of example](../img/pattern.png "Diagram1") -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # examples 2 | 3 | There are two sets of examples for `cicdstatemgr` 4 | 5 | The first, located under [basics/](basics) covers using the CLI and all of its options 6 | 7 | The second is a more real world example located under [tekton/](tekton) that demonstrates using `cicdstatemgr` in a CICD system that uses [Tekton Pipelines](https://tekton.dev/). -------------------------------------------------------------------------------- /examples/basics/set.manual-choice-gen-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "item1": { 3 | "name": "item-one", 4 | "description": "item1 desc" 5 | }, 6 | "item2": { 7 | "name": "item-two", 8 | "description": "item2 desc" 9 | }, 10 | "item3": { 11 | "name": "item-three", 12 | "description": "item3 desc" 13 | } 14 | } -------------------------------------------------------------------------------- /examples/basics/test/handle-event.respond.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # HANDLE_EVENT.md 5 | # respond 6 | #----------------- 7 | 8 | cicdstatemgr \ 9 | --config config.yaml \ 10 | --secrets secrets.yaml \ 11 | --id "context-data-id-1" \ 12 | --handle-event build=testRespondEvent 13 | 14 | # really nothing to validate here 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/simple/app.yaml: -------------------------------------------------------------------------------- 1 | # See: examples/basics for a more complete example 2 | # this is an ultra minimalistic example 3 | jinja2-macros: 4 | echo: | 5 | {%- macro echo(msg) -%} 6 | hello {{msg}} 7 | {%- endmacro -%} 8 | cicd-contexts: 9 | stage: 10 | pipelines: 11 | build: 12 | event-handlers: 13 | testEvent: 14 | notify: 15 | message: "Basic message {{state.key1}}" -------------------------------------------------------------------------------- /examples/basics/test/handle-event.trigger-pipeline.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # HANDLE_EVENT.md 5 | # trigger-pipeline 6 | #----------------- 7 | 8 | cicdstatemgr \ 9 | --config config.yaml \ 10 | --secrets secrets.yaml \ 11 | --id "context-data-id-1" \ 12 | --set state.triggerAutoArg1=dummyVal \ 13 | --handle-event build=testTriggerPipelineEvent 14 | 15 | # really nothing to validate here 16 | 17 | -------------------------------------------------------------------------------- /examples/simple/config.yaml: -------------------------------------------------------------------------------- 1 | # See: examples/basics for a more complete example 2 | # this is an ultra minimalistic example 3 | cicdstatemgr: 4 | datasources: 5 | yamlfile: 6 | path: localdata/cicdContextData.yaml 7 | isPrimary: true 8 | jsonfile: 9 | path: localdata/cicdContextData.json 10 | 11 | slack: 12 | url: https://postman-echo.com/post 13 | 14 | templates: 15 | notify: > 16 | {"message": "{{notify.message}} {{echo('world')}}" 17 | -------------------------------------------------------------------------------- /examples/tekton/core/dashboard-ingress.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | apiVersion: extensions/v1beta1 5 | kind: Ingress 6 | metadata: 7 | name: tekton-dashboard 8 | namespace: tekton-pipelines 9 | labels: 10 | bitsofinfo-ingress: "yes" 11 | annotations: 12 | kubernetes.io/ingress.class: traefik 13 | spec: 14 | rules: 15 | - host: cicdstatemgr-tekton.local 16 | http: 17 | paths: 18 | - backend: 19 | serviceName: tekton-dashboard 20 | servicePort: 9097 -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM bitsofinfo/cicd-toolbox:3.0.5 2 | 3 | ARG GIT_TAG=master 4 | 5 | RUN echo GIT_TAG=${GIT_TAG} 6 | 7 | RUN pip3 -V 8 | 9 | RUN found=1; while [ $found -eq 1 ]; do sleep 5; x=$(curl -s https://pypi.org/simple/cicdstatemgr/ 2>&1 | grep $GIT_TAG); found=$?; echo "found? $found"; done 10 | 11 | RUN curl -s https://pypi.org/simple/cicdstatemgr/ 2>&1 12 | 13 | RUN pip3 --no-cache-dir -vvv install cicdstatemgr==$GIT_TAG 14 | 15 | RUN pip3 show cicdstatemgr 16 | 17 | RUN cicdstatemgr -h 18 | 19 | -------------------------------------------------------------------------------- /examples/tekton/core/redis.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: v1 4 | kind: Pod 5 | metadata: 6 | name: cicd-redis-server 7 | labels: 8 | app: cicd-redis-server 9 | spec: 10 | containers: 11 | - image: redis:latest 12 | name: cicd-redis-server 13 | 14 | --- 15 | 16 | apiVersion: v1 17 | kind: Service 18 | metadata: 19 | name: cicd-redis-server 20 | spec: 21 | type: NodePort 22 | selector: 23 | app: cicd-redis-server 24 | ports: 25 | - protocol: TCP 26 | port: 6379 27 | targetPort: 6379 28 | nodePort: 32444 -------------------------------------------------------------------------------- /examples/tekton/pipelines/common/README.md: -------------------------------------------------------------------------------- 1 | # common 2 | 3 | This folder contains shared `common` `Tasks` and `Conditions` 4 | 5 | ## tasks/handle-event.yaml 6 | 7 | Called by various pipelines, amongst other params, takes a string `eventName` and a `setContextDataValues` list of `state` values to set prior to firing the named event. 8 | 9 | ## conditions/conditions.yaml 10 | 11 | Defines various Tekton `Condition` tasks that are used for evaluating exit-codes or arbitrary inputs. These are used by most `Pipelines` for evaluating success/failures etc. 12 | -------------------------------------------------------------------------------- /examples/tekton/core/registry-aliases-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: registry-aliases 5 | namespace: kube-system 6 | labels: 7 | kubernetes.io/minikube-addons: registry-aliases 8 | addonmanager.kubernetes.io/mode: Reconcile 9 | data: 10 | # Add additonal hosts seperated by new-line 11 | registryAliases: >- 12 | registry.kube-system.svc.cluster.local 13 | # default registry address in minikube when enabled via minikube addons enable registry 14 | registrySvc: registry.kube-system.svc.cluster.local 15 | 16 | -------------------------------------------------------------------------------- /examples/tekton/core/image-registry-cache.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: v1 4 | kind: Pod 5 | metadata: 6 | name: cicd-cache-registry 7 | labels: 8 | app: cicd-cache-registry 9 | spec: 10 | containers: 11 | - image: registry:2.7.1 12 | name: cicd-cache-registry 13 | env: 14 | - name: "REGISTRY_HTTP_ADDR" 15 | value: "0.0.0.0:80" 16 | --- 17 | 18 | apiVersion: v1 19 | kind: Service 20 | metadata: 21 | name: cicd-cache-registry 22 | spec: 23 | type: ClusterIP 24 | selector: 25 | app: cicd-cache-registry 26 | ports: 27 | - protocol: TCP 28 | port: 80 29 | targetPort: 80 -------------------------------------------------------------------------------- /examples/basics/test/handle-event.disable.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # HANDLE_EVENT.md 5 | # disable/enable 6 | #----------------- 7 | 8 | OUTPUT=$(cicdstatemgr \ 9 | --config config.yaml \ 10 | --secrets secrets.yaml \ 11 | --id "context-data-id-1" \ 12 | --handle-event test=another-event 2>&1) 13 | echo "$OUTPUT" 14 | if [[ "$OUTPUT" != *"notify enabled:False skipping"* ]]; then 15 | echo 16 | echo "FAIL: HANDLE_EVENT [enabled test]: output != ... notify enabled:False skipping ..." 17 | echo 18 | exit 1 19 | else 20 | echo 21 | echo "OK: HANDLE_EVENT [enabled test]: success" 22 | echo 23 | fi 24 | 25 | -------------------------------------------------------------------------------- /examples/tekton/core/trivy-server.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: v1 4 | kind: Pod 5 | metadata: 6 | name: cicd-trivy-server 7 | labels: 8 | app: cicd-trivy-server 9 | spec: 10 | containers: 11 | - image: docker.io/aquasec/trivy:0.19.2 12 | name: cicd-trivy-server 13 | command: 14 | - /bin/ash 15 | args: 16 | - -c 17 | - | 18 | /usr/local/bin/trivy \ 19 | server --listen 0.0.0.0:8080 20 | 21 | --- 22 | 23 | apiVersion: v1 24 | kind: Service 25 | metadata: 26 | name: cicd-trivy-server 27 | spec: 28 | type: ClusterIP 29 | selector: 30 | app: cicd-trivy-server 31 | ports: 32 | - protocol: TCP 33 | port: 80 34 | targetPort: 8080 35 | -------------------------------------------------------------------------------- /cicdstatemgr/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | CONFIG_DATA_KEY = "cicdstatemgr" 8 | SECRET_DATA_KEY = "cicdstatemgr" 9 | STATE = 'state' 10 | CONFIG_DATA = 'configData' 11 | SECRET_DATA = 'secretData' 12 | CICD_CONTEXT_DATA_ID = 'cicdContextDataId' 13 | CICD_CONTEXT_NAME = 'cicdContextName' 14 | 15 | class CicdContextData(): 16 | cicdContextData:dict 17 | 18 | def __init__(self,cicdContextData:dict): 19 | self.cicdContextData = cicdContextData 20 | 21 | 22 | def getCicdContextDataId(self) -> str: 23 | return self.cicdContextData[STATE][CICD_CONTEXT_DATA_ID] 24 | 25 | 26 | def getCicdContextData(self) -> str: 27 | return self.cicdContextData -------------------------------------------------------------------------------- /examples/basics/test/handle-event.set-values.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # HANDLE_EVENT.md 5 | # set-values 6 | #----------------- 7 | 8 | cicdstatemgr \ 9 | --config config.yaml \ 10 | --secrets secrets.yaml \ 11 | --id "context-data-id-1" \ 12 | --handle-event build=testSetValuesEvent 13 | 14 | VALUE=$(cat localdata/cicdContextData.yaml | yq r - state.lastPostedNotifyMessage) 15 | if [ "$VALUE" != "This is basicMacro! msg = build is successful" ]; then 16 | echo 17 | echo "FAIL: HANDLE_EVENT [set-values]: state.lastPostedNotifyMessage != This is basicMacro! msg = build is successful" 18 | echo 19 | exit 1 20 | else 21 | echo 22 | echo "OK: HANDLE_EVENT [set-values]: state.lastPostedNotifyMessage success" 23 | echo 24 | fi 25 | -------------------------------------------------------------------------------- /examples/tekton/core/tekton-dashboard-mods.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | labels: 6 | app: tekton-dashboard 7 | app.kubernetes.io/component: dashboard 8 | app.kubernetes.io/instance: default 9 | app.kubernetes.io/name: dashboard 10 | app.kubernetes.io/part-of: tekton-dashboard 11 | app.kubernetes.io/version: v0.8.0 12 | dashboard.tekton.dev/release: v0.8.0 13 | version: v0.8.0 14 | name: tekton-dashboard 15 | namespace: tekton-pipelines 16 | spec: 17 | type: NodePort 18 | ports: 19 | - name: http 20 | port: 9097 21 | protocol: TCP 22 | targetPort: 9097 23 | nodePort: 32222 24 | selector: 25 | app.kubernetes.io/component: dashboard 26 | app.kubernetes.io/instance: default 27 | app.kubernetes.io/name: dashboard 28 | app.kubernetes.io/part-of: tekton-dashboard -------------------------------------------------------------------------------- /examples/basics/bases/base1.yaml: -------------------------------------------------------------------------------- 1 | 2 | jinja2-macros: 3 | 4 | basicMacro: | 5 | {%- macro basicMacro(msg) -%} 6 | This is basicMacro! msg = {{msg}} 7 | {%- endmacro -%} 8 | 9 | echo: | 10 | {%- macro echo(msg) -%} 11 | {{msg}} 12 | {%- endmacro -%} 13 | 14 | random: | 15 | {%- macro random() -%} 16 | {{ range(10000, 99999) | random }} 17 | {%- endmacro -%} 18 | 19 | 20 | variables: 21 | baseVar1: "baseVarVal1" 22 | 23 | cicd-contexts: 24 | 25 | stage: 26 | channel: stage 27 | 28 | pipelines: 29 | start: 30 | event-handlers: 31 | 32 | some-event: 33 | notify: 34 | message: | 35 | {{ basicMacro('some-event fired in the start pipeline') }} 36 | 37 | test: 38 | event-handlers: 39 | another-event: 40 | notify: 41 | message: | 42 | {{ basicMacro('another-event fired in the test pipeline') }} 43 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="cicdstatemgr", 8 | version="TRAVISTAGHERE", 9 | author="bitsofinfo", 10 | author_email="bitsofinfo.g@gmail.com", 11 | description="State management utility for CICD systems", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/bitsofinfo/cicdstatemgr", 15 | packages=setuptools.find_packages(), 16 | classifiers=[ 17 | "Programming Language :: Python :: 3", 18 | "License :: OSI Approved :: MIT License", 19 | "Operating System :: OS Independent", 20 | ], 21 | install_requires=[ 22 | 'jinja2', 23 | 'pyyaml', 24 | 'redis ', 25 | 'jsonpath-ng', 26 | 'requests' 27 | ], 28 | entry_points = { 29 | "console_scripts": ['cicdstatemgr = cicdstatemgr.cli:main'] 30 | }, 31 | python_requires='>=3.8', 32 | ) -------------------------------------------------------------------------------- /examples/tekton/core/slack-payload-handler.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: slack-payload-handler 5 | namespace: tekton-pipelines 6 | spec: 7 | replicas: 1 8 | selector: 9 | matchLabels: 10 | app: slack-payload-handler 11 | template: 12 | metadata: 13 | labels: 14 | app: slack-payload-handler 15 | spec: 16 | serviceAccountName: default 17 | containers: 18 | - name: slack-payload-handler 19 | image: bitsofinfo/slack-payload-handler:1.5.2 20 | command: 21 | - "slack-payload-handler" 22 | args: 23 | - "--debug-request=true" 24 | - "--debug-response=true" 25 | - "--listen-port" 26 | - "8080" 27 | 28 | --- 29 | apiVersion: v1 30 | kind: Service 31 | metadata: 32 | name: slack-payload-handler 33 | namespace: tekton-pipelines 34 | spec: 35 | type: NodePort 36 | selector: 37 | app: slack-payload-handler 38 | ports: 39 | - protocol: TCP 40 | port: 80 41 | targetPort: 8080 42 | nodePort: 32333 43 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 4 | 5 | kubectl delete secret github-secret 6 | kubectl create secret generic github-secret --from-literal=secretToken=123 7 | 8 | kubectl apply -f triggers.event-listener.yaml 9 | 10 | kubectl apply -f common/tasks/handle-event.yaml 11 | kubectl apply -f common/tasks/init.yaml 12 | kubectl apply -f common/conditions/conditions.yaml 13 | 14 | kubectl apply -f start/tasks/start.yaml 15 | kubectl apply -f start/pipeline.yaml 16 | kubectl apply -f start/triggers.yaml 17 | 18 | kubectl apply -f build/tasks/build.yaml 19 | kubectl apply -f build/pipeline.yaml 20 | kubectl apply -f build/triggers.yaml 21 | 22 | kubectl apply -f deploy/tasks/deploy.yaml 23 | kubectl apply -f deploy/pipeline.yaml 24 | kubectl apply -f deploy/triggers.yaml 25 | 26 | kubectl apply -f validate/tasks/trivy-scan.yaml 27 | kubectl apply -f validate/pipeline.yaml 28 | kubectl apply -f validate/triggers.yaml 29 | 30 | kubectl apply -f test/tasks/test-invoke.yaml 31 | kubectl apply -f test/pipeline.yaml 32 | kubectl apply -f test/triggers.yaml 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 bitsofinfo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /cicdstatemgr/datasources/idfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | import yaml 8 | import os 9 | import logging 10 | 11 | from . import CicdContextDataSource 12 | 13 | from ..utils import get_file_path 14 | 15 | class DataSource(CicdContextDataSource): 16 | filePath = None 17 | isPrimary = False 18 | 19 | def __init__(self, config=dict): 20 | self.filePath = config['path'] 21 | 22 | if 'isPrimary' in config: 23 | self.isPrimary = config['isPrimary'] 24 | 25 | self.filePath = get_file_path(self.filePath) 26 | 27 | logging.debug("{} -> {} isPrimary:{} isLocal:{}".format(self.get_name(),self.filePath,self.isPrimary,self.is_local())) 28 | 29 | def is_local(self) -> bool: 30 | return True 31 | 32 | def supports_load(self) -> bool: 33 | return False 34 | 35 | def get_name(self) -> str: 36 | return 'idfile' 37 | 38 | def is_primary(self) -> bool: 39 | return self.isPrimary 40 | 41 | def persist(self, cicdContextDataId:str, cicdContextData:object): 42 | os.makedirs(os.path.dirname(self.filePath),exist_ok=True) 43 | with open(self.filePath, 'w') as f: 44 | f.write(cicdContextDataId) 45 | 46 | def load(self, cicdContextDataId:str) -> dict: 47 | raise Exception("idfile does not support load()") 48 | -------------------------------------------------------------------------------- /examples/basics/test/load.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # LOAD.md 5 | #----------------- 6 | SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 7 | 8 | rm -rf $SCRIPTPATH/localdata/* 9 | 10 | cicdstatemgr \ 11 | --config config.yaml \ 12 | --secrets secrets.yaml \ 13 | --id "context-data-id-1" \ 14 | --load 15 | 16 | VALUE=$(cat localdata/cicdContextData.json | jq -r .'state.key1') 17 | if [ "$VALUE" != "value1" ]; then 18 | echo 19 | echo "FAIL: LOAD: localdata/cicdContextData.json invalid" 20 | echo 21 | exit 1 22 | else 23 | echo 24 | echo "OK: LOAD: localdata/cicdContextData.json" 25 | echo 26 | fi 27 | 28 | 29 | 30 | cicdstatemgr \ 31 | --config config.yaml \ 32 | --secrets secrets.yaml \ 33 | --id "context-data-id-1" \ 34 | --load \ 35 | --set state.key1=valuechanged \ 36 | --set state.testHeader2Value="myvalueforheader2" \ 37 | --set state.triggerAutoArg1=99999 \ 38 | --handle-event build=testEvent 39 | 40 | VALUE=$(cicdstatemgr \ 41 | --config config.yaml \ 42 | --secrets secrets.yaml \ 43 | --id "context-data-id-1" \ 44 | --get "state.triggerAutoArg1") 45 | if [ "$VALUE" != "99999" ]; then 46 | echo 47 | echo "FAIL: LOAD: state.triggerAutoArg1 != 99999" 48 | echo 49 | exit 1 50 | else 51 | echo 52 | echo "OK: LOAD: state.triggerAutoArg1 success" 53 | echo 54 | fi -------------------------------------------------------------------------------- /examples/basics/test/handle-event.manual-choice.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # HANDLE_EVENT.md 5 | # manual-choice 6 | #----------------- 7 | 8 | cicdstatemgr \ 9 | --config config.yaml \ 10 | --secrets secrets.yaml \ 11 | --id "context-data-id-1" \ 12 | --handle-event build=testManualChoiceEvent 13 | 14 | # really nothing to validate here 15 | 16 | 17 | 18 | 19 | #----------------- 20 | # HANDLE_EVENT.md 21 | # choiceGeneratorItems test 22 | #----------------- 23 | 24 | cicdstatemgr \ 25 | --config config.yaml \ 26 | --secrets secrets.yaml \ 27 | --id "context-data-id-1" \ 28 | --set 'state.choiceGeneratorItems=file://set.manual-choice-gen-data.json' 29 | 30 | cicdstatemgr \ 31 | --config config.yaml \ 32 | --secrets secrets.yaml \ 33 | --id "context-data-id-1" \ 34 | --handle-event test2=blah-event 35 | 36 | 37 | OPTION_3_VALUE=$(cat localdata/cicdContextData.yaml | yq r - state.lastPostedHttpResponseFromManualChoice | jq -r .data | jq -r .choices[3].options[0].value) 38 | if [ "$OPTION_3_VALUE" != "item-three item3 desc" ]; then 39 | echo 40 | echo "FAIL: HANDLE_EVENT [manual-choice]: state.lastPostedHttpResponseFromManualChoice | jq -r .data | jq -r .choices[3].options[0].value != item-three item3 desc" 41 | echo 42 | exit 1 43 | else 44 | echo 45 | echo "OK: HANDLE_EVENT [manual-choice]: state.lastPostedHttpResponseFromManualChoice success" 46 | echo 47 | fi 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /cicdstatemgr/datasources/jsonfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | import os 8 | import json 9 | import logging 10 | 11 | from . import CicdContextDataSource 12 | from ..utils import get_file_path 13 | 14 | class DataSource(CicdContextDataSource): 15 | filePath = None 16 | isPrimary = False 17 | 18 | def __init__(self, config=dict): 19 | self.filePath = config['path'] 20 | 21 | if 'isPrimary' in config: 22 | self.isPrimary = config['isPrimary'] 23 | 24 | self.filePath = get_file_path(self.filePath) 25 | 26 | logging.debug("{} -> {} isPrimary:{} isLocal:{}".format(self.get_name(),self.filePath,self.isPrimary,self.is_local())) 27 | 28 | def is_local(self) -> bool: 29 | return True 30 | 31 | def get_name(self) -> str: 32 | return 'jsonfile' 33 | 34 | def is_primary(self) -> bool: 35 | return self.isPrimary 36 | 37 | def supports_load(self) -> bool: 38 | return True 39 | 40 | def persist(self, cicdContextDataId:str, cicdContextData:object): 41 | os.makedirs(os.path.dirname(self.filePath),exist_ok=True) 42 | with open(self.filePath, 'w') as f: 43 | jsonString = json.dumps(cicdContextData, sort_keys=False, indent=2) 44 | f.write(jsonString) 45 | 46 | def load(self, cicdContextDataId:str) -> dict: 47 | if os.path.isfile(self.filePath): 48 | with open(self.filePath, 'r') as f: 49 | return json.load(f) 50 | return None 51 | 52 | -------------------------------------------------------------------------------- /cicdstatemgr/jinja2util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | import base64 8 | import yaml 9 | import json 10 | from jinja2 import Environment 11 | 12 | def createEnvironment() -> Environment: 13 | env = Environment(extensions=['jinja2.ext.loopcontrols']) 14 | env.filters['to_nice_yaml'] = to_nice_yaml 15 | env.filters['from_json'] = from_json 16 | env.filters['json_dumps'] = json_dumps 17 | env.filters['kv_comma_pairs_to_json'] = kv_comma_pairs_to_json 18 | env.filters['to_base64'] = to_base64 19 | return env 20 | 21 | # Jinja filter 22 | def json_dumps(value,indent=None,stripLeadingTrailingQuotes=False): 23 | toReturn = json.dumps(value,indent=indent) 24 | if stripLeadingTrailingQuotes: 25 | return toReturn[1:-1] 26 | return toReturn 27 | 28 | # Jinja filter 29 | def from_json(value): 30 | return json.loads(value) 31 | 32 | # Jinja filter 33 | def to_nice_yaml(value, default_flow_style=False, sort_keys=False, indent=2): 34 | return yaml.dump(value, default_flow_style=False, sort_keys=sort_keys, indent=indent) 35 | 36 | # Jinja filter 37 | def kv_comma_pairs_to_json(value): 38 | obj = {} 39 | for kvpair in value.split(","): 40 | if kvpair and kvpair != '' and '=' in kvpair: 41 | key = kvpair.split("=")[0] 42 | val = kvpair.split("=")[1] 43 | obj[key] = val 44 | 45 | return json.dumps(obj) 46 | 47 | # Jinja filter 48 | def to_base64(value): 49 | return base64.b64encode(value.encode('utf-8')).decode('utf-8') 50 | 51 | 52 | -------------------------------------------------------------------------------- /cicdstatemgr/datasources/yamlfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | import yaml 8 | import os 9 | import logging 10 | 11 | from . import CicdContextDataSource 12 | 13 | from ..utils import get_file_path 14 | 15 | class DataSource(CicdContextDataSource): 16 | filePath = None 17 | isPrimary = False 18 | 19 | def __init__(self, config=dict): 20 | self.filePath = config['path'] 21 | 22 | if 'isPrimary' in config: 23 | self.isPrimary = config['isPrimary'] 24 | 25 | self.filePath = get_file_path(self.filePath) 26 | 27 | logging.debug("{} -> {} isPrimary:{} isLocal:{}".format(self.get_name(),self.filePath,self.isPrimary,self.is_local())) 28 | 29 | def is_local(self) -> bool: 30 | return True 31 | 32 | def supports_load(self) -> bool: 33 | return True 34 | 35 | def get_name(self) -> str: 36 | return 'yamlfile' 37 | 38 | def is_primary(self) -> bool: 39 | return self.isPrimary 40 | 41 | def persist(self, cicdContextDataId:str, cicdContextData:object): 42 | os.makedirs(os.path.dirname(self.filePath),exist_ok=True) 43 | with open(self.filePath, 'w') as f: 44 | yamlString = yaml.dump(cicdContextData, default_flow_style=False, sort_keys=False) 45 | f.write(yamlString) 46 | 47 | def load(self, cicdContextDataId:str) -> dict: 48 | if os.path.isfile(self.filePath): 49 | with open(self.filePath, 'r') as f: 50 | return yaml.load(f, Loader=yaml.FullLoader) 51 | return None 52 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/install-confs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 4 | 5 | 6 | NGROK_URL=$1 7 | TEKTON_DASHBOARD_URL=$2 8 | SLACK_ORG_URL="https://bitsofinfo.slack.com/" 9 | 10 | SED_OPTIONS="" 11 | SED_VERSION=$(sed --version >/dev/null 2>&1) 12 | if [ "$?" == "1" ]; then 13 | SED_OPTIONS=" -e " 14 | fi 15 | 16 | cp confs/cicdstatemgr-config.yaml confs/cicdstatemgr-config.yaml.tmp 17 | sed -i $SED_OPTIONS "s|@NGROK_URL@|${NGROK_URL}|g" confs/cicdstatemgr-config.yaml.tmp 18 | sed -i $SED_OPTIONS "s|@TEKTON_DASHBOARD_URL@|${TEKTON_DASHBOARD_URL}|g" confs/cicdstatemgr-config.yaml.tmp 19 | sed -i $SED_OPTIONS "s|@SLACK_ORG_URL@|${SLACK_ORG_URL}|g" confs/cicdstatemgr-config.yaml.tmp 20 | 21 | 22 | kubectl delete configmap cicdstatemgr-configs 23 | 24 | kubectl create configmap cicdstatemgr-configs \ 25 | --from-file=cicdstatemgr-config.yaml=$SCRIPTPATH/confs/cicdstatemgr-config.yaml.tmp \ 26 | --from-file=$SCRIPTPATH/bases/core.yaml \ 27 | --from-file=$SCRIPTPATH/bases/dev.yaml \ 28 | --from-file=$SCRIPTPATH/bases/prod.yaml \ 29 | -n tekton-pipelines 30 | 31 | kubectl delete secret cicdstatemgr-secrets 32 | 33 | # replace 34 | SLACK_BEARER_TOKEN=$(cat secrets/slack-bearer-token) 35 | cp confs/cicdstatemgr-secrets.yaml confs/cicdstatemgr-secrets.yaml.tmp 36 | sed -i $SED_OPTIONS "s|@SLACK_BEARER_TOKEN@|${SLACK_BEARER_TOKEN}|g" confs/cicdstatemgr-secrets.yaml.tmp 37 | 38 | kubectl create secret generic cicdstatemgr-secrets \ 39 | --from-file=cicdstatemgr-secrets.yaml=$SCRIPTPATH/confs/cicdstatemgr-secrets.yaml.tmp \ 40 | -n tekton-pipelines -------------------------------------------------------------------------------- /cicdstatemgr/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | import collections.abc 8 | import os 9 | 10 | # https://gist.github.com/angstwad/bf22d1822c38a92ec0a9 11 | def dict_merge(*args, add_keys=True, append_to_lists=False): 12 | assert len(args) >= 2, "dict_merge requires at least two dicts to merge" 13 | rtn_dct = args[0].copy() 14 | merge_dicts = args[1:] 15 | for merge_dct in merge_dicts: 16 | if add_keys is False: 17 | merge_dct = {key: merge_dct[key] for key in set(rtn_dct).intersection(set(merge_dct))} 18 | for k, v in merge_dct.items(): 19 | if not rtn_dct.get(k): 20 | rtn_dct[k] = v 21 | elif k in rtn_dct and type(v) != type(rtn_dct[k]): 22 | raise TypeError(f"Overlapping keys exist with different types: original is {type(rtn_dct[k])}, new value is {type(v)}") 23 | elif isinstance(rtn_dct[k], dict) and isinstance(merge_dct[k], collections.abc.Mapping): 24 | rtn_dct[k] = dict_merge(rtn_dct[k], merge_dct[k], add_keys=add_keys) 25 | elif isinstance(v, list) and append_to_lists: 26 | for list_value in v: 27 | if list_value not in rtn_dct[k]: 28 | rtn_dct[k].append(list_value) 29 | else: 30 | rtn_dct[k] = v 31 | return rtn_dct 32 | 33 | 34 | def get_file_path(filePath:str): 35 | if filePath and not filePath.startswith('./') and not filePath.startswith('/'): 36 | filePath = os.path.join(os.getcwd(),filePath) 37 | filePath = os.path.abspath(os.path.realpath(filePath)) 38 | 39 | return filePath -------------------------------------------------------------------------------- /examples/basics/test/handle-event.notify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # HANDLE_EVENT.md 5 | # notify 6 | #----------------- 7 | 8 | cicdstatemgr \ 9 | --config config.yaml \ 10 | --secrets secrets.yaml \ 11 | --id "context-data-id-1" \ 12 | --handle-event build=testNotifyEvent 13 | 14 | 15 | LAST_POSTED_ID=$(cat localdata/cicdContextData.yaml | yq r - state.lastPostedDataRandomId) 16 | VALUE=$(cat localdata/cicdContextData.yaml | yq r - state.postedData.${LAST_POSTED_ID}.body.message) 17 | if [ "$VALUE" != "This is basicMacro! msg = build is successful" ]; then 18 | echo 19 | echo "FAIL: HANDLE_EVENT [notify]: state.postedData.${LAST_POSTED_ID}.body.message != This is basicMacro! msg = build is successful" 20 | echo 21 | exit 1 22 | else 23 | echo 24 | echo "OK: HANDLE_EVENT [notify]: state.postedData.${LAST_POSTED_ID}.body.message success" 25 | echo 26 | fi 27 | 28 | 29 | #----------------- 30 | # HANDLE_EVENT.md 31 | # testEmbeddedJsonEvent 32 | #----------------- 33 | 34 | cicdstatemgr \ 35 | --config config.yaml \ 36 | --secrets secrets.yaml \ 37 | --id "context-data-id-1" \ 38 | --handle-event build=testEmbeddedJsonEvent 39 | 40 | 41 | LAST_POSTED_ID=$(cat localdata/cicdContextData.yaml | yq r - state.lastPostedDataRandomId) 42 | VALUE=$(cat localdata/cicdContextData.yaml | yq r - state.postedData.${LAST_POSTED_ID}.body.message) 43 | if [ "$VALUE" != "Here is some JSON from c:\windowspath\test and \"quotes\" { \"dog\":\"beagle\" }" ]; then 44 | echo 45 | echo "FAIL: HANDLE_EVENT [notify]: state.postedData.${LAST_POSTED_ID}.body.message != Here is some JSON from c:\windowspath\test and \"quotes\" { \"dog\":\"beagle\" }" 46 | echo 47 | exit 1 48 | else 49 | echo 50 | echo "OK: HANDLE_EVENT [notify]: state.postedData.${LAST_POSTED_ID}.body.message success" 51 | echo 52 | fi 53 | -------------------------------------------------------------------------------- /cicdstatemgr/datasources/redis.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | import yaml 8 | import os 9 | import redis 10 | import logging 11 | 12 | from . import CicdContextDataSource 13 | 14 | class DataSource(CicdContextDataSource): 15 | host = "localhost" 16 | port = 6379 17 | db = 0 18 | conn = None 19 | password = None 20 | username = 'default' 21 | isPrimary = False 22 | 23 | def __init__(self, config:dict): 24 | self.host = config['host'] if 'host' in config else None 25 | self.port = config['port'] if 'port' in config else None 26 | self.db = config['db'] if 'db' in config else None 27 | self.password = config['password'] if 'password' in config else None 28 | self.username = config['username'] if 'username' in config else None 29 | 30 | if 'isPrimary' in config: 31 | self.isPrimary = config['isPrimary'] 32 | 33 | self.init() 34 | 35 | logging.debug("{} -> {} isPrimary:{} isLocal:{}".format(self.get_name(),self.host,self.isPrimary,self.is_local())) 36 | 37 | def is_local(self) -> bool: 38 | return False 39 | 40 | def get_name(self) -> str: 41 | return 'redis' 42 | 43 | def is_primary(self) -> bool: 44 | return self.isPrimary 45 | 46 | def init(self): 47 | self.conn = redis.Redis(host=self.host, port=self.port, db=self.db, username=self.username, password=self.password) 48 | 49 | def persist(self, cicdContextDataId:str, cicdContextData:object): 50 | obj = yaml.dump(cicdContextData, default_flow_style=False, sort_keys=False) 51 | self.conn.set(cicdContextDataId, obj) 52 | 53 | def load(self, cicdContextDataId:str) -> dict: 54 | self.init() 55 | yamlStr = self.conn.get(cicdContextDataId) 56 | return yaml.load(yamlStr, Loader=yaml.FullLoader) 57 | 58 | def supports_load(self) -> bool: 59 | return True 60 | -------------------------------------------------------------------------------- /examples/basics/test/test_basics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple test script that validates the actual 4 | # `basics` example covered via the docs in order 5 | # 6 | # Should be run from the root of the basics/ dir 7 | 8 | SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 9 | 10 | #----------------- 11 | # README.md 12 | #----------------- 13 | 14 | # start redis 15 | docker rm -f cicdstatemgr-redis 16 | docker run \ 17 | -d \ 18 | -v `pwd`/redis.conf:/usr/local/etc/redis/redis.conf \ 19 | -p 6379:6379 \ 20 | --name cicdstatemgr-redis redis redis-server /usr/local/etc/redis/redis.conf 21 | 22 | # yq 23 | if [[ `uname` == 'Darwin' ]]; then 24 | if [ -z "$(which yq)" ]; then 25 | sudobrew install yq 26 | fi 27 | else 28 | sudo snap install yq 29 | fi 30 | 31 | # jq 32 | if [[ `uname` == 'Darwin' ]]; then 33 | if [ -z "$(which jq)" ]; then 34 | brew install jq 35 | fi 36 | else 37 | sudo snap install yq 38 | fi 39 | 40 | # setup python venv 41 | python3 -m venv venv 42 | source venv/bin/activate 43 | pip install wheel 44 | pip install --requirement requirements.txt 45 | 46 | 47 | # run the scripts 48 | $SCRIPTPATH/init-new.sh 49 | if [ "$?" != "0" ]; then exit 1; fi 50 | 51 | $SCRIPTPATH/get.sh 52 | if [ "$?" != "0" ]; then exit 1; fi 53 | 54 | $SCRIPTPATH/set.sh 55 | if [ "$?" != "0" ]; then exit 1; fi 56 | 57 | $SCRIPTPATH/load.sh 58 | if [ "$?" != "0" ]; then exit 1; fi 59 | 60 | $SCRIPTPATH/handle-event.notify.sh 61 | if [ "$?" != "0" ]; then exit 1; fi 62 | 63 | $SCRIPTPATH/handle-event.trigger-pipeline.sh 64 | if [ "$?" != "0" ]; then exit 1; fi 65 | 66 | $SCRIPTPATH/handle-event.manual-choice.sh 67 | if [ "$?" != "0" ]; then exit 1; fi 68 | 69 | $SCRIPTPATH/handle-event.set-values.sh 70 | if [ "$?" != "0" ]; then exit 1; fi 71 | 72 | $SCRIPTPATH/handle-event.respond.sh 73 | if [ "$?" != "0" ]; then exit 1; fi 74 | 75 | $SCRIPTPATH/handle-event.disable.sh 76 | if [ "$?" != "0" ]; then exit 1; fi 77 | 78 | $SCRIPTPATH/generate.sh 79 | if [ "$?" != "0" ]; then exit 1; fi 80 | -------------------------------------------------------------------------------- /examples/basics/test/init-new.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | #----------------- 5 | # INIT_NEW.md 6 | #----------------- 7 | 8 | 9 | # initialize new context 10 | cicdstatemgr \ 11 | --config config.yaml \ 12 | --secrets secrets.yaml \ 13 | \ 14 | --init-new "context-data-id-1" \ 15 | --init-bases-dir bases \ 16 | --init-app-config-file app.yaml \ 17 | --init-cicd-context-name stage \ 18 | \ 19 | --set "state.key1=value1" 20 | 21 | VALUE=$(redis-cli --user cicdstatemgr --pass '123$aBcZ' mget context-data-id-1 | yq r - state.key1) 22 | if [ -z "$VALUE" ]; then 23 | echo 24 | echo "FAIL: INIT_NEW: state.key1 failed to exist post create" 25 | echo 26 | exit 1 27 | else 28 | echo 29 | echo "OK: INIT_NEW: state.key1 exists post create" 30 | echo 31 | fi 32 | 33 | VALUE=$(cat localdata/cicdContextData.id) 34 | if [ "$VALUE" != "context-data-id-1" ]; then 35 | echo 36 | echo "FAIL: INIT_NEW: localdata/cicdContextData.id invalid" 37 | echo 38 | exit 1 39 | else 40 | echo 41 | echo "OK: INIT_NEW: localdata/cicdContextData.id" 42 | echo 43 | fi 44 | 45 | VALUE=$(cat localdata/cicdContextData.json | jq -r .'state.key1') 46 | if [ "$VALUE" != "value1" ]; then 47 | echo 48 | echo "FAIL: INIT_NEW: localdata/cicdContextData.json invalid" 49 | echo 50 | exit 1 51 | else 52 | echo 53 | echo "OK: INIT_NEW: localdata/cicdContextData.json" 54 | echo 55 | fi 56 | 57 | VALUE=$(cat localdata/cicdContextData.yaml | yq r - 'state.key1') 58 | if [ "$VALUE" != "value1" ]; then 59 | echo 60 | echo "FAIL: INIT_NEW: localdata/cicdContextData.yaml invalid" 61 | echo 62 | exit 1 63 | else 64 | echo 65 | echo "OK: INIT_NEW: localdata/cicdContextData.yaml" 66 | echo 67 | fi 68 | 69 | source localdata/cicdContextData.sh 70 | VALUE=$(echo $CICD_state__key1) 71 | if [ "$VALUE" != "value1" ]; then 72 | echo 73 | echo "FAIL: INIT_NEW: localdata/cicdContextData.sh invalid" 74 | echo 75 | exit 1 76 | else 77 | echo 78 | echo "OK: INIT_NEW: localdata/cicdContextData.sh" 79 | echo 80 | fi 81 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/test/tasks/test-invoke.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: tekton.dev/v1beta1 3 | kind: Task 4 | metadata: 5 | name: test-invoke 6 | spec: 7 | 8 | workspaces: 9 | - name: cicdstatemgr-configs 10 | description: The workspace where cicdstatemgr configs reside 11 | mountPath: /workspace/cicdstatemgr-configs 12 | readOnly: true 13 | 14 | - name: cicdstatemgr-secrets 15 | description: The workspace where cicdstatemgr secrets reside 16 | mountPath: /workspace/cicdstatemgr-secrets 17 | readOnly: true 18 | 19 | params: 20 | 21 | - name: pipelineRunUid 22 | type: string 23 | description: the pipelineRun uid 24 | default: NONE 25 | 26 | - name: cicdContextDataId 27 | type: string 28 | 29 | results: 30 | - name: last-exit-code 31 | description: The last exit code of this task 32 | 33 | steps: 34 | 35 | #---------------------------------- 36 | # STEP load CICD context data 37 | #---------------------------------- 38 | - name: load-cicd-context-data 39 | image: bitsofinfo/cicdstatemgr:1.2.4 40 | command: 41 | - /bin/bash 42 | args: 43 | - -c 44 | - | 45 | cicdstatemgr \ 46 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 47 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 48 | --id $(inputs.params.cicdContextDataId) \ 49 | --load 50 | echo 51 | 52 | cat /tekton/results/cicdContextDataId 53 | echo 54 | echo 55 | 56 | cat /tekton/results/cicdContextDataJson 57 | echo 58 | echo 59 | 60 | #---------------------------------- 61 | # 62 | # STEP Invoke - TODO 63 | # 64 | #---------------------------------- 65 | 66 | - name: test-invoke 67 | image: bitsofinfo/cicdstatemgr:1.2.4 68 | command: 69 | - /bin/bash 70 | args: 71 | - -c 72 | - | 73 | echo "YOU COULD FILL IN IMAGE TEST STEPS/TASKS HERE" 74 | 75 | LAST_EXIT_CODE=$? 76 | echo -n $LAST_EXIT_CODE > /tekton/results/last-exit-code 77 | echo 78 | 79 | 80 | -------------------------------------------------------------------------------- /examples/basics/test/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # GENERATE.md 5 | #----------------- 6 | 7 | cicdstatemgr \ 8 | --config config.yaml \ 9 | --secrets secrets.yaml \ 10 | --id "context-data-id-1" \ 11 | --set "state.gitTag=myapp-alpine-1.5.9" \ 12 | --set "state.gitAppName=my-application" 13 | 14 | cicdstatemgr \ 15 | --config config.yaml \ 16 | --secrets secrets.yaml \ 17 | --id "context-data-id-1" \ 18 | --tmpl-ctx-var "ctx.var1=v1val" \ 19 | --tmpl-ctx-var "ctx.var2=v2val" \ 20 | --generate pipelines.build.generators.dockerfile 21 | 22 | VALUE=$(cicdstatemgr \ 23 | --config config.yaml \ 24 | --secrets secrets.yaml \ 25 | --id "context-data-id-1" \ 26 | --get state.build.dockerfileInfo.generateDockerfileCmd) 27 | 28 | if [ "$VALUE" != './gendockerfile.sh -f alpine:latest -x myapp-alpine-1.5.9 -z v1val -q v2val' ]; then 29 | echo 30 | echo "FAIL: GET: state.build.dockerfileInfo.generateDockerfileCmd (alpine) != expected value" 31 | echo 32 | exit 1 33 | else 34 | echo 35 | echo "OK: GET: state.build.dockerfileInfo.generateDockerfileCmd (alpine)" 36 | echo 37 | fi 38 | 39 | 40 | 41 | cicdstatemgr \ 42 | --config config.yaml \ 43 | --secrets secrets.yaml \ 44 | --id "context-data-id-1" \ 45 | --set "state.gitTag=myapp-centos-1.5.9" \ 46 | --set "state.gitAppName=my-application" 47 | 48 | cicdstatemgr \ 49 | --config config.yaml \ 50 | --secrets secrets.yaml \ 51 | --id "context-data-id-1" \ 52 | --tmpl-ctx-var "ctx.var1=v1val" \ 53 | --tmpl-ctx-var "ctx.var2=v2val" \ 54 | --generate pipelines.build.generators.dockerfile 55 | 56 | VALUE=$(cicdstatemgr \ 57 | --config config.yaml \ 58 | --secrets secrets.yaml \ 59 | --id "context-data-id-1" \ 60 | --get state.build.dockerfileInfo.generateDockerfileCmd) 61 | 62 | if [ "$VALUE" != './gendockerfile.sh -f centos:latest -x myapp-centos-1.5.9 -z v1val -q v2val' ]; then 63 | echo 64 | echo "FAIL: GET: state.build.dockerfileInfo.generateDockerfileCmd (centos) != expected value" 65 | echo 66 | exit 1 67 | else 68 | echo 69 | echo "OK: GET: state.build.dockerfileInfo.generateDockerfileCmd (centos)" 70 | echo 71 | fi 72 | 73 | -------------------------------------------------------------------------------- /examples/tekton/core/tekton-pipelines-mods.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: v1 4 | kind: ConfigMap 5 | metadata: 6 | name: config-artifact-pvc 7 | namespace: tekton-pipelines 8 | labels: 9 | app.kubernetes.io/instance: default 10 | app.kubernetes.io/part-of: tekton-pipelines 11 | data: 12 | # size of the PVC volume 13 | size: 5Gi 14 | --- 15 | 16 | apiVersion: v1 17 | kind: ConfigMap 18 | metadata: 19 | name: feature-flags 20 | namespace: tekton-pipelines 21 | labels: 22 | app.kubernetes.io/instance: default 23 | app.kubernetes.io/part-of: tekton-pipelines 24 | data: 25 | # Setting this flag to "true" will prevent Tekton to create an 26 | # Affinity Assistant for every TaskRun sharing a PVC workspace 27 | # 28 | # The default behaviour is for Tekton to create Affinity Assistants 29 | # 30 | # See more in the workspace documentation about Affinity Assistant 31 | # https://github.com/tektoncd/pipeline/blob/master/docs/workspaces.md#affinity-assistant-and-specifying-workspace-order-in-a-pipeline 32 | # or https://github.com/tektoncd/pipeline/pull/2630 for more info. 33 | disable-affinity-assistant: "false" 34 | # Setting this flag to "true" will prevent Tekton overriding your 35 | # Task container's $HOME environment variable. 36 | # 37 | # The default behaviour currently is for Tekton to override the 38 | # $HOME environment variable but this will change in an upcoming 39 | # release. 40 | # 41 | # See https://github.com/tektoncd/pipeline/issues/2013 for more 42 | # info. 43 | disable-home-env-overwrite: "true" 44 | # Setting this flag to "true" will prevent Tekton overriding your 45 | # Task container's working directory. 46 | # 47 | # The default behaviour currently is for Tekton to override the 48 | # working directory if not set by the user but this will change 49 | # in an upcoming release. 50 | # 51 | # See https://github.com/tektoncd/pipeline/issues/1836 for more 52 | # info. 53 | disable-working-directory-overwrite: "true" 54 | # This option should be set to false when Pipelines is running in a 55 | # cluster that does not use injected sidecars such as Istio. Setting 56 | # it to false should decrease the time it takes for a TaskRun to start 57 | # running. For clusters that use injected sidecars, setting this 58 | # option to false can lead to unexpected behavior. 59 | # 60 | # See https://github.com/tektoncd/pipeline/issues/2080 for more info. 61 | running-in-environment-with-injected-sidecars: "false" 62 | 63 | 64 | -------------------------------------------------------------------------------- /examples/tekton/core/tekton-triggers-rbac.yaml: -------------------------------------------------------------------------------- 1 | 2 | kind: Role 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | metadata: 5 | name: tekton-triggers-admin 6 | rules: 7 | - apiGroups: 8 | - triggers.tekton.dev 9 | resources: 10 | - eventlisteners 11 | - triggerbindings 12 | - triggertemplates 13 | - pipelineresources 14 | - clusterinterceptors 15 | verbs: 16 | - get 17 | - list 18 | - watch 19 | - apiGroups: 20 | - tekton.dev 21 | resources: 22 | - taskruns 23 | - pipelineruns 24 | - pipelineresources 25 | verbs: 26 | - create 27 | - apiGroups: 28 | - "" 29 | resources: 30 | - configmaps 31 | verbs: 32 | - get 33 | - list 34 | - watch 35 | - apiGroups: 36 | - "" 37 | resources: 38 | - secrets 39 | verbs: 40 | - get 41 | - list 42 | - create 43 | - apiGroups: 44 | - "extensions" 45 | - "networking.k8s.io" 46 | resources: 47 | - ingresses 48 | verbs: 49 | - get 50 | - list 51 | - watch 52 | - create 53 | - patch 54 | --- 55 | apiVersion: v1 56 | kind: ServiceAccount 57 | metadata: 58 | name: tekton-triggers-admin 59 | 60 | --- 61 | apiVersion: rbac.authorization.k8s.io/v1 62 | kind: RoleBinding 63 | metadata: 64 | name: tekton-triggers-admin-binding 65 | subjects: 66 | - kind: ServiceAccount 67 | name: tekton-triggers-admin 68 | roleRef: 69 | apiGroup: rbac.authorization.k8s.io 70 | kind: Role 71 | name: tekton-triggers-admin 72 | 73 | --- 74 | 75 | kind: ClusterRole 76 | apiVersion: rbac.authorization.k8s.io/v1 77 | metadata: 78 | name: tekton-triggers-admin-el-clusterrole 79 | rules: 80 | # Permissions for every EventListener deployment to function 81 | - apiGroups: ["triggers.tekton.dev"] 82 | resources: ["clustertriggerbindings", "eventlisteners", "triggerbindings", "triggertemplates", "triggers", "clusterinterceptors"] 83 | verbs: ["get", "list", "watch"] 84 | - apiGroups: [""] 85 | # secrets are only needed for GitHub/GitLab interceptors 86 | resources: ["configmaps", "secrets"] 87 | verbs: ["get", "list", "watch"] 88 | # Permissions to create resources in associated TriggerTemplates 89 | - apiGroups: ["tekton.dev"] 90 | resources: ["pipelineruns", "pipelineresources", "taskruns"] 91 | verbs: ["create"] 92 | 93 | --- 94 | 95 | apiVersion: rbac.authorization.k8s.io/v1 96 | kind: ClusterRoleBinding 97 | metadata: 98 | name: tekton-triggers-admin-el-clusterbinding 99 | subjects: 100 | - kind: ServiceAccount 101 | name: tekton-triggers-admin 102 | namespace: tekton-pipelines 103 | roleRef: 104 | apiGroup: rbac.authorization.k8s.io 105 | kind: ClusterRole 106 | name: tekton-triggers-admin-el-clusterrole 107 | -------------------------------------------------------------------------------- /examples/basics/README.md: -------------------------------------------------------------------------------- 1 | # Basic example 2 | 3 | The following examples will give you a decent overview of how you can make use of `cicdstatemgr` 4 | 5 | [For more background info see this blog post](https://bitsofinfo.wordpress.com/2020/08/13/tekton-pipelines-cicd-slack-triggers-state/) 6 | 7 | ## scripts 8 | 9 | Note that all the examples provided in the various markdown files in this directory are also covered in the [scripts in the test/ directory](test/) 10 | 11 | ## Pre-req: Setup Redis 12 | 13 | Make sure you are within this directory (examples/basics) on your local machine when running the commands below 14 | 15 | Start a local redis 16 | ``` 17 | docker run \ 18 | -d \ 19 | -v `pwd`/redis.conf:/usr/local/etc/redis/redis.conf \ 20 | -p 6379:6379 \ 21 | --name cicdstatemgr-redis redis redis-server /usr/local/etc/redis/redis.conf 22 | ``` 23 | 24 | Verify connectivity: 25 | ``` 26 | $ redis-cli 27 | 28 | 127.0.0.1:6379> mset x y 29 | (error) NOAUTH Authentication required. 30 | 31 | 127.0.0.1:6379> auth cicdstatemgr 123$aBcZ 32 | OK 33 | 34 | 127.0.0.1:6379> mset x y 35 | OK 36 | 37 | 127.0.0.1:6379> mget x 38 | 1) "y" 39 | ``` 40 | 41 | ## Setup your redis configuration 42 | 43 | Take a look at `config.yaml` and `secrets.yaml`. Within these files are non-secret configs and sensitive configs. The general structure of both files is the same with secrets separated from non-secret config data. 44 | 45 | ## Setup a local python virtual env 46 | 47 | ``` 48 | python3 -m venv venv 49 | source venv/bin/activate 50 | pip install wheel 51 | pip install cicdstatemgr 52 | ``` 53 | 54 | ## Optionally install yq & jq 55 | 56 | https://github.com/mikefarah/yq 57 | 58 | https://github.com/stedolan/jq 59 | 60 | # Exploring the cicdstatemgr CLI options 61 | 62 | ## --init-new 63 | 64 | Lets start by imagining a new pipeline starts in our CICD system, we have a task that needs to initialize a new set of CICD context data. 65 | 66 | Lets start by using the [--init-new set of arguments](INIT_NEW.md) 67 | 68 | ## --get 69 | 70 | Next lets `--get` some values from the context data by using the [--get argument](GET.md) 71 | 72 | ## --set 73 | 74 | Next lets `--set` some values into the context data by using the [--set argument](SET.md) 75 | 76 | ## --load 77 | 78 | Next lets `--load` the context data for usage via [--load argument](LOAD.md) 79 | 80 | ## --generate 81 | 82 | Next lets `--generate` pre-defined content into the `cicdContextData` based on evaluating current state via the [--generate argument](GENERATE.md) 83 | 84 | ## --handle-event 85 | 86 | Finally lets play with the `--handle-event` option to fire off HTTP POSTs to endpoints and automatically set values etc via the [--handle-event argument](HANDLE_EVENT.md) 87 | 88 | # Notes 89 | 90 | local dev in this dir: 91 | ``` 92 | pip install --requirement requirements.txt 93 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | 8 | localdata 9 | 10 | .DS_Store 11 | 12 | *.mp4 13 | 14 | # C extensions 15 | *.so 16 | 17 | # Distribution / packaging 18 | .Python 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | .eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | wheels/ 31 | share/python-wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | MANIFEST 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .nox/ 51 | .coverage 52 | .coverage.* 53 | .cache 54 | nosetests.xml 55 | coverage.xml 56 | *.cover 57 | *.py,cover 58 | .hypothesis/ 59 | .pytest_cache/ 60 | cover/ 61 | 62 | # Translations 63 | *.mo 64 | *.pot 65 | 66 | # Django stuff: 67 | *.log 68 | local_settings.py 69 | db.sqlite3 70 | db.sqlite3-journal 71 | 72 | # Flask stuff: 73 | instance/ 74 | .webassets-cache 75 | 76 | # Scrapy stuff: 77 | .scrapy 78 | 79 | # Sphinx documentation 80 | docs/_build/ 81 | 82 | # PyBuilder 83 | .pybuilder/ 84 | target/ 85 | 86 | # Jupyter Notebook 87 | .ipynb_checkpoints 88 | 89 | # IPython 90 | profile_default/ 91 | ipython_config.py 92 | 93 | # pyenv 94 | # For a library or package, you might want to ignore these files since the code is 95 | # intended to run in multiple environments; otherwise, check them in: 96 | # .python-version 97 | 98 | # pipenv 99 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 100 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 101 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 102 | # install all needed dependencies. 103 | #Pipfile.lock 104 | 105 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 106 | __pypackages__/ 107 | 108 | # Celery stuff 109 | celerybeat-schedule 110 | celerybeat.pid 111 | 112 | # SageMath parsed files 113 | *.sage.py 114 | 115 | # Environments 116 | .env 117 | .venv 118 | env/ 119 | venv/ 120 | ENV/ 121 | env.bak/ 122 | venv.bak/ 123 | 124 | # Spyder project settings 125 | .spyderproject 126 | .spyproject 127 | 128 | # Rope project settings 129 | .ropeproject 130 | 131 | # mkdocs documentation 132 | /site 133 | 134 | # mypy 135 | .mypy_cache/ 136 | .dmypy.json 137 | dmypy.json 138 | 139 | # Pyre type checker 140 | .pyre/ 141 | 142 | # pytype static type analyzer 143 | .pytype/ 144 | 145 | # Cython debug symbols 146 | cython_debug/ -------------------------------------------------------------------------------- /examples/tekton/pipelines/common/conditions/conditions.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1alpha1 2 | kind: Condition 3 | metadata: 4 | name: exit-code-is-success 5 | spec: 6 | check: 7 | args: 8 | - if [ "$(params.exit-code)" = "0" ]; then exit 0; else exit 1 ; fi 9 | command: 10 | - bash 11 | - -c 12 | image: bitsofinfo/cicd-toolbox:3.0.5 13 | params: 14 | - name: exit-code 15 | 16 | --- 17 | 18 | apiVersion: tekton.dev/v1alpha1 19 | kind: Condition 20 | metadata: 21 | name: exit-code-is-failure 22 | spec: 23 | check: 24 | args: 25 | - if [ "$(params.exit-code)" != "0" ]; then exit 0; else exit 1 ; fi 26 | command: 27 | - bash 28 | - -c 29 | image: bitsofinfo/cicd-toolbox:3.0.5 30 | params: 31 | - name: exit-code 32 | 33 | --- 34 | 35 | 36 | apiVersion: tekton.dev/v1alpha1 37 | kind: Condition 38 | metadata: 39 | name: comparison 40 | spec: 41 | params: 42 | - name: lhs 43 | - name: operator 44 | - name: rhs 45 | check: 46 | image: bitsofinfo/cicd-toolbox:3.0.5 47 | command: 48 | - /bin/bash 49 | args: 50 | - -c 51 | - | 52 | if [ "$(params.lhs)" $(params.operator) "$(params.rhs)" ]; then exit 0; else exit 1 ; fi 53 | --- 54 | 55 | apiVersion: tekton.dev/v1alpha1 56 | kind: Condition 57 | metadata: 58 | name: or-comparison 59 | spec: 60 | params: 61 | - name: lhs1 62 | - name: operator1 63 | - name: rhs1 64 | - name: lhs2 65 | - name: operator2 66 | - name: rhs2 67 | 68 | check: 69 | image: bitsofinfo/cicd-toolbox:3.0.5 70 | command: 71 | - /bin/bash 72 | args: 73 | - -c 74 | - | 75 | if [ "$(params.lhs1)" $(params.operator1) "$(params.rhs1)" ] || [ "$(params.lhs2)" $(params.operator2) "$(params.rhs2)" ]; then exit 0; else exit 1 ; fi 76 | 77 | --- 78 | 79 | apiVersion: tekton.dev/v1alpha1 80 | kind: Condition 81 | metadata: 82 | name: and-comparison 83 | spec: 84 | params: 85 | - name: lhs1 86 | - name: operator1 87 | - name: rhs1 88 | - name: lhs2 89 | - name: operator2 90 | - name: rhs2 91 | 92 | check: 93 | image: bitsofinfo/cicd-toolbox:3.0.5 94 | command: 95 | - /bin/bash 96 | args: 97 | - -c 98 | - | 99 | if [ "$(params.lhs1)" $(params.operator1) "$(params.rhs1)" ] && [ "$(params.lhs2)" $(params.operator2) "$(params.rhs2)" ]; then exit 0; else exit 1 ; fi 100 | 101 | --- 102 | 103 | 104 | apiVersion: tekton.dev/v1alpha1 105 | kind: Condition 106 | metadata: 107 | name: python-eval 108 | spec: 109 | params: 110 | - name: code 111 | check: 112 | image: bitsofinfo/cicd-toolbox:3.0.5 113 | command: 114 | - /usr/local/bin/python 115 | args: 116 | - -c 117 | - | 118 | $(params.code) 119 | 120 | --- -------------------------------------------------------------------------------- /cicdstatemgr/datasources/shellfile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | import os 8 | import collections.abc 9 | import logging 10 | import re 11 | 12 | from . import CicdContextDataSource 13 | from ..utils import get_file_path 14 | 15 | class DataSource(CicdContextDataSource): 16 | filePath = None 17 | isPrimary = False 18 | excludeKeyNames = [] 19 | 20 | def __init__(self, config=dict): 21 | self.filePath = config['path'] 22 | if 'excludeKeyNames' in config: 23 | self.excludeKeyNames = config['excludeKeyNames'] 24 | 25 | if 'isPrimary' in config: 26 | self.isPrimary = config['isPrimary'] 27 | 28 | self.filePath = get_file_path(self.filePath) 29 | 30 | logging.debug("{} -> {} isPrimary:{} isLocal:{}".format(self.get_name(),self.filePath,self.isPrimary,self.is_local())) 31 | 32 | def is_local(self) -> bool: 33 | return True 34 | 35 | def get_name(self) -> str: 36 | return 'shellfile' 37 | 38 | def is_primary(self) -> bool: 39 | return self.isPrimary 40 | 41 | def flatten(self, d, parent_key='', sep="__"): 42 | items = [] 43 | for k, v in d.items(): 44 | 45 | # skip.... if key is in excludes 46 | if self.excludeKeyNames and k in self.excludeKeyNames: 47 | continue 48 | 49 | if isinstance(v,list) and len(v) > 0: 50 | for i in range(len(v)): 51 | new_key = parent_key + sep + k + sep + str(i) if parent_key else k 52 | itemVal = v[i] 53 | if isinstance(itemVal, collections.abc.MutableMapping): 54 | items.extend(self.flatten(itemVal, new_key, sep=sep).items()) 55 | else: 56 | items.append((new_key, itemVal)) 57 | else: 58 | new_key = parent_key + sep + k if parent_key else k 59 | if isinstance(v, collections.abc.MutableMapping): 60 | items.extend(self.flatten(v, new_key, sep=sep).items()) 61 | else: 62 | items.append((new_key, v)) 63 | return dict(items) 64 | 65 | def supports_load(self) -> bool: 66 | return False 67 | 68 | def persist(self, cicdContextDataId:str, cicdContextData:object): 69 | 70 | os.makedirs(os.path.dirname(self.filePath),exist_ok=True) 71 | 72 | kvMap = self.flatten(cicdContextData) 73 | 74 | shellVars = "" 75 | for prop in kvMap: 76 | if kvMap[prop]: 77 | propVal = kvMap[prop] 78 | if propVal and isinstance(propVal,str): 79 | propVal = propVal.replace('`','\`').replace('"','\"') 80 | 81 | shellVars += "CICD_{}=\"{}\"\n".format(re.sub('[^0-9a-zA-Z_]+', '_', prop),propVal) 82 | 83 | with open(self.filePath,'w') as f: 84 | f.write(shellVars) 85 | 86 | def load(self, cicdContextDataId:str) -> dict: 87 | raise NotImplementedError 88 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # TBD 2 | * update `examples/tekton` w/ latest components 3 | * update `examples/tekton` w/ `WhenExpressions` and comment out `Conditions` 4 | 5 | # 1.2.4 6 | * fix `generate` json patch match comparison 7 | 8 | # 1.2.3 9 | * bump `FROM bitsofinfo/cicd-toolbox:3.0.5` 10 | 11 | # 1.2.2 12 | * Do not emit `None` on `--get` if value returned is `None` (null, not found etc) 13 | 14 | # 1.2.1 15 | * Ensure `--tmpl-ctx-var` can be used in combination w/ `--generate` 16 | 17 | # 1.2.0 18 | * Support for new `generators` capability and `--generate` argument to the CLI. https://github.com/bitsofinfo/cicdstatemgr/issues/9 19 | 20 | # 1.1.3 21 | * Fix bug w/ `trigger-pipeline` and no subkey configuration 22 | 23 | # 1.1.2 24 | * Support multiple handlers of type `notify` and `trigger-pipeline` per https://github.com/bitsofinfo/cicdstatemgr/issues/1 25 | * Add `if` support for `notify` and `trigger-pipeline` blocks 26 | 27 | # 1.1.1 28 | * Fix non-json http responses in `respond` handling 29 | * Support arbitrary variables in `respond` config blocks and jinja2 parse them 30 | 31 | # 1.1.0 32 | * Added support for `choice-generators` within `manual-choice` event handlers. Permits the dynamic generation of new choice sets via a `template` based on a pointer to a dictionary containing data that can be cross referened in an `iterator`. 33 | * Added `*-capture-response-data` support for `manual-choice` POSTs 34 | 35 | # 1.0.26 36 | * address #8 new jinja2 filter `json_dumps(value,indent=None,stripLeadingTrailingQuotes=False)` 37 | 38 | # 1.0.25 39 | * address #6 fix `--tmpl-ctx-var` w/ `=` vals 40 | 41 | # 1.0.24 42 | * address #5 exclude shell datasource from generating jinja2macro shell vars (too large) 43 | 44 | # 1.0.23 45 | 46 | * document/refine support for the `list` datatype for the `--set` argument 47 | * i.e. `--set some.path.myset[]=e,f,g,e` = [e,f,g,e] 48 | * any subsequent `--set` calls to same key act as an add/extension to existing values 49 | * `--set some.path.myset[]=[]` clears any pre-existing values 50 | 51 | * add support for the `set` datatype (no-duplicates list) for the `--set` argument 52 | * i.e. `--set some.path.myset{}=e,f,g,e` = [e,f,g] 53 | * any subsequent `--set` calls to same key act as an add/extension to existing values 54 | * `--set some.path.myset{}=[]` clears any pre-existing values 55 | 56 | # 1.0.22 57 | * add support for toggling any `event-handlers:` sub entry in a derivative via `enabled: False` See #4 58 | 59 | # 1.0.21 60 | * jinja2 parse error include original non-macro prefixed template 61 | * parse `to` on set-values handlers 62 | * add `from_json` jinja2 filter 63 | * all CLI env var names change prefix from `CICDOPS_` to `CICDSTATEMGR_` 64 | 65 | # 1.0.20 66 | * fix `--get` when `get_via_jsonng_expression()` has jsonpath parse error. return literal expression as the value 67 | 68 | # 1.0.19 69 | * fix `--get` value parse handling where right hand side is not an expression, return literal value on no-match 70 | 71 | # 1.0.18 72 | * documentation 73 | * log statement level clarity 74 | * added `latest` docker tag 75 | * fix `setup.py` for dependencies 76 | 77 | # 1.0.0 - 1.0.17 78 | * getting travis functioning properly -------------------------------------------------------------------------------- /examples/basics/LOAD.md: -------------------------------------------------------------------------------- 1 | # --load 2 | 3 | *NOTE: see the functioning [scripts in the test/ directory](test/) for commands that match what is described in this doc* 4 | 5 | This demonstrates the usage of `--load` which provides a way to load all data for a given `cicdContextDataId` into both memory for the current invocation as well as into all non-primary stores, sourced from the primary store. 6 | 7 | If you look at [config.yaml](config.yaml) you will see there are multiple `datasources` listed. When `--load` is invoked, the data is loaded from the primary and then flushed to non-primary stores. In this example's case the non-primary stores are just local files, so when you are running a CICD system, `--load` would be called at the start of a task, where the data can then be referenced from numerous subsequent steps. This works nicely w/ [Tekton pipelines](https://github.com/tektoncd/pipeline) as each `Task` is a pod that is made up of multiple `steps` which are `containers`, all of which share a common filesystem. In this setup your first Tekton `Task` `step` can call `--load` to seed the filesystem with local copies of the context data that originates from the primary. 8 | 9 | `--load` can be decorated with `--set`, `--handle-event` 10 | 11 | This assumes you've already run [INIT_NEW](INIT_NEW.md) 12 | 13 | Let's just ensure our latest data exists locally for use by invoking `--load` 14 | ``` 15 | rm -rf localdata/* 16 | 17 | cicdstatemgr \ 18 | --config config.yaml \ 19 | --secrets secrets.yaml \ 20 | --id "context-data-id-1" \ 21 | --load 22 | 23 | ls -al localdata/* 24 | -rw-r--r-- 1 bof staff 17 Aug 2 18:17 localdata/cicdContextData.id 25 | -rw-r--r-- 1 bof staff 1821 Aug 2 18:17 localdata/cicdContextData.json 26 | -rw-r--r-- 1 bof staff 1444 Aug 2 18:17 localdata/cicdContextData.sh 27 | -rw-r--r-- 1 bof staff 1415 Aug 2 18:17 localdata/cicdContextData.yaml 28 | ``` 29 | 30 | Note that the PATH of where the local files are written as well as the filenames themselves can be controlled via the `datasources` section within a [config.yaml](config.yaml) file. 31 | 32 | After data is locally cached via `--load` the idea is that you can then utilize the data in various ways: 33 | 34 | * Via any programs that can read directly from JSON or YAML such as `jq`, `yq` or others you wrote yourself. Really anything. 35 | 36 | * Via shell scripts by doing a `source localdata/cicdContextData.sh` to load all variables into the shell for subsequent use 37 | 38 | * Via `cicdstatemgr` itself via its `--get` method etc 39 | 40 | 41 | Load can also be combined w/ `--set` and `--handle-event` such as: 42 | ``` 43 | cicdstatemgr \ 44 | --config config.yaml \ 45 | --secrets secrets.yaml \ 46 | --id "context-data-id-1" \ 47 | --load \ 48 | --set state.key1=valuechanged \ 49 | --set state.testHeader2Value="myvalueforheader2" \ 50 | --set state.triggerAutoArg1=99999 \ 51 | --handle-event build=testEvent 52 | ``` 53 | 54 | The above will load the data, write it to all non-primary datasources, then `--set` all your specified values, and then finally `--handle-event` which can subsequently have handlers that reference the data `--set` previously. -------------------------------------------------------------------------------- /examples/basics/test/get.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # GET.md 5 | #----------------- 6 | 7 | cicdstatemgr \ 8 | --config config.yaml \ 9 | --secrets secrets.yaml \ 10 | --id "context-data-id-1" \ 11 | --set "state.key1=value1" 12 | 13 | cicdstatemgr \ 14 | --config config.yaml \ 15 | --secrets secrets.yaml \ 16 | --id "context-data-id-1" \ 17 | --set 'state.templateTest={{tmplctx.prop1}}' 18 | 19 | VALUE=$(cicdstatemgr \ 20 | --config config.yaml \ 21 | --secrets secrets.yaml \ 22 | --id "context-data-id-1" \ 23 | --get "state.templateTest" \ 24 | --tmpl-ctx-var tmplctx.prop1=state.key1) 25 | if [ "$VALUE" != "value1" ]; then 26 | echo 27 | echo "FAIL: GET: state.templateTest != value1" 28 | echo 29 | exit 1 30 | else 31 | echo 32 | echo "OK: GET: state.templateTest success" 33 | echo 34 | fi 35 | 36 | cicdstatemgr \ 37 | --config config.yaml \ 38 | --secrets secrets.yaml \ 39 | --id "context-data-id-1" \ 40 | --set "state.key1=yet a new value" 41 | 42 | VALUE=$(cicdstatemgr \ 43 | --config config.yaml \ 44 | --secrets secrets.yaml \ 45 | --id "context-data-id-1" \ 46 | --get "state.templateTest" \ 47 | --tmpl-ctx-var tmplctx.prop1=state.key1) 48 | if [ "$VALUE" != "yet a new value" ]; then 49 | echo 50 | echo "FAIL: GET: state.templateTest != yet a new value" 51 | echo 52 | exit 1 53 | else 54 | echo 55 | echo "OK: GET: state.templateTest update yet a new value success" 56 | echo 57 | fi 58 | 59 | 60 | VALUE=$(cicdstatemgr \ 61 | --config config.yaml \ 62 | --secrets secrets.yaml \ 63 | --id "context-data-id-1" \ 64 | --get "state.templateTest" \ 65 | --tmpl-ctx-var tmplctx.prop1=just-a-literal-value) 66 | if [ "$VALUE" != "just-a-literal-value" ]; then 67 | echo 68 | echo "FAIL: GET: state.templateTest != just-a-literal-value" 69 | echo 70 | exit 1 71 | else 72 | echo 73 | echo "OK: GET: state.templateTest update just-a-literal-value success" 74 | echo 75 | fi 76 | 77 | 78 | VALUE=$(cicdstatemgr \ 79 | --config config.yaml \ 80 | --secrets secrets.yaml \ 81 | --id "context-data-id-1" \ 82 | --get "state.templateTest") 83 | if [ "$VALUE" != ' {{tmplctx.prop1}}' ]; then 84 | echo 85 | echo "FAIL: GET: state.templateTest != {{tmplctx.prop1}}" 86 | echo 87 | exit 1 88 | else 89 | echo 90 | echo "OK: GET: state.templateTest invalid template success" 91 | echo 92 | fi 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | cicdstatemgr \ 102 | --config config.yaml \ 103 | --secrets secrets.yaml \ 104 | --id "context-data-id-1" \ 105 | --set 'state.templateTest={{tmplctx.prop1}}' 106 | 107 | 108 | VALUE=$(cicdstatemgr \ 109 | --config config.yaml \ 110 | --secrets secrets.yaml \ 111 | --id "context-data-id-1" \ 112 | --get "state.templateTest" \ 113 | --tmpl-ctx-var tmplctx.prop1='x=1 y=2 z=3') 114 | if [ "$VALUE" != 'x=1 y=2 z=3' ]; then 115 | echo 116 | echo 'FAIL: GET: state.templateTest != x=1 y=2 z=3' 117 | echo 118 | exit 1 119 | else 120 | echo 121 | echo "OK: GET: state.templateTest RHS (=) template success" 122 | echo 123 | fi -------------------------------------------------------------------------------- /examples/tekton/pipelines/common/tasks/handle-event.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: tekton.dev/v1beta1 3 | kind: Task 4 | metadata: 5 | name: handle-event 6 | spec: 7 | 8 | workspaces: 9 | 10 | - name: cicdstatemgr-configs 11 | description: The workspace where cicdstatemgr configs reside 12 | mountPath: /workspace/cicdstatemgr-configs 13 | readOnly: true 14 | 15 | - name: cicdstatemgr-secrets 16 | description: The workspace where cicdstatemgr secrets reside 17 | mountPath: /workspace/cicdstatemgr-secrets 18 | readOnly: true 19 | 20 | results: 21 | - name: last-exit-code 22 | description: The last exit code of this task 23 | 24 | params: 25 | - name: cicdContextDataId 26 | type: string 27 | 28 | - name: pipelineRunUid 29 | type: string 30 | description: the pipelineRun uid 31 | default: NONE 32 | 33 | - name: pipelineName 34 | type: string 35 | description: the pipeline name 36 | default: pipelineName-NULL 37 | 38 | - name: eventName 39 | type: string 40 | description: the eventName name 41 | default: eventName-NULL 42 | 43 | - name: exitWithExitCode 44 | type: string 45 | description: the exit code to exit with 46 | default: "1" 47 | 48 | - name: setContextDataValues 49 | type: string 50 | description: PIPE (|) delimited list of k=v pairs 51 | default: "state.loadAndHandleEvent=setContextDataValues.not.provided" 52 | 53 | 54 | steps: 55 | 56 | #---------------------------------- 57 | # STEP handle event 58 | #---------------------------------- 59 | - name: handle-event 60 | image: bitsofinfo/cicdstatemgr:1.2.4 61 | command: 62 | - /bin/bash 63 | args: 64 | - -c 65 | - | 66 | 67 | EVENT_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ") 68 | 69 | cicdstatemgr \ 70 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 71 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 72 | --id $(inputs.params.cicdContextDataId) \ 73 | --load \ 74 | --set '$(inputs.params.setContextDataValues)' \ 75 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.pipelineName=$(inputs.params.pipelineName)" \ 76 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).$(inputs.params.eventName)At=$EVENT_AT" \ 77 | --handle-event $(inputs.params.pipelineName)=$(inputs.params.eventName) \ 78 | --tmpl-ctx-var "event.data=state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid)" 79 | 80 | HANDLE_EVENT_EXIT_CODE=$? 81 | 82 | if [ "$HANDLE_EVENT_EXIT_CODE" != "0" ]; then 83 | 84 | echo "handle-event step failed with exit code: $HANDLE_EVENT_EXIT_CODE" 85 | echo -n $HANDLE_EVENT_EXIT_CODE > /tekton/results/last-exit-code 86 | 87 | else 88 | cat /tekton/results/cicdContextDataId 89 | echo 90 | echo 91 | 92 | cat /tekton/results/cicdContextDataJson 93 | echo 94 | echo 95 | 96 | exit $(inputs.params.exitWithExitCode) 97 | 98 | fi 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: "Xenial" 2 | language: python 3 | python: 4 | - "3.8" 5 | 6 | addons: 7 | snaps: 8 | - name: yq 9 | channel: v3/stable 10 | - jq 11 | 12 | services: 13 | - docker 14 | 15 | env: 16 | DOCKERHUB_USER: 17 | secure: ncyM5gbArjxGcczo/DKdv8xLyqRJx44fNYeOyUSJeVxoTCNGBcA79Jv9hPu3tHXdUGd2lHisjCEsSE6nZ0YmSZ2IipxtnDNu+HddZdNRPCL6HOE1EgreBJAK4+4TwOk6SOUnw+mF+fz796pIMpLq+xSciUQoxkVTXLaWT/orcoEnTgnEEWJkwWvPkDLnliC5OKnm6F1i8Rny+jKIO6gHBHanJeczspohURMzyXde9+PfAPsk8lFkSCVXDb+4kQlPaWod2djIyB1Ar6XewcxLA2fe/qyI/BTkBdTJu8sqFFUpc2dicQAAYx9prW3xdzCoaCtC/lVCmmrQlGqcpO7YJoCWPQClYdM1muDXMN0vVbmbUd95uapgKbQNqG8YDOq+7oRxCia7UFmhZAUPb2kK1Vt2yJYyEMOULVFKIapAb5DTUGONy8l8AQXnEWbq4XQCeUmbqJEnILJEAwPQTIwoVJXnIhJ6OP0uJi8oyQ1T3+oGKVWSUN0GzCCKlUeN5QL7UZIItpKkijfJ9WyP1bLMpvjCZBZjYF9RCTIiVXtg4reuVekSZ7PeTBBbK3Uwpl9GPvlTTq41QB1Tmb/qL0OmxL4x4CrXDTwIZCQ9BvKPg2Lr/QbIb7xyREZIlQcHjSuB/TJA/OsrJi538x+fBcdFtHU43Ed2wZ+ShF+nIfxYmMs= 18 | DOCKERHUB_TOKEN: 19 | secure: p7yQ6sWvT9Ui7ltNDmuEOmw6clJaWCL0QuPMLO5RUwpXllOIogoxAK/2sA6mcv4B+OdLfsHugrPfxPPSfCP2o41Tp1tKqwJniQCWu4KfMZtK47hEAg4f0fKVfv/TRRidfSjpeLpUFXEK+yk7ESz5rBNkwxGNeYCu02sFK/ZP5v1Qi1sXLvcXi7N6YRaJF1ST0LzSHjceDqnkgUkqf6aPctVA7/RKu1sfvG2TpH+T7i1i75C/rnKDst1QwL98H58HeDVIb+28HR7+1LatDDt2fV0z0ybNdmHXu+KN7rUZkEIPCwKJImYhQfltMl6CJb5uJejd9i+K8YrguRaEKje48bxUUSs+7SNnjV0Fe66FKM3ueelz2pxC35uQ1+TkOfXZf8NMcJcfAOPtkxJYXSbs0V+V2MUsmflJlyhnOppiP4VI+4JZAUkPNE4EXql/R5qA6paL/JTEkGw/j0ERF5ElsDe8Zs3BvYK6XJlWlQyOaeNwYKoFIgIi4/KbIA/DFoRg/dhow9kSfkKDw8olvuN0HaCqgmgsrRvIlcV68/QZHypYj3GzJv+q2hB5JMvUyCEcCv5yZyb3osiJqkHWUOJ0Bxd06N8V9J+pq5RF5Zl20bo/0CbILOE0qG884rWvchIq0PPcsNVDA9nU4sxZmtRXrr2krw2EjqdTAn6wEghyxFQ= 20 | 21 | install: 22 | - pip3 install -r requirements.txt 23 | - pip3 install . 24 | 25 | script: 26 | - pytest 27 | - cd examples/basics 28 | - ./test/test_basics.sh 29 | - cd ../../ 30 | 31 | before_deploy: 32 | - echo TRAVIS_TAG=$TRAVIS_TAG 33 | - sed -i "s/TRAVISTAGHERE/${TRAVIS_TAG}/g" setup.py 34 | - cat setup.py 35 | 36 | deploy: 37 | skip_cleanup: true 38 | provider: pypi 39 | username: __token__ 40 | password: 41 | secure: GNXzs+nyElG3AtZ8p70RE9UqiQapUtlGl4qIXLs15BMyUNahknuRN/5XmpkMGsLpTUbY3AzzZP1RB4CbqWaw3YFQg2D7l2jToD14jHQsVM6VNnbGLpuD3YqsM/dqRSzm8nb5smYouvPRDdckh0NvKNX/kw5V8DI05plqWp4kkWg9MRPLTdQph7Y3nTrb20oqvcAO9dkI8Z5jjs2XACdxBgpYDFonxyTqlmDZiLoEo5l1+iwN6jfLICe8cmx9cjMOKzg7AKcmpPnttb7tWksRc5ULDKD+iiT6zCtbG56Hcf6xOM9HMB7mo66TYL3LvHqNs0yBgiTUqN9jShW/JX4GsASuHgoZYQsCC7N19E2GgXpOWMLHvF7JDc+Yhtj2UPNXJpgsKuJB/RWFyalXNv6y6ausdE8xJoIF4KwJpHjSRVne1X1XswCexS9iWINl8Y9YokJVwT9DTHZUT/ke9lJqruALaTfm0OcG25tWkaGB1NiSTiFZHhbcZA0tUp4/X/Ga7oO5xM3Z5HPxWygr77B8SuTRokLt0cKd9Kcmz8HIALrwOXtYdrZRPpWIxa2B1pkWjlj9GbeAhEOSDwp12dLr3hIuZuEUubm4VLVOdz0oa70HUdX3o58GKiWbAC4gXbpPesuyARXKv8QFqw79+Jk6Wm+aT4HhSAD0RljXnfseC1g= 42 | on: 43 | tags: true 44 | 45 | # ugh, there is some latency in pypi, sleep.... 46 | after_deploy: 47 | - sleep 300 48 | - found=1; while [ $found -eq 1 ]; do sleep 5; x=$(curl -s https://pypi.org/simple/cicdstatemgr/ 2>&1 | grep $TRAVIS_TAG); found=$?; echo "found? $found"; done 49 | - docker build --build-arg GIT_TAG=$TRAVIS_TAG -t bitsofinfo/cicdstatemgr:$TRAVIS_TAG . 50 | - echo "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USER" --password-stdin 51 | - docker push bitsofinfo/cicdstatemgr:$TRAVIS_TAG 52 | - docker tag bitsofinfo/cicdstatemgr:$TRAVIS_TAG bitsofinfo/cicdstatemgr:latest 53 | - docker push bitsofinfo/cicdstatemgr:latest 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /examples/tekton/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd core 4 | ./install.sh 5 | 6 | echo "Sleeping 30s for tekton core resources to come up....." 7 | sleep 30 8 | 9 | cd ../pipelines 10 | ./install.sh 11 | 12 | cd .. 13 | 14 | echo 15 | echo "Waiting for services to come up " 16 | READY=1 17 | while [ "$READY" != "0" ]; do 18 | sleep 1 19 | CAPTURE=$(kubectl get pods | grep el-event-listener| grep Running) 20 | READY=$? 21 | echo -n "." 22 | done 23 | 24 | sleep 5 25 | 26 | EVENT_LISTENER_TUNNEL_URI=$(minikube service el-event-listener --url -n tekton-pipelines) 27 | DASHBOARD_TUNNEL_URI=$(minikube service tekton-dashboard --url -n tekton-pipelines) 28 | 29 | echo 30 | echo 31 | 32 | #exit 0 33 | 34 | NGROK_URL="" 35 | 36 | if [ ! -z "$EVENT_LISTENER_TUNNEL_URI" ]; then 37 | EVENT_LISTENER_TUNNEL_URI_IP_PORT="${EVENT_LISTENER_TUNNEL_URI#http://}" 38 | NGROK_CMD="ngrok http $EVENT_LISTENER_TUNNEL_URI_IP_PORT &>/dev/null" 39 | echo "Starting ngrok: cmd: $NGROK_CMD ...." 40 | eval "($NGROK_CMD) &" 41 | NGROK_PID=$! 42 | echo -n $NGROK_PID > .ngrok.pid 43 | sleep 5 44 | NGROK_URL=$(curl -s http://localhost:4040/api/tunnels | jq .tunnels[0].public_url) 45 | NGROK_URL="${NGROK_URL%\"}" 46 | NGROK_URL="${NGROK_URL#\"}" 47 | echo 48 | echo "Local EventListener: $EVENT_LISTENER_TUNNEL_URI" 49 | echo "Public NGROK EventListener: $NGROK_URL" 50 | echo "Kill with: kill -TERM $NGROK_PID" 51 | echo 52 | else 53 | echo 54 | echo "You should run the following in a separate terminal:" 55 | echo "" 56 | echo " minikube service el-event-listener-v1 --url -n tekton-pipelines" 57 | echo 58 | echo "Once up, grab the port for the el-event-listener-v1 service exposed locally and pass it to ngrok" 59 | echo 60 | echo " ngrok http -log-level \"debug\" [port]" 61 | echo "" 62 | fi 63 | 64 | if [ ! -z "$DASHBOARD_TUNNEL_URI" ]; then 65 | echo "Tekton dashboard: $DASHBOARD_TUNNEL_URI" 66 | echo 67 | else 68 | echo 69 | echo "You should run the following in a separate terminal:" 70 | echo "" 71 | echo " minikube service tekton-dashboard --url -n tekton-pipelines" 72 | fi 73 | 74 | 75 | # install the confs 76 | cd pipelines 77 | ./install-confs.sh $NGROK_URL $DASHBOARD_TUNNEL_URI 78 | cd .. 79 | 80 | echo 81 | echo "Finally go to your fork of https://github.com/bitsofinfo/nginx-hello-world and go to Setting > Webhooks" 82 | echo "and add new new webhook with the following settings:" 83 | echo 84 | echo " Payload URL = $NGROK_URL" 85 | echo " Context type = application/json" 86 | echo " Secret = 123" 87 | echo "" 88 | echo "Next you can trigger a build by pushing a tag from within your nginx-hello-world fork" 89 | echo " git tag -a [tag] -m \"test\"; git push origin [tag]" 90 | echo "" 91 | echo "After pushing a new tag, go to your tekton-dashboard to view whats going on" 92 | echo "" 93 | echo "Slack notifications are emitted to the bitsofinfo.slack.com #cicdstatemgr-example channel:" 94 | echo " https://app.slack.com/client/TE2KJDF4L/CE46X7NQN" 95 | echo 96 | echo "Your tag of nginx-hello-world should be deployed, lets hit it:" 97 | echo " minikube service list" 98 | echo " minikube service nginx-hello-world-[tag] --url -n apps-dev" 99 | echo "" 100 | echo " curl http://localhost:[port]" 101 | echo " You should see 'nginx-hello-world:[tag]] is running within environment: apps-dev'" 102 | echo 103 | echo 104 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/build/tasks/build.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: tekton.dev/v1beta1 4 | kind: Task 5 | metadata: 6 | name: build 7 | spec: 8 | 9 | workspaces: 10 | 11 | - name: git-source 12 | description: The workspace where the app source code resides 13 | mountPath: /workspace/git-source 14 | readOnly: false 15 | 16 | - name: cicdstatemgr-configs 17 | description: The workspace where cicdstatemgr configs reside 18 | mountPath: /workspace/cicdstatemgr-configs 19 | readOnly: true 20 | 21 | - name: cicdstatemgr-secrets 22 | description: The workspace where cicdstatemgr secrets reside 23 | mountPath: /workspace/cicdstatemgr-secrets 24 | readOnly: true 25 | 26 | results: 27 | - name: last-exit-code 28 | description: The last exit code of this task 29 | 30 | params: 31 | - name: cicdContextDataId 32 | type: string 33 | 34 | - name: pipelineRunUid 35 | type: string 36 | description: the pipelineRun uid 37 | default: NONE 38 | 39 | steps: 40 | 41 | #---------------------------------- 42 | # STEP load CICD context data 43 | #---------------------------------- 44 | - name: load-cicd-context-data 45 | image: bitsofinfo/cicdstatemgr:1.2.4 46 | command: 47 | - /bin/bash 48 | args: 49 | - -c 50 | - | 51 | cicdstatemgr \ 52 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 53 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 54 | --id $(inputs.params.cicdContextDataId) \ 55 | --load 56 | echo 57 | 58 | echo 59 | echo "------ /tekton/results/cicdContextDataJson ------" 60 | cat /tekton/results/cicdContextDataJson 61 | echo 62 | echo 63 | 64 | echo 65 | echo "------ /tekton/results/cicdContextDataYaml ------" 66 | cat /tekton/results/cicdContextDataYaml 67 | echo 68 | echo 69 | 70 | echo 71 | echo "------ /tekton/results/cicdContextDataShell ------" 72 | cat /tekton/results/cicdContextDataShell 73 | echo 74 | echo 75 | 76 | echo 77 | echo "------ /tekton/results/cicdContextDataId ------" 78 | cat /tekton/results/cicdContextDataId 79 | echo 80 | echo 81 | 82 | #---------------------------------- 83 | # STEP kaniko build and push 84 | #---------------------------------- 85 | - name: build-and-push 86 | image: gcr.io/kaniko-project/executor:v1.6.0-debug 87 | command: 88 | - /busybox/sh 89 | args: 90 | - -c 91 | - | 92 | source /tekton/results/cicdContextDataShell 93 | 94 | IMAGE_TAG_TO_PUSH="registry.kube-system.svc.cluster.local/apps/$CICD_state__gitAppName:$CICD_state__appTag" 95 | 96 | echo "Attempting to build and push $IMAGE_TAG_TO_PUSH via Kaniko" 97 | echo 98 | 99 | ls -al /workspace/git-source 100 | 101 | echo 102 | echo "----- /workspace/git-source/index.html.tmpl -----" 103 | cat /workspace/git-source/Dockerfile 104 | echo 105 | 106 | /kaniko/executor \ 107 | --insecure-registry cicd-cache-registry.tekton-pipelines:80 \ 108 | --skip-tls-verify-registry cicd-cache-registry.tekton-pipelines:80 \ 109 | --insecure \ 110 | --cache=true \ 111 | --cache-repo=cicd-cache-registry.tekton-pipelines:80/kaniko-cache \ 112 | --dockerfile=Dockerfile \ 113 | --destination=$IMAGE_TAG_TO_PUSH \ 114 | --context=/workspace/git-source \ 115 | --build-arg=IMAGE_VERSION=$CICD_state__appTag 116 | 117 | LAST_EXIT_CODE=$? 118 | echo 119 | if [ "$LAST_EXIT_CODE" = "1" ]; then echo "KANIKO BUILD FAILED"; else echo "KANIKO BUILD SUCCEEDED" ; fi 120 | echo -n $LAST_EXIT_CODE > /tekton/results/last-exit-code 121 | -------------------------------------------------------------------------------- /examples/tekton/core/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" 4 | 5 | # start (k8s 1.16.x due to minikube dynamic PVC issues) 6 | minikube start --kubernetes-version v1.21.2 --insecure-registry "10.0.0.0/8" --driver=hyperkit 7 | minikube addons enable registry 8 | 9 | # https://github.com/kameshsampath/minikube-helpers/tree/master/registry 10 | # hacking to get push/pull against minikube registry to work 11 | git clone https://github.com/kameshsampath/minikube-helpers.git $SCRIPTPATH/minikube-helpers 12 | 13 | kubectl apply -n kube-system \ 14 | -f $SCRIPTPATH/registry-aliases-config.yaml \ 15 | -f $SCRIPTPATH/minikube-helpers/registry/node-etc-hosts-update.yaml \ 16 | -f $SCRIPTPATH/minikube-helpers/registry/patch-coredns-job.yaml 17 | 18 | # switch to context 19 | kubectl config use-context minikube 20 | 21 | # ensure namespace 22 | kubectl create namespace tekton-pipelines 23 | 24 | # force default NS to tekton-pipelines for this context 25 | kubectl config set-context --current --namespace=tekton-pipelines 26 | 27 | # install pipelines 28 | kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/previous/v0.26.0/release.yaml 29 | 30 | # install pipelines customizations (configmaps) 31 | # - feature-flags 32 | # - config-artifact-pvc (disk size) 33 | kubectl apply -f $SCRIPTPATH/tekton-pipelines-mods.yaml 34 | 35 | # install triggers 36 | kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/previous/v0.16.1/release.yaml 37 | kubectl apply -f https://storage.googleapis.com/tekton-releases/triggers/previous/v0.16.1/interceptors.yaml 38 | 39 | # install dashboard 40 | kubectl apply --filename https://github.com/tektoncd/dashboard/releases/download/v0.18.1/tekton-dashboard-release.yaml 41 | 42 | # NodePort change 43 | kubectl apply -f $SCRIPTPATH/tekton-dashboard-mods.yaml 44 | 45 | # install git clone task and apply it 46 | # https://github.com/tektoncd/catalog/issues/531 47 | git clone https://github.com/tektoncd/catalog.git $SCRIPTPATH/catalog 48 | CDIR=`pwd`; cd $SCRIPTPATH/catalog/; git checkout 5250f30; cd $CDIR 49 | kubectl apply -f $SCRIPTPATH/catalog/task/git-clone/0.1/git-clone.yaml -n tekton-pipelines 50 | 51 | # RBAC perms for the tekton-triggers-admin service account 52 | kubectl apply -f $SCRIPTPATH/tekton-triggers-rbac.yaml 53 | 54 | # Custom Tekton trigger interceptor that converts Slack's payload=[urlencodeddata] 55 | # into plain JSON before handing off to our EventListener 56 | # https://github.com/bitsofinfo/slack-payload-handler 57 | kubectl apply -f $SCRIPTPATH/slack-payload-handler.yaml 58 | 59 | # These are the k8s serviceaccounts that the various PipelineRuns execute as 60 | # when triggered by triggers 61 | kubectl apply -f $SCRIPTPATH/tekton-service-accounts.yaml 62 | 63 | # The redis instance that is used as the primary datastore for cicdstatemgr 64 | # https://github.com/bitsofinfo/cicdstatemgr 65 | kubectl apply -f $SCRIPTPATH/redis.yaml 66 | 67 | # a Docker registry instance that resides in the cluster that Kaniko uses 68 | # for its cache. https://github.com/GoogleContainerTools/kaniko 69 | kubectl apply -f $SCRIPTPATH/image-registry-cache.yaml 70 | \ 71 | # Trivy server 72 | kubectl apply -f $SCRIPTPATH/trivy-server.yaml 73 | 74 | # Create secrets for slack oauth token (i.e to verify inbound slack actions) 75 | cat secrets/slack-oauth-token | tr -d '\n' > secrets/slack-oauth-token.tmp; mv secrets/slack-oauth-token.tmp secrets/slack-oauth-token 76 | kubectl create secret generic slack-oauth-token --from-file=slack-oauth-token=secrets/slack-oauth-token 77 | 78 | # setup our namespaces for where the apps will be deployed 79 | # separate ones for dev/prod 80 | kubectl create namespace apps-dev 81 | kubectl create namespace apps-prod 82 | 83 | kubectl create clusterrolebinding cicd-tekton-dev \ 84 | --serviceaccount=tekton-pipelines:cicd-tekton \ 85 | --clusterrole=cluster-admin \ 86 | --namespace=apps-dev 87 | 88 | kubectl create clusterrolebinding cicd-tekton-prod \ 89 | --serviceaccount=tekton-pipelines:cicd-tekton \ 90 | --clusterrole=cluster-admin \ 91 | --namespace=apps-prod 92 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/build/triggers.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | apiVersion: triggers.tekton.dev/v1alpha1 5 | kind: TriggerTemplate 6 | metadata: 7 | name: build 8 | annotations: 9 | # https://tektoncd.slack.com/archives/CKUSJ2A5D/p1606222895139800 10 | # https://github.com/tektoncd/triggers/pull/842 11 | triggers.tekton.dev/old-escape-quotes: "true" 12 | spec: 13 | params: 14 | - name: cicdContextName 15 | - name: cicdContextDataId 16 | - name: gitAppName 17 | - name: appTag 18 | - name: originalRequestBody 19 | - name: gitCloneUrl 20 | 21 | # for security checks 22 | - name: cicdContextName 23 | - name: invokerUid 24 | - name: relatedCicdContextDataId 25 | - name: relatedPipelineRunUid 26 | - name: relatedPipelineName 27 | - name: triggeredBySystem 28 | - name: triggeredByDescription 29 | 30 | resourcetemplates: 31 | - apiVersion: tekton.dev/v1beta1 32 | kind: PipelineRun 33 | metadata: 34 | generateName: build-$(tt.params.cicdContextName)-$(tt.params.gitAppName)- 35 | spec: 36 | serviceAccountName: cicd-tekton 37 | 38 | pipelineRef: 39 | name: build 40 | 41 | params: 42 | - name: cicdContextDataId 43 | value: $(tt.params.cicdContextDataId) 44 | 45 | - name: gitAppName 46 | value: $(tt.params.gitAppName) 47 | 48 | - name: appTag 49 | value: $(tt.params.appTag) 50 | 51 | - name: originalRequestBody 52 | value: $(tt.params.originalRequestBody) 53 | 54 | - name: gitCloneUrl 55 | value: $(tt.params.gitCloneUrl) 56 | 57 | # for security checks 58 | - name: cicdContextName 59 | value: $(tt.params.cicdContextName) 60 | - name: invokerUid 61 | value: $(tt.params.invokerUid) 62 | - name: relatedCicdContextDataId 63 | value: $(tt.params.relatedCicdContextDataId) 64 | - name: relatedPipelineRunUid 65 | value: $(tt.params.relatedPipelineRunUid) 66 | - name: relatedPipelineName 67 | value: $(tt.params.relatedPipelineName) 68 | - name: triggeredBySystem 69 | value: $(tt.params.triggeredBySystem) 70 | - name: triggeredByDescription 71 | value: $(tt.params.triggeredByDescription) 72 | 73 | workspaces: 74 | 75 | - name: cicdstatemgr-secrets 76 | secret: 77 | secretName: cicdstatemgr-secrets 78 | items: 79 | - key: cicdstatemgr-secrets.yaml 80 | path: cicdstatemgr-secrets.yaml 81 | 82 | - name: cicdstatemgr-configs 83 | configmap: 84 | name: cicdstatemgr-configs 85 | 86 | - name: scratch 87 | volumeClaimTemplate: 88 | metadata: 89 | name: scratch 90 | spec: 91 | storageClassName: standard 92 | accessModes: 93 | - ReadWriteOnce 94 | resources: 95 | requests: 96 | storage: 50Mi 97 | 98 | --- 99 | apiVersion: triggers.tekton.dev/v1alpha1 100 | kind: TriggerBinding 101 | metadata: 102 | name: build 103 | spec: 104 | params: 105 | - name: cicdContextName 106 | value: $(extensions.cicdContextName) 107 | - name: cicdContextDataId 108 | value: $(extensions.cicdContextDataId) 109 | - name: gitAppName 110 | value: $(extensions.gitAppName) 111 | - name: appTag 112 | value: $(extensions.appTag) 113 | - name: originalRequestBody 114 | value: $(body) 115 | - name: gitCloneUrl 116 | value: $(body.gitCloneUrl) 117 | 118 | # for security checks 119 | - name: invokerUid 120 | value: $(extensions.invokerUid) 121 | - name: relatedCicdContextDataId 122 | value: $(extensions.relatedCicdContextDataId) 123 | - name: relatedPipelineRunUid 124 | value: $(extensions.relatedPipelineRunUid) 125 | - name: relatedPipelineName 126 | value: $(extensions.relatedPipelineName) 127 | - name: triggeredBySystem 128 | value: $(extensions.triggeredBySystem) 129 | - name: triggeredByDescription 130 | value: $(extensions.triggeredByDescription) 131 | -------------------------------------------------------------------------------- /examples/basics/GENERATE.md: -------------------------------------------------------------------------------- 1 | # --generate 2 | 3 | *NOTE: see the functioning [scripts in the test/ directory](test/) for commands that match what is described in this doc* 4 | 5 | This demonstrates the usage of `--generate` which provides a way pre-define data to be generated into the `cicdContextData` on demand at some point in the future. A `generator` is simply a key within an app pipeline config file (like [app.yaml](app.yaml)) that contains one or more `generator configs` defined as: 6 | 7 | ``` 8 | ... 9 | generators: 10 | # an arbitrarily named "generator" which 11 | # contains one or more generator configs 12 | : 13 | 14 | # a generator config, this can be 15 | # named anything you want 16 | : 17 | 18 | # an optional 'if' condition (jinja2 tmpl) 19 | # which if yields a non blank result lets 20 | # the `set:` items to be set into the cicdContextData 21 | if: "" 22 | 23 | # the key paths and values that will be evaluated 24 | # and set into the cicdContextData if the condition 25 | # above passes (or is not specified aat all) 26 | set: 27 | - key: "path.to.targetkey (will be jinja2 evaluated)" 28 | value: "value to be set @key, (will be jinja2 evaluated)" 29 | ``` 30 | 31 | Whenever you call `--generate ` the named generator will be looked up at the given ``; for each sub-key within it (i.e. named generatorConfigs), each one will have it's optional `if` evaluated (if it exists); and then each `set` stanza will have its `key/value` evaluated and the resulting `value` will be written into the `cicdContextData` 32 | 33 | The `generators` feature can be used to pre-define possible values to be set into the `cicdContextData` at some point in the future, based on the current state in the `cicdContextData` when the `--generate ` command is invoked. 34 | 35 | The `--tmpl-ctx-var` argument can be used w/ `--generate` as well. The left hand side of the value in `--tmpl-ctx-var` is just an arbitrary KV pair that will be added to the `jinja2` context used in the parsing of templates used by `--generate`, and the right hand side of the `--tmpl-ctx-var` can either be a literal value or a `jsonpath` expression referencing any other variable in the `cicdContextData` 36 | 37 | This assumes you've already run [INIT_NEW](INIT_NEW.md) 38 | 39 | This example is using the `generators` defined in the example [app.yaml](app.yaml) 40 | 41 | Let's ensure the following value exists via `--set`: 42 | ``` 43 | cicdstatemgr \ 44 | --config config.yaml \ 45 | --secrets secrets.yaml \ 46 | --id "context-data-id-1" \ 47 | --set "state.gitTag=myapp-alpine-1.5.9" \ 48 | --set "state.gitAppName=my-application" 49 | ``` 50 | 51 | Run the generator that consumes these values: 52 | ``` 53 | cicdstatemgr \ 54 | --config config.yaml \ 55 | --secrets secrets.yaml \ 56 | --id "context-data-id-1" \ 57 | --tmpl-ctx-var "ctx.var1=v1val" \ 58 | --tmpl-ctx-var "ctx.var2=v2val" \ 59 | --generate pipelines.build.generators.dockerfile 60 | ``` 61 | 62 | Now lets `--get` the value the generator writes to: 63 | ``` 64 | cicdstatemgr \ 65 | --config config.yaml \ 66 | --secrets secrets.yaml \ 67 | --id "context-data-id-1" \ 68 | --get state.build.dockerfileInfo.generateDockerfileCmd 69 | 70 | ./gendockerfile.sh -f alpine:latest -x myapp-alpine-1.5.9 -z v1val -q v2val 71 | ``` 72 | 73 | Let's change some variables via `--set`: 74 | ``` 75 | cicdstatemgr \ 76 | --config config.yaml \ 77 | --secrets secrets.yaml \ 78 | --id "context-data-id-1" \ 79 | --set "state.gitTag=myapp-centos-1.5.9" \ 80 | --set "state.gitAppName=my-application" 81 | ``` 82 | 83 | re-run the generator that consumes these values: 84 | ``` 85 | cicdstatemgr \ 86 | --config config.yaml \ 87 | --secrets secrets.yaml \ 88 | --id "context-data-id-1" \ 89 | --tmpl-ctx-var "ctx.var1=v1val" \ 90 | --tmpl-ctx-var "ctx.var2=v2val" \ 91 | --generate pipelines.build.generators.dockerfile 92 | ``` 93 | 94 | Now lets `--get` the value the generator writes to again, note its different: 95 | ``` 96 | cicdstatemgr \ 97 | --config config.yaml \ 98 | --secrets secrets.yaml \ 99 | --id "context-data-id-1" \ 100 | --get state.build.dockerfileInfo.generateDockerfileCmd 101 | 102 | ./gendockerfile.sh -f centos:latest -x myapp-centos-1.5.9 -z v1val -q v2val 103 | ``` 104 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/test/triggers.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | apiVersion: triggers.tekton.dev/v1alpha1 5 | kind: TriggerTemplate 6 | metadata: 7 | name: test 8 | annotations: 9 | # https://tektoncd.slack.com/archives/CKUSJ2A5D/p1606222895139800 10 | # https://github.com/tektoncd/triggers/pull/842 11 | triggers.tekton.dev/old-escape-quotes: "true" 12 | spec: 13 | params: 14 | - name: cicdContextName 15 | - name: appName 16 | - name: cicdContextDataId 17 | - name: originalRequestBody 18 | 19 | # for security checks 20 | - name: cicdContextName 21 | - name: invokerUid 22 | - name: relatedCicdContextDataId 23 | - name: relatedPipelineRunUid 24 | - name: relatedPipelineName 25 | - name: triggeredBySystem 26 | - name: triggeredByDescription 27 | 28 | resourcetemplates: 29 | - apiVersion: tekton.dev/v1beta1 30 | kind: PipelineRun 31 | metadata: 32 | generateName: test-$(tt.params.cicdContextName)-$(tt.params.appName)- 33 | spec: 34 | serviceAccountName: cicd-tekton 35 | 36 | pipelineRef: 37 | name: test 38 | 39 | params: 40 | - name: cicdContextDataId 41 | value: $(tt.params.cicdContextDataId) 42 | 43 | - name: originalRequestBody 44 | value: $(tt.params.originalRequestBody) 45 | 46 | # for security checks 47 | - name: cicdContextName 48 | value: $(tt.params.cicdContextName) 49 | - name: invokerUid 50 | value: $(tt.params.invokerUid) 51 | - name: relatedCicdContextDataId 52 | value: $(tt.params.relatedCicdContextDataId) 53 | - name: relatedPipelineRunUid 54 | value: $(tt.params.relatedPipelineRunUid) 55 | - name: relatedPipelineName 56 | value: $(tt.params.relatedPipelineName) 57 | - name: triggeredBySystem 58 | value: $(tt.params.triggeredBySystem) 59 | - name: triggeredByDescription 60 | value: $(tt.params.triggeredByDescription) 61 | 62 | workspaces: 63 | 64 | - name: cicdstatemgr-secrets 65 | secret: 66 | secretName: cicdstatemgr-secrets 67 | items: 68 | - key: cicdstatemgr-secrets.yaml 69 | path: cicdstatemgr-secrets.yaml 70 | 71 | - name: cicdstatemgr-configs 72 | configmap: 73 | name: cicdstatemgr-configs 74 | 75 | --- 76 | apiVersion: triggers.tekton.dev/v1alpha1 77 | kind: TriggerBinding 78 | metadata: 79 | name: test 80 | spec: 81 | params: 82 | - name: cicdContextName 83 | value: $(extensions.cicdContextName) 84 | 85 | - name: appName 86 | value: $(extensions.appName) 87 | 88 | - name: cicdContextDataId 89 | value: $(extensions.cicdContextDataId) 90 | 91 | - name: originalRequestBody 92 | value: $(body) 93 | 94 | # for security checks 95 | - name: invokerUid 96 | value: $(extensions.invokerUid) 97 | - name: relatedCicdContextDataId 98 | value: $(extensions.relatedCicdContextDataId) 99 | - name: relatedPipelineRunUid 100 | value: $(extensions.relatedPipelineRunUid) 101 | - name: relatedPipelineName 102 | value: $(extensions.relatedPipelineName) 103 | - name: triggeredBySystem 104 | value: $(extensions.triggeredBySystem) 105 | - name: triggeredByDescription 106 | value: $(extensions.triggeredByDescription) 107 | 108 | 109 | --- 110 | apiVersion: triggers.tekton.dev/v1alpha1 111 | kind: TriggerBinding 112 | metadata: 113 | name: test-via-slack 114 | spec: 115 | params: 116 | 117 | - name: cicdContextName 118 | value: $(extensions.cicdContextName) 119 | 120 | - name: cicdContextDataId 121 | value: $(extensions.cicdContextDataId) 122 | 123 | - name: appName 124 | value: $(extensions.targetAppName) 125 | 126 | - name: originalRequestBody 127 | value: $(body) 128 | 129 | - name: gitCloneUrl 130 | value: $(extensions.gitCloneUrl) 131 | 132 | # for security checks 133 | - name: invokerUid 134 | value: $(extensions.slackUsername) 135 | - name: relatedCicdContextDataId 136 | value: $(extensions.relatedCicdContextDataId) 137 | - name: relatedPipelineRunUid 138 | value: $(extensions.relatedPipelineRunUid) 139 | - name: relatedPipelineName 140 | value: $(extensions.relatedPipelineName) 141 | - name: triggeredBySystem 142 | value: 'slack' 143 | - name: triggeredByDescription 144 | value: 'slack button press by <@$(extensions.slackUserId)>' 145 | 146 | --- 147 | -------------------------------------------------------------------------------- /cicdstatemgr/datasources/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | """ 6 | 7 | import abc 8 | import logging 9 | import importlib 10 | 11 | from .. import CicdContextData 12 | 13 | class CicdContextDataSource(metaclass=abc.ABCMeta): 14 | @classmethod 15 | def __subclasshook__(cls, subclass): 16 | return (hasattr(subclass, 'persist') and 17 | callable(subclass.persist) and 18 | hasattr(subclass, 'load') and 19 | callable(subclass.load) or 20 | NotImplemented) 21 | 22 | @abc.abstractmethod 23 | def get_name(self) -> str: 24 | raise NotImplementedError 25 | 26 | @abc.abstractmethod 27 | def is_primary(self) -> bool: 28 | raise NotImplementedError 29 | 30 | @abc.abstractmethod 31 | def is_local(self) -> bool: 32 | raise NotImplementedError 33 | 34 | @abc.abstractmethod 35 | def persist(self, cicdContextDataId:str, cicdContextData:object): 36 | raise NotImplementedError 37 | 38 | @abc.abstractmethod 39 | def load(self, cicdContextDataId:str) -> dict: 40 | raise NotImplementedError 41 | 42 | @abc.abstractmethod 43 | def supports_load(self) -> bool: 44 | raise NotImplementedError 45 | 46 | 47 | class DataSourceMgr(): 48 | 49 | dataSources:dict = {} 50 | primaryDataSource:CicdContextDataSource = None 51 | 52 | def __init__(self, dsConfigs:dict): 53 | for dsName in dsConfigs: 54 | logging.debug("DataSourceMgr() Initializing datasource: {}".format(dsName)) 55 | 56 | try: 57 | module = importlib.import_module('.{}'.format(dsName), package=__name__) 58 | ds = module.DataSource(dsConfigs[dsName]) 59 | self.dataSources[dsName] = ds 60 | if ds.is_primary(): 61 | if self.primaryDataSource: 62 | logging.error("DataSourceMgr() you cannot have more than one isPrimary data source defined: {} , current primary = {}".format(dsName,self.primaryDataSource.get_name())) 63 | else: 64 | logging.info("DataSourceMgr() primary ds = {}".format(dsName)) 65 | self.primaryDataSource = ds 66 | 67 | except ModuleNotFoundError as e: 68 | logging.error("No datasource module found by name: {} .. ignoring".format(dsName)) 69 | 70 | if not self.primaryDataSource: 71 | raise Exception("DataSourceMgr() you have no isPrimary=True datasource configured! Invalid") 72 | 73 | def persist(self, cicdContextData:CicdContextData, skipPrimary=False): 74 | logging.debug("DataSourceMgr.persist() skipPrimary={} cicdContextDataId={}".format(skipPrimary,cicdContextData.getCicdContextDataId())) 75 | 76 | for dsName in self.dataSources: 77 | ds = self.dataSources[dsName] 78 | if skipPrimary and ds.is_primary(): 79 | logging.debug("DataSourceMgr.persist() skipping primary: {} as skipPrimary={}".format(dsName,skipPrimary)) 80 | continue 81 | 82 | logging.debug("DataSourceMgr.persist() persisting in: {}".format(dsName)) 83 | ds.persist(cicdContextData.getCicdContextDataId(),cicdContextData.getCicdContextData()) 84 | 85 | def load(self, cicdContextDataId:str, fromPrimary=True, fromLocal=False): 86 | logging.debug("DataSourceMgr.load() fromPrimary={} fromLocal={} cicdContextDataId={}".format(fromPrimary,fromLocal,cicdContextDataId)) 87 | 88 | if fromPrimary and self.primaryDataSource.is_primary(): 89 | 90 | if fromLocal and not self.primaryDataSource.is_local(): 91 | raise Exception("DataSourceMgr.load() cannot pass " + \ 92 | "fromPrimary={} and fromLocal={} as the primaryDataSource[{}] is_local()=false".format(fromPrimary,fromLocal,self.primaryDataSource.get_name())) 93 | 94 | return self.primaryDataSource.load(cicdContextDataId) 95 | 96 | else: 97 | for dsName in self.dataSources: 98 | ds = self.dataSources[dsName] 99 | if not ds.is_primary() and ds.supports_load(): 100 | if fromLocal and ds.is_local(): 101 | logging.debug("DataSourceMgr.load() loading from {}".format(dsName)) 102 | return ds.load(cicdContextDataId) 103 | 104 | raise Exception("DataSourceMgr.load() cannot pass " + \ 105 | "fromPrimary={} and fromLocal={}, no datasources meet this criteria".format(fromPrimary,fromLocal)) 106 | 107 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/validate/triggers.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | apiVersion: triggers.tekton.dev/v1alpha1 5 | kind: TriggerTemplate 6 | metadata: 7 | name: validate 8 | annotations: 9 | # https://tektoncd.slack.com/archives/CKUSJ2A5D/p1606222895139800 10 | # https://github.com/tektoncd/triggers/pull/842 11 | triggers.tekton.dev/old-escape-quotes: "true" 12 | spec: 13 | params: 14 | - name: cicdContextName 15 | - name: cicdContextDataId 16 | - name: appName 17 | - name: originalRequestBody 18 | 19 | # for security checks 20 | - name: cicdContextName 21 | - name: invokerUid 22 | - name: relatedCicdContextDataId 23 | - name: relatedPipelineRunUid 24 | - name: relatedPipelineName 25 | - name: triggeredBySystem 26 | - name: triggeredByDescription 27 | 28 | resourcetemplates: 29 | - apiVersion: tekton.dev/v1beta1 30 | kind: PipelineRun 31 | metadata: 32 | generateName: validate-$(tt.params.cicdContextName)-$(tt.params.appName)- 33 | spec: 34 | serviceAccountName: cicd-tekton 35 | 36 | pipelineRef: 37 | name: validate 38 | 39 | workspaces: 40 | 41 | - name: cicdstatemgr-secrets 42 | secret: 43 | secretName: cicdstatemgr-secrets 44 | items: 45 | - key: cicdstatemgr-secrets.yaml 46 | path: cicdstatemgr-secrets.yaml 47 | 48 | - name: cicdstatemgr-configs 49 | configmap: 50 | name: cicdstatemgr-configs 51 | 52 | 53 | params: 54 | - name: cicdContextDataId 55 | value: $(tt.params.cicdContextDataId) 56 | 57 | - name: originalRequestBody 58 | value: $(tt.params.originalRequestBody) 59 | 60 | # for security checks 61 | - name: cicdContextName 62 | value: $(tt.params.cicdContextName) 63 | - name: invokerUid 64 | value: $(tt.params.invokerUid) 65 | - name: relatedCicdContextDataId 66 | value: $(tt.params.relatedCicdContextDataId) 67 | - name: relatedPipelineRunUid 68 | value: $(tt.params.relatedPipelineRunUid) 69 | - name: relatedPipelineName 70 | value: $(tt.params.relatedPipelineName) 71 | - name: triggeredBySystem 72 | value: $(tt.params.triggeredBySystem) 73 | - name: triggeredByDescription 74 | value: $(tt.params.triggeredByDescription) 75 | 76 | --- 77 | apiVersion: triggers.tekton.dev/v1alpha1 78 | kind: TriggerBinding 79 | metadata: 80 | name: validate 81 | spec: 82 | params: 83 | 84 | - name: cicdContextName 85 | value: $(extensions.cicdContextName) 86 | 87 | - name: appName 88 | value: $(extensions.appName) 89 | 90 | - name: cicdContextDataId 91 | value: $(extensions.cicdContextDataId) 92 | 93 | - name: originalRequestBody 94 | value: $(body) 95 | 96 | # for security checks 97 | - name: invokerUid 98 | value: $(extensions.invokerUid) 99 | - name: relatedCicdContextDataId 100 | value: $(extensions.relatedCicdContextDataId) 101 | - name: relatedPipelineRunUid 102 | value: $(extensions.relatedPipelineRunUid) 103 | - name: relatedPipelineName 104 | value: $(extensions.relatedPipelineName) 105 | - name: triggeredBySystem 106 | value: $(extensions.triggeredBySystem) 107 | - name: triggeredByDescription 108 | value: $(extensions.triggeredByDescription) 109 | 110 | --- 111 | apiVersion: triggers.tekton.dev/v1alpha1 112 | kind: TriggerBinding 113 | metadata: 114 | name: validate-via-slack 115 | spec: 116 | params: 117 | 118 | - name: cicdContextName 119 | value: $(extensions.cicdContextName) 120 | 121 | - name: cicdContextDataId 122 | value: $(extensions.cicdContextDataId) 123 | 124 | - name: appName 125 | value: $(extensions.targetAppName) 126 | 127 | - name: originalRequestBody 128 | value: $(body) 129 | 130 | - name: gitCloneUrl 131 | value: $(extensions.gitCloneUrl) 132 | 133 | # for security checks 134 | - name: invokerUid 135 | value: $(extensions.slackUsername) 136 | - name: relatedCicdContextDataId 137 | value: $(extensions.relatedCicdContextDataId) 138 | - name: relatedPipelineRunUid 139 | value: $(extensions.relatedPipelineRunUid) 140 | - name: relatedPipelineName 141 | value: $(extensions.relatedPipelineName) 142 | - name: triggeredBySystem 143 | value: 'slack' 144 | - name: triggeredByDescription 145 | value: 'slack button press by <@$(extensions.slackUserId)>' 146 | 147 | --- 148 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/validate/tasks/trivy-scan.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: tekton.dev/v1beta1 3 | kind: Task 4 | metadata: 5 | name: trivy-scan 6 | spec: 7 | 8 | workspaces: 9 | 10 | - name: cicdstatemgr-configs 11 | description: The workspace where cicdstatemgr configs reside 12 | mountPath: /workspace/cicdstatemgr-configs 13 | readOnly: true 14 | 15 | - name: cicdstatemgr-secrets 16 | description: The workspace where cicdstatemgr secrets reside 17 | mountPath: /workspace/cicdstatemgr-secrets 18 | readOnly: true 19 | 20 | params: 21 | 22 | - name: pipelineRunUid 23 | type: string 24 | description: the pipelineRun uid 25 | default: NONE 26 | 27 | - name: cicdContextDataId 28 | type: string 29 | 30 | results: 31 | - name: last-exit-code 32 | description: The last exit code of this task 33 | 34 | steps: 35 | 36 | #---------------------------------- 37 | # 38 | # STEP Run Trivy client 39 | # 40 | #---------------------------------- 41 | 42 | #---------------------------------- 43 | # STEP load CICD context data 44 | #---------------------------------- 45 | - name: load-cicd-context-data 46 | image: bitsofinfo/cicdstatemgr:1.2.4 47 | command: 48 | - /bin/bash 49 | args: 50 | - -c 51 | - | 52 | cicdstatemgr \ 53 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 54 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 55 | --id $(inputs.params.cicdContextDataId) \ 56 | --load 57 | echo 58 | 59 | cat /tekton/results/cicdContextDataId 60 | echo 61 | echo 62 | 63 | cat /tekton/results/cicdContextDataJson 64 | echo 65 | echo 66 | 67 | - name: trivy-scan 68 | image: docker.io/aquasec/trivy:0.19.2 69 | command: 70 | - /bin/ash 71 | args: 72 | - -c 73 | - | 74 | 75 | source /tekton/results/cicdContextDataShell 76 | 77 | IMAGE_TO_SCAN="registry.kube-system.svc.cluster.local/apps/$CICD_state__gitAppName:$CICD_state__appTag" 78 | 79 | echo "Evaluating $IMAGE_TO_SCAN" 80 | echo 81 | 82 | TRIVY_RESULTS=$(/usr/local/bin/trivy \ 83 | client \ 84 | --remote http://cicd-trivy-server.tekton-pipelines:80 \ 85 | --exit-code 0 \ 86 | --severity MEDIUM,HIGH,CRITICAL \ 87 | $IMAGE_TO_SCAN) 88 | 89 | LAST_EXIT_CODE=$? 90 | echo -n $LAST_EXIT_CODE > /tekton/results/last-exit-code 91 | echo 92 | if [ "$LAST_EXIT_CODE" = "1" ]; then echo "TRIVY SCAN FAILED"; else echo "TRIVY SCAN SUCCESSFUL" ; fi 93 | 94 | echo "$TRIVY_RESULTS" > /tekton/results/trivy-scan.results 95 | echo 96 | echo "$TRIVY_RESULTS" | head -n 4 > /tekton/results/trivy-scan.results.top 97 | echo 98 | 99 | cat /tekton/results/trivy-scan.results 100 | 101 | #---------------------------------- 102 | # STEP update CICD context data 103 | # w/ scan results 104 | #---------------------------------- 105 | - name: update-cicd-context-data 106 | image: bitsofinfo/cicdstatemgr:1.2.4 107 | command: 108 | - /bin/bash 109 | args: 110 | - -c 111 | - | 112 | 113 | source /tekton/results/cicdContextDataShell 114 | 115 | TRIVY_EXIT_CODE=$(cat /tekton/results/last-exit-code) 116 | 117 | if [ "$TRIVY_EXIT_CODE" == "0" ]; then 118 | 119 | echo "Updating cicd-context-data with latest state.validate.$(inputs.params.pipelineRunUid).trivy.scanResults from /tekton/results/trivy-scan.results" 120 | echo 121 | 122 | cicdstatemgr \ 123 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 124 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 125 | --id $(inputs.params.cicdContextDataId) \ 126 | --pre-set-force-reload \ 127 | --set state.validate.$(inputs.params.pipelineRunUid).trivy.scanResults=file:///tekton/results/trivy-scan.results.top 128 | 129 | cat /tekton/results/cicdContextDataId 130 | echo 131 | echo 132 | 133 | cat /tekton/results/cicdContextDataJson 134 | echo 135 | echo 136 | 137 | else 138 | echo "TRIVY_EXIT_CODE was $TRIVY_EXIT_CODE, skipping update-cicd-context-data" 139 | fi 140 | -------------------------------------------------------------------------------- /examples/basics/GET.md: -------------------------------------------------------------------------------- 1 | # --get 2 | 3 | *NOTE: see the functioning [scripts in the test/ directory](test/) for commands that match what is described in this doc* 4 | 5 | This demonstrates the usage of `--get` which provides a way to fetch any value from the `cicdContextData`. The key feature of `--get` is that the raw value will be evaluated as a `jinja2` template. This permits you to define templates in your `cicdContextData` that refer to any variables, including short lived variables that you can define via the `--tmpl-ctx-data` attribute that will be made available in the `jinja2` context when the `--get` rendering occurs. 6 | 7 | Its important to note that the you can still `--get` any literal value, the values can optionally contain `jinja2` syntax. 8 | 9 | This assumes you've already run [INIT_NEW](INIT_NEW.md) 10 | 11 | Whenever you call `--get` the `cicdContextData` that is loaded will be fetched from any non-primary stores first (i.e. disk files) and then only fetched from a primary store as a last resort if data does not exist in non-primary stores already. See [config.yaml](config.yaml) 12 | 13 | Let's ensure the following value exists via `--set`: 14 | ``` 15 | cicdstatemgr \ 16 | --config config.yaml \ 17 | --secrets secrets.yaml \ 18 | --id "context-data-id-1" \ 19 | --set "state.key1=value1" 20 | ``` 21 | 22 | Get the value: 23 | ``` 24 | cicdstatemgr \ 25 | --config config.yaml \ 26 | --secrets secrets.yaml \ 27 | --id "context-data-id-1" \ 28 | --get "state.key1" 29 | 30 | value1 31 | ``` 32 | 33 | Lets create a little template in our `state` called `templateTest` 34 | ``` 35 | cicdstatemgr \ 36 | --config config.yaml \ 37 | --secrets secrets.yaml \ 38 | --id "context-data-id-1" \ 39 | --set 'state.templateTest={{tmplctx.prop1}}' 40 | ``` 41 | 42 | Lets use `--get`'s functionality to render a template on a `--get` call by passing one or more `--tmpl-ctx-var` that the template references. The left hand side of the value in `--tmpl-ctx-var` is just an arbitrary KV pair that will be added to the `jinja2` context used in the parsing of the value retrieved by `--get`, and the right hand side of the `--tmpl-ctx-var` can either be a literal value or a `jsonpath` expression referencing any other variable in the `cicdContextData`: 43 | ``` 44 | cicdstatemgr \ 45 | --config config.yaml \ 46 | --secrets secrets.yaml \ 47 | --id "context-data-id-1" \ 48 | --get "state.templateTest" \ 49 | --tmpl-ctx-var tmplctx.prop1=state.key1 50 | 51 | value1 52 | ``` 53 | 54 | Ok, lets change the value referenced in the `state.templateTest` value: 55 | ``` 56 | cicdstatemgr \ 57 | --config config.yaml \ 58 | --secrets secrets.yaml \ 59 | --id "context-data-id-1" \ 60 | --set "state.key1=yet a new value" 61 | ``` 62 | 63 | Lets `--get` the template again: 64 | ``` 65 | cicdstatemgr \ 66 | --config config.yaml \ 67 | --secrets secrets.yaml \ 68 | --id "context-data-id-1" \ 69 | --get "state.templateTest" \ 70 | --tmpl-ctx-var tmplctx.prop1=state.key1 71 | 72 | yet a new value 73 | ``` 74 | 75 | We can also just use `--get` in combination with `--tmpl-ctx-var` where the right hand side of `--tmpl-ctx-var` is just a string literal: 76 | ``` 77 | cicdstatemgr \ 78 | --config config.yaml \ 79 | --secrets secrets.yaml \ 80 | --id "context-data-id-1" \ 81 | --get "state.templateTest" \ 82 | --tmpl-ctx-var tmplctx.prop1=just-a-literal-value 83 | 84 | just-a-literal-value 85 | ``` 86 | 87 | If we `--get` the templated value, but without the `--tmpl-ctx-data`, we will get a normal `jinja2` error 88 | ``` 89 | cicdstatemgr \ 90 | --config config.yaml \ 91 | --secrets secrets.yaml \ 92 | --id "context-data-id-1" \ 93 | --get "state.templateTest" 94 | 95 | ... 96 | 2020-08-02 20:23:16,083 - root - DEBUG - get_value() attempting to get: state.templateTest 97 | 2020-08-02 20:23:16,104 - root - ERROR - parse_template() template = {{tmplctx.prop1}} error: (, UndefinedError("'tmplctx' is undefined")) 98 | 99 | ``` 100 | 101 | 102 | And as always, you can just use `--get` to just get a value where the raw value is not a jinja2 template: 103 | ``` 104 | cicdstatemgr \ 105 | --config config.yaml \ 106 | --secrets secrets.yaml \ 107 | --id "context-data-id-1" \ 108 | --get "state.key1" 109 | 110 | yet a new value 111 | ``` 112 | 113 | You can also just assign it to a variable: 114 | ``` 115 | FETCHED_VALUE=$(cicdstatemgr --config config.yaml --secrets secrets.yaml --id "context-data-id-1" --get "state.key1") 116 | 117 | echo $FETCHED_VALUE 118 | 119 | yet a new value 120 | ``` -------------------------------------------------------------------------------- /examples/tekton/pipelines/deploy/tasks/deploy.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: tekton.dev/v1beta1 3 | kind: Task 4 | metadata: 5 | name: deploy 6 | spec: 7 | 8 | params: 9 | 10 | - name: pipelineRunUid 11 | type: string 12 | description: the pipelineRun uid 13 | default: NONE 14 | 15 | - name: cicdContextDataId 16 | type: string 17 | 18 | - name: classifier 19 | type: string 20 | default: "NO_CLASSIFIER" 21 | 22 | - name: targetAppTag # app version 23 | type: string 24 | 25 | workspaces: 26 | - name: git-source 27 | description: The workspace where the app source code resides 28 | mountPath: /workspace/git-source 29 | readOnly: false 30 | 31 | - name: cicdstatemgr-configs 32 | description: The workspace where cicdstatemgr configs reside 33 | mountPath: /workspace/cicdstatemgr-configs 34 | readOnly: true 35 | 36 | - name: cicdstatemgr-secrets 37 | description: The workspace where cicdstatemgr secrets reside 38 | mountPath: /workspace/cicdstatemgr-secrets 39 | readOnly: true 40 | 41 | 42 | results: 43 | - name: last-exit-code 44 | description: The last exit code of this task 45 | 46 | steps: 47 | 48 | #---------------------------------- 49 | # STEP load CICD context data 50 | #---------------------------------- 51 | - name: load-cicd-context-data 52 | image: bitsofinfo/cicdstatemgr:1.2.4 53 | command: 54 | - /bin/bash 55 | args: 56 | - -c 57 | - | 58 | cicdstatemgr \ 59 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 60 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 61 | --id $(inputs.params.cicdContextDataId) \ 62 | --load 63 | echo 64 | 65 | cat /tekton/results/cicdContextDataId 66 | echo 67 | echo 68 | 69 | cat /tekton/results/cicdContextDataJson 70 | echo 71 | echo 72 | 73 | #---------------------------------- 74 | # STEP Generate deployment.yaml 75 | #---------------------------------- 76 | - name: generate-deployment-yaml 77 | image: bitsofinfo/cicdstatemgr:1.2.4 78 | command: 79 | - /bin/bash 80 | args: 81 | - -c 82 | - | 83 | source /tekton/results/cicdContextDataShell 84 | 85 | DEPLOYMENT_NAME=$(echo "nginx-hello-world-$CICD_state__appTag" | sed 's/\./-/g') 86 | TARGET_NAMESPACE=$CICD_pipelines__deploy__deployTask__targetNamespace 87 | IMAGE_NAME="registry.kube-system.svc.cluster.local/apps/nginx-hello-world:$CICD_state__appTag" 88 | 89 | 90 | echo $DEPLOYMENT_NAME 91 | echo $TARGET_NAMESPACE 92 | echo $IMAGE_NAME 93 | echo 94 | 95 | # create the actual deployment manifest using --get's rendering functionality 96 | cicdstatemgr \ 97 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 98 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 99 | --id $(inputs.params.cicdContextDataId) \ 100 | --get pipelines.deploy.deployTask.deploymentYamlTemplate \ 101 | --tmpl-ctx-var tmplctx.deploymentName=$DEPLOYMENT_NAME \ 102 | --tmpl-ctx-var tmplctx.imageName=$IMAGE_NAME \ 103 | --tmpl-ctx-var tmplctx.targetNamespace=$TARGET_NAMESPACE \ 104 | > /tekton/results/generated-deployment.yaml 105 | 106 | echo 107 | echo "---------- GENERATED: pipelines.deploy.deployTask.deploymentYamlTemplate > /tekton/results/generated-deployment.yaml ---------" 108 | cat /tekton/results/generated-deployment.yaml 109 | echo 110 | 111 | 112 | 113 | #---------------------------------- 114 | # STEP deploy 115 | #---------------------------------- 116 | - name: deploy 117 | image: bitsofinfo/cicdstatemgr:1.2.4 118 | command: 119 | - /bin/bash 120 | args: 121 | - -c 122 | - | 123 | 124 | source /tekton/results/cicdContextDataShell 125 | 126 | DEPLOYMENT_NAME=$(echo "nginx-hello-world-$CICD_state__appTag" | sed 's/\./-/g') 127 | TARGET_NAMESPACE=$CICD_pipelines__deploy__deployTask__targetNamespace 128 | IMAGE_NAME="registry.kube-system.svc.cluster.local/apps/nginx-hello-world:$CICD_state__appTag" 129 | 130 | echo $DEPLOYMENT_NAME 131 | echo $TARGET_NAMESPACE 132 | echo $IMAGE_NAME 133 | echo 134 | 135 | echo 136 | echo "------ kubectly apply -f /tekton/results/generated-deployment.yaml -n $TARGET_NAMESPACE ------" 137 | cat /tekton/results/generated-deployment.yaml 138 | echo 139 | 140 | curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl 141 | 142 | chmod +x ./kubectl 143 | 144 | ./kubectl apply -f /tekton/results/generated-deployment.yaml -n $TARGET_NAMESPACE 145 | 146 | DEPLOY_EXIT_CODE=$? 147 | 148 | echo -n $DEPLOY_EXIT_CODE > /tekton/results/last-exit-code 149 | echo 150 | if [ "$DEPLOY_EXIT_CODE" != "0" ]; then echo "DEPLOY FAILED exit-code=$DEPLOY_EXIT_CODE"; else echo "DEPLOY SUCCEEDED" ; fi 151 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/validate/pipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: tekton.dev/v1beta1 4 | kind: Pipeline 5 | metadata: 6 | name: validate 7 | 8 | spec: 9 | params: 10 | - name: cicdContextDataId 11 | type: string 12 | - name: originalRequestBody 13 | type: string 14 | 15 | # for security checks 16 | - name: cicdContextName 17 | type: string 18 | - name: invokerUid 19 | type: string 20 | - name: relatedCicdContextDataId 21 | type: string 22 | - name: relatedPipelineRunUid 23 | type: string 24 | - name: relatedPipelineName 25 | type: string 26 | - name: triggeredBySystem 27 | type: string 28 | - name: triggeredByDescription 29 | type: string 30 | 31 | workspaces: 32 | - name: cicdstatemgr-secrets 33 | description: cicdstatemgr secrets workspace 34 | - name: cicdstatemgr-configs 35 | description: cicdstatemgr configs workspace 36 | 37 | tasks: 38 | 39 | #--------------------- 40 | # init 41 | #--------------------- 42 | - name: init 43 | taskRef: 44 | name: init 45 | workspaces: 46 | - name: cicdstatemgr-configs 47 | workspace: cicdstatemgr-configs 48 | - name: cicdstatemgr-secrets 49 | workspace: cicdstatemgr-secrets 50 | params: 51 | - name: pipelineRunUid 52 | value: $(context.pipelineRun.name) 53 | - name: cicdContextDataId 54 | value: $(params.cicdContextDataId) 55 | - name: pipelineName 56 | value: validate 57 | - name: originalRequestBody 58 | value: $(params.originalRequestBody) 59 | 60 | # for security checks 61 | - name: cicdContextName 62 | value: $(params.cicdContextName) 63 | - name: invokerUid 64 | value: $(params.invokerUid) 65 | - name: relatedCicdContextDataId 66 | value: $(params.relatedCicdContextDataId) 67 | - name: relatedPipelineRunUid 68 | value: $(params.relatedPipelineRunUid) 69 | - name: relatedPipelineName 70 | value: $(params.relatedPipelineName) 71 | - name: triggeredBySystem 72 | value: $(params.triggeredBySystem) 73 | - name: triggeredByDescription 74 | value: $(params.triggeredByDescription) 75 | 76 | 77 | #--------------------- 78 | # execute trivy scan 79 | #--------------------- 80 | - name: trivy-scan 81 | 82 | taskRef: 83 | name: trivy-scan 84 | 85 | runAfter: 86 | - init 87 | 88 | params: 89 | - name: pipelineRunUid 90 | value: $(context.pipelineRun.name) 91 | - name: cicdContextDataId 92 | value: $(params.cicdContextDataId) 93 | 94 | workspaces: 95 | - name: cicdstatemgr-configs 96 | workspace: cicdstatemgr-configs 97 | - name: cicdstatemgr-secrets 98 | workspace: cicdstatemgr-secrets 99 | 100 | 101 | #--------------------------- 102 | # Finish success 103 | #--------------------------- 104 | - name: finish-success 105 | taskRef: 106 | name: handle-event 107 | 108 | runAfter: 109 | - trivy-scan 110 | 111 | workspaces: 112 | - name: cicdstatemgr-configs 113 | workspace: cicdstatemgr-configs 114 | - name: cicdstatemgr-secrets 115 | workspace: cicdstatemgr-secrets 116 | 117 | when: 118 | - input: "$(tasks.trivy-scan.results.last-exit-code)" 119 | operator: in 120 | values: ["0"] 121 | 122 | #conditions: 123 | # - conditionRef: exit-code-is-success 124 | # params: 125 | # - name: exit-code 126 | # value: $(tasks.trivy-scan.results.last-exit-code) 127 | 128 | params: 129 | - name: pipelineRunUid 130 | value: $(context.pipelineRun.name) 131 | - name: cicdContextDataId 132 | value: $(params.cicdContextDataId) 133 | - name: pipelineName 134 | value: validate 135 | - name: eventName 136 | value: success 137 | - name: exitWithExitCode 138 | value: "0" 139 | 140 | 141 | #--------------------------- 142 | # Finish failure 143 | #--------------------------- 144 | - name: finish-failure 145 | taskRef: 146 | name: handle-event 147 | 148 | workspaces: 149 | - name: cicdstatemgr-configs 150 | workspace: cicdstatemgr-configs 151 | - name: cicdstatemgr-secrets 152 | workspace: cicdstatemgr-secrets 153 | 154 | when: 155 | - input: "$(tasks.trivy-scan.results.last-exit-code)" 156 | operator: notin 157 | values: ["0"] 158 | 159 | #conditions: 160 | # - conditionRef: exit-code-is-failure 161 | # params: 162 | # - name: exit-code 163 | # value: $(tasks.trivy-scan.results.last-exit-code) 164 | 165 | params: 166 | - name: pipelineRunUid 167 | value: $(context.pipelineRun.name) 168 | - name: cicdContextDataId 169 | value: $(params.cicdContextDataId) 170 | - name: pipelineName 171 | value: validate 172 | - name: eventName 173 | value: failure 174 | - name: exitWithExitCode 175 | value: "1" 176 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/test/pipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | apiVersion: tekton.dev/v1beta1 4 | kind: Pipeline 5 | metadata: 6 | name: test 7 | 8 | spec: 9 | params: 10 | - name: cicdContextDataId 11 | type: string 12 | - name: originalRequestBody 13 | type: string 14 | 15 | # for security checks 16 | - name: cicdContextName 17 | type: string 18 | - name: invokerUid 19 | type: string 20 | - name: relatedCicdContextDataId 21 | type: string 22 | - name: relatedPipelineRunUid 23 | type: string 24 | - name: relatedPipelineName 25 | type: string 26 | - name: triggeredBySystem 27 | type: string 28 | - name: triggeredByDescription 29 | type: string 30 | 31 | workspaces: 32 | - name: cicdstatemgr-secrets 33 | description: cicdstatemgr secrets workspace 34 | - name: cicdstatemgr-configs 35 | description: cicdstatemgr configs workspace 36 | 37 | tasks: 38 | 39 | #--------------------- 40 | # init 41 | #--------------------- 42 | - name: init 43 | taskRef: 44 | name: init 45 | workspaces: 46 | - name: cicdstatemgr-configs 47 | workspace: cicdstatemgr-configs 48 | - name: cicdstatemgr-secrets 49 | workspace: cicdstatemgr-secrets 50 | params: 51 | - name: pipelineRunUid 52 | value: $(context.pipelineRun.name) 53 | - name: cicdContextDataId 54 | value: $(params.cicdContextDataId) 55 | - name: pipelineName 56 | value: test 57 | - name: originalRequestBody 58 | value: $(params.originalRequestBody) 59 | 60 | # for security checks 61 | - name: cicdContextName 62 | value: $(params.cicdContextName) 63 | - name: invokerUid 64 | value: $(params.invokerUid) 65 | - name: relatedCicdContextDataId 66 | value: $(params.relatedCicdContextDataId) 67 | - name: relatedPipelineRunUid 68 | value: $(params.relatedPipelineRunUid) 69 | - name: relatedPipelineName 70 | value: $(params.relatedPipelineName) 71 | - name: triggeredBySystem 72 | value: $(params.triggeredBySystem) 73 | - name: triggeredByDescription 74 | value: $(params.triggeredByDescription) 75 | 76 | 77 | #--------------------- 78 | # execute test invocation 79 | #--------------------- 80 | - name: test-invoke 81 | 82 | taskRef: 83 | name: test-invoke 84 | 85 | runAfter: 86 | - init 87 | 88 | workspaces: 89 | - name: cicdstatemgr-configs 90 | workspace: cicdstatemgr-configs 91 | - name: cicdstatemgr-secrets 92 | workspace: cicdstatemgr-secrets 93 | 94 | params: 95 | - name: pipelineRunUid 96 | value: $(context.pipelineRun.name) 97 | - name: cicdContextDataId 98 | value: $(params.cicdContextDataId) 99 | 100 | 101 | 102 | 103 | #--------------------------- 104 | # Finish success 105 | #--------------------------- 106 | - name: finish-success 107 | taskRef: 108 | name: handle-event 109 | 110 | runAfter: 111 | - test-invoke 112 | 113 | workspaces: 114 | - name: cicdstatemgr-configs 115 | workspace: cicdstatemgr-configs 116 | - name: cicdstatemgr-secrets 117 | workspace: cicdstatemgr-secrets 118 | 119 | when: 120 | - input: "$(tasks.test-invoke.results.last-exit-code)" 121 | operator: in 122 | values: ["0"] 123 | 124 | #conditions: 125 | # - conditionRef: exit-code-is-success 126 | # params: 127 | # - name: exit-code 128 | # value: $(tasks.test-invoke.results.last-exit-code) 129 | 130 | params: 131 | - name: pipelineRunUid 132 | value: $(context.pipelineRun.name) 133 | - name: cicdContextDataId 134 | value: $(params.cicdContextDataId) 135 | - name: pipelineName 136 | value: test 137 | - name: eventName 138 | value: success 139 | - name: exitWithExitCode 140 | value: "0" 141 | 142 | 143 | 144 | 145 | #--------------------------- 146 | # Finish failure 147 | #--------------------------- 148 | - name: finish-failure 149 | taskRef: 150 | name: handle-event 151 | 152 | workspaces: 153 | - name: cicdstatemgr-configs 154 | workspace: cicdstatemgr-configs 155 | - name: cicdstatemgr-secrets 156 | workspace: cicdstatemgr-secrets 157 | 158 | when: 159 | - input: "$(tasks.test-invoke.results.last-exit-code)" 160 | operator: notin 161 | values: ["0"] 162 | 163 | #conditions: 164 | # - conditionRef: exit-code-is-failure 165 | # params: 166 | # - name: exit-code 167 | # value: $(tasks.test-invoke.results.last-exit-code) 168 | 169 | params: 170 | - name: pipelineRunUid 171 | value: $(context.pipelineRun.name) 172 | - name: cicdContextDataId 173 | value: $(params.cicdContextDataId) 174 | - name: pipelineName 175 | value: test 176 | - name: eventName 177 | value: failure 178 | - name: exitWithExitCode 179 | value: "1" 180 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/start/pipeline.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | apiVersion: tekton.dev/v1beta1 5 | kind: Pipeline 6 | metadata: 7 | name: start 8 | 9 | spec: 10 | params: 11 | - name: cicdContextName 12 | type: string 13 | 14 | - name: invokerUid 15 | type: string 16 | 17 | - name: gitAppName 18 | type: string 19 | 20 | - name: appTag 21 | type: string 22 | 23 | - name: commitId 24 | type: string 25 | default: none 26 | 27 | - name: relatedCicdContextDataId 28 | type: string 29 | default: none 30 | 31 | - name: relatedPipelineRunUid 32 | type: string 33 | default: none 34 | 35 | - name: relatedPipelineName 36 | type: string 37 | default: none 38 | 39 | - name: triggeredBySystem 40 | type: string 41 | 42 | - name: triggeredByDescription 43 | type: string 44 | 45 | - name: originalRequestBody 46 | type: string 47 | 48 | - name: uniqueString 49 | type: string 50 | 51 | - name: gitCloneUrl 52 | type: string 53 | 54 | workspaces: 55 | - name: cicdstatemgr-secrets 56 | description: cicdstatemgr secrets workspace 57 | - name: cicdstatemgr-configs 58 | description: cicdstatemgr configs workspace 59 | 60 | 61 | #--------------------- 62 | # T A S K S 63 | #--------------------- 64 | tasks: 65 | 66 | #--------------------- 67 | # Start 68 | #--------------------- 69 | - name: start 70 | taskRef: 71 | name: start 72 | 73 | workspaces: 74 | - name: cicdstatemgr-configs 75 | workspace: cicdstatemgr-configs 76 | - name: cicdstatemgr-secrets 77 | workspace: cicdstatemgr-secrets 78 | 79 | params: 80 | - name: cicdContextName 81 | value: $(params.cicdContextName) 82 | - name: pipelineRunUid 83 | value: $(context.pipelineRun.name) 84 | - name: invokerUid 85 | value: $(params.invokerUid) 86 | - name: gitAppName 87 | value: $(params.gitAppName) 88 | - name: appTag 89 | value: $(params.appTag) 90 | - name: commitId 91 | value: $(params.commitId) 92 | 93 | - name: relatedCicdContextDataId 94 | value: $(params.relatedCicdContextDataId) 95 | - name: relatedPipelineRunUid 96 | value: $(params.relatedPipelineRunUid) 97 | - name: relatedPipelineName 98 | value: $(params.relatedPipelineName) 99 | - name: triggeredBySystem 100 | value: $(params.triggeredBySystem) 101 | - name: triggeredByDescription 102 | value: $(params.triggeredByDescription) 103 | 104 | - name: originalRequestBody 105 | value: $(params.originalRequestBody) 106 | 107 | - name: uniqueString 108 | value: $(params.uniqueString) 109 | - name: gitCloneUrl 110 | value: $(params.gitCloneUrl) 111 | 112 | #--------------------------- 113 | # Finish success 114 | #--------------------------- 115 | - name: finish-success 116 | taskRef: 117 | name: handle-event 118 | 119 | runAfter: 120 | - start 121 | 122 | workspaces: 123 | - name: cicdstatemgr-configs 124 | workspace: cicdstatemgr-configs 125 | - name: cicdstatemgr-secrets 126 | workspace: cicdstatemgr-secrets 127 | 128 | when: 129 | - input: "$(tasks.start.results.last-exit-code)" 130 | operator: in 131 | values: ["0"] 132 | 133 | #conditions: 134 | # - conditionRef: exit-code-is-success 135 | # params: 136 | # - name: exit-code 137 | # value: $(tasks.start.results.last-exit-code) 138 | 139 | params: 140 | - name: cicdContextDataId 141 | value: $(tasks.start.results.cicdContextDataId) 142 | - name: pipelineRunUid 143 | value: $(context.pipelineRun.name) 144 | - name: pipelineName 145 | value: start 146 | - name: eventName 147 | value: success 148 | - name: exitWithExitCode 149 | value: "0" 150 | 151 | #--------------------------- 152 | # Finish failure 153 | #--------------------------- 154 | - name: finish-failure 155 | 156 | taskRef: 157 | name: handle-event 158 | 159 | runAfter: 160 | - start 161 | 162 | workspaces: 163 | - name: cicdstatemgr-configs 164 | workspace: cicdstatemgr-configs 165 | - name: cicdstatemgr-secrets 166 | workspace: cicdstatemgr-secrets 167 | 168 | when: 169 | - input: "$(tasks.start.results.last-exit-code)" 170 | operator: notin 171 | values: ["0"] 172 | 173 | #conditions: 174 | # - conditionRef: exit-code-is-failure 175 | # params: 176 | # - name: exit-code 177 | # value: $(tasks.start.results.last-exit-code) 178 | 179 | params: 180 | - name: cicdContextDataId 181 | value: $(tasks.start.results.cicdContextDataId) 182 | - name: pipelineRunUid 183 | value: $(context.pipelineRun.name) 184 | - name: pipelineName 185 | value: start 186 | - name: eventName 187 | value: failure 188 | - name: exitWithExitCode 189 | value: "1" 190 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/common/tasks/init.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: tekton.dev/v1beta1 3 | kind: Task 4 | metadata: 5 | name: init 6 | spec: 7 | 8 | workspaces: 9 | 10 | - name: cicdstatemgr-configs 11 | description: The workspace where cicdstatemgr configs reside 12 | mountPath: /workspace/cicdstatemgr-configs 13 | readOnly: true 14 | 15 | - name: cicdstatemgr-secrets 16 | description: The workspace where cicdstatemgr secrets reside 17 | mountPath: /workspace/cicdstatemgr-secrets 18 | readOnly: true 19 | 20 | results: 21 | - name: last-exit-code 22 | description: The last exit code of this task 23 | 24 | params: 25 | 26 | - name: pipelineRunUid 27 | type: string 28 | description: the pipelineRun uid 29 | default: NONE 30 | 31 | - name: cicdContextDataId 32 | type: string 33 | 34 | - name: pipelineName 35 | type: string 36 | description: the pipelineName 37 | default: pipelineName-NULL 38 | 39 | - name: setContextDataValues 40 | type: string 41 | description: PIPE (|) delimited list of k=v pairs 42 | default: "state.loadAndInit=setContextDataValues.not.provided" 43 | 44 | - name: originalRequestBody 45 | type: string 46 | default: "NO_ORIGINAL_REQUEST_BODY" 47 | 48 | - name: triggeredBySystem 49 | type: string 50 | 51 | - name: triggeredByDescription 52 | type: string 53 | 54 | - name: cicdContextName 55 | type: string 56 | 57 | - name: invokerUid 58 | type: string 59 | 60 | - name: relatedPipelineName 61 | type: string 62 | 63 | - name: relatedPipelineRunUid 64 | type: string 65 | 66 | - name: relatedCicdContextDataId 67 | type: string 68 | 69 | steps: 70 | 71 | #---------------------------------- 72 | # STEP load CICD context data 73 | # set vars + handle init event 74 | #---------------------------------- 75 | - name: load-and-init 76 | image: bitsofinfo/cicdstatemgr:1.2.4 77 | command: 78 | - /bin/bash 79 | args: 80 | - -c 81 | - | 82 | 83 | # initialized at 84 | INITIALIZED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ") 85 | 86 | # put the original request body into a file 87 | echo -n '$(inputs.params.originalRequestBody)' > /tekton/results/originalRequestBody 88 | cat /tekton/results/originalRequestBody | grep NO_ORIGINAL_REQUEST_BODY 89 | ORIGINAL_REQUEST_BODY_EXISTS=$? 90 | 91 | # load, set vars, fire init event 92 | cicdstatemgr \ 93 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 94 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 95 | \ 96 | --load \ 97 | --id $(inputs.params.cicdContextDataId) \ 98 | \ 99 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).security.invokerUid=$(inputs.params.invokerUid)" \ 100 | \ 101 | --set '$(inputs.params.setContextDataValues)' \ 102 | \ 103 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).initializedAt=$INITIALIZED_AT" \ 104 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.cicdContextDataId=$(inputs.params.cicdContextDataId)" \ 105 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.cicdContextName=$(inputs.params.cicdContextName)" \ 106 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.invokerUid=$(inputs.params.invokerUid)" \ 107 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.pipelineName=$(inputs.params.pipelineName)" \ 108 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.pipelineRunUid=$(inputs.params.pipelineRunUid)" \ 109 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.triggeredBySystem=$(inputs.params.triggeredBySystem)" \ 110 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.triggeredByDescription=$(inputs.params.triggeredByDescription)" \ 111 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.relatedPipelineName=$(inputs.params.relatedPipelineName)" \ 112 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.relatedPipelineRunUid=$(inputs.params.relatedPipelineRunUid)" \ 113 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.relatedCicdContextDataId=$(inputs.params.relatedCicdContextDataId)" \ 114 | --set "state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid).params.request.body=file:///tekton/results/originalRequestBody" \ 115 | \ 116 | --handle-event $(inputs.params.pipelineName)=init \ 117 | --tmpl-ctx-var "event.data=state.$(inputs.params.pipelineName).$(inputs.params.pipelineRunUid)" \ 118 | 119 | INIT_EXIT_CODE=$? 120 | 121 | echo 122 | 123 | cat /tekton/results/cicdContextDataId 124 | echo 125 | echo 126 | 127 | cat /tekton/results/cicdContextDataJson 128 | echo 129 | 130 | if [ "$INIT_EXIT_CODE" != "0" ]; then echo "INIT FAILED"; else echo "INIT SUCCEEDED" ; fi 131 | echo 132 | 133 | echo -n $INIT_EXIT_CODE > /tekton/results/last-exit-code 134 | 135 | # exit fast 136 | if [ "$INIT_EXIT_CODE" != "0" ]; then 137 | exit $INIT_EXIT_CODE 138 | fi 139 | 140 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/deploy/triggers.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | apiVersion: triggers.tekton.dev/v1alpha1 5 | kind: TriggerTemplate 6 | metadata: 7 | name: deploy 8 | annotations: 9 | # https://tektoncd.slack.com/archives/CKUSJ2A5D/p1606222895139800 10 | # https://github.com/tektoncd/triggers/pull/842 11 | triggers.tekton.dev/old-escape-quotes: "true" 12 | spec: 13 | params: 14 | - name: cicdContextName 15 | - name: cicdContextDataId 16 | - name: targetAppName 17 | - name: targetAppTag 18 | - name: originalRequestBody 19 | - name: gitCloneUrl 20 | 21 | # for security checks 22 | - name: cicdContextName 23 | - name: invokerUid 24 | - name: relatedCicdContextDataId 25 | - name: relatedPipelineRunUid 26 | - name: relatedPipelineName 27 | - name: triggeredBySystem 28 | - name: triggeredByDescription 29 | 30 | resourcetemplates: 31 | 32 | #--------------------------- 33 | # Deploy PipelineRun 34 | #--------------------------- 35 | - apiVersion: tekton.dev/v1beta1 36 | kind: PipelineRun 37 | metadata: 38 | generateName: deploy-$(tt.params.cicdContextName)-$(tt.params.targetAppName)- 39 | spec: 40 | serviceAccountName: cicd-tekton 41 | 42 | pipelineRef: 43 | name: deploy 44 | 45 | params: 46 | - name: cicdContextDataId 47 | value: $(tt.params.cicdContextDataId) 48 | 49 | - name: targetAppName 50 | value: $(tt.params.targetAppName) 51 | 52 | - name: targetAppTag 53 | value: $(tt.params.targetAppTag) 54 | 55 | - name: originalRequestBody 56 | value: $(tt.params.originalRequestBody) 57 | 58 | - name: gitCloneUrl 59 | value: $(tt.params.gitCloneUrl) 60 | 61 | # for security checks 62 | - name: cicdContextName 63 | value: $(tt.params.cicdContextName) 64 | - name: invokerUid 65 | value: $(tt.params.invokerUid) 66 | - name: relatedCicdContextDataId 67 | value: $(tt.params.relatedCicdContextDataId) 68 | - name: relatedPipelineRunUid 69 | value: $(tt.params.relatedPipelineRunUid) 70 | - name: relatedPipelineName 71 | value: $(tt.params.relatedPipelineName) 72 | - name: triggeredBySystem 73 | value: $(tt.params.triggeredBySystem) 74 | - name: triggeredByDescription 75 | value: $(tt.params.triggeredByDescription) 76 | 77 | workspaces: 78 | 79 | - name: cicdstatemgr-secrets 80 | secret: 81 | secretName: cicdstatemgr-secrets 82 | items: 83 | - key: cicdstatemgr-secrets.yaml 84 | path: cicdstatemgr-secrets.yaml 85 | 86 | - name: cicdstatemgr-configs 87 | configmap: 88 | name: cicdstatemgr-configs 89 | 90 | - name: scratch 91 | volumeClaimTemplate: 92 | metadata: 93 | name: scratch 94 | spec: 95 | storageClassName: standard 96 | accessModes: 97 | - ReadWriteOnce 98 | resources: 99 | requests: 100 | storage: 50Mi 101 | 102 | 103 | --- 104 | apiVersion: triggers.tekton.dev/v1alpha1 105 | kind: TriggerBinding 106 | metadata: 107 | name: deploy-via-trigger 108 | spec: 109 | params: 110 | 111 | - name: cicdContextName 112 | value: $(extensions.cicdContextName) 113 | 114 | - name: cicdContextDataId 115 | value: $(extensions.cicdContextDataId) 116 | 117 | - name: targetAppName 118 | value: $(extensions.targetAppName) 119 | 120 | - name: targetAppTag 121 | value: $(extensions.targetAppTag) 122 | 123 | - name: originalRequestBody 124 | value: $(body) 125 | 126 | - name: gitCloneUrl 127 | value: $(body.gitCloneUrl) 128 | 129 | # for security checks 130 | - name: invokerUid 131 | value: $(extensions.invokerUid) 132 | - name: relatedCicdContextDataId 133 | value: $(extensions.relatedCicdContextDataId) 134 | - name: relatedPipelineRunUid 135 | value: $(extensions.relatedPipelineRunUid) 136 | - name: relatedPipelineName 137 | value: $(extensions.relatedPipelineName) 138 | - name: triggeredBySystem 139 | value: $(extensions.triggeredBySystem) 140 | - name: triggeredByDescription 141 | value: $(extensions.triggeredByDescription) 142 | 143 | --- 144 | 145 | 146 | --- 147 | apiVersion: triggers.tekton.dev/v1alpha1 148 | kind: TriggerBinding 149 | metadata: 150 | name: deploy-via-slack 151 | spec: 152 | params: 153 | 154 | - name: cicdContextName 155 | value: $(extensions.cicdContextName) 156 | 157 | - name: cicdContextDataId 158 | value: $(extensions.cicdContextDataId) 159 | 160 | - name: targetAppName 161 | value: $(extensions.targetAppName) 162 | 163 | - name: targetAppTag 164 | value: $(extensions.targetAppTag) 165 | 166 | - name: originalRequestBody 167 | value: $(body) 168 | 169 | - name: gitCloneUrl 170 | value: $(extensions.gitCloneUrl) 171 | 172 | # for security checks 173 | - name: invokerUid 174 | value: $(extensions.slackUsername) 175 | - name: relatedCicdContextDataId 176 | value: $(extensions.relatedCicdContextDataId) 177 | - name: relatedPipelineRunUid 178 | value: $(extensions.relatedPipelineRunUid) 179 | - name: relatedPipelineName 180 | value: $(extensions.relatedPipelineName) 181 | - name: triggeredBySystem 182 | value: 'slack' 183 | - name: triggeredByDescription 184 | value: 'slack button press by <@$(extensions.slackUserId)>' 185 | 186 | --- 187 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/start/triggers.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | apiVersion: triggers.tekton.dev/v1alpha1 5 | kind: TriggerTemplate 6 | metadata: 7 | name: start 8 | annotations: 9 | # https://tektoncd.slack.com/archives/CKUSJ2A5D/p1606222895139800 10 | # https://github.com/tektoncd/triggers/pull/842 11 | triggers.tekton.dev/old-escape-quotes: "true" 12 | spec: 13 | params: 14 | - name: cicdContextName 15 | 16 | - name: invokerUid 17 | 18 | - name: gitAppName 19 | 20 | - name: appTag 21 | 22 | - name: commitId 23 | 24 | - name: triggeredBySystem 25 | 26 | - name: triggeredByDescription 27 | 28 | - name: relatedCicdContextDataId 29 | default: "none" 30 | 31 | - name: relatedPipelineRunUid 32 | default: "none" 33 | 34 | - name: relatedPipelineName 35 | default: "none" 36 | 37 | - name: originalRequestBody 38 | 39 | - name: uniqueString 40 | 41 | - name: gitCloneUrl 42 | 43 | resourcetemplates: 44 | - apiVersion: tekton.dev/v1beta1 45 | kind: PipelineRun 46 | metadata: 47 | generateName: start-$(tt.params.cicdContextName)-$(tt.params.gitAppName)- 48 | spec: 49 | serviceAccountName: cicd-tekton 50 | 51 | pipelineRef: 52 | name: start 53 | 54 | params: 55 | - name: cicdContextName 56 | value: $(tt.params.cicdContextName) 57 | 58 | - name: relatedCicdContextDataId 59 | value: $(tt.params.relatedCicdContextDataId) 60 | 61 | - name: relatedPipelineRunUid 62 | value: $(tt.params.relatedPipelineRunUid) 63 | 64 | - name: relatedPipelineName 65 | value: $(tt.params.relatedPipelineName) 66 | 67 | - name: invokerUid # the invoking user uid 68 | value: $(tt.params.invokerUid) 69 | 70 | - name: gitAppName 71 | value: $(tt.params.gitAppName) 72 | 73 | - name: appTag 74 | value: $(tt.params.appTag) 75 | 76 | - name: commitId 77 | value: $(tt.params.commitId) 78 | 79 | - name: triggeredBySystem 80 | value: $(tt.params.triggeredBySystem) 81 | 82 | - name: triggeredByDescription 83 | value: $(tt.params.triggeredByDescription) 84 | 85 | - name: originalRequestBody 86 | value: $(tt.params.originalRequestBody) 87 | 88 | - name: uniqueString 89 | value: $(tt.params.uniqueString) 90 | 91 | - name: gitCloneUrl 92 | value: $(tt.params.gitCloneUrl) 93 | 94 | workspaces: 95 | 96 | - name: cicdstatemgr-secrets 97 | secret: 98 | secretName: cicdstatemgr-secrets 99 | items: 100 | - key: cicdstatemgr-secrets.yaml 101 | path: cicdstatemgr-secrets.yaml 102 | 103 | - name: cicdstatemgr-configs 104 | configmap: 105 | name: cicdstatemgr-configs 106 | 107 | --- 108 | apiVersion: triggers.tekton.dev/v1alpha1 109 | kind: TriggerBinding 110 | metadata: 111 | name: start-via-git 112 | spec: 113 | params: 114 | - name: cicdContextName 115 | value: dev 116 | 117 | - name: invokerUid # the invoking user uid! 118 | value: $(extensions.invokedByPrincipalId) 119 | 120 | - name: gitAppName 121 | value: $(extensions.gitAppName) 122 | 123 | - name: appTag 124 | value: $(extensions.appTag) 125 | 126 | - name: commitId 127 | value: $(extensions.commitId) 128 | 129 | - name: triggeredBySystem 130 | value: "github" 131 | 132 | - name: triggeredByDescription 133 | value: "Github push of git tag $(extensions.gitAppName):$(extensions.appTag)" 134 | 135 | - name: originalRequestBody 136 | value: $(body) 137 | 138 | - name: uniqueString 139 | value: $(extensions.commitId) 140 | 141 | - name: gitCloneUrl 142 | value: $(body.repository.clone_url) 143 | 144 | 145 | 146 | --- 147 | apiVersion: triggers.tekton.dev/v1alpha1 148 | kind: TriggerBinding 149 | metadata: 150 | name: start-via-change-context 151 | spec: 152 | params: 153 | 154 | - name: cicdContextName 155 | value: $(extensions.targetCicdContextName) 156 | 157 | - name: invokerUid 158 | value: "<@$(extensions.slackUserId)>" 159 | 160 | - name: gitAppName 161 | value: $(extensions.gitAppName) 162 | 163 | - name: appTag 164 | value: $(extensions.appTag) 165 | 166 | - name: commitId 167 | value: $(extensions.commitId) 168 | 169 | - name: gitCloneUrl 170 | value: $(extensions.gitCloneUrl) 171 | 172 | - name: triggeredBySystem 173 | value: "slack" 174 | 175 | - name: triggeredByDescription 176 | value: "Slack button press by <@$(extensions.slackUserId)>" 177 | 178 | - name: relatedCicdContextDataId 179 | value: $(extensions.relatedCicdContextDataId) 180 | 181 | - name: relatedPipelineRunUid 182 | value: $(extensions.relatedPipelineRunUid) 183 | 184 | - name: relatedPipelineName 185 | value: $(extensions.relatedPipelineName) 186 | 187 | - name: originalRequestBody 188 | value: $(body) 189 | 190 | - name: uniqueString 191 | value: $(extensions.commitId) 192 | 193 | 194 | --- 195 | apiVersion: triggers.tekton.dev/v1alpha1 196 | kind: TriggerBinding 197 | metadata: 198 | name: start-via-cmd-startdeploy 199 | spec: 200 | params: 201 | 202 | - name: cicdContextName 203 | value: $(extensions.targetCicdContextName) 204 | 205 | - name: invokerUid 206 | value: "<@$(extensions.slackUserId)>" 207 | 208 | - name: gitAppName 209 | value: $(extensions.gitAppName) 210 | 211 | - name: appTag 212 | value: $(extensions.appTag) 213 | 214 | - name: gitCloneUrl 215 | value: $(extensions.gitCloneUrl) 216 | 217 | - name: triggeredBySystem 218 | value: "slack slash cmd" 219 | 220 | - name: triggeredByDescription 221 | value: "Slack slash command /start-app-deploy by <@$(extensions.slackUserId)>" 222 | 223 | - name: originalRequestBody 224 | value: $(body) 225 | 226 | - name: uniqueString 227 | value: $(extensions.uniqueString) 228 | -------------------------------------------------------------------------------- /examples/tekton/pipelines/build/pipeline.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | apiVersion: tekton.dev/v1beta1 5 | kind: Pipeline 6 | metadata: 7 | name: build 8 | 9 | spec: 10 | params: 11 | - name: cicdContextDataId 12 | type: string 13 | - name: gitAppName 14 | type: string 15 | - name: appTag 16 | type: string 17 | - name: originalRequestBody 18 | type: string 19 | - name: gitCloneUrl 20 | type: string 21 | 22 | # for security checks 23 | - name: cicdContextName 24 | type: string 25 | - name: invokerUid 26 | type: string 27 | - name: relatedCicdContextDataId 28 | type: string 29 | - name: relatedPipelineRunUid 30 | type: string 31 | - name: relatedPipelineName 32 | type: string 33 | - name: triggeredBySystem 34 | type: string 35 | - name: triggeredByDescription 36 | type: string 37 | 38 | 39 | workspaces: 40 | - name: cicdstatemgr-secrets 41 | description: cicdstatemgr secrets workspace 42 | - name: cicdstatemgr-configs 43 | description: cicdstatemgr configs workspace 44 | - name: scratch 45 | description: git clone scratch 46 | 47 | #--------------------- 48 | # T A S K S 49 | #--------------------- 50 | tasks: 51 | 52 | 53 | #--------------------- 54 | # git clone app 55 | #--------------------- 56 | - name: git-clone-app-source 57 | taskRef: 58 | name: git-clone 59 | workspaces: 60 | - name: output 61 | workspace: scratch 62 | params: 63 | - name: url 64 | value: $(params.gitCloneUrl) 65 | - name: revision 66 | value: $(params.appTag) 67 | 68 | #--------------------- 69 | # init 70 | #--------------------- 71 | - name: init 72 | runAfter: 73 | - git-clone-app-source 74 | taskRef: 75 | name: init 76 | 77 | workspaces: 78 | - name: cicdstatemgr-configs 79 | workspace: cicdstatemgr-configs 80 | - name: cicdstatemgr-secrets 81 | workspace: cicdstatemgr-secrets 82 | 83 | params: 84 | - name: pipelineRunUid 85 | value: $(context.pipelineRun.name) 86 | - name: cicdContextDataId 87 | value: $(params.cicdContextDataId) 88 | - name: pipelineName 89 | value: build 90 | - name: originalRequestBody 91 | value: $(params.originalRequestBody) 92 | 93 | # for security checks 94 | - name: cicdContextName 95 | value: $(params.cicdContextName) 96 | - name: invokerUid 97 | value: $(params.invokerUid) 98 | - name: relatedCicdContextDataId 99 | value: $(params.relatedCicdContextDataId) 100 | - name: relatedPipelineRunUid 101 | value: $(params.relatedPipelineRunUid) 102 | - name: relatedPipelineName 103 | value: $(params.relatedPipelineName) 104 | - name: triggeredBySystem 105 | value: $(params.triggeredBySystem) 106 | - name: triggeredByDescription 107 | value: $(params.triggeredByDescription) 108 | 109 | 110 | #--------------------- 111 | # Build 112 | #--------------------- 113 | - name: build 114 | taskRef: 115 | name: build 116 | 117 | runAfter: 118 | - init 119 | 120 | workspaces: 121 | - name: cicdstatemgr-configs 122 | workspace: cicdstatemgr-configs 123 | - name: cicdstatemgr-secrets 124 | workspace: cicdstatemgr-secrets 125 | - name: git-source 126 | workspace: scratch 127 | 128 | params: 129 | - name: cicdContextDataId 130 | value: $(params.cicdContextDataId) 131 | - name: pipelineRunUid 132 | value: $(context.pipelineRun.name) 133 | 134 | #--------------------------- 135 | # Finish success 136 | #--------------------------- 137 | - name: finish-success 138 | taskRef: 139 | name: handle-event 140 | 141 | runAfter: 142 | - build 143 | 144 | workspaces: 145 | - name: cicdstatemgr-configs 146 | workspace: cicdstatemgr-configs 147 | - name: cicdstatemgr-secrets 148 | workspace: cicdstatemgr-secrets 149 | 150 | when: 151 | - input: "$(tasks.build.results.last-exit-code)" 152 | operator: in 153 | values: ["0"] 154 | 155 | #conditions: 156 | # - conditionRef: exit-code-is-success 157 | # params: 158 | # - name: exit-code 159 | # value: $(tasks.build.results.last-exit-code) 160 | 161 | params: 162 | - name: cicdContextDataId 163 | value: $(params.cicdContextDataId) 164 | - name: pipelineRunUid 165 | value: $(context.pipelineRun.name) 166 | - name: pipelineName 167 | value: build 168 | - name: eventName 169 | value: success 170 | - name: exitWithExitCode 171 | value: "0" 172 | 173 | #--------------------------- 174 | # Finish failure 175 | #--------------------------- 176 | - name: finish-failure 177 | 178 | taskRef: 179 | name: handle-event 180 | 181 | runAfter: 182 | - build 183 | 184 | workspaces: 185 | - name: cicdstatemgr-configs 186 | workspace: cicdstatemgr-configs 187 | - name: cicdstatemgr-secrets 188 | workspace: cicdstatemgr-secrets 189 | 190 | when: 191 | - input: "$(tasks.build.results.last-exit-code)" 192 | operator: notin 193 | values: ["0"] 194 | 195 | #conditions: 196 | # - conditionRef: exit-code-is-failure 197 | # params: 198 | # - name: exit-code 199 | # value: $(tasks.build.results.last-exit-code) 200 | 201 | params: 202 | - name: cicdContextDataId 203 | value: $(params.cicdContextDataId) 204 | - name: pipelineRunUid 205 | value: $(context.pipelineRun.name) 206 | - name: pipelineName 207 | value: build 208 | - name: eventName 209 | value: failure 210 | - name: exitWithExitCode 211 | value: "1" 212 | -------------------------------------------------------------------------------- /examples/basics/SET.md: -------------------------------------------------------------------------------- 1 | # --set 2 | 3 | *NOTE: see the functioning [scripts in the test/ directory](test/) for commands that match what is described in this doc* 4 | 5 | This demonstrates the usage of `--set` which provides a way to set any value within the `cicdContextData` as well as load values from `file://` references. 6 | 7 | `--set` can be used in combination with `--init-new`, `--handle-event` and `--load` 8 | 9 | This assumes you've already run [INIT_NEW](INIT_NEW.md) 10 | 11 | Whenever you call `--set` the `cicdContextData` that is loaded will be fetched from any non-primary stores first (i.e. disk files) and then only fetched from a primary store as a last resort. See [config.yaml](config.yaml) 12 | 13 | Each time you `--set` the data is re-flushed to all data sources (primary and non-primary). See [config.yaml](config.yaml) 14 | 15 | Let's ensure the following value exists via `--set`: 16 | ``` 17 | cicdstatemgr \ 18 | --config config.yaml \ 19 | --secrets secrets.yaml \ 20 | --id "context-data-id-1" \ 21 | --set "state.key1=value1" 22 | ``` 23 | 24 | Get the value: 25 | ``` 26 | cicdstatemgr \ 27 | --config config.yaml \ 28 | --secrets secrets.yaml \ 29 | --id "context-data-id-1" \ 30 | --get "state.key1" 31 | 32 | value1 33 | ``` 34 | 35 | Let's `--set` multiple times: 36 | ``` 37 | cicdstatemgr \ 38 | --config config.yaml \ 39 | --secrets secrets.yaml \ 40 | --id "context-data-id-1" \ 41 | --set "state.key1=value1" \ 42 | --set "state.key2=value2" 43 | ``` 44 | 45 | 46 | Let's reference the contents of a file as the value for `--set`. The syntax in this case is `--set key=file://` where the file's contents will be written to the `key` within `cicdContextData`. If the file is `JSON` or `YAML` the object/array marshalled from those file types will be injected under the `key`. 47 | 48 | Lets just try a simple text file: 49 | ``` 50 | echo "simple body contents" > set.simple.file 51 | 52 | cicdstatemgr \ 53 | --config config.yaml \ 54 | --secrets secrets.yaml \ 55 | --id "context-data-id-1" \ 56 | --set 'state.fileBody=file://set.simple.file' 57 | 58 | cicdstatemgr \ 59 | --config config.yaml \ 60 | --secrets secrets.yaml \ 61 | --id "context-data-id-1" \ 62 | --get 'state.fileBody' 63 | 64 | simple body contents 65 | ``` 66 | 67 | 68 | JSON contents w/ an array: 69 | ``` 70 | echo "[1,2,3]" > set.simple.json 71 | 72 | cicdstatemgr \ 73 | --config config.yaml \ 74 | --secrets secrets.yaml \ 75 | --id "context-data-id-1" \ 76 | --set 'state.fileBody=file://set.simple.json' 77 | 78 | cicdstatemgr \ 79 | --config config.yaml \ 80 | --secrets secrets.yaml \ 81 | --id "context-data-id-1" \ 82 | --get 'state.fileBody[2]' 83 | 84 | 3 85 | ``` 86 | 87 | JSON contents w/ an object: 88 | ``` 89 | echo '{"dog":"beagle", "bark":{"quality":"high","volume":"loud"}}' > set.simple.json 90 | 91 | cicdstatemgr \ 92 | --config config.yaml \ 93 | --secrets secrets.yaml \ 94 | --id "context-data-id-1" \ 95 | --set 'state.fileBody=file://set.simple.json' 96 | 97 | cicdstatemgr \ 98 | --config config.yaml \ 99 | --secrets secrets.yaml \ 100 | --id "context-data-id-1" \ 101 | --get 'state.fileBody.bark.quality' 102 | 103 | high 104 | ``` 105 | 106 | 107 | YAML contents: 108 | ``` 109 | printf "dog: beagle\nbark:\n quality: high\n volume: loud\n" > set.simple.yaml 110 | 111 | cicdstatemgr \ 112 | --config config.yaml \ 113 | --secrets secrets.yaml \ 114 | --id "context-data-id-1" \ 115 | --set 'state.fileBody=file://set.simple.yaml' 116 | 117 | cicdstatemgr \ 118 | --config config.yaml \ 119 | --secrets secrets.yaml \ 120 | --id "context-data-id-1" \ 121 | --get 'state.fileBody.bark.quality' 122 | 123 | high 124 | ``` 125 | 126 | --- 127 | 128 | Let's set an `list`: 129 | ``` 130 | cicdstatemgr \ 131 | --config config.yaml \ 132 | --secrets secrets.yaml \ 133 | --id "context-data-id-1" \ 134 | --set 'state.testList[]=a,b,c' 135 | ``` 136 | 137 | Get it: 138 | ``` 139 | cicdstatemgr \ 140 | --config config.yaml \ 141 | --secrets secrets.yaml \ 142 | --id "context-data-id-1" \ 143 | --get 'state.testList[2]' 144 | 145 | c 146 | ``` 147 | 148 | Append: 149 | ``` 150 | cicdstatemgr \ 151 | --config config.yaml \ 152 | --secrets secrets.yaml \ 153 | --id "context-data-id-1" \ 154 | --set 'state.testList[]=d,e,f' 155 | 156 | cicdstatemgr \ 157 | --config config.yaml \ 158 | --secrets secrets.yaml \ 159 | --id "context-data-id-1" \ 160 | --get 'state.testList' 161 | 162 | ["a", "b", "c", "d", "e", "f"] 163 | ``` 164 | 165 | Clear and reset + new + append 166 | ``` 167 | cicdstatemgr \ 168 | --config config.yaml \ 169 | --secrets secrets.yaml \ 170 | --id "context-data-id-1" \ 171 | --set 'state.testList[]=[]' \ 172 | --set 'state.testList[]=x,y,z' \ 173 | --set 'state.testList[]=d' 174 | 175 | cicdstatemgr \ 176 | --config config.yaml \ 177 | --secrets secrets.yaml \ 178 | --id "context-data-id-1" \ 179 | --get 'state.testList' 180 | 181 | ["x", "y", "z", "d"] 182 | ``` 183 | 184 | --- 185 | 186 | Let's set an `set` (list w/ no duplicates permitted): 187 | ``` 188 | cicdstatemgr \ 189 | --config config.yaml \ 190 | --secrets secrets.yaml \ 191 | --id "context-data-id-1" \ 192 | --set 'state.testSet{}=a,b,c,c,c,c' 193 | ``` 194 | 195 | Get it: 196 | ``` 197 | cicdstatemgr \ 198 | --config config.yaml \ 199 | --secrets secrets.yaml \ 200 | --id "context-data-id-1" \ 201 | --get 'state.testSet' 202 | 203 | ["a", "b", "c"] 204 | ``` 205 | 206 | Append: 207 | ``` 208 | cicdstatemgr \ 209 | --config config.yaml \ 210 | --secrets secrets.yaml \ 211 | --id "context-data-id-1" \ 212 | --set 'state.testSet{}=d,e,e,e,f' 213 | 214 | cicdstatemgr \ 215 | --config config.yaml \ 216 | --secrets secrets.yaml \ 217 | --id "context-data-id-1" \ 218 | --get 'state.testSet' 219 | 220 | ["a", "b", "c", "d", "e", "f"] 221 | ``` 222 | 223 | Clear and reset + new + append 224 | ``` 225 | cicdstatemgr \ 226 | --config config.yaml \ 227 | --secrets secrets.yaml \ 228 | --id "context-data-id-1" \ 229 | --set 'state.testSet{}=[]' \ 230 | --set 'state.testSet{}=x,y,z' \ 231 | --set 'state.testSet{}=d,d,d' 232 | 233 | cicdstatemgr \ 234 | --config config.yaml \ 235 | --secrets secrets.yaml \ 236 | --id "context-data-id-1" \ 237 | --get 'state.testSet' 238 | 239 | ["x", "y", "z", "d"] 240 | ``` -------------------------------------------------------------------------------- /examples/tekton/pipelines/deploy/pipeline.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: tekton.dev/v1beta1 3 | kind: Pipeline 4 | metadata: 5 | name: deploy 6 | spec: 7 | params: 8 | - name: cicdContextDataId 9 | type: string 10 | - name: targetAppName 11 | type: string 12 | - name: targetAppTag 13 | type: string 14 | - name: originalRequestBody 15 | type: string 16 | - name: gitCloneUrl 17 | type: string 18 | 19 | # for security checks 20 | - name: cicdContextName 21 | type: string 22 | - name: invokerUid 23 | type: string 24 | - name: relatedCicdContextDataId 25 | type: string 26 | - name: relatedPipelineRunUid 27 | type: string 28 | - name: relatedPipelineName 29 | type: string 30 | - name: triggeredBySystem 31 | type: string 32 | - name: triggeredByDescription 33 | type: string 34 | 35 | workspaces: 36 | - name: cicdstatemgr-secrets 37 | description: cicdstatemgr secrets workspace 38 | - name: cicdstatemgr-configs 39 | description: cicdstatemgr configs workspace 40 | - name: scratch 41 | description: git clone scratch 42 | 43 | tasks: 44 | 45 | 46 | #--------------------- 47 | # git clone app 48 | #--------------------- 49 | - name: git-clone-app-source 50 | taskRef: 51 | name: git-clone 52 | workspaces: 53 | - name: output 54 | workspace: scratch 55 | params: 56 | - name: url 57 | value: $(params.gitCloneUrl) 58 | - name: revision 59 | value: $(params.targetAppTag) 60 | 61 | #--------------------- 62 | # init 63 | #--------------------- 64 | - name: init 65 | runAfter: 66 | - git-clone-app-source 67 | taskRef: 68 | name: init 69 | 70 | workspaces: 71 | - name: cicdstatemgr-configs 72 | workspace: cicdstatemgr-configs 73 | - name: cicdstatemgr-secrets 74 | workspace: cicdstatemgr-secrets 75 | 76 | params: 77 | - name: pipelineRunUid 78 | value: $(context.pipelineRun.name) 79 | - name: cicdContextDataId 80 | value: $(params.cicdContextDataId) 81 | - name: pipelineName 82 | value: deploy 83 | - name: originalRequestBody 84 | value: $(params.originalRequestBody) 85 | 86 | # for security checks 87 | - name: cicdContextName 88 | value: $(params.cicdContextName) 89 | - name: invokerUid 90 | value: $(params.invokerUid) 91 | - name: relatedCicdContextDataId 92 | value: $(params.relatedCicdContextDataId) 93 | - name: relatedPipelineRunUid 94 | value: $(params.relatedPipelineRunUid) 95 | - name: relatedPipelineName 96 | value: $(params.relatedPipelineName) 97 | - name: triggeredBySystem 98 | value: $(params.triggeredBySystem) 99 | - name: triggeredByDescription 100 | value: $(params.triggeredByDescription) 101 | 102 | - name: setContextDataValues 103 | value: >- 104 | state.deploy.$(context.pipelineRun.name).params.targetAppName=$(params.targetAppName)| 105 | state.deploy.$(context.pipelineRun.name).params.cicdContextDataId=$(params.cicdContextDataId)| 106 | state.deploy.$(context.pipelineRun.name).params.targetAppTag=$(params.targetAppTag) 107 | 108 | 109 | #--------------------- 110 | # Deploy 111 | #--------------------- 112 | - name: deploy 113 | taskRef: 114 | name: deploy 115 | 116 | runAfter: 117 | - init 118 | 119 | workspaces: 120 | - name: cicdstatemgr-configs 121 | workspace: cicdstatemgr-configs 122 | - name: cicdstatemgr-secrets 123 | workspace: cicdstatemgr-secrets 124 | - name: git-source 125 | workspace: scratch 126 | 127 | params: 128 | - name: pipelineRunUid 129 | value: $(context.pipelineRun.name) 130 | - name: cicdContextDataId 131 | value: $(params.cicdContextDataId) 132 | - name: targetAppTag 133 | value: $(params.targetAppTag) 134 | 135 | #--------------------------- 136 | # Deploy finish success 137 | #--------------------------- 138 | - name: deploy-finish-success 139 | taskRef: 140 | name: handle-event 141 | 142 | runAfter: 143 | - deploy 144 | 145 | workspaces: 146 | - name: cicdstatemgr-configs 147 | workspace: cicdstatemgr-configs 148 | - name: cicdstatemgr-secrets 149 | workspace: cicdstatemgr-secrets 150 | 151 | 152 | when: 153 | - input: "$(tasks.deploy.results.last-exit-code)" 154 | operator: in 155 | values: ["0"] 156 | 157 | #conditions: 158 | # - conditionRef: exit-code-is-success 159 | # params: 160 | # - name: exit-code 161 | # value: $(tasks.deploy.results.last-exit-code) 162 | 163 | params: 164 | - name: pipelineRunUid 165 | value: $(context.pipelineRun.name) 166 | - name: cicdContextDataId 167 | value: $(params.cicdContextDataId) 168 | - name: pipelineName 169 | value: deploy 170 | - name: eventName 171 | value: success 172 | - name: exitWithExitCode 173 | value: "0" 174 | 175 | #--------------------------- 176 | # Deploy finish failure 177 | #--------------------------- 178 | - name: deploy-finish-failure 179 | taskRef: 180 | name: handle-event 181 | 182 | runAfter: 183 | - deploy 184 | 185 | workspaces: 186 | - name: cicdstatemgr-configs 187 | workspace: cicdstatemgr-configs 188 | - name: cicdstatemgr-secrets 189 | workspace: cicdstatemgr-secrets 190 | 191 | when: 192 | - input: "$(tasks.deploy.results.last-exit-code)" 193 | operator: notin 194 | values: ["0"] 195 | 196 | #conditions: 197 | # - conditionRef: exit-code-is-failure 198 | # params: 199 | # - name: exit-code 200 | # value: $(tasks.deploy.results.last-exit-code) 201 | 202 | params: 203 | - name: pipelineRunUid 204 | value: $(context.pipelineRun.name) 205 | - name: cicdContextDataId 206 | value: $(params.cicdContextDataId) 207 | - name: pipelineName 208 | value: deploy 209 | - name: eventName 210 | value: failure 211 | - name: exitWithExitCode 212 | value: "1" 213 | -------------------------------------------------------------------------------- /examples/basics/app.yaml: -------------------------------------------------------------------------------- 1 | bases: 2 | - base1.yaml 3 | 4 | jinja2-macros: 5 | 6 | helloWorld: | 7 | {%- macro helloWorld(msg) -%} 8 | Hello world msg = {{msg}} 9 | {%- endmacro -%} 10 | 11 | variables: 12 | myVar1: "test" 13 | 14 | cicd-contexts: 15 | 16 | stage: 17 | channel: stage 18 | 19 | pipelines: 20 | build: 21 | 22 | # 23 | # Example of "generators" for --generate 24 | # 25 | generators: 26 | 27 | # here is a "generator" named "dockerfile" 28 | dockerfile: 29 | 30 | # here is one generatorConfig named "alpine" 31 | alpine: 32 | 33 | # our optional condition that if yields non-blank 34 | # will cause all K/Vs in 'set' to be written into 35 | # cicdContextData 36 | if: "{%- if 'alpine' in state.gitTag -%}true{%- endif -%}" 37 | set: 38 | - key: "state.build.dockerfileInfo.targetImageName" 39 | value: "{{state.gitAppName}}" 40 | - key: "state.build.dockerfileInfo.generateDockerfileCmd" 41 | value: | 42 | ./gendockerfile.sh -f alpine:latest -x {{state.gitTag}} -z {{ctx.var1}} -q {{ctx.var2}} 43 | 44 | # here is one generatorConfig named "centos" 45 | centos: 46 | 47 | # our optional condition that if yields non-blank 48 | # will cause all K/Vs in 'set' to be written into 49 | # cicdContextData 50 | if: "{%- if 'centos' in state.gitTag -%}true{%- endif -%}" 51 | set: 52 | - key: "state.build.dockerfileInfo.targetImageName" 53 | value: "{{state.gitAppName}}" 54 | - key: "state.build.dockerfileInfo.generateDockerfileCmd" 55 | value: | 56 | ./gendockerfile.sh -f centos:latest -x {{state.gitTag}} -z {{ctx.var1}} -q {{ctx.var2}} 57 | 58 | 59 | event-handlers: 60 | 61 | testEvent: 62 | notify: 63 | message: | 64 | {{ basicMacro('testEventFired!!! yes...') }} 65 | 66 | testNotifyEvent: 67 | 68 | notify: 69 | message: | 70 | {{ basicMacro('build is successful') }} 71 | 72 | capture-response-data: 73 | - from: "{{ body.data.channel }}" 74 | to: state.lastPostedToNotifyChannel 75 | - from: "{{ body|tojson }}" 76 | to: state.lastPostedHttpResponse 77 | 78 | 79 | testRespondEvent: 80 | respond: 81 | someArbitraryKey: "lastPostedDataRandomId={{ state.lastPostedDataRandomId }}" 82 | if: "{{ state.lastPostedHttpResponse }}" 83 | url: "{{ (state.lastPostedHttpResponse|from_json).url }}" 84 | message: | 85 | dummy responder message for {{ state.lastPostedDataRandomId }} 86 | 87 | testSetValuesEvent: 88 | set-values: 89 | extractLastPostedNotifyMessage: 90 | if: | 91 | {%- if state.lastPostedHttpResponse -%} 92 | 1 93 | {%- endif -%} 94 | set: 95 | - from: | 96 | {%- set lastPostedHttpResponse = (state.lastPostedHttpResponse|from_json) -%} 97 | {{- lastPostedHttpResponse.data.message -}} 98 | to: "state.lastPostedNotifyMessage" 99 | 100 | testTriggerPipelineEvent: 101 | # see config.yaml for additional headers 102 | # and auto-args that will supplement what is 103 | # specified here 104 | trigger-pipeline: 105 | name: build 106 | args: 107 | whatever: "{{state.postedData[state.lastPostedDataRandomId].headers.userAgent}}" 108 | 109 | testManualChoiceEvent: 110 | manual-choice: 111 | title: | 112 | {{ basicMacro('here are my choices') }} 113 | choices: 114 | choiceGroup1: 115 | header: "Choice group one:" 116 | options: 117 | - text: "Choice 1" 118 | value: "c1" 119 | - text: "Choice 2" 120 | value: "c2" 121 | 122 | choiceGroup2: 123 | header: "Choice group two {{ echo('blah') }}:" 124 | options: 125 | - text: "{{state.postedData[state.lastPostedDataRandomId].headers.userAgent}}" 126 | value: "{{state.postedData[state.lastPostedDataRandomId].headers.userAgent}}" 127 | 128 | testEmbeddedJsonEvent: 129 | notify: 130 | message: | 131 | Here is some JSON from c:\windowspath\test and "quotes" { "dog":"beagle" } 132 | 133 | # example of disabling something inheriterd from a base 134 | test: 135 | event-handlers: 136 | another-event: 137 | notify: 138 | enabled: False 139 | 140 | # example for manual choice generators 141 | test2: 142 | event-handlers: 143 | blah-event: 144 | manual-choice: 145 | title: "test manual choice with choice-generators" 146 | 147 | capture-response-data: 148 | - from: "{{ body|json_dumps }}" 149 | to: state.lastPostedHttpResponseFromManualChoice 150 | 151 | choice-generators: 152 | testGenerator1: 153 | 154 | # The cicdContextData JsonPath that should yield a dictionary 155 | # This can also OPTIONALLY be a jinja2 template that will be parsed to 156 | # render the jsonPath expression that will then be used to find 157 | # the foreach dictionary that will be iterated over 158 | foreach: 'state.choiceGeneratorItems' 159 | 160 | # the name that each key within the yielded `foreach` can be 161 | # referenced as within the template below 162 | iterator: 'currentItem' 163 | 164 | # the template, which will render out one or more "choices" 165 | # that will be appended to the overall list of manual-choice.choices 166 | template: > 167 | testkey-{{currentItem.name}}: 168 | header: | 169 | {{'{{'}} echo('{{currentItem.name}}') {{'}}'}} 170 | options: 171 | - style: primary 172 | value: "{{'{{'}} echo('{{currentItem.name}} {{currentItem.description}}') {{'}}'}}" 173 | text: '{{currentItem.name}}' 174 | - style: primary 175 | value: "{{'{{'}} echo('{{currentItem.name}} {{currentItem.description}}') {{'}}'}}" 176 | text: '{{currentItem.name}}' 177 | 178 | choices: 179 | choiceGroup1: 180 | header: "Choice group one:" 181 | options: 182 | - text: "Choice 1" 183 | value: "c1" 184 | - text: "Choice 2" 185 | value: "c2" -------------------------------------------------------------------------------- /examples/tekton/pipelines/start/tasks/start.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: tekton.dev/v1beta1 3 | kind: Task 4 | metadata: 5 | name: start 6 | spec: 7 | 8 | workspaces: 9 | 10 | - name: cicdstatemgr-configs 11 | description: The workspace where cicdstatemgr configs reside 12 | mountPath: /workspace/cicdstatemgr-configs 13 | readOnly: true 14 | 15 | - name: cicdstatemgr-secrets 16 | description: The workspace where cicdstatemgr secrets reside 17 | mountPath: /workspace/cicdstatemgr-secrets 18 | readOnly: true 19 | 20 | results: 21 | - name: last-exit-code 22 | description: The last exit code of this task 23 | 24 | - name: cicdContextDataId 25 | description: The CICD context data id 26 | 27 | params: 28 | 29 | - name: cicdContextName 30 | type: string 31 | 32 | - name: version 33 | type: string 34 | default: v1 35 | 36 | - name: relatedCicdContextDataId 37 | type: string 38 | description: 39 | the related cicdContextDataId 40 | default: "none" 41 | 42 | - name: relatedPipelineRunUid 43 | type: string 44 | description: 45 | the related pipelineRunUid 46 | default: "none" 47 | 48 | - name: relatedPipelineName 49 | type: string 50 | description: 51 | the related pipelineName 52 | default: "none" 53 | 54 | - name: pipelineRunUid 55 | type: string 56 | description: 57 | the pipelineRun uid 58 | default: NONE 59 | 60 | - name: invokerUid 61 | type: string 62 | description: 63 | The invokerUid triggering this 64 | default: tekton-deploy-task 65 | 66 | - name: triggeredBySystem 67 | type: string 68 | description: 69 | The system that triggered this 70 | 71 | - name: triggeredByDescription 72 | type: string 73 | description: 74 | The system that triggered this description 75 | 76 | - name: gitAppName 77 | type: string 78 | description: 79 | The git app name a pipeline is being started for 80 | 81 | - name: appTag 82 | type: string 83 | description: 84 | The application tag 85 | 86 | - name: commitId 87 | type: string 88 | default: none 89 | description: 90 | The application commitId 91 | 92 | - name: originalRequestBody 93 | type: string 94 | 95 | - name: uniqueString 96 | type: string 97 | 98 | - name: gitCloneUrl 99 | type: string 100 | 101 | steps: 102 | 103 | #---------------------------------- 104 | # STEP init CICD context data 105 | #---------------------------------- 106 | - name: init-cicd-context-data 107 | image: bitsofinfo/cicdstatemgr:1.2.4 108 | command: 109 | - /bin/bash 110 | args: 111 | - -c 112 | - | 113 | 114 | echo -n '$(inputs.params.originalRequestBody)' > /tekton/results/originalRequestBody 115 | 116 | # ok proceed w/ clone so we can get our required pipeline file 117 | git clone --depth 1 --branch $(inputs.params.appTag) $(inputs.params.gitCloneUrl) /tmp/$(inputs.params.gitAppName) 118 | 119 | GIT_CLONE_EXIT_CODE=$? 120 | 121 | if [ "$GIT_CLONE_EXIT_CODE" != "0" ] || [ ! -d "/tmp/$(inputs.params.gitAppName)/.git" ]; then 122 | 123 | echo "Git clone failed with exit code: $GIT_CLONE_EXIT_CODE OR /tmp/$(inputs.params.gitAppName)/.git does not exist!" 124 | echo -n 1 > /tekton/results/last-exit-code 125 | 126 | else 127 | 128 | # adjust appName 129 | GIT_APP_NAME="$(inputs.params.gitAppName)" 130 | APP_NAME=${GIT_APP_NAME} 131 | 132 | 133 | INITIALIZED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ") 134 | 135 | CICD_CONTEXT_DATA_ID="${GIT_APP_NAME}-$(inputs.params.appTag)-$(inputs.params.version)-$(inputs.params.cicdContextName)-$(inputs.params.uniqueString)" 136 | 137 | cicdstatemgr \ 138 | --config /workspace/cicdstatemgr-configs/cicdstatemgr-config.yaml \ 139 | --secrets /workspace/cicdstatemgr-secrets/cicdstatemgr-secrets.yaml \ 140 | \ 141 | --init-new $CICD_CONTEXT_DATA_ID \ 142 | --init-bases-dir /workspace/cicdstatemgr-configs/ \ 143 | --init-app-config-file /tmp/$(inputs.params.gitAppName)/app-pipeline-config.yaml \ 144 | --init-cicd-context-name $(inputs.params.cicdContextName) \ 145 | \ 146 | --set "state.cicdVersion=$(inputs.params.version)" \ 147 | --set "state.appName=${APP_NAME}" \ 148 | --set "state.appTag=$(inputs.params.appTag)" \ 149 | --set "state.gitAppName=${GIT_APP_NAME}" \ 150 | --set "state.commitId=$(inputs.params.commitId)" \ 151 | --set "state.gitCloneUrl=$(inputs.params.gitCloneUrl)" \ 152 | \ 153 | --set "state.start.$(inputs.params.pipelineRunUid).security.invokerUid=$(inputs.params.invokerUid)" \ 154 | \ 155 | --set "state.start.$(inputs.params.pipelineRunUid).initializedAt=$INITIALIZED_AT" \ 156 | --set 'state.start.$(inputs.params.pipelineRunUid).params.request.body=file:///tekton/results/originalRequestBody' \ 157 | --set "state.start.$(inputs.params.pipelineRunUid).params.pipelineRunUid=$(inputs.params.pipelineRunUid)" \ 158 | --set "state.start.$(inputs.params.pipelineRunUid).params.pipelineName=start" \ 159 | --set "state.start.$(inputs.params.pipelineRunUid).params.cicdContextName=$(inputs.params.cicdContextName)" \ 160 | --set "state.start.$(inputs.params.pipelineRunUid).params.cicdContextDataId=$CICD_CONTEXT_DATA_ID" \ 161 | --set "state.start.$(inputs.params.pipelineRunUid).params.invokerUid=$(inputs.params.invokerUid)" \ 162 | --set "state.start.$(inputs.params.pipelineRunUid).params.relatedCicdContextDataId=$(inputs.params.relatedCicdContextDataId)" \ 163 | --set "state.start.$(inputs.params.pipelineRunUid).params.relatedPipelineRunUid=$(inputs.params.relatedPipelineRunUid)" \ 164 | --set "state.start.$(inputs.params.pipelineRunUid).params.relatedPipelineName=$(inputs.params.relatedPipelineName)" \ 165 | --set "state.start.$(inputs.params.pipelineRunUid).params.triggeredBySystem=$(inputs.params.triggeredBySystem)" \ 166 | --set "state.start.$(inputs.params.pipelineRunUid).params.triggeredByDescription=$(inputs.params.triggeredByDescription)" \ 167 | --handle-event start=init \ 168 | --tmpl-ctx-var "event.data=state.start.$(inputs.params.pipelineRunUid)" 169 | 170 | 171 | INIT_NEW_EXIT_CODE=$? 172 | 173 | # otherwise proceed 174 | echo 175 | 176 | cat /tekton/results/cicdContextDataId 177 | echo 178 | echo 179 | 180 | cat /tekton/results/cicdContextDataJson 181 | echo 182 | echo 183 | 184 | echo 185 | 186 | if [ "$INIT_NEW_EXIT_CODE" != "0" ]; then echo "INIT NEW CONTEXT FAILED"; else echo "INIT NEW CONTEXT SUCCEEDED" ; fi 187 | echo 188 | 189 | echo -n $INIT_NEW_EXIT_CODE > /tekton/results/last-exit-code 190 | 191 | # exit fast if was a failure above 192 | if [ "$INIT_NEW_EXIT_CODE" != "0" ]; then 193 | exit $INIT_NEW_EXIT_CODE 194 | fi 195 | 196 | fi 197 | 198 | 199 | -------------------------------------------------------------------------------- /examples/basics/config.yaml: -------------------------------------------------------------------------------- 1 | #----------------------------------- 2 | # The start of the cicdstatemgr 3 | # configuration file. 4 | #----------------------------------- 5 | cicdstatemgr: 6 | 7 | # 8 | # Datasources: required 9 | # 10 | # Here you can list one or more datasource 11 | # of any of the available types as supported 12 | # under the datasources sub-package. 13 | # 14 | # ONE of the datasources should be flagged 15 | # as isPrimary=true to indicate it is authoritative 16 | # 17 | datasources: 18 | 19 | # 20 | # The redis datasource 21 | # 22 | # you can configure the AUTH username/password 23 | # in a separate "secrets.yaml", with the same structure 24 | redis: 25 | host: localhost 26 | port: 6379 27 | isPrimary: true 28 | 29 | 30 | # 31 | # YAML file output 32 | # 33 | # path: if relative will be assumed relative from where 34 | # the cicdstatemgr cmd's working directory when run 35 | # Can also just be absolute 36 | yamlfile: 37 | path: localdata/cicdContextData.yaml 38 | 39 | # 40 | # JSON file output 41 | # 42 | # path: if relative will be assumed relative from where 43 | # the cicdstatemgr cmd's working directory when run 44 | # Can also just be absolute 45 | jsonfile: 46 | path: localdata/cicdContextData.json 47 | 48 | # 49 | # SHELL file output - a file that can be sourced to 50 | # expose all data as ENV vars 51 | # 52 | # path: if relative will be assumed relative from where 53 | # the cicdstatemgr cmd's working directory when run 54 | # Can also just be absolute 55 | shellfile: 56 | path: localdata/cicdContextData.sh 57 | 58 | # you can list out key names that will be excluded 59 | # from shell VARIABLE generation completely 60 | excludeKeyNames: 61 | - jinja2Macros 62 | - testGenerator1 63 | 64 | # 65 | # ID file output = only contains the cicdContextData ID 66 | # 67 | # path: if relative will be assumed relative from where 68 | # the cicdstatemgr cmd's working directory when run 69 | # Can also just be absolute 70 | idfile: 71 | path: localdata/cicdContextData.id 72 | 73 | 74 | 75 | 76 | # 77 | # This is the default baseline configuration for 78 | # the 'trigger-pipeline' event handler which can 79 | # be referenced in any individual app's pipeline-config.yaml 80 | # under any: 81 | # [pipelineName].event-handlers.[eventName].trigger-pipeline section 82 | # 83 | # 84 | trigger: 85 | 86 | # The URL all POSTs will be sent to 87 | url: https://postman-echo.com/post 88 | 89 | # Headers that will be automatically applied 90 | # jinja2 references are valid here 91 | headers: 92 | - name: "test-header-1" 93 | value: "{{secretData.someSuperSecret}}" 94 | - name: "test-header-2" 95 | value: "{{state.testHeader2Value}}" 96 | - name: "random-header" 97 | value: "{{random()}}" 98 | 99 | # these argument are always sent in the payload that 100 | # a 'trigger-pipeline' event handler sends in 101 | # addition to ones that you can optionally configure 102 | # in each individual event handler config in a pipeline 103 | # config yaml file under its trigger-pipeline.args section 104 | # the "trigger-pipeline.name" can be referenced here in the 105 | # context variable "triggerPipeline" 106 | auto-args: 107 | autoArg1: "{{state.triggerAutoArg1}}" 108 | randomId: "{{random()}}" 109 | triggerPipelineName: "{{triggerPipeline.name}}" 110 | 111 | 112 | # 113 | # This is the default baseline configuration for 114 | # the 'notify' event handler which can 115 | # be referenced in any individual app's pipeline-config.yaml 116 | # under any: 117 | # [pipelineName].event-handlers.[eventName].notify section 118 | # 119 | # 120 | notify: 121 | 122 | # 123 | # After a notify POST is sent to the endpoint the response 124 | # body (unmarshalled from JSON) can be parsed via the 125 | # 'auto-capture-response-data' config below based on one or 126 | # more items below. 127 | # 128 | # Each array element below defines a individual instruction 129 | # set for data to capture from the 'body'. 130 | # 131 | # from: jinja2 template that when parsed yields a string value 132 | # to: sets the value yielded via the 'from:' to this target in the cicdContextData 133 | # note the 'to' value can have jinja2 template syntax in it as well 134 | # 135 | auto-capture-response-data: 136 | # the slack thread ID, only set if not previously set 137 | - from: | 138 | {%- if body.data -%} 139 | {{- body.data.message -}} 140 | {%- endif -%} 141 | to: state.postedData.{{body.data.randomId}}.body.message 142 | 143 | - from: | 144 | {%- if body.headers -%} 145 | {{- body.headers['user-agent'] -}} 146 | {%- endif -%} 147 | to: state.postedData.{{body.data.randomId}}.headers.userAgent 148 | 149 | - from: | 150 | {%- if body.data -%} 151 | {{- body.data.randomId -}} 152 | {%- endif -%} 153 | to: state.lastPostedDataRandomId 154 | 155 | # 156 | # Configuration for the slack http client within cicdstatmgr 157 | # used for the responder/notify/manual-choice handlers 158 | # For this example we are just sending everything to postman echo 159 | # 160 | slack: 161 | url: https://postman-echo.com/post 162 | orgUrl: http://bitsofinfo.slack.com 163 | 164 | # 165 | # Configuration for the Tekton dashboard 166 | # 167 | dashboard: 168 | pipelineRunUrl: http://localhost/#/namespaces/tekton-pipelines/pipelineruns 169 | 170 | # 171 | # Base templates used by the manual-choice and notify event handlers 172 | # 173 | templates: 174 | 175 | # The notify handler constructs a context containing each handler invocations 176 | # rendered message, target channel and the cicdContextData in the jinja2 context 177 | # this will render the body to be POSTED to the notification endpoint 178 | notify: > 179 | { 180 | "channel": "{{channel}}", 181 | "message": "{{notify.message|json_dumps(stripLeadingTrailingQuotes=True)}}", 182 | "randomId": "{{random()}}" 183 | } 184 | 185 | # The manual-choice handler constructs a context containing each handler invocations 186 | # rendered manualChoice objects, target channel and the cicdContextData in the jinja2 context 187 | # this will render the body to be POSTED to the notification endpoint 188 | manual-choice: > 189 | { 190 | "channel": "{{channel}}", 191 | "randomId": "{{random()}}", 192 | "choices": [ 193 | {% for choiceName,choice in manualChoice.choices.items() %} 194 | { 195 | "header": "{{choice.header}}", 196 | "options": [ 197 | {% for option in choice.options %} 198 | { 199 | "value": "{{option.value}}", 200 | "text": "{{option.text}}" 201 | } {{ "," if not loop.last }} 202 | {% endfor %} 203 | ] 204 | } {{ "," if not loop.last }} 205 | {% endfor %} 206 | ] 207 | } 208 | 209 | # The respond handler constructs a context containing each handler invocations 210 | # rendered message object, target channel and the cicdContextData in the jinja2 context 211 | # this will render the body to be POSTED to the notification endpoint 212 | respond: > 213 | { 214 | "response_text": "{{respond.message}}", 215 | "someArbitraryKey":"{{respond.someArbitraryKey}}" 216 | } -------------------------------------------------------------------------------- /examples/basics/test/set.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #----------------- 4 | # SET.md 5 | #----------------- 6 | 7 | cicdstatemgr \ 8 | --config config.yaml \ 9 | --secrets secrets.yaml \ 10 | --id "context-data-id-1" \ 11 | --set "state.key1=value1" 12 | 13 | cicdstatemgr \ 14 | --config config.yaml \ 15 | --secrets secrets.yaml \ 16 | --id "context-data-id-1" \ 17 | --set "state.key1=value1" \ 18 | --set "state.key2=value2" 19 | 20 | echo "simple body contents" > set.simple.file 21 | 22 | cicdstatemgr \ 23 | --config config.yaml \ 24 | --secrets secrets.yaml \ 25 | --id "context-data-id-1" \ 26 | --set 'state.fileBody=file://set.simple.file' 27 | 28 | VALUE=$(cicdstatemgr \ 29 | --config config.yaml \ 30 | --secrets secrets.yaml \ 31 | --id "context-data-id-1" \ 32 | --get 'state.fileBody') 33 | if [ "$VALUE" != 'simple body contents' ]; then 34 | echo 35 | echo "FAIL: GET: state.fileBody != simple body contents" 36 | echo 37 | exit 1 38 | else 39 | echo 40 | echo "OK: GET: state.fileBody" 41 | echo 42 | fi 43 | 44 | 45 | echo "[1,2,3]" > set.simple.json 46 | 47 | cicdstatemgr \ 48 | --config config.yaml \ 49 | --secrets secrets.yaml \ 50 | --id "context-data-id-1" \ 51 | --set 'state.fileBody=file://set.simple.json' 52 | 53 | 54 | VALUE=$(cicdstatemgr \ 55 | --config config.yaml \ 56 | --secrets secrets.yaml \ 57 | --id "context-data-id-1" \ 58 | --get 'state.fileBody[2]') 59 | if [ "$VALUE" != '3' ]; then 60 | echo 61 | echo "FAIL: GET: state.fileBody[2] != 3" 62 | echo 63 | exit 1 64 | else 65 | echo 66 | echo "OK: GET: state.fileBody[2]" 67 | echo 68 | fi 69 | 70 | 71 | 72 | echo '{"dog":"beagle", "bark":{"quality":"high","volume":"loud"}}' > set.simple.json 73 | 74 | cicdstatemgr \ 75 | --config config.yaml \ 76 | --secrets secrets.yaml \ 77 | --id "context-data-id-1" \ 78 | --set 'state.fileBody=file://set.simple.json' 79 | 80 | VALUE=$(cicdstatemgr \ 81 | --config config.yaml \ 82 | --secrets secrets.yaml \ 83 | --id "context-data-id-1" \ 84 | --get 'state.fileBody.bark.quality') 85 | if [ "$VALUE" != 'high' ]; then 86 | echo 87 | echo "FAIL: GET: json: state.fileBody.bark.quality != high" 88 | echo 89 | exit 1 90 | else 91 | echo 92 | echo "OK: GET: json: state.fileBody.bark.quality" 93 | echo 94 | fi 95 | 96 | 97 | 98 | printf "dog: beagle\nbark:\n quality: high\n volume: loud\n" > set.simple.yaml 99 | 100 | cicdstatemgr \ 101 | --config config.yaml \ 102 | --secrets secrets.yaml \ 103 | --id "context-data-id-1" \ 104 | --set 'state.fileBody=file://set.simple.yaml' 105 | 106 | VALUE=$(cicdstatemgr \ 107 | --config config.yaml \ 108 | --secrets secrets.yaml \ 109 | --id "context-data-id-1" \ 110 | --get 'state.fileBody.bark.quality') 111 | if [ "$VALUE" != 'high' ]; then 112 | echo 113 | echo "FAIL: GET: yaml: state.fileBody.bark.quality != state.fileBody.bark.quality" 114 | echo 115 | exit 1 116 | else 117 | echo 118 | echo "OK: GET: yaml: state.fileBody.bark.quality" 119 | echo 120 | fi 121 | 122 | 123 | 124 | 125 | 126 | # set: list[] 127 | cicdstatemgr \ 128 | --config config.yaml \ 129 | --secrets secrets.yaml \ 130 | --id "context-data-id-1" \ 131 | --set 'state.testList[]=a,b,c' 132 | 133 | VALUE=$(cicdstatemgr \ 134 | --config config.yaml \ 135 | --secrets secrets.yaml \ 136 | --id "context-data-id-1" \ 137 | --get 'state.testList[2]') 138 | if [ "$VALUE" != 'c' ]; then 139 | echo 140 | echo "FAIL: GET: yaml: state.testList[2] != c" 141 | echo 142 | exit 1 143 | else 144 | echo 145 | echo "OK: GET: yaml: state.testList[2] == c" 146 | echo 147 | fi 148 | 149 | 150 | 151 | 152 | 153 | cicdstatemgr \ 154 | --config config.yaml \ 155 | --secrets secrets.yaml \ 156 | --id "context-data-id-1" \ 157 | --set 'state.testList[]=d,e,f' 158 | 159 | VALUE=$(cicdstatemgr \ 160 | --config config.yaml \ 161 | --secrets secrets.yaml \ 162 | --id "context-data-id-1" \ 163 | --get 'state.testList') 164 | if [ "$VALUE" != '["a", "b", "c", "d", "e", "f"]' ]; then 165 | echo 166 | echo "FAIL: GET: yaml: state.testList != [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"]" 167 | echo 168 | exit 1 169 | else 170 | echo 171 | echo "OK: GET: yaml: state.testList == [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"]" 172 | echo 173 | fi 174 | 175 | 176 | 177 | 178 | cicdstatemgr \ 179 | --config config.yaml \ 180 | --secrets secrets.yaml \ 181 | --id "context-data-id-1" \ 182 | --set 'state.testList[]=x,y,z' 183 | 184 | VALUE=$(cicdstatemgr \ 185 | --config config.yaml \ 186 | --secrets secrets.yaml \ 187 | --id "context-data-id-1" \ 188 | --get 'state.testList') 189 | if [ "$VALUE" != '["a", "b", "c", "d", "e", "f", "x", "y", "z"]' ]; then 190 | echo 191 | echo "FAIL: GET: yaml: state.testList != [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"x\", \"y\", \"z\"]" 192 | echo 193 | exit 1 194 | else 195 | echo 196 | echo "OK: GET: yaml: state.testList == [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\", \"x\", \"y\", \"z\"]" 197 | echo 198 | fi 199 | 200 | 201 | 202 | 203 | cicdstatemgr \ 204 | --config config.yaml \ 205 | --secrets secrets.yaml \ 206 | --id "context-data-id-1" \ 207 | --set 'state.testList[]=[]' 208 | 209 | VALUE=$(cicdstatemgr \ 210 | --config config.yaml \ 211 | --secrets secrets.yaml \ 212 | --id "context-data-id-1" \ 213 | --get 'state.testList') 214 | if [ "$VALUE" != '[]' ]; then 215 | echo 216 | echo "FAIL: GET: yaml: state.testList != []" 217 | echo 218 | exit 1 219 | else 220 | echo 221 | echo "OK: GET: yaml: state.testList == []" 222 | echo 223 | fi 224 | 225 | 226 | 227 | 228 | cicdstatemgr \ 229 | --config config.yaml \ 230 | --secrets secrets.yaml \ 231 | --id "context-data-id-1" \ 232 | --set 'state.testList[]=a' 233 | 234 | VALUE=$(cicdstatemgr \ 235 | --config config.yaml \ 236 | --secrets secrets.yaml \ 237 | --id "context-data-id-1" \ 238 | --get 'state.testList') 239 | if [ "$VALUE" != '["a"]' ]; then 240 | echo 241 | echo "FAIL: GET: yaml: state.testList != [\"a\"]" 242 | echo 243 | exit 1 244 | else 245 | echo 246 | echo "OK: GET: yaml: state.testList == [\"a\"]" 247 | echo 248 | fi 249 | 250 | 251 | 252 | 253 | 254 | ## SET (set{}) 255 | cicdstatemgr \ 256 | --config config.yaml \ 257 | --secrets secrets.yaml \ 258 | --id "context-data-id-1" \ 259 | --set 'state.testSet{}=a,a,a' 260 | 261 | VALUE=$(cicdstatemgr \ 262 | --config config.yaml \ 263 | --secrets secrets.yaml \ 264 | --id "context-data-id-1" \ 265 | --get 'state.testSet') 266 | if [ "$VALUE" != '["a"]' ]; then 267 | echo 268 | echo "FAIL: GET: yaml: state.testSet != [\"a\"]" 269 | echo 270 | exit 1 271 | else 272 | echo 273 | echo "OK: GET: yaml: state.testSet == [\"a\"]" 274 | echo 275 | fi 276 | 277 | 278 | cicdstatemgr \ 279 | --config config.yaml \ 280 | --secrets secrets.yaml \ 281 | --id "context-data-id-1" \ 282 | --set 'state.testSet{}=a,b,b' 283 | 284 | VALUE=$(cicdstatemgr \ 285 | --config config.yaml \ 286 | --secrets secrets.yaml \ 287 | --id "context-data-id-1" \ 288 | --get 'state.testSet') 289 | if [ "$VALUE" != '["a", "b"]' ]; then 290 | echo 291 | echo "FAIL: GET: yaml: state.testSet != [\"a\", \"b\"]" 292 | echo 293 | exit 1 294 | else 295 | echo 296 | echo "OK: GET: yaml: state.testSet == [\"a\", \"b\"]" 297 | echo 298 | fi 299 | 300 | 301 | 302 | cicdstatemgr \ 303 | --config config.yaml \ 304 | --secrets secrets.yaml \ 305 | --id "context-data-id-1" \ 306 | --set 'state.testSet{}=[]' 307 | 308 | VALUE=$(cicdstatemgr \ 309 | --config config.yaml \ 310 | --secrets secrets.yaml \ 311 | --id "context-data-id-1" \ 312 | --get 'state.testSet') 313 | if [ "$VALUE" != '[]' ]; then 314 | echo 315 | echo "FAIL: GET: yaml: state.testSet != []" 316 | echo 317 | exit 1 318 | else 319 | echo 320 | echo "OK: GET: yaml: state.testSet == []" 321 | echo 322 | fi 323 | 324 | 325 | cicdstatemgr \ 326 | --config config.yaml \ 327 | --secrets secrets.yaml \ 328 | --id "context-data-id-1" \ 329 | --set 'state.testSet{}=a,b,b,c,d,d' 330 | 331 | VALUE=$(cicdstatemgr \ 332 | --config config.yaml \ 333 | --secrets secrets.yaml \ 334 | --id "context-data-id-1" \ 335 | --get 'state.testSet') 336 | if [ "$VALUE" != '["a", "b", "c", "d"]' ]; then 337 | echo 338 | echo "FAIL: GET: yaml: state.testSet != [\"a\", \"b\", \"c\", \"d\"]" 339 | echo 340 | exit 1 341 | else 342 | echo 343 | echo "OK: GET: yaml: state.testSet == [\"a\", \"b\", \"c\", \"d\"]" 344 | echo 345 | fi -------------------------------------------------------------------------------- /examples/tekton/pipelines/confs/cicdstatemgr-config.yaml: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Config for: 4 | # https://github.com/bitsofinfo/cicdstatemgr 5 | # 6 | cicdstatemgr: 7 | 8 | datasources: 9 | 10 | # 11 | # The redis datasource 12 | # 13 | # you can configure the AUTH username/password 14 | # in a separate "secrets.yaml", with the same structure 15 | redis: 16 | host: cicd-redis-server.tekton-pipelines 17 | port: 6379 18 | isPrimary: true 19 | 20 | # 21 | # YAML file output 22 | # 23 | # path: if relative will be assumed relative from where 24 | # the cicdstatemgr cmd's working directory when run 25 | # Can also just be absolute 26 | yamlfile: 27 | path: /tekton/results/cicdContextDataYaml 28 | 29 | # 30 | # JSON file output 31 | # 32 | # path: if relative will be assumed relative from where 33 | # the cicdstatemgr cmd's working directory when run 34 | # Can also just be absolute 35 | jsonfile: 36 | path: /tekton/results/cicdContextDataJson 37 | 38 | # 39 | # SHELL file output - a file that can be sourced to 40 | # expose all data as ENV vars 41 | # 42 | # path: if relative will be assumed relative from where 43 | # the cicdstatemgr cmd's working directory when run 44 | # Can also just be absolute 45 | shellfile: 46 | path: /tekton/results/cicdContextDataShell 47 | 48 | # you can list out key names that will be excluded 49 | # from shell VARIABLE generation completely 50 | excludeKeyNames: 51 | - jinja2Macros 52 | 53 | # 54 | # ID file output = only contains the cicdContextData ID 55 | # 56 | # path: if relative will be assumed relative from where 57 | # the cicdstatemgr cmd's working directory when run 58 | # Can also just be absolute 59 | idfile: 60 | path: /tekton/results/cicdContextDataId 61 | 62 | # 63 | # This is the configuration for the 'trigger-pipeline' event handler. 64 | # 65 | trigger: 66 | 67 | # The URL all POSTs will be sent to 68 | url: @NGROK_URL@ 69 | 70 | # Headers that will be automatically applied 71 | # jinja2 references are valid here 72 | headers: 73 | - name: "x-cicdstatemgr-autotrigger-key" 74 | value: "{{secretData.trigger.token}}" 75 | - name: "x-cicdstatemgr-pipeline-name" 76 | value: "{{triggerPipeline.name}}" 77 | 78 | # these argument are always sent in the payload that 79 | # a 'trigger-pipeline' event handler sends in 80 | # addition to ones that you can optionally configure 81 | # in each individual event handler config in a pipeline 82 | # config yaml file 83 | auto-args: 84 | cicdContextDataId: "{{state.cicdContextDataId}}" 85 | appName: "{{state.appName}}" 86 | appTag: "{{state.appTag}}" 87 | gitAppName: "{{state.gitAppName}}" 88 | cicdContextName: "{{state.cicdContextName}}" 89 | triggeredBySystem: "trigger-pipeline" 90 | triggeredByDescription: "Auto triggered by {{event.data.security.invokerUid}}" 91 | invokerUid: "{{event.data.security.invokerUid}}" 92 | relatedPipelineName: "{{event.data.params.pipelineName}}" 93 | relatedPipelineRunUid: "{{event.data.params.pipelineRunUid}}" 94 | relatedCicdContextDataId: "{{event.data.params.cicdContextDataId}}" 95 | 96 | # 97 | # This is the configuration for the 'notify' event handler. 98 | # 99 | notify: 100 | 101 | # 102 | # After a notify POST is sent to the endpoint the response 103 | # body (unmarshalled from JSON) can be parsed via the 104 | # 'auto-capture-response-data' config below based on one or 105 | # more items below. 106 | # 107 | # Each array element below defines a individual instruction 108 | # set for data to capture from the 'body'. 109 | # 110 | # from: jinja2 template that when parsed yields a string value 111 | # to: sets the value yielded via the 'from:' to this target in the cicdContextData 112 | # 113 | auto-capture-response-data: 114 | # the slack thread ID, only set if not previously set 115 | - from: | 116 | {%- if not state.slack or not state.slack.thread_ts -%} 117 | {{ body.ts }} 118 | {%- endif -%} 119 | to: state.slack.thread_ts 120 | 121 | # the slack channel 122 | - from: "{{body.channel}}" 123 | to: state.slack.channel 124 | 125 | # the last message posted, its thread_ts identifier (i.e. id within the thread) 126 | - from: "{{body.message.thread_ts}}" 127 | to: state.slack.lastResponse.message.thread_ts 128 | 129 | # the last message posted, web link 130 | - from: "{{configData.slack.orgUrl}}/archives/{{body.channel}}/p{{body.ts|replace('.','')}}{% if 'thread_ts' in body.message %}?thread_ts={{body.message.thread_ts}}&cid={{body.channel}}{% endif %}" 131 | to: state.slack.lastMessagePermalink 132 | 133 | 134 | 135 | # 136 | # Configuration for the slack http client within cicdstatmgr 137 | # used for the notify/manual-choice handlers 138 | # 139 | slack: 140 | url: https://slack.com/api/chat.postMessage 141 | orgUrl: @SLACK_ORG_URL@ 142 | 143 | # 144 | # Configuration for the Tekton dashboard 145 | # 146 | dashboard: 147 | pipelineRunUrl: @TEKTON_DASHBOARD_URL@/#/namespaces/tekton-pipelines/pipelineruns 148 | 149 | # 150 | # Base templates used by the manual-choice and notify event handlers 151 | # 152 | templates: 153 | 154 | # The notify handler constructs a context containing each handler invocations 155 | # rendered message, target channel and the cicdContextData in the jinja2 context 156 | # this will render the body to be POSTED to the notification endpoint 157 | notify: > 158 | { 159 | "channel": "{{channel}}", 160 | {% if state.slack and state.slack.thread_ts and state.slack.thread_ts != 'None' %} 161 | "thread_ts": "{{state.slack.thread_ts}}", 162 | {% endif %} 163 | "blocks": [ 164 | { 165 | "type": "section", 166 | "text": { 167 | "type": "mrkdwn", 168 | "text": "{{notify.message}}" 169 | } 170 | } 171 | ] 172 | 173 | } 174 | 175 | # The manual-choice handler constructs a context containing each handler invocations 176 | # rendered manualChoice objects, target channel and the cicdContextData in the jinja2 context 177 | # this will render the body to be POSTED to the notification endpoint 178 | manual-choice: > 179 | { 180 | "channel": "{{channel}}", 181 | {% if state.slack and state.slack.thread_ts and state.slack.thread_ts != 'None' %} 182 | "thread_ts": "{{state.slack.thread_ts}}", 183 | {% endif %} 184 | "text": "{{manualChoice.title}}", 185 | "attachments": [ 186 | {% for choiceName,choice in manualChoice.choices.items() %} 187 | { 188 | "blocks": [ 189 | { 190 | "type": "section", 191 | "text": { 192 | "type": "mrkdwn", 193 | "text": "{{choice.header}}" 194 | } 195 | }, 196 | { 197 | "type": "actions", 198 | "elements": [ 199 | {% for option in choice.options %} 200 | { 201 | "type": "button", 202 | "style": "{{option.style}}", 203 | "value": "{{option.value}}", 204 | "text": { 205 | "type": "plain_text", 206 | "emoji": true, 207 | "text": "{{option.text}}" 208 | } 209 | } {{ "," if not loop.last }} 210 | {% endfor %} 211 | ] 212 | } 213 | ] 214 | } {{ "," if not loop.last }} 215 | {% endfor %} 216 | ] 217 | } 218 | 219 | # The respond handler constructs a context containing each handler invocations 220 | # rendered message object, target channel and the cicdContextData in the jinja2 context 221 | # this will render the body to be POSTED to the notification endpoint 222 | # 223 | # The respond handler can be responding to two different mechanims 224 | # - slash commands: here 'response_type' is valid 225 | # - interactive msgs: here 'replace_original' is valid 226 | respond: > 227 | { 228 | {% if respond.isSlackSlashCommand == 'true' %} 229 | "response_type": "in_channel", 230 | {% else %} 231 | "replace_original": "true", 232 | {% endif %} 233 | "text": "{{respond.message}}" 234 | } 235 | --------------------------------------------------------------------------------