Skip to content

Commit

Permalink
add titus autoscaling support
Browse files Browse the repository at this point in the history
  • Loading branch information
anotherchrisberry authored and tomaslin committed Mar 16, 2018
1 parent 4ab4504 commit 25b6488
Show file tree
Hide file tree
Showing 20 changed files with 639 additions and 36 deletions.
15 changes: 15 additions & 0 deletions help/titus.help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@ const helpContents: {[key: string]: string} = {
<p>Will be automatically enabled when any non "custom" deployment strategy is selected.</p>`,
'titus.deploy.securityGroups': 'AWS Security Groups to assign to this service. Security groups are set only if <samp>Allocate IP?</samp> has been selected and are assigned to the Titus AWS Elastic Network Interface.',
'titus.job.securityGroups': 'AWS Security Groups to assign to this job',
'titus.autoscaling.cooldown': `
<p>The amount of time, in seconds, after a scaling activity completes where previous trigger-related scaling
activities can influence future scaling events.</p>
<p>For scale out policies, while the cooldown period is in effect, the capacity that has been added by the
previous scale out event that initiated the cooldown is calculated as part of the desired capacity for the next
scale out. The intention is to continuously (but not excessively) scale out. For example, an alarm triggers a
step scaling policy to scale out an Amazon ECS service by 2 tasks, the scaling activity completes successfully,
and a cooldown period of 5 minutes starts. During the Cooldown period, if the alarm triggers the same policy
again but at a more aggressive step adjustment to scale out the service by 3 tasks, the 2 tasks that were added
in the previous scale out event are considered part of that capacity and only 1 additional task is added to the desired count.</p>
<p>For scale in policies, the cooldown period is used to block subsequent scale in requests until it has expired.
The intention is to scale in conservatively to protect your application's availability. However, if another
alarm triggers a scale out policy during the cooldown period after a scale-in, Application Auto Scaling scales
out your scalable target immediately.</p>
`,

};

Expand Down
15 changes: 15 additions & 0 deletions reactShims/titus.react.injector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import IInjectorService = angular.auto.IInjectorService;

import { ReactInject } from '@spinnaker/core';

export class TitusReactInject extends ReactInject {

public get titusServerGroupTransformer() { return this.$injector.get('titusServerGroupTransformer') as any; }

public initialize($injector: IInjectorService) {
this.$injector = $injector;
}

}

export const TitusReactInjector: TitusReactInject = new TitusReactInject();
8 changes: 8 additions & 0 deletions reactShims/titus.react.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { module } from 'angular';
import { TitusReactInjector } from './titus.react.injector';

export const TITUS_REACT_MODULE = 'spinnaker.titus.react';
module(TITUS_REACT_MODULE, [
]).run(function ($injector: any) {
TitusReactInjector.initialize($injector);
});
100 changes: 100 additions & 0 deletions serverGroup/details/scalingPolicy/CreateScalingPolicyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import * as React from 'react';
import { BindAll } from 'lodash-decorators';

import { Application, IServerGroup, ReactInjector } from '@spinnaker/core';

// import { PolicyTypeSelectionModal } from './upsert/PolicyTypeSelectionModal';
// import { UpsertTargetTrackingController } from './targetTracking/upsertTargetTracking.controller';
import { TitusReactInjector } from 'titus/reactShims/titus.react.injector';

export interface ICreateScalingPolicyButtonProps {
application: Application;
serverGroup: IServerGroup;
}

export interface ICreateScalingPolicyButtonState {
showSelection: boolean;
showModal: boolean;
typeSelection: string;
awsAccount: string;
}

@BindAll()
export class CreateScalingPolicyButton extends React.Component<ICreateScalingPolicyButtonProps, ICreateScalingPolicyButtonState> {

constructor(props: ICreateScalingPolicyButtonProps) {
super(props);
this.state = {
showSelection: false,
showModal: false,
typeSelection: null,
awsAccount: null,
};
ReactInjector.accountService.getAccountDetails(props.serverGroup.account).then((details) => {
this.setState({awsAccount: details.awsAccount});
});
}

public handleClick(): void {
this.typeSelected('step');
// this.setState({showSelection: true});
}

public createStepPolicy(): void {
const { serverGroup, application } = this.props;

ReactInjector.modalService.open({
templateUrl: require('./upsert/upsertScalingPolicy.modal.html'),
controller: 'titusUpsertScalingPolicyCtrl',
controllerAs: 'ctrl',
size: 'lg',
resolve: {
policy: () => TitusReactInjector.titusServerGroupTransformer.constructNewStepScalingPolicyTemplate(serverGroup),
serverGroup: () => serverGroup,
alarmServerGroup: () => ({ type: 'aws', account: this.state.awsAccount, region: serverGroup.region, name: serverGroup.name}),
application: () => application,
}
}).result.catch(() => {});
}

// public createTargetTrackingPolicy(): void {
// const { serverGroup, application } = this.props;
//
// ReactInjector.modalService.open({
// templateUrl: require('./targetTracking/upsertTargetTracking.modal.html'),
// controller: UpsertTargetTrackingController,
// controllerAs: '$ctrl',
// size: 'lg',
// resolve: {
// policy: () => TitusReactInjector.titusServerGroupTransformer.constructNewTargetTrackingPolicyTemplate(),
// serverGroup: () => serverGroup,
// application: () => application,
// }
// }).result.catch(() => {});
// }

public typeSelected(typeSelection: string): void {
this.setState({typeSelection, showSelection: false, showModal: true});
if (typeSelection === 'step') {
this.createStepPolicy();
}
// if (typeSelection === 'targetTracking') {
// this.createTargetTrackingPolicy();
// }
}

// public showModalCallback(): void {
// this.setState({showSelection: false, showModal: false, typeSelection: null});
// }

public render() {
return (
<div>
{this.state.awsAccount ? <a className="clickable" onClick={this.handleClick}>Create new scaling policy</a> : null}
{/*{ this.state.showSelection && (*/}
{/*<PolicyTypeSelectionModal typeSelectedCallback={this.typeSelected} showCallback={this.showModalCallback}/>*/}
{/*)}*/}
</div>
);
}
}
28 changes: 28 additions & 0 deletions serverGroup/details/scalingPolicy/alarmBasedSummary.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<span class="label label-default">{{$ctrl.policy.policyType | robotToHuman | uppercase }}</span>
<span class="label small" ng-if="$ctrl.policy.status.state !== 'Applied'">({{$ctrl.policy.status.state}})</span>
<div ng-repeat="alarm in $ctrl.policy.alarms track by $index">
<div uib-popover-template="$ctrl.popoverTemplate"
popover-placement="left"
popover-title="{{$ctrl.policy.policyName}}"
popover-trigger="'mouseenter'">
<div>
<strong>Whenever</strong>
{{alarm.statistic}} of <span class="alarm-name">{{alarm.metricName}}</span>
is <span ng-bind-html="alarm.comparator"></span> {{alarm.threshold}}
</div>
<div>
<strong>for at least</strong>
{{alarm.evaluationPeriods}} consecutive periods of {{alarm.period}} seconds
</div>
</div>
<div class="actions text-right">
<button class="btn btn-xs btn-link" ng-click="$ctrl.editPolicy()">
<span class="glyphicon glyphicon-cog" uib-tooltip="Edit policy"></span>
<span class="sr-only">Edit policy</span>
</button>
<button class="btn btn-xs btn-link" ng-click="$ctrl.deletePolicy()">
<span class="glyphicon glyphicon-trash" uib-tooltip="Delete policy"></span>
<span class="sr-only">Delete policy</span>
</button>
</div>
</div>
87 changes: 87 additions & 0 deletions serverGroup/details/scalingPolicy/alarmBasedSummary.component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict';

const angular = require('angular');

import { CONFIRMATION_MODAL_SERVICE, TASK_EXECUTOR } from '@spinnaker/core';

import { SCALING_POLICY_WRITE_SERVICE } from '@spinnaker/amazon';

import './scalingPolicySummary.component.less';

module.exports = angular.module('spinnaker.titus.serverGroup.details.scalingPolicy.alarmBasedSummary.component', [
require('./upsert/upsertScalingPolicy.controller').name,
CONFIRMATION_MODAL_SERVICE,
SCALING_POLICY_WRITE_SERVICE,
TASK_EXECUTOR,
])
.component('titusAlarmBasedSummary', {
bindings: {
policy: '=',
serverGroup: '=',
application: '=',
},
templateUrl: require('./alarmBasedSummary.component.html'),
controller: function($uibModal, scalingPolicyWriter, confirmationModalService, accountService, taskExecutor) {

this.$onInit = () => {
accountService.getAccountDetails(this.serverGroup.account).then(details => {
// alarmServerGroup is used to trick the chart rendering into using AWS metrics
this.alarmServerGroup = {
type: 'aws',
name: this.serverGroup.name,
account: details.awsAccount,
region: this.serverGroup.region,
};
});
};

this.popoverTemplate = require('./popover/scalingPolicyDetails.popover.html');

this.editPolicy = () => {
$uibModal.open({
templateUrl: require('./upsert/upsertScalingPolicy.modal.html'),
controller: 'titusUpsertScalingPolicyCtrl',
controllerAs: 'ctrl',
size: 'lg',
resolve: {
policy: () => this.policy,
alarmServerGroup: () => this.alarmServerGroup,
serverGroup: () => this.serverGroup,
application: () => this.application,
}
});
};

this.deletePolicy = () => {
const { application, policy, serverGroup } = this;
const taskMonitor = {
application,
title: 'Deleting scaling policy ' + policy.id,
};

const submitMethod = () => taskExecutor.executeTask({
application,
description: 'Delete scaling policy ' + policy.id,
job: [
{
type: 'deleteScalingPolicy',
cloudProvider: 'titus',
credentials: serverGroup.account,
region: serverGroup.region,
scalingPolicyID: policy.id,
serverGroupName: serverGroup.name,
}
]
});

confirmationModalService.confirm({
header: `Really delete ${policy.id}?`,
buttonText: 'Delete scaling policy',
account: serverGroup.account,
provider: 'titus',
taskMonitorConfig: taskMonitor,
submitMethod: submitMethod
});
};
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<titus-alarm-based-summary
policy="$ctrl.policy"
server-group="$ctrl.serverGroup"
application="$ctrl.application"></titus-alarm-based-summary>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { module } from 'angular';
import { react2angular } from 'react2angular';
import { CreateScalingPolicyButton } from './CreateScalingPolicyButton';

export const TITUS_CREATE_SCALING_POLICY_BUTTON = 'spinnaker.titus.serverGroup.details.scaling.policy.button';
module(TITUS_CREATE_SCALING_POLICY_BUTTON, [])
.component('titusCreateScalingPolicyButton', react2angular(CreateScalingPolicyButton, ['application', 'serverGroup']));
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<aws-scaling-policy-popover policy="$ctrl.policy" server-group="$ctrl.alarmServerGroup"></aws-scaling-policy-popover>
13 changes: 13 additions & 0 deletions serverGroup/details/scalingPolicy/scalingPolicy.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { module } from 'angular';

import { SCALING_POLICY_SUMMARY } from './scalingPolicySummary.component';
import { TITUS_CREATE_SCALING_POLICY_BUTTON } from './createScalingPolicyButton.component';
// import { TARGET_TRACKING_MODULE } from './targetTracking/targetTracking.module';

export const SCALING_POLICY_MODULE = 'spinnaker.titus.scalingPolicy.module';
module(SCALING_POLICY_MODULE, [
SCALING_POLICY_SUMMARY,
TITUS_CREATE_SCALING_POLICY_BUTTON,
// TARGET_TRACKING_MODULE,
require('./alarmBasedSummary.component').name,
]);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
scaling-policy-summary {
.alarm-name {
word-break: break-all;
}
.actions {
font-size: 85%;
margin: 0 0 15px 0;
border-bottom: 1px solid var(--color-alto);

.btn-left {
padding-left: 0;
border-left-width: 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { IController, IComponentOptions, module } from 'angular';

import { Application, IServerGroup } from '@spinnaker/core';

import { IScalingPolicy, ScalingPolicyTypeRegistry } from '@spinnaker/amazon';

class ScalingPolicyDetailsSummaryController implements IController {

public templateUrl: string;
public policy: IScalingPolicy;
public serverGroup: IServerGroup;
public application: Application;

public $onInit() {
const config = ScalingPolicyTypeRegistry.getPolicyConfig(this.policy.policyType);
this.templateUrl = config ? config.summaryTemplateUrl : require('./alarmBasedSummary.template.html');
}
}

const component: IComponentOptions = {
bindings: {
policy: '<',
serverGroup: '<',
application: '<',
},
controller: ScalingPolicyDetailsSummaryController,
template: `<div ng-include src="$ctrl.templateUrl"></div>`
};

export const SCALING_POLICY_SUMMARY = 'spinnaker.titus.scalingPolicy.details.summary.component';
module(SCALING_POLICY_SUMMARY, []).component('titusScalingPolicySummary', component);
Loading

0 comments on commit 25b6488

Please sign in to comment.