diff --git a/config/pod.cache2disk.yaml.tim b/config/pod.cache2disk.yaml.tim index 69af837..a26b1b8 100644 --- a/config/pod.cache2disk.yaml.tim +++ b/config/pod.cache2disk.yaml.tim @@ -20,6 +20,7 @@ spec: values: - screwdriver-vm restartPolicy: Never + terminationGracePeriodSeconds: {{termination_grace_period_seconds}} containers: - name: vm-launcher image: {{base_image}} diff --git a/config/pod.yaml.tim b/config/pod.yaml.tim index f603a85..ff8637f 100644 --- a/config/pod.yaml.tim +++ b/config/pod.yaml.tim @@ -20,6 +20,7 @@ spec: values: - screwdriver-vm restartPolicy: Never + terminationGracePeriodSeconds: {{termination_grace_period_seconds}} containers: - name: vm-launcher image: {{base_image}} diff --git a/index.js b/index.js index 8c3a4d6..c6e885c 100644 --- a/index.js +++ b/index.js @@ -28,6 +28,7 @@ const AFFINITY_PREFERRED_NODE_SELECTOR_PATH = 'spec.affinity.nodeAffinity.' + 'preferredDuringSchedulingIgnoredDuringExecution'; const PREFERRED_WEIGHT = 100; const DISK_CACHE_STRATEGY = 'disk'; +const TERMINATION_GRACE_PERIOD_SECONDS = 'terminationGracePeriodSeconds'; /** * Parses nodeSelector config and update intended nodeSelector in tolerations @@ -133,6 +134,7 @@ class K8sVMExecutor extends Executor { * @param {String} [options.kubernetes.resources.disk.speed] Value for disk speed label (e.g.: screwdriver.cd/diskSpeed) * @param {Number} [options.kubernetes.jobsNamespace=default] Pods namespace for Screwdriver Jobs * @param {Object} [options.kubernetes.nodeSelectors] Object representing node label-value pairs + * @param {String} [options.kubernetes.terminationGracePeriodSeconds] TerminationGracePeriodSeconds setting for k8s pods * @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) @@ -168,6 +170,7 @@ class K8sVMExecutor extends Executor { this.baseImage = this.kubernetes.baseImage; this.buildTimeout = this.kubernetes.buildTimeout || DEFAULT_BUILD_TIMEOUT; this.maxBuildTimeout = this.kubernetes.maxBuildTimeout || MAX_BUILD_TIMEOUT; + this.terminationGracePeriodSeconds = this.kubernetes.terminationGracePeriodSeconds || 60; this.podsUrl = `https://${this.host}/api/v1/namespaces/${this.jobsNamespace}/pods`; this.breaker = new Fusebox(requestretry, options.fusebox); this.retryDelay = this.requestretryOptions.retryDelay || DEFAULT_RETRYDELAY; @@ -320,6 +323,12 @@ class K8sVMExecutor extends Executor { ? Math.min(annotations[ANNOTATION_BUILD_TIMEOUT], this.maxBuildTimeout) : this.buildTimeout; + const terminationGracePeriod = annotations[TERMINATION_GRACE_PERIOD_SECONDS] + ? Math.max( + annotations[TERMINATION_GRACE_PERIOD_SECONDS], this.terminationGracePeriodSeconds + ) + : this.terminationGracePeriodSeconds; + const podSpec = { cpu, memory, @@ -337,6 +346,7 @@ class K8sVMExecutor extends Executor { pushgateway_url: hoek.reach(this.ecosystem, 'pushgatewayUrl', { default: '' }), token, launcher_image: `${this.launchImage}:${this.launchVersion}`, + termination_grace_period_seconds: terminationGracePeriod, launcher_version: this.launchVersion, base_image: this.baseImage, cache_strategy: this.cacheStrategy, diff --git a/test/index.test.js b/test/index.test.js index 29d94a6..10fafb8 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -23,6 +23,7 @@ metadata: command: - "/opt/sd/launch {{api_uri}} {{store_uri}} {{token}} {{build_timeout}} {{build_id}}" spec: + terminationGracePeriodSeconds: {{termination_grace_period_seconds}} affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: @@ -53,6 +54,7 @@ describe('index', () => { const testLaunchVersion = 'stable'; const podsUrl = 'https://kubernetes.default/api/v1/namespaces/default/pods'; const testSpec = { + terminationGracePeriodSeconds: 60, tolerations: [{ key: 'key', value: 'value', @@ -85,6 +87,7 @@ describe('index', () => { effect: 'NoSchedule', operator: 'Equal' }], + terminationGracePeriodSeconds: 60, affinity: { nodeAffinity: { requiredDuringSchedulingIgnoredDuringExecution: { @@ -106,6 +109,7 @@ describe('index', () => { } }; const testSpecWithDiskSpeed = { + terminationGracePeriodSeconds: 60, tolerations: [{ key: 'screwdriver.cd/diskspeed', value: 'high', @@ -138,6 +142,7 @@ describe('index', () => { } }; const testPreferredSpec = { + terminationGracePeriodSeconds: 60, affinity: { nodeAffinity: { preferredDuringSchedulingIgnoredDuringExecution: [ @@ -163,6 +168,7 @@ describe('index', () => { } }; const testPodSpec = { + terminationGracePeriodSeconds: 60, affinity: { podAntiAffinity: { preferredDuringSchedulingIgnoredDuringExecution: [ @@ -251,6 +257,7 @@ describe('index', () => { host: 'kubernetes2', jobsNamespace: 'baz', baseImage: 'hyperctl', + terminationGracePeriodSeconds: 30, resources: { cpu: { turbo: 10, @@ -300,6 +307,7 @@ describe('index', () => { assert.equal(executor.cacheMd5Check, 'true'); assert.equal(executor.cacheMaxSizeInMB, '2048'); assert.equal(executor.cacheMaxGoThreads, '10000'); + assert.equal(executor.terminationGracePeriodSeconds, 30); }); it('allow empty options', () => { @@ -307,6 +315,7 @@ describe('index', () => { executor = new Executor(); assert.equal(executor.buildTimeout, DEFAULT_BUILD_TIMEOUT); assert.equal(executor.maxBuildTimeout, MAX_BUILD_TIMEOUT); + assert.equal(executor.terminationGracePeriodSeconds, 60); assert.equal(executor.launchVersion, 'stable'); assert.equal(executor.host, 'kubernetes.default'); assert.equal(executor.launchVersion, 'stable'); @@ -666,6 +675,16 @@ describe('index', () => { }); }); + it('sets the terminationGracePeriodSeconds appropriately when annotation is set', () => { + postConfig.body.spec.terminationGracePeriodSeconds = 60; + fakeStartConfig.annotations = { 'screwdriver.cd/terminationGracePeriodSeconds': 60 }; + + return executor.start(fakeStartConfig).then(() => { + assert.calledWith(requestRetryMock.firstCall, postConfig); + assert.calledWith(requestRetryMock.secondCall, sinon.match(getConfig)); + }); + }); + it('sets tolerations and node affinity with appropriate node config', () => { const spec = _.merge({}, testSpec, testPodSpec); const options = _.assign({}, executorOptions, { @@ -1029,6 +1048,7 @@ describe('index', () => { beforeEach(() => { nodeSelectors = null; fakeConfig = yaml.safeLoad(TEST_TIM_YAML); + fakeConfig.spec.terminationGracePeriodSeconds = 60; }); it('does nothing if nodeSelector is not set', () => { @@ -1060,6 +1080,7 @@ describe('index', () => { beforeEach(() => { nodeSelectors = null; fakeConfig = yaml.safeLoad(TEST_TIM_YAML); + fakeConfig.spec.terminationGracePeriodSeconds = 60; }); it('does nothing if preferredNodeSelector is not set', () => {