Create a test and production stage

We create a test environment similar to the test environment of the REST service.

#!/usr/bin/jjs -fv

var FileWriter = Java.type("java.io.FileWriter");

var version = $ENV.VERSION;
var kubectl = $ENV.KUBECTL;

var name = "battleapp-frontend-test";
var baseUrl = "disruptor.ninja";
var image = baseUrl + ":30500/robertbrem/battleapp-frontend:" + version;
var replicas = 1;
var port = 80;
var clusterPort = 3001;
var nodePort = 31030;
var deploymentFileName = "deployment.yml";
var serviceFileName = "service.yml";
var registrysecret = "registrykey";
var url = "http://" + baseUrl + ":" + nodePort;
var timeout = 2;

var deleteDeployment = kubectl + " delete deployment " + name;
execute(deleteDeployment);

var dfw = new FileWriter(deploymentFileName);
dfw.write("apiVersion: extensions/v1beta1\n");
dfw.write("kind: Deployment\n");
dfw.write("metadata:\n");
dfw.write("  name: " + name + "\n");
dfw.write("spec:\n");
dfw.write("  replicas: " + replicas + "\n");
dfw.write("  template:\n");
dfw.write("    metadata:\n");
dfw.write("      labels:\n");
dfw.write("        name: " + name + "\n");
dfw.write("    spec:\n");
dfw.write("      containers:\n");
dfw.write("      - resources:\n");
dfw.write("        name: " + name + "\n");
dfw.write("        image: " + image + "\n");
dfw.write("        ports:\n");
dfw.write("        - name: port\n");
dfw.write("          containerPort: " + port + "\n");
dfw.write("        volumeMounts:\n");
dfw.write("        - name: environment\n");
dfw.write("          mountPath: /usr/share/nginx/html/environment\n");
dfw.write("      volumes:\n");
dfw.write("      - name: environment\n");
dfw.write("        configMap:\n");
dfw.write("          name: battleapp-frontend-test\n");
dfw.write("          items:\n");
dfw.write("          - key: environment.json\n");
dfw.write("            path: environment.json\n");
dfw.write("      imagePullSecrets:\n");
dfw.write("      - name: " + registrysecret + "\n");
dfw.close();

var deploy = kubectl + " create -f " + deploymentFileName;
execute(deploy);

var deleteService = kubectl + " delete service " + name;
execute(deleteService);

var sfw = new FileWriter(serviceFileName);
sfw.write("apiVersion: v1\n");
sfw.write("kind: Service\n");
sfw.write("metadata:\n");
sfw.write("  name: " + name + "\n");
sfw.write("  labels:\n");
sfw.write("    name: " + name + "\n");
sfw.write("spec:\n");
sfw.write("  ports:\n");
sfw.write("  - port: " + clusterPort + "\n");
sfw.write("    targetPort: " + port + "\n");
sfw.write("    nodePort: " + nodePort + "\n");
sfw.write("  selector:\n");
sfw.write("    name: " + name + "\n");
sfw.write("  type: NodePort\n");
sfw.close();

var deployService = kubectl + " create -f " + serviceFileName;
execute(deployService);

var testUrl = "curl --write-out %{http_code} --silent --output /dev/null " + url + " --max-time " + timeout;
execute(testUrl);
while ($OUT != "200") {
    $EXEC("sleep 1");
    execute(testUrl);
}

function execute(command) {
    $EXEC(command);
    print($OUT);
    print($ERR);
}

It is important to make this script file executable:

chmod 750 start.js

And the corresponding pipeline entry:

stage "start test environment"
node {
  git url: "http://disruptor.ninja:30130/rob/battleapp-frontend-starttestenv"
  sh "./start.js"
}

After the test environment we add a manual pipeline step:

stage "manual testing"
input "everything ok?"

The next step is the canary release:

#!/usr/bin/jjs -fv

var FileWriter = Java.type("java.io.FileWriter");

var version = $ENV.VERSION;
var kubectl = $ENV.KUBECTL;

var name = "battleapp-frontend";
var nameWithVersion = name + "-" + version;
var image = "disruptor.ninja:30500/robertbrem/" + name + ":" + version;
var replicas = 1;
var port = 80;
var clusterPort = 3002;
var nodePort = 30030;
var deploymentFileName = "deployment.yml";
var serviceFileName = "service.yml";
var registrysecret = "registrykey";
var relativeUrl = "/";
var url = "http://disruptor.ninja:" + nodePort;
var timeout = 2;
var initialDelay = 15;
var readinessProbeTimeout = 10;

