Skip to content

Commit

Permalink
feat(core/monkey): add cluster match
Browse files Browse the repository at this point in the history
* add cluster match functionality/column to chaos monkey config section
in application config.
  • Loading branch information
icfantv committed Mar 25, 2017
1 parent d44e694 commit 60487be
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,7 @@ <h4 class="text-center"><span class="glyphicon glyphicon-asterisk glyphicon-spin
ng-change="$ctrl.configChanged()"/>
</td>
<td>
<div ng-repeat="match in $ctrl.clusterMatches[$index]">
<account-tag account="match.account"></account-tag>
{{match.name}}
<i ng-if="match.regions">in {{match.regions.join(', ')}}</i>
</div>
<div ng-if="$ctrl.clusterMatches[$index].length === 0">(no matches)</div>

<cluster-matches matches="$ctrl.clusterMatches" index="$index"></cluster-matches>
</td>
<td><a href ng-click="$ctrl.removeGuard($index)"><span class="glyphicon glyphicon-trash" uib-tooltip="Remove guard"></span></a></td>
</tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from 'core/account/account.service';
import {Application} from 'core/application/application.model';
import {IViewState} from '../footer/configSectionFooter.component';
import {CLUSTER_MATCHES_COMPONENT, IClusterMatch} from 'core/widgets/cluster/clusterMatches.component';
import {TRAFFIC_GUARD_CONFIG_HELP} from './trafficGuardConfig.help';
import {NAMING_SERVICE, NamingService} from 'core/naming/naming.service';

Expand All @@ -17,12 +18,6 @@ interface ITrafficGuard {
detail: string;
}

interface IClusterMatch {
name: string;
account: string;
regions: string[];
}

