From 5cbbd4b5e766e6c2161077a527cf05927ea93f41 Mon Sep 17 00:00:00 2001 From: Jacob Kiefer Date: Fri, 22 Sep 2017 17:00:00 -0400 Subject: [PATCH] feat(provider/gce): Support named ports for global LBs. --- .../google/src/domain/backendService.ts | 8 ++- .../modules/google/src/help/gce.help.ts | 2 + .../common/commonCreateLoadBalancer.html | 6 ++ .../common/commonEditLoadBalancer.html | 6 ++ .../backendService.component.html | 13 ++++ .../gceCreateSslLoadBalancer.controller.ts | 2 + .../loadBalancer/configure/ssl/portName.html | 16 +++++ .../gceCreateTcpLoadBalancer.controller.ts | 2 + .../loadBalancer/configure/tcp/portName.html | 16 +++++ ...loadBalancingPolicySelector.component.html | 54 ++++++++++---- .../loadBalancingPolicySelector.component.ts | 72 +++++++++++++++---- 11 files changed, 172 insertions(+), 25 deletions(-) create mode 100644 app/scripts/modules/google/src/loadBalancer/configure/ssl/portName.html create mode 100644 app/scripts/modules/google/src/loadBalancer/configure/tcp/portName.html diff --git a/app/scripts/modules/google/src/domain/backendService.ts b/app/scripts/modules/google/src/domain/backendService.ts index 27558af6cce..2853a2006f1 100644 --- a/app/scripts/modules/google/src/domain/backendService.ts +++ b/app/scripts/modules/google/src/domain/backendService.ts @@ -5,4 +5,10 @@ export interface IGceBackendService { backends: any[]; healthCheck: IGceHealthCheck; sessionAffinity: string; -}; + portName: string; +} + +export interface INamedPort { + name: string; + port: number; +} diff --git a/app/scripts/modules/google/src/help/gce.help.ts b/app/scripts/modules/google/src/help/gce.help.ts index 3f5a2ad455f..507e95ed6da 100644 --- a/app/scripts/modules/google/src/help/gce.help.ts +++ b/app/scripts/modules/google/src/help/gce.help.ts @@ -33,6 +33,7 @@ const helpContents: {[key: string]: string} = { 'gce.loadBalancer.advancedSettings.healthyThreshold': '

Configures the number of healthy observations before reinstituting an instance into the load balancer’s traffic rotation.

Default: 10

', 'gce.loadBalancer.advancedSettings.unhealthyThreshold': '

Configures the number of unhealthy observations before deservicing an instance from the load balancer.

Default: 2

', 'gce.loadBalancer.healthCheck': '(Optional) Health Checks use HTTP requests to determine if a VM instance is healthy.', + 'gce.loadBalancer.portName': '(Required) The Port Name this backend service will forward traffic to. Load balancers in GCP specify a Port Name, and each server group added to a load balancer needs to specify a mapping from that Port Name to a port to actually receive traffic.', 'gce.loadBalancer.portRange': '(Optional) Only packets addressed to ports in the specified Port Range will be forwarded. If left empty, all ports are forwarded. Must be a single port number or two port numbers separated by a dash. Each port number must be between 1 and 65535, inclusive. For example: 5000-5999.', 'gce.securityGroup.sourceCIDRs': 'Traffic is only allowed from sources that match one of these CIDR ranges, or one of the source tags above.', 'gce.securityGroup.sourceTags': 'Traffic is only allowed from sources that match one of these tags, or one of the source CIDR ranges below.', @@ -91,6 +92,7 @@ const helpContents: {[key: string]: string} = { An additional control to manage your maximum CPU utilization or RPS. If you want your instances to operate at a max 80% CPU utilization, set your balancing mode to 80% max CPU utilization and your capacity to 100%. If you want to cut instance utilization by half, set your balancing mode to 80% max CPU utilization and your capacity to 50%. Input must be a number between 0 and 100.`, + 'gce.serverGroup.loadBalancingPolicy.portName': 'A load balancer sends traffic to an instance group through a named port. Input must be a port name.', 'gce.serverGroup.loadBalancingPolicy.listeningPort': 'A load balancer sends traffic to an instance group through a named port. Input must be a port number (i.e., between 1 and 65535).', 'gce.serverGroup.traffic': 'Registers the server group with any associated load balancers. These registrations are used by Spinnaker to determine if the server group is enabled.', 'pipeline.config.gce.bake.accountName': '

(Optional) The name of a Google account configured within Rosco. If left blank, the first configured account will be used.

', diff --git a/app/scripts/modules/google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html b/app/scripts/modules/google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html index 9c6261b9169..274549a30f9 100644 --- a/app/scripts/modules/google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html +++ b/app/scripts/modules/google/src/loadBalancer/configure/common/commonCreateLoadBalancer.html @@ -12,6 +12,12 @@

+ + + diff --git a/app/scripts/modules/google/src/loadBalancer/configure/common/commonEditLoadBalancer.html b/app/scripts/modules/google/src/loadBalancer/configure/common/commonEditLoadBalancer.html index 37726957d74..d25901f2945 100644 --- a/app/scripts/modules/google/src/loadBalancer/configure/common/commonEditLoadBalancer.html +++ b/app/scripts/modules/google/src/loadBalancer/configure/common/commonEditLoadBalancer.html @@ -9,6 +9,12 @@

+ + + diff --git a/app/scripts/modules/google/src/loadBalancer/configure/http/backendService/backendService.component.html b/app/scripts/modules/google/src/loadBalancer/configure/http/backendService/backendService.component.html index c69da4a63b8..c9d6eabda91 100644 --- a/app/scripts/modules/google/src/loadBalancer/configure/http/backendService/backendService.component.html +++ b/app/scripts/modules/google/src/loadBalancer/configure/http/backendService/backendService.component.html @@ -101,6 +101,19 @@ +
+
+ Port Name + +
+
+ +
+
+
Enable CDN diff --git a/app/scripts/modules/google/src/loadBalancer/configure/ssl/gceCreateSslLoadBalancer.controller.ts b/app/scripts/modules/google/src/loadBalancer/configure/ssl/gceCreateSslLoadBalancer.controller.ts index cd5b4371fbf..b082c4cf15a 100644 --- a/app/scripts/modules/google/src/loadBalancer/configure/ssl/gceCreateSslLoadBalancer.controller.ts +++ b/app/scripts/modules/google/src/loadBalancer/configure/ssl/gceCreateSslLoadBalancer.controller.ts @@ -66,6 +66,7 @@ class SslLoadBalancerCtrl extends CommonGceLoadBalancerCtrl implements IControll 'listener': require('./listener.html'), 'healthCheck': require('../common/commonHealthCheckPage.html'), 'advancedSettings': require('../common/commonAdvancedSettingsPage.html'), + 'portName': require('./portName'), }; public sessionAffinityViewToModelMap: any = { 'None': 'NONE', @@ -87,6 +88,7 @@ class SslLoadBalancerCtrl extends CommonGceLoadBalancerCtrl implements IControll public viewState: ViewState = new ViewState('None'); public maxCookieTtl = 60 * 60 * 24; // One day. public taskMonitor: any; + public hasPortName = true; private sessionAffinityModelToViewMap: any = _.invert(this.sessionAffinityViewToModelMap); diff --git a/app/scripts/modules/google/src/loadBalancer/configure/ssl/portName.html b/app/scripts/modules/google/src/loadBalancer/configure/ssl/portName.html new file mode 100644 index 00000000000..45ca9608c80 --- /dev/null +++ b/app/scripts/modules/google/src/loadBalancer/configure/ssl/portName.html @@ -0,0 +1,16 @@ +
+ +
+
+ Port Name + +
+
+ +
+
+
+
diff --git a/app/scripts/modules/google/src/loadBalancer/configure/tcp/gceCreateTcpLoadBalancer.controller.ts b/app/scripts/modules/google/src/loadBalancer/configure/tcp/gceCreateTcpLoadBalancer.controller.ts index 14706c67c77..a6cd1dc552b 100644 --- a/app/scripts/modules/google/src/loadBalancer/configure/tcp/gceCreateTcpLoadBalancer.controller.ts +++ b/app/scripts/modules/google/src/loadBalancer/configure/tcp/gceCreateTcpLoadBalancer.controller.ts @@ -65,6 +65,7 @@ class TcpLoadBalancerCtrl extends CommonGceLoadBalancerCtrl implements ng.ICompo 'listener': require('./listener.html'), 'healthCheck': require('../common/commonHealthCheckPage.html'), 'advancedSettings': require('../common/commonAdvancedSettingsPage.html'), + 'portName': require('./portName'), }; public sessionAffinityViewToModelMap: any = { 'None': 'NONE', @@ -85,6 +86,7 @@ class TcpLoadBalancerCtrl extends CommonGceLoadBalancerCtrl implements ng.ICompo public viewState: ViewState = new ViewState('None'); public maxCookieTtl = 60 * 60 * 24; // One day. public taskMonitor: any; + public hasPortName = true; private sessionAffinityModelToViewMap: any = _.invert(this.sessionAffinityViewToModelMap); diff --git a/app/scripts/modules/google/src/loadBalancer/configure/tcp/portName.html b/app/scripts/modules/google/src/loadBalancer/configure/tcp/portName.html new file mode 100644 index 00000000000..45ca9608c80 --- /dev/null +++ b/app/scripts/modules/google/src/loadBalancer/configure/tcp/portName.html @@ -0,0 +1,16 @@ +
+ +
+
+ Port Name + +
+
+ +
+
+
+
diff --git a/app/scripts/modules/google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.html b/app/scripts/modules/google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.html index 2c4696c29f9..75a69ab2858 100644 --- a/app/scripts/modules/google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.html +++ b/app/scripts/modules/google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.html @@ -1,19 +1,49 @@
-
- http +
+
+
+ Name + +
+
+ + {{namedPort.name}} + + + + +
+
+ Port + +
+
+ +
+
+ +
+
+ Must be between 1 and {{ $ctrl.maxPort }}. +
+
-
-
- Must be between 1 and {{ $ctrl.maxPort }}. +
+
diff --git a/app/scripts/modules/google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.ts b/app/scripts/modules/google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.ts index e49d2171e42..85b8b88660b 100644 --- a/app/scripts/modules/google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.ts +++ b/app/scripts/modules/google/src/serverGroup/configure/wizard/loadBalancingPolicy/loadBalancingPolicySelector.component.ts @@ -1,22 +1,28 @@ import { IComponentOptions, IController, module } from 'angular'; -import { set, get, has, without } from 'lodash'; +import { set, get, has, without, intersection, chain } from 'lodash'; import './loadBalancingPolicySelector.component.less'; +import { IGceBackendService, INamedPort } from 'google/domain'; class GceLoadBalancingPolicySelectorController implements IController { public maxPort = 65535; public command: any; [key: string]: any; + public globalBackendServices: IGceBackendService[]; - public setModel (propertyName: string, viewValue: number): void { + constructor(private gceBackendServiceReader: any) { + 'ngInject'; + } + + public setModel(propertyName: string, viewValue: number): void { set(this, propertyName, viewValue / 100); }; - public setView (propertyName: string , modelValue: number): void { + public setView(propertyName: string, modelValue: number): void { this[propertyName] = this.decimalToPercent(modelValue); }; - public onBalancingModeChange (mode: string): void { + public onBalancingModeChange(mode: string): void { const keys: string[] = ['maxUtilization', 'maxRatePerInstance', 'maxConnectionsPerInstance']; let toDelete: string[] = []; switch (mode) { @@ -36,14 +42,14 @@ class GceLoadBalancingPolicySelectorController implements IController { toDelete.forEach((key) => delete this.command.loadBalancingPolicy[key]); } - public getBalancingModes (): string[] { + public getBalancingModes(): string[] { let balancingModes: string[] = []; /* - * Three cases: - * - If we have only HTTP(S) load balancers, our balancing mode can be RATE or UTILIZATION. - * - If we have only SSL/TCP load balancers, our balancing mode can be CONNECTION or UTILIZATION. - * - If we have both, only UTILIZATION. - * */ + * Three cases: + * - If we have only HTTP(S) load balancers, our balancing mode can be RATE or UTILIZATION. + * - If we have only SSL/TCP load balancers, our balancing mode can be CONNECTION or UTILIZATION. + * - If we have both, only UTILIZATION. + * */ if (has(this, 'command.backingData.filtered.loadBalancerIndex')) { const index = this.command.backingData.filtered.loadBalancerIndex; const selected = this.command.loadBalancers; @@ -67,11 +73,53 @@ class GceLoadBalancingPolicySelectorController implements IController { return balancingModes; } - public $onDestroy (): void { + public $onInit(): void { + this.gceBackendServiceReader.listBackendServices('globalBackendService') + .then((services: IGceBackendService[]) => { + this.globalBackendServices = services; + }); + } + + public $onDestroy(): void { delete this.command.loadBalancingPolicy; } - private decimalToPercent (value: number): number { + public addNamedPort() { + if (!this.command.loadBalancingPolicy.namedPorts) { + this.command.loadBalancingPolicy.namedPorts = []; + } + + this.command.loadBalancingPolicy.namedPorts.push({name: '', port: 80}); + } + + public removeNamedPort(index: number) { + this.command.loadBalancingPolicy.namedPorts.splice(index, 1); + } + + public getPortNames(): string[] { + const index = this.command.backingData.filtered.loadBalancerIndex; + const selected = this.command.loadBalancers; + const inUsePortNames = this.command.loadBalancingPolicy.namedPorts.map((namedPort: INamedPort) => namedPort.name); + + const getThem = (globalBackendServices: IGceBackendService[], loadBalancer: string): string[] => { + switch (get(index[loadBalancer], 'loadBalancerType')) { + case 'SSL': + case 'TCP': + case 'HTTP': + const lbBackendServices: string[] = get(index[loadBalancer], 'backendServices'); + const filteredBackendServices = globalBackendServices.filter((service: IGceBackendService) => lbBackendServices.includes(service.name)); + const portNames = filteredBackendServices.map((service: IGceBackendService) => service.portName); + const portNameIntersection = intersection(portNames, inUsePortNames); + return portNames.filter(portName => !portNameIntersection.includes(portName)); + default: + return []; + } + }; + + return chain(selected).flatMap((lbName: string) => getThem(this.globalBackendServices, lbName)).uniq().value(); + } + + private decimalToPercent(value: number): number { if (value === 0) { return 0; }