diff --git a/app/scripts/modules/core/application/config/trafficGuard/trafficGuardConfig.component.html b/app/scripts/modules/core/application/config/trafficGuard/trafficGuardConfig.component.html index fb76e17068c..8d7c07e7d4d 100644 --- a/app/scripts/modules/core/application/config/trafficGuard/trafficGuardConfig.component.html +++ b/app/scripts/modules/core/application/config/trafficGuard/trafficGuardConfig.component.html @@ -54,13 +54,7 @@

-
- - {{match.name}} - in {{match.regions.join(', ')}} -
-
(no matches)
- + diff --git a/app/scripts/modules/core/application/config/trafficGuard/trafficGuardConfig.component.ts b/app/scripts/modules/core/application/config/trafficGuard/trafficGuardConfig.component.ts index ca87e778de2..fd73ee4130d 100644 --- a/app/scripts/modules/core/application/config/trafficGuard/trafficGuardConfig.component.ts +++ b/app/scripts/modules/core/application/config/trafficGuard/trafficGuardConfig.component.ts @@ -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'; @@ -17,12 +18,6 @@ interface ITrafficGuard { detail: string; } -interface IClusterMatch { - name: string; - account: string; - regions: string[]; -} - export class TrafficGuardConfigController { public application: Application; @@ -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()); diff --git a/app/scripts/modules/core/chaosMonkey/chaosMonkeyConfig.component.html b/app/scripts/modules/core/chaosMonkey/chaosMonkeyConfig.component.html index fe8f690ed5b..82626c45b44 100644 --- a/app/scripts/modules/core/chaosMonkey/chaosMonkeyConfig.component.html +++ b/app/scripts/modules/core/chaosMonkey/chaosMonkeyConfig.component.html @@ -53,7 +53,7 @@
Termination frequency
- +
Documentation
diff --git a/app/scripts/modules/core/chaosMonkey/chaosMonkeyConfig.component.ts b/app/scripts/modules/core/chaosMonkey/chaosMonkeyConfig.component.ts index 0ae4554fdcf..22525f90b3e 100644 --- a/app/scripts/modules/core/chaosMonkey/chaosMonkeyConfig.component.ts +++ b/app/scripts/modules/core/chaosMonkey/chaosMonkeyConfig.component.ts @@ -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'; @@ -14,6 +15,7 @@ class GroupingOption { } export class ChaosMonkeyConfig { + [key: string]: any; public enabled = false; public meanTimeBetweenKillsInWorkDays = 2; public minTimeBetweenKillsInWorkDays = 1; @@ -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, diff --git a/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.html b/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.html index 175327aee57..8c99f1168f4 100644 --- a/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.html +++ b/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.html @@ -8,6 +8,7 @@
Exceptions
Region Stack Detail + Matched Clusters @@ -19,13 +20,13 @@
Exceptions
field="account" provider="'aws'" accounts="$ctrl.accounts" - on-change="$ctrl.configChanged()"> + on-change="$ctrl.updateConfig()"> (select account) + + + diff --git a/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.spec.ts b/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.spec.ts index d7f85d21c0a..8f4ff67858c 100644 --- a/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.spec.ts +++ b/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.spec.ts @@ -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 = $componentController( @@ -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', () => { @@ -41,11 +47,15 @@ describe('Controller: ChaosMonkeyExceptions', () => { }; spyOn(accountService, 'listAccounts').and.returnValue($q.when(accounts)); - spyOn(accountService, 'getAccountDetails').and.callFake((accountName: string): ng.IPromise => { + spyOn(accountService, 'getAccountDetails').and.callFake((accountName: string): IPromise => { 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(); diff --git a/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.ts b/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.ts index 55e4e046a18..6d6dd2a4b08 100644 --- a/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.ts +++ b/app/scripts/modules/core/chaosMonkey/chaosMonkeyExceptions.component.ts @@ -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 { @@ -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: '&', }; @@ -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()); diff --git a/app/scripts/modules/core/widgets/cluster/clusterMatches.component.ts b/app/scripts/modules/core/widgets/cluster/clusterMatches.component.ts new file mode 100644 index 00000000000..2cbc68b17e5 --- /dev/null +++ b/app/scripts/modules/core/widgets/cluster/clusterMatches.component.ts @@ -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 = ` +
+ + {{match.name}} + in {{match.regions.join(', ')}} +
+
(no matches)
+ `; +} + +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());