var dfw = new FileWriter(deploymentFileName);
dfw.write("apiVersion: extensions/v1beta1\n");
dfw.write("kind: Deployment\n");
dfw.write("metadata:\n");
dfw.write("  name: " + nameWithVersion + "\n");
dfw.write("spec:\n");
dfw.write("  replicas: " + replicas + "\n");
dfw.write("  template:\n");
dfw.write("    metadata:\n");
dfw.write("      labels:\n");
dfw.write("        name: " + name + "\n");
dfw.write("        version: " + version + "\n");
dfw.write("    spec:\n");
dfw.write("      containers:\n");
dfw.write("      - resources:\n");
dfw.write("        name: " + name + "\n");
dfw.write("        image: " + image + "\n");
dfw.write("        ports:\n");
dfw.write("        - name: port\n");
dfw.write("          containerPort: " + port + "\n");
dfw.write("        readinessProbe:\n");
dfw.write("          httpGet:\n");
dfw.write("            path: " + relativeUrl + "\n");
dfw.write("            port: " + port + "\n");
dfw.write("          initialDelaySeconds: " + initialDelay + "\n");
dfw.write("          timeoutSeconds: " + readinessProbeTimeout + "\n");
dfw.write("        volumeMounts:\n");
dfw.write("        - name: environment\n");
dfw.write("          mountPath: /usr/share/nginx/html/environment\n");
dfw.write("      volumes:\n");
dfw.write("      - name: environment\n");
dfw.write("        configMap:\n");
dfw.write("          name: battleapp-frontend\n");
dfw.write("          items:\n");
dfw.write("          - key: environment.json\n");
dfw.write("            path: environment.json\n");
dfw.write("      imagePullSecrets:\n");
dfw.write("      - name: " + registrysecret + "\n");
dfw.close();

var deploy = kubectl + " create -f " + deploymentFileName;
execute(deploy);

var sfw = new FileWriter(serviceFileName);
sfw.write("apiVersion: v1\n");
sfw.write("kind: Service\n");
sfw.write("metadata:\n");
sfw.write("  name: " + name + "\n");
sfw.write("  labels:\n");
sfw.write("    name: " + name + "\n");
sfw.write("spec:\n");
sfw.write("  ports:\n");
sfw.write("  - port: " + clusterPort + "\n");
sfw.write("    targetPort: " + port + "\n");
sfw.write("    nodePort: " + nodePort + "\n");
sfw.write("  selector:\n");
sfw.write("    name: " + name + "\n");
sfw.write("  type: NodePort\n");
sfw.close();

var deployService = kubectl + " create -f " + serviceFileName;
execute(deployService);

var testUrl = "curl --write-out %{http_code} --silent --output /dev/null " + url + " --max-time " + timeout;
execute(testUrl);
while ($OUT != "200") {
    $EXEC("sleep 1");
    execute(testUrl);
}

function execute(command) {
    $EXEC(command);
    print($OUT);
    print($ERR);
}

Make the script file executable:

chmod 750 start.js

The corresponding pipeline entry:

stage "start canary"
input "deploy the canary?"
node {
  git url: "http://disruptor.ninja:30130/rob/battleapp-frontend-canary"
  sh "./start.js"
}

The last step is the production step with the following script:

#!/usr/bin/jjs -fv

var version = $ENV.VERSION;
var kubectl = $ENV.KUBECTL;

var name = "battleapp-frontend";
var url = "http://disruptor.ninja:30030";
var timeout = 2;

var deleteDeployment = kubectl + " delete deployment -l name=" + name + ",version!=" + version;
execute(deleteDeployment);

var testUrl = "curl --write-out %{http_code} --silent --output /dev/null " + url + " --max-time " + timeout;
execute(testUrl);
while ($OUT != "200") {
    $EXEC("sleep 1");
    execute(testUrl);
}

function execute(command) {
    $EXEC(command);
    print($OUT);
    print($ERR);
}

Make the script file executable:

chmod 750 start.js

The corresponding pipeline entry:

stage "go full production"
input "undeploy other versions?"
node {
  git url: "http://disruptor.ninja:30130/rob/battleapp-frontend-prod"
  sh "./start.js"
}

Creation of Kubernetes ConfigMap

In both the test and the production stage we referenced a Kubernetes ConfigMap that we've to create first. Therefore we have to create a folder on the local machine with the name battleapp-frontend and a file environment.json with the following content:

{
  "host": "disruptor.ninja",
  "port": 30080
}

We need also a environment.json file for the test stage. Therefore we create a folder battleapp-frontend-test and a file environment.json with the following content:

{
  "host": "disruptor.ninja",
  "port": 31080
}

Now we create the two ConfigMaps:

kc create configmap battleapp-frontend-test --from-file=battleapp-frontend-test
kc create configmap battleapp-frontend --from-file=battleapp-frontend

We can test display the ConfigMaps with the following command:

kc get configmap battleapp-frontend-test -o yaml
apiVersion: v1
data:
  environment.json: |
    {
      "host": "disruptor.ninja",
      "port": 31080
    }
kind: ConfigMap
metadata:
  creationTimestamp: 2016-12-31T15:54:44Z
  name: battleapp-frontend-test
  namespace: default
  resourceVersion: "1049486"
  selfLink: /api/v1/namespaces/default/configmaps/battleapp-frontend-test
  uid: 72a5399b-cf71-11e6-a836-0050563cad2a