Skip to content

Commit

Permalink
feat(google): add gce scale-down controls (#7570)
Browse files Browse the repository at this point in the history
* feat(google): add feature flag for gce scale down controls

* feat(google): add gce scale-down controls to autoscaling policy ui

* fix(google): add min and max timeWindowSec, sidebar details
  • Loading branch information
maggieneterval authored and mergify[bot] committed Oct 29, 2019
1 parent 2c100b6 commit 5d89a04
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 3 deletions.
1 change: 1 addition & 0 deletions app/scripts/modules/core/src/config/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface IFeatures {
dockerBake?: boolean;
entityTags?: boolean;
fiatEnabled?: boolean;
gceScaleDownControlsEnabled?: boolean;
gceStatefulMigsEnabled?: boolean;
iapRefresherEnabled?: boolean;
// whether stages affecting infrastructure (like "Create Load Balancer") should be enabled or not
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,5 @@
</select>
</div>
</div>
<gce-scale-down-controls policy="$ctrl.policy" update-policy="$ctrl.updatePolicy"> </gce-scale-down-controls>
</ng-form>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = angular
.component('gceAutoscalingPolicyBasicSettings', {
bindings: {
policy: '=',
updatePolicy: '<',
},
templateUrl: require('./basicSettings.component.html'),
controller: function controller() {
Expand Down
2 changes: 2 additions & 0 deletions app/scripts/modules/google/src/gce.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { GCE_TCP_LOAD_BALANCER_CTRL } from './loadBalancer/configure/tcp/gceCrea
import { IAP_INTERCEPTOR } from 'google/interceptors/iap.interceptor';
import { LOAD_BALANCER_SET_TRANSFORMER } from './loadBalancer/loadBalancer.setTransformer';
import { GCE_SERVER_GROUP_DISK_DESCRIPTIONS } from './serverGroup/details/ServerGroupDiskDescriptions';
import { GCE_SCALE_DOWN_CONTROLS } from './serverGroup/details/autoscalingPolicy/modal/GceScaleDownControls';
import { GceImageReader } from './image';
import './help/gce.help';

Expand All @@ -29,6 +30,7 @@ module(GOOGLE_MODULE, [
GCE_TCP_LOAD_BALANCER_CTRL,
IAP_INTERCEPTOR,
GCE_SERVER_GROUP_DISK_DESCRIPTIONS,
GCE_SCALE_DOWN_CONTROLS,
require('./serverGroup/details/serverGroup.details.gce.module').name,
require('./serverGroup/configure/serverGroupCommandBuilder.service').name,
require('./serverGroup/configure/wizard/cloneServerGroup.gce.controller').name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@
<help-field key="gce.serverGroup.autoscaling.mode"></help-field>
</dt>
<dd>{{$ctrl.policy.mode}}</dd>
<dt ng-if="$ctrl.scaleDownControlsEnabled">
Scale-down Controls
</dt>
<dd ng-if="$ctrl.scaleDownControlsEnabled">{{$ctrl.scaleDownControlsConfigured ? 'Enabled' : 'Disabled'}}</dd>
<dt ng-if="$ctrl.scaleDownControlsConfigured">
Max Scaled-down Replicas
</dt>
<dd ng-if="$ctrl.scaleDownControlsConfigured">{{$ctrl.maxScaledDownReplicasMessage}}</dd>
<dt ng-if="$ctrl.scaleDownControlsConfigured">
Scale-down Time Window
</dt>
<dd ng-if="$ctrl.scaleDownControlsConfigured">{{$ctrl.timeWindowSecMessage}}</dd>
<dt ng-if="$ctrl.serverGroup.autoscalingMessages">Messages</dt>
<dd ng-if="$ctrl.serverGroup.autoscalingMessages" ng-repeat="message in $ctrl.serverGroup.autoscalingMessages">
{{message}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

const angular = require('angular');

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

module.exports = angular
.module('spinnaker.gce.instance.details.scalingPolicy.directive', [
Expand Down Expand Up @@ -77,6 +77,23 @@ module.exports = angular
policy.bases.push(basis);
}

this.scaleDownControlsEnabled = SETTINGS.feature.gceScaleDownControlsEnabled;
this.scaleDownControlsConfigured =
this.scaleDownControlsEnabled &&
policy.scaleDownControl &&
policy.scaleDownControl.timeWindowSec &&
policy.scaleDownControl.maxScaledDownReplicas &&
(policy.scaleDownControl.maxScaledDownReplicas.percent ||
policy.scaleDownControl.maxScaledDownReplicas.fixed);

if (this.scaleDownControlsConfigured) {
this.maxScaledDownReplicasMessage = policy.scaleDownControl.maxScaledDownReplicas.percent
? `${policy.scaleDownControl.maxScaledDownReplicas.percent}%`
: `${policy.scaleDownControl.maxScaledDownReplicas.fixed}`;

this.timeWindowSecMessage = `${policy.scaleDownControl.timeWindowSec} seconds`;
}

this.editPolicy = () => {
$uibModal.open({
templateUrl: require('./modal/upsertAutoscalingPolicy.modal.html'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as React from 'react';
import { isEmpty } from 'lodash';

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

import './gceScaleDownControls.less';

// todo(mneterval): remove when GCE Autoscaling Controls are entirely converted to React & Formik
export function GceAutoScalingFieldLayout(props: ILayoutProps) {
const { label, help, input, actions, validation, required } = props;

const showLabel = !isEmpty(label) || !isEmpty(help);
const { hidden, messageNode } = validation;

return (
<div className="gce-scale-down-controls">
{showLabel && (
<label className="col-md-3 sm-label-right">
{label}
{required && <span>*</span>} {help}
</label>
)}
<div className="col-md-2 content-fields">
<div>
{input} {actions}
</div>
{!hidden && <div className="message">{messageNode}</div>}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { module } from 'angular';

import * as React from 'react';
import { react2angular } from 'react2angular';
import { isEmpty } from 'lodash';

import { CheckboxInput, FormField, LayoutProvider, NumberInput, ReactSelectInput, SETTINGS } from '@spinnaker/core';

import { GceAutoScalingFieldLayout } from './GceAutoScalingFieldLayout';

enum maxReplicasUnit {
fixed = 'fixed',
percent = 'percent',
}

interface IGceScaleDownControl {
maxScaledDownReplicas?: {
fixed?: number;
percent?: number;
};
timeWindowSec?: number;
}

interface IGceAutoscalingPolicy {
scaleDownControl: IGceScaleDownControl;
}

interface IGceScaleDownControlsProps {
policy: IGceAutoscalingPolicy;
updatePolicy: (policy: IGceAutoscalingPolicy) => void;
}

const defaultScaleDownControl: IGceScaleDownControl = {
maxScaledDownReplicas: {
fixed: null,
percent: 0,
},
timeWindowSec: 60,
};

function GceScaleDownControls({ policy, updatePolicy }: IGceScaleDownControlsProps) {
if (!SETTINGS.feature.gceScaleDownControlsEnabled) {
return null;
}

function updateScaleDownControl(scaleDownControl: IGceScaleDownControl) {
updatePolicy({
...policy,
scaleDownControl,
});
}

function getMaxReplicasUnit(): maxReplicasUnit {
if (Number.isInteger(policy.scaleDownControl.maxScaledDownReplicas.percent)) {
return maxReplicasUnit.percent;
}
return maxReplicasUnit.fixed;
}

return (
<LayoutProvider value={GceAutoScalingFieldLayout}>
<div className="row">
<FormField
input={inputProps => <CheckboxInput {...inputProps} />}
label="Enable scale-down controls"
onChange={(e: React.ChangeEvent<any>) => {
updateScaleDownControl(e.target.checked ? defaultScaleDownControl : {});
}}
value={!isEmpty(policy.scaleDownControl)}
/>
</div>
{!isEmpty(policy.scaleDownControl) && (
<>
<div className="row">
<FormField
input={inputProps => (
<NumberInput
{...inputProps}
min={0}
max={getMaxReplicasUnit() === maxReplicasUnit.percent ? 100 : null}
/>
)}
label="Max scaled-down replicas"
onChange={(e: React.ChangeEvent<any>) => {
updateScaleDownControl({
...policy.scaleDownControl,
maxScaledDownReplicas: {
[getMaxReplicasUnit()]: parseInt(e.target.value, 10),
},
});
}}
value={policy.scaleDownControl.maxScaledDownReplicas[getMaxReplicasUnit()]}
/>
<FormField
input={inputProps => (
<ReactSelectInput
{...inputProps}
clearable={false}
stringOptions={[maxReplicasUnit.percent, maxReplicasUnit.fixed]}
/>
)}
label=""
onChange={(e: React.ChangeEvent<any>) => {
updateScaleDownControl({
...policy.scaleDownControl,
maxScaledDownReplicas: {
[e.target.value]: policy.scaleDownControl.maxScaledDownReplicas[getMaxReplicasUnit()],
},
});
}}
value={getMaxReplicasUnit()}
/>
</div>
<div className="row">
<FormField
input={inputProps => <NumberInput {...inputProps} min={60} max={3600} />}
label="Time window (seconds)"
onChange={(e: React.ChangeEvent<any>) => {
updateScaleDownControl({
...policy.scaleDownControl,
timeWindowSec: parseInt(e.target.value, 10),
});
}}
value={policy.scaleDownControl.timeWindowSec}
/>
</div>
</>
)}
</LayoutProvider>
);
}

export const GCE_SCALE_DOWN_CONTROLS = 'spinnaker.gce.scaleDownControls';
module(GCE_SCALE_DOWN_CONTROLS, []).component(
'gceScaleDownControls',
react2angular(GceScaleDownControls, ['policy', 'updatePolicy']),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// todo(mneterval): remove when GCE Autoscaling Controls are entirely converted to React & Formik
.gce-scale-down-controls {
.checkbox {
margin-left: 5px;
margin-top: 7px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ module.exports = angular
'serverGroup',
'$uibModalInstance',
'gceAutoscalingPolicyWriter',
function(policy, application, serverGroup, $uibModalInstance, gceAutoscalingPolicyWriter) {
'$scope',
function(policy, application, serverGroup, $uibModalInstance, gceAutoscalingPolicyWriter, $scope) {
[this.action, this.isNew] = policy ? ['Edit', false] : ['New', true];
this.policy = _.cloneDeep(policy || {});

Expand All @@ -36,5 +37,11 @@ module.exports = angular

this.taskMonitor.submit(submitMethod);
};

this.updatePolicy = updatedPolicy => {
$scope.$applyAsync(() => {
this.policy = updatedPolicy;
});
};
},
]);
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ <h3>{{ctrl.action}} scaling policy</h3>
<div class="modal-body clearfix">
<h4 class="section-heading">Basic Settings</h4>
<div class="section-body">
<gce-autoscaling-policy-basic-settings policy="ctrl.policy"></gce-autoscaling-policy-basic-settings>
<gce-autoscaling-policy-basic-settings policy="ctrl.policy" update-policy="ctrl.updatePolicy" />
</div>
<h4 class="section-heading">Metric Types</h4>
<gce-autoscaling-policy-metric-settings show-no-metrics-warning="ctrl.showNoMetricsWarning" policy="ctrl.policy">
Expand Down
2 changes: 2 additions & 0 deletions halconfig/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var defaultStorageAccountName = '{%canary.defaultStorageAccount%}';
var displayTimestampsInUserLocalTime = '{%features.displayTimestampsInUserLocalTime%}' === 'true';
var entityTagsEnabled = false;
var fiatEnabled = '{%features.fiat%}' === 'true';
var gceScaleDownControlsEnabled = '{%features.gceScaleDownControlsEnabled%}' === 'true';
var gceStatefulMigsEnabled = '{%features.gceStatefulMigsEnabled%}' === 'true';
var gremlinEnabled = '{%features.gremlin%}' === 'true';
var iapRefresherEnabled = '{%features.iapRefresherEnabled%}' === 'true';
Expand Down Expand Up @@ -157,6 +158,7 @@ window.spinnakerSettings = {
displayTimestampsInUserLocalTime: displayTimestampsInUserLocalTime,
entityTags: entityTagsEnabled,
fiatEnabled: fiatEnabled,
gceScaleDownControlsEnabled: gceScaleDownControlsEnabled,
gceStatefulMigsEnabled: gceStatefulMigsEnabled,
gremlinEnabled: gremlinEnabled,
iapRefresherEnabled: iapRefresherEnabled,
Expand Down
2 changes: 2 additions & 0 deletions settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var displayTimestampsInUserLocalTime = process.env.DISPLAY_TIMESTAMPS_IN_USER_LO
var dryRunEnabled = process.env.DRYRUN_ENABLED === 'true' ? true : false;
var entityTagsEnabled = process.env.ENTITY_TAGS_ENABLED === 'true' ? true : false;
var fiatEnabled = process.env.FIAT_ENABLED === 'true' ? true : false;
var gceScaleDownControlsEnabled = process.env.GCE_SCALE_DOWN_CONTROLS_ENABLED === 'true' ? true : false;
var gceStatefulMigsEnabled = process.env.GCE_STATEFUL_MIGS_ENABLED === 'true' ? true : false;
var gremlinEnabled = process.env.GREMLIN_ENABLED === 'false' ? false : true;
var iapRefresherEnabled = process.env.IAP_REFRESHER_ENABLED === 'true' ? true : false;
Expand Down Expand Up @@ -68,6 +69,7 @@ window.spinnakerSettings = {
dryRunEnabled: dryRunEnabled,
entityTags: entityTagsEnabled,
fiatEnabled: fiatEnabled,
gceScaleDownControlsEnabled: gceScaleDownControlsEnabled,
gceStatefulMigsEnabled: gceStatefulMigsEnabled,
gremlinEnabled: gremlinEnabled,
iapRefresherEnabled: iapRefresherEnabled,
Expand Down

0 comments on commit 5d89a04

Please sign in to comment.