screwdriver-api 7.0.24 → 7.0.26
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/bin/server +17 -0
- package/config/default.yaml +1 -0
- package/lib/server.js +7 -0
- package/package.json +3 -3
- package/plugins/events/index.js +9 -1
- package/plugins/events/listStageBuilds.js +45 -0
- package/plugins/pipelines/README.md +0 -3
- package/plugins/pipelines/index.js +5 -1
- package/plugins/pipelines/listStages.js +0 -2
- package/plugins/pipelines/templates/create.js +61 -0
- package/plugins/pipelines/templates/validate.js +38 -0
- package/plugins/stages/README.md +32 -0
- package/plugins/stages/index.js +19 -0
- package/plugins/stages/stageBuilds/list.js +68 -0
- package/plugins/template-validator.js +1 -1
- package/plugins/templates/create.js +1 -1
package/bin/server
CHANGED
|
@@ -216,6 +216,10 @@ const stageFactory = Models.StageFactory.getInstance({
|
|
|
216
216
|
datastore,
|
|
217
217
|
datastoreRO
|
|
218
218
|
});
|
|
219
|
+
const stageBuildFactory = Models.StageBuildFactory.getInstance({
|
|
220
|
+
datastore,
|
|
221
|
+
datastoreRO
|
|
222
|
+
});
|
|
219
223
|
const triggerFactory = Models.TriggerFactory.getInstance({
|
|
220
224
|
datastore,
|
|
221
225
|
datastoreRO
|
|
@@ -225,6 +229,16 @@ const buildClusterFactory = Models.BuildClusterFactory.getInstance({
|
|
|
225
229
|
datastoreRO,
|
|
226
230
|
scm
|
|
227
231
|
});
|
|
232
|
+
const pipelineTemplateFactory = Models.PipelineTemplateFactory.getInstance({
|
|
233
|
+
datastore,
|
|
234
|
+
datastoreRO,
|
|
235
|
+
scm
|
|
236
|
+
});
|
|
237
|
+
const pipelineTemplateVersionFactory = Models.PipelineTemplateVersionFactory.getInstance({
|
|
238
|
+
datastore,
|
|
239
|
+
datastoreRO,
|
|
240
|
+
scm
|
|
241
|
+
});
|
|
228
242
|
|
|
229
243
|
// @TODO run setup for SCM and Executor
|
|
230
244
|
// datastoreConfig.ddlSync => sync datastore schema (ddl) via api (default: true)
|
|
@@ -246,6 +260,8 @@ datastore.setup(datastoreConfig.ddlSyncEnabled).then(() =>
|
|
|
246
260
|
userFactory,
|
|
247
261
|
buildFactory,
|
|
248
262
|
buildClusterFactory,
|
|
263
|
+
pipelineTemplateFactory,
|
|
264
|
+
pipelineTemplateVersionFactory,
|
|
249
265
|
stepFactory,
|
|
250
266
|
bannerFactory,
|
|
251
267
|
secretFactory,
|
|
@@ -253,6 +269,7 @@ datastore.setup(datastoreConfig.ddlSyncEnabled).then(() =>
|
|
|
253
269
|
eventFactory,
|
|
254
270
|
collectionFactory,
|
|
255
271
|
stageFactory,
|
|
272
|
+
stageBuildFactory,
|
|
256
273
|
triggerFactory,
|
|
257
274
|
banners: authConfig,
|
|
258
275
|
builds: {
|
package/config/default.yaml
CHANGED
package/lib/server.js
CHANGED
|
@@ -75,6 +75,7 @@ function prettyPrintErrors(request, h) {
|
|
|
75
75
|
* @param {Factory} config.eventFactory Event Factory instance
|
|
76
76
|
* @param {Factory} config.collectionFactory Collection Factory instance
|
|
77
77
|
* @param {Factory} config.stageFactory Stage Factory instance
|
|
78
|
+
* @param {Factory} config.stageBuildFactory Stage Build Factory instance
|
|
78
79
|
* @param {Factory} config.triggerFactory Trigger Factory instance
|
|
79
80
|
* @param {Object} config.builds Config to include for builds plugin
|
|
80
81
|
* @param {Object} config.builds.ecosystem List of hosts in the ecosystem
|
|
@@ -125,7 +126,13 @@ module.exports = async config => {
|
|
|
125
126
|
commandTagFactory: config.commandTagFactory,
|
|
126
127
|
templateFactory: config.templateFactory,
|
|
127
128
|
templateTagFactory: config.templateTagFactory,
|
|
129
|
+
pipelineTemplateFactory: config.pipelineTemplateFactory,
|
|
130
|
+
pipelineTemplateVersionFactory: config.pipelineTemplateVersionFactory,
|
|
131
|
+
templateMetaFactory: config.templateMetaFactory,
|
|
132
|
+
jobTemplateTagFactory: config.jobTemplateTagFactory,
|
|
133
|
+
pipelineTemplateTagFactory: config.pipelineTemplateTagFactory,
|
|
128
134
|
stageFactory: config.stageFactory,
|
|
135
|
+
stageBuildFactory: config.stageBuildFactory,
|
|
129
136
|
triggerFactory: config.triggerFactory,
|
|
130
137
|
pipelineFactory: config.pipelineFactory,
|
|
131
138
|
jobFactory: config.jobFactory,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "screwdriver-api",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.26",
|
|
4
4
|
"description": "API server for the Screwdriver.cd service",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"screwdriver-executor-queue": "^4.0.0",
|
|
115
115
|
"screwdriver-executor-router": "^3.0.0",
|
|
116
116
|
"screwdriver-logger": "^2.0.0",
|
|
117
|
-
"screwdriver-models": "^29.
|
|
117
|
+
"screwdriver-models": "^29.11.0",
|
|
118
118
|
"screwdriver-notifications-email": "^3.0.0",
|
|
119
119
|
"screwdriver-notifications-slack": "^5.0.0",
|
|
120
120
|
"screwdriver-request": "^2.0.1",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"screwdriver-scm-github": "^12.1.1",
|
|
124
124
|
"screwdriver-scm-gitlab": "^3.1.0",
|
|
125
125
|
"screwdriver-scm-router": "^7.0.0",
|
|
126
|
-
"screwdriver-template-validator": "^
|
|
126
|
+
"screwdriver-template-validator": "^7.0.0",
|
|
127
127
|
"screwdriver-workflow-parser": "^4.1.1",
|
|
128
128
|
"sqlite3": "^5.1.4",
|
|
129
129
|
"stream": "0.0.2",
|
package/plugins/events/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const boom = require('@hapi/boom');
|
|
|
4
4
|
const createRoute = require('./create');
|
|
5
5
|
const getRoute = require('./get');
|
|
6
6
|
const listBuildsRoute = require('./listBuilds');
|
|
7
|
+
const listStageBuildsRoute = require('./listStageBuilds');
|
|
7
8
|
const stopBuildsRoute = require('./stopBuilds');
|
|
8
9
|
const metricsRoute = require('./metrics');
|
|
9
10
|
|
|
@@ -51,7 +52,14 @@ const eventsPlugin = {
|
|
|
51
52
|
return Promise.resolve();
|
|
52
53
|
});
|
|
53
54
|
|
|
54
|
-
server.route([
|
|
55
|
+
server.route([
|
|
56
|
+
createRoute(),
|
|
57
|
+
getRoute(),
|
|
58
|
+
listBuildsRoute(),
|
|
59
|
+
listStageBuildsRoute(),
|
|
60
|
+
stopBuildsRoute(),
|
|
61
|
+
metricsRoute()
|
|
62
|
+
]);
|
|
55
63
|
}
|
|
56
64
|
};
|
|
57
65
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const boom = require('@hapi/boom');
|
|
4
|
+
const joi = require('joi');
|
|
5
|
+
const schema = require('screwdriver-data-schema');
|
|
6
|
+
const stageBuildListSchema = joi.array().items(schema.models.stageBuild.get).label('List of stage builds');
|
|
7
|
+
const eventIdSchema = schema.models.event.base.extract('id');
|
|
8
|
+
|
|
9
|
+
module.exports = () => ({
|
|
10
|
+
method: 'GET',
|
|
11
|
+
path: '/events/{id}/stageBuilds',
|
|
12
|
+
options: {
|
|
13
|
+
description: 'Get stage builds for a given event',
|
|
14
|
+
notes: 'Returns stage builds for a given event',
|
|
15
|
+
tags: ['api', 'events', 'stageBuilds'],
|
|
16
|
+
auth: {
|
|
17
|
+
strategies: ['token'],
|
|
18
|
+
scope: ['user', 'pipeline']
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
handler: async (request, h) => {
|
|
22
|
+
const { eventFactory } = request.server.app;
|
|
23
|
+
const event = await eventFactory.get(request.params.id);
|
|
24
|
+
|
|
25
|
+
if (!event) {
|
|
26
|
+
throw boom.notFound('Event does not exist');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return event
|
|
30
|
+
.getStageBuilds()
|
|
31
|
+
.then(stageBuilds => h.response(stageBuilds.map(c => c.toJson())))
|
|
32
|
+
.catch(err => {
|
|
33
|
+
throw err;
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
response: {
|
|
37
|
+
schema: stageBuildListSchema
|
|
38
|
+
},
|
|
39
|
+
validate: {
|
|
40
|
+
params: joi.object({
|
|
41
|
+
id: eventIdSchema
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
@@ -121,9 +121,6 @@ Only PR events of specified PR number will be searched when `prNum` is set
|
|
|
121
121
|
#### Get all stages for a single pipeline
|
|
122
122
|
|
|
123
123
|
`GET /pipelines/{id}/stages`
|
|
124
|
-
Will get latest commit event's stages if no event ID or group event ID is provided
|
|
125
|
-
|
|
126
|
-
`GET /pipelines/{id}/stages?groupEventId={groupEventId}`
|
|
127
124
|
|
|
128
125
|
`GET /pipelines/{id}/stages?eventId={eventId}`
|
|
129
126
|
|
|
@@ -29,6 +29,8 @@ const latestCommitEvent = require('./latestCommitEvent');
|
|
|
29
29
|
const getAdmin = require('./admins/get');
|
|
30
30
|
const deleteCache = require('./caches/delete');
|
|
31
31
|
const openPrRoute = require('./openPr');
|
|
32
|
+
const createTemplate = require('./templates/create');
|
|
33
|
+
const validateTemplate = require('./templates/validate');
|
|
32
34
|
|
|
33
35
|
/**
|
|
34
36
|
* Pipeline API Plugin
|
|
@@ -195,7 +197,9 @@ const pipelinesPlugin = {
|
|
|
195
197
|
latestCommitEvent(),
|
|
196
198
|
getAdmin(),
|
|
197
199
|
deleteCache(),
|
|
198
|
-
openPrRoute()
|
|
200
|
+
openPrRoute(),
|
|
201
|
+
createTemplate(),
|
|
202
|
+
validateTemplate()
|
|
199
203
|
]);
|
|
200
204
|
}
|
|
201
205
|
};
|
|
@@ -49,8 +49,6 @@ module.exports = () => ({
|
|
|
49
49
|
throw boom.notFound(`Latest event does not exist for pipeline ${pipelineId}`);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
config.params.eventId = latestCommitEvents[0].id;
|
|
53
|
-
|
|
54
52
|
return stageFactory.list(config);
|
|
55
53
|
})
|
|
56
54
|
.then(stages => h.response(stages.map(s => s.toJson())))
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const boom = require('@hapi/boom');
|
|
4
|
+
const schema = require('screwdriver-data-schema');
|
|
5
|
+
const validator = require('screwdriver-template-validator').parsePipelineTemplate;
|
|
6
|
+
const templateSchema = schema.api.templateValidator;
|
|
7
|
+
|
|
8
|
+
module.exports = () => ({
|
|
9
|
+
method: 'POST',
|
|
10
|
+
path: '/pipeline/template',
|
|
11
|
+
options: {
|
|
12
|
+
description: 'Create a new pipeline template',
|
|
13
|
+
notes: 'Create a specific pipeline template',
|
|
14
|
+
tags: ['api', 'pipelineTemplate'],
|
|
15
|
+
auth: {
|
|
16
|
+
strategies: ['token'],
|
|
17
|
+
scope: ['build']
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
handler: async (request, h) => {
|
|
21
|
+
const { pipelineTemplateVersionFactory, pipelineTemplateFactory } = request.server.app;
|
|
22
|
+
|
|
23
|
+
const config = await validator(request.payload.yaml);
|
|
24
|
+
|
|
25
|
+
if (config.errors.length > 0) {
|
|
26
|
+
throw boom.badRequest(`Template has invalid format: ${config.errors.length} error(s).`, config.errors);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const pipelineTemplate = await pipelineTemplateFactory.get({
|
|
30
|
+
name: config.template.name,
|
|
31
|
+
namespace: config.template.namespace
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const { isPR, pipelineId } = request.auth.credentials;
|
|
35
|
+
|
|
36
|
+
// If template name exists, but this build's pipelineId is not the same as template's pipelineId
|
|
37
|
+
// Then this build does not have permission to publish
|
|
38
|
+
if (isPR || (pipelineTemplate && pipelineId !== pipelineTemplate.pipelineId)) {
|
|
39
|
+
throw boom.forbidden('Not allowed to publish this template');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const template = await pipelineTemplateVersionFactory.create(
|
|
43
|
+
{
|
|
44
|
+
...config.template,
|
|
45
|
+
pipelineId
|
|
46
|
+
},
|
|
47
|
+
pipelineTemplateFactory
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const location = new URL(
|
|
51
|
+
`${request.path}/${template.id}`,
|
|
52
|
+
`${request.server.info.protocol}://${request.headers.host}`
|
|
53
|
+
).toString();
|
|
54
|
+
|
|
55
|
+
return h.response(template.toJson()).header('Location', location).code(201);
|
|
56
|
+
},
|
|
57
|
+
validate: {
|
|
58
|
+
payload: templateSchema.input
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const boom = require('@hapi/boom');
|
|
4
|
+
const schema = require('screwdriver-data-schema');
|
|
5
|
+
const templateSchema = schema.api.templateValidator;
|
|
6
|
+
const pipelineValidator = require('screwdriver-template-validator').parsePipelineTemplate;
|
|
7
|
+
|
|
8
|
+
module.exports = () => ({
|
|
9
|
+
method: 'POST',
|
|
10
|
+
path: '/pipeline/template/validate',
|
|
11
|
+
options: {
|
|
12
|
+
description: 'Validate a given sd-template.yaml',
|
|
13
|
+
notes: 'returns the parsed config, validation errors, or both',
|
|
14
|
+
tags: ['api', 'validation', 'yaml'],
|
|
15
|
+
auth: {
|
|
16
|
+
strategies: ['token'],
|
|
17
|
+
scope: ['user', 'pipeline']
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
handler: async (request, h) => {
|
|
21
|
+
try {
|
|
22
|
+
const { yaml: templateString } = request.payload;
|
|
23
|
+
|
|
24
|
+
const result = await pipelineValidator(templateString);
|
|
25
|
+
|
|
26
|
+
return h.response(result);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
throw boom.badRequest(err.toString());
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
validate: {
|
|
32
|
+
payload: templateSchema.input
|
|
33
|
+
},
|
|
34
|
+
response: {
|
|
35
|
+
schema: templateSchema.output
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Stages Plugin
|
|
2
|
+
> API stages plugin for the Screwdriver API
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
|
|
6
|
+
### Register plugin
|
|
7
|
+
|
|
8
|
+
```javascript
|
|
9
|
+
const Hapi = require('@hapi/hapi');
|
|
10
|
+
const server = new Hapi.Server();
|
|
11
|
+
const stagesPlugin = require('./');
|
|
12
|
+
|
|
13
|
+
server.connection({ port: 3000 });
|
|
14
|
+
|
|
15
|
+
server.register({
|
|
16
|
+
register: stagesPlugin,
|
|
17
|
+
options: {}
|
|
18
|
+
}, () => {
|
|
19
|
+
server.start((err) => {
|
|
20
|
+
if (err) {
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
console.log('Server running at:', server.info.uri);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Routes
|
|
29
|
+
|
|
30
|
+
#### Get a listing of all stage builds for a stage
|
|
31
|
+
|
|
32
|
+
`GET /stages/{id}/stageBuilds`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const getStageBuildsRoute = require('./stageBuilds/list');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Stage API Plugin
|
|
7
|
+
* @method register
|
|
8
|
+
* @param {Hapi} server Hapi Server
|
|
9
|
+
* @param {Object} options Configuration
|
|
10
|
+
* @param {Function} next Function to call when done
|
|
11
|
+
*/
|
|
12
|
+
const stagesPlugin = {
|
|
13
|
+
name: 'stages',
|
|
14
|
+
async register(server) {
|
|
15
|
+
server.route([getStageBuildsRoute()]);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
module.exports = stagesPlugin;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const boom = require('@hapi/boom');
|
|
4
|
+
const joi = require('joi');
|
|
5
|
+
const schema = require('screwdriver-data-schema');
|
|
6
|
+
const listSchema = joi.array().items(schema.models.stageBuild.get).label('List of stage builds for a stage');
|
|
7
|
+
const idSchema = schema.models.stage.base.extract('id');
|
|
8
|
+
const eventIdSchema = schema.models.stageBuild.base.extract('eventId');
|
|
9
|
+
|
|
10
|
+
module.exports = () => ({
|
|
11
|
+
method: 'GET',
|
|
12
|
+
path: '/stages/{id}/stageBuilds',
|
|
13
|
+
options: {
|
|
14
|
+
description: 'Get stage builds for a stage',
|
|
15
|
+
notes: 'Returns all stage builds for a stage',
|
|
16
|
+
tags: ['api', 'stageBuilds'],
|
|
17
|
+
auth: {
|
|
18
|
+
strategies: ['token'],
|
|
19
|
+
scope: ['user', '!guest']
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
handler: async (request, h) => {
|
|
23
|
+
const { stageFactory, stageBuildFactory } = request.server.app;
|
|
24
|
+
const { page, count } = request.query;
|
|
25
|
+
const config = {
|
|
26
|
+
sort: request.query.sort,
|
|
27
|
+
params: {
|
|
28
|
+
stageId: request.params.id
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return stageFactory.get(config.params.stageId).then(async stage => {
|
|
33
|
+
if (!stage) {
|
|
34
|
+
throw boom.notFound(`Stage ${config.params.stageId} does not exist`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (page || count) {
|
|
38
|
+
config.paginate = { page, count };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Set eventId if provided
|
|
42
|
+
if (request.query.eventId) {
|
|
43
|
+
config.params.eventId = request.query.eventId;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return stageBuildFactory
|
|
47
|
+
.list(config)
|
|
48
|
+
.then(stageBuilds => h.response(stageBuilds.map(c => c.toJson())))
|
|
49
|
+
.catch(err => {
|
|
50
|
+
throw err;
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
response: {
|
|
55
|
+
schema: listSchema
|
|
56
|
+
},
|
|
57
|
+
validate: {
|
|
58
|
+
params: joi.object({
|
|
59
|
+
id: idSchema
|
|
60
|
+
}),
|
|
61
|
+
query: schema.api.pagination.concat(
|
|
62
|
+
joi.object({
|
|
63
|
+
eventId: eventIdSchema
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const boom = require('@hapi/boom');
|
|
4
4
|
const schema = require('screwdriver-data-schema');
|
|
5
5
|
const templateSchema = schema.api.templateValidator;
|
|
6
|
-
const validator = require('screwdriver-template-validator');
|
|
6
|
+
const validator = require('screwdriver-template-validator').parseJobTemplate;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Hapi Template Validator Plugin
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const urlLib = require('url');
|
|
4
4
|
const boom = require('@hapi/boom');
|
|
5
5
|
const schema = require('screwdriver-data-schema');
|
|
6
|
-
const validator = require('screwdriver-template-validator');
|
|
6
|
+
const validator = require('screwdriver-template-validator').parseJobTemplate;
|
|
7
7
|
const templateSchema = schema.api.templateValidator;
|
|
8
8
|
const hoek = require('@hapi/hoek');
|
|
9
9
|
|