export class TrafficGuardConfigController {

public application: Application;
Expand Down Expand Up @@ -115,6 +110,7 @@ export const TRAFFIC_GUARD_CONFIG_COMPONENT = 'spinnaker.core.application.config
module(TRAFFIC_GUARD_CONFIG_COMPONENT, [
ACCOUNT_SERVICE,
NAMING_SERVICE,
CLUSTER_MATCHES_COMPONENT,
TRAFFIC_GUARD_CONFIG_HELP,
])
.component('trafficGuardConfig', new TrafficGuardConfigComponent());
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ <h5 class="first-header">Termination frequency</h5>
</div>
</div>

<chaos-monkey-exceptions config="$ctrl.config" config-changed="$ctrl.configChanged()"></chaos-monkey-exceptions>
<chaos-monkey-exceptions application="$ctrl.application" config="$ctrl.config" config-changed="$ctrl.configChanged()"></chaos-monkey-exceptions>
<h5 style="margin-top: 0">Documentation</h5>
<div class="row">
<div class="col-md-12">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as _ from 'lodash';
import {module, toJson} from 'angular';

import {CLUSTER_MATCHES_COMPONENT} from 'core/widgets/cluster/clusterMatches.component';
import {CHAOS_MONKEY_HELP} from './chaosMonkey.help';
import {CHAOS_MONKEY_EXCEPTIONS_COMPONENT} from './chaosMonkeyExceptions.component';
import {CONFIG_SECTION_FOOTER, IViewState} from '../application/config/footer/configSectionFooter.component';
Expand All @@ -14,6 +15,7 @@ class GroupingOption {
}

export class ChaosMonkeyConfig {
[key: string]: any;
public enabled = false;
public meanTimeBetweenKillsInWorkDays = 2;
public minTimeBetweenKillsInWorkDays = 1;
Expand Down Expand Up @@ -73,6 +75,7 @@ class ChaosMonkeyConfigComponent implements ng.IComponentOptions {
export const CHAOS_MONKEY_CONFIG_COMPONENT = 'spinnaker.core.chaosMonkey.config.component';
module(CHAOS_MONKEY_CONFIG_COMPONENT, [
require('../config/settings'),
CLUSTER_MATCHES_COMPONENT,
CHAOS_MONKEY_EXCEPTIONS_COMPONENT,
CHAOS_MONKEY_HELP,
CONFIG_SECTION_FOOTER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ <h5>Exceptions <help-field key="chaos.exceptions"></help-field></h5>
<th>Region</th>
<th>Stack</th>
<th>Detail</th>
<th>Matched Clusters</th>
<th></th>
</tr>
</thead>
Expand All @@ -19,13 +20,13 @@ <h5>Exceptions <help-field key="chaos.exceptions"></help-field></h5>
field="account"
provider="'aws'"
accounts="$ctrl.accounts"
on-change="$ctrl.configChanged()"></account-select-field>
on-change="$ctrl.updateConfig()"></account-select-field>
</td>
<td>
<span ng-if="!exception.account">(select account)</span>
<select class="form-control input-sm"
ng-model="exception.region"
ng-change="$ctrl.configChanged()"
ng-change="$ctrl.updateConfig()"
ng-if="exception.account"
>
<option ng-repeat="region in $ctrl.regionsByAccount[exception.account]" value="{{region}}">{{region}}</option>
Expand All @@ -34,15 +35,18 @@ <h5>Exceptions <help-field key="chaos.exceptions"></help-field></h5>
<td>
<input type="text" class="form-control input-sm"
ng-model="exception.stack"
ng-change="$ctrl.configChanged()"
ng-change="$ctrl.updateConfig()"
required/>
</td>
<td>
<input type="text" class="form-control input-sm"
ng-model="exception.detail"
ng-change="$ctrl.configChanged()"
ng-change="$ctrl.updateConfig()"
required/>
</td>
<td>
<cluster-matches matches="$ctrl.clusterMatches" index="$index"></cluster-matches>
</td>
<td><a href ng-click="$ctrl.removeException($index)"><span class="glyphicon glyphicon-trash" uib-tooltip="Remove exception"></span></a></td>
</tr>
</tbody>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import {mock} from 'angular';
import {mock, IComponentControllerService, IScope, IQService, IRootScopeService, IPromise} from 'angular';

import {CHAOS_MONKEY_EXCEPTIONS_COMPONENT, ChaosMonkeyExceptionsController} from './chaosMonkeyExceptions.component';
import {AccountService} from 'core/account/account.service';
import {APPLICATION_MODEL_BUILDER, ApplicationModelBuilder} from 'core/application/applicationModel.builder';
import {ChaosMonkeyConfig} from 'core/chaosMonkey/chaosMonkeyConfig.component';

describe('Controller: ChaosMonkeyExceptions', () => {

let $componentController: ng.IComponentControllerService,
accountService: any,
let $componentController: IComponentControllerService,
accountService: AccountService,
$ctrl: ChaosMonkeyExceptionsController,
$scope: ng.IScope,
$q: ng.IQService;
$scope: IScope,
$q: IQService,
applicationBuilder: ApplicationModelBuilder;

let initializeController = (data: any) => {
$ctrl = <ChaosMonkeyExceptionsController> $componentController(
Expand All @@ -18,17 +22,19 @@ describe('Controller: ChaosMonkeyExceptions', () => {
);
};

beforeEach(mock.module(CHAOS_MONKEY_EXCEPTIONS_COMPONENT));
beforeEach(mock.module(APPLICATION_MODEL_BUILDER, CHAOS_MONKEY_EXCEPTIONS_COMPONENT));

beforeEach(mock.inject((
_$componentController_: ng.IComponentControllerService,
_$q_: ng.IQService,
$rootScope: ng.IRootScopeService,
_accountService_: any) => {
_$componentController_: IComponentControllerService,
_$q_: IQService,
$rootScope: IRootScopeService,
_accountService_: AccountService,
_applicationModelBuilder_: ApplicationModelBuilder) => {
$scope = $rootScope.$new();
accountService = _accountService_;
$componentController = _$componentController_;
$q = _$q_;
applicationBuilder = _applicationModelBuilder_;
}));

describe('data initialization', () => {
Expand All @@ -41,11 +47,15 @@ describe('Controller: ChaosMonkeyExceptions', () => {
};

spyOn(accountService, 'listAccounts').and.returnValue($q.when(accounts));
spyOn(accountService, 'getAccountDetails').and.callFake((accountName: string): ng.IPromise<any> => {
spyOn(accountService, 'getAccountDetails').and.callFake((accountName: string): IPromise<any> => {
return $q.when(details[accountName]);
});

initializeController(null);
$ctrl.application =
applicationBuilder.createApplication({key: 'serverGroups', data: [], ready: () => $q.when(null), loaded: true});
$ctrl.config = new ChaosMonkeyConfig($ctrl.application.attributes.chaosMonkey || {});

$ctrl.$onInit();
$scope.$digest();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
import {uniq} from 'lodash';
import {module} from 'angular';

import {
ACCOUNT_SERVICE, AccountService, IAccountDetails, IRegion,
IAggregatedAccounts
} from 'core/account/account.service';
import {NAMING_SERVICE, NamingService} from 'core/naming/naming.service';
import {Application} from 'core/application/application.model';
import './chaosMonkeyExceptions.component.less';
import {ChaosMonkeyConfig} from './chaosMonkeyConfig.component';
import {IClusterMatch} from 'core/widgets/cluster/clusterMatches.component';

export class ChaosMonkeyExceptionsController {

static get $inject() { return ['accountService']; }

public application: Application;
public accounts: IAccountDetails[] = [];
public regionsByAccount: any;
public config: any;
public config: ChaosMonkeyConfig;
public configChanged: () => void;
public clusterMatches: IClusterMatch[][] = [];

static get $inject() {
return ['accountService', 'namingService'];
}

public constructor(private accountService: AccountService) {}
public constructor(private accountService: AccountService, private namingService: NamingService) {}

public addException(): void {
this.config.exceptions = this.config.exceptions || [];
this.config.exceptions.push({});
this.configChanged();
this.updateConfig();
};

public removeException(index: number): void {
this.config.exceptions.splice(index, 1);
this.configChanged();
this.updateConfig();
};

public $onInit(): void {
Expand All @@ -37,12 +46,41 @@ export class ChaosMonkeyExceptionsController {
this.accounts.forEach((details: IAccountDetails) => {
this.regionsByAccount[details.name] = ['*'].concat(details.regions.map((region: IRegion) => region.name));
});
this.application.getDataSource('serverGroups').ready().then(() => this.configureMatches());
});
}

public configureMatches(): void {
this.clusterMatches.length = 0;
this.config.exceptions.forEach((exception: any) => {
this.clusterMatches.push(
this.application.clusters.filter(c => {
const clusterNames = this.namingService.parseClusterName(c.name);
return (exception.account === '*' || exception.account === c.account) &&
(exception.region === '*' || c.serverGroups.map(s => s.region).includes(exception.region)) &&
(exception.stack === '*' || clusterNames.stack === (exception.stack || '')) &&
(exception.detail === '*' || clusterNames.freeFormDetails === (exception.detail || ''));
}).map(c => {
return {
name: c.name,
account: exception.account,
regions: exception.region === '*' ? uniq(c.serverGroups.map(g => g.region)).sort() : [exception.region]
};
})
);
});
this.clusterMatches.forEach(m => m.sort((a: IClusterMatch, b: IClusterMatch) => a.name.localeCompare(b.name)));
}

public updateConfig(): void {
this.configureMatches();
this.configChanged();
}
}

class ChaosMonkeyExceptionsComponent implements ng.IComponentOptions {
public bindings: any = {
application: '=',
config: '=',
configChanged: '&',
};
Expand All @@ -51,5 +89,5 @@ class ChaosMonkeyExceptionsComponent implements ng.IComponentOptions {
}

export const CHAOS_MONKEY_EXCEPTIONS_COMPONENT = 'spinnaker.core.chaosMonkey.exceptions.directive';
module(CHAOS_MONKEY_EXCEPTIONS_COMPONENT, [ACCOUNT_SERVICE])
module(CHAOS_MONKEY_EXCEPTIONS_COMPONENT, [ACCOUNT_SERVICE, NAMING_SERVICE])
.component('chaosMonkeyExceptions', new ChaosMonkeyExceptionsComponent());
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {module, IComponentOptions} from 'angular';

export interface IClusterMatch {
name: string;
account: string;
regions: string[];
}

class ClusterMatchesComponent implements IComponentOptions {

public bindings: any = {
matches: '=',
index: '='
};
public template = `
<div ng-repeat="match in $ctrl.matches[$ctrl.index]">
<account-tag account="match.account"></account-tag>
{{match.name}}
<i ng-if="match.regions">in {{match.regions.join(', ')}}</i>
</div>
<div ng-if="$ctrl.matches[$ctrl.index].length === 0">(no matches)</div>
`;
}

export const CLUSTER_MATCHES_COMPONENT = 'spinnaker.core.widget.cluster.clusterMatches.component';
module(CLUSTER_MATCHES_COMPONENT, [require('core/account/accountTag.directive.js')])
.component('clusterMatches', new ClusterMatchesComponent());

0 comments on commit 60487be

Please sign in to comment.