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.
- package/design/sd-build-cluster-selection.md +82 -0
- package/package.json +1 -1
- package/plugins/builds/triggers/helpers.js +16 -1
- package/plugins/builds/triggers/joinBase.js +2 -1
- package/plugins/builds/triggers/orBase.js +3 -0
- package/plugins/pipelines/index.js +3 -1
- package/plugins/pipelines/updateBuildCluster.js +102 -0
|
@@ -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
|
@@ -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
|
}
|
|
@@ -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
|
+
});
|