@spotinst/spinnaker-deck
Version:
Spinnaker-Deck service, forked with support to Spotinst
279 lines (241 loc) • 10.5 kB
text/typescript
import { StateService } from '@uirouter/angularjs';
import { IController, IScope, module } from 'angular';
import { IModalInstanceService } from 'angular-ui-bootstrap';
import _ from 'lodash';
import {
AccountService,
Application,
IAccount,
ICredentials,
IRegion,
LoadBalancerWriter,
TaskMonitor,
} from '@spinnaker/core';
import { GOOGLE_COMMON_XPNNAMING_GCE_SERVICE } from 'google/common/xpnNaming.gce.service';
import { IGceBackendService, IGceHealthCheck, IGceLoadBalancer, IGceNetwork, IGceSubnet } from 'google/domain/index';
import { GCEProviderSettings } from 'google/gce.settings';
import { CommonGceLoadBalancerCtrl } from '../common/commonLoadBalancer.controller';
import {
GCE_COMMON_LOAD_BALANCER_COMMAND_BUILDER,
GceCommonLoadBalancerCommandBuilder,
} from '../common/commonLoadBalancerCommandBuilder.service';
import { GCE_HEALTH_CHECK_SELECTOR_COMPONENT } from '../common/healthCheck.component';
class ViewState {
constructor(public sessionAffinity: string) {}
}
interface IListKeyedByAccount {
[account: string]: string[];
}
interface IPrivateScope extends IScope {
$$destroyed: boolean;
}
class InternalLoadBalancer implements IGceLoadBalancer {
public name: string;
public stack: string;
public detail: string;
public loadBalancerName: string;
public ports: any;
public ipProtocol = 'TCP';
public loadBalancerType = 'INTERNAL';
public credentials: string;
public account: string;
public project: string;
public network = 'default';
public subnet: string;
public cloudProvider = 'gce';
public backendService: IGceBackendService = { healthCheck: { healthCheckType: 'TCP' } } as IGceBackendService;
constructor(public region: string) {}
}
class InternalLoadBalancerCtrl extends CommonGceLoadBalancerCtrl implements IController {
public pages: any = {
location: require('./createLoadBalancerProperties.html'),
listener: require('./listener.html'),
healthCheck: require('../common/commonHealthCheckPage.html'),
advancedSettings: require('../common/commonAdvancedSettingsPage.html'),
};
public sessionAffinityViewToModelMap: any = {
None: 'NONE',
'Client IP': 'CLIENT_IP',
'Client IP and protocol': 'CLIENT_IP_PROTO',
'Client IP, port and protocol': 'CLIENT_IP_PORT_PROTO',
};
public accounts: ICredentials[];
public regions: string[];
public networks: IGceNetwork[];
public networkOptions: string[];
public subnets: IGceSubnet[];
public subnetOptions: string[];
public healthChecksByAccountAndType: { [account: string]: { [healthCheckType: string]: IGceHealthCheck[] } };
// The 'by account' maps populate the corresponding 'existing names' lists below.
public existingLoadBalancerNamesByAccount: IListKeyedByAccount;
public existingHealthCheckNamesByAccount: IListKeyedByAccount;
public existingLoadBalancerNames: string[];
public existingHealthCheckNames: string[];
public viewState: ViewState = new ViewState('None');
public taskMonitor: any;
private sessionAffinityModelToViewMap: any = _.invert(this.sessionAffinityViewToModelMap);
public static $inject = [
'$scope',
'application',
'$uibModalInstance',
'loadBalancer',
'gceCommonLoadBalancerCommandBuilder',
'isNew',
'wizardSubFormValidation',
'gceXpnNamingService',
'$state',
];
constructor(
public $scope: IPrivateScope,
public application: Application,
public $uibModalInstance: IModalInstanceService,
private loadBalancer: InternalLoadBalancer,
private gceCommonLoadBalancerCommandBuilder: GceCommonLoadBalancerCommandBuilder,
private isNew: boolean,
private wizardSubFormValidation: any,
private gceXpnNamingService: any,
$state: StateService,
) {
super($scope, application, $uibModalInstance, $state);
}
public $onInit(): void {
this.gceCommonLoadBalancerCommandBuilder
.getBackingData(['existingLoadBalancerNamesByAccount', 'accounts', 'networks', 'subnets', 'healthChecks'])
.then((backingData) => {
if (!this.isNew) {
this.initializeEditMode();
} else {
this.loadBalancer = new InternalLoadBalancer(
GCEProviderSettings ? GCEProviderSettings.defaults.region : null,
);
}
this.loadBalancer.loadBalancerName = this.getName(this.loadBalancer, this.application);
const accountNames: string[] = backingData.accounts.map((account: IAccount) => account.name);
if (accountNames.length && !accountNames.includes(this.loadBalancer.account)) {
this.loadBalancer.credentials = accountNames[0];
} else {
this.loadBalancer.credentials = this.loadBalancer.account;
}
this.accounts = backingData.accounts;
this.networks = backingData.networks;
this.subnets = backingData.subnets;
this.existingLoadBalancerNamesByAccount = backingData.existingLoadBalancerNamesByAccount;
this.healthChecksByAccountAndType = this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(
backingData.healthChecks as IGceHealthCheck[],
);
// We don't count the load balancer's health check in the existing health checks list.
const healthCheckNamesToOmit = this.isNew ? [] : [this.loadBalancer.backendService.healthCheck.name];
this.existingHealthCheckNamesByAccount = this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(
backingData.healthChecks as IGceHealthCheck[],
healthCheckNamesToOmit,
);
this.accountUpdated();
this.wizardSubFormValidation
.config({ scope: this.$scope, form: 'form' })
.register({ page: 'location', subForm: 'locationForm' })
.register({ page: 'listener', subForm: 'listenerForm' })
.register({ page: 'healthCheck', subForm: 'healthCheckForm' })
.register({ page: 'advancedSettings', subForm: 'advancedSettingsForm' });
this.taskMonitor = new TaskMonitor({
application: this.application,
title: (this.isNew ? 'Creating ' : 'Updating ') + 'your load balancer',
modalInstance: this.$uibModalInstance,
onTaskComplete: () => this.onTaskComplete(this.loadBalancer),
});
});
}
public onHealthCheckRefresh(): void {
this.gceCommonLoadBalancerCommandBuilder.getBackingData(['healthChecks']).then((data) => {
this.healthChecksByAccountAndType = this.gceCommonLoadBalancerCommandBuilder.groupHealthChecksByAccountAndType(
data.healthChecks as IGceHealthCheck[],
);
const healthCheckNamesToOmit = this.isNew ? [] : [this.loadBalancer.backendService.healthCheck.name];
this.existingHealthCheckNamesByAccount = this.gceCommonLoadBalancerCommandBuilder.groupHealthCheckNamesByAccount(
data.healthChecks as IGceHealthCheck[],
healthCheckNamesToOmit,
);
});
}
public networkUpdated(): void {
this.subnetOptions = this.subnets
.filter((subnet) => {
return (
subnet.region === this.loadBalancer.region &&
(subnet.account === this.loadBalancer.credentials || subnet.account === this.loadBalancer.account) &&
subnet.network === this.loadBalancer.network
);
})
.map((subnet) => subnet.id);
if (!this.subnetOptions.includes(this.loadBalancer.subnet)) {
this.loadBalancer.subnet = this.subnetOptions[0];
}
}
public protocolUpdated(): void {
if (this.loadBalancer.ipProtocol === 'UDP') {
this.viewState = new ViewState('None');
this.loadBalancer.backendService.sessionAffinity = 'NONE';
}
}
public accountUpdated(): void {
const existingHealthCheckNames = _.get<any, string[]>(this, [
'existingHealthCheckNamesByAccount',
this.loadBalancer.credentials,
]);
this.existingHealthCheckNames = existingHealthCheckNames || [];
const existingLoadBalancerNames = _.get<any, string[]>(this, [
'existingLoadBalancerNamesByAccount',
this.loadBalancer.credentials,
]);
this.existingLoadBalancerNames = existingLoadBalancerNames || [];
this.networkOptions = this.networks
.filter((network: IGceNetwork) => network.account === this.loadBalancer.credentials)
.map((network) => network.id);
AccountService.getRegionsForAccount(this.loadBalancer.credentials).then((regions: IRegion[]) => {
this.regions = regions.map((region: IRegion) => region.name);
this.networkUpdated();
});
}
public regionUpdated(): void {
this.networkUpdated();
}
public updateName(): void {
this.loadBalancer.loadBalancerName = this.getName(this.loadBalancer, this.application);
}
public setSessionAffinity(viewState: ViewState): void {
this.loadBalancer.backendService.sessionAffinity = this.sessionAffinityViewToModelMap[viewState.sessionAffinity];
}
public submit(): void {
const descriptor = this.isNew ? 'Create' : 'Update';
const toSubmitLoadBalancer = _.cloneDeep(this.loadBalancer) as any;
toSubmitLoadBalancer.ports = toSubmitLoadBalancer.ports.split(',').map((port: string) => port.trim());
toSubmitLoadBalancer.cloudProvider = 'gce';
toSubmitLoadBalancer.name = toSubmitLoadBalancer.loadBalancerName;
toSubmitLoadBalancer.backendService.name = toSubmitLoadBalancer.loadBalancerName;
delete toSubmitLoadBalancer.instances;
this.taskMonitor.submit(() =>
LoadBalancerWriter.upsertLoadBalancer(toSubmitLoadBalancer, this.application, descriptor, {
healthCheck: {},
}),
);
}
private initializeEditMode(): void {
this.loadBalancer.ports = this.loadBalancer.ports.join(', ');
this.loadBalancer.subnet = this.gceXpnNamingService.decorateXpnResourceIfNecessary(
this.loadBalancer.project,
this.loadBalancer.subnet,
);
this.loadBalancer.network = this.gceXpnNamingService.decorateXpnResourceIfNecessary(
this.loadBalancer.project,
this.loadBalancer.network,
);
this.viewState = new ViewState(
this.sessionAffinityModelToViewMap[this.loadBalancer.backendService.sessionAffinity],
);
}
}
export const GCE_INTERNAL_LOAD_BALANCER_CTRL = 'spinnaker.gce.internalLoadBalancer.controller';
module(GCE_INTERNAL_LOAD_BALANCER_CTRL, [
GCE_HEALTH_CHECK_SELECTOR_COMPONENT,
GCE_COMMON_LOAD_BALANCER_COMMAND_BUILDER,
GOOGLE_COMMON_XPNNAMING_GCE_SERVICE,
]).controller('gceInternalLoadBalancerCtrl', InternalLoadBalancerCtrl);