Skip to content

Commit

Permalink
Merge branch 'develop' into feature/megalinter
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-anz committed Feb 8, 2024
2 parents 95ffea7 + 024cb12 commit 621706c
Show file tree
Hide file tree
Showing 53 changed files with 4,075 additions and 813 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = {
'import/no-commonjs': 'warn',
'import/no-amd': 'error',
'import/no-absolute-path': 'error',
'prettier/prettier': ['error', { endOfLine: 'auto' }],
'prettier/prettier': ['warn', { endOfLine: 'auto' }],
'@typescript-eslint/no-explicit-any': 'warn',
},
settings: {
Expand Down
6 changes: 5 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ updates:
- package-ecosystem: 'npm' # See documentation for possible values
directory: '/' # Location of package manifests
schedule:
interval: 'weekly'
interval: "weekly"
ignore:
# For AWS SDK, ignore all patch updates for version updates only
- dependency-name: "aws-sdk"
update-types: ["version-update:semver-patch"]
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
revolver-config.yaml

# Build and auto-generated files
dist
revolver.zip
*.js
*.d.ts
Expand Down
199 changes: 177 additions & 22 deletions README.md

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions actions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export class SetTagAction extends RevolverActionWithTags {

constructor(who: RevolverPlugin, tag: string, value: string) {
super(who, 'setTag');
this.reason = `${tag}:${value}`
this.tags = [
{
Key: tag,
Expand Down Expand Up @@ -86,6 +87,7 @@ export class UnsetTagAction extends RevolverActionWithTags {

constructor(who: RevolverPlugin, tag: string) {
super(who, 'unsetTag');
this.reason = tag;
this.tags = [
{
Key: tag,
Expand Down Expand Up @@ -115,18 +117,20 @@ export class UnsetTagAction extends RevolverActionWithTags {
export class StopAction extends RevolverAction {
public changesState: boolean;

constructor(who: RevolverPlugin) {
constructor(who: RevolverPlugin, reason: string) {
super(who, 'stop');
this.changesState = true;
this.reason = reason;
}
}

export class StartAction extends RevolverAction {
public changesState: boolean;

constructor(who: RevolverPlugin) {
constructor(who: RevolverPlugin, reason: string) {
super(who, 'start');
this.changesState = true;
this.reason = reason;
}
}

Expand Down
110 changes: 110 additions & 0 deletions actions/audit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { DateTime } from 'luxon';
import { promises as fs } from 'node:fs';
import { logger } from "../lib/logger";

export interface ActionAuditEntry {
time: DateTime;
accountId: string;
plugin: string;
driver: string;
resourceType: string;
resourceId: string;
status: string;
action: string;
reason: string;
}

abstract class ActionAuditLog {
protected readonly entries: ActionAuditEntry[];
protected readonly accountConfig: any;

protected constructor(entries: ActionAuditEntry[], accountConfig: any) {
this.entries = entries;
this.accountConfig = accountConfig;
}

abstract process(): void;
}

export class ActionAuditLogCSV extends ActionAuditLog {
private readonly outputFile: string;
private readonly append: boolean;
protected logger;
constructor(entries: ActionAuditEntry[], accountConfig: any, outputFile: string, append: boolean) {
super(entries, accountConfig);
this.outputFile = outputFile;
this.append = append;
this.logger = logger;
}

process() {
let mode = 'w';
if (this.append) mode = 'a';
fs.open(this.outputFile, mode).then(async (f) => {
this.logger.info(`Writing audit log to ${this.outputFile}`);

if (!this.append) {
await f.write(
`time,` +
`accountId,` +
`accountName,` +
`plugin,` +
`driver,` +
`resourceType,` +
`resourceId,` +
`action,` +
`status,` +
`reason\n`
);
}
for (const e of this.entries) {
const reason = `"${e.reason.replaceAll('"', '""')}"`;
const line =
`${e.time},` +
`${e.accountId},` +
`${this.accountConfig.settings.name || ''},` +
`${e.plugin},` +
`${e.driver},` +
`${e.resourceType},` +
`${e.resourceId},` +
`${e.action},` +
`${e.status},` +
`${reason}\n`;
await f.write(line);
}
});
}
}

export class ActionAuditLogConsole extends ActionAuditLog {
constructor(entries: ActionAuditEntry[], accountConfig: any) {
super(entries, accountConfig);
}

process() {
const header =
`${'ACCOUNT_ID'.padEnd(16)} ` +
`${'ACCOUNT_NAME'.padEnd(16)} ` +
`${'PLUGIN'.padEnd(20)} ` +
`${'DRIVER'.padEnd(25)} ` +
`${'TYPE'.padEnd(5)} ` +
`${'ID'.padEnd(40)} ` +
`${'ACTION'.padEnd(10)} ` +
`${'STATUS'.padEnd(10)} ` +
`${'REASON'}`
const lines = this.entries.map((e) =>
`${e.accountId.padEnd(16)} ` +
`${(this.accountConfig.settings.name || '').padEnd(16)} ` +
`${e.plugin.padEnd(20)} ` +
`${e.driver.padEnd(25)} ` +
`${e.resourceType.padEnd(5)} ` +
`${e.resourceId.padEnd(40)} ` +
`${e.action.padEnd(10)} ` +
`${e.status.padEnd(10)} ` +
`${e.reason}`
);

logger.info('Audit log follows');
logger.info(`\n${[header].concat(lines).join('\n')}\n`);
}
}
96 changes: 59 additions & 37 deletions drivers/driverInterface.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Logger } from 'tslog';
import { logger, RevolverLogObject } from '../lib/logger';
import { ToolingInterface } from './instrumentedResource';
import { InstrumentedResource, ToolingInterface } from "./instrumentedResource";
import { RevolverAction } from '../actions/actions';
import { ActionAuditEntry } from "../actions/audit";
import { DateTime } from "luxon";

export abstract class DriverInterface {
protected accountConfig: any;
protected driverConfig: any;
protected accountId: string;
protected logger: Logger<RevolverLogObject>;

protected actionAuditLog: ActionAuditEntry[];

constructor(accountConfig: any, driverConfig: any) {
this.accountConfig = accountConfig.settings;
this.driverConfig = driverConfig;
Expand All @@ -18,6 +22,7 @@ export abstract class DriverInterface {
{ accountId: this.accountId, accountName: this.accountConfig.name, driverName: this.name },
);
this.logger.debug(`Initialising driver ${this.name} for account ${this.accountConfig.name}`);
this.actionAuditLog = [];
}

get name() {
Expand All @@ -29,19 +34,46 @@ export abstract class DriverInterface {
}

pretendAction(resources: ToolingInterface[], action: RevolverAction) {
this.logger.info(
'Pretending that %s resources %j will %s',
this.name,
resources.map((xr) => xr.resourceId),
action.present,
);
this.logger.info(`Pretending that ${this.name} resources ${resources.map((xr) => xr.resourceId)} will ${action.present}`);
}

initialise() {
this.logger.info(`Driver ${this.name} is initialising...`);
return Promise.resolve(this);
}

getAuditLog(): ActionAuditEntry[] {
return this.actionAuditLog;
}

private appendAuditLog(xa: RevolverAction, allWithAction: ToolingInterface[], status: string): void {
for (const ti of allWithAction) {
let plugin: string = xa.who.name;
let action: string = xa.what;
let reason: string = xa.reason;

// Find the specfic action for this resource as the plugin and reason may differ from xa
const ownAction = ti.actions.find((a) => xa.like(a) && a.done);
if (ownAction !== undefined) {
plugin = ownAction.who.name;
action = ownAction.what;
reason = ownAction.reason;
}

this.actionAuditLog.push({
accountId: ti.accountId || '',
time: DateTime.now(),
plugin: plugin,
driver: this.name,
resourceType: ti.awsResourceType || '',
resourceId: ti.resourceId,
action: action,
reason: reason,
status: status,
});
}
}

processActions(resources: ToolingInterface[]): Promise<any> {
const logger = this.logger;
logger.info(`Driver ${this.name} is processing actions...`);
Expand All @@ -60,17 +92,12 @@ export abstract class DriverInterface {
if (typeof (this as any)[`mask${matchingAction.what}`] === 'function') {
const reason = (this as any)[`mask${matchingAction.what}`](xxr, matchingAction);
if (reason !== undefined) {
logger.debug(
'Resource %s also has action %s, but it is masked because %s',
xxr.resourceId,
matchingAction.present,
reason,
);
logger.debug(`Resource ${xxr.resourceId} also has action ${matchingAction.present}, but it is masked because ${reason}`);
matchingAction.done = true;
return false;
}
}
logger.debug('Resource %s also has action %s', xxr.resourceId, matchingAction.present);
logger.debug(`Resource ${xxr.resourceId} also has action ${matchingAction.present}`);
matchingAction.done = true;
return true;
});
Expand All @@ -79,38 +106,33 @@ export abstract class DriverInterface {
return null;
}

logger.info(`${xa.who.name} will execute ${xa.present} on ${xr.resourceType} ${allWithAction.map((xxr) => xxr.resourceId)}`);

if (this.driverConfig.pretend !== false) {
logger.info(
'Pretending that %s will execute %s on %s %j',
xa.who.name,
xa.present,
xr.resourceType,
allWithAction.map((xxr) => xxr.resourceId),
);
this.appendAuditLog(xa, allWithAction, 'pretend');
return this.pretendAction(allWithAction, xa);
}

logger.info(
'%s will execute %s on %s %j',
xa.who.name,
xa.present,
xr.resourceType,
allWithAction.map((xxr) => xxr.resourceId),
);
return (this as any)[xa.what](allWithAction, xa).catch((err: Error) => {
logger.error(
'Error in driver %s processing action [%s] on resources %j, stack trace will follow:',
this.name,
xa.present,
allWithAction.map((xxr) => xxr.resourceId),
);
logger.error(err);
});
if ((this as any)[xa.what] === undefined) {
logger.error(`Driver ${this.name} doesn't implement action ${xa.what}`);
}

return (this as any)[xa.what](allWithAction, xa)
.then(() => {
this.appendAuditLog(xa, allWithAction, 'success');
})
.catch((err: Error) => {
this.appendAuditLog(xa, allWithAction, err.message);
logger.error(`Error in driver ${this.name} processing action [${xa.present}] on resources ${allWithAction.map((xxr) => xxr.resourceId)}, stack trace will follow:`);
logger.error(err);
});
});
return o.concat(a.filter((xa) => xa));
}, []),
);
}

abstract collect(): Promise<ToolingInterface[]>;

abstract resource(obj: InstrumentedResource): ToolingInterface;
}
15 changes: 7 additions & 8 deletions drivers/ebs.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CreateVolumeCommandOutput, EC2Client, Tag, paginateDescribeVolumes, paginateDescribeInstances } from '@aws-sdk/client-ec2';
import { DateTime } from 'luxon';
import { paginateAwsCall } from '../lib/common';
import { ToolingInterface } from './instrumentedResource';
import { InstrumentedResource, ToolingInterface } from "./instrumentedResource";
import { DriverInterface } from './driverInterface';
import { RevolverAction, RevolverActionWithTags } from '../actions/actions';
import { ec2Tagger } from './tags';
Expand Down Expand Up @@ -81,17 +81,13 @@ class EBSDriver extends DriverInterface {
}

noop(resources: InstrumentedEBS[], action: RevolverAction) {
this.logger.info(
'EBS volumes %j will noop because: %s',
resources.map((xr) => xr.resourceId),
action.reason,
);
this.logger.info(`EBS volumes ${resources.map((xr) => xr.resourceId)} will noop because: ${action.reason}`);
return Promise.resolve();
}

async collect() {
const logger = this.logger;
logger.debug('EBS module collecting account: %j', this.accountConfig.name);
logger.debug(`EBS module collecting account: ${this.accountConfig.name}`);

const ec2 = await getAwsClientForAccount(EC2Client, this.accountConfig);

Expand All @@ -100,7 +96,7 @@ class EBSDriver extends DriverInterface {
(xr) => xr.Instances,
);

logger.debug('Found %d ebs volumes', ebsVolumes.length);
logger.debug(`Found ${ebsVolumes.length} ebs volumes`);

for (const volume of ebsVolumes) {
if (volume.State === 'in-use') {
Expand All @@ -114,6 +110,9 @@ class EBSDriver extends DriverInterface {
new InstrumentedEBS(xe, `arn:aws:ec2:${this.accountConfig.region}:${this.accountId}:volume/${xe.VolumeId}`),
);
}
resource(obj: InstrumentedResource): ToolingInterface {
return new InstrumentedEBS(obj.resource, obj.resourceArn)
}
}

export default EBSDriver;
Loading

0 comments on commit 621706c

Please sign in to comment.