Skip to main content

Run a Generic Shell Script with Spinnaker

Introduction

Spinnaker is a tool specialized in completing Deployments natively, without having to write shell scripts. That said, sometimes when end users may need to run custom logic related to deployments. Spinnaker has a stage called Scriptto help deal with this logic.  However, this execution relies on a Jenkins job under the hood and has been deprecated in favor of the Jenkins stage. If end users want to run a script, then Kubernetes jobs can be used to execute, but then the user will need to develop a Docker container that contains the script. A solution is to use a generic docker container and mount the script in a Kubernetes ConfigMap. In this way, end users can define the script contents as part of the deployment yaml for the ConfigMap.

Prerequisites

N/A

Instructions

Table of Contents

Basic Method Stage: Deploy (Manifest)

apiVersion: v1
data:
script.sh: |-
echo "Hello world!"
kubectl get pods
kind: ConfigMap
metadata:
name: script-configmap
namespace: german
---
apiVersion: batch/v1
kind: Job
metadata:
labels:
app: script-job
name: script-job
namespace: german
spec:
backoffLimit: 2
template:
spec:
containers:
- command:
- sh
- /opt/script/script.sh
image: 'bitnami/kubectl:1.12'
name: script
volumeMounts:
- mountPath: /opt/script
name: script-configmap
readOnly: false
restartPolicy: Never
serviceAccountName: spinnaker-service-account
volumes:
- configMap:
name: script-configmap
name: script-configmap

Here we define a generic Deploy (Manifest) stage, supplying the above text contents. End users define the ConfigMap as well as the Job. The script is part of the ConfigMap, and the Job runs a generic Docker container from the Docker hub with the tool kubectl. The job will execute a command, which is the script mounted in the ConfigMap.

In the situation there is a need to edit the script, this can be done without generating another Docker image.  Another consideration is that jobs can be run only once in Kubernetes and are immutable, so the end user will also need to add a Delete (Manifest) stage for cleaning up/deleting old jobs before another Deploy (manifest) stage is executed.

Retrieving the Shell Script from Version Control

The basic approach is fine for small scripts, but writing something more complex inside a ConfigMap definition is no fun.Fortunately, a trick in Spinnaker allows users to read file contents from an artifact, which can be a GitHub repo file, Bitbucket repo file, AWS S3 file, and any supported Spinnaker artifact that refers to text files.See the complete list of supported artifact sources: https://spinnaker.io/docs/setup/other_config/artifacts/ In the following example, a GitHub repository is used as an artifact source for the script file. End users will a Webhook stage to make a request to Spinnaker’s Clouddriver service.  The service already has all the credentials needed for downloading artifacts.  However, administrators may need to enable and supply those artifact credentials through Spinnaker configuration, if they don't already exist. Clouddriver will use the same logic it already uses in other parts of Spinnaker to download the file and return it to the Webhook stage. The output of this stage (and hence the file) is available in the pipeline execution context, so user can then refer to it using Pipeline Expressions in the ConfigMap definition:

Example of the Webhook stage configuration

Webhook URL:  http://spin-clouddriver:7002/artifacts/fetch
Method: PUT
Payload: {
"artifactAccount": "",
"reference": "https://api.github.com/repos///contents/",
"type": "github/file",
"version": "master"
}

Example of the Deploy (Manifest) - ConfigMap

apiVersion: v1
data:
script.sh: '${#stage("Webhook")["context"]["webhook"]["body"]}'
kind: ConfigMap
metadata:
name: script-configmap
namespace: german

Then the Run job stage can contain only the yaml description of the kubernetes job.