Skip to content

Commit 69e3d7c

Browse files
authoredJun 14, 2021
feat(2437): Support read-only SCM (#2460)
1 parent c740216 commit 69e3d7c

37 files changed

+1006
-1109
lines changed
 

‎bin/server

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ delete datastorePluginConfig.readOnly;
8181
// Default datastore
8282
const datastore = new DatastorePlugin(hoek.applyToDefaults({ ecosystem }, datastorePluginConfig || {}));
8383

84-
// Source Code Plugin
84+
// Setup Source Code Plugin(s)
8585
const scmConfig = { scms: config.get('scms') };
8686
const ScmPlugin = require('screwdriver-scm-router');
8787
const scm = new ScmPlugin(scmConfig || {});

‎config/custom-environment-variables.yaml

+11-1
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,16 @@ scms:
320320
# # if you have on-premise gitlab, you can specify that here
321321
# gitlabHost: SCM_GITLAB_HOST
322322
# gitlabProtocol: SCM_GITLAB_PROTOCOL
323+
# # readOnly scm config, default false
324+
# readOnly:
325+
# # set true to enable read-only scm mode
326+
# enabled: SCM_GITLAB_RO_ENABLED
327+
# # headless username
328+
# username: SCM_GITLAB_RO_USERNAME
329+
# # headless access token
330+
# accessToken: SCM_GITLAB_RO_TOKEN
331+
# # SCM clone type (https or ssh)
332+
# cloneType: SCM_GITLAB_RO_CLONE_TYPE
323333
webhooks:
324334
# Obtains the SCM token for a given user. If a user does not have a valid SCM token registered with Screwdriver, it will use this user's token instead.
325335
username: SCM_USERNAME
@@ -399,7 +409,7 @@ redisLock:
399409
enabled: REDLOCK_ENABLED
400410
options:
401411
# maximum retry limit to obtain lock
402-
retryCount: REDLOCK_RETRY_COUNT
412+
retryCount: REDLOCK_RETRY_COUNT
403413
# the expected clock drift
404414
driftFactor: REDLOCK_DRIFT_FACTOR
405415
# the time in milliseconds between retry attempts

‎config/default.yaml

+13-3
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,18 @@ scms: {}
229229
# # gitlabHost: mygitlab.com
230230
# # gitlabProtocol: https
231231
# # The username and email used for checkout with gitlab
232-
# # username: sd-buildbot
232+
# username: sd-buildbot
233233
# # email: dev-null@screwdriver.cd
234+
# # read-only scm config, default false
235+
# readOnly:
236+
# # set true to enable read-only scm mode
237+
# enabled: false
238+
# # headless username
239+
# username: headless-user
240+
# # headless access token
241+
# accessToken: headlesstoken
242+
# # SCM clone type (https or ssh)
243+
# cloneType: https
234244
webhooks:
235245
# Obtains the SCM token for a given user. If a user does not have a valid SCM token registered with Screwdriver, it will use this user's token instead.
236246
username: sd-buildbot
@@ -331,7 +341,7 @@ redisLock:
331341
enabled: false
332342
options:
333343
# maximum retry limit to obtain lock
334-
retryCount: 200
344+
retryCount: 200
335345
# the expected clock drift
336346
driftFactor: 0.01
337347
# the time in milliseconds between retry attempts
@@ -345,4 +355,4 @@ redisLock:
345355
options:
346356
password: "THIS-IS-A-PASSWORD"
347357
tls: false
348-
database: 0
358+
database: 0

‎package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
"@hapi/good": "^9.0.1",
7575
"@hapi/good-console": "^9.0.1",
7676
"@hapi/good-squeeze": "^6.0.0",
77-
"@hapi/hapi": "^20.1.0",
77+
"@hapi/hapi": "^20.1.3",
7878
"@hapi/hoek": "^9.1.1",
7979
"@hapi/inert": "^6.0.3",
8080
"@hapi/vision": "^6.1.0",
@@ -87,7 +87,7 @@
8787
"hapi-auth-bearer-token": "^8.0.0",
8888
"hapi-auth-jwt2": "^10.2.0",
8989
"hapi-rate-limit": "^5.0.0",
90-
"hapi-swagger": "^14.1.0",
90+
"hapi-swagger": "^14.1.3",
9191
"ioredis": "^3.2.2",
9292
"joi": "^17.4.0",
9393
"js-yaml": "^3.14.1",
@@ -116,12 +116,12 @@
116116
"screwdriver-executor-queue": "^3.0.2",
117117
"screwdriver-executor-router": "^2.1.2",
118118
"screwdriver-logger": "^1.0.2",
119-
"screwdriver-models": "^28.6.3",
119+
"screwdriver-models": "^28.7.3",
120120
"screwdriver-notifications-email": "^2.1.5",
121121
"screwdriver-notifications-slack": "^3.1.6",
122122
"screwdriver-scm-base": "^7.1.3",
123123
"screwdriver-scm-bitbucket": "^4.2.0",
124-
"screwdriver-scm-github": "^11.1.4",
124+
"screwdriver-scm-github": "^11.3.0",
125125
"screwdriver-scm-gitlab": "^2.2.1",
126126
"screwdriver-scm-router": "^6.1.1",
127127
"screwdriver-template-validator": "^5.1.2",
@@ -145,10 +145,10 @@
145145
"coveralls": "^3.1.0",
146146
"cucumber": "6.0.4",
147147
"cucumber-pretty": "^6.0.0",
148-
"eslint": "^7.24.0",
148+
"eslint": "^7.26.0",
149149
"eslint-config-screwdriver": "^5.0.5",
150150
"form-data": "^2.5.1",
151-
"mocha": "^8.2.1",
151+
"mocha": "^8.4.0",
152152
"mocha-multi-reporters": "^1.5.1",
153153
"mocha-sonarqube-reporter": "^1.0.2",
154154
"mockery": "^2.0.0",

‎plugins/auth/contexts.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ module.exports = config => ({
3232
displayName: scm.getDisplayName({ scmContext }),
3333
autoDeployKeyGeneration: scm.autoDeployKeyGenerationEnabled({
3434
scmContext
35-
})
35+
}),
36+
readOnly: scm.getReadOnlyInfo({ scmContext }).enabled
3637
};
3738

