Skip to content

Commit

Permalink
feat: add ability to restart cluster after app installation
Browse files Browse the repository at this point in the history
  • Loading branch information
remie committed Dec 13, 2024
1 parent a6cc781 commit 914376b
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 31 deletions.
23 changes: 23 additions & 0 deletions src/apt/helpers/getAppLicense.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { input } from '@inquirer/prompts';
import { existsSync, readFileSync } from 'fs';

import { app3hour } from '../../helpers/licences'
import { provisioning } from '../messages';


export const getAppLicense = (license?: string, force?: boolean) => {

const appLicense = license && existsSync(license) ? readFileSync(license, 'utf8') : license;

if (force) {
return appLicense || app3hour;
}

console.log(provisioning.askForAppLicense);

return input({
message: `License`,
default: appLicense || app3hour,
required: true
});
}
2 changes: 1 addition & 1 deletion src/apt/helpers/getAptDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import simpleGit from 'simple-git';

import { emptyLine } from '../messages';

export const getAptDictory = async (cwd: string, mustUseDefaultConfiguration: boolean, force?: boolean) => {
export const getAptDictory = async (cwd?: string, mustUseDefaultConfiguration?: boolean, force?: boolean) => {
// Translate common relative paths to absolute paths
let result: string = cwd ?
cwd.startsWith('~/')
Expand Down
2 changes: 1 addition & 1 deletion src/apt/helpers/getHostLicense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const getHostLicense = (product: TSupportedApplications, license?: string
return productLicense || timebomb[product];
}

console.log(provisioning.askForLicense);
console.log(provisioning.askForHostLicense);

return input({
message: `License`,
Expand Down
112 changes: 91 additions & 21 deletions src/apt/helpers/installApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ import { createWriteStream, mkdirSync, rmSync } from 'fs';
import { homedir } from 'os';
import { join } from 'path';

import { app3hour } from '../../helpers/licences';
import { registerLicense, uploadToUPM, waitForPluginToBeEnabled } from '../../helpers/upm';
import { TInstallOptions } from '../../types/Install';
import { getAppLicense } from './getAppLicense';
import { getAptDictory } from './getAptDirectory';
import { getAWSCredentials } from './getAWSCredentials';
import { getEnvironmentName } from './getEnvironmentName';
import { getProduct } from './getProduct';
import { restartCluster } from './restartCluster';

const progressBar = new SingleBar({
format: ' [{bar}] {percentage}% | ETA: {eta_formatted}m',
Expand Down Expand Up @@ -50,18 +56,25 @@ const download = async (addonKey: string) => {
return tmpFile;
}

export const installApp = async (baseUrl: string, appKey?: string, license: string = app3hour, username: string = 'admin', password: string = 'admin', force?: boolean) => {
export const installApp = async (options: TInstallOptions) => {

// Set default value for username/password
const username = options.username || 'admin';
const password = options.password || 'admin';

// If we are in non-interactive mode, we will download it from MPAC
if (force) {
if (options.force) {

// In order to do so, we need an appkey. If this is not provided, throw a hissy fit
if (!appKey) {
if (!options.appKey) {
throw new Error('Failed to automatically install app into cluster, `appKey` was not provided');
}

// Get the app license
const appLicense = await getAppLicense(options.license, options.force);

// Download the file from MPAC
const file = await download(appKey);
const file = await download(options.appKey);

let count = 0;
let timerId = null;
Expand All @@ -72,30 +85,59 @@ export const installApp = async (baseUrl: string, appKey?: string, license: stri

if (count === 0) {
console.log(`
Installing the app (${appKey}) into the cluster using the Universal Plugin Manager REST API`);
Installing the app (${options.appKey}) into the cluster using the Universal Plugin Manager REST API`);
} else {
console.log(`
Retrying installation of the app (${appKey}) into the cluster using the Universal Plugin Manager REST API (attempt ${count + 1})`);
Retrying installation of the app (${options.appKey}) into the cluster using the Universal Plugin Manager REST API (attempt ${count + 1})`);
}

// Show a progress bar
progressBar.start(180, 0)
timerId = setInterval(() => progressBar.increment(), 1000);

// Upload it into the cluster using the UPM REST API
const isInstalled = await uploadToUPM(baseUrl, file, username, password, false);
const isInstalled = await uploadToUPM(options.baseUrl, file, username, password, false);
if (!isInstalled) {
throw new Error('Failed to install app into the cluster using the Universal Plugin Manager REST API');
}

// Check if we need to restart the application container
if (options.restartAfterInstall) {

// For restarts, the product option is required
if (!options.product) {
console.log(' Failed to restart application, required option `product` is missing');

// For restarts, the environment option is required
} else if (!options.environment) {
console.log(' Failed to restart application, required option `product` is missing');

// Ok, good to go!
} else {
// Oh right, we also need DCAPT in order to be able to restart the cluster
const cwd = await getAptDictory(options.cwd, false, options.force);

// We are now going to restart the cluster and hope for the best
console.log(` Restarting ${options.product} to ensure app installation`);
await restartCluster({
cwd,
product: options.product,
environment: options.environment,
aws_access_key_id: options.aws_access_key_id,
aws_secret_access_key: options.aws_secret_access_key,
force: options.force
});
}
}

// Wait for the plugin to be enabled
const isEnabled = await waitForPluginToBeEnabled(appKey, baseUrl, username, password, false);
const isEnabled = await waitForPluginToBeEnabled(options.appKey, options.baseUrl, username, password, false);
if (!isEnabled) {
throw new Error('The app could not be enabled on the cluster, please refer to the application log files for more information');
}

// Register the license (use the 3 hour timebomb in non-interactive mode)
const isLicensed = await registerLicense(appKey, license, baseUrl, username, password, false);
const isLicensed = await registerLicense(options.appKey, appLicense, options.baseUrl, username, password, false);
if (!isLicensed) {
throw new Error('The license could not be applied for the app on the cluster, please refer to the application log files for more information');
}
Expand Down Expand Up @@ -123,7 +165,7 @@ export const installApp = async (baseUrl: string, appKey?: string, license: stri
rmSync(file, { force: true });

// Tell them we succeeded
console.log(`✔ Finished installing the app (${appKey})`);
console.log(`✔ Finished installing the app (${options.appKey})`);

// If we're in interactive mode, let's be nice
} else {
Expand All @@ -150,16 +192,12 @@ export const installApp = async (baseUrl: string, appKey?: string, license: stri
// Ask them nicely for the app key
const addonKey = await input({
message: 'Please provide the key of the app to be installed',
default: appKey,
default: options.appKey,
required: true
});

// Ask them nicely for the app license to be used
const appLicense = await input({
message: `License`,
default: license,
required: true
});
const appLicense = await getAppLicense(options.license, options.force);

// Ask them nicely for the username
const adminUsername = await input({
Expand All @@ -174,6 +212,11 @@ export const installApp = async (baseUrl: string, appKey?: string, license: stri
validate: item => typeof item === 'string' && item.length > 0
});

// Ask them nicely if we need to restart the application
const restartAfterInstall = await confirm({
message: 'Restart the host application after installation?',
});

// Tell them we are starting
console.log(`
Installing the app (${addonKey}) into the cluster using the Universal Plugin Manager REST API`);
Expand All @@ -186,19 +229,46 @@ export const installApp = async (baseUrl: string, appKey?: string, license: stri
const file = await download(addonKey);

// Upload it into the cluster using the UPM REST API
const isInstalled = await uploadToUPM(baseUrl, file, adminUsername, adminPassword, false);
const isInstalled = await uploadToUPM(options.baseUrl, file, adminUsername, adminPassword, false);
if (!isInstalled) {
throw new Error('Failed to install app into the cluster using the Universal Plugin Manager REST API');
}

// Check if we need to restart the application container
if (restartAfterInstall) {

// Ask them nicely for the host product
const product = options.product || await getProduct();

// Ask for the AWS credentials
const [ aws_access_key_id, aws_secret_access_key ] = await getAWSCredentials(product, options.force);

// Ask for the environment name
const environment = options.environment || await getEnvironmentName();

// Oh right, we also need DCAPT in order to be able to restart the cluster
const cwd = await getAptDictory(options.cwd, false, options.force);

// We are now going to restart the cluster and hope for the best
console.log(` Restarting ${options.product} to ensure app installation`);
await restartCluster({
cwd,
product: product,
environment: environment,
aws_access_key_id: aws_access_key_id,
aws_secret_access_key: aws_secret_access_key,
force: options.force
});
}

// Wait for the plugin to be enabled
const isEnabled = await waitForPluginToBeEnabled(addonKey, baseUrl, adminUsername, adminPassword, false);
const isEnabled = await waitForPluginToBeEnabled(addonKey, options.baseUrl, adminUsername, adminPassword, false);
if (!isEnabled) {
throw new Error('The app could not be enabled on the cluster, please refer to the application log files for more information');
}

// Register the provided license
const isLicensed = await registerLicense(addonKey, appLicense, baseUrl, adminUsername, adminPassword, false);
const isLicensed = await registerLicense(addonKey, appLicense, options.baseUrl, adminUsername, adminPassword, false);
if (!isLicensed) {
throw new Error('The license could not be applied for the app on the cluster, please refer to the application log files for more information');
}
Expand All @@ -221,7 +291,7 @@ export const installApp = async (baseUrl: string, appKey?: string, license: stri
You can now install the app manually into the cluster.
Please go to the following page:
${baseUrl}/plugins/servlet/upm?source=side_nav_manage_addons
${options.baseUrl}/plugins/servlet/upm?source=side_nav_manage_addons
`);

// Make them grovel
Expand Down
10 changes: 9 additions & 1 deletion src/apt/helpers/runLuceneTimingTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,15 @@ export const runLuceneTimingTest = async (stage: TPerformanceTestTypes, options:
const baseUrl = options.baseUrl || await getClusterURL(cwd, options.product);

// Install the app into the cluster
await installApp(baseUrl, options.appKey, options.appLicense, 'admin', 'admin', options.force);
await installApp({
baseUrl,
appKey: options.appKey,
license: options.appLicense,
username: 'admin',
password: 'admin',
restartAfterInstall: options.restartAfterInstall,
force: options.force
});

// Inform the user that we will now start the Lucene index test
console.log(messages.startTest);
Expand Down
10 changes: 9 additions & 1 deletion src/apt/helpers/runPerformanceTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,15 @@ export const runPerformanceTest = async (stage: TPerformanceTestTypes, options:
if (stage === 'regression') {

// Install the app into the cluster
await installApp(baseUrl, options.appKey, options.appLicense, 'admin', 'admin', options.force);
await installApp({
baseUrl,
appKey: options.appKey,
license: options.appLicense,
username: 'admin',
password: 'admin',
restartAfterInstall: options.restartAfterInstall,
force: options.force
});

}

Expand Down
10 changes: 9 additions & 1 deletion src/apt/helpers/runScalabilityTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,15 @@ export const runScalabilityTest = async (stage: TScalabilityTestTypes, options:
const baseUrl = await getClusterURL(cwd, options.product);

// Install the app into the cluster
await installApp(baseUrl, options.appKey, options.appLicense, 'admin', 'admin', options.force);
await installApp({
baseUrl,
appKey: options.appKey,
license: options.appLicense,
username: 'admin',
password: 'admin',
restartAfterInstall: options.restartAfterInstall,
force: options.force
});

// Allow users to set a different test duration (for validation purposes)
const duration = await getDuration(options.force);
Expand Down
9 changes: 8 additions & 1 deletion src/apt/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,20 @@ export const provisioning = {
This will allow you to create multiple clusters (i.e. for different host products) and to identify which cluster to use during testing & teardown.
`,

askForLicense: `
askForHostLicense: `
The host product requires a valid license.
By default we use the 72h developer license provided by Atlassian. If you need a more extensive license,
you can generate an evaluation license from my.atlassian.com or ask Atlassian Marketplace Developer Support for a test license.
`,

askForAppLicense: `
The app requires a valid license.
By default we use the 3 hour timebomb license provided by Atlassian. If you need a more extensive license,
you can generate an evaluation license from my.atlassian.com or ask Atlassian Marketplace Developer Support for a test license.
`,

ready: `
We have now gathered all the information required to provision the cluster.
Expand Down
3 changes: 3 additions & 0 deletions src/apt/performance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const Performance = async (options: TAPTPerformanceTestArgs) => {
aws_secret_access_key,
license,
appKey: options.appKey,
restartAfterInstall: options.restartAfterInstall,
force: options.force
}, Run1);

Expand All @@ -64,6 +65,7 @@ export const Performance = async (options: TAPTPerformanceTestArgs) => {
aws_secret_access_key,
license,
appKey: options.appKey,
restartAfterInstall: options.restartAfterInstall,
force: options.force
}, LuceneTimingTest)

Expand All @@ -81,6 +83,7 @@ export const Performance = async (options: TAPTPerformanceTestArgs) => {
aws_secret_access_key,
license,
appKey: options.appKey,
restartAfterInstall: options.restartAfterInstall,
force: options.force
}, Run2);

Expand Down
3 changes: 3 additions & 0 deletions src/apt/scalability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const Scalability = async (options: TAPTScalabilityTestArgs) => {
license,
appKey: options.appKey,
appLicense: options.appLicense,
restartAfterInstall: options.restartAfterInstall,
force: options.force
}, ScalabilityTestMessages);

Expand All @@ -56,6 +57,7 @@ export const Scalability = async (options: TAPTScalabilityTestArgs) => {
license,
appKey: options.appKey,
appLicense: options.appLicense,
restartAfterInstall: options.restartAfterInstall,
force: options.force
}, ScalabilityTestMessages);

Expand All @@ -71,6 +73,7 @@ export const Scalability = async (options: TAPTScalabilityTestArgs) => {
license,
appKey: options.appKey,
appLicense: options.appLicense,
restartAfterInstall: options.restartAfterInstall,
force: options.force
}, ScalabilityTestMessages);

Expand Down
Loading

0 comments on commit 914376b

Please sign in to comment.