screwdriver-api 7.0.39 → 7.0.41
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/package.json +1 -1
- package/plugins/pipelines/README.md +42 -1
- package/plugins/pipelines/index.js +60 -1
- package/plugins/pipelines/templates/createTag.js +7 -2
- package/plugins/pipelines/templates/getVersion.js +9 -6
- package/plugins/pipelines/templates/listVersions.js +2 -2
- package/plugins/pipelines/templates/remove.js +57 -0
- package/plugins/pipelines/templates/removeTag.js +66 -0
- package/plugins/pipelines/templates/removeVersion.js +81 -0
package/package.json
CHANGED
|
@@ -321,4 +321,45 @@ If the template tag already exists, it will update the tag with the new version.
|
|
|
321
321
|
|
|
322
322
|
`PUT /templates/{templateName}/tags/{tagName}` with the following payload
|
|
323
323
|
|
|
324
|
-
* `version` - Exact version of the template (ex: `1.1.0`)
|
|
324
|
+
* `version` - Exact version of the template (ex: `1.1.0`)
|
|
325
|
+
|
|
326
|
+
##### Delete a pipeline template
|
|
327
|
+
Deleting a pipeline template will delete a template and all of its associated tags and versions.
|
|
328
|
+
|
|
329
|
+
`DELETE /pipeline/templates/{namespace}/{name}`
|
|
330
|
+
|
|
331
|
+
###### Arguments
|
|
332
|
+
|
|
333
|
+
* `name` - Name of the template
|
|
334
|
+
|
|
335
|
+
##### Delete a pipeline template version
|
|
336
|
+
|
|
337
|
+
Delete the template version and all of its associated tags.
|
|
338
|
+
If the deleted version was the latest version, the API would set the `latestVersion` attribute of the templateMeta to the previous version.
|
|
339
|
+
|
|
340
|
+
`DELETE /pipeline/templates/{namespace}/{name}/versions/{version}`
|
|
341
|
+
|
|
342
|
+
###### Arguments
|
|
343
|
+
|
|
344
|
+
'namespace', 'name', 'version'
|
|
345
|
+
|
|
346
|
+
* `namespace` - Namespace of the template
|
|
347
|
+
* `name` - Name of the template
|
|
348
|
+
* `version` - Version of the template
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
##### Delete a pipeline template tag
|
|
352
|
+
|
|
353
|
+
Delete the template tag. This does not delete the template itself.
|
|
354
|
+
|
|
355
|
+
*Note: This endpoint is only accessible in `build` scope and the permission is tied to the pipeline that creates the template.*
|
|
356
|
+
|
|
357
|
+
`DELETE /pipeline/templates/{namespace}/{name}/tags/{tag}`
|
|
358
|
+
|
|
359
|
+
###### Arguments
|
|
360
|
+
|
|
361
|
+
'namespace', 'name', 'tag'
|
|
362
|
+
|
|
363
|
+
* `namespace` - Namespace of the template
|
|
364
|
+
* `name` - Name of the template
|
|
365
|
+
* `tag` - Tag name of the template
|
|
@@ -38,6 +38,9 @@ const getTemplateRoute = require('./templates/get');
|
|
|
38
38
|
const getTemplateByIdRoute = require('./templates/getTemplateById');
|
|
39
39
|
const createTagRoute = require('./templates/createTag');
|
|
40
40
|
const getVersionRoute = require('./templates/getVersion');
|
|
41
|
+
const removeTemplateRoute = require('./templates/remove');
|
|
42
|
+
const removeTemplateTagRoute = require('./templates/removeTag');
|
|
43
|
+
const removeTemplateVersionRoute = require('./templates/removeVersion');
|
|
41
44
|
|
|
42
45
|
/**
|
|
43
46
|
* Pipeline API Plugin
|
|
@@ -176,6 +179,59 @@ const pipelinesPlugin = {
|
|
|
176
179
|
});
|
|
177
180
|
});
|
|
178
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Throws error if a credential does not have permission to remove pipeline template
|
|
184
|
+
* If credential has access, resolves to true
|
|
185
|
+
* @method canRemove
|
|
186
|
+
* @param {Object} credentials Credential object from Hapi
|
|
187
|
+
* @param {String} credentials.username Username of the person logged in (or build ID)
|
|
188
|
+
* @param {String} credentials.scmContext Scm of the person logged in (or build ID)
|
|
189
|
+
* @param {Array} credentials.scope Scope of the credential (user, build, admin)
|
|
190
|
+
* @param {String} [credentials.pipelineId] If credential is a build, this is the pipeline ID
|
|
191
|
+
* @param {Object} pipelineTemplate Target pipeline template object
|
|
192
|
+
* @param {String} permission Required permission level
|
|
193
|
+
* @param {String} app Server app object
|
|
194
|
+
* @return {Promise}
|
|
195
|
+
*/
|
|
196
|
+
server.expose('canRemove', async (credentials, pipelineTemplate, permission, app) => {
|
|
197
|
+
const { username, scmContext, scope } = credentials;
|
|
198
|
+
const { userFactory, pipelineFactory } = app;
|
|
199
|
+
|
|
200
|
+
if (credentials.scope.includes('admin')) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const pipeline = await pipelineFactory.get(pipelineTemplate.pipelineId);
|
|
205
|
+
|
|
206
|
+
if (!pipeline) {
|
|
207
|
+
throw boom.notFound(`Pipeline ${pipelineTemplate.pipelineId} does not exist`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (scope.includes('user')) {
|
|
211
|
+
const user = await userFactory.get({ username, scmContext });
|
|
212
|
+
|
|
213
|
+
if (!user) {
|
|
214
|
+
throw boom.notFound(`User ${username} does not exist`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const permissions = await user.getPermissions(pipeline.scmUri);
|
|
218
|
+
|
|
219
|
+
if (!permissions[permission]) {
|
|
220
|
+
throw boom.forbidden(
|
|
221
|
+
`User ${username} does not have ${permission} access for this pipelineTemplate`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (pipelineTemplate.pipelineId !== credentials.pipelineId || credentials.isPR) {
|
|
229
|
+
throw boom.forbidden('Not allowed to remove this pipelineTemplate');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return true;
|
|
233
|
+
});
|
|
234
|
+
|
|
179
235
|
server.route([
|
|
180
236
|
createRoute(),
|
|
181
237
|
removeRoute(),
|
|
@@ -213,7 +269,10 @@ const pipelinesPlugin = {
|
|
|
213
269
|
getVersionRoute(),
|
|
214
270
|
getTemplateByIdRoute(),
|
|
215
271
|
getTemplateRoute(),
|
|
216
|
-
createTagRoute()
|
|
272
|
+
createTagRoute(),
|
|
273
|
+
removeTemplateRoute(),
|
|
274
|
+
removeTemplateTagRoute(),
|
|
275
|
+
removeTemplateVersionRoute()
|
|
217
276
|
]);
|
|
218
277
|
}
|
|
219
278
|
};
|
|
@@ -18,7 +18,12 @@ module.exports = () => ({
|
|
|
18
18
|
scope: ['build']
|
|
19
19
|
},
|
|
20
20
|
handler: async (request, h) => {
|
|
21
|
-
const {
|
|
21
|
+
const {
|
|
22
|
+
pipelineFactory,
|
|
23
|
+
pipelineTemplateFactory,
|
|
24
|
+
pipelineTemplateVersionFactory,
|
|
25
|
+
pipelineTemplateTagFactory
|
|
26
|
+
} = request.server.app;
|
|
22
27
|
|
|
23
28
|
const { isPR, pipelineId } = request.auth.credentials;
|
|
24
29
|
|
|
@@ -28,7 +33,7 @@ module.exports = () => ({
|
|
|
28
33
|
|
|
29
34
|
const [pipeline, template, templateTag] = await Promise.all([
|
|
30
35
|
pipelineFactory.get(pipelineId),
|
|
31
|
-
pipelineTemplateVersionFactory.get({ name, namespace, version }),
|
|
36
|
+
pipelineTemplateVersionFactory.get({ name, namespace, version }, pipelineTemplateFactory),
|
|
32
37
|
pipelineTemplateTagFactory.get({ name, namespace, tag })
|
|
33
38
|
]);
|
|
34
39
|
|
|
@@ -20,13 +20,16 @@ module.exports = () => ({
|
|
|
20
20
|
},
|
|
21
21
|
handler: async (request, h) => {
|
|
22
22
|
const { namespace, name, versionOrTag } = request.params;
|
|
23
|
-
const { pipelineTemplateVersionFactory } = request.server.app;
|
|
23
|
+
const { pipelineTemplateFactory, pipelineTemplateVersionFactory } = request.server.app;
|
|
24
24
|
|
|
25
|
-
const pipelineTemplate = await pipelineTemplateVersionFactory.getWithMetadata(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
const pipelineTemplate = await pipelineTemplateVersionFactory.getWithMetadata(
|
|
26
|
+
{
|
|
27
|
+
name,
|
|
28
|
+
namespace,
|
|
29
|
+
versionOrTag
|
|
30
|
+
},
|
|
31
|
+
pipelineTemplateFactory
|
|
32
|
+
);
|
|
30
33
|
|
|
31
34
|
if (!pipelineTemplate) {
|
|
32
35
|
throw boom.notFound('Pipeline Template does not exist');
|
|
@@ -22,7 +22,7 @@ module.exports = () => ({
|
|
|
22
22
|
scope: ['user', 'build']
|
|
23
23
|
},
|
|
24
24
|
handler: async (request, h) => {
|
|
25
|
-
const { pipelineTemplateVersionFactory } = request.server.app;
|
|
25
|
+
const { pipelineTemplateFactory, pipelineTemplateVersionFactory } = request.server.app;
|
|
26
26
|
const config = {
|
|
27
27
|
params: request.params,
|
|
28
28
|
sort: request.query.sort
|
|
@@ -35,7 +35,7 @@ module.exports = () => ({
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const templates = await pipelineTemplateVersionFactory.list(config);
|
|
38
|
+
const templates = await pipelineTemplateVersionFactory.list(config, pipelineTemplateFactory);
|
|
39
39
|
|
|
40
40
|
if (!templates || templates.length === 0) {
|
|
41
41
|
throw boom.notFound('Template does not exist');
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const boom = require('@hapi/boom');
|
|
4
|
+
const joi = require('joi');
|
|
5
|
+
const schema = require('screwdriver-data-schema');
|
|
6
|
+
const baseSchema = schema.models.templateMeta.base;
|
|
7
|
+
|
|
8
|
+
module.exports = () => ({
|
|
9
|
+
method: 'DELETE',
|
|
10
|
+
path: '/pipeline/templates/{namespace}/{name}',
|
|
11
|
+
options: {
|
|
12
|
+
description: 'Delete a pipeline template and its related versions and tags',
|
|
13
|
+
notes: 'Returns null if successful',
|
|
14
|
+
tags: ['api', 'templates'],
|
|
15
|
+
auth: {
|
|
16
|
+
strategies: ['token'],
|
|
17
|
+
scope: ['build', 'user', '!guest']
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
handler: async (request, h) => {
|
|
21
|
+
const { namespace, name } = request.params;
|
|
22
|
+
const { credentials } = request.auth;
|
|
23
|
+
const { pipelineTemplateFactory, pipelineTemplateTagFactory, pipelineTemplateVersionFactory } =
|
|
24
|
+
request.server.app;
|
|
25
|
+
const { canRemove } = request.server.plugins.pipelines;
|
|
26
|
+
|
|
27
|
+
const pipelineTemplate = await pipelineTemplateFactory.get({ namespace, name });
|
|
28
|
+
|
|
29
|
+
if (!pipelineTemplate) {
|
|
30
|
+
throw boom.notFound(`PipelineTemplate ${namespace / name} does not exist`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const [tags, templateVersions] = await Promise.all([
|
|
34
|
+
pipelineTemplateTagFactory.list({ params: { namespace, name } }),
|
|
35
|
+
pipelineTemplateVersionFactory.list({ params: { namespace, name } }, pipelineTemplateFactory)
|
|
36
|
+
]);
|
|
37
|
+
|
|
38
|
+
const canRemoveFlag = await canRemove(credentials, pipelineTemplate, 'admin', request.server.app);
|
|
39
|
+
|
|
40
|
+
if (canRemoveFlag) {
|
|
41
|
+
const templatePromise = pipelineTemplate.remove();
|
|
42
|
+
const tagPromises = tags.map(tag => tag.remove());
|
|
43
|
+
const versionPromises = templateVersions.map(version => version.remove());
|
|
44
|
+
|
|
45
|
+
await Promise.all([templatePromise, ...tagPromises, ...versionPromises]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return h.response().code(204);
|
|
49
|
+
},
|
|
50
|
+
validate: {
|
|
51
|
+
params: joi.object({
|
|
52
|
+
namespace: baseSchema.extract('namespace'),
|
|
53
|
+
name: baseSchema.extract('name')
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const boom = require('@hapi/boom');
|
|
4
|
+
const joi = require('joi');
|
|
5
|
+
const schema = require('screwdriver-data-schema');
|
|
6
|
+
const baseSchema = schema.models.templateTag.base;
|
|
7
|
+
const metaSchema = schema.models.templateMeta.base;
|
|
8
|
+
|
|
9
|
+
module.exports = () => ({
|
|
10
|
+
method: 'DELETE',
|
|
11
|
+
path: '/pipeline/templates/{namespace}/{name}/tags/{tag}',
|
|
12
|
+
options: {
|
|
13
|
+
description: 'Delete a pipeline template tag',
|
|
14
|
+
notes: 'Delete a specific pipeline template',
|
|
15
|
+
tags: ['api', 'templates'],
|
|
16
|
+
auth: {
|
|
17
|
+
strategies: ['token'],
|
|
18
|
+
scope: ['build']
|
|
19
|
+
},
|
|
20
|
+
handler: async (request, h) => {
|
|
21
|
+
const {
|
|
22
|
+
pipelineFactory,
|
|
23
|
+
pipelineTemplateFactory,
|
|
24
|
+
pipelineTemplateTagFactory,
|
|
25
|
+
pipelineTemplateVersionFactory
|
|
26
|
+
} = request.server.app;
|
|
27
|
+
const { pipelineId, isPR } = request.auth.credentials;
|
|
28
|
+
const { name, namespace, tag } = request.params;
|
|
29
|
+
|
|
30
|
+
const templateTag = await pipelineTemplateTagFactory.get({ namespace, name, tag });
|
|
31
|
+
|
|
32
|
+
if (!templateTag) {
|
|
33
|
+
throw boom.notFound('PipelineTemplate tag does not exist');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const [pipeline, pipelineTemplate] = await Promise.all([
|
|
37
|
+
pipelineFactory.get(pipelineId),
|
|
38
|
+
pipelineTemplateVersionFactory.getWithMetadata(
|
|
39
|
+
{
|
|
40
|
+
namespace,
|
|
41
|
+
name,
|
|
42
|
+
version: templateTag.version
|
|
43
|
+
},
|
|
44
|
+
pipelineTemplateFactory
|
|
45
|
+
)
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
// Check for permission
|
|
49
|
+
if (pipeline.id !== pipelineTemplate.pipelineId || isPR) {
|
|
50
|
+
throw boom.forbidden('Not allowed to delete this pipeline template tag');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Remove the template tag
|
|
54
|
+
await templateTag.remove();
|
|
55
|
+
|
|
56
|
+
return h.response().code(204);
|
|
57
|
+
},
|
|
58
|
+
validate: {
|
|
59
|
+
params: joi.object({
|
|
60
|
+
namespace: metaSchema.extract('namespace'),
|
|
61
|
+
name: metaSchema.extract('name'),
|
|
62
|
+
tag: baseSchema.extract('tag')
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const boom = require('@hapi/boom');
|
|
4
|
+
const joi = require('joi');
|
|
5
|
+
const schema = require('screwdriver-data-schema');
|
|
6
|
+
const baseSchema = schema.models.templateMeta.base;
|
|
7
|
+
const exactVersionSchema = schema.models.pipelineTemplateVersions.base.extract('version');
|
|
8
|
+
|
|
9
|
+
module.exports = () => ({
|
|
10
|
+
method: 'DELETE',
|
|
11
|
+
path: '/pipeline/templates/{namespace}/{name}/versions/{version}',
|
|
12
|
+
options: {
|
|
13
|
+
description: 'Delete the specified version of a pipeline template and the tags associated with it',
|
|
14
|
+
notes: 'Returns null if successful',
|
|
15
|
+
tags: ['api', 'templates'],
|
|
16
|
+
auth: {
|
|
17
|
+
strategies: ['token'],
|
|
18
|
+
scope: ['build', 'user', '!guest']
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
handler: async (request, h) => {
|
|
22
|
+
const { namespace, name, version } = request.params;
|
|
23
|
+
const { credentials } = request.auth;
|
|
24
|
+
|
|
25
|
+
const { pipelineTemplateFactory, pipelineTemplateVersionFactory, pipelineTemplateTagFactory } =
|
|
26
|
+
request.server.app;
|
|
27
|
+
|
|
28
|
+
const [templateVersion, tags] = await Promise.all([
|
|
29
|
+
pipelineTemplateVersionFactory.getWithMetadata({ namespace, name, version }, pipelineTemplateFactory),
|
|
30
|
+
pipelineTemplateTagFactory.list({ params: { namespace, name, version } })
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
if (!templateVersion) {
|
|
34
|
+
throw boom.notFound(`PipelineTemplate ${namespace}/${name} with version ${version} does not exist`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const { canRemove } = request.server.plugins.pipelines;
|
|
38
|
+
|
|
39
|
+
const canRemoveFlag = await canRemove(credentials, templateVersion, 'admin', request.server.app);
|
|
40
|
+
|
|
41
|
+
if (canRemoveFlag) {
|
|
42
|
+
const { latestVersion, templateId } = templateVersion;
|
|
43
|
+
const removeTemplatePromise = templateVersion.remove();
|
|
44
|
+
const removeTagPromises = tags.map(tag => tag.remove());
|
|
45
|
+
|
|
46
|
+
await Promise.all([removeTemplatePromise, ...removeTagPromises]);
|
|
47
|
+
|
|
48
|
+
if (latestVersion === templateVersion.version) {
|
|
49
|
+
const templateVersions = await pipelineTemplateVersionFactory.list(
|
|
50
|
+
{
|
|
51
|
+
params: { templateId },
|
|
52
|
+
sort: 'descending',
|
|
53
|
+
sortBy: 'createTime',
|
|
54
|
+
paginate: { count: 1 }
|
|
55
|
+
},
|
|
56
|
+
pipelineTemplateFactory
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (templateVersions.length > 0) {
|
|
60
|
+
const templateMeta = await pipelineTemplateFactory.get({ id: templateId });
|
|
61
|
+
|
|
62
|
+
const newLatestTemplateVersion = templateVersions[0];
|
|
63
|
+
|
|
64
|
+
templateMeta.latestVersion = newLatestTemplateVersion.version;
|
|
65
|
+
|
|
66
|
+
await templateMeta.update();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return h.response().code(204);
|
|
72
|
+
},
|
|
73
|
+
validate: {
|
|
74
|
+
params: joi.object({
|
|
75
|
+
namespace: baseSchema.extract('namespace'),
|
|
76
|
+
name: baseSchema.extract('name'),
|
|
77
|
+
version: exactVersionSchema
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|