diff --git a/README.md b/README.md index 48bd06e..ba193bf 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,26 @@ npm install screwdriver-executor-k8s-vm ``` +### Initialization +The class provides a couple options that are configurable in the instantiation of this Executor + +| Parameter | Type | Default | Description | +| :------------- | :---- | :----------| :-----------| +| config | Object | | Configuration Object | +| config.kubernetes | Object | {} | Kubernetes configuration Object | +| config.kubernetes.token | String | '' | The JWT token used for authenticating to the Kubernetes cluster. (If not passed in, we will read from `/var/run/secrets/kubernetes.io/serviceaccount/token`.) | +| config.kubernetes.host | String | 'kubernetes.defaults' | The hostname for the Kubernetes cluster (kubernetes) | +| config.ecosystem | Object | | Screwdriver Ecosystem (ui, api, store, etc.) | +| config.launchVersion | String | 'stable' | Launcher container version to use (stable) | +| config.prefix | String | '' |Prefix to container names ("") | +| config.jobsNamespace | String | 'default' | Kubernetes namespace where builds are running on | +| config.baseImage | String | '' | Base image used to start the VM | + + +### Methods + +For more information on `start`, `stop`, and `stats` please see the [executor-base-class]. + ## Testing ```bash @@ -25,7 +45,7 @@ Code licensed under the BSD 3-Clause license. See LICENSE file for terms. [license-image]: https://img.shields.io/npm/l/screwdriver-executor-k8s-vm.svg [issues-image]: https://img.shields.io/github/issues/screwdriver-cd/executor-k8s-vm.svg [issues-url]: https://github.com/screwdriver-cd/executor-k8s-vm/issues -[status-image]: https://cd.screwdriver.cd/pipelines/pipelineid/badge -[status-url]: https://cd.screwdriver.cd/pipelines/pipelineid +[status-image]: https://cd.screwdriver.cd/pipelines/235/badge +[status-url]: https://cd.screwdriver.cd/pipelines/235 [daviddm-image]: https://david-dm.org/screwdriver-cd/executor-k8s-vm.svg?theme=shields.io [daviddm-url]: https://david-dm.org/screwdriver-cd/executor-k8s-vm diff --git a/config/pod.yaml.tim b/config/pod.yaml.tim index 0e7fc09..6b6d231 100644 --- a/config/pod.yaml.tim +++ b/config/pod.yaml.tim @@ -4,52 +4,62 @@ metadata: name: "{{build_id_with_prefix}}" labels: sdbuild: "{{build_id_with_prefix}}" - app: screwdriver + app: screwdriver-vm tier: builds annotations: - pod.alpha.kubernetes.io/init-containers: '[ - { - "name": "launcher", - "image": "screwdrivercd/launcher:{{launcher_version}}", - "command": ["/bin/sh", "-c", "cp -a /opt/sd/* /opt/launcher"], - "volumeMounts": [ - { - "name": "screwdriver", - "mountPath": "/opt/launcher" - } - ] - } - ]' + pod.alpha.kubernetes.io/init-containers: '[ + { + "name": "launcher", + "image": "screwdrivercd/launcher:{{launcher_version}}", + "command": ["/bin/sh", "-c", "cp -a /opt/sd/* /opt/launcher"], + "volumeMounts": [ + { + "name": "sdlauncher", + "mountPath": "/opt/launcher" + } + ] + } + ]' spec: - serviceAccount: {{service_account}} restartPolicy: Never containers: - - name: build - image: {{container}} + - name: vm-launcher + image: {{base_image}} + imagePullPolicy: Always + securityContext: + privileged: true resources: limits: memory: 2Gi - command: - - "/opt/sd/tini" - - "--" - - "/bin/sh" - - "-c" - # Run the launcher and logservice in the background - # Wait for both background jobs to complete - - | - /opt/sd/launch --api-uri {{api_uri}} --emitter /opt/sd/emitter {{build_id}} & - /opt/sd/logservice --emitter /opt/sd/emitter --api-uri {{store_uri}} --build {{build_id}} & - wait $(jobs -p) + command: ["/sd/hyper-runner.sh"] + args: [ + "--container", "{{container}}", + "--api_uri", "{{api_uri}}", + "--store_uri", "{{store_uri}}", + "--build_id", "{{build_id}}", + "--id_with_prefix", "{{build_id_with_prefix}}", + "--build_token", "{{token}}" + ] volumeMounts: - - mountPath: /opt/sd - name: screwdriver - - mountPath: /sd - name: workspace - env: - - name: SD_TOKEN - value: "{{token}}" + - mountPath: /var/run + name: hyper-socket + - mountPath: /var/sd-workspaces + name: sd-workspaces + lifecycle: + preStop: + exec: + command: ["/bin/bash", "-c", "sleep 2; # This sleep is to wait for hyper to gracefully exit normally on successful build + rm -rf /var/sd-workspaces/{{build_id_with_prefix}}; + hyperctl rm builder-{{build_id_with_prefix}}"] volumes: - - name: screwdriver - emptyDir: {} - - name: workspace - emptyDir: {} + - name: hyper-socket + hostPath: + path: /var/run + + - name: sd-workspaces + hostPath: + path: /opt/screwdriver + + - name: sdlauncher + hostPath: + path: /opt/screwdriver/{{build_id_with_prefix}}/sdlauncher diff --git a/index.js b/index.js index a86748e..a7fdec9 100644 --- a/index.js +++ b/index.js @@ -8,7 +8,7 @@ const tinytim = require('tinytim'); const yaml = require('js-yaml'); const fs = require('fs'); -class K8sExecutor extends Executor { +class K8sVMExecutor extends Executor { /** * Constructor * @method constructor @@ -19,8 +19,8 @@ class K8sExecutor extends Executor { * @param {Object} options.kubernetes Kubernetes configuration * @param {String} [options.kubernetes.token] API Token (loaded from /var/run/secrets/kubernetes.io/serviceaccount/token if not provided) * @param {String} [options.kubernetes.host=kubernetes.default] Kubernetes hostname - * @param {String} [options.kubernetes.serviceAccount=default] Service Account for builds * @param {String} [options.kubernetes.jobsNamespace=default] Pods namespace for Screwdriver Jobs + * @param {String} [options.kubernetes.baseImage] Base image for the pod * @param {String} [options.launchVersion=stable] Launcher container version to use * @param {String} [options.prefix=''] Prefix for job name * @param {String} [options.fusebox] Options for the circuit breaker (https://github.com/screwdriver-cd/circuit-fuses) @@ -30,13 +30,19 @@ class K8sExecutor extends Executor { this.kubernetes = options.kubernetes || {}; this.ecosystem = options.ecosystem; - this.token = this.kubernetes.token || - fs.readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token').toString(); + + if (this.kubernetes.token) { + this.token = this.kubernetes.token; + } else { + const filepath = '/var/run/secrets/kubernetes.io/serviceaccount/token'; + + this.token = fs.existsSync(filepath) ? fs.readFileSync(filepath) : ''; + } this.host = this.kubernetes.host || 'kubernetes.default'; this.launchVersion = options.launchVersion || 'stable'; this.prefix = options.prefix || ''; - this.serviceAccount = this.kubernetes.serviceAccount || 'default'; this.jobsNamespace = this.kubernetes.jobsNamespace || 'default'; + this.baseImage = this.kubernetes.baseImage; this.podsUrl = `https://${this.host}/api/v1/namespaces/${this.jobsNamespace}/pods`; this.breaker = new Fusebox(request, options.fusebox); } @@ -59,7 +65,7 @@ class K8sExecutor extends Executor { store_uri: this.ecosystem.store, token: config.token, launcher_version: this.launchVersion, - service_account: this.serviceAccount + base_image: this.baseImage }); const options = { @@ -122,4 +128,4 @@ class K8sExecutor extends Executor { } } -module.exports = K8sExecutor; +module.exports = K8sVMExecutor; diff --git a/test/index.test.js b/test/index.test.js index 3abfd6b..d189c1f 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -11,7 +11,6 @@ metadata: name: {{build_id_with_prefix}} container: {{container}} launchVersion: {{launcher_version}} - serviceAccount: {{service_account}} command: - "/opt/sd/launch {{api_uri}} {{store_uri}} {{token}} {{build_id}}" `; @@ -30,7 +29,6 @@ describe('index', function () { const testStoreUri = 'http://store:8080'; const testContainer = 'node:4'; const testLaunchVersion = 'stable'; - const testServiceAccount = 'default'; const podsUrl = 'https://kubernetes.default/api/v1/namespaces/default/pods'; before(() => { @@ -44,13 +42,15 @@ describe('index', function () { requestMock = sinon.stub(); fsMock = { - readFileSync: sinon.stub() + readFileSync: sinon.stub(), + existsSync: sinon.stub() }; fsMock.readFileSync.withArgs('/var/run/secrets/kubernetes.io/serviceaccount/token') .returns('api_key'); fsMock.readFileSync.withArgs(sinon.match(/config\/pod.yaml.tim/)) .returns(TEST_TIM_YAML); + fsMock.existsSync.returns(true); mockery.registerMock('fs', fsMock); mockery.registerMock('request', requestMock); @@ -80,15 +80,14 @@ describe('index', function () { it('supports specifying a specific version', () => { assert.equal(executor.launchVersion, 'stable'); - assert.equal(executor.serviceAccount, 'default'); assert.equal(executor.token, 'api_key'); assert.equal(executor.host, 'kubernetes.default'); executor = new Executor({ kubernetes: { token: 'api_key2', host: 'kubernetes2', - serviceAccount: 'foobar', - jobsNamespace: 'baz' + jobsNamespace: 'baz', + baseImage: 'hyperctl' }, prefix: 'beta_', launchVersion: 'v1.2.3' @@ -97,18 +96,18 @@ describe('index', function () { assert.equal(executor.token, 'api_key2'); assert.equal(executor.host, 'kubernetes2'); assert.equal(executor.launchVersion, 'v1.2.3'); - assert.equal(executor.serviceAccount, 'foobar'); assert.equal(executor.jobsNamespace, 'baz'); + assert.equal(executor.baseImage, 'hyperctl'); }); it('allow empty options', () => { + fsMock.existsSync.returns(false); executor = new Executor(); assert.equal(executor.launchVersion, 'stable'); - assert.equal(executor.serviceAccount, 'default'); - assert.equal(executor.token, 'api_key'); assert.equal(executor.host, 'kubernetes.default'); assert.equal(executor.launchVersion, 'stable'); assert.equal(executor.prefix, ''); + assert.equal(executor.token, ''); }); it('extends base class', () => { @@ -224,8 +223,7 @@ describe('index', function () { metadata: { name: 'beta_15', container: testContainer, - launchVersion: testLaunchVersion, - serviceAccount: testServiceAccount + launchVersion: testLaunchVersion }, command: [ '/opt/sd/launch http://api:8080 http://store:8080 abcdefg '