@spinnaker/google 2026.0.3 → 2026.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/loadBalancer.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loadBalancer/configure/choice/gceLoadBalancerPipelineModal.d.ts +9 -0
- package/dist/loadBalancer/configure/http/templates.d.ts +3 -0
- package/package.json +3 -3
- package/src/domain/loadBalancer.ts +4 -2
- package/src/gce.module.ts +2 -0
- package/src/help/gce.help.ts +2 -0
- package/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.spec.js +66 -0
- package/src/loadBalancer/configure/choice/gceLoadBalancerChoice.modal.ts +20 -5
- package/src/loadBalancer/configure/choice/gceLoadBalancerPipelineModal.ts +47 -0
- package/src/loadBalancer/configure/gceL4LoadBalancerPipeline.spec.js +143 -0
- package/src/loadBalancer/configure/http/createHttpLoadBalancer.controller.js +22 -0
- package/src/loadBalancer/configure/http/createHttpLoadBalancer.controller.spec.js +96 -0
- package/src/loadBalancer/configure/http/listeners/listener.component.html +79 -24
- package/src/loadBalancer/configure/http/listeners/listener.component.js +82 -0
- package/src/loadBalancer/configure/http/listeners/listener.component.spec.js +125 -0
- package/src/loadBalancer/configure/http/templates.ts +3 -0
- package/src/loadBalancer/configure/http/transformer.service.js +16 -1
- package/src/loadBalancer/configure/http/transformer.service.spec.js +182 -0
- package/src/loadBalancer/configure/internal/gceCreateInternalLoadBalancer.controller.ts +8 -0
- package/src/loadBalancer/configure/internalhttp/createInternalHttpLoadBalancer.controller.spec.js +89 -0
- package/src/loadBalancer/configure/internalhttp/createInternalHttpLoadBalancer.controller.ts +23 -0
- package/src/loadBalancer/configure/network/createLoadBalancer.controller.js +48 -32
- package/src/loadBalancer/configure/network/createLoadBalancer.controller.spec.js +36 -0
- package/src/loadBalancer/configure/ssl/gceCreateSslLoadBalancer.controller.ts +8 -0
- package/src/loadBalancer/configure/tcp/gceCreateTcpLoadBalancer.controller.ts +8 -0
- package/src/loadBalancer/details/loadBalancerType/loadBalancerType.component.js +3 -1
- package/src/loadBalancer/details/loadBalancerType/loadBalancerType.component.spec.js +85 -0
- package/src/loadBalancer/loadBalancer.setTransformer.ts +1 -0
- package/src/serverGroup/configure/serverGroupConfiguration.service.js +0 -1
- package/src/serverGroup/configure/wizard/autoScalingPolicy/autoScalingPolicySelector.component.js +1 -4
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { IModalService } from 'angular-ui-bootstrap';
|
|
2
|
+
import type { Application } from '@spinnaker/core';
|
|
3
|
+
export interface IGCEPipelineLoadBalancerModalOptions {
|
|
4
|
+
application: Application;
|
|
5
|
+
loadBalancer: any;
|
|
6
|
+
isNew: boolean;
|
|
7
|
+
$uibModal: IModalService;
|
|
8
|
+
}
|
|
9
|
+
export declare const openGCEPipelineLoadBalancerModal: ({ application, loadBalancer, isNew, $uibModal, }: IGCEPipelineLoadBalancerModalOptions) => PromiseLike<any>;
|
|
@@ -6,6 +6,7 @@ export declare class HttpLoadBalancerTemplate {
|
|
|
6
6
|
region: string;
|
|
7
7
|
loadBalancerType: string;
|
|
8
8
|
certificate: string;
|
|
9
|
+
certificateMap: string;
|
|
9
10
|
defaultService: BackendServiceTemplate;
|
|
10
11
|
hostRules: HostRuleTemplate[];
|
|
11
12
|
listeners: ListenerTemplate[];
|
|
@@ -39,4 +40,6 @@ export declare class ListenerTemplate {
|
|
|
39
40
|
name: string;
|
|
40
41
|
port: number;
|
|
41
42
|
certificate: string | null;
|
|
43
|
+
certificateMap: string | null;
|
|
44
|
+
certificateSource: 'certificate' | 'certificateMap';
|
|
42
45
|
}
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"url": "https://github.com/spinnaker/spinnaker.git"
|
|
6
6
|
},
|
|
7
7
|
"license": "Apache-2.0",
|
|
8
|
-
"version": "2026.
|
|
8
|
+
"version": "2026.1.1",
|
|
9
9
|
"module": "dist/index.js",
|
|
10
10
|
"typings": "dist/index.d.ts",
|
|
11
11
|
"publishConfig": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@uirouter/angularjs": "1.0.26",
|
|
26
26
|
"angular": "1.6.10",
|
|
27
27
|
"angular-ui-bootstrap": "2.5.0",
|
|
28
|
-
"lodash": "4.
|
|
28
|
+
"lodash": "4.18.1",
|
|
29
29
|
"ngimport": "0.6.1",
|
|
30
30
|
"react": "16.14.0",
|
|
31
31
|
"react-select": "1.2.1",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"shx": "0.3.3",
|
|
46
46
|
"typescript": "5.0.4"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "f842aaebe413ea1a14ccec06dd6824e07dc7426b"
|
|
49
49
|
}
|
|
@@ -14,7 +14,8 @@ export interface IGceLoadBalancer extends ILoadBalancer {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface IGceHttpLoadBalancer extends IGceLoadBalancer {
|
|
17
|
-
certificate
|
|
17
|
+
certificate?: string;
|
|
18
|
+
certificateMap?: string;
|
|
18
19
|
defaultService: IGceBackendService;
|
|
19
20
|
detail: string;
|
|
20
21
|
hostRules: IGceHostRule;
|
|
@@ -42,7 +43,8 @@ export interface IGcePathRule {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export interface IGceListener {
|
|
45
|
-
certificate
|
|
46
|
+
certificate?: string | null;
|
|
47
|
+
certificateMap?: string | null;
|
|
46
48
|
name: string;
|
|
47
49
|
port: string;
|
|
48
50
|
ipAddress: string;
|
package/src/gce.module.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { GOOGLE_INSTANCE_GCEINSTANCETYPE_SERVICE } from './instance/gceInstanceT
|
|
|
13
13
|
import { GOOGLE_INSTANCE_GCEMULTIINSTANCETASK_TRANSFORMER } from './instance/gceMultiInstanceTask.transformer';
|
|
14
14
|
import { IAP_INTERCEPTOR } from './interceptors/iap.interceptor';
|
|
15
15
|
import { GCE_LOAD_BALANCER_CHOICE_MODAL } from './loadBalancer/configure/choice/gceLoadBalancerChoice.modal';
|
|
16
|
+
import { openGCEPipelineLoadBalancerModal } from './loadBalancer/configure/choice/gceLoadBalancerPipelineModal';
|
|
16
17
|
import { GOOGLE_LOADBALANCER_CONFIGURE_HTTP_CREATEHTTPLOADBALANCER_CONTROLLER } from './loadBalancer/configure/http/createHttpLoadBalancer.controller';
|
|
17
18
|
import { GCE_INTERNAL_LOAD_BALANCER_CTRL } from './loadBalancer/configure/internal/gceCreateInternalLoadBalancer.controller';
|
|
18
19
|
import { GOOGLE_LOADBALANCER_CONFIGURE_INTERNAL_HTTP_CREATEHTTPLOADBALANCER_CONTROLLER } from './loadBalancer/configure/internalhttp/createInternalHttpLoadBalancer.controller';
|
|
@@ -133,6 +134,7 @@ module(GOOGLE_MODULE, [
|
|
|
133
134
|
detailsController: 'gceLoadBalancerDetailsCtrl',
|
|
134
135
|
createLoadBalancerTemplateUrl: require('./loadBalancer/configure/choice/gceLoadBalancerChoice.modal.html'),
|
|
135
136
|
createLoadBalancerController: 'gceLoadBalancerChoiceCtrl',
|
|
137
|
+
pipelineCreateLoadBalancerModal: openGCEPipelineLoadBalancerModal,
|
|
136
138
|
},
|
|
137
139
|
securityGroup: {
|
|
138
140
|
transformer: 'gceSecurityGroupTransformer',
|
package/src/help/gce.help.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { HelpContentsRegistry } from '@spinnaker/core';
|
|
|
3
3
|
const helpContents: { [key: string]: string } = {
|
|
4
4
|
'gce.httpLoadBalancer.certificate':
|
|
5
5
|
'The name of an SSL certificate. If specified, Spinnaker will create an HTTPS load balancer.',
|
|
6
|
+
'gce.httpLoadBalancer.certificateMap':
|
|
7
|
+
'A Certificate Manager certificate map name. Certificate maps let GCP manage and auto-rotate TLS certificates, replacing individual certificate selection.',
|
|
6
8
|
'gce.httpLoadBalancer.defaultService':
|
|
7
9
|
'A default service handles any requests that do not match a specified host rule or path matching rule.',
|
|
8
10
|
'gce.httpLoadBalancer.externalIP':
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { GCE_LOAD_BALANCER_CHOICE_MODAL } from './gceLoadBalancerChoice.modal';
|
|
2
|
+
|
|
3
|
+
describe('Controller: gceLoadBalancerChoiceCtrl', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
window.module(GCE_LOAD_BALANCER_CHOICE_MODAL);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const buildController = (overrides = {}) => {
|
|
9
|
+
let result;
|
|
10
|
+
window.inject(function ($controller, $rootScope, $q) {
|
|
11
|
+
const $scope = $rootScope.$new();
|
|
12
|
+
const modalInstance = {
|
|
13
|
+
close: jasmine.createSpy('close'),
|
|
14
|
+
dismiss: jasmine.createSpy('dismiss'),
|
|
15
|
+
};
|
|
16
|
+
const wizardResult = $q.when('wizard');
|
|
17
|
+
const $uibModal = {
|
|
18
|
+
open: jasmine.createSpy('open').and.returnValue({ result: wizardResult }),
|
|
19
|
+
};
|
|
20
|
+
const ctrl = $controller('gceLoadBalancerChoiceCtrl', {
|
|
21
|
+
$scope,
|
|
22
|
+
$uibModal,
|
|
23
|
+
$uibModalInstance: modalInstance,
|
|
24
|
+
application: { name: 'app' },
|
|
25
|
+
loadBalancerTypeToWizardMap: {
|
|
26
|
+
NETWORK: { label: 'Network', createTemplateUrl: 'template', controller: 'ctrl' },
|
|
27
|
+
},
|
|
28
|
+
forPipelineConfig: true,
|
|
29
|
+
...overrides,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
result = { ctrl, $uibModal, modalInstance, wizardResult };
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
it('closes with wizard config in pipeline mode', function () {
|
|
39
|
+
const { ctrl, $uibModal, modalInstance } = buildController();
|
|
40
|
+
|
|
41
|
+
ctrl.choose('Network');
|
|
42
|
+
|
|
43
|
+
expect($uibModal.open).not.toHaveBeenCalled();
|
|
44
|
+
expect(modalInstance.close).toHaveBeenCalledWith(jasmine.objectContaining({ controller: 'ctrl' }));
|
|
45
|
+
expect(modalInstance.dismiss).not.toHaveBeenCalled();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('opens wizard in non-pipeline mode', function () {
|
|
49
|
+
const { ctrl, $uibModal, modalInstance, wizardResult } = buildController({ forPipelineConfig: false });
|
|
50
|
+
|
|
51
|
+
ctrl.choose('Network');
|
|
52
|
+
|
|
53
|
+
expect($uibModal.open).toHaveBeenCalled();
|
|
54
|
+
expect(modalInstance.close).toHaveBeenCalledWith(wizardResult);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('dismisses when choice has no wizard config', function () {
|
|
58
|
+
const { ctrl, $uibModal, modalInstance } = buildController();
|
|
59
|
+
|
|
60
|
+
ctrl.choose('Missing');
|
|
61
|
+
|
|
62
|
+
expect($uibModal.open).not.toHaveBeenCalled();
|
|
63
|
+
expect(modalInstance.close).not.toHaveBeenCalled();
|
|
64
|
+
expect(modalInstance.dismiss).toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -12,12 +12,19 @@ class GceLoadBalancerChoiceCtrl implements IController {
|
|
|
12
12
|
public choices: string[];
|
|
13
13
|
public choice = 'Network';
|
|
14
14
|
|
|
15
|
-
public static $inject = [
|
|
15
|
+
public static $inject = [
|
|
16
|
+
'$uibModal',
|
|
17
|
+
'$uibModalInstance',
|
|
18
|
+
'application',
|
|
19
|
+
'loadBalancerTypeToWizardMap',
|
|
20
|
+
'forPipelineConfig',
|
|
21
|
+
];
|
|
16
22
|
constructor(
|
|
17
23
|
public $uibModal: IModalService,
|
|
18
24
|
public $uibModalInstance: IModalInstanceService,
|
|
19
25
|
private application: Application,
|
|
20
26
|
private loadBalancerTypeToWizardMap: IGceLoadBalancerToWizardMap,
|
|
27
|
+
private forPipelineConfig: boolean,
|
|
21
28
|
) {}
|
|
22
29
|
|
|
23
30
|
public $onInit(): void {
|
|
@@ -26,8 +33,15 @@ class GceLoadBalancerChoiceCtrl implements IController {
|
|
|
26
33
|
|
|
27
34
|
public choose(choice: string): void {
|
|
28
35
|
const wizard = find(this.loadBalancerTypeToWizardMap, (wizardConfig) => wizardConfig.label === choice);
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
if (!wizard) {
|
|
37
|
+
this.$uibModalInstance.dismiss('No wizard config for choice');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (this.forPipelineConfig) {
|
|
41
|
+
this.$uibModalInstance.close(wizard);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const wizardResult = this.$uibModal.open({
|
|
31
45
|
templateUrl: wizard.createTemplateUrl,
|
|
32
46
|
controller: `${wizard.controller} as ctrl`,
|
|
33
47
|
size: 'lg',
|
|
@@ -35,9 +49,10 @@ class GceLoadBalancerChoiceCtrl implements IController {
|
|
|
35
49
|
application: () => this.application,
|
|
36
50
|
loadBalancer: (): null => null,
|
|
37
51
|
isNew: () => true,
|
|
38
|
-
forPipelineConfig: () =>
|
|
52
|
+
forPipelineConfig: () => this.forPipelineConfig,
|
|
39
53
|
},
|
|
40
|
-
});
|
|
54
|
+
}).result;
|
|
55
|
+
this.$uibModalInstance.close(wizardResult);
|
|
41
56
|
}
|
|
42
57
|
}
|
|
43
58
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { IModalService } from 'angular-ui-bootstrap';
|
|
2
|
+
|
|
3
|
+
import type { Application } from '@spinnaker/core';
|
|
4
|
+
|
|
5
|
+
import type { IGceLoadBalancerWizardConfig } from './loadBalancerTypeToWizardMap.constant';
|
|
6
|
+
|
|
7
|
+
export interface IGCEPipelineLoadBalancerModalOptions {
|
|
8
|
+
application: Application;
|
|
9
|
+
loadBalancer: any;
|
|
10
|
+
isNew: boolean;
|
|
11
|
+
$uibModal: IModalService;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const openGCEPipelineLoadBalancerModal = ({
|
|
15
|
+
application,
|
|
16
|
+
loadBalancer,
|
|
17
|
+
isNew,
|
|
18
|
+
$uibModal,
|
|
19
|
+
}: IGCEPipelineLoadBalancerModalOptions): PromiseLike<any> => {
|
|
20
|
+
return $uibModal
|
|
21
|
+
.open({
|
|
22
|
+
templateUrl: require('./gceLoadBalancerChoice.modal.html'),
|
|
23
|
+
controller: 'gceLoadBalancerChoiceCtrl as ctrl',
|
|
24
|
+
size: 'lg',
|
|
25
|
+
resolve: {
|
|
26
|
+
application: () => application,
|
|
27
|
+
forPipelineConfig: () => true,
|
|
28
|
+
},
|
|
29
|
+
})
|
|
30
|
+
.result.then((wizardConfig: IGceLoadBalancerWizardConfig) => {
|
|
31
|
+
if (!wizardConfig) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const templateUrl = isNew ? wizardConfig.createTemplateUrl : wizardConfig.editTemplateUrl;
|
|
35
|
+
return $uibModal.open({
|
|
36
|
+
templateUrl,
|
|
37
|
+
controller: `${wizardConfig.controller} as ctrl`,
|
|
38
|
+
size: 'lg',
|
|
39
|
+
resolve: {
|
|
40
|
+
application: () => application,
|
|
41
|
+
loadBalancer: () => loadBalancer,
|
|
42
|
+
isNew: () => isNew,
|
|
43
|
+
forPipelineConfig: () => true,
|
|
44
|
+
},
|
|
45
|
+
}).result;
|
|
46
|
+
});
|
|
47
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { ApplicationModelBuilder } from '@spinnaker/core';
|
|
2
|
+
import { GCE_INTERNAL_LOAD_BALANCER_CTRL } from './internal/gceCreateInternalLoadBalancer.controller';
|
|
3
|
+
import { GCE_SSL_LOAD_BALANCER_CTRL } from './ssl/gceCreateSslLoadBalancer.controller';
|
|
4
|
+
import { GCE_TCP_LOAD_BALANCER_CTRL } from './tcp/gceCreateTcpLoadBalancer.controller';
|
|
5
|
+
|
|
6
|
+
function buildApp() {
|
|
7
|
+
return ApplicationModelBuilder.createApplicationForTests('app', {
|
|
8
|
+
key: 'loadBalancers',
|
|
9
|
+
lazy: true,
|
|
10
|
+
defaultData: [],
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('GCE L4 load balancer controllers (pipeline mode)', function () {
|
|
15
|
+
describe('gceSslLoadBalancerCtrl', function () {
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
window.module(GCE_SSL_LOAD_BALANCER_CTRL);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it(
|
|
21
|
+
'closes with a pipeline command',
|
|
22
|
+
window.inject(function ($controller, $rootScope) {
|
|
23
|
+
const modalInstance = { close: jasmine.createSpy('close'), dismiss: jasmine.createSpy('dismiss') };
|
|
24
|
+
const loadBalancer = {
|
|
25
|
+
loadBalancerName: 'ssl-lb',
|
|
26
|
+
backendService: { healthCheck: { healthCheckType: 'TCP' } },
|
|
27
|
+
instances: [],
|
|
28
|
+
credentials: 'test',
|
|
29
|
+
region: 'global',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const ctrl = $controller('gceSslLoadBalancerCtrl', {
|
|
33
|
+
$scope: $rootScope.$new(),
|
|
34
|
+
application: buildApp(),
|
|
35
|
+
$uibModalInstance: modalInstance,
|
|
36
|
+
loadBalancer,
|
|
37
|
+
gceCommonLoadBalancerCommandBuilder: {},
|
|
38
|
+
isNew: true,
|
|
39
|
+
forPipelineConfig: true,
|
|
40
|
+
wizardSubFormValidation: {},
|
|
41
|
+
$state: {},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
ctrl.submit();
|
|
45
|
+
|
|
46
|
+
expect(modalInstance.close).toHaveBeenCalledWith(
|
|
47
|
+
jasmine.objectContaining({
|
|
48
|
+
loadBalancerName: 'ssl-lb',
|
|
49
|
+
cloudProvider: 'gce',
|
|
50
|
+
healthCheck: {},
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('gceTcpLoadBalancerCtrl', function () {
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
window.module(GCE_TCP_LOAD_BALANCER_CTRL);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it(
|
|
63
|
+
'closes with a pipeline command',
|
|
64
|
+
window.inject(function ($controller, $rootScope) {
|
|
65
|
+
const modalInstance = { close: jasmine.createSpy('close'), dismiss: jasmine.createSpy('dismiss') };
|
|
66
|
+
const loadBalancer = {
|
|
67
|
+
loadBalancerName: 'tcp-lb',
|
|
68
|
+
backendService: { healthCheck: { healthCheckType: 'TCP' } },
|
|
69
|
+
instances: [],
|
|
70
|
+
credentials: 'test',
|
|
71
|
+
region: 'global',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const ctrl = $controller('gceTcpLoadBalancerCtrl', {
|
|
75
|
+
$scope: $rootScope.$new(),
|
|
76
|
+
application: buildApp(),
|
|
77
|
+
$uibModalInstance: modalInstance,
|
|
78
|
+
loadBalancer,
|
|
79
|
+
gceCommonLoadBalancerCommandBuilder: {},
|
|
80
|
+
isNew: true,
|
|
81
|
+
forPipelineConfig: true,
|
|
82
|
+
wizardSubFormValidation: {},
|
|
83
|
+
$state: {},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
ctrl.submit();
|
|
87
|
+
|
|
88
|
+
expect(modalInstance.close).toHaveBeenCalledWith(
|
|
89
|
+
jasmine.objectContaining({
|
|
90
|
+
loadBalancerName: 'tcp-lb',
|
|
91
|
+
cloudProvider: 'gce',
|
|
92
|
+
healthCheck: {},
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('gceInternalLoadBalancerCtrl', function () {
|
|
100
|
+
beforeEach(() => {
|
|
101
|
+
window.module(GCE_INTERNAL_LOAD_BALANCER_CTRL);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it(
|
|
105
|
+
'closes with a pipeline command',
|
|
106
|
+
window.inject(function ($controller, $rootScope) {
|
|
107
|
+
const modalInstance = { close: jasmine.createSpy('close'), dismiss: jasmine.createSpy('dismiss') };
|
|
108
|
+
const loadBalancer = {
|
|
109
|
+
loadBalancerName: 'internal-lb',
|
|
110
|
+
backendService: { healthCheck: { healthCheckType: 'TCP' } },
|
|
111
|
+
instances: [],
|
|
112
|
+
ports: '80, 8080',
|
|
113
|
+
credentials: 'test',
|
|
114
|
+
region: 'us-west-2',
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const ctrl = $controller('gceInternalLoadBalancerCtrl', {
|
|
118
|
+
$scope: $rootScope.$new(),
|
|
119
|
+
application: buildApp(),
|
|
120
|
+
$uibModalInstance: modalInstance,
|
|
121
|
+
loadBalancer,
|
|
122
|
+
gceCommonLoadBalancerCommandBuilder: {},
|
|
123
|
+
isNew: true,
|
|
124
|
+
forPipelineConfig: true,
|
|
125
|
+
wizardSubFormValidation: {},
|
|
126
|
+
gceXpnNamingService: {},
|
|
127
|
+
$state: {},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
ctrl.submit();
|
|
131
|
+
|
|
132
|
+
expect(modalInstance.close).toHaveBeenCalledWith(
|
|
133
|
+
jasmine.objectContaining({
|
|
134
|
+
loadBalancerName: 'internal-lb',
|
|
135
|
+
cloudProvider: 'gce',
|
|
136
|
+
healthCheck: {},
|
|
137
|
+
ports: ['80', '8080'],
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -43,6 +43,7 @@ module(GOOGLE_LOADBALANCER_CONFIGURE_HTTP_CREATEHTTPLOADBALANCER_CONTROLLER, [
|
|
|
43
43
|
'application',
|
|
44
44
|
'loadBalancer',
|
|
45
45
|
'isNew',
|
|
46
|
+
'forPipelineConfig',
|
|
46
47
|
'gceHttpLoadBalancerWriter',
|
|
47
48
|
'$state',
|
|
48
49
|
'wizardSubFormValidation',
|
|
@@ -55,6 +56,7 @@ module(GOOGLE_LOADBALANCER_CONFIGURE_HTTP_CREATEHTTPLOADBALANCER_CONTROLLER, [
|
|
|
55
56
|
application,
|
|
56
57
|
loadBalancer,
|
|
57
58
|
isNew,
|
|
59
|
+
forPipelineConfig,
|
|
58
60
|
gceHttpLoadBalancerWriter,
|
|
59
61
|
$state,
|
|
60
62
|
wizardSubFormValidation,
|
|
@@ -129,6 +131,26 @@ module(GOOGLE_LOADBALANCER_CONFIGURE_HTTP_CREATEHTTPLOADBALANCER_CONTROLLER, [
|
|
|
129
131
|
const serializedCommands = gceHttpLoadBalancerTransformer.serialize(this.command, loadBalancer);
|
|
130
132
|
const descriptor = this.isNew ? 'Create' : 'Update';
|
|
131
133
|
|
|
134
|
+
if (forPipelineConfig) {
|
|
135
|
+
const pipelineCommands = serializedCommands.map((command) => ({
|
|
136
|
+
...command,
|
|
137
|
+
cloudProvider: 'gce',
|
|
138
|
+
loadBalancerName: command.name,
|
|
139
|
+
listeners: [
|
|
140
|
+
{
|
|
141
|
+
name: command.name,
|
|
142
|
+
port: command.portRange,
|
|
143
|
+
certificate: command.certificate || null,
|
|
144
|
+
certificateMap: command.certificateMap || null,
|
|
145
|
+
ipAddress: command.ipAddress,
|
|
146
|
+
subnet: command.subnet,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
}));
|
|
150
|
+
$uibModalInstance.close(pipelineCommands);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
132
154
|
this.taskMonitor.submit(() =>
|
|
133
155
|
gceHttpLoadBalancerWriter.upsertLoadBalancers(serializedCommands, application, descriptor),
|
|
134
156
|
);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ApplicationModelBuilder } from '@spinnaker/core';
|
|
2
|
+
|
|
3
|
+
describe('Controller: gceCreateHttpLoadBalancerCtrl (pipeline mode)', function () {
|
|
4
|
+
beforeEach(function () {
|
|
5
|
+
this.serializedCommands = [
|
|
6
|
+
{
|
|
7
|
+
name: 'app-http',
|
|
8
|
+
portRange: '443',
|
|
9
|
+
certificate: null,
|
|
10
|
+
certificateMap: 'map',
|
|
11
|
+
ipAddress: null,
|
|
12
|
+
subnet: null,
|
|
13
|
+
urlMapName: 'app-http',
|
|
14
|
+
credentials: 'test',
|
|
15
|
+
region: 'global',
|
|
16
|
+
loadBalancerType: 'HTTP',
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
this.transformer = {
|
|
20
|
+
serialize: jasmine.createSpy('serialize').and.returnValue(this.serializedCommands),
|
|
21
|
+
};
|
|
22
|
+
this.writer = {
|
|
23
|
+
upsertLoadBalancers: jasmine.createSpy('upsertLoadBalancers'),
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
beforeEach(function () {
|
|
28
|
+
window.module(require('./createHttpLoadBalancer.controller').name, ($provide) => {
|
|
29
|
+
$provide.factory('gceHttpLoadBalancerCommandBuilder', ($q) => ({
|
|
30
|
+
buildCommand: () =>
|
|
31
|
+
$q.when({
|
|
32
|
+
loadBalancer: {
|
|
33
|
+
listeners: [{ name: 'app-http', port: 443 }],
|
|
34
|
+
credentials: 'test',
|
|
35
|
+
region: 'global',
|
|
36
|
+
urlMapName: 'app-http',
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
}));
|
|
40
|
+
const wizardSubFormValidation = {
|
|
41
|
+
config: () => wizardSubFormValidation,
|
|
42
|
+
register: () => wizardSubFormValidation,
|
|
43
|
+
};
|
|
44
|
+
$provide.value('wizardSubFormValidation', wizardSubFormValidation);
|
|
45
|
+
$provide.value('gceHttpLoadBalancerTransformer', this.transformer);
|
|
46
|
+
$provide.value('gceHttpLoadBalancerWriter', this.writer);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
beforeEach(
|
|
51
|
+
window.inject(function ($controller, $rootScope) {
|
|
52
|
+
this.$scope = $rootScope.$new();
|
|
53
|
+
this.modalInstance = {
|
|
54
|
+
close: jasmine.createSpy('close'),
|
|
55
|
+
dismiss: jasmine.createSpy('dismiss'),
|
|
56
|
+
result: { then: () => {} },
|
|
57
|
+
};
|
|
58
|
+
const app = ApplicationModelBuilder.createApplicationForTests('app', {
|
|
59
|
+
key: 'loadBalancers',
|
|
60
|
+
lazy: true,
|
|
61
|
+
defaultData: [],
|
|
62
|
+
});
|
|
63
|
+
this.ctrl = $controller('gceCreateHttpLoadBalancerCtrl', {
|
|
64
|
+
$scope: this.$scope,
|
|
65
|
+
$uibModal: {},
|
|
66
|
+
$uibModalInstance: this.modalInstance,
|
|
67
|
+
application: app,
|
|
68
|
+
loadBalancer: null,
|
|
69
|
+
isNew: true,
|
|
70
|
+
forPipelineConfig: true,
|
|
71
|
+
$state: {},
|
|
72
|
+
});
|
|
73
|
+
$rootScope.$digest();
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
it('returns pipeline commands without submitting', function () {
|
|
78
|
+
this.ctrl.submit();
|
|
79
|
+
|
|
80
|
+
expect(this.writer.upsertLoadBalancers).not.toHaveBeenCalled();
|
|
81
|
+
expect(this.modalInstance.close).toHaveBeenCalledWith([
|
|
82
|
+
jasmine.objectContaining({
|
|
83
|
+
name: 'app-http',
|
|
84
|
+
loadBalancerName: 'app-http',
|
|
85
|
+
cloudProvider: 'gce',
|
|
86
|
+
listeners: [
|
|
87
|
+
jasmine.objectContaining({
|
|
88
|
+
name: 'app-http',
|
|
89
|
+
port: '443',
|
|
90
|
+
certificateMap: 'map',
|
|
91
|
+
}),
|
|
92
|
+
],
|
|
93
|
+
}),
|
|
94
|
+
]);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
<ui-select
|
|
113
113
|
ng-model="$ctrl.listener.port"
|
|
114
114
|
ng-disabled="$ctrl.listener.created"
|
|
115
|
+
ng-change="$ctrl.onPortChanged($ctrl.listener)"
|
|
115
116
|
required
|
|
116
117
|
class="form-control input-sm"
|
|
117
118
|
>
|
|
@@ -123,31 +124,80 @@
|
|
|
123
124
|
</ui-select-choices>
|
|
124
125
|
</ui-select>
|
|
125
126
|
</div>
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
</div>
|
|
128
|
+
<!-- Certificate controls on a separate row to avoid exceeding Bootstrap's 12-column
|
|
129
|
+
grid when cert-source radios are visible (Port 2+3 + CertSrc 2+3 + Cert 2+3+1 = 16). -->
|
|
130
|
+
<div class="form-group" ng-if="$ctrl.isHttps($ctrl.listener.port)">
|
|
131
|
+
<div class="col-md-2 sm-label-right" ng-if="$ctrl.supportsCertificateMap()">Certificate Source</div>
|
|
132
|
+
<div class="col-md-3" ng-if="$ctrl.supportsCertificateMap()">
|
|
133
|
+
<label class="radio-inline">
|
|
134
|
+
<input
|
|
135
|
+
type="radio"
|
|
136
|
+
ng-model="$ctrl.listener.certificateSource"
|
|
137
|
+
value="certificate"
|
|
138
|
+
ng-change="$ctrl.onCertificateSourceChanged($ctrl.listener)"
|
|
139
|
+
/>
|
|
128
140
|
Certificate
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
ng-model="$ctrl.listener.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
141
|
+
</label>
|
|
142
|
+
<label class="radio-inline">
|
|
143
|
+
<input
|
|
144
|
+
type="radio"
|
|
145
|
+
ng-model="$ctrl.listener.certificateSource"
|
|
146
|
+
value="certificateMap"
|
|
147
|
+
ng-change="$ctrl.onCertificateSourceChanged($ctrl.listener)"
|
|
148
|
+
/>
|
|
149
|
+
Certificate Map
|
|
150
|
+
</label>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="col-md-2 sm-label-right">
|
|
153
|
+
<span ng-if="$ctrl.listener.certificateSource !== 'certificateMap'">Certificate</span>
|
|
154
|
+
<span ng-if="$ctrl.listener.certificateSource === 'certificateMap'">Certificate Map</span>
|
|
155
|
+
<help-field
|
|
156
|
+
ng-if="$ctrl.listener.certificateSource !== 'certificateMap'"
|
|
157
|
+
key="gce.httpLoadBalancer.certificate"
|
|
158
|
+
class="help-field-absolute"
|
|
159
|
+
></help-field>
|
|
160
|
+
<help-field
|
|
161
|
+
ng-if="$ctrl.listener.certificateSource === 'certificateMap'"
|
|
162
|
+
key="gce.httpLoadBalancer.certificateMap"
|
|
163
|
+
class="help-field-absolute"
|
|
164
|
+
></help-field>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="col-md-3" ng-if="$ctrl.listener.certificateSource !== 'certificateMap'">
|
|
167
|
+
<ui-select
|
|
168
|
+
ng-model="$ctrl.listener.certificate"
|
|
169
|
+
ng-change="$ctrl.onCertificateSelected($ctrl.listener)"
|
|
170
|
+
ng-required="$ctrl.isHttps($ctrl.listener.port) && $ctrl.listener.certificateSource !== 'certificateMap'"
|
|
171
|
+
class="form-control input-sm"
|
|
172
|
+
>
|
|
173
|
+
<ui-select-match allow-clear placeholder="Select...">{{ $select.selected }}</ui-select-match>
|
|
174
|
+
<ui-select-choices repeat="certificate in $ctrl.getCertificates() | filter: $select.search">
|
|
175
|
+
<span ng-bind-html="certificate | highlight: $select.search"></span>
|
|
176
|
+
</ui-select-choices>
|
|
177
|
+
</ui-select>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="col-md-3" ng-if="$ctrl.listener.certificateSource === 'certificateMap'">
|
|
180
|
+
<input
|
|
181
|
+
type="text"
|
|
182
|
+
class="form-control input-sm"
|
|
183
|
+
name="certificateMap"
|
|
184
|
+
ng-model="$ctrl.listener.certificateMap"
|
|
185
|
+
ng-change="$ctrl.onCertificateMapChanged($ctrl.listener)"
|
|
186
|
+
ng-required="$ctrl.isHttps($ctrl.listener.port) && $ctrl.listener.certificateSource === 'certificateMap'"
|
|
187
|
+
ng-pattern="$ctrl.certificateMapPattern"
|
|
188
|
+
placeholder="certificate-map-name"
|
|
189
|
+
/>
|
|
190
|
+
</div>
|
|
191
|
+
<div
|
|
192
|
+
class="col-md-1"
|
|
193
|
+
style="padding-left: 0; margin-top: 4px"
|
|
194
|
+
ng-if="$ctrl.listener.certificateSource !== 'certificateMap'"
|
|
195
|
+
>
|
|
196
|
+
<gce-cache-refresh
|
|
197
|
+
cache-key="certificates"
|
|
198
|
+
render-compact="true"
|
|
199
|
+
on-refresh="$ctrl.command.onCertificateRefresh($ctrl.command)"
|
|
200
|
+
></gce-cache-refresh>
|
|
151
201
|
</div>
|
|
152
202
|
</div>
|
|
153
203
|
|
|
@@ -184,6 +234,11 @@
|
|
|
184
234
|
message="Detail can only contain lowercase letters, numbers, and dashes(-)."
|
|
185
235
|
></validation-error>
|
|
186
236
|
</div>
|
|
237
|
+
<div class="col-md-7 col-md-offset-2" ng-if="listener.certificateMap.$error.pattern">
|
|
238
|
+
<validation-error
|
|
239
|
+
message="Certificate map can only contain lowercase letters, numbers, and dashes(-), and must start with a letter."
|
|
240
|
+
></validation-error>
|
|
241
|
+
</div>
|
|
187
242
|
</div>
|
|
188
243
|
</div>
|
|
189
244
|
</ng-form>
|