underpost 3.2.0 → 3.2.2
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/.github/workflows/publish.ci.yml +6 -0
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -1
- package/.github/workflows/release.cd.yml +10 -5
- package/CHANGELOG.md +73 -1
- package/CLI-HELP.md +5 -4
- package/Dockerfile +4 -2
- package/README.md +2 -2
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -2
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -2
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +18 -26
- package/package.json +2 -2
- package/src/cli/db.js +687 -620
- package/src/cli/deploy.js +47 -28
- package/src/cli/env.js +18 -0
- package/src/cli/fs.js +3 -1
- package/src/cli/index.js +3 -1
- package/src/cli/repository.js +143 -0
- package/src/cli/run.js +1 -1
- package/src/cli/secrets.js +73 -0
- package/src/client/components/core/DropDown.js +13 -5
- package/src/index.js +1 -1
- package/src/runtime/express/Dockerfile +4 -0
- package/src/runtime/lampp/Dockerfile +4 -0
- package/src/runtime/lampp/Lampp.js +23 -1
- package/src/runtime/wp/Dockerfile +4 -0
- package/src/runtime/wp/Wp.js +148 -6
- package/src/server/backup.js +57 -41
- package/src/server/cron.js +23 -18
- package/src/server/start.js +2 -7
package/src/runtime/wp/Wp.js
CHANGED
|
@@ -132,10 +132,11 @@ class WpService {
|
|
|
132
132
|
const subDir = pathRoute && pathRoute !== '/' ? pathRoute.replace(/^\/+/, '').replace(/\/+$/, '') : '';
|
|
133
133
|
const wpDir = subDir ? path.join(vhostDir, subDir) : vhostDir;
|
|
134
134
|
|
|
135
|
+
let freshInstall = false;
|
|
135
136
|
if (repository) {
|
|
136
|
-
WpService.provisionClone({ host, siteRoot: wpDir, repository, db, wp, subDir });
|
|
137
|
+
({ freshInstall } = WpService.provisionClone({ host, siteRoot: wpDir, repository, db, wp, subDir }));
|
|
137
138
|
} else {
|
|
138
|
-
WpService.provisionFresh({ host, siteRoot: wpDir, db, wp, subDir });
|
|
139
|
+
({ freshInstall } = WpService.provisionFresh({ host, siteRoot: wpDir, db, wp, subDir }));
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
// Ensure git is initialized and linked to the backup repository.
|
|
@@ -152,6 +153,9 @@ class WpService {
|
|
|
152
153
|
WpService.ensureSubdirHtaccess({ vhostDir, subDir });
|
|
153
154
|
}
|
|
154
155
|
|
|
156
|
+
// Write security rules into the WordPress root .htaccess
|
|
157
|
+
WpService.ensureSecurityHtaccess({ dir: wpDir });
|
|
158
|
+
|
|
155
159
|
// Make the site writable by the XAMPP Apache process (runs as daemon:daemon).
|
|
156
160
|
// This is required for plugins like Wordfence WAF and Sucuri that write config/upload files.
|
|
157
161
|
shellExec(`sudo chown -R daemon:daemon "${vhostDir}"`);
|
|
@@ -170,6 +174,13 @@ class WpService {
|
|
|
170
174
|
resetRouter,
|
|
171
175
|
});
|
|
172
176
|
|
|
177
|
+
// Immediately commit and push all generated files (wp-config.php, .htaccess,
|
|
178
|
+
// security rules, plugins, etc.) so that on rollout/restart the clone will
|
|
179
|
+
// have a complete working state and won't fall back to fresh install again.
|
|
180
|
+
if (repository && freshInstall) {
|
|
181
|
+
WpService.persistToRepo({ siteRoot: wpDir, repository, host });
|
|
182
|
+
}
|
|
183
|
+
|
|
173
184
|
return { disabled };
|
|
174
185
|
}
|
|
175
186
|
|
|
@@ -194,8 +205,7 @@ class WpService {
|
|
|
194
205
|
logger.info(`${host}: remote accessible = ${repoAccessible} (${repository})`);
|
|
195
206
|
if (!repoAccessible) {
|
|
196
207
|
logger.warn(`${host}: remote repository not accessible (${repository}) — running fresh install`);
|
|
197
|
-
WpService.provisionFresh({ host, siteRoot, db, wp, subDir });
|
|
198
|
-
return;
|
|
208
|
+
return WpService.provisionFresh({ host, siteRoot, db, wp, subDir });
|
|
199
209
|
}
|
|
200
210
|
|
|
201
211
|
// Step 1 — clone if the directory does not exist yet
|
|
@@ -219,8 +229,10 @@ class WpService {
|
|
|
219
229
|
if (!fs.existsSync(path.join(siteRoot, 'wp-config.php'))) {
|
|
220
230
|
logger.warn(`${host}: wp-config.php not found — wiping site root and running fresh install`);
|
|
221
231
|
shellExec(`sudo rm -rf "${siteRoot}"`);
|
|
222
|
-
WpService.provisionFresh({ host, siteRoot, db, wp, subDir });
|
|
232
|
+
return WpService.provisionFresh({ host, siteRoot, db, wp, subDir });
|
|
223
233
|
}
|
|
234
|
+
|
|
235
|
+
return { freshInstall: false };
|
|
224
236
|
}
|
|
225
237
|
|
|
226
238
|
/**
|
|
@@ -235,7 +247,7 @@ class WpService {
|
|
|
235
247
|
// Validator: wp-config.php presence means installation is complete/valid
|
|
236
248
|
if (fs.existsSync(path.join(siteRoot, 'wp-config.php'))) {
|
|
237
249
|
logger.info(`${host}: wp-config.php found at ${siteRoot}, skipping fresh install`);
|
|
238
|
-
return;
|
|
250
|
+
return { freshInstall: false };
|
|
239
251
|
}
|
|
240
252
|
|
|
241
253
|
logger.info(`${host}: fresh install → ${siteRoot}`);
|
|
@@ -267,6 +279,8 @@ class WpService {
|
|
|
267
279
|
} else {
|
|
268
280
|
logger.warn(`${host}: no db config provided — wp-config.php not written`);
|
|
269
281
|
}
|
|
282
|
+
|
|
283
|
+
return { freshInstall: true };
|
|
270
284
|
}
|
|
271
285
|
|
|
272
286
|
/**
|
|
@@ -378,6 +392,134 @@ ${marker} end`;
|
|
|
378
392
|
logger.info(`subdirectory .htaccess updated`, { vhostDir, subDir });
|
|
379
393
|
}
|
|
380
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Writes security rules into the WordPress site root `.htaccess`.
|
|
397
|
+
* Protects `.git` directories, sensitive config files, and SQL dumps
|
|
398
|
+
* from being served by Apache. Idempotent — uses marker comments to
|
|
399
|
+
* detect and replace existing blocks on re-runs.
|
|
400
|
+
* @param {{ dir: string }} opts
|
|
401
|
+
* @param {string} opts.dir - Absolute path to the WordPress root (where .htaccess lives).
|
|
402
|
+
*/
|
|
403
|
+
static ensureSecurityHtaccess({ dir }) {
|
|
404
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
405
|
+
const htaccessPath = path.join(dir, '.htaccess');
|
|
406
|
+
|
|
407
|
+
const marker = '# -- wp-security --';
|
|
408
|
+
const block = `${marker}
|
|
409
|
+
# Block access to .git directories and files
|
|
410
|
+
RedirectMatch 404 /\\.git
|
|
411
|
+
|
|
412
|
+
# Block access to sensitive dotfiles
|
|
413
|
+
<FilesMatch "^\\.(env|htpasswd|htaccess\\.bak|DS_Store)">
|
|
414
|
+
Require all denied
|
|
415
|
+
</FilesMatch>
|
|
416
|
+
|
|
417
|
+
# Block access to WordPress config backups and SQL dumps
|
|
418
|
+
<FilesMatch "(wp-config\\.php\\.bak|wp-config-sample\\.php|\\.sql|\\.sql\\.gz)$">
|
|
419
|
+
Require all denied
|
|
420
|
+
</FilesMatch>
|
|
421
|
+
|
|
422
|
+
# Block direct access to PHP files in uploads
|
|
423
|
+
<IfModule mod_rewrite.c>
|
|
424
|
+
RewriteEngine On
|
|
425
|
+
RewriteRule ^wp-content/uploads/.*\\.php$ - [F,L]
|
|
426
|
+
</IfModule>
|
|
427
|
+
|
|
428
|
+
# Block access to xmlrpc.php (common attack vector)
|
|
429
|
+
<Files "xmlrpc.php">
|
|
430
|
+
Require all denied
|
|
431
|
+
</Files>
|
|
432
|
+
|
|
433
|
+
# Block access to readme.html and license.txt (version disclosure)
|
|
434
|
+
<FilesMatch "^(readme\\.html|license\\.txt)$">
|
|
435
|
+
Require all denied
|
|
436
|
+
</FilesMatch>
|
|
437
|
+
${marker} end`;
|
|
438
|
+
|
|
439
|
+
let existing = '';
|
|
440
|
+
if (fs.existsSync(htaccessPath)) {
|
|
441
|
+
existing = fs.readFileSync(htaccessPath, 'utf8');
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const markerRegex = new RegExp(
|
|
445
|
+
`${marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[\\s\\S]*?${marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')} end`,
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
if (markerRegex.test(existing)) {
|
|
449
|
+
existing = existing.replace(markerRegex, block);
|
|
450
|
+
} else {
|
|
451
|
+
existing = existing ? `${existing}\n${block}\n` : `${block}\n`;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
fs.writeFileSync(htaccessPath, existing, 'utf8');
|
|
455
|
+
logger.info(`security .htaccess updated`, { dir });
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Ensures a WordPress-specific `.gitignore` exists in the site root so that
|
|
460
|
+
* large/transient files are excluded from the backup repository while
|
|
461
|
+
* wp-config.php and security .htaccess ARE tracked.
|
|
462
|
+
* Idempotent — only writes when the file is missing.
|
|
463
|
+
* @param {{ dir: string }} opts
|
|
464
|
+
*/
|
|
465
|
+
static ensureGitignore({ dir }) {
|
|
466
|
+
const gitignorePath = path.join(dir, '.gitignore');
|
|
467
|
+
if (fs.existsSync(gitignorePath)) return;
|
|
468
|
+
const content = `# WordPress .gitignore
|
|
469
|
+
# Cache and temp
|
|
470
|
+
wp-content/cache/
|
|
471
|
+
wp-content/upgrade/
|
|
472
|
+
wp-content/backup-db/
|
|
473
|
+
wp-content/backups/
|
|
474
|
+
wp-content/blogs.dir/
|
|
475
|
+
wp-content/advanced-cache.php
|
|
476
|
+
wp-content/wp-cache-config.php
|
|
477
|
+
wp-content/debug.log
|
|
478
|
+
|
|
479
|
+
# OS / editor
|
|
480
|
+
.DS_Store
|
|
481
|
+
Thumbs.db
|
|
482
|
+
*.swp
|
|
483
|
+
*.swo
|
|
484
|
+
*~
|
|
485
|
+
`;
|
|
486
|
+
fs.writeFileSync(gitignorePath, content, 'utf8');
|
|
487
|
+
logger.info(`.gitignore written`, { dir });
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Commits all files in the WordPress site root and pushes to the remote
|
|
492
|
+
* repository. This persists wp-config.php, .htaccess security rules,
|
|
493
|
+
* installed plugins, and theme files so that on pod rollout/restart a
|
|
494
|
+
* `git clone` yields a fully working site without needing a fresh install.
|
|
495
|
+
*
|
|
496
|
+
* Safe to call repeatedly — `git commit` is a no-op when the working tree
|
|
497
|
+
* is clean (`|| true` prevents non-zero exit).
|
|
498
|
+
*
|
|
499
|
+
* @param {object} opts
|
|
500
|
+
* @param {string} opts.siteRoot - Absolute path to the WordPress root.
|
|
501
|
+
* @param {string} opts.repository - Git remote URL.
|
|
502
|
+
* @param {string} opts.host - Virtual-host name (for logging/commit msg).
|
|
503
|
+
*/
|
|
504
|
+
static persistToRepo({ siteRoot, repository, host }) {
|
|
505
|
+
if (!fs.existsSync(path.join(siteRoot, '.git'))) {
|
|
506
|
+
logger.warn(`persistToRepo: .git missing at ${siteRoot} — skipping`);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
WpService.ensureGitignore({ dir: siteRoot });
|
|
511
|
+
|
|
512
|
+
const githubOrg = process.env.GITHUB_USERNAME || 'underpostnet';
|
|
513
|
+
const repoName = repository.split('/').pop().split('.')[0];
|
|
514
|
+
|
|
515
|
+
logger.info(`${host}: persisting site to repository`);
|
|
516
|
+
shellExec(
|
|
517
|
+
`cd "${siteRoot}" && git add -A && git commit -m "wp provision ${host} $(date -u +%Y-%m-%dT%H:%M:%SZ)" || true`,
|
|
518
|
+
);
|
|
519
|
+
shellExec(`cd "${siteRoot}" && underpost push . ${githubOrg}/${repoName} -f`);
|
|
520
|
+
logger.info(`${host}: initial commit pushed to ${githubOrg}/${repoName}`);
|
|
521
|
+
}
|
|
522
|
+
|
|
381
523
|
/**
|
|
382
524
|
* Drops and recreates a MariaDB database to ensure a clean state for fresh installs.
|
|
383
525
|
* @param {{ host: string, name: string, user: string, password: string }} db
|
package/src/server/backup.js
CHANGED
|
@@ -6,10 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
import fs from 'fs-extra';
|
|
8
8
|
import { loggerFactory } from './logger.js';
|
|
9
|
-
import { shellExec } from './process.js';
|
|
10
9
|
import Underpost from '../index.js';
|
|
11
|
-
import { loadCronDeployEnv
|
|
12
|
-
import { WpService } from '../runtime/wp/Wp.js';
|
|
10
|
+
import { loadCronDeployEnv } from './conf.js';
|
|
13
11
|
|
|
14
12
|
const logger = loggerFactory(import.meta);
|
|
15
13
|
|
|
@@ -22,59 +20,77 @@ class BackUp {
|
|
|
22
20
|
/**
|
|
23
21
|
* @method callback
|
|
24
22
|
* @description Initiates a backup operation for the specified deployment list.
|
|
25
|
-
*
|
|
23
|
+
* Orchestrates two backup phases per deployment:
|
|
24
|
+
* 1. Database export (MariaDB / MongoDB dump via `node bin db --export`).
|
|
25
|
+
* 2. Repository backup (git commit+push inside the deployment pod via `node bin db --repo-backup`).
|
|
26
|
+
*
|
|
27
|
+
* Commands are always forwarded to the host node via SSH because the CronJob
|
|
28
|
+
* container itself has no kubectl access. GITHUB_TOKEN and GITHUB_USERNAME
|
|
29
|
+
* are passed as ephemeral inline env vars so they never touch the host filesystem.
|
|
30
|
+
*
|
|
31
|
+
* @param {string} deployList - Comma-separated list of deployment IDs.
|
|
26
32
|
* @param {Object} options - The options for the backup operation.
|
|
27
33
|
* @param {boolean} options.git - Whether to backup data using Git.
|
|
28
34
|
* @param {boolean} [options.k3s] - Use k3s cluster context.
|
|
29
35
|
* @param {boolean} [options.kind] - Use kind cluster context.
|
|
30
36
|
* @param {boolean} [options.kubeadm] - Use kubeadm cluster context.
|
|
31
|
-
* @param {boolean} [options.ssh] - Execute backup commands via SSH on the remote node.
|
|
32
37
|
* @memberof UnderpostBakcUp
|
|
33
38
|
*/
|
|
34
39
|
static callback = async function (deployList, options = { git: false }) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const firstDeployId = deployList && deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
|
|
41
|
+
const { ephemeral } = Underpost.repo.privateEngineRepoFactory(firstDeployId || undefined);
|
|
42
|
+
try {
|
|
43
|
+
loadCronDeployEnv();
|
|
44
|
+
if ((!deployList || deployList === 'dd') && fs.existsSync(`./engine-private/deploy/dd.router`))
|
|
45
|
+
deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').trim();
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
logger.info('init backups callback', deployList);
|
|
48
|
+
await logger.setUpInfo();
|
|
41
49
|
|
|
42
|
-
|
|
50
|
+
const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : options.kubeadm ? ' --kubeadm' : '';
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
for (const _deployId of deployList.split(',')) {
|
|
53
|
+
const deployId = _deployId.trim();
|
|
54
|
+
if (!deployId) continue;
|
|
47
55
|
|
|
48
|
-
|
|
56
|
+
const dbCommand = `node bin db ${options.git ? '--git --force-clone ' : ''}--export --primary-pod${clusterFlag} ${deployId}`;
|
|
57
|
+
const repoCommand = `node bin db --repo-backup${clusterFlag} ${deployId}`;
|
|
49
58
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
59
|
+
// Pass GITHUB_TOKEN and GITHUB_USERNAME ephemerally through the SSH command
|
|
60
|
+
// so git operations can push backups without relying on host env files.
|
|
61
|
+
const envPrefix = [
|
|
62
|
+
process.env.GITHUB_TOKEN ? `GITHUB_TOKEN=${process.env.GITHUB_TOKEN}` : '',
|
|
63
|
+
process.env.GITHUB_USERNAME ? `GITHUB_USERNAME=${process.env.GITHUB_USERNAME}` : '',
|
|
64
|
+
]
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.join(' ');
|
|
67
|
+
const prefixCmd = (cmd) => (envPrefix ? `${envPrefix} ${cmd}` : cmd);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
logger.info('Executing database export via SSH for', deployId);
|
|
71
|
+
await Underpost.ssh.sshRemoteRunner(prefixCmd(dbCommand), {
|
|
72
|
+
remote: true,
|
|
73
|
+
useSudo: true,
|
|
74
|
+
cd: '/home/dd/engine',
|
|
75
|
+
});
|
|
76
|
+
} catch (err) {
|
|
77
|
+
logger.error(`Error during database export for ${deployId}:`, err);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Repository backup: Cron container → SSH to host → host finds pod → kubectl exec git backup
|
|
81
|
+
try {
|
|
82
|
+
logger.info('Executing repository backup via SSH for', deployId);
|
|
83
|
+
await Underpost.ssh.sshRemoteRunner(prefixCmd(repoCommand), {
|
|
84
|
+
remote: true,
|
|
85
|
+
useSudo: true,
|
|
86
|
+
cd: '/home/dd/engine',
|
|
87
|
+
});
|
|
88
|
+
} catch (err) {
|
|
89
|
+
logger.error(`Error during repository backup for ${deployId}:`, err);
|
|
76
90
|
}
|
|
77
91
|
}
|
|
92
|
+
} finally {
|
|
93
|
+
if (ephemeral) Underpost.repo.cleanupPrivateEngineRepo();
|
|
78
94
|
}
|
|
79
95
|
};
|
|
80
96
|
}
|
package/src/server/cron.js
CHANGED
|
@@ -33,7 +33,9 @@ const underpostContainerEnvPath = '/usr/lib/node_modules/underpost/.env';
|
|
|
33
33
|
* @param {string} [params.cmd] - Optional pre-script commands to run before cron execution
|
|
34
34
|
* @param {boolean} [params.suspend=false] - Whether the CronJob is suspended
|
|
35
35
|
* @param {boolean} [params.dryRun=false] - Pass --dry-run flag to the cron command inside the container
|
|
36
|
-
* @param {boolean} [params.
|
|
36
|
+
* @param {boolean} [params.k3s=false] - Pass --k3s flag to the cron command inside the container
|
|
37
|
+
* @param {boolean} [params.kind=false] - Pass --kind flag to the cron command inside the container
|
|
38
|
+
* @param {boolean} [params.kubeadm=false] - Pass --kubeadm flag to the cron command inside the container
|
|
37
39
|
* @returns {string} Kubernetes CronJob YAML manifest
|
|
38
40
|
* @memberof UnderpostCron
|
|
39
41
|
*/
|
|
@@ -49,7 +51,9 @@ const cronJobYamlFactory = ({
|
|
|
49
51
|
cmd,
|
|
50
52
|
suspend = false,
|
|
51
53
|
dryRun = false,
|
|
52
|
-
|
|
54
|
+
k3s = false,
|
|
55
|
+
kind = false,
|
|
56
|
+
kubeadm = false,
|
|
53
57
|
}) => {
|
|
54
58
|
const containerImage = image || `underpost/underpost-engine:${Underpost.version}`;
|
|
55
59
|
|
|
@@ -60,10 +64,12 @@ const cronJobYamlFactory = ({
|
|
|
60
64
|
.replace(/^-|-$/g, '')
|
|
61
65
|
.substring(0, 52);
|
|
62
66
|
|
|
63
|
-
const cmdPart = cmd ? `${cmd} && ` : '';
|
|
64
67
|
const cronBin = dev ? 'node bin' : 'underpost';
|
|
65
|
-
const flags = `${git ? '--git ' : ''}${dev ? '--dev ' : ''}${dryRun ? '--dry-run ' : ''}${
|
|
66
|
-
const
|
|
68
|
+
const flags = `${git ? '--git ' : ''}${dev ? '--dev ' : ''}${dryRun ? '--dry-run ' : ''}${k3s ? '--k3s ' : ''}${kind ? '--kind ' : ''}${kubeadm ? '--kubeadm ' : ''}`;
|
|
69
|
+
const commands = [`cd ${enginePath}`, `node bin run secret`];
|
|
70
|
+
if (cmd) commands.push(cmd);
|
|
71
|
+
commands.push(`${cronBin} cron ${flags}${deployList} ${jobList}`);
|
|
72
|
+
const fullCommand = commands.join(' &&\n ');
|
|
67
73
|
|
|
68
74
|
return `apiVersion: batch/v1
|
|
69
75
|
kind: CronJob
|
|
@@ -95,7 +101,7 @@ spec:
|
|
|
95
101
|
- /bin/sh
|
|
96
102
|
- -c
|
|
97
103
|
- >
|
|
98
|
-
${
|
|
104
|
+
${fullCommand}
|
|
99
105
|
volumeMounts:
|
|
100
106
|
- mountPath: ${enginePath}
|
|
101
107
|
name: ${cronVolumeName}
|
|
@@ -183,7 +189,6 @@ class UnderpostCron {
|
|
|
183
189
|
* @param {boolean} [options.kubeadm] - Use kubeadm cluster context (apply directly on host)
|
|
184
190
|
* @param {boolean} [options.dryRun] - Preview cron jobs without executing them
|
|
185
191
|
* @param {boolean} [options.createJobNow] - After applying, immediately create a Job from each CronJob (requires --apply)
|
|
186
|
-
* @param {boolean} [options.ssh] - Execute backup commands via SSH on the remote node
|
|
187
192
|
* @memberof UnderpostCron
|
|
188
193
|
*/
|
|
189
194
|
callback: async function (
|
|
@@ -227,7 +232,6 @@ class UnderpostCron {
|
|
|
227
232
|
* @param {boolean} [options.k3s] - k3s cluster context (apply directly on host)
|
|
228
233
|
* @param {boolean} [options.kind] - kind cluster context (apply via kind-worker container)
|
|
229
234
|
* @param {boolean} [options.kubeadm] - kubeadm cluster context (apply directly on host)
|
|
230
|
-
* @param {boolean} [options.ssh] - Execute backup commands via SSH on the remote node
|
|
231
235
|
* @memberof UnderpostCron
|
|
232
236
|
*/
|
|
233
237
|
setupDeployStart: async function (deployId, options = {}) {
|
|
@@ -270,20 +274,20 @@ class UnderpostCron {
|
|
|
270
274
|
}
|
|
271
275
|
|
|
272
276
|
// Generate and apply cron job manifests for this deploy-id
|
|
277
|
+
const hasExplicitCluster = options.k3s || options.kind || options.kubeadm;
|
|
273
278
|
await Underpost.cron.generateK8sCronJobs({
|
|
274
279
|
deployId,
|
|
275
280
|
namespace: options.namespace,
|
|
276
281
|
image: options.image,
|
|
277
282
|
apply: options.apply,
|
|
278
283
|
createJobNow: options.createJobNow,
|
|
279
|
-
git: true,
|
|
280
|
-
dev: true,
|
|
281
|
-
kubeadm: true,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
dryRun: false,
|
|
284
|
+
git: options.git !== undefined ? options.git : true,
|
|
285
|
+
dev: options.dev !== undefined ? options.dev : true,
|
|
286
|
+
kubeadm: hasExplicitCluster ? !!options.kubeadm : true,
|
|
287
|
+
cmd: options.cmd || `node bin env ${deployId} production`,
|
|
288
|
+
k3s: !!options.k3s,
|
|
289
|
+
kind: !!options.kind,
|
|
290
|
+
dryRun: !!options.dryRun,
|
|
287
291
|
});
|
|
288
292
|
},
|
|
289
293
|
|
|
@@ -305,7 +309,6 @@ class UnderpostCron {
|
|
|
305
309
|
* @param {boolean} [options.kubeadm=false] - kubeadm cluster context (apply directly on host)
|
|
306
310
|
* @param {boolean} [options.createJobNow=false] - After applying, create a Job from each CronJob immediately
|
|
307
311
|
* @param {boolean} [options.dryRun=false] - Pass --dry-run=client to kubectl commands
|
|
308
|
-
* @param {boolean} [options.ssh=false] - Execute backup commands via SSH on the remote node
|
|
309
312
|
* @memberof UnderpostCron
|
|
310
313
|
*/
|
|
311
314
|
generateK8sCronJobs: async function (options = {}) {
|
|
@@ -362,7 +365,9 @@ class UnderpostCron {
|
|
|
362
365
|
cmd: options.cmd,
|
|
363
366
|
suspend: false,
|
|
364
367
|
dryRun: !!options.dryRun,
|
|
365
|
-
|
|
368
|
+
k3s: !!options.k3s,
|
|
369
|
+
kind: !!options.kind,
|
|
370
|
+
kubeadm: !!options.kubeadm,
|
|
366
371
|
});
|
|
367
372
|
|
|
368
373
|
const yamlFilePath = `${outputDir}/${cronJobName}.yaml`;
|
package/src/server/start.js
CHANGED
|
@@ -164,10 +164,9 @@ class UnderpostStartUp {
|
|
|
164
164
|
shellExec(`mkdir -p ${buildBasePath}/engine`);
|
|
165
165
|
shellExec(`cd ${buildBasePath} && sudo cp -a ./${repoName}/. ./engine`);
|
|
166
166
|
shellExec(`cd ${buildBasePath} && sudo rm -rf ./${repoName}`);
|
|
167
|
-
shellExec(`cd ${buildBasePath}/engine && underpost clone ${process.env.GITHUB_USERNAME}/${repoName}-private`);
|
|
168
|
-
shellExec(`cd ${buildBasePath}/engine && sudo mv ./${repoName}-private ./engine-private`);
|
|
169
167
|
}
|
|
170
168
|
shellCd(`${buildBasePath}/engine`);
|
|
169
|
+
Underpost.repo.privateEngineRepoFactory(deployId);
|
|
171
170
|
shellExec(options?.underpostQuicklyInstall ? `underpost install` : `npm install`);
|
|
172
171
|
shellExec(`node bin env ${deployId} ${env}`);
|
|
173
172
|
if (fs.existsSync('./engine-private/itc-scripts')) {
|
|
@@ -198,12 +197,8 @@ class UnderpostStartUp {
|
|
|
198
197
|
shellExec(`node bin env ${deployId} ${env}`);
|
|
199
198
|
shellExec(`npm ${runCmd} ${deployId}`, { async: true });
|
|
200
199
|
await awaitDeployMonitor(true);
|
|
200
|
+
if (env === 'production' && isInsideContainer()) Underpost.secret.globalSecretClean();
|
|
201
201
|
Underpost.env.set('container-status', `${deployId}-${env}-running-deployment`);
|
|
202
|
-
if (env === 'production' && isInsideContainer()) {
|
|
203
|
-
Underpost.env.clean();
|
|
204
|
-
shellExec(`sudo rm -rf /home/dd/engine/engine-private`);
|
|
205
|
-
if (fs.existsSync('/etc/config/.env.production')) fs.removeSync('/etc/config/.env.production');
|
|
206
|
-
}
|
|
207
202
|
},
|
|
208
203
|
};
|
|
209
204
|
}
|