3839
contexts.push(context);
@@ -42,7 +43,8 @@ module.exports = config => ({
4243
contexts.push({
4344
context: 'guest',
4445
displayName: 'Guest Access',
45-
autoDeployKeyGeneration: false
46+
autoDeployKeyGeneration: false,
47+
readOnly: false
4648
});
4749
}
4850

‎plugins/builds/update.js

+26-40
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const hoek = require('@hapi/hoek');
55
const schema = require('screwdriver-data-schema');
66
const joi = require('joi');
77
const idSchema = schema.models.job.base.extract('id');
8+
const { getScmUri, getUserPermissions } = require('../helper');
89

910
/**
1011
* Determine if this build is FIXED build or not.
@@ -68,7 +69,8 @@ module.exports = () => ({
6869
jobFactory,
6970
userFactory,
7071
stepFactory,
71-
bannerFactory
72+
bannerFactory,
73+
pipelineFactory
7274
} = request.server.app;
7375
const { id } = request.params;
7476
const { statusMessage, stats, status: desiredStatus } = request.payload;
@@ -82,7 +84,7 @@ module.exports = () => ({
8284

8385
return buildFactory
8486
.get(id)
85-
.then(build => {
87+
.then(async build => {
8688
if (!build) {
8789
throw boom.notFound(`Build ${id} does not exist`);
8890
}
@@ -110,36 +112,24 @@ module.exports = () => ({
110112
throw boom.badRequest('User can only update builds to ABORTED');
111113
}
112114

113-
// Check permission against the pipeline
114115
// Fetch the job and user models
115-
return (
116-
Promise.all([jobFactory.get(build.jobId), userFactory.get({ username, scmContext })])
117-
// scmUri is buried in the pipeline, so we get that from the job
118-
.then(([job, user]) =>
119-
job.pipeline.then(pipeline =>
120-
user
121-
.getPermissions(pipeline.scmUri)
122-
// Check if user has push access or is a Screwdriver admin
123-
.then(permissions => {
124-
if (!permissions.push && !adminDetails.isAdmin) {
125-
throw boom.forbidden(
126-
`User ${user.getFullDisplayName()} does not ` +
127-
'have permission to abort this build'
128-
);
129-
}
130-
131-
return eventFactory
132-
.get(build.eventId)
133-
.then(event => ({ build, event }));
134-
})
135-
)
136-
)
137-
);
116+
const [job, user] = await Promise.all([
117+
jobFactory.get(build.jobId),
118+
userFactory.get({ username, scmContext })
119+
]);
120+
121+
const pipeline = await job.pipeline;
122+
123+
// Use parent's scmUri if pipeline is child pipeline and using read-only SCM
124+
const scmUri = await getScmUri({ pipeline, pipelineFactory });
125+
126+
// Check the user's permission
127+
await getUserPermissions({ user, scmUri, level: 'push', isAdmin: adminDetails.isAdmin });
138128
}
139129

140130
return eventFactory.get(build.eventId).then(event => ({ build, event }));
141131
})
142-
.then(({ build, event }) => {
132+
.then(async ({ build, event }) => {
143133
// We can't merge from executor-k8s/k8s-vm side because executor doesn't have build object
144134
// So we do merge logic here instead
145135

@@ -209,20 +199,17 @@ module.exports = () => ({
209199

210200
// If status got updated to RUNNING or COLLAPSED, update init endTime and code
211201
if (['RUNNING', 'COLLAPSED', 'FROZEN'].includes(desiredStatus)) {
212-
return stepFactory
213-
.get({ buildId: id, name: 'sd-setup-init' })
214-
.then(step => {
215-
// If there is no init step, do nothing
216-
if (!step) {
217-
return null;
218-
}
202+
const step = await stepFactory.get({ buildId: id, name: 'sd-setup-init' });
219203

220-
step.endTime = build.startTime || new Date().toISOString();
221-
step.code = 0;
204+
// If there is no init step, do nothing
205+
if (step) {
206+
step.endTime = build.startTime || new Date().toISOString();
207+
step.code = 0;
222208

223-
return step.update();
224-
})
225-
.then(() => Promise.all([build.update(), event.update()]));
209+
await step.update();
210+
}
211+
212+
return Promise.all([build.update(), event.update()]);
226213
}
227214

228215
// Only trigger next build on success
@@ -252,7 +239,6 @@ module.exports = () => ({
252239

253240
// Guard against triggering non-successful or unstable builds
254241
// Don't further trigger pipeline if intented to skip further jobs
255-
256242
if (newBuild.status !== 'SUCCESS' || skipFurther) {
257243
// Check for failed jobs and remove any child jobs in created state
258244
if (newBuild.status === 'FAILURE') {

0 commit comments

Comments
 (0)
Please sign in to comment.