Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(provider/google): enhanced autoscaler functionality #31

Open
wants to merge 3 commits into
base: google-autoscaler
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/app/src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,9 @@ window.spinnakerSettings = {
defaultSecurityGroups: [],
},
gce: {
feature: {
predictiveAutoscaling: true,
},
defaults: {
account: 'my-google-account',
instanceTypeStorage: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ export interface IGceAutoscalingCpuUtilization {

export enum GcePredictiveMethod {
NONE = 'NONE',
STANDARD = 'STANDARD',
STANDARD = 'OPTIMIZE_AVAILABILITY',
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<ng-form name="cpuUtilization">
<div ng-if="$ctrl.showMetric('cpuUtilization')">
<div class="row">
<div class="col-md-3 sm-label-right">
<div class="col-md-4 sm-label-right">
Utilization Target (%)
<help-field key="gce.serverGroup.scalingPolicy.cpuUtilization"></help-field>
</div>
Expand Down Expand Up @@ -97,55 +97,112 @@
<collapsible-section heading="Custom Metric Utilizations" expanded="true" subsection="true">
<div class="section-body" ng-repeat="custom in $ctrl.policy.customMetricUtilizations track by $index">
<hr ng-if="$index > 0" />

<div class="row">
<div class="col-md-3 sm-label-right">Metric Identifier</div>
<div class="col-md-2 content-fields">
<div class="col-md-3 content-fields">
<input class="form-control input-sm" required ng-model="custom.metric" />
</div>
<div class="col-md-offset-5 col-md-1">
<div class="col-md-offset-4 col-md-1">
<button class="btn btn-sm btn-default" ng-click="$ctrl.deleteMetric('customMetricUtilizations', $index)">
<span class="glyphicon glyphicon-trash visible-lg-inline"></span>
<span class="visible-lg-inline">Delete</span>
</button>
</div>
</div>
<div class="row">
<div class="col-md-3 sm-label-right">
Utilization Target
<help-field key="gce.serverGroup.scalingPolicy.customMetricUtilizations"></help-field>
<div class="col-md-3 sm-label-right">Additional Filter Expression</div>
<div class="col-md-3 content-fields">
<input class="form-control input-sm" ng-model="custom.additionalFilter" />
</div>
<div class="col-md-2 content-fields">
<input
type="number"
name="utilizationTarget"
class="form-control input-sm"
required
ng-model="custom.utilizationTarget"
/>
</div>
<div class="col-md-2">
</div>

<div class="row">
<div class="col-md-3 sm-label-right">Metric Export Scope</div>
<div class="col-md-3">
<select
class="form-control input-sm"
style="margin: 0px"
ng-options="value as displayValue for (value, displayValue) in $ctrl.targetTypesToDisplayMap"
ng-model="custom.utilizationTargetType"
ng-options="value as displayValue for (value, displayValue) in $ctrl.metricScopeTypesToDisplayMap"
ng-model="custom.metricExportScope"
required
>
<option value="">-- Target Type --</option>
</select>
></select>
</div>
<div class="col-md-5 error-message" ng-if="custom.utilizationTarget <= 0">
Utilization target must be greater than 0.0.
</div>

<div ng-if="$ctrl.isSingleTimeSeriesPerGroup('SINGLE_TIME_SERIES_PER_GROUP', $index)">
<div class="row">
<div class="col-md-3 sm-label-right">Scaling Policy</div>
<div class="col-md-3">
<select
class="form-control input-sm"
ng-options="value as displayValue for (value, displayValue) in $ctrl.scalingpolicyTypesToDisplayMap"
ng-model="custom.scalingpolicy"
required
></select>
</div>
</div>

<div ng-if="$ctrl.isScalingPolicySingleInstanceAssignment('SINGLE_INSTANCE_ASSIGNMENT', $index)">
<div class="row">
<div class="col-md-3 sm-label-right">Single Instance Assignment</div>
<div class="col-md-3 content-fields">
<input
type="number"
name="singleInstanceAssignment"
class="form-control input-sm"
required
ng-model="custom.singleInstaceAssignment"
min="0"
/>
</div>
</div>
</div>
</div>
<div
ng-if="
!$ctrl.isScalingPolicySingleInstanceAssignment('SINGLE_INSTANCE_ASSIGNMENT', $index) ||
!$ctrl.isSingleTimeSeriesPerGroup('SINGLE_TIME_SERIES_PER_GROUP', $index)
"
>
<div class="row">
<div class="col-md-3 sm-label-right">
Utilization Target
<help-field key="gce.serverGroup.scalingPolicy.customMetricUtilizations"></help-field>
</div>
<div class="col-md-3 content-fields">
<input
type="number"
name="utilizationTarget"
class="form-control input-sm"
required
ng-model="custom.utilizationTarget"
/>
</div>
<div class="col-md-3">
<select
class="form-control input-sm"
ng-options="value as displayValue for (value, displayValue) in $ctrl.targetTypesToDisplayMap"
ng-model="custom.utilizationTargetType"
required
>
<option value="">-- Target Type --</option>
</select>
</div>
<div class="col-md-5 error-message" ng-if="custom.utilizationTarget <= 0">
Utilization target must be greater than 0.0.
</div>
</div>
</div>
</div>
<button class="add-new col-md-12" ng-click="$ctrl.addMetric('customMetricUtilizations')">
<span class="glyphicon glyphicon-plus-sign"></span> Add custom metric
</button>
</collapsible-section>
<div class="col-md-12" ng-if="$ctrl.showNoMetricsWarning()">
<div class="alert alert-warning text-center">
<i class="fa fa-exclamation-triangle pull-left"></i>
<span>At least one metric is required.</span>
<div class="row">
<div class="col-md-12" ng-if="$ctrl.showNoMetricsWarning()">
<div class="alert alert-warning text-center">
<i class="fa fa-exclamation-triangle pull-left"></i>
<span>At least one metric is required.</span>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ module(GOOGLE_AUTOSCALINGPOLICY_COMPONENTS_METRICSETTINGS_METRICSETTINGS_COMPONE
DELTA_PER_MINUTE: 'Delta / minute',
};

this.metricScopeTypesToDisplayMap = {
TIME_SERIES_PER_INSTANCE: 'Time series per instance',
SINGLE_TIME_SERIES_PER_GROUP: 'Single time series per group',
};

this.scalingpolicyTypesToDisplayMap = {
UTILIZATION_TARGET: 'Utilization target',
SINGLE_INSTANCE_ASSIGNMENT: 'singleInstanceAssignment',
};

this.addMetric = (metricType) => {
if (multipleAllowedFor[metricType]) {
this.policy[metricType] = this.policy[metricType] || [];
Expand All @@ -54,6 +64,14 @@ module(GOOGLE_AUTOSCALINGPOLICY_COMPONENTS_METRICSETTINGS_METRICSETTINGS_COMPONE
return !emptyOrUndefined(metric);
};

this.isSingleTimeSeriesPerGroup = (scopeType, index) => {
if (this.policy.customMetricUtilizations[index].metricExportScope === scopeType) return true;
};

this.isScalingPolicySingleInstanceAssignment = (policyType, index) => {
if (this.policy.customMetricUtilizations[index].scalingpolicy === policyType) return true;
};

this.showNoMetricsWarning = () => {
return _.every(
metricTypes.map((type) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<ng-form name="scalingSchedulesForm">
<div class="section-body" ng-repeat="schedule in $ctrl.policy.scalingSchedules track by $index">
<ng-form name="scalingSchedule">
<hr ng-if="$index > 0" />

<div class="row">
<div class="col-md-3 sm-label-right">Name</div>
<div class="col-md-3 content-fields">
<input class="form-control input-sm" required ng-model="schedule.scheduleName" />
</div>
<div class="col-md-offset-4 col-md-1">
<button class="btn btn-sm btn-default" ng-click="$ctrl.deleteSchedule('scalingSchedules', $index)">
<span class="glyphicon glyphicon-trash visible-lg-inline"></span>
<span class="visible-lg-inline">Delete</span>
</button>
</div>
</div>
<div class="row">
<div class="col-md-3 sm-label-right">Description</div>
<div class="col-md-3 content-fields">
<input class="form-control input-sm" ng-model="schedule.scheduleDescription" />
</div>
</div>
<div class="row">
<div class="col-md-3 sm-label-right">Enabled</div>
<input type="checkbox" class="col-md-1" ng-model="schedule.enabled" value="{schedule.enabled}" />
</div>
<div class="row">
<div class="col-md-3 sm-label-right">Minimum required instances</div>
<div class="col-md-3 content-fields">
<input
type="number"
name="minimumRequiredInstances"
class="form-control input-sm"
min="0"
required
ng-model="schedule.minimumRequiredInstances"
/>
</div>
</div>
<div class="row">
<div class="col-md-3 sm-label-right">
CRON Expression
<help-field key="gce.serverGroup.scalingPolicy.cronExpression"></help-field>
</div>
<div class="col-md-3 content-fields">
<input class="form-control input-sm" required ng-model="schedule.scheduleCron" />
</div>
</div>
<div class="row">
<div class="col-md-3 sm-label-right">Time Zone</div>
<div class="col-md-3">
<gce-timezone-select
available-timezones="$ctrl.timezones"
selected-timezone="$ctrl.policy.scalingSchedules[$index].timezone"
select-timezone="$ctrl.selectTimezone"
target="$index"
></gce-timezone-select>
</div>
</div>
<div class="row">
<div class="col-md-3 sm-label-right">Duration (seconds)</div>
<div class="col-md-3 content-fields">
<input
type="number"
name="duration"
class="form-control input-sm"
required
ng-model="schedule.duration"
min="301"
/>
</div>
<div class="col-md-4 error-message" ng-if="scalingSchedule.duration.$error.min">
Must be greater than 300 seconds.
</div>
</div>
</ng-form>
</div>
<div class="schedule-container">
<button class="add-new col-md-12" ng-click="$ctrl.addSchedule('scalingSchedules')">
<span class="glyphicon glyphicon-plus-sign"></span> Add Scaling Schedule
</button>
</div>
</ng-form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use strict';

import { module } from 'angular';
import _ from 'lodash';
import timezones from './standardTimezone.json';

export const GOOGLE_AUTOSCALINGPOLICY_COMPONENTS_SCALINGSCHEDULES_SCALINGSCHEDULES_COMPONENT =
'spinnaker.deck.gce.autoscalingPolicy.scalingSchedules.component';
export const name = GOOGLE_AUTOSCALINGPOLICY_COMPONENTS_SCALINGSCHEDULES_SCALINGSCHEDULES_COMPONENT; // for backwards compatibility
module(GOOGLE_AUTOSCALINGPOLICY_COMPONENTS_SCALINGSCHEDULES_SCALINGSCHEDULES_COMPONENT, []).component(
'gceAutoscalingPolicyScalingSchedules',
{
bindings: {
policy: '=',
updatePolicy: '<',
},
templateUrl: require('./scalingSchedules.component.html'),
controller: function () {
const multipleAllowedFor = {
scalingSchedules: true,
};

this.timezones = timezones;

this.addSchedule = (scheduleType) => {
if (multipleAllowedFor[scheduleType]) {
this.policy[scheduleType] = this.policy[scheduleType] || [];
this.policy[scheduleType].push({});
} else if (emptyOrUndefined(this.policy[scheduleType])) {
this.policy[scheduleType] = {};
}
};

this.deleteSchedule = (scheduleType, index) => {
if (multipleAllowedFor[scheduleType]) {
this.policy[scheduleType].splice(index, 1);
} else {
// sending an empty object to the API deletes the policy.
this.policy[scheduleType] = {};
}
};

this.selectTimezone = (timezone, index) => {
const { scalingSchedules } = this.policy;
const schedule = scalingSchedules[index];
scalingSchedules[index] = { ...schedule, timezone };

this.updatePolicy({
...this.policy,
scalingSchedules: [...scalingSchedules],
});
};

function emptyOrUndefined(value) {
return _.isEqual(value, {}) || _.isUndefined(value);
}
},
},
);
Loading