@docupace/pipeline 1.0.1-2.ervinsUpdate2.6 → 1.0.2-3.tes1
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 +2 -1
- package/src/auth0/alias.js +75 -0
- package/src/auth0/cli.js +13 -3
- package/src/auth0/configure.js +92 -28
- package/src/next/alias.js +53 -0
- package/src/next/cli.js +24 -25
- package/src/next/deploy.js +109 -42
- package/src/next/vault.js +34 -30
- package/terraform/init.sh +13 -1
- package/terraform/plan.sh +10 -0
- package/terraform/validate.sh +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docupace/pipeline",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23.tes1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"license": "ISC",
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@changesets/cli": "^2.27.10",
|
|
19
|
+
"@docupace/core-client": "^0.1.59",
|
|
19
20
|
"auth0-deploy-cli": "^7.24.3",
|
|
20
21
|
"axios": "^1.7.9",
|
|
21
22
|
"commander": "^12.1.0",
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createAlias
|
|
5
|
+
} from '../next/alias.js';
|
|
6
|
+
|
|
7
|
+
import {getAuth0AuthToken} from './configure.js';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
getVaultSecrets,
|
|
11
|
+
} from './vault.js';
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
getVaultSecretsConfigs,
|
|
15
|
+
} from '../next/config.js';
|
|
16
|
+
|
|
17
|
+
import {
|
|
18
|
+
cloneEnvRepository,
|
|
19
|
+
readDeployConfig,
|
|
20
|
+
} from '../next/deploy.js';
|
|
21
|
+
|
|
22
|
+
export async function getCCAlias ({application, auth0Domain, auth0AuthToken}){
|
|
23
|
+
console.log(`Forming alias name for application ${application}...`);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const auth0Response = await axios.get(`https://${auth0Domain}/api/v2/clients`, {
|
|
27
|
+
headers: {authorization: `Bearer ${auth0AuthToken}`},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const appAuth0ClientIdData = auth0Response.data.find(client => client.name === `${application}`);
|
|
31
|
+
const appAuth0ClientId = appAuth0ClientIdData.client_id;
|
|
32
|
+
|
|
33
|
+
console.log(`Client Credentials Alias: ${appAuth0ClientId}@clients`);
|
|
34
|
+
return `${appAuth0ClientId}@clients`;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
console.error(`Failed to form alias name for application: ${application}: ${err.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function configureAlias(site, env, username, password, realm) {
|
|
41
|
+
|
|
42
|
+
console.log(`Creating alias for site: ${site} in env: ${env}`);
|
|
43
|
+
|
|
44
|
+
const commonSecretsPath = getVaultSecretsConfigs(site).commonSecretsPath;
|
|
45
|
+
console.log(`commonSecretsPath => ${commonSecretsPath}`)
|
|
46
|
+
const commonSecrets = await getVaultSecrets(commonSecretsPath);
|
|
47
|
+
|
|
48
|
+
// Site Common Secrets
|
|
49
|
+
const siteCommonSecretsPath = getVaultSecretsConfigs(site).siteCommonSecretsPath;
|
|
50
|
+
console.log(`siteCommonSecretsPath => ${siteCommonSecretsPath}`)
|
|
51
|
+
const siteCommonSecrets = await getVaultSecrets(siteCommonSecretsPath);
|
|
52
|
+
|
|
53
|
+
const auth0Domain = `docupace-${env}-${site}.us.auth0.com`
|
|
54
|
+
const auth0ClientId = siteCommonSecrets.AUTH0_CLIENT_ID;
|
|
55
|
+
const auth0ClientSecret = siteCommonSecrets.AUTH0_CLIENT_SECRET;
|
|
56
|
+
const auth0Token = await getAuth0AuthToken(auth0Domain, auth0ClientId, auth0ClientSecret);
|
|
57
|
+
|
|
58
|
+
await cloneEnvRepository(env);
|
|
59
|
+
|
|
60
|
+
const { deployConfig: deployGraphQLServerConfig} = await readDeployConfig('graphql-server', env, site, 'values');
|
|
61
|
+
const coreUrl = deployGraphQLServerConfig.graphqlServer.coreUrl;
|
|
62
|
+
|
|
63
|
+
const clientCredentialsAlias = await getCCAlias({
|
|
64
|
+
application: 'docupace-graphql-client-credentials',
|
|
65
|
+
auth0Domain: auth0Domain,
|
|
66
|
+
auth0AuthToken: auth0Token
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await createAlias(
|
|
70
|
+
coreUrl,
|
|
71
|
+
username,
|
|
72
|
+
password,
|
|
73
|
+
realm,
|
|
74
|
+
clientCredentialsAlias);
|
|
75
|
+
}
|
package/src/auth0/cli.js
CHANGED
|
@@ -9,16 +9,19 @@ import {
|
|
|
9
9
|
} from './vault.js'
|
|
10
10
|
|
|
11
11
|
import dotenv from 'dotenv';
|
|
12
|
+
import {
|
|
13
|
+
configureAlias
|
|
14
|
+
} from "./alias.js";
|
|
12
15
|
|
|
13
16
|
dotenv.config({ path: ['.env.pipeline.local', 'config/.env.pipeline.local'] });
|
|
14
17
|
|
|
15
18
|
const program = new Command();
|
|
16
19
|
|
|
17
20
|
program
|
|
18
|
-
.command('configureFromSite <fromSite> <toSite> <fromEnv> <toEnv> <deployUI> <deployGraphQLServer> <useNewDomain>')
|
|
21
|
+
.command('configureFromSite <fromSite> <toSite> <fromEnv> <toEnv> <deployUI> <deployGraphQLServer> <useNewDomain> <configureMFA>')
|
|
19
22
|
.description('Configure the Auth0 tenants in env for the given site')
|
|
20
|
-
.action(async (fromSite, toSite, fromEnv,toEnv, deployUI, deployGraphQLServer, useNewDomain) => {
|
|
21
|
-
await configureFromSite(fromSite, toSite, fromEnv, toEnv, deployUI, deployGraphQLServer, useNewDomain);
|
|
23
|
+
.action(async (fromSite, toSite, fromEnv,toEnv, deployUI, deployGraphQLServer, useNewDomain, configureMFA) => {
|
|
24
|
+
await configureFromSite(fromSite, toSite, fromEnv, toEnv, deployUI, deployGraphQLServer, useNewDomain, configureMFA);
|
|
22
25
|
});
|
|
23
26
|
|
|
24
27
|
program
|
|
@@ -28,4 +31,11 @@ program
|
|
|
28
31
|
await getVaultSecrets(vaultPath);
|
|
29
32
|
});
|
|
30
33
|
|
|
34
|
+
program
|
|
35
|
+
.command('configureAlias <site> <env> <username> <password> <realm>')
|
|
36
|
+
.description('Configure Core Alias for the given site')
|
|
37
|
+
.action(async (site, env, username, password, realm) => {
|
|
38
|
+
await configureAlias(site, env, username, password, realm);
|
|
39
|
+
});
|
|
40
|
+
|
|
31
41
|
program.parse(process.argv);
|
package/src/auth0/configure.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import yaml from 'js-yaml';
|
|
4
|
-
import { $ } from 'zx';
|
|
5
3
|
import axios from 'axios';
|
|
6
4
|
import crypto from 'crypto';
|
|
7
5
|
import { deploy } from 'auth0-deploy-cli';
|
|
6
|
+
import fl from 'fs/promises';
|
|
8
7
|
|
|
9
8
|
import {
|
|
10
9
|
cloneEnvRepository,
|
|
@@ -22,6 +21,8 @@ import {
|
|
|
22
21
|
getVaultSecretsConfigs,
|
|
23
22
|
environmentConfig
|
|
24
23
|
} from '../next/config.js';
|
|
24
|
+
import { connect } from 'http2';
|
|
25
|
+
import { promise } from 'zod';
|
|
25
26
|
|
|
26
27
|
async function ensureDirectoryExists(dirPath) {
|
|
27
28
|
try {
|
|
@@ -32,6 +33,27 @@ async function ensureDirectoryExists(dirPath) {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
async function copyDirectory(source, destination) {
|
|
37
|
+
try {
|
|
38
|
+
await fl.mkdir(destination, { recursive: true });
|
|
39
|
+
const items = await fl.readdir(source, { withFileTypes: true });
|
|
40
|
+
|
|
41
|
+
for (const item of items) {
|
|
42
|
+
const sourcePath = path.join(source, item.name);
|
|
43
|
+
const destinationPath = path.join(destination, item.name);
|
|
44
|
+
|
|
45
|
+
if (item.isDirectory()) {
|
|
46
|
+
await copyDirectory(sourcePath, destinationPath);
|
|
47
|
+
} else {
|
|
48
|
+
await fl.copyFile(sourcePath, destinationPath);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error(`Error copying directory: ${err.message}`);
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
35
57
|
async function addApplicationToApi ({application, auth0Domain, auth0AuthToken, coreApiIdentifier}){
|
|
36
58
|
console.log("Adding application to API authorized applications...");
|
|
37
59
|
|
|
@@ -155,13 +177,18 @@ async function replaceInFile(filePath, replacements) {
|
|
|
155
177
|
await fs.promises.writeFile(filePath, content, 'utf8');
|
|
156
178
|
}
|
|
157
179
|
|
|
158
|
-
async function templateAuth0(siteUrl, coreApiContext, env, filePaths, deployUI, deployGraphQLServer, commonSecrets) {
|
|
180
|
+
async function templateAuth0(siteUrl, coreApiContext, env, filePaths, deployUI, deployGraphQLServer, commonSecrets, configureMFA) {
|
|
159
181
|
try {
|
|
160
182
|
let uiAllowedCallbacksUrls,
|
|
161
183
|
uiAllowedLogoutUrls;
|
|
162
184
|
let graphqlAllowedCallbacksUrls,
|
|
163
185
|
graphqlAllowedLogoutUrls;
|
|
186
|
+
let connectAllowedCallbacksUrls,
|
|
187
|
+
connectlAllowedLogoutUrls;
|
|
164
188
|
|
|
189
|
+
let connectClientMetadata,
|
|
190
|
+
connectCallbacks,
|
|
191
|
+
connectAddons;
|
|
165
192
|
// Configure URLs based on environment
|
|
166
193
|
if (env === 'prd') {
|
|
167
194
|
uiAllowedCallbacksUrls = [`${siteUrl}/api/auth/callback`];
|
|
@@ -172,8 +199,6 @@ async function templateAuth0(siteUrl, coreApiContext, env, filePaths, deployUI,
|
|
|
172
199
|
|
|
173
200
|
connectAllowedCallbacksUrls = [`${siteUrl}/connect/samlCallback?client_name=auth0`];
|
|
174
201
|
connectlAllowedLogoutUrls = [ `${siteUrl}/connect`];
|
|
175
|
-
|
|
176
|
-
|
|
177
202
|
} else {
|
|
178
203
|
uiAllowedCallbacksUrls = [
|
|
179
204
|
`${siteUrl}/api/auth/callback`,
|
|
@@ -183,7 +208,6 @@ async function templateAuth0(siteUrl, coreApiContext, env, filePaths, deployUI,
|
|
|
183
208
|
`${siteUrl}`,
|
|
184
209
|
`http://localhost:3000`,
|
|
185
210
|
];
|
|
186
|
-
|
|
187
211
|
graphqlAllowedCallbacksUrls = [
|
|
188
212
|
`${siteUrl}/api/graphql/callback`,
|
|
189
213
|
`http://localhost:4000/api/graphql/callback`,
|
|
@@ -192,7 +216,6 @@ async function templateAuth0(siteUrl, coreApiContext, env, filePaths, deployUI,
|
|
|
192
216
|
`http://localhost:4000/api/graphql/`,
|
|
193
217
|
`${siteUrl}/api/graphql/`,
|
|
194
218
|
];
|
|
195
|
-
|
|
196
219
|
connectAllowedCallbacksUrls = [
|
|
197
220
|
`${siteUrl}/connect/samlCallback?client_name=auth0`,
|
|
198
221
|
`http://localhost:9000/connect/samlCallback?client_name=auth0`,
|
|
@@ -200,10 +223,38 @@ async function templateAuth0(siteUrl, coreApiContext, env, filePaths, deployUI,
|
|
|
200
223
|
connectlAllowedLogoutUrls = [
|
|
201
224
|
`${siteUrl}/connect`
|
|
202
225
|
];
|
|
203
|
-
|
|
204
|
-
|
|
226
|
+
|
|
227
|
+
const siteUrlNoPrefix=siteUrl.replace(/^https?:\/\//, '');
|
|
228
|
+
connectClientMetadata ={
|
|
229
|
+
"docupaceContext" : "core",
|
|
230
|
+
"docupaceHost" : `${siteUrlNoPrefix}`,
|
|
231
|
+
"docupace_context" : "core",
|
|
232
|
+
"docupace_host" : `${siteUrlNoPrefix} `
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
connectCallbacks = [
|
|
236
|
+
`${siteUrl}/connect/samlCallback?client_name=auth0`
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
connectAddons = {
|
|
240
|
+
"samlp": {
|
|
241
|
+
"audience": `${siteUrl}/connect`,
|
|
242
|
+
"recipient": `${siteUrl}/connect/samlCallback?client_name=auth0`,
|
|
243
|
+
"destination": `${siteUrl}/connect/samlCallback?client_name=auth0`,
|
|
244
|
+
"nameIdentifierFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
|
|
245
|
+
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
|
246
|
+
"nameIdentifierProbes": [
|
|
247
|
+
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/docupace-username"
|
|
248
|
+
],
|
|
249
|
+
"mappings": {
|
|
250
|
+
"proposalHandle": "ProposalHandle"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
205
255
|
}
|
|
206
256
|
|
|
257
|
+
|
|
207
258
|
const configureUI = deployUI === 'true';
|
|
208
259
|
if (configureUI) {
|
|
209
260
|
console.log('ui_allowed_callbacks_urls ->', uiAllowedCallbacksUrls);
|
|
@@ -214,15 +265,21 @@ async function templateAuth0(siteUrl, coreApiContext, env, filePaths, deployUI,
|
|
|
214
265
|
allowed_logout_urls: uiAllowedLogoutUrls,
|
|
215
266
|
});
|
|
216
267
|
|
|
268
|
+
|
|
217
269
|
//TODO: move to separate function for connect
|
|
218
270
|
console.log('connect_allowed_callbacks_urls ->', connectAllowedCallbacksUrls);
|
|
219
|
-
console.log('connect_allowed_logout_urls ->',
|
|
271
|
+
console.log('connect_allowed_logout_urls ->', connectlAllowedLogoutUrls);
|
|
220
272
|
|
|
273
|
+
// update allowed callback url
|
|
221
274
|
await updateJsonFile(filePaths.connectJson, {
|
|
222
275
|
callbacks: connectAllowedCallbacksUrls,
|
|
223
|
-
allowed_logout_urls:
|
|
276
|
+
allowed_logout_urls: connectlAllowedLogoutUrls,
|
|
277
|
+
client_metadata: connectClientMetadata,
|
|
278
|
+
addons: connectAddons
|
|
224
279
|
});
|
|
225
280
|
|
|
281
|
+
|
|
282
|
+
|
|
226
283
|
console.log('Updated URLs for main UI...');
|
|
227
284
|
} else {
|
|
228
285
|
await fs.promises.unlink(filePaths.uiJson);
|
|
@@ -259,28 +316,36 @@ async function templateAuth0(siteUrl, coreApiContext, env, filePaths, deployUI,
|
|
|
259
316
|
|
|
260
317
|
console.log('TWILIO configuration updated...');
|
|
261
318
|
|
|
319
|
+
const tmpSiteUrl = siteUrl.replace(/^https?:\/\//, '');
|
|
262
320
|
// Database configuration
|
|
263
321
|
await replaceInFile(filePaths.databaseLoginJs, [
|
|
264
|
-
['${docupaceHost}',
|
|
322
|
+
['${docupaceHost}', tmpSiteUrl],
|
|
265
323
|
['${docupaceContext}', coreApiContext],
|
|
266
324
|
]);
|
|
267
325
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
326
|
+
console.log('MFA Template configuration started...');
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
const sourceDir = configureMFA === 'DISABLED' ? 'auth0-site-setup/templateNoMFA/' : 'auth0-site-setup/templateMFA/';
|
|
330
|
+
const destinationDir = 'auth0-site-setup/template/';
|
|
331
|
+
|
|
332
|
+
console.log(`AUTH0 MFA is ${configureMFA === 'DISABLED' ? 'disabled' : 'enabled'}`);
|
|
333
|
+
await copyDirectory(sourceDir, destinationDir);
|
|
334
|
+
|
|
335
|
+
console.log('MFA configuration completed.');
|
|
336
|
+
} catch (err) {
|
|
337
|
+
console.error('Error configuring MFA:', err);
|
|
338
|
+
throw err;
|
|
339
|
+
}
|
|
276
340
|
|
|
277
341
|
console.log('Template configuration completed.');
|
|
278
342
|
} catch (err) {
|
|
279
|
-
console.error('Error configuring Auth0 templates:', err);
|
|
343
|
+
// console.error('Error configuring Auth0 templates:', err);
|
|
344
|
+
throw err;
|
|
280
345
|
}
|
|
281
346
|
}
|
|
282
347
|
|
|
283
|
-
async function getAuth0AuthToken(auth0Domain, clientId, clientSecret) {
|
|
348
|
+
export async function getAuth0AuthToken(auth0Domain, clientId, clientSecret) {
|
|
284
349
|
try {
|
|
285
350
|
const response = await axios.post(`https://${auth0Domain}/oauth/token`, {
|
|
286
351
|
client_id: clientId,
|
|
@@ -487,6 +552,7 @@ graphqlServer:
|
|
|
487
552
|
env:
|
|
488
553
|
AUTH0_CLIENT_SECRET: vault:secret/data/${site}/graphql-server/config#AUTH0_CLIENT_SECRET
|
|
489
554
|
STANDARD_CONFIG_REMOTE_PATH: vault:secret/data/common/config#STANDARD_CONFIG_REMOTE_PATH
|
|
555
|
+
SITE_CONFIG_REMOTE_PATH: vault:secret/data/${site}/common/config#SITE_CONFIG_REMOTE_PATH
|
|
490
556
|
AUTH0_CC_CLIENT_ID: vault:secret/data/${site}/graphql-server/config#AUTH0_CC_CLIENT_ID
|
|
491
557
|
AUTH0_CC_CLIENT_SECRET: vault:secret/data/${site}/graphql-server/config#AUTH0_CC_CLIENT_SECRET
|
|
492
558
|
image:
|
|
@@ -539,7 +605,7 @@ ingress:
|
|
|
539
605
|
}
|
|
540
606
|
|
|
541
607
|
|
|
542
|
-
export async function configureFromSite(fromSite, toSite, fromEnv,toEnv, deployUI, deployGraphQLServer, useNewDomain) {
|
|
608
|
+
export async function configureFromSite(fromSite, toSite, fromEnv,toEnv, deployUI, deployGraphQLServer, useNewDomain, configureMFA) {
|
|
543
609
|
console.log(`Configuring Auth0 tenants and ArgoCD apps for site: ${toSite} in env: ${toEnv} from site: ${fromSite}`);
|
|
544
610
|
// Common Secrets
|
|
545
611
|
const commonSecretsPath = getVaultSecretsConfigs(toSite).commonSecretsPath;
|
|
@@ -575,7 +641,6 @@ export async function configureFromSite(fromSite, toSite, fromEnv,toEnv, deployU
|
|
|
575
641
|
repoPathToEnv = await cloneEnvRepository(toEnv);
|
|
576
642
|
}
|
|
577
643
|
|
|
578
|
-
|
|
579
644
|
//TODO: clone fromEnv and toEnv. replace above with this
|
|
580
645
|
|
|
581
646
|
const auth0RepoPath = await cloneRepository('auth0-site-setup');
|
|
@@ -584,13 +649,12 @@ export async function configureFromSite(fromSite, toSite, fromEnv,toEnv, deployU
|
|
|
584
649
|
const { deployConfig: deployUIConfig } = await readDeployConfig('ui', fromEnv, fromSite, 'values');
|
|
585
650
|
const uiImageTag = deployUIConfig.image.tag;
|
|
586
651
|
|
|
587
|
-
const { deployConfig: deployGraphQLServerConfig} = await readDeployConfig('graphql-server', fromEnv, fromSite, 'values');
|
|
652
|
+
const { deployConfig: deployGraphQLServerConfig } = await readDeployConfig('graphql-server', fromEnv, fromSite, 'values');
|
|
588
653
|
const graphqlImageTag = deployGraphQLServerConfig.image.tag;
|
|
589
654
|
const graphqlStandardConfigRemoteBranch = deployGraphQLServerConfig.graphqlServer.standardConfigRemoteBranch;
|
|
590
655
|
|
|
591
656
|
// File paths
|
|
592
657
|
const filePaths = {
|
|
593
|
-
clientJson: './auth0-site-setup/template/clients/docupace-connect.json',
|
|
594
658
|
databaseLoginJs: './auth0-site-setup/template/database-connections/docupace/login.js',
|
|
595
659
|
loginHtml: './auth0-site-setup/template/pages/login.html',
|
|
596
660
|
graphqlJson: './auth0-site-setup/template/clients/docupace-graphql.json',
|
|
@@ -625,7 +689,7 @@ export async function configureFromSite(fromSite, toSite, fromEnv,toEnv, deployU
|
|
|
625
689
|
CONNECT_URL = `${oldUrl}_ui`;
|
|
626
690
|
}
|
|
627
691
|
|
|
628
|
-
await templateAuth0(SITE_HOST, CORE_API_CONTEXT, toEnv, filePaths, deployUI, deployGraphQLServer, commonSecrets);
|
|
692
|
+
await templateAuth0(SITE_HOST, CORE_API_CONTEXT, toEnv, filePaths, deployUI, deployGraphQLServer, commonSecrets, configureMFA);
|
|
629
693
|
await importAuth0Templates('./auth0-site-setup/template', auth0Domain, auth0ClientId, auth0ClientSecret);
|
|
630
694
|
|
|
631
695
|
if (deployUI === 'true') {
|
|
@@ -648,4 +712,4 @@ export async function configureFromSite(fromSite, toSite, fromEnv,toEnv, deployU
|
|
|
648
712
|
auth0AuthToken: auth0Token,
|
|
649
713
|
coreApiIdentifier: commonSecrets.CORE_API_IDENTIFIER
|
|
650
714
|
})
|
|
651
|
-
}
|
|
715
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { BasicAuth, DomainApi } from '@docupace/core-client';
|
|
2
|
+
|
|
3
|
+
const userFilter = (userId) => {
|
|
4
|
+
return {
|
|
5
|
+
orSections: [
|
|
6
|
+
{
|
|
7
|
+
andSections: [
|
|
8
|
+
{
|
|
9
|
+
attrId: 9501,
|
|
10
|
+
'@type': 'string',
|
|
11
|
+
not: false,
|
|
12
|
+
orSections: [
|
|
13
|
+
{ searchType: 'EXACT_MATCH', matchToValues: [userId] },
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
Example:
|
|
24
|
+
await createAlias(
|
|
25
|
+
'https://www.dev.docupaceinc.com/financialdev',
|
|
26
|
+
'docupace',
|
|
27
|
+
'pw',
|
|
28
|
+
'Connect',
|
|
29
|
+
'qhnospjRjwUAeKFDhucHEabPsfihrGQQ@clients',
|
|
30
|
+
);
|
|
31
|
+
*/
|
|
32
|
+
export async function createAlias(coreUrl, username, password, realm, alias) {
|
|
33
|
+
const coreInfo = {
|
|
34
|
+
url: coreUrl,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
let auth = new BasicAuth(username, password);
|
|
38
|
+
|
|
39
|
+
const domainApi = new DomainApi(coreInfo, auth);
|
|
40
|
+
|
|
41
|
+
const userResults = await domainApi.listRecords(50, {
|
|
42
|
+
filter: JSON.stringify(userFilter(username)),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const record = {
|
|
46
|
+
domainId: 302,
|
|
47
|
+
'5001': userResults.records[0].id,
|
|
48
|
+
'9102': realm,
|
|
49
|
+
'9504': alias,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
await domainApi.create(record);
|
|
53
|
+
}
|
package/src/next/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { deployTemplate } from './deploy.js';
|
|
|
9
9
|
import { copy } from './copy.js';
|
|
10
10
|
import { vaultAddSiteData } from './vault.js';
|
|
11
11
|
import { addApacheSite } from './apache.js';
|
|
12
|
-
import { haproxySync }
|
|
12
|
+
import { haproxySync } from './haproxy.js';
|
|
13
13
|
import { connectClone } from './connect.js';
|
|
14
14
|
import dotenv from 'dotenv';
|
|
15
15
|
|
|
@@ -17,9 +17,8 @@ dotenv.config({ path: ['.env.pipeline.local', 'config/.env.pipeline.local'] });
|
|
|
17
17
|
|
|
18
18
|
const program = new Command();
|
|
19
19
|
|
|
20
|
-
// GIT SHA computation
|
|
21
20
|
import { $ } from 'zx';
|
|
22
|
-
import {deployConnect, deployCore} from './jenkins.js';
|
|
21
|
+
import { deployConnect, deployCore } from './jenkins.js';
|
|
23
22
|
import { runPipeline } from './pipline.js';
|
|
24
23
|
|
|
25
24
|
const GIT_SHA = (await $`git log --pretty=format:'%h' -n 1`).stdout.trim();
|
|
@@ -39,36 +38,37 @@ program
|
|
|
39
38
|
});
|
|
40
39
|
|
|
41
40
|
program
|
|
42
|
-
.command('addApacheSite')
|
|
43
|
-
.description('Add site to apache')
|
|
44
|
-
.action(async () => {
|
|
45
|
-
|
|
46
|
-
});
|
|
41
|
+
.command('addApacheSite')
|
|
42
|
+
.description('Add site to apache')
|
|
43
|
+
.action(async () => {
|
|
44
|
+
await addApacheSite();
|
|
45
|
+
});
|
|
47
46
|
|
|
48
47
|
program
|
|
49
|
-
.command('connectClone')
|
|
50
|
-
.description('Clone connect site')
|
|
51
|
-
.action(async () => {
|
|
52
|
-
|
|
53
|
-
});
|
|
54
|
-
|
|
48
|
+
.command('connectClone')
|
|
49
|
+
.description('Clone connect site')
|
|
50
|
+
.action(async () => {
|
|
51
|
+
await connectClone();
|
|
52
|
+
});
|
|
55
53
|
|
|
56
54
|
program
|
|
57
|
-
.command('haproxySync')
|
|
58
|
-
.description('Sync haproxy config')
|
|
59
|
-
.action(async () => {
|
|
60
|
-
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
.command('haproxySync')
|
|
56
|
+
.description('Sync haproxy config')
|
|
57
|
+
.action(async () => {
|
|
58
|
+
await haproxySync();
|
|
59
|
+
});
|
|
64
60
|
|
|
65
61
|
program
|
|
66
62
|
.command('vaultAddSiteData')
|
|
67
63
|
.description('Add m2m auth0 data to vault')
|
|
68
64
|
.requiredOption('-s, --site-name <siteName>', 'Site name')
|
|
65
|
+
.requiredOption(
|
|
66
|
+
'-p, --site-config-remote-path <siteConfigRemotePath>',
|
|
67
|
+
'Site Config Remote Path',
|
|
68
|
+
)
|
|
69
69
|
.action(async (options) => {
|
|
70
|
-
const { siteName,
|
|
71
|
-
await vaultAddSiteData(siteName);
|
|
70
|
+
const { siteName, siteConfigRemotePath } = options;
|
|
71
|
+
await vaultAddSiteData(siteName, siteConfigRemotePath);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
program
|
|
@@ -130,8 +130,7 @@ program
|
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
program
|
|
133
|
+
program
|
|
135
134
|
.command('copy')
|
|
136
135
|
.description('Copy a Docker image between registries')
|
|
137
136
|
.requiredOption('-a, --application <application>', 'Application name')
|
package/src/next/deploy.js
CHANGED
|
@@ -6,7 +6,6 @@ import lodash from 'lodash';
|
|
|
6
6
|
import { appInfo, environmentConfig, getRepUrl } from './config.js';
|
|
7
7
|
import { copy } from './copy.js';
|
|
8
8
|
import axios from 'axios';
|
|
9
|
-
import { runPipeline } from './pipline.js';
|
|
10
9
|
import { deployCore } from './jenkins.js';
|
|
11
10
|
|
|
12
11
|
/**
|
|
@@ -34,7 +33,7 @@ function cleanUpRepoPath(repoPath) {
|
|
|
34
33
|
console.log(`${repoPath} directory exists. Removing it...`);
|
|
35
34
|
fs.rmSync(repoPath, {
|
|
36
35
|
recursive: true,
|
|
37
|
-
force: true
|
|
36
|
+
force: true,
|
|
38
37
|
});
|
|
39
38
|
}
|
|
40
39
|
}
|
|
@@ -43,6 +42,7 @@ export async function cloneEnvRepository(env) {
|
|
|
43
42
|
const { repoPath, repoName } = getRepoLocalPath(env);
|
|
44
43
|
const repoUrl = getRepUrl(repoName);
|
|
45
44
|
console.log(`Cloning repository: ${repoName}`);
|
|
45
|
+
await cleanUpRepoPath(repoPath);
|
|
46
46
|
await $`git clone --depth 1 ${repoUrl}`;
|
|
47
47
|
console.log(`Repository cloned to: ${repoPath}`);
|
|
48
48
|
return repoPath;
|
|
@@ -52,7 +52,7 @@ export async function cloneRepository(repoSlug) {
|
|
|
52
52
|
const { repoPath, repoName } = getRepoLocalPathByRepoSlug(repoSlug);
|
|
53
53
|
const repoUrl = getRepUrl(repoName);
|
|
54
54
|
|
|
55
|
-
await cleanUpRepoPath(repoPath)
|
|
55
|
+
await cleanUpRepoPath(repoPath);
|
|
56
56
|
|
|
57
57
|
console.log(`Cloning repository: ${repoName}`);
|
|
58
58
|
await $`git clone --depth 1 ${repoUrl}`;
|
|
@@ -70,6 +70,24 @@ function getConfigPath(application, env, site, name) {
|
|
|
70
70
|
return { repoPath, deployConfigFilePath };
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
function getAppTypesFromRepo(env, site) {
|
|
74
|
+
const { repoPath } = getRepoLocalPath(env);
|
|
75
|
+
const deployConfigFilePath = path.join(repoPath, `dp/${site}`);
|
|
76
|
+
|
|
77
|
+
const applications = [];
|
|
78
|
+
if (fs.existsSync(deployConfigFilePath)) {
|
|
79
|
+
const files = fs.readdirSync(deployConfigFilePath);
|
|
80
|
+
files.forEach((file) => {
|
|
81
|
+
if (file.includes('.values.yaml')) {
|
|
82
|
+
const appName = file.split('.')[0];
|
|
83
|
+
applications.push(appName);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { applications };
|
|
89
|
+
}
|
|
90
|
+
|
|
73
91
|
export async function readDeployConfig(application, env, site, name) {
|
|
74
92
|
const { deployConfigFilePath } = getConfigPath(application, env, site, name);
|
|
75
93
|
|
|
@@ -265,6 +283,60 @@ async function getCoreBranchName(fromEnv, fromSite) {
|
|
|
265
283
|
return branchName;
|
|
266
284
|
}
|
|
267
285
|
|
|
286
|
+
export async function deployApp(
|
|
287
|
+
application,
|
|
288
|
+
appDetails,
|
|
289
|
+
fromEnv,
|
|
290
|
+
fromSite,
|
|
291
|
+
toEnv,
|
|
292
|
+
toSite,
|
|
293
|
+
) {
|
|
294
|
+
let changesMade = false;
|
|
295
|
+
console.log(`Processing application: ${application}`);
|
|
296
|
+
|
|
297
|
+
// Read the deployment configuration for the source environment and site
|
|
298
|
+
const { deployConfig: fromDeployConfig } = await readDeployConfig(
|
|
299
|
+
application,
|
|
300
|
+
fromEnv,
|
|
301
|
+
fromSite,
|
|
302
|
+
'values',
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
if (fromEnv !== toEnv) {
|
|
306
|
+
await copy(application, fromEnv, toEnv, fromDeployConfig.image.tag);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Read the deployment configuration for the target environment and site
|
|
310
|
+
const {
|
|
311
|
+
deployConfig: toDeployConfig,
|
|
312
|
+
deployConfigFilePath: toDeployConfigFilePath,
|
|
313
|
+
} = await readDeployConfig(application, toEnv, toSite, 'values');
|
|
314
|
+
|
|
315
|
+
// Update target configuration using attributes specified in appInfo
|
|
316
|
+
for (const attribute of appDetails.deployAttributes) {
|
|
317
|
+
const sourceValue = lodash.get(fromDeployConfig, attribute);
|
|
318
|
+
|
|
319
|
+
if (sourceValue !== undefined) {
|
|
320
|
+
// Update the target configuration with the source value
|
|
321
|
+
lodash.set(toDeployConfig, attribute, sourceValue);
|
|
322
|
+
changesMade = true;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const {
|
|
327
|
+
deployConfig: toDeployAppConfig,
|
|
328
|
+
deployConfigFilePath: toDeployAppConfigFilePath,
|
|
329
|
+
} = await readDeployConfig(application, toEnv, toSite, 'app');
|
|
330
|
+
|
|
331
|
+
toDeployAppConfig.revision = fromDeployConfig.image.tag;
|
|
332
|
+
|
|
333
|
+
// Write updated configuration to the target YAML file
|
|
334
|
+
writeDeployConfig(toDeployConfigFilePath, toDeployConfig);
|
|
335
|
+
|
|
336
|
+
writeDeployConfig(toDeployAppConfigFilePath, toDeployAppConfig);
|
|
337
|
+
return changesMade;
|
|
338
|
+
}
|
|
339
|
+
|
|
268
340
|
export async function deployFromSite(
|
|
269
341
|
fromEnv,
|
|
270
342
|
fromSite,
|
|
@@ -294,52 +366,47 @@ export async function deployFromSite(
|
|
|
294
366
|
|
|
295
367
|
// Initialize a flag to track changes
|
|
296
368
|
let changesMade = false;
|
|
297
|
-
|
|
369
|
+
const applications = await getAppTypesFromRepo(fromEnv, fromSite);
|
|
298
370
|
// Iterate over applications in appInfo
|
|
299
|
-
|
|
300
|
-
|
|
371
|
+
let appInfoLocal = {};
|
|
372
|
+
// let appInfoLocal = Object.fromEntries(
|
|
373
|
+
// applications
|
|
374
|
+
// .filter((application) => appInfo[application])
|
|
375
|
+
// .map((application) => [application, appInfo[application]]),
|
|
376
|
+
// );
|
|
377
|
+
|
|
378
|
+
for (const application of applications) {
|
|
379
|
+
if (Object.prototype.hasOwnProperty.call(appInfo, application)) {
|
|
380
|
+
appInfoLocal[application] = appInfo[application];
|
|
381
|
+
}
|
|
382
|
+
}
|
|
301
383
|
|
|
302
|
-
|
|
303
|
-
const { deployConfig:
|
|
304
|
-
application,
|
|
305
|
-
fromEnv,
|
|
384
|
+
if (Object.keys(appInfoLocal).length === 0) {
|
|
385
|
+
const { deployConfig: toDeployAppConfig } = await readDeployConfig(
|
|
306
386
|
fromSite,
|
|
307
|
-
|
|
387
|
+
toEnv,
|
|
388
|
+
toSite,
|
|
389
|
+
'app',
|
|
308
390
|
);
|
|
309
391
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const {
|
|
316
|
-
deployConfig: toDeployConfig,
|
|
317
|
-
deployConfigFilePath: toDeployConfigFilePath,
|
|
318
|
-
} = await readDeployConfig(application, toEnv, toSite, 'values');
|
|
319
|
-
|
|
320
|
-
// Update target configuration using attributes specified in appInfo
|
|
321
|
-
for (const attribute of appDetails.deployAttributes) {
|
|
322
|
-
const sourceValue = lodash.get(fromDeployConfig, attribute);
|
|
392
|
+
appInfoLocal[fromSite] = {
|
|
393
|
+
repositoryName: toDeployAppConfig.repo,
|
|
394
|
+
deployAttributes: ['image.tag'],
|
|
395
|
+
};
|
|
396
|
+
}
|
|
323
397
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
398
|
+
for (const [application, appDetails] of Object.entries(appInfo)) {
|
|
399
|
+
let cm = await deployApp(
|
|
400
|
+
application,
|
|
401
|
+
appDetails,
|
|
402
|
+
fromEnv,
|
|
403
|
+
fromSite,
|
|
404
|
+
toEnv,
|
|
405
|
+
toSite,
|
|
406
|
+
);
|
|
407
|
+
if (cm) {
|
|
408
|
+
changesMade = true;
|
|
329
409
|
}
|
|
330
|
-
|
|
331
|
-
const {
|
|
332
|
-
deployConfig: toDeployAppConfig,
|
|
333
|
-
deployConfigFilePath: toDeployAppConfigFilePath,
|
|
334
|
-
} = await readDeployConfig(application, toEnv, toSite, 'app');
|
|
335
|
-
|
|
336
|
-
toDeployAppConfig.revision = fromDeployConfig.image.tag;
|
|
337
|
-
|
|
338
|
-
// Write updated configuration to the target YAML file
|
|
339
|
-
writeDeployConfig(toDeployConfigFilePath, toDeployConfig);
|
|
340
|
-
|
|
341
|
-
writeDeployConfig(toDeployAppConfigFilePath, toDeployAppConfig);
|
|
342
|
-
|
|
343
410
|
}
|
|
344
411
|
|
|
345
412
|
// Push all changes if any updates were made
|
package/src/next/vault.js
CHANGED
|
@@ -1,39 +1,43 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
|
|
3
|
-
export async function vaultAddSiteData(SITE) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
export async function vaultAddSiteData(SITE, SITE_CONFIG_REMOTE_PATH) {
|
|
4
|
+
const AUTH0_CLIENT_ID = process.env.AUTH0_CLIENT_ID;
|
|
5
|
+
const AUTH0_CLIENT_SECRET = process.env.AUTH0_CLIENT_SECRET;
|
|
6
|
+
const VAULT_ADDR = process.env.VAULT_ADDR; // Vault host URL from environment variables
|
|
7
|
+
const VAULT_TOKEN = process.env.VAULT_TOKEN; // Vault token from environment variables
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
if (!VAULT_ADDR || !VAULT_TOKEN) {
|
|
10
|
+
throw new Error('Missing VAULT_ADDR or VAULT_TOKEN environment variable.');
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
const vaultApiUrl = `${VAULT_ADDR}/v1/secret/data/${SITE}/common/config`;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
const response = await axios.post(
|
|
17
|
-
vaultApiUrl,
|
|
18
|
-
{
|
|
19
|
-
data: {
|
|
20
|
-
AUTH0_CLIENT_ID: AUTH0_CLIENT_ID,
|
|
21
|
-
AUTH0_CLIENT_SECRET: AUTH0_CLIENT_SECRET,
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
headers: {
|
|
26
|
-
'X-Vault-Token': VAULT_TOKEN,
|
|
27
|
-
'Content-Type': 'application/json',
|
|
28
|
-
},
|
|
29
|
-
}
|
|
30
|
-
);
|
|
15
|
+
try {
|
|
31
16
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
console.error('Error adding data to Vault:', error.response?.data || error.message);
|
|
35
|
-
throw error;
|
|
17
|
+
if (process.env.VAULT_SKIP_VERIFY === 'true') {
|
|
18
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
36
19
|
}
|
|
37
|
-
}
|
|
38
20
|
|
|
21
|
+
const response = await axios.post(
|
|
22
|
+
vaultApiUrl,
|
|
23
|
+
{
|
|
24
|
+
data: {
|
|
25
|
+
AUTH0_CLIENT_ID: AUTH0_CLIENT_ID,
|
|
26
|
+
AUTH0_CLIENT_SECRET: AUTH0_CLIENT_SECRET,
|
|
27
|
+
SITE_CONFIG_REMOTE_PATH: SITE_CONFIG_REMOTE_PATH,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
headers: {
|
|
32
|
+
'X-Vault-Token': VAULT_TOKEN,
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
);
|
|
39
37
|
|
|
38
|
+
console.log('Data added to Vault successfully:', response.data);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('Error adding data to Vault:', error.response?.data || error.message);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
package/terraform/init.sh
CHANGED
|
@@ -33,4 +33,16 @@ echo "$atlassian_workspace_runner_ssh_key" | base64 -d > ~/.ssh/id_ed25519
|
|
|
33
33
|
chmod 600 ~/.ssh/id_ed25519
|
|
34
34
|
ssh-keyscan bitbucket.org > ~/.ssh/known_hosts
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if [[ "$PROJECT" == "s3" ]]; then
|
|
39
|
+
terraform init \
|
|
40
|
+
-input=false \
|
|
41
|
+
-reconfigure \
|
|
42
|
+
-backend=true \
|
|
43
|
+
-backend-config=./env/${CURRENT_ENVIRONMENT}/us-west-2/backend.tfvars \
|
|
44
|
+
-backend-config="key=us-west-2/${PROJECT}/${SITE_NAME}/${CURRENT_ENVIRONMENT}/terraform.tfstate"
|
|
45
|
+
|
|
46
|
+
else
|
|
47
|
+
terraform init -backend-config=./env/${CURRENT_ENVIRONMENT}/us-west-2/backend.tfvars --reconfigure
|
|
48
|
+
fi
|
package/terraform/plan.sh
CHANGED
|
@@ -39,6 +39,16 @@ if [[ "$PROJECT" == "bb-build-agent-shell" ]]; then
|
|
|
39
39
|
terraform plan -var-file="./env/${CURRENT_ENVIRONMENT}/us-west-2/terraform.tfvars" \
|
|
40
40
|
-var-file="./env/${CURRENT_ENVIRONMENT}/us-west-2/build_agent_configuration.tfvars.json" \
|
|
41
41
|
-out .tfplan
|
|
42
|
+
elif [[ "$PROJECT" == "s3" ]];then
|
|
43
|
+
terraform plan -var-file="./env/${CURRENT_ENVIRONMENT}/us-west-2/terraform.tfvars" \
|
|
44
|
+
-var="site=${SITE_NAME}" \
|
|
45
|
+
-var="env=${CURRENT_ENVIRONMENT}" \
|
|
46
|
+
-out .tfplan
|
|
42
47
|
else
|
|
43
48
|
terraform plan -var-file="./env/${CURRENT_ENVIRONMENT}/us-west-2/terraform.tfvars" -out .tfplan
|
|
44
49
|
fi
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
package/terraform/validate.sh
CHANGED