-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DOP-4599: Deploy-all slack button deploys all active versions of repos #1042
Changes from 37 commits
544843d
cb4e403
dcd0287
0f135d3
de56312
299f50b
a9178df
9d07f14
b34ce94
7eaad4d
c5b0ec2
1c2b892
ea820e1
9af98cb
551c9c8
c0e5341
7086c63
e22f65f
a89d735
5eec23f
59c7d6b
de648ad
929f904
8e2018d
a9a5af6
476c5e8
b3c67e7
8633599
9c96bfb
12ac0ac
a2bae05
03aaeb3
0b608ac
685fff4
6f2da5f
45cdaa4
8a7935b
5598d46
88a39ff
d9be310
25b5e1e
dbf9c6e
e063139
8c37f85
fc2f934
59277a5
478512c
089e06f
7920ecb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,8 @@ import { | |
} from '../../handlers/slack'; | ||
import { DocsetsRepository } from '../../../src/repositories/docsetsRepository'; | ||
import { Payload } from '../../../src/entities/job'; | ||
import { ProjectsRepository } from '../../../src/repositories/projectsRepository'; | ||
import { DOCS_METADATA } from '../../../src/constants'; | ||
|
||
export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise<APIGatewayProxyResult> => { | ||
const consoleLogger = new ConsoleLogger(); | ||
|
@@ -34,21 +36,34 @@ export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise<APIGat | |
const client = new mongodb.MongoClient(c.get('dbUrl')); | ||
await client.connect(); | ||
const db = client.db(process.env.DB_NAME); | ||
const projectsRepository = new ProjectsRepository(client.db(DOCS_METADATA), c, consoleLogger); | ||
const repoEntitlementRepository = new RepoEntitlementsRepository(db, c, consoleLogger); | ||
const repoBranchesRepository = new RepoBranchesRepository(db, c, consoleLogger); | ||
const key_val = getQSString(event.body); | ||
const entitlement = await repoEntitlementRepository.getRepoEntitlementsBySlackUserId(key_val['user_id']); | ||
if (!isUserEntitled(entitlement) || isRestrictedToDeploy(key_val['user_id'])) { | ||
const { restrictedProdDeploy } = c.get<any>('prodDeploy'); | ||
const response = restrictedProdDeploy | ||
? 'Production freeze in place - please notify DOP if seeing this past 3/26' | ||
: 'User is not entitled!'; | ||
return prepResponse(401, 'text/plain', response); | ||
} | ||
|
||
const isAdmin = await repoEntitlementRepository.getIsAdmin(key_val['user_id']); | ||
let entitledRepos: any[] = []; | ||
//if user has admin permissions, they can deploy all repo branches | ||
if (isAdmin) { | ||
const repos = await repoBranchesRepository.getProdDeployableRepoBranches(); | ||
for (const repo of repos) { | ||
const projectEntry = await projectsRepository.getProjectEntry(repo.project); | ||
const repoOwner = projectEntry?.github?.organization; | ||
if (repoOwner) entitledRepos.push(`${repoOwner}/${repo.repoName}`); | ||
} | ||
} else { | ||
const entitlements = await repoEntitlementRepository.getRepoEntitlementsBySlackUserId(key_val['user_id']); | ||
if (!isUserEntitled(entitlements) || isRestrictedToDeploy(key_val['user_id'])) { | ||
const { restrictedProdDeploy } = c.get<any>('prodDeploy'); | ||
const response = restrictedProdDeploy | ||
? 'Production freeze in place - please notify DOP if seeing this past 3/26' | ||
: 'User is not entitled!'; | ||
return prepResponse(401, 'text/plain', response); | ||
} | ||
entitledRepos = entitlements.repos; | ||
} | ||
|
||
const entitledBranches = await buildEntitledGroupsList(entitlement, repoBranchesRepository); | ||
const entitledBranches = await buildEntitledGroupsList(entitledRepos, repoBranchesRepository); | ||
const resp = await slackConnector.displayRepoOptions(entitledBranches, key_val['trigger_id'], isAdmin); | ||
if (resp?.status == 200 && resp?.data?.ok) { | ||
return { | ||
|
@@ -65,8 +80,9 @@ export const DisplayRepoOptions = async (event: APIGatewayEvent): Promise<APIGat | |
async function deployRepo(deployable: Array<any>, logger: ILogger, jobRepository: JobRepository, jobQueueUrl) { | ||
try { | ||
await jobRepository.insertBulkJobs(deployable, jobQueueUrl); | ||
console.error('testing console error logging'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we remove this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yup, it will be- left it in for Matt to reference regarding his comment on logging ! |
||
} catch (err) { | ||
logger.error('deployRepo', err); | ||
console.error('Deploy repo error'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we still log the error? It can sometimes be such a blessing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fascinating! I wonder if that's always the case... I lean towards keeping the error logged just in case it can sometimes be helpful. But good catch! |
||
} | ||
} | ||
|
||
|
@@ -86,25 +102,17 @@ export const getDeployableJobs = async ( | |
const deployable = []; | ||
|
||
for (let i = 0; i < values?.repo_option?.length; i++) { | ||
let jobTitle: string, repoOwner: string, repoName: string, branchName: string, directory: string | undefined; | ||
if (values.deploy_option == 'deploy_all') { | ||
repoOwner = 'mongodb'; | ||
branchName = 'master'; | ||
repoName = values.repo_option[i].repoName; | ||
jobTitle = `Slack deploy: ${repoOwner}/${repoName}/${branchName}, by ${entitlement.github_username}`; | ||
let repoOwner: string, repoName: string, branchName: string, directory: string | undefined; | ||
const splitValues = values.repo_option[i].value.split('/'); | ||
const jobTitle = `Slack deploy: ${values.repo_option[i].value}, by ${entitlement.github_username}`; | ||
if (splitValues.length === 3) { | ||
// e.g. mongodb/docs-realm/master => (owner/repo/branch) | ||
[repoOwner, repoName, branchName] = splitValues; | ||
} else if (splitValues.length === 4 && process.env.FEATURE_FLAG_MONOREPO_PATH === 'true') { | ||
// e.g. 10gen/docs-monorepo/cloud-docs/master => (owner/monorepo/repoDirectory/branch) | ||
[repoOwner, repoName, directory, branchName] = splitValues; | ||
} else { | ||
const splitValues = values.repo_option[i].value.split('/'); | ||
jobTitle = `Slack deploy: ${values.repo_option[i].value}, by ${entitlement.github_username}`; | ||
|
||
if (splitValues.length === 3) { | ||
// e.g. mongodb/docs-realm/master => (owner/repo/branch) | ||
[repoOwner, repoName, branchName] = splitValues; | ||
} else if (splitValues.length === 4 && process.env.FEATURE_FLAG_MONOREPO_PATH === 'true') { | ||
// e.g. 10gen/docs-monorepo/cloud-docs/master => (owner/monorepo/repoDirectory/branch) | ||
[repoOwner, repoName, directory, branchName] = splitValues; | ||
} else { | ||
throw Error('Selected entitlement value is configured incorrectly. Check user entitlements!'); | ||
} | ||
throw Error('Selected entitlement value is configured incorrectly. Check user entitlements!'); | ||
} | ||
|
||
const hashOption = values?.hash_option ?? null; | ||
|
@@ -115,11 +123,12 @@ export const getDeployableJobs = async ( | |
const non_versioned = repoInfo.branches.length === 1; | ||
|
||
const branchObject = await repoBranchesRepository.getRepoBranchAliases(repoName, branchName, repoInfo.project); | ||
if (!branchObject?.aliasObject) continue; | ||
if (branchObject.status == 'failure' || !branchObject?.aliasObject) | ||
return prepResponse(401, 'text/plain', 'Branch not found in repos branches repository'); | ||
|
||
const publishOriginalBranchName: boolean = branchObject.aliasObject.publishOriginalBranchName; | ||
const aliases: string[] | null = branchObject.aliasObject.urlAliases; | ||
let urlSlug: string = branchObject.aliasObject.urlSlug; // string or null, string must match value in urlAliases or gitBranchName | ||
let urlSlug: string = branchObject?.aliasObject.urlSlug; // string or null, string must match value in urlAliases or gitBranchName | ||
const isStableBranch = !!branchObject.aliasObject.isStableBranch; // bool or Falsey, add strong typing | ||
|
||
if (!urlSlug || !urlSlug.trim()) { | ||
|
@@ -142,7 +151,7 @@ export const getDeployableJobs = async ( | |
directory | ||
); | ||
|
||
if (!aliases || aliases.length === 0) { | ||
if (!aliases || !aliases.length) { | ||
if (non_versioned) { | ||
newPayload.urlSlug = ''; | ||
} | ||
|
@@ -214,16 +223,24 @@ export const DeployRepo = async (event: any = {}): Promise<any> => { | |
|
||
let values = []; | ||
const isAdmin = await repoEntitlementRepository.getIsAdmin(parsed.user.id); | ||
const optionGroups = parsed.view.blocks[0]?.element?.option_groups; | ||
try { | ||
values = await slackConnector.parseSelection(stateValues, isAdmin, repoBranchesRepository); | ||
values = await slackConnector.parseSelection(stateValues, isAdmin, optionGroups); | ||
} catch (e) { | ||
console.log(`Error parsing selection: ${e}`); | ||
return prepResponse(401, 'text/plain', e); | ||
} | ||
const deployable = await getDeployableJobs(values, entitlement, repoBranchesRepository, docsetsRepository); | ||
|
||
let deployable; | ||
try { | ||
deployable = await getDeployableJobs(values, entitlement, repoBranchesRepository, docsetsRepository); | ||
} catch (e) { | ||
return prepResponse(401, 'text/plain', `${e} error within get deployable jobs`); | ||
} | ||
if (deployable.length > 0) { | ||
await deployRepo(deployable, consoleLogger, jobRepository, c.get('jobsQueueUrl')); | ||
try { | ||
await deployRepo(deployable, consoleLogger, jobRepository, c.get('jobsQueueUrl')); | ||
} catch (e) { | ||
return prepResponse(401, 'text/plain', `${e} error deploying repos`); | ||
} | ||
} | ||
return { | ||
statusCode: 200, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const DOCS_METADATA = 'docs_metadata'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,6 @@ import axios from 'axios'; | |
import { ILogger } from './logger'; | ||
import { IConfig } from 'config'; | ||
import * as crypto from 'crypto'; | ||
import { RepoBranchesRepository } from '../repositories/repoBranchesRepository'; | ||
export const axiosApi = axios.create(); | ||
|
||
function bufferEqual(a: Buffer, b: Buffer) { | ||
|
@@ -24,7 +23,7 @@ function timeSafeCompare(a: string, b: string) { | |
export interface ISlackConnector { | ||
validateSlackRequest(payload: any): boolean; | ||
displayRepoOptions(repos: Array<string>, triggerId: string, isAdmin: boolean): Promise<any>; | ||
parseSelection(payload: any, isAdmin: boolean, repoBranchesRepository: RepoBranchesRepository): any; | ||
parseSelection(payload: any, isAdmin: boolean, optionGroups: any[]): any; | ||
sendMessage(message: any, user: string): Promise<any>; | ||
} | ||
|
||
|
@@ -54,11 +53,7 @@ export class SlackConnector implements ISlackConnector { | |
return {}; | ||
} | ||
|
||
async parseSelection( | ||
stateValues: any, | ||
isAdmin: boolean, | ||
repoBranchesRepository: RepoBranchesRepository | ||
): Promise<any> { | ||
async parseSelection(stateValues: any, isAdmin: boolean, optionGroups: any[]): Promise<any> { | ||
const values = {}; | ||
const inputMapping = { | ||
block_repo_option: 'repo_option', | ||
|
@@ -72,7 +67,16 @@ export class SlackConnector implements ISlackConnector { | |
} | ||
|
||
values['deploy_option'] = 'deploy_all'; | ||
values['repo_option'] = await repoBranchesRepository.getProdDeployableRepoBranches(); | ||
//go through all options in dropdown by option group | ||
//append version to repo_option if active | ||
values['repo_option'] = []; | ||
for (const group of optionGroups) { | ||
values['repo_option'].push( | ||
...group.options.filter((option) => { | ||
!option.text.text.startsWith('(!inactive)'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should return this to ensure things are being filtered as expected. Right now, I believe the array would be empty |
||
}) | ||
); | ||
} | ||
return values; | ||
} | ||
|
||
|
@@ -207,6 +211,7 @@ export class SlackConnector implements ISlackConnector { | |
}, | ||
option_groups: repos, | ||
}, | ||
optional: true, | ||
}, | ||
{ | ||
type: 'input', | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be deleted upon PR approval