screwdriver-api 7.0.259 → 7.0.261

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,82 @@
1
+ ### Build Cluster Selection
2
+
3
+ ##### On pipeline creation
4
+
5
+ ```mermaid
6
+ graph TD
7
+
8
+ A(Start) -->|Create| B[Pipeline Create]
9
+ B -->|Sync| C[Pipeline Sync]
10
+ C -->|Fetch Config| D[Get Config]
11
+
12
+ subgraph GH
13
+ E1@{ shape: lean-r, label: "SD YAML" }
14
+ end
15
+ E1 --> D
16
+
17
+ D --> E{Pipeline-level build cluster annotation?}
18
+ E -->|Yes| F[Store annotation]
19
+ E -->|No| F1[Get pipeline annotation]
20
+
21
+ subgraph Database
22
+ F1
23
+ F
24
+ end
25
+
26
+ F1 --> H{Pipeline has build cluster annotation?}
27
+ H -->|Yes| I[Use annotation] --> F
28
+ H -->|No| J[Derive build cluster] --> F
29
+ F --> Z@{ shape: stadium, label: "End" }
30
+
31
+ subgraph buildCluster Table in DB
32
+ K@{ shape: lean-r, label: "group" }
33
+ L@{ shape: lean-r, label: "weight" }
34
+ end
35
+
36
+ K --> J
37
+ L --> J
38
+
39
+ click B "https://github.com/screwdriver-cd/screwdriver/blob/master/plugins/pipelines/create.js"
40
+ click C "https://github.com/screwdriver-cd/screwdriver/blob/5a74c24e232a95a12d28e0ae7c4c3a5b25e6f872/plugins/pipelines/create.js#L104"
41
+ click D "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/pipeline.js#L1009-L1021"
42
+ click I "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/pipeline.js#L1018-L1020"
43
+ click J "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/helper.js#L229-L293"
44
+
45
+ ```
46
+
47
+ ##### On build creation
48
+
49
+ ```mermaid
50
+ graph TD
51
+
52
+ A(Start) --> B[Build Create]
53
+ B --> C[Get build cluster]
54
+ subgraph input
55
+ D@{ shape: lean-r, label: "Job permutations" }
56
+ E@{ shape: lean-r, label: "Pipeline annotations" }
57
+ F@{ shape: lean-r, label: "Job provider" }
58
+ end
59
+ D --> C
60
+ E --> C
61
+ F --> C
62
+ C --> G{Is provider present?}
63
+ G -->|Yes| H[Derive build cluster based on provider]
64
+ H --> Z@{ shape: stadium, label: "Done" }
65
+ G -->|No| I{Is job annotation present?}
66
+ I --> |Yes| J[Use the build cluster from job annotation]
67
+ J --> Z
68
+ I --> |No| K{Is pipeline annotation present?}
69
+ K --> |Yes| L[Use the build cluster from pipeline annotation]
70
+ K --> |No| M[Derive randomly]
71
+ L --> Z
72
+ M --> Z
73
+
74
+ click C "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/buildFactory.js#L221-L226"
75
+ click D "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/buildFactory.js#L213"
76
+ click E "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/buildFactory.js#L214"
77
+ click F "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/buildFactory.js#L215-L219"
78
+ click H "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/helper.js#L245-L248"
79
+ click J "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/helper.js#L235-L237"
80
+ click L "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/helper.js#L239-L241"
81
+ click M "https://github.com/screwdriver-cd/models/blob/7eac5d79e11620793ab8936cf6e06971a2c04eea/lib/helper.js#L259-L265"
82
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screwdriver-api",
3
- "version": "7.0.259",
3
+ "version": "7.0.261",
4
4
  "description": "API server for the Screwdriver.cd service",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -634,10 +634,22 @@ async function getParentBuildStatus({ newBuild, joinListNames, pipelineId, build
634
634
  * @param {String|undefined} arg.pipelineId Pipeline ID
635
635
  * @param {String|undefined} arg.stageName Stage name
636
636
  * @param {Event} arg.event Event
637
+ * @param {Build} arg.currentBuild Current build
637
638
  * @returns {Promise<Build|null>} The newly updated/created build
638
639
  */
639
- async function handleNewBuild({ done, hasFailure, newBuild, job, pipelineId, stageName, event }) {
640
+ async function handleNewBuild({ done, hasFailure, newBuild, job, pipelineId, stageName, event, currentBuild }) {
640
641
  if (!done || Status.isStarted(newBuild.status)) {
642
+ // The virtual job does not inherit metadata because the Launcher is not executed.
643
+ // Therefore, it is necessary to take over the metadata from the previous build.
644
+
645
+ // TODO There is a bug when virtual job requires [a, b] and if "a" and "b" are completed simultaneously.
646
+ // https://github.com/screwdriver-cd/screwdriver/issues/3287
647
+ if (isVirtualJob(job) && !hasFreezeWindows(job)) {
648
+ newBuild.meta = merge({}, newBuild.meta, currentBuild.meta);
649
+
650
+ await newBuild.update();
651
+ }
652
+
641
653
  return null;
642
654
  }
643
655
 
@@ -661,6 +673,9 @@ async function handleNewBuild({ done, hasFailure, newBuild, job, pipelineId, sta
661
673
  newBuild.status = Status.SUCCESS;
662
674
  newBuild.statusMessage = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessage;
663
675
  newBuild.statusMessageType = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessageType;
676
+ // The virtual job does not inherit metadata because the Launcher is not executed.
677
+ // Therefore, it is necessary to take over the metadata from the previous build.
678
+ newBuild.meta = merge({}, newBuild.meta, currentBuild.meta);
664
679
 
665
680
  return newBuild.update();
666
681
  }
@@ -103,7 +103,8 @@ class JoinBase {
103
103
  job: nextJob,
104
104
  pipelineId,
105
105
  stageName: nextJobStageName,
106
- event
106
+ event,
107
+ currentBuild: this.currentBuild
107
108
  });
108
109
  }
109
110
  }
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ const merge = require('lodash.mergewith');
3
4
  const { createInternalBuild, Status, BUILD_STATUS_MESSAGES, isVirtualJob, hasFreezeWindows } = require('./helpers');
4
5
 
5
6
  /**
@@ -60,6 +61,7 @@ class OrBase {
60
61
  nextBuild.status = Status.SUCCESS;
61
62
  nextBuild.statusMessage = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessage;
62
63
  nextBuild.statusMessageType = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessageType;
64
+ nextBuild.meta = merge({}, nextBuild.meta, this.currentBuild.meta);
63
65
 
64
66
  return nextBuild.update();
65
67
  }
@@ -91,6 +93,7 @@ class OrBase {
91
93
  nextBuild.status = Status.SUCCESS;
92
94
  nextBuild.statusMessage = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessage;
93
95
  nextBuild.statusMessageType = BUILD_STATUS_MESSAGES.SKIP_VIRTUAL_JOB.statusMessageType;
96
+ nextBuild.meta = merge({}, nextBuild.meta, this.currentBuild.meta);
94
97
 
95
98
  await nextBuild.update();
96
99
  }
@@ -43,6 +43,7 @@ const removeTemplateRoute = require('./templates/remove');
43
43
  const removeTemplateTagRoute = require('./templates/removeTag');
44
44
  const removeTemplateVersionRoute = require('./templates/removeVersion');
45
45
  const updateTrustedRoute = require('./templates/updateTrusted');
46
+ const updateBuildCluster = require('./updateBuildCluster');
46
47
 
47
48
  /**
48
49
  * Pipeline API Plugin
@@ -276,7 +277,8 @@ const pipelinesPlugin = {
276
277
  removeTemplateRoute(),
277
278
  removeTemplateTagRoute(),
278
279
  removeTemplateVersionRoute(),
279
- updateTrustedRoute()
280
+ updateTrustedRoute(),
281
+ updateBuildCluster()
280
282
  ]);
281
283
  }
282
284
  };
@@ -0,0 +1,102 @@
1
+ 'use strict';
2
+
3
+ const boom = require('@hapi/boom');
4
+ const joi = require('joi');
5
+ const schema = require('screwdriver-data-schema');
6
+ const logger = require('screwdriver-logger');
7
+ const idSchema = schema.models.pipeline.base.extract('id');
8
+
9
+ module.exports = () => ({
10
+ method: 'PUT',
11
+ path: '/pipelines/{id}/buildCluster',
12
+ options: {
13
+ description: 'Update the buildCluster of a pipeline',
14
+ notes: 'Update the buildCluster of a specific pipeline',
15
+ tags: ['api', 'pipelines'],
16
+ auth: {
17
+ strategies: ['token'],
18
+ scope: ['user', '!guest']
19
+ },
20
+ handler: async (request, h) => {
21
+ const buildClusterAnnotation = 'screwdriver.cd/buildCluster';
22
+ const { payload } = request;
23
+
24
+ // payload should have buildCluster annotation
25
+ if (!payload[buildClusterAnnotation]) {
26
+ throw boom.badRequest(`Payload must contain ${buildClusterAnnotation}`);
27
+ }
28
+
29
+ const { pipelineFactory, bannerFactory, buildClusterFactory } = request.server.app;
30
+ const { scmContext, username, scmUserId } = request.auth.credentials;
31
+
32
+ // only SD cluster admins can update the buildCluster
33
+ const scmDisplayName = bannerFactory.scm.getDisplayName({ scmContext });
34
+ const adminDetails = request.server.plugins.banners.screwdriverAdminDetails(
35
+ username,
36
+ scmDisplayName,
37
+ scmUserId
38
+ );
39
+
40
+ if (!adminDetails.isAdmin) {
41
+ throw boom.forbidden(
42
+ `User ${username} does not have Screwdriver administrative privileges to update the buildCluster`
43
+ );
44
+ }
45
+
46
+ const { id } = request.params;
47
+ const pipeline = await pipelineFactory.get({ id });
48
+ // check if pipeline exists
49
+
50
+ if (!pipeline) {
51
+ throw boom.notFound(`Pipeline ${id} does not exist`);
52
+ }
53
+
54
+ const pipelineConfig = await pipeline.getConfiguration({});
55
+ // check if pipeline has buildCluster annotation
56
+
57
+ if (pipelineConfig.annotations && pipelineConfig.annotations[buildClusterAnnotation]) {
58
+ const scmUrl = pipeline.scmRepo.url;
59
+
60
+ throw boom.conflict(
61
+ `Pipeline ${id} already has a buildCluster annotation set in the YAML configuration: check ${scmUrl}`
62
+ );
63
+ }
64
+
65
+ // ensure that the buildCluster is a valid cluster
66
+ const buildClusterName = payload[buildClusterAnnotation];
67
+ const buildCluster = await buildClusterFactory.get({ name: buildClusterName, scmContext });
68
+
69
+ if (!buildCluster) {
70
+ throw boom.badRequest(`Build cluster ${buildClusterName} does not exist`);
71
+ }
72
+
73
+ // ensure that the buildCluster is active
74
+ if (!buildCluster.isActive) {
75
+ throw boom.badRequest(`Build cluster ${buildClusterName} is not active`);
76
+ }
77
+
78
+ // update pipeline with buildCluster annotation
79
+ if (!pipeline.annotations) {
80
+ pipeline.annotations = {};
81
+ }
82
+ pipeline.annotations[buildClusterAnnotation] = buildClusterName;
83
+ try {
84
+ const result = await pipeline.update();
85
+
86
+ logger.info(
87
+ `[Audit] user ${username} updates ${buildClusterAnnotation} for pipelineID:${id} to ${buildClusterName}.`
88
+ );
89
+
90
+ return h.response(result.toJson()).code(200);
91
+ } catch (err) {
92
+ logger.error(`Failed to update ${buildClusterAnnotation} for pipeline ${id}: ${err.message}`);
93
+ throw boom.internal(`Failed to update ${buildClusterAnnotation} for pipeline ${id}`);
94
+ }
95
+ },
96
+ validate: {
97
+ params: joi.object({
98
+ id: idSchema
99
+ })
100
+ }
101
+ }
102
+ });