Convert everything over to TypeScript
This was mostly minor minor tweaking of imports and fiddling with a few types
This commit is contained in:
parent
7b539d05c2
commit
044ba25859
|
@ -8,6 +8,8 @@ lint:: compile
|
||||||
|
|
||||||
check:: compile
|
check:: compile
|
||||||
|
|
||||||
|
unit:: compile
|
||||||
|
|
||||||
compile:
|
compile:
|
||||||
tsc
|
tsc
|
||||||
|
|
||||||
|
@ -27,6 +29,6 @@ run: depends build
|
||||||
EVERGREEN_DISABLE_SNAPSHOT=true \
|
EVERGREEN_DISABLE_SNAPSHOT=true \
|
||||||
EVERGREEN_ENDPOINT=http://127.0.0.1:3030 \
|
EVERGREEN_ENDPOINT=http://127.0.0.1:3030 \
|
||||||
FLAVOR=docker-cloud \
|
FLAVOR=docker-cloud \
|
||||||
npm run client
|
npm run start
|
||||||
|
|
||||||
.PHONY: run build docs compile
|
.PHONY: run build docs compile
|
||||||
|
|
|
@ -173,6 +173,12 @@
|
||||||
"integrity": "sha512-ojnbBiKkZFYRfQpmtnnWTMw+rzGp/JiystjluW9jgN3VzRwilXddJ6aGQ9V/7iuDG06SBgn7ozW9k3zcAnYjYQ==",
|
"integrity": "sha512-ojnbBiKkZFYRfQpmtnnWTMw+rzGp/JiystjluW9jgN3VzRwilXddJ6aGQ9V/7iuDG06SBgn7ozW9k3zcAnYjYQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/tmp": {
|
||||||
|
"version": "0.0.33",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.0.33.tgz",
|
||||||
|
"integrity": "sha1-EHPEvIJHVK49EM+riKsCN7qWTk0=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@webassemblyjs/ast": {
|
"@webassemblyjs/ast": {
|
||||||
"version": "1.5.13",
|
"version": "1.5.13",
|
||||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz",
|
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz",
|
||||||
|
|
|
@ -4,10 +4,11 @@
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/client.js",
|
"main": "src/client.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"client": "node src/client.js",
|
"prestart": "tsc",
|
||||||
"test": "npm run eslint && npm run jest",
|
"start": "node build/client.js",
|
||||||
"eslint": "eslint src/. test/. --config .eslintrc.json",
|
"pretest": "eslint src/. test/. --config .eslintrc.json",
|
||||||
"jest": "jest",
|
"eslint": "npm run pretest",
|
||||||
|
"test": "jest",
|
||||||
"postinstall": "patch-package"
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"author": "R Tyler Croy",
|
"author": "R Tyler Croy",
|
||||||
|
@ -15,6 +16,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/feathersjs__feathers": "^3.0.5",
|
"@types/feathersjs__feathers": "^3.0.5",
|
||||||
"@types/jest": "^23.3.3",
|
"@types/jest": "^23.3.3",
|
||||||
|
"@types/node": "^10.11.4",
|
||||||
|
"@types/tmp": "0.0.33",
|
||||||
"asciidoctor.js": "^1.5.7-rc.1",
|
"asciidoctor.js": "^1.5.7-rc.1",
|
||||||
"css-loader": "^1.0.0",
|
"css-loader": "^1.0.0",
|
||||||
"eslint": "^4.19.1",
|
"eslint": "^4.19.1",
|
||||||
|
@ -60,11 +63,23 @@
|
||||||
"<rootDir>/ui/index.js",
|
"<rootDir>/ui/index.js",
|
||||||
"<rootDir>/src/lib/ui.js"
|
"<rootDir>/src/lib/ui.js"
|
||||||
],
|
],
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.tsx?$": "ts-jest"
|
||||||
|
},
|
||||||
|
"testRegex": "(/test/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||||
"coverageReporters": [
|
"coverageReporters": [
|
||||||
"json",
|
"json",
|
||||||
"lcov",
|
"lcov",
|
||||||
"text-summary"
|
"text-summary"
|
||||||
],
|
],
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"ts",
|
||||||
|
"tsx",
|
||||||
|
"js",
|
||||||
|
"jsx",
|
||||||
|
"json",
|
||||||
|
"node"
|
||||||
|
],
|
||||||
"coverageThreshold": {
|
"coverageThreshold": {
|
||||||
"global": {
|
"global": {
|
||||||
"statements": 54,
|
"statements": 54,
|
||||||
|
|
|
@ -2,28 +2,37 @@
|
||||||
* This is the main entrypoint for the evergreen-client
|
* This is the main entrypoint for the evergreen-client
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const process = require('process');
|
import feathers from '@feathersjs/feathers';
|
||||||
|
import * as logger from 'winston';
|
||||||
|
import socketio from '@feathersjs/socketio-client';
|
||||||
|
import auth from '@feathersjs/authentication-client';
|
||||||
|
import io from 'socket.io-client';
|
||||||
|
|
||||||
const feathers = require('@feathersjs/feathers');
|
import ErrorTelemetry from './lib/error-telemetry';
|
||||||
const logger = require('winston');
|
import HealthChecker from './lib/healthchecker';
|
||||||
const socketio = require('@feathersjs/socketio-client');
|
import Registration from './lib/registration';
|
||||||
const auth = require('@feathersjs/authentication-client');
|
import Status from './lib/status';
|
||||||
const io = require('socket.io-client');
|
import Storage from './lib/storage';
|
||||||
|
import UI from './lib/ui'
|
||||||
const createCron = require('./lib/periodic');
|
import Update from './lib/update';
|
||||||
const ErrorTelemetry = require('./lib/error-telemetry');
|
import Periodic from './lib/periodic';
|
||||||
const HealthChecker = require('./lib/healthchecker');
|
|
||||||
const Registration = require('./lib/registration');
|
|
||||||
const Status = require('./lib/status');
|
|
||||||
const Storage = require('./lib/storage');
|
|
||||||
const UI = require('./lib/ui');
|
|
||||||
const Update = require('./lib/update');
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Client class is a simple wrapper meant to start the basics of the client
|
* The Client class is a simple wrapper meant to start the basics of the client
|
||||||
* and then run a simple runloop to block the client from ever exiting
|
* and then run a simple runloop to block the client from ever exiting
|
||||||
*/
|
*/
|
||||||
class Client {
|
export default class Client {
|
||||||
|
protected readonly app : any;
|
||||||
|
protected readonly reg : Registration;
|
||||||
|
protected readonly healthChecker : HealthChecker;
|
||||||
|
protected readonly update : Update;
|
||||||
|
protected readonly status : Status;
|
||||||
|
protected readonly errorTelemetry : ErrorTelemetry;
|
||||||
|
|
||||||
|
protected socket : any;
|
||||||
|
|
||||||
|
public updating : boolean;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
if (!process.env.FLAVOR) {
|
if (!process.env.FLAVOR) {
|
||||||
logger.error('Fatal error encountered while trying to start, no flavor set, exiting the client');
|
logger.error('Fatal error encountered while trying to start, no flavor set, exiting the client');
|
||||||
|
@ -79,7 +88,7 @@ class Client {
|
||||||
* Only setting on the cron once we have registered and logged in,
|
* Only setting on the cron once we have registered and logged in,
|
||||||
* otherwise it's not really useful to have anything running periodically
|
* otherwise it's not really useful to have anything running periodically
|
||||||
*/
|
*/
|
||||||
const cron = createCron(app);
|
const cron = new Periodic();
|
||||||
|
|
||||||
this.runUpdates();
|
this.runUpdates();
|
||||||
|
|
||||||
|
@ -151,7 +160,7 @@ class Client {
|
||||||
this.runUpdates();
|
this.runUpdates();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.reg.register().then((res, newRegistration) => {
|
this.reg.register().then(({res, newRegistration}) => {
|
||||||
UI.publish('Registered this Evergreen instance', { log: 'debug', error: res} );
|
UI.publish('Registered this Evergreen instance', { log: 'debug', error: res} );
|
||||||
this.status.authenticate(this.reg.uuid, this.reg.token);
|
this.status.authenticate(this.reg.uuid, this.reg.token);
|
||||||
this.update.authenticate(this.reg.uuid, this.reg.token);
|
this.update.authenticate(this.reg.uuid, this.reg.token);
|
||||||
|
@ -168,8 +177,6 @@ class Client {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Client;
|
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
Storage.setBootingFlag();
|
Storage.setBootingFlag();
|
||||||
UI.serve();
|
UI.serve();
|
||||||
|
@ -180,6 +187,6 @@ if (require.main === module) {
|
||||||
logger.level = process.env.LOG_LEVEL || 'warn';
|
logger.level = process.env.LOG_LEVEL || 'warn';
|
||||||
/* Main entrypoint for module */
|
/* Main entrypoint for module */
|
||||||
UI.publish('Starting the evergreen-client..', { log: 'info' });
|
UI.publish('Starting the evergreen-client..', { log: 'info' });
|
||||||
let client = new Client();
|
const client = new Client();
|
||||||
client.bootstrap();
|
client.bootstrap();
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const crypto = require('crypto');
|
import crypto from 'crypto';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const logger = require('winston');
|
import * as logger from 'winston';
|
||||||
|
|
||||||
class Checksum {
|
export default class Checksum {
|
||||||
/*
|
/*
|
||||||
* Generate a SHA-256 checksum signature from the provided relative or
|
* Generate a SHA-256 checksum signature from the provided relative or
|
||||||
* absolute file path
|
* absolute file path
|
||||||
|
@ -26,5 +26,3 @@ class Checksum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Checksum;
|
|
|
@ -3,18 +3,19 @@
|
||||||
* disk and checking them against provided checksums.
|
* disk and checking them against provided checksums.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const url = require('url');
|
import url from 'url';
|
||||||
|
|
||||||
const rp = require('promise-request-retry');
|
import rp from 'promise-request-retry';
|
||||||
const logger = require('winston');
|
import * as logger from 'winston';
|
||||||
const mkdirp = require('mkdirp');
|
import mkdirp from 'mkdirp';
|
||||||
|
|
||||||
const Checksum = require('./checksum');
|
import UI from './ui';
|
||||||
const UI = require('./ui');
|
import Checksum from './checksum';
|
||||||
|
import { RequestOptions } from './request-options';
|
||||||
|
|
||||||
class Downloader {
|
export default class Downloader {
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +41,10 @@ class Downloader {
|
||||||
* @param {string} the filename to output at
|
* @param {string} the filename to output at
|
||||||
* @parma {string} Optional sha256 signature to verify of the file
|
* @parma {string} Optional sha256 signature to verify of the file
|
||||||
*/
|
*/
|
||||||
static download(item, dir, fileNameToWrite, sha256, downloadOptions = {}) {
|
static download(item, dir,
|
||||||
|
fileNameToWrite?: string,
|
||||||
|
sha256?: string,
|
||||||
|
downloadOptions: RequestOptions = {}) {
|
||||||
const itemUrl = url.parse(item);
|
const itemUrl = url.parse(item);
|
||||||
const itemUrlBaseName = path.basename(itemUrl.pathname);
|
const itemUrlBaseName = path.basename(itemUrl.pathname);
|
||||||
|
|
||||||
|
@ -113,5 +117,3 @@ class Downloader {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Downloader;
|
|
|
@ -5,13 +5,24 @@
|
||||||
* handle both error and "metrics" telemetry, but let's start with a smaller
|
* handle both error and "metrics" telemetry, but let's start with a smaller
|
||||||
* scope at least for now.
|
* scope at least for now.
|
||||||
*/
|
*/
|
||||||
const { Tail } = require('tail');
|
import { Tail } from 'tail';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const logger = require('winston');
|
import * as logger from 'winston'
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const Storage = require('./storage');
|
|
||||||
|
import Storage from './storage';
|
||||||
|
|
||||||
|
export interface ErrorTelemetryOptions {
|
||||||
|
flavor?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class ErrorTelemetry {
|
||||||
|
protected readonly app : any;
|
||||||
|
protected readonly options : ErrorTelemetryOptions;
|
||||||
|
|
||||||
|
public uuid : string;
|
||||||
|
public token : string;
|
||||||
|
|
||||||
class ErrorTelemetry {
|
|
||||||
constructor(app, options) {
|
constructor(app, options) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
@ -45,7 +56,7 @@ class ErrorTelemetry {
|
||||||
* monitoredFile: path to the log file to watch
|
* monitoredFile: path to the log file to watch
|
||||||
* outputFunction(app,line): the function that will be called on each new line detected
|
* outputFunction(app,line): the function that will be called on each new line detected
|
||||||
*/
|
*/
|
||||||
setup(monitoredFile) {
|
setup(monitoredFile?: string) {
|
||||||
logger.info('Setting up error logging...');
|
logger.info('Setting up error logging...');
|
||||||
let loggingFile = monitoredFile || this.fileToWatch();
|
let loggingFile = monitoredFile || this.fileToWatch();
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
const logger = require('winston');
|
|
||||||
const rp = require('promise-request-retry');
|
import * as logger from 'winston'
|
||||||
|
import rp from 'promise-request-retry';
|
||||||
|
|
||||||
|
import { RequestOptions } from './request-options';
|
||||||
|
|
||||||
const INSTANCE_IDENTITY_URL = '/instance-identity/';
|
const INSTANCE_IDENTITY_URL = '/instance-identity/';
|
||||||
const METRICS_URL = '/metrics/evergreen/healthcheck';
|
const METRICS_URL = '/metrics/evergreen/healthcheck';
|
||||||
|
|
||||||
class HealthChecker {
|
export default class HealthChecker {
|
||||||
|
protected readonly defaultRequestOptions : any;
|
||||||
|
|
||||||
constructor(jenkinsRootUrl, requestOptions = {}) {
|
public readonly jenkinsRootUrl : string;
|
||||||
|
public readonly retry : number;
|
||||||
|
public readonly delay : number;
|
||||||
|
public readonly factor : number;
|
||||||
|
|
||||||
|
constructor(jenkinsRootUrl, requestOptions: RequestOptions = {}) {
|
||||||
this.jenkinsRootUrl = jenkinsRootUrl;
|
this.jenkinsRootUrl = jenkinsRootUrl;
|
||||||
// let's target ~3 to 5 minutes overall of attempts for updates to arrive + Jenkins to start
|
// let's target ~3 to 5 minutes overall of attempts for updates to arrive + Jenkins to start
|
||||||
// TODO: later, introduce some smarter delay depending on the number of things to download?
|
// TODO: later, introduce some smarter delay depending on the number of things to download?
|
||||||
|
@ -111,5 +120,3 @@ class HealthChecker {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = HealthChecker;
|
|
|
@ -4,16 +4,17 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const logger = require('winston');
|
import * as logger from 'winston'
|
||||||
const cron = require('cron');
|
import cron from 'cron';
|
||||||
|
|
||||||
|
export default class Periodic {
|
||||||
|
protected readonly jobs : any;
|
||||||
|
protected readonly offset : number;
|
||||||
|
|
||||||
class Periodic {
|
|
||||||
/*
|
/*
|
||||||
* Requires the feathersjs app instance on initialization
|
* Requires the feathersjs app instance on initialization
|
||||||
*/
|
*/
|
||||||
constructor (app, options) {
|
constructor() {
|
||||||
this.app = app;
|
|
||||||
this.options = options || {};
|
|
||||||
this.jobs = {};
|
this.jobs = {};
|
||||||
this.offset = this.computeOffset();
|
this.offset = this.computeOffset();
|
||||||
logger.info('Periodic using minute offset of', this.offset);
|
logger.info('Periodic using minute offset of', this.offset);
|
||||||
|
@ -48,8 +49,3 @@ class Periodic {
|
||||||
return Math.floor(Math.random() * 59);
|
return Math.floor(Math.random() * 59);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(app, options) {
|
|
||||||
return new Periodic(app, options);
|
|
||||||
};
|
|
||||||
module.exports.Periodic = Periodic;
|
|
|
@ -6,8 +6,8 @@
|
||||||
* It seems to not believe that we're in nodejs when we really are
|
* It seems to not believe that we're in nodejs when we really are
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const crypto = require('crypto');
|
import crypto from 'crypto';
|
||||||
const rand = require('brorand');
|
import rand from 'brorand';
|
||||||
|
|
||||||
rand.Rand.prototype._rand = function _rand(n) {
|
rand.Rand.prototype._rand = function _rand(n) {
|
||||||
return crypto.randomBytes(n);
|
return crypto.randomBytes(n);
|
|
@ -3,20 +3,35 @@
|
||||||
* of the evergreen-client with the backend services layer
|
* of the evergreen-client with the backend services layer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const ecc = require('elliptic');
|
import ecc from 'elliptic';
|
||||||
const fs = require('fs');
|
import * as logger from 'winston';
|
||||||
const logger = require('winston');
|
import path from 'path';
|
||||||
const path = require('path');
|
import mkdirp from 'mkdirp'
|
||||||
const mkdirp = require('mkdirp');
|
import fs from 'fs';
|
||||||
|
|
||||||
const storage = require('./storage');
|
import Storage from './storage';
|
||||||
|
|
||||||
require('./rand-patch');
|
require('./rand-patch');
|
||||||
|
|
||||||
class Registration {
|
export interface FileOptions {
|
||||||
constructor (app, options) {
|
encoding?: string,
|
||||||
|
flag?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class Registration {
|
||||||
|
protected readonly app : any;
|
||||||
|
protected readonly options : Map<string, any>;
|
||||||
|
protected readonly curve : string;
|
||||||
|
|
||||||
|
protected privateKey : string;
|
||||||
|
protected publicKey : string;
|
||||||
|
protected fileOptions : FileOptions;
|
||||||
|
|
||||||
|
public uuid : string;
|
||||||
|
public token : string;
|
||||||
|
|
||||||
|
constructor(app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.options = options || {};
|
|
||||||
this.uuid = null;
|
this.uuid = null;
|
||||||
this.publicKey = null;
|
this.publicKey = null;
|
||||||
this.privateKey = null;
|
this.privateKey = null;
|
||||||
|
@ -46,15 +61,19 @@ class Registration {
|
||||||
* @return Promise
|
* @return Promise
|
||||||
*/
|
*/
|
||||||
async register() {
|
async register() {
|
||||||
let self = this;
|
const self = this;
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let api = self.app.service('registration');
|
const api = self.app.service('registration');
|
||||||
logger.info('Checking registration status..');
|
logger.info('Checking registration status..');
|
||||||
if (self.hasKeys() && self.hasUUID()) {
|
if (self.hasKeys() && self.hasUUID()) {
|
||||||
logger.info('We have keys and a UUID already');
|
logger.info('We have keys and a UUID already');
|
||||||
self.loadKeysSync();
|
self.loadKeysSync();
|
||||||
self.loadUUIDSync();
|
self.loadUUIDSync();
|
||||||
return self.login().then(res => resolve(res, false));
|
return self.login().then(res => resolve(
|
||||||
|
{
|
||||||
|
result: res,
|
||||||
|
created: false,
|
||||||
|
}));
|
||||||
} else {
|
} else {
|
||||||
if (!self.generateKeys()) {
|
if (!self.generateKeys()) {
|
||||||
return reject('Failed to generate keys');
|
return reject('Failed to generate keys');
|
||||||
|
@ -72,7 +91,10 @@ class Registration {
|
||||||
if (!self.saveUUIDSync()) {
|
if (!self.saveUUIDSync()) {
|
||||||
reject('Failed to save UUID!');
|
reject('Failed to save UUID!');
|
||||||
} else {
|
} else {
|
||||||
return self.login().then(res => resolve(res, true));
|
return self.login().then(res => resolve({
|
||||||
|
result: res,
|
||||||
|
created: true,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}).catch((res) => {
|
}).catch((res) => {
|
||||||
logger.error('Failed to register:', res);
|
logger.error('Failed to register:', res);
|
||||||
|
@ -108,8 +130,8 @@ class Registration {
|
||||||
* @return Boolean
|
* @return Boolean
|
||||||
*/
|
*/
|
||||||
generateKeys() {
|
generateKeys() {
|
||||||
let ec = new ecc.ec(this.curve);
|
const ec = new ecc.ec(this.curve);
|
||||||
let privkey = ec.genKeyPair();
|
const privkey = ec.genKeyPair();
|
||||||
this.publicKey = privkey.getPublic('hex');
|
this.publicKey = privkey.getPublic('hex');
|
||||||
this.privateKey = privkey.getPrivate('hex');
|
this.privateKey = privkey.getPrivate('hex');
|
||||||
|
|
||||||
|
@ -126,7 +148,7 @@ class Registration {
|
||||||
*
|
*
|
||||||
* Will return null if no public key has yet been generated
|
* Will return null if no public key has yet been generated
|
||||||
*
|
*
|
||||||
* @return String
|
* @return string
|
||||||
*/
|
*/
|
||||||
getPublicKey() {
|
getPublicKey() {
|
||||||
return this.publicKey;
|
return this.publicKey;
|
||||||
|
@ -154,7 +176,7 @@ class Registration {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadUUIDSync() {
|
loadUUIDSync() {
|
||||||
let config = JSON.parse(fs.readFileSync(this.uuidPath(), this.fileOptions));
|
const config = JSON.parse(fs.readFileSync(this.uuidPath(), this.fileOptions) as string);
|
||||||
this.uuid = config.uuid;
|
this.uuid = config.uuid;
|
||||||
return (!!this.uuid);
|
return (!!this.uuid);
|
||||||
}
|
}
|
||||||
|
@ -215,8 +237,8 @@ class Registration {
|
||||||
if (!this.hasKeys()) {
|
if (!this.hasKeys()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.publicKey = fs.readFileSync(this.publicKeyPath(), this.fileOptions);
|
this.publicKey = fs.readFileSync(this.publicKeyPath(), this.fileOptions) as string;
|
||||||
this.privateKey = fs.readFileSync(this.privateKeyPath(), this.fileOptions);
|
this.privateKey = fs.readFileSync(this.privateKeyPath(), this.fileOptions) as string;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,10 +262,10 @@ class Registration {
|
||||||
|
|
||||||
/* Return the directory where registration keys should be stored
|
/* Return the directory where registration keys should be stored
|
||||||
*
|
*
|
||||||
* @return String
|
* @return string
|
||||||
*/
|
*/
|
||||||
keyPath() {
|
keyPath() {
|
||||||
const keys = path.join(storage.homeDirectory(), 'keys');
|
const keys = path.join(Storage.homeDirectory(), 'keys');
|
||||||
|
|
||||||
/* Only bother making the directory if it doesn't already exist */
|
/* Only bother making the directory if it doesn't already exist */
|
||||||
try {
|
try {
|
||||||
|
@ -263,7 +285,7 @@ class Registration {
|
||||||
*
|
*
|
||||||
* This public key is safe to be shared with external services
|
* This public key is safe to be shared with external services
|
||||||
*
|
*
|
||||||
* @return String
|
* @return string
|
||||||
*/
|
*/
|
||||||
publicKeyPath() {
|
publicKeyPath() {
|
||||||
return path.join(this.keyPath(), 'evergreen.pub');
|
return path.join(this.keyPath(), 'evergreen.pub');
|
||||||
|
@ -277,5 +299,3 @@ class Registration {
|
||||||
return path.join(this.keyPath(), 'uuid.json');
|
return path.join(this.keyPath(), 'uuid.json');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Registration;
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
export interface RequestOptions {
|
||||||
|
retry?: number,
|
||||||
|
delay?: number,
|
||||||
|
factor?: number,
|
||||||
|
};
|
|
@ -4,9 +4,9 @@
|
||||||
* https://github.com/jenkinsci/jep/tree/master/jep/302
|
* https://github.com/jenkinsci/jep/tree/master/jep/302
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { spawnSync } = require('child_process');
|
import { spawnSync } from 'child_process';
|
||||||
const logger = require('winston');
|
import * as logger from 'winston';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
|
|
||||||
const LOG_PREFIX = '[snapshotting]';
|
const LOG_PREFIX = '[snapshotting]';
|
||||||
// https://github.com/jenkinsci/jep/tree/master/jep/302#user-content-files-to-store
|
// https://github.com/jenkinsci/jep/tree/master/jep/302#user-content-files-to-store
|
||||||
|
@ -16,10 +16,10 @@ const GITIGNORE_CONTENT = `
|
||||||
/secrets/master.key
|
/secrets/master.key
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class Snapshotter {
|
export default class Snapshotter {
|
||||||
|
protected workingDirectory : string;
|
||||||
|
|
||||||
constructor(app, options) {
|
constructor() {
|
||||||
this.options = options || {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
|
@ -1,20 +1,30 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const fs = require('fs');
|
import path from 'path';
|
||||||
const path = require('path');
|
import fs from 'fs';
|
||||||
|
|
||||||
const logger = require('winston');
|
import * as logger from 'winston';
|
||||||
const Storage = require('./storage');
|
import Storage from './storage';
|
||||||
const Checksum = require('./checksum');
|
import Checksum from './checksum';
|
||||||
|
|
||||||
|
|
||||||
|
export interface StatusOptions {
|
||||||
|
flavor?: string,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The status module is responsible for collecting and reporting the current
|
* The status module is responsible for collecting and reporting the current
|
||||||
* state of this instance to the Evergreen backend `Status` service
|
* state of this instance to the Evergreen backend `Status` service
|
||||||
*/
|
*/
|
||||||
class Status {
|
export default class Status {
|
||||||
constructor(app, options) {
|
protected readonly app : any;
|
||||||
this.options = options || {};
|
protected readonly options : StatusOptions;
|
||||||
|
|
||||||
|
public uuid : string;
|
||||||
|
public token : string;
|
||||||
|
|
||||||
|
constructor(app : any, options : StatusOptions = {}) {
|
||||||
|
this.options = options;
|
||||||
this.token = null;
|
this.token = null;
|
||||||
this.uuid = null;
|
this.uuid = null;
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
@ -38,8 +48,8 @@ class Status {
|
||||||
* Create a status record in the backend for this particular instance
|
* Create a status record in the backend for this particular instance
|
||||||
*/
|
*/
|
||||||
async create() {
|
async create() {
|
||||||
let api = this.app.service('status');
|
const api = this.app.service('status');
|
||||||
let record = {
|
const record = {
|
||||||
uuid: this.uuid,
|
uuid: this.uuid,
|
||||||
flavor: this.options.flavor,
|
flavor: this.options.flavor,
|
||||||
timezone: this.getTimezone(),
|
timezone: this.getTimezone(),
|
||||||
|
@ -68,8 +78,8 @@ class Status {
|
||||||
}
|
}
|
||||||
|
|
||||||
reportLevel(updateLevel) {
|
reportLevel(updateLevel) {
|
||||||
let api = this.app.service('status');
|
const api = this.app.service('status');
|
||||||
let record = {
|
const record = {
|
||||||
uuid: this.uuid,
|
uuid: this.uuid,
|
||||||
updateId: updateLevel,
|
updateId: updateLevel,
|
||||||
};
|
};
|
||||||
|
@ -152,10 +162,6 @@ class Status {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return versions;
|
return versions;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Status;
|
|
|
@ -1,15 +1,16 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const path = require('path');
|
import fs from 'fs';
|
||||||
const fs = require('fs');
|
import path from 'path';
|
||||||
const logger = require('winston');
|
|
||||||
const UI = require('./ui');
|
import * as logger from 'winston';
|
||||||
|
import UI from './ui';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Storage module simply contains common functions necessary for the
|
* The Storage module simply contains common functions necessary for the
|
||||||
* evergreen-client to store its own data.
|
* evergreen-client to store its own data.
|
||||||
*/
|
*/
|
||||||
class Storage {
|
export default class Storage {
|
||||||
/*
|
/*
|
||||||
* Returns the default home directory or the value of EVERGREEN_HOME
|
* Returns the default home directory or the value of EVERGREEN_HOME
|
||||||
*
|
*
|
||||||
|
@ -69,12 +70,12 @@ class Storage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static removePlugins(plugins) {
|
static removePlugins(plugins?: Array<any>) {
|
||||||
if (!plugins) {
|
if (!plugins) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let pluginPath = this.pluginsDirectory();
|
const pluginPath = this.pluginsDirectory();
|
||||||
let retArray = [];
|
const retArray = [];
|
||||||
plugins.forEach((plugin) => {
|
plugins.forEach((plugin) => {
|
||||||
retArray.push(fs.unlink(`${pluginPath}/${plugin}.hpi`, () => {
|
retArray.push(fs.unlink(`${pluginPath}/${plugin}.hpi`, () => {
|
||||||
logger.info(`${pluginPath}/${plugin}.hpi was deleted`);
|
logger.info(`${pluginPath}/${plugin}.hpi was deleted`);
|
||||||
|
@ -84,5 +85,3 @@ class Storage {
|
||||||
return retArray;
|
return retArray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Storage;
|
|
|
@ -3,15 +3,18 @@
|
||||||
* XML-RPC API: http://supervisord.org/api.html
|
* XML-RPC API: http://supervisord.org/api.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const xmlrpc = require('xmlrpc');
|
import xmlrpc from 'xmlrpc';
|
||||||
const client = xmlrpc.createClient('http://localhost:9001/RPC2');
|
import * as logger from 'winston'
|
||||||
const logger = require('winston');
|
|
||||||
|
const client = xmlrpc.createClient('http://localhost:9001/RPC2');
|
||||||
|
|
||||||
|
export default class Supervisord {
|
||||||
|
protected readonly client : xmlrpc.Client;
|
||||||
|
|
||||||
class Supervisord {
|
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
isRunning() {
|
static isRunning() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.methodCall('supervisor.getState', null, (e, v) => {
|
client.methodCall('supervisor.getState', null, (e, v) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
|
@ -22,7 +25,7 @@ class Supervisord {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
printState(name) {
|
static printState(name) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.methodCall('supervisor.getProcessInfo', [name], (e, value) => {
|
client.methodCall('supervisor.getProcessInfo', [name], (e, value) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
|
@ -33,7 +36,7 @@ class Supervisord {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isProcessRunning(name) {
|
static isProcessRunning(name) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.methodCall('supervisor.getProcessInfo', [name], (e, value) => {
|
client.methodCall('supervisor.getProcessInfo', [name], (e, value) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
|
@ -44,7 +47,7 @@ class Supervisord {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
startProcess(name) {
|
static startProcess(name) {
|
||||||
logger.info(`[supervisord] Starting ${name} process`);
|
logger.info(`[supervisord] Starting ${name} process`);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.methodCall('supervisor.startProcess', [name], (e, value) => {
|
client.methodCall('supervisor.startProcess', [name], (e, value) => {
|
||||||
|
@ -56,7 +59,7 @@ class Supervisord {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
stopProcess(name) {
|
static stopProcess(name) {
|
||||||
logger.info(`[supervisord] Stopping ${name} process`);
|
logger.info(`[supervisord] Stopping ${name} process`);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.methodCall('supervisor.stopProcess', [name], (e, value) => {
|
client.methodCall('supervisor.stopProcess', [name], (e, value) => {
|
||||||
|
@ -68,7 +71,7 @@ class Supervisord {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async restartProcess(name) {
|
static async restartProcess(name) {
|
||||||
logger.info(`[supervisord] Restarting ${name} process`);
|
logger.info(`[supervisord] Restarting ${name} process`);
|
||||||
if (await this.isProcessRunning(name)) {
|
if (await this.isProcessRunning(name)) {
|
||||||
await this.stopProcess(name);
|
await this.stopProcess(name);
|
||||||
|
@ -76,5 +79,3 @@ class Supervisord {
|
||||||
return this.startProcess(name);
|
return this.startProcess(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Supervisord();
|
|
|
@ -14,6 +14,9 @@ const logger = require('winston');
|
||||||
* Simple shim feathers service just to enable events
|
* Simple shim feathers service just to enable events
|
||||||
*/
|
*/
|
||||||
class MessageService {
|
class MessageService {
|
||||||
|
protected readonly app : any;
|
||||||
|
protected recent : Array<any>;
|
||||||
|
|
||||||
constructor(app) {
|
constructor(app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.recent = [];
|
this.recent = [];
|
||||||
|
@ -37,6 +40,9 @@ class MessageService {
|
||||||
}
|
}
|
||||||
|
|
||||||
class UI {
|
class UI {
|
||||||
|
protected readonly app : any;
|
||||||
|
protected server : any;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const app = express(feathers());
|
const app = express(feathers());
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
@ -60,7 +66,7 @@ class UI {
|
||||||
* necessarily be presented to the user.
|
* necessarily be presented to the user.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
publish(message, params) {
|
publish(message, params?: any) {
|
||||||
return this.app.service('messages').create({
|
return this.app.service('messages').create({
|
||||||
message: message,
|
message: message,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
|
@ -75,4 +81,4 @@ class UI {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new UI();
|
export default new UI();
|
|
@ -4,26 +4,38 @@
|
||||||
* https://github.com/jenkinsci/jep/tree/master/jep/307
|
* https://github.com/jenkinsci/jep/tree/master/jep/307
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const mkdirp = require('mkdirp');
|
import mkdirp from 'mkdirp';
|
||||||
const logger = require('winston');
|
import * as logger from 'winston';
|
||||||
|
|
||||||
const Downloader = require('./downloader');
|
import Downloader from './downloader';
|
||||||
const HealthChecker = require('./healthchecker');
|
import HealthChecker from './healthchecker';
|
||||||
const Storage = require('./storage');
|
import Storage from './storage';
|
||||||
const Supervisord = require('./supervisord');
|
import Supervisord from './supervisord';
|
||||||
const UI = require('./ui');
|
import UI from './ui';
|
||||||
const Snapshotter = require('./snapshotter');
|
import Snapshotter from './snapshotter';
|
||||||
|
|
||||||
class Update {
|
export interface FileOptions {
|
||||||
constructor(app, options) {
|
encoding?: string,
|
||||||
this.options = options || {};
|
flag?: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class Update {
|
||||||
|
protected readonly app : any;
|
||||||
|
protected readonly snapshotter : Snapshotter;
|
||||||
|
protected readonly fileOptions : FileOptions;
|
||||||
|
protected readonly options : any;
|
||||||
|
protected readonly healthChecker : HealthChecker;
|
||||||
|
|
||||||
|
public uuid : string;
|
||||||
|
public token : string;
|
||||||
|
public manifest : any;
|
||||||
|
public updateInProgress : Date;
|
||||||
|
|
||||||
|
constructor(app, options = {}) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.token = null;
|
this.options = options;
|
||||||
this.uuid = null;
|
|
||||||
this.manifest = null;
|
|
||||||
this.updateInProgress = null;
|
|
||||||
this.fileOptions = { encoding: 'utf8' };
|
this.fileOptions = { encoding: 'utf8' };
|
||||||
this.snapshotter = new Snapshotter();
|
this.snapshotter = new Snapshotter();
|
||||||
this.snapshotter.init(Storage.jenkinsHome());
|
this.snapshotter.init(Storage.jenkinsHome());
|
||||||
|
@ -84,7 +96,7 @@ class Update {
|
||||||
UI.publish('Starting to apply updates');
|
UI.publish('Starting to apply updates');
|
||||||
// Setting this to a timestamp to make a timeout in the future
|
// Setting this to a timestamp to make a timeout in the future
|
||||||
this.updateInProgress = new Date();
|
this.updateInProgress = new Date();
|
||||||
let tasks = [];
|
const tasks = [];
|
||||||
|
|
||||||
if ((updates.core) && (updates.core.url)) {
|
if ((updates.core) && (updates.core.url)) {
|
||||||
tasks.push(Downloader.download(updates.core.url,
|
tasks.push(Downloader.download(updates.core.url,
|
||||||
|
@ -150,7 +162,7 @@ class Update {
|
||||||
while it's not been yet marked as tainted in the backend?
|
while it's not been yet marked as tainted in the backend?
|
||||||
* how to report that borked case in a clear way
|
* how to report that borked case in a clear way
|
||||||
*/
|
*/
|
||||||
restartJenkins(rollingBack) { // Add param to stop recursion?
|
restartJenkins(rollingBack?: boolean) { // Add param to stop recursion?
|
||||||
Supervisord.restartProcess('jenkins');
|
Supervisord.restartProcess('jenkins');
|
||||||
|
|
||||||
const messageWhileRestarting = 'Jenkins should now be online, health checking!';
|
const messageWhileRestarting = 'Jenkins should now be online, health checking!';
|
||||||
|
@ -207,7 +219,7 @@ class Update {
|
||||||
getCurrentLevel() {
|
getCurrentLevel() {
|
||||||
this.loadUpdateSync();
|
this.loadUpdateSync();
|
||||||
if (this.manifest) {
|
if (this.manifest) {
|
||||||
let level = this.manifest.meta.level;
|
const level = this.manifest.meta.level;
|
||||||
logger.silly('Currently at Update Level %d', level);
|
logger.silly('Currently at Update Level %d', level);
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
@ -235,7 +247,7 @@ class Update {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.manifest = JSON.parse(fs.readFileSync(this.updatePath(),
|
this.manifest = JSON.parse(fs.readFileSync(this.updatePath(),
|
||||||
this.fileOptions));
|
this.fileOptions) as string);
|
||||||
return this.manifest;
|
return this.manifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,5 +272,3 @@ class Update {
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Update;
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const Checksum = require('../src/lib/checksum');
|
|
||||||
|
import Checksum from '../src/lib/checksum';
|
||||||
|
|
||||||
describe('Checksum', () => {
|
describe('Checksum', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
|
@ -1,13 +1,12 @@
|
||||||
const assert = require('assert');
|
|
||||||
const tmp = require('tmp');
|
|
||||||
const Client = require('../src/client');
|
|
||||||
const Storage = require('../src/lib/storage');
|
|
||||||
const mkdirp = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
|
|
||||||
describe('The base client module', () => {
|
import tmp from 'tmp';
|
||||||
|
import Client from '../src/client';
|
||||||
|
import Storage from '../src/lib/storage';
|
||||||
|
|
||||||
|
describe('The base client module', () => {
|
||||||
it('should interpret properly', () => {
|
it('should interpret properly', () => {
|
||||||
assert(Client);
|
expect(Client).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('flavorCheck', () => {
|
describe('flavorCheck', () => {
|
|
@ -2,8 +2,9 @@ jest.mock('fs');
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const mkdirp = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
const Downloader = require('../src/lib/downloader');
|
|
||||||
const Checksum = require('../src/lib/checksum');
|
import Downloader from '../src/lib/downloader';
|
||||||
|
import Checksum from '../src/lib/checksum';
|
||||||
|
|
||||||
describe('the Downloader class', () => {
|
describe('the Downloader class', () => {
|
||||||
describe('download()', () => {
|
describe('download()', () => {
|
||||||
|
@ -63,8 +64,9 @@ describe('the Downloader class', () => {
|
||||||
await Downloader.download(toDownload, dir, 'thefile', null, {delay: 20, retry: 4, factor: 10});
|
await Downloader.download(toDownload, dir, 'thefile', null, {delay: 20, retry: 4, factor: 10});
|
||||||
expect(false).toBeTruthy(); // fail(), should not reach this line.
|
expect(false).toBeTruthy(); // fail(), should not reach this line.
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
const endTime = new Date();
|
||||||
// 4 attempts, no delay for the first, then 20 ms, 20*10 (exponential factor), 20*10*10
|
// 4 attempts, no delay for the first, then 20 ms, 20*10 (exponential factor), 20*10*10
|
||||||
expect(new Date() - startTime).toBeGreaterThan(0 + 20 + 200 + 2000);
|
expect((endTime as any) - (startTime as any)).toBeGreaterThan(0 + 20 + 200 + 2000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -5,7 +5,7 @@ const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const mkdirp = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
|
|
||||||
const ErrorTelemetry = require('../src/lib/error-telemetry');
|
import ErrorTelemetry from '../src/lib/error-telemetry';
|
||||||
|
|
||||||
describe('Error Telemetry Logging', () => {
|
describe('Error Telemetry Logging', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -15,13 +15,13 @@ describe('Error Telemetry Logging', () => {
|
||||||
|
|
||||||
describe('authenticate()', () => {
|
describe('authenticate()', () => {
|
||||||
it('should store values', () => {
|
it('should store values', () => {
|
||||||
const telemetry = new ErrorTelemetry().authenticate('you-you-i-Dee', 'toe-ken-that-guy');
|
const telemetry = new ErrorTelemetry(null, null).authenticate('you-you-i-Dee', 'toe-ken-that-guy');
|
||||||
assert.equal(telemetry.uuid, 'you-you-i-Dee');
|
assert.equal(telemetry.uuid, 'you-you-i-Dee');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('setup() call', () => {
|
describe('setup() call', () => {
|
||||||
const errorTelemetryService = new ErrorTelemetry();
|
const errorTelemetryService = new ErrorTelemetry(null, null);
|
||||||
|
|
||||||
let logsDir = '/evergreen/jenkins/war/logs';
|
let logsDir = '/evergreen/jenkins/war/logs';
|
||||||
let logFile = path.join(logsDir, 'evergreen.log.0');
|
let logFile = path.join(logsDir, 'evergreen.log.0');
|
|
@ -1,7 +1,8 @@
|
||||||
const HealthChecker = require('../src/lib/healthchecker');
|
|
||||||
const logger = require('winston');
|
const logger = require('winston');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
|
||||||
|
import HealthChecker from '../src/lib/healthchecker';
|
||||||
|
|
||||||
describe('The healthchecker module', () => {
|
describe('The healthchecker module', () => {
|
||||||
let server = null;
|
let server = null;
|
||||||
let port = -1;
|
let port = -1;
|
||||||
|
@ -13,17 +14,19 @@ describe('The healthchecker module', () => {
|
||||||
instanceIdentity: true,
|
instanceIdentity: true,
|
||||||
metrics: {
|
metrics: {
|
||||||
plugins: true,
|
plugins: true,
|
||||||
deadlock: true
|
deadlock: true,
|
||||||
}
|
body: null,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('constructor', () => {
|
describe('constructor', () => {
|
||||||
it('should use default values if none passed in', () => {
|
it('should use default values if none passed in', () => {
|
||||||
const healthChecker = new HealthChecker();
|
const healthChecker = new HealthChecker('http://example.com');
|
||||||
expect(healthChecker.retry).toEqual(25);
|
expect(healthChecker.retry).toEqual(25);
|
||||||
expect(healthChecker.delay).toEqual(3000);
|
expect(healthChecker.delay).toEqual(3000);
|
||||||
expect(healthChecker.factor).toEqual(1.10);
|
expect(healthChecker.factor).toEqual(1.10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use passed in values', () => {
|
it('should use passed in values', () => {
|
||||||
const jenkinsRootUrl = `http://localhost:${port}`;
|
const jenkinsRootUrl = `http://localhost:${port}`;
|
||||||
const requestOptions = {delay: 100, retry: 1, factor: 1.01};
|
const requestOptions = {delay: 100, retry: 1, factor: 1.01};
|
||||||
|
@ -35,7 +38,7 @@ describe('The healthchecker module', () => {
|
||||||
});
|
});
|
||||||
it('should override the delay if env var set', () => {
|
it('should override the delay if env var set', () => {
|
||||||
const retryOverride = 21;
|
const retryOverride = 21;
|
||||||
process.env.PROCESS_RETRY_OVERRIDE = retryOverride;
|
process.env.PROCESS_RETRY_OVERRIDE = retryOverride.toString();
|
||||||
const jenkinsRootUrl = `http://localhost:${port}`;
|
const jenkinsRootUrl = `http://localhost:${port}`;
|
||||||
const requestOptions = {delay: 100, retry: 1, factor: 1.01};
|
const requestOptions = {delay: 100, retry: 1, factor: 1.01};
|
||||||
const healthChecker = new HealthChecker(jenkinsRootUrl, requestOptions);
|
const healthChecker = new HealthChecker(jenkinsRootUrl, requestOptions);
|
||||||
|
@ -45,7 +48,7 @@ describe('The healthchecker module', () => {
|
||||||
expect(healthChecker.factor).toEqual(requestOptions.factor);
|
expect(healthChecker.factor).toEqual(requestOptions.factor);
|
||||||
});
|
});
|
||||||
it('should handle non int value in env var', () => {
|
it('should handle non int value in env var', () => {
|
||||||
process.env.PROCESS_RETRY_OVERRIDE = true;
|
process.env.PROCESS_RETRY_OVERRIDE = 'true';
|
||||||
const jenkinsRootUrl = `http://localhost:${port}`;
|
const jenkinsRootUrl = `http://localhost:${port}`;
|
||||||
const requestOptions = {delay: 100, retry: 1, factor: 1.01};
|
const requestOptions = {delay: 100, retry: 1, factor: 1.01};
|
||||||
const healthChecker = new HealthChecker(jenkinsRootUrl, requestOptions);
|
const healthChecker = new HealthChecker(jenkinsRootUrl, requestOptions);
|
|
@ -1,33 +0,0 @@
|
||||||
const assert = require('assert');
|
|
||||||
const periodic = require('../src/lib/periodic');
|
|
||||||
|
|
||||||
describe('The periodic module', () => {
|
|
||||||
/* Just a simple fake app for unit test
|
|
||||||
*/
|
|
||||||
let app = new Object();
|
|
||||||
|
|
||||||
describe('runHourly()', () => {
|
|
||||||
it('allow registration of an hourly callback', () => {
|
|
||||||
let p = periodic(app);
|
|
||||||
assert.ok(p.runHourly('jest-fun', () => { }));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('runDaily()', () => {
|
|
||||||
it('allows registration of a daily callback', () => {
|
|
||||||
let p = periodic(app);
|
|
||||||
assert.ok(p.runDaily('jest-fun', () => { }));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('computeOffset()', () => {
|
|
||||||
let p = periodic(app);
|
|
||||||
|
|
||||||
it('should return a number between 0-59', () => {
|
|
||||||
let offset = p.computeOffset();
|
|
||||||
assert.equal(typeof offset, 'number');
|
|
||||||
assert.ok(offset <= 59);
|
|
||||||
assert.ok(offset >= 0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import Periodic from '../src/lib/periodic';
|
||||||
|
|
||||||
|
describe('The periodic module', () => {
|
||||||
|
/* Just a simple fake app for unit test
|
||||||
|
*/
|
||||||
|
describe('runHourly()', () => {
|
||||||
|
it('allow registration of an hourly callback', () => {
|
||||||
|
const p = new Periodic();
|
||||||
|
expect(p.runHourly('jest-fun', () => {})).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('runDaily()', () => {
|
||||||
|
it('allows registration of a daily callback', () => {
|
||||||
|
const p = new Periodic();
|
||||||
|
expect(p.runDaily('jest-fun', () => {})).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('computeOffset()', () => {
|
||||||
|
const p = new Periodic();
|
||||||
|
|
||||||
|
it('should return a number between 0-59', () => {
|
||||||
|
const offset = p.computeOffset();
|
||||||
|
expect(offset).toBeGreaterThanOrEqual(0);
|
||||||
|
expect(offset).toBeLessThanOrEqual(59);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -3,9 +3,11 @@ jest.mock('fs');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Registration = require('../src/lib/registration');
|
|
||||||
|
import Registration from '../src/lib/registration';
|
||||||
|
|
||||||
describe('The registration module', () => {
|
describe('The registration module', () => {
|
||||||
|
const app = new Object();
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
/* Make sure memfs is flushed every time */
|
/* Make sure memfs is flushed every time */
|
||||||
fs.volume.reset();
|
fs.volume.reset();
|
||||||
|
@ -13,19 +15,19 @@ describe('The registration module', () => {
|
||||||
|
|
||||||
describe('register()', () => {
|
describe('register()', () => {
|
||||||
it('should return a Promise', () => {
|
it('should return a Promise', () => {
|
||||||
const response = (new Registration()).register();
|
const response = (new Registration(app)).register();
|
||||||
assert(response instanceof Promise);
|
assert(response instanceof Promise);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getPublicKey()', () => {
|
describe('getPublicKey()', () => {
|
||||||
it('should return null on a new instance', () => {
|
it('should return null on a new instance', () => {
|
||||||
const r = new Registration();
|
const r = new Registration(app);
|
||||||
assert.equal(null, r.getPublicKey());
|
assert.equal(null, r.getPublicKey());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return a public key after generateKeys() has been called', () => {
|
it('should return a public key after generateKeys() has been called', () => {
|
||||||
const r = new Registration();
|
const r = new Registration(app);
|
||||||
assert(r.generateKeys());
|
assert(r.generateKeys());
|
||||||
assert.equal(typeof r.getPublicKey(), 'string');
|
assert.equal(typeof r.getPublicKey(), 'string');
|
||||||
});
|
});
|
||||||
|
@ -33,7 +35,7 @@ describe('The registration module', () => {
|
||||||
|
|
||||||
describe('saveUUIDSync()', () => {
|
describe('saveUUIDSync()', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
this.reg = new Registration();
|
this.reg = new Registration(app);
|
||||||
});
|
});
|
||||||
it('should not write anything by default', () => {
|
it('should not write anything by default', () => {
|
||||||
assert.equal(this.reg.saveUUIDSync(), false);
|
assert.equal(this.reg.saveUUIDSync(), false);
|
||||||
|
@ -62,7 +64,7 @@ describe('The registration module', () => {
|
||||||
describe('loadUUIDSync()', () => {
|
describe('loadUUIDSync()', () => {
|
||||||
let uuid = 'just another-fake-uuid';
|
let uuid = 'just another-fake-uuid';
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
this.reg = new Registration();
|
this.reg = new Registration(app);
|
||||||
this.reg.uuid = uuid;
|
this.reg.uuid = uuid;
|
||||||
this.reg.saveUUIDSync();
|
this.reg.saveUUIDSync();
|
||||||
});
|
});
|
||||||
|
@ -76,13 +78,13 @@ describe('The registration module', () => {
|
||||||
|
|
||||||
describe('saveKeysSync()', () => {
|
describe('saveKeysSync()', () => {
|
||||||
it('should return false if there are not keys', () => {
|
it('should return false if there are not keys', () => {
|
||||||
const r = new Registration();
|
const r = new Registration(app);
|
||||||
assert(!r.saveKeysSync());
|
assert(!r.saveKeysSync());
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when keys have been generated', () => {
|
describe('when keys have been generated', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
this.reg = new Registration();
|
this.reg = new Registration(app);
|
||||||
this.reg.generateKeys();
|
this.reg.generateKeys();
|
||||||
});
|
});
|
||||||
it('should return true if the public key has been written', () => {
|
it('should return true if the public key has been written', () => {
|
||||||
|
@ -107,7 +109,7 @@ describe('The registration module', () => {
|
||||||
|
|
||||||
describe('loadKeysSync()', () => {
|
describe('loadKeysSync()', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
this.reg = new Registration();
|
this.reg = new Registration(app);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return false by default when there are no keys', () => {
|
it('should return false by default when there are no keys', () => {
|
||||||
|
@ -121,7 +123,7 @@ describe('The registration module', () => {
|
||||||
|
|
||||||
describe('when keys are already on disk', () => {
|
describe('when keys are already on disk', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const preflight = new Registration();
|
const preflight = new Registration(app);
|
||||||
preflight.generateKeys();
|
preflight.generateKeys();
|
||||||
preflight.saveKeysSync();
|
preflight.saveKeysSync();
|
||||||
});
|
});
|
||||||
|
@ -135,31 +137,31 @@ describe('The registration module', () => {
|
||||||
|
|
||||||
describe('hasKeys()', () => {
|
describe('hasKeys()', () => {
|
||||||
it('should return false by default', () => {
|
it('should return false by default', () => {
|
||||||
assert.equal((new Registration()).hasKeys(), false);
|
assert.equal((new Registration(app)).hasKeys(), false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('generateKeys()', () => {
|
describe('generateKeys()', () => {
|
||||||
it('should return a boolean on success', () => {
|
it('should return a boolean on success', () => {
|
||||||
assert.ok((new Registration()).generateKeys());
|
assert.ok((new Registration(app)).generateKeys());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('publicKeyPath()', () => {
|
describe('publicKeyPath()', () => {
|
||||||
it('should return a path', () => {
|
it('should return a path', () => {
|
||||||
const p = (new Registration()).publicKeyPath();
|
const p = (new Registration(app)).publicKeyPath();
|
||||||
assert(p != path.basename(p), 'This doesn\'t look like a path');
|
assert(p != path.basename(p), 'This doesn\'t look like a path');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('keyPath()', () => {
|
describe('keyPath()', () => {
|
||||||
it('should return a path', () => {
|
it('should return a path', () => {
|
||||||
const keys = (new Registration()).keyPath();
|
const keys = (new Registration(app)).keyPath();
|
||||||
assert(keys != path.basename(keys), 'This doesn\'t look like a path');
|
assert(keys != path.basename(keys), 'This doesn\'t look like a path');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a directory if one does not exist', () => {
|
it('should create a directory if one does not exist', () => {
|
||||||
const keyPath = (new Registration()).keyPath();
|
const keyPath = (new Registration(app)).keyPath();
|
||||||
const stats = fs.statSync(keyPath);
|
const stats = fs.statSync(keyPath);
|
||||||
assert(stats.isDirectory());
|
assert(stats.isDirectory());
|
||||||
});
|
});
|
||||||
|
@ -176,7 +178,7 @@ describe('The registration module', () => {
|
||||||
|
|
||||||
describe('uuidPath()', () => {
|
describe('uuidPath()', () => {
|
||||||
it('should return a path', () => {
|
it('should return a path', () => {
|
||||||
const p = (new Registration()).uuidPath();
|
const p = (new Registration(app)).uuidPath();
|
||||||
assert(p != path.basename(p), 'This doesn\'t look like a path');
|
assert(p != path.basename(p), 'This doesn\'t look like a path');
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -1,10 +1,10 @@
|
||||||
const Snapshotter = require('../src/lib/snapshotter');
|
|
||||||
const tmp = require('tmp');
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
describe('The snapshotting module', () => {
|
import tmp from 'tmp';
|
||||||
|
import Snapshotter from '../src/lib/snapshotter';
|
||||||
|
|
||||||
let tmpDir = '';
|
describe('The snapshotting module', () => {
|
||||||
|
let tmpDir = null;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
tmpDir = tmp.dirSync({unsafeCleanup: true});
|
tmpDir = tmp.dirSync({unsafeCleanup: true});
|
||||||
});
|
});
|
|
@ -5,7 +5,8 @@ jest.mock('fs');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const mkdirp = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
const feathers = require('@feathersjs/feathers');
|
const feathers = require('@feathersjs/feathers');
|
||||||
const Status = require('../src/lib/status');
|
|
||||||
|
import Status from '../src/lib/status';
|
||||||
|
|
||||||
describe('The status module', () => {
|
describe('The status module', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -57,7 +58,7 @@ describe('The status module', () => {
|
||||||
|
|
||||||
describe('collectVersions()', () => {
|
describe('collectVersions()', () => {
|
||||||
it('should contain a node version', () => {
|
it('should contain a node version', () => {
|
||||||
const versions = (new Status(app)).collectVersions();
|
const versions : any = (new Status(app)).collectVersions();
|
||||||
expect(versions.container.tools.node).toBeTruthy();
|
expect(versions.container.tools.node).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -80,7 +81,7 @@ describe('The status module', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain the signature of the plugin', () => {
|
it('should contain the signature of the plugin', () => {
|
||||||
const versions = (new Status(app)).collectVersions();
|
const versions : any = (new Status(app)).collectVersions();
|
||||||
expect(versions.jenkins.plugins.git).toBeTruthy();
|
expect(versions.jenkins.plugins.git).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -3,8 +3,9 @@ jest.mock('fs');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const mkdirp = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const h = require('./helpers');
|
|
||||||
const Storage = require('../src/lib/storage');
|
import h from '../testlib/helpers';
|
||||||
|
import Storage from '../src/lib/storage';
|
||||||
|
|
||||||
describe('The storage module', () => {
|
describe('The storage module', () => {
|
||||||
let dir = '/tmp';
|
let dir = '/tmp';
|
|
@ -1,4 +1,5 @@
|
||||||
const UI = require('../src/lib/ui');
|
|
||||||
|
import UI from '../src/lib/ui';
|
||||||
|
|
||||||
describe('The UI module', () => {
|
describe('The UI module', () => {
|
||||||
it('should be a singleton', () => {
|
it('should be a singleton', () => {
|
|
@ -1,17 +1,19 @@
|
||||||
jest.mock('../src/lib/supervisord');
|
jest.mock('../src/lib/supervisord');
|
||||||
jest.mock('../src/lib/downloader');
|
jest.mock('../src/lib/downloader');
|
||||||
|
|
||||||
const tmp = require('tmp');
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const feathers = require('@feathersjs/feathers');
|
const feathers = require('@feathersjs/feathers');
|
||||||
const h = require('./helpers');
|
|
||||||
const Update = require('../src/lib/update');
|
|
||||||
const HealthChecker = require('../src/lib/healthchecker');
|
|
||||||
const Storage = require('../src/lib/storage');
|
|
||||||
const Supervisord = require('../src/lib/supervisord');
|
|
||||||
const Downloader = require('../src/lib/downloader');
|
|
||||||
const mkdirp = require('mkdirp');
|
const mkdirp = require('mkdirp');
|
||||||
|
|
||||||
|
import tmp from 'tmp';
|
||||||
|
import h from '../testlib/helpers';
|
||||||
|
|
||||||
|
import Update from '../src/lib/update';
|
||||||
|
import HealthChecker from '../src/lib/healthchecker';
|
||||||
|
import Storage from '../src/lib/storage';
|
||||||
|
import Supervisord from '../src/lib/supervisord';
|
||||||
|
import Downloader from '../src/lib/downloader';
|
||||||
|
|
||||||
describe('The update module', () => {
|
describe('The update module', () => {
|
||||||
let app = null;
|
let app = null;
|
||||||
let update = null;
|
let update = null;
|
||||||
|
@ -124,7 +126,7 @@ describe('The update module', () => {
|
||||||
|
|
||||||
it ('should execute updates if passed in with no deletes', async () => {
|
it ('should execute updates if passed in with no deletes', async () => {
|
||||||
jest.setTimeout(10000);
|
jest.setTimeout(10000);
|
||||||
Downloader.mockImplementationOnce(() => {
|
(Downloader as unknown as jest.Mock).mockImplementationOnce(() => {
|
||||||
return require.requireActual('../src/lib/downloader').default();
|
return require.requireActual('../src/lib/downloader').default();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,7 +148,7 @@ describe('The update module', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should execute both updates and deletes if both passed in', async () => {
|
it('should execute both updates and deletes if both passed in', async () => {
|
||||||
Downloader.mockImplementationOnce(() => {
|
(Downloader as unknown as jest.Mock).mockImplementationOnce(() => {
|
||||||
return require.requireActual('../src/lib/downloader').default();
|
return require.requireActual('../src/lib/downloader').default();
|
||||||
});
|
});
|
||||||
manifest.plugins.deletes = ['delete1'];
|
manifest.plugins.deletes = ['delete1'];
|
|
@ -8,7 +8,7 @@ const open = promisify(fs.open);
|
||||||
const close = promisify(fs.close);
|
const close = promisify(fs.close);
|
||||||
const access = promisify(fs.access);
|
const access = promisify(fs.access);
|
||||||
|
|
||||||
class Helpers {
|
export class Helpers {
|
||||||
constructor () {
|
constructor () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,4 +23,4 @@ class Helpers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = new Helpers();
|
export default new Helpers();
|
|
@ -5,7 +5,7 @@
|
||||||
"outDir": "./build",
|
"outDir": "./build",
|
||||||
"module" : "commonjs",
|
"module" : "commonjs",
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"lib" : ["es2017"],
|
"lib" : ["es2015"],
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
|
4
node.mk
4
node.mk
|
@ -12,8 +12,8 @@ fix-formatting: depends
|
||||||
check:: lint
|
check:: lint
|
||||||
$(MAKE) unit
|
$(MAKE) unit
|
||||||
|
|
||||||
unit: depends
|
unit:: depends
|
||||||
if [ -z "$${SKIP_TESTS}" ]; then $(NODE) npm run jest; \
|
if [ -z "$${SKIP_TESTS}" ]; then $(NODE) npm run test; \
|
||||||
else echo "Tests are skipped!"; fi;
|
else echo "Tests are skipped!"; fi;
|
||||||
|
|
||||||
debug-unit: depends
|
debug-unit: depends
|
||||||
|
|
Loading…
Reference in New Issue