@spinnaker/google 2026.3.2 → 2026.4.0
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/autoscalingPolicy/GceAutoScalingFieldLayout.d.ts +2 -2
- package/dist/domain/loadBalancer.d.ts +6 -3
- package/dist/image/ImageSelect.d.ts +1 -1
- package/dist/index.js +159 -1
- package/dist/index.js.map +1 -1
- package/dist/loadBalancer/configure/common/gceLoadBalancerModels.d.ts +13 -2
- package/dist/loadBalancer/configure/http/GceHttpLoadBalancerEditor.d.ts +3 -0
- package/dist/loadBalancer/configure/http/GceHttpLoadBalancerModal.d.ts +1 -1
- package/dist/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerEditor.d.ts +30 -0
- package/dist/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerModal.d.ts +53 -0
- package/dist/loadBalancer/httpLoadBalancerUtils.service.d.ts +3 -1
- package/dist/serverGroup/configure/wizard/GceCloneServerGroupModal.d.ts +3 -0
- package/dist/serverGroup/configure/wizard/GceServerGroupWizard.types.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/customInstance/CustomInstanceConfigurer.d.ts +1 -1
- package/dist/serverGroup/configure/wizard/customInstance/GceCustomInstanceBuilder.d.ts +2 -2
- package/dist/serverGroup/configure/wizard/pages/AdvancedSettings.d.ts +3 -0
- package/dist/serverGroup/configure/wizard/pages/GceServerGroupLoadBalancers.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/pages/Policies.d.ts +21 -3
- package/dist/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/pages/ServerGroupCapacity.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/pages/ServerGroupImageSettings.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/pages/ServerGroupInstanceType.d.ts +1 -0
- package/dist/serverGroup/configure/wizard/zones/GceInstanceFlexibilityConfigurer.d.ts +2 -2
- package/dist/serverGroup/details/GceInstanceFlexibilityPolicyDetails.d.ts +2 -2
- package/dist/serverGroup/details/stateful/MarkDiskStatefulButton.d.ts +2 -2
- package/dist/serverGroup/details/stateful/UpdateBootImageButton.d.ts +2 -2
- package/package.json +5 -5
- package/src/autoHealingPolicy/GceAutoHealingPolicyEditor.spec.tsx +68 -52
- package/src/autoscalingPolicy/GceAutoscalingPolicyEditor.spec.tsx +43 -50
- package/src/autoscalingPolicy/GceAutoscalingPolicyEditor.tsx +1 -1
- package/src/autoscalingPolicy/GceAutoscalingPolicyWriter.spec.ts +1 -1
- package/src/domain/loadBalancer.ts +6 -3
- package/src/gce.module.spec.ts +132 -153
- package/src/instance/details/GceInstanceDetails.spec.tsx +86 -83
- package/src/loadBalancer/configure/choice/GceLoadBalancerChoiceModal.spec.tsx +162 -121
- package/src/loadBalancer/configure/choice/GceLoadBalancerChoiceModal.tsx +13 -1
- package/src/loadBalancer/configure/common/GceProxyLoadBalancerEditor.spec.tsx +47 -30
- package/src/loadBalancer/configure/common/GceProxyLoadBalancerEditor.tsx +2 -1
- package/src/loadBalancer/configure/common/GceProxyLoadBalancerModal.spec.tsx +85 -57
- package/src/loadBalancer/configure/common/gceLoadBalancerData.spec.ts +17 -16
- package/src/loadBalancer/configure/common/gceLoadBalancerModels.spec.ts +230 -5
- package/src/loadBalancer/configure/common/gceLoadBalancerModels.ts +186 -22
- package/src/loadBalancer/configure/common/gceLoadBalancerSubmission.spec.ts +298 -2
- package/src/loadBalancer/configure/common/gceLoadBalancerSubmission.ts +35 -5
- package/src/loadBalancer/configure/common/useGceLoadBalancerData.spec.tsx +37 -46
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerEditor.spec.tsx +399 -58
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerEditor.tsx +148 -41
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerListenerEditor.spec.tsx +169 -36
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerListenerEditor.tsx +80 -12
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerModal.spec.tsx +224 -124
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerModal.tsx +8 -2
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerResourceEditors.spec.tsx +57 -22
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerResourceEditors.tsx +21 -0
- package/src/loadBalancer/configure/http/GceHttpLoadBalancerRoutingEditor.spec.tsx +24 -20
- package/src/loadBalancer/configure/network/GceNetworkLoadBalancerEditor.spec.tsx +41 -32
- package/src/loadBalancer/configure/network/GceNetworkLoadBalancerModal.spec.tsx +35 -30
- package/src/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerEditor.spec.tsx +443 -0
- package/src/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerEditor.tsx +401 -0
- package/src/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerModal.spec.tsx +330 -0
- package/src/loadBalancer/configure/network/GceRegionalExternalNetworkLoadBalancerModal.tsx +348 -0
- package/src/loadBalancer/details/gceLoadBalancerDetails.spec.tsx +439 -35
- package/src/loadBalancer/details/gceLoadBalancerDetails.tsx +104 -21
- package/src/loadBalancer/httpLoadBalancerUtils.service.spec.ts +126 -0
- package/src/loadBalancer/httpLoadBalancerUtils.service.ts +27 -11
- package/src/loadBalancer/loadBalancer.setTransformer.spec.ts +165 -0
- package/src/loadBalancer/loadBalancer.setTransformer.ts +15 -2
- package/src/pipeline/stages/googleStageConfig.spec.js +22 -16
- package/src/securityGroup/configure/GceSecurityGroupModal.spec.tsx +177 -193
- package/src/securityGroup/configure/GceSecurityGroupModal.tsx +14 -2
- package/src/securityGroup/details/GceSecurityGroupDetails.spec.tsx +69 -78
- package/src/serverGroup/configure/serverGroupCommandBuilder.service.spec.js +35 -15
- package/src/serverGroup/configure/serverGroupConfiguration.service.js +7 -1
- package/src/serverGroup/configure/serverGroupConfiguration.service.spec.js +74 -4
- package/src/serverGroup/configure/wizard/GceCloneServerGroupModal.spec.tsx +593 -855
- package/src/serverGroup/configure/wizard/GceCloneServerGroupModal.tsx +120 -38
- package/src/serverGroup/configure/wizard/GceServerGroupWizard.types.ts +1 -0
- package/src/serverGroup/configure/wizard/GceServerGroupWizardPage.spec.tsx +53 -50
- package/src/serverGroup/configure/wizard/customInstance/GceCustomInstanceBuilder.spec.tsx +65 -49
- package/src/serverGroup/configure/wizard/hiddenMetadataKeys.value.js +1 -0
- package/src/serverGroup/configure/wizard/pages/AdvancedSettings.spec.tsx +101 -108
- package/src/serverGroup/configure/wizard/pages/AdvancedSettings.tsx +32 -28
- package/src/serverGroup/configure/wizard/pages/GceServerGroupFirewalls.spec.tsx +60 -59
- package/src/serverGroup/configure/wizard/pages/GceServerGroupLoadBalancers.spec.tsx +240 -114
- package/src/serverGroup/configure/wizard/pages/GceServerGroupLoadBalancers.tsx +152 -63
- package/src/serverGroup/configure/wizard/pages/Policies.spec.tsx +93 -48
- package/src/serverGroup/configure/wizard/pages/Policies.tsx +80 -55
- package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.spec.tsx +123 -94
- package/src/serverGroup/configure/wizard/pages/ServerGroupBasicSettings.tsx +24 -18
- package/src/serverGroup/configure/wizard/pages/ServerGroupCapacity.spec.tsx +152 -133
- package/src/serverGroup/configure/wizard/pages/ServerGroupCapacity.tsx +40 -37
- package/src/serverGroup/configure/wizard/pages/ServerGroupImageSettings.spec.tsx +66 -60
- package/src/serverGroup/configure/wizard/pages/ServerGroupImageSettings.tsx +8 -2
- package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.spec.tsx +131 -101
- package/src/serverGroup/configure/wizard/pages/ServerGroupInstanceType.tsx +29 -28
- package/src/serverGroup/configure/wizard/zones/GceInstanceFlexibilityConfigurer.spec.tsx +104 -81
- package/src/serverGroup/details/GceInstanceFlexibilityPolicyDetails.spec.tsx +10 -11
- package/src/serverGroup/details/autoHealingPolicy/GceAutoHealingPolicyDetails.spec.tsx +51 -59
- package/src/serverGroup/details/autoHealingPolicy/modal/GceUpsertAutoHealingPolicyModal.spec.tsx +3 -3
- package/src/serverGroup/details/autoscalingPolicy/GceAutoscalingPolicyDetails.spec.tsx +49 -58
- package/src/serverGroup/details/autoscalingPolicy/modal/GceUpsertAutoscalingPolicyModal.spec.tsx +3 -3
- package/src/serverGroup/details/gceServerGroupDetails.spec.tsx +162 -141
- package/src/serverGroup/details/resize/GceResizeServerGroupModal.spec.tsx +102 -86
- package/src/serverGroup/details/resize/GceResizeServerGroupModal.tsx +4 -0
- package/src/serverGroup/details/rollback/GceRollbackServerGroupModal.spec.tsx +40 -31
- package/src/serverGroup/serverGroup.transformer.js +1 -0
- package/src/serverGroup/serverGroup.transformer.spec.js +48 -0
- package/src/validation/ApplicationNameValidator.spec.ts +1 -1
- /package/dist/{gce.logo2ae85d8704b1d6b8.png → gce.logoChI3w_A5.png} +0 -0
|
@@ -1,20 +1,28 @@
|
|
|
1
|
+
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import { shallow } from 'enzyme';
|
|
3
3
|
|
|
4
4
|
import { ReactModal } from '@spinnaker/core';
|
|
5
5
|
|
|
6
|
-
import { GceProxyLoadBalancerModal } from '../common/GceProxyLoadBalancerModal';
|
|
7
|
-
import { GceHttpLoadBalancerModal } from '../http/GceHttpLoadBalancerModal';
|
|
8
|
-
import { GceNetworkLoadBalancerModal } from '../network/GceNetworkLoadBalancerModal';
|
|
9
6
|
import {
|
|
10
7
|
GCE_LOAD_BALANCER_CHOICES,
|
|
11
8
|
GceLoadBalancerChoiceModal,
|
|
12
9
|
getGceLoadBalancerModal,
|
|
13
10
|
} from './GceLoadBalancerChoiceModal';
|
|
11
|
+
import { GceProxyLoadBalancerModal } from '../common/GceProxyLoadBalancerModal';
|
|
12
|
+
import { GceHttpLoadBalancerModal } from '../http/GceHttpLoadBalancerModal';
|
|
13
|
+
import { GceNetworkLoadBalancerModal } from '../network/GceNetworkLoadBalancerModal';
|
|
14
|
+
import { GceRegionalExternalNetworkLoadBalancerModal } from '../network/GceRegionalExternalNetworkLoadBalancerModal';
|
|
14
15
|
|
|
15
16
|
describe('GceLoadBalancerChoiceModal', () => {
|
|
16
17
|
const application = { name: 'fnord' } as any;
|
|
17
18
|
|
|
19
|
+
const spyOnAllGceLoadBalancerModals = (impl: (props: any) => Promise<any> = () => Promise.resolve() as any): void => {
|
|
20
|
+
vi.spyOn(GceNetworkLoadBalancerModal, 'show').mockImplementation(impl);
|
|
21
|
+
vi.spyOn(GceProxyLoadBalancerModal, 'show').mockImplementation(impl);
|
|
22
|
+
vi.spyOn(GceHttpLoadBalancerModal, 'show').mockImplementation(impl);
|
|
23
|
+
vi.spyOn(GceRegionalExternalNetworkLoadBalancerModal, 'show').mockImplementation(impl);
|
|
24
|
+
};
|
|
25
|
+
|
|
18
26
|
it('routes every exposed GCE load balancer type to its React modal', () => {
|
|
19
27
|
expect(GCE_LOAD_BALANCER_CHOICES.map(({ type }) => type)).toEqual([
|
|
20
28
|
'NETWORK',
|
|
@@ -23,14 +31,17 @@ describe('GceLoadBalancerChoiceModal', () => {
|
|
|
23
31
|
'SSL',
|
|
24
32
|
'HTTP',
|
|
25
33
|
'INTERNAL_MANAGED',
|
|
34
|
+
'EXTERNAL_MANAGED',
|
|
35
|
+
'REGIONAL_EXTERNAL_NETWORK',
|
|
26
36
|
]);
|
|
27
37
|
expect(getGceLoadBalancerModal('NETWORK')).toBe(GceNetworkLoadBalancerModal);
|
|
28
38
|
(['INTERNAL', 'TCP', 'SSL'] as const).forEach((type) => {
|
|
29
|
-
expect(getGceLoadBalancerModal(type)
|
|
39
|
+
expect(getGceLoadBalancerModal(type), type).toBe(GceProxyLoadBalancerModal);
|
|
30
40
|
});
|
|
31
|
-
(['HTTP', 'INTERNAL_MANAGED'] as const).forEach((type) => {
|
|
32
|
-
expect(getGceLoadBalancerModal(type)
|
|
41
|
+
(['HTTP', 'INTERNAL_MANAGED', 'EXTERNAL_MANAGED'] as const).forEach((type) => {
|
|
42
|
+
expect(getGceLoadBalancerModal(type), type).toBe(GceHttpLoadBalancerModal);
|
|
33
43
|
});
|
|
44
|
+
expect(getGceLoadBalancerModal('REGIONAL_EXTERNAL_NETWORK')).toBe(GceRegionalExternalNetworkLoadBalancerModal);
|
|
34
45
|
});
|
|
35
46
|
|
|
36
47
|
it('advertises pipeline support only at the fully routed choice entry point', () => {
|
|
@@ -39,12 +50,12 @@ describe('GceLoadBalancerChoiceModal', () => {
|
|
|
39
50
|
|
|
40
51
|
it('passes modal sizing as ReactModal dialog options instead of component props', () => {
|
|
41
52
|
const result = Promise.resolve();
|
|
42
|
-
const show = spyOn(ReactModal, 'show').
|
|
53
|
+
const show = vi.spyOn(ReactModal, 'show').mockReturnValue(result);
|
|
43
54
|
|
|
44
55
|
const opened = GceLoadBalancerChoiceModal.show({ application } as any);
|
|
45
56
|
|
|
46
57
|
expect(opened).toBe(result);
|
|
47
|
-
expect(show).
|
|
58
|
+
expect(show).toHaveBeenCalledExactlyOnceWith(
|
|
48
59
|
GceLoadBalancerChoiceModal,
|
|
49
60
|
{ application },
|
|
50
61
|
{ dialogClassName: 'create-pipeline-modal-overflow-visible modal-lg' },
|
|
@@ -52,73 +63,69 @@ describe('GceLoadBalancerChoiceModal', () => {
|
|
|
52
63
|
});
|
|
53
64
|
|
|
54
65
|
it('renders native modal sections and controls', () => {
|
|
55
|
-
const
|
|
66
|
+
const { container } = render(<GceLoadBalancerChoiceModal application={application} />);
|
|
56
67
|
|
|
57
|
-
expect(
|
|
58
|
-
expect(
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
68
|
+
expect(container.querySelectorAll('.modal-header')).toHaveLength(1);
|
|
69
|
+
expect(container.querySelectorAll('.modal-body')).toHaveLength(1);
|
|
70
|
+
expect(container.querySelectorAll('.modal-footer')).toHaveLength(1);
|
|
71
|
+
expect(screen.getByRole('button', { name: /Configure Load Balancer/ })).toBeInTheDocument();
|
|
61
72
|
});
|
|
62
73
|
|
|
63
74
|
it('renders each type card as a native pressed-state button', () => {
|
|
64
|
-
const
|
|
65
|
-
const choices =
|
|
75
|
+
const { container } = render(<GceLoadBalancerChoiceModal application={application} />);
|
|
76
|
+
const choices = Array.from(container.querySelectorAll<HTMLButtonElement>('button.card'));
|
|
66
77
|
|
|
67
|
-
expect(choices).
|
|
78
|
+
expect(choices).toHaveLength(GCE_LOAD_BALANCER_CHOICES.length);
|
|
68
79
|
choices.forEach((choice, index) => {
|
|
69
|
-
expect(choice.
|
|
70
|
-
expect(choice.
|
|
71
|
-
|
|
72
|
-
.toBe(index === 0);
|
|
73
|
-
expect(choice.prop('disabled')).withContext(GCE_LOAD_BALANCER_CHOICES[index].type).toBe(false);
|
|
80
|
+
expect(choice.type, GCE_LOAD_BALANCER_CHOICES[index].type).toBe('button');
|
|
81
|
+
expect(choice, GCE_LOAD_BALANCER_CHOICES[index].type).toHaveAttribute('aria-pressed', String(index === 0));
|
|
82
|
+
expect(choice, GCE_LOAD_BALANCER_CHOICES[index].type).not.toBeDisabled();
|
|
74
83
|
});
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
wrapper.update();
|
|
85
|
+
fireEvent.click(choices[2]);
|
|
78
86
|
|
|
79
|
-
expect(
|
|
80
|
-
expect(
|
|
81
|
-
expect(
|
|
87
|
+
expect(choices[0]).toHaveAttribute('aria-pressed', 'false');
|
|
88
|
+
expect(choices[2]).toHaveAttribute('aria-pressed', 'true');
|
|
89
|
+
expect(choices[2]).toHaveClass('active');
|
|
82
90
|
});
|
|
83
91
|
|
|
84
92
|
it('opens every exposed type in create and pipeline modes', () => {
|
|
85
|
-
|
|
86
|
-
spyOn(GceProxyLoadBalancerModal, 'show').and.returnValue(Promise.resolve() as any);
|
|
87
|
-
spyOn(GceHttpLoadBalancerModal, 'show').and.returnValue(Promise.resolve() as any);
|
|
93
|
+
spyOnAllGceLoadBalancerModals();
|
|
88
94
|
|
|
89
95
|
([false, true] as const).forEach((forPipelineConfig) => {
|
|
90
96
|
GCE_LOAD_BALANCER_CHOICES.forEach((choice) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
render(<GceLoadBalancerChoiceModal application={application} forPipelineConfig={forPipelineConfig} />);
|
|
98
|
+
fireEvent.click(choiceButton(choice.label));
|
|
99
|
+
fireEvent.click(screen.getByRole('button', { name: /Configure Load Balancer/ }));
|
|
100
|
+
|
|
101
|
+
expect(
|
|
102
|
+
getGceLoadBalancerModal(choice.type).show,
|
|
103
|
+
`${choice.type} ${forPipelineConfig ? 'pipeline' : 'create'}`,
|
|
104
|
+
).toHaveBeenCalledWith(
|
|
105
|
+
expect.objectContaining({
|
|
106
|
+
forPipelineConfig,
|
|
107
|
+
isNew: !forPipelineConfig,
|
|
108
|
+
loadBalancer: null,
|
|
109
|
+
loadBalancerType: choice.type,
|
|
110
|
+
mode: forPipelineConfig ? 'pipeline' : 'create',
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
cleanup();
|
|
107
114
|
});
|
|
108
115
|
});
|
|
109
116
|
});
|
|
110
117
|
|
|
111
118
|
it('opens a selected type in create mode without persisted data', () => {
|
|
112
|
-
const closeModal =
|
|
119
|
+
const closeModal = vi.fn();
|
|
113
120
|
const result = Promise.resolve({ loadBalancerType: 'SSL' });
|
|
114
|
-
const show = spyOn(GceProxyLoadBalancerModal, 'show').
|
|
115
|
-
|
|
121
|
+
const show = vi.spyOn(GceProxyLoadBalancerModal, 'show').mockReturnValue(result as any);
|
|
122
|
+
render(<GceLoadBalancerChoiceModal application={application} closeModal={closeModal} />);
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
(
|
|
124
|
+
fireEvent.click(choiceButton('SSL'));
|
|
125
|
+
fireEvent.click(screen.getByRole('button', { name: /Configure Load Balancer/ }));
|
|
119
126
|
|
|
120
|
-
expect(show).
|
|
121
|
-
|
|
127
|
+
expect(show).toHaveBeenCalledExactlyOnceWith(
|
|
128
|
+
expect.objectContaining({
|
|
122
129
|
app: application,
|
|
123
130
|
application,
|
|
124
131
|
forPipelineConfig: false,
|
|
@@ -128,64 +135,58 @@ describe('GceLoadBalancerChoiceModal', () => {
|
|
|
128
135
|
mode: 'create',
|
|
129
136
|
}),
|
|
130
137
|
);
|
|
131
|
-
expect(closeModal).
|
|
138
|
+
expect(closeModal).toHaveBeenCalledExactlyOnceWith(result);
|
|
132
139
|
});
|
|
133
140
|
|
|
134
141
|
it('opens a selected type in pipeline mode and propagates its command promise', async () => {
|
|
135
|
-
const closeModal =
|
|
142
|
+
const closeModal = vi.fn();
|
|
136
143
|
const command = { loadBalancerType: 'INTERNAL_MANAGED', type: 'upsertLoadBalancer' };
|
|
137
144
|
const result = Promise.resolve(command);
|
|
138
|
-
const show = spyOn(GceHttpLoadBalancerModal, 'show').
|
|
139
|
-
|
|
145
|
+
const show = vi.spyOn(GceHttpLoadBalancerModal, 'show').mockReturnValue(result);
|
|
146
|
+
render(<GceLoadBalancerChoiceModal application={application} closeModal={closeModal} forPipelineConfig={true} />);
|
|
140
147
|
|
|
141
|
-
|
|
142
|
-
(
|
|
148
|
+
fireEvent.click(choiceButton('Internal HTTP(S)'));
|
|
149
|
+
fireEvent.click(screen.getByRole('button', { name: /Configure Load Balancer/ }));
|
|
143
150
|
|
|
144
|
-
expect(show).
|
|
145
|
-
|
|
151
|
+
expect(show).toHaveBeenCalledExactlyOnceWith(
|
|
152
|
+
expect.objectContaining({
|
|
146
153
|
forPipelineConfig: true,
|
|
147
154
|
loadBalancer: null,
|
|
148
155
|
loadBalancerType: 'INTERNAL_MANAGED',
|
|
149
156
|
mode: 'pipeline',
|
|
150
157
|
}),
|
|
151
158
|
);
|
|
152
|
-
expect(closeModal).
|
|
153
|
-
await expectAsync(closeModal.
|
|
159
|
+
expect(closeModal).toHaveBeenCalledExactlyOnceWith(result);
|
|
160
|
+
await expectAsync(closeModal.mock.lastCall[0]).toBeResolvedTo(command);
|
|
154
161
|
});
|
|
155
162
|
|
|
156
163
|
it('routes every exposed type directly to edit mode with the current load balancer', () => {
|
|
157
|
-
|
|
158
|
-
spyOn(
|
|
159
|
-
spyOn(GceHttpLoadBalancerModal, 'show').and.callFake((props: any) => Promise.resolve(props.loadBalancer) as any);
|
|
160
|
-
const reactModalShow = spyOn(ReactModal, 'show');
|
|
164
|
+
spyOnAllGceLoadBalancerModals((props: any) => Promise.resolve(props.loadBalancer) as any);
|
|
165
|
+
const reactModalShow = vi.spyOn(ReactModal, 'show').mockReturnValue(undefined);
|
|
161
166
|
|
|
162
167
|
GCE_LOAD_BALANCER_CHOICES.forEach((choice) => {
|
|
163
168
|
const current = { account: 'account-a', loadBalancerType: choice.type, name: `fnord-${choice.type}` };
|
|
164
169
|
GceLoadBalancerChoiceModal.show({ application, isNew: false, loadBalancer: current } as any);
|
|
165
170
|
|
|
166
|
-
expect(getGceLoadBalancerModal(choice.type).show)
|
|
167
|
-
.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}),
|
|
178
|
-
);
|
|
171
|
+
expect(getGceLoadBalancerModal(choice.type).show, choice.type).toHaveBeenCalledWith(
|
|
172
|
+
expect.objectContaining({
|
|
173
|
+
app: application,
|
|
174
|
+
application,
|
|
175
|
+
forPipelineConfig: false,
|
|
176
|
+
isNew: false,
|
|
177
|
+
loadBalancer: current,
|
|
178
|
+
loadBalancerType: choice.type,
|
|
179
|
+
mode: 'edit',
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
179
182
|
});
|
|
180
183
|
|
|
181
184
|
expect(reactModalShow).not.toHaveBeenCalled();
|
|
182
185
|
});
|
|
183
186
|
|
|
184
187
|
it('routes every existing type to pipeline mode when editing pipeline configuration', () => {
|
|
185
|
-
|
|
186
|
-
spyOn(
|
|
187
|
-
spyOn(GceHttpLoadBalancerModal, 'show').and.callFake((props: any) => Promise.resolve(props.loadBalancer) as any);
|
|
188
|
-
const reactModalShow = spyOn(ReactModal, 'show');
|
|
188
|
+
spyOnAllGceLoadBalancerModals((props: any) => Promise.resolve(props.loadBalancer) as any);
|
|
189
|
+
const reactModalShow = vi.spyOn(ReactModal, 'show').mockReturnValue(undefined);
|
|
189
190
|
|
|
190
191
|
GCE_LOAD_BALANCER_CHOICES.forEach((choice) => {
|
|
191
192
|
const current = { account: 'account-a', loadBalancerType: choice.type, name: `fnord-${choice.type}` };
|
|
@@ -196,19 +197,17 @@ describe('GceLoadBalancerChoiceModal', () => {
|
|
|
196
197
|
loadBalancer: current,
|
|
197
198
|
} as any);
|
|
198
199
|
|
|
199
|
-
expect(getGceLoadBalancerModal(choice.type).show)
|
|
200
|
-
.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}),
|
|
211
|
-
);
|
|
200
|
+
expect(getGceLoadBalancerModal(choice.type).show, choice.type).toHaveBeenCalledWith(
|
|
201
|
+
expect.objectContaining({
|
|
202
|
+
app: application,
|
|
203
|
+
application,
|
|
204
|
+
forPipelineConfig: true,
|
|
205
|
+
isNew: false,
|
|
206
|
+
loadBalancer: current,
|
|
207
|
+
loadBalancerType: choice.type,
|
|
208
|
+
mode: 'pipeline',
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
212
211
|
});
|
|
213
212
|
|
|
214
213
|
expect(reactModalShow).not.toHaveBeenCalled();
|
|
@@ -216,17 +215,20 @@ describe('GceLoadBalancerChoiceModal', () => {
|
|
|
216
215
|
|
|
217
216
|
([undefined, '', 'http', 'UNKNOWN'] as const).forEach((persistedType) => {
|
|
218
217
|
it(`blocks edit routing for unsupported persisted type ${String(persistedType)}`, () => {
|
|
219
|
-
const networkShow = spyOn(GceNetworkLoadBalancerModal, 'show');
|
|
220
|
-
const proxyShow = spyOn(GceProxyLoadBalancerModal, 'show');
|
|
221
|
-
const httpShow = spyOn(GceHttpLoadBalancerModal, 'show');
|
|
218
|
+
const networkShow = vi.spyOn(GceNetworkLoadBalancerModal, 'show').mockReturnValue(undefined);
|
|
219
|
+
const proxyShow = vi.spyOn(GceProxyLoadBalancerModal, 'show').mockReturnValue(undefined);
|
|
220
|
+
const httpShow = vi.spyOn(GceHttpLoadBalancerModal, 'show').mockReturnValue(undefined);
|
|
221
|
+
const regionalExternalNetworkShow = vi
|
|
222
|
+
.spyOn(GceRegionalExternalNetworkLoadBalancerModal, 'show')
|
|
223
|
+
.mockReturnValue(undefined);
|
|
222
224
|
const blockedResult = Promise.resolve();
|
|
223
|
-
const reactModalShow = spyOn(ReactModal, 'show').
|
|
225
|
+
const reactModalShow = vi.spyOn(ReactModal, 'show').mockReturnValue(blockedResult);
|
|
224
226
|
const loadBalancer = { name: 'fnord', loadBalancerType: persistedType };
|
|
225
227
|
|
|
226
228
|
const result = GceLoadBalancerChoiceModal.show({ application, isNew: false, loadBalancer } as any);
|
|
227
229
|
|
|
228
230
|
expect(result).toBe(blockedResult);
|
|
229
|
-
expect(reactModalShow).
|
|
231
|
+
expect(reactModalShow).toHaveBeenCalledExactlyOnceWith(
|
|
230
232
|
GceLoadBalancerChoiceModal,
|
|
231
233
|
{ application, isNew: false, loadBalancer },
|
|
232
234
|
{ dialogClassName: 'create-pipeline-modal-overflow-visible modal-lg' },
|
|
@@ -234,12 +236,13 @@ describe('GceLoadBalancerChoiceModal', () => {
|
|
|
234
236
|
expect(networkShow).not.toHaveBeenCalled();
|
|
235
237
|
expect(proxyShow).not.toHaveBeenCalled();
|
|
236
238
|
expect(httpShow).not.toHaveBeenCalled();
|
|
239
|
+
expect(regionalExternalNetworkShow).not.toHaveBeenCalled();
|
|
237
240
|
});
|
|
238
241
|
});
|
|
239
242
|
|
|
240
243
|
([undefined, 'http', 'UNKNOWN'] as const).forEach((persistedType) => {
|
|
241
244
|
it(`renders a non-submittable blocked pipeline-edit state for ${String(persistedType)}`, () => {
|
|
242
|
-
const
|
|
245
|
+
const { container } = render(
|
|
243
246
|
<GceLoadBalancerChoiceModal
|
|
244
247
|
application={application}
|
|
245
248
|
forPipelineConfig={true}
|
|
@@ -248,32 +251,70 @@ describe('GceLoadBalancerChoiceModal', () => {
|
|
|
248
251
|
/>,
|
|
249
252
|
);
|
|
250
253
|
|
|
251
|
-
expect(
|
|
252
|
-
expect(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
expect(choice
|
|
254
|
+
expect(screen.getByRole('alert')).toHaveTextContent('cannot be edited');
|
|
255
|
+
expect(screen.getByRole('alert')).toHaveTextContent(persistedType || 'missing');
|
|
256
|
+
const choices = Array.from(container.querySelectorAll<HTMLButtonElement>('button.card'));
|
|
257
|
+
expect(choices).toHaveLength(GCE_LOAD_BALANCER_CHOICES.length);
|
|
258
|
+
choices.forEach((choice) => {
|
|
259
|
+
expect(choice).toBeDisabled();
|
|
260
|
+
expect(choice).toHaveAttribute('aria-pressed', 'false');
|
|
257
261
|
});
|
|
258
|
-
expect(
|
|
262
|
+
expect(screen.getByRole('button', { name: /Configure Load Balancer/ })).toBeDisabled();
|
|
259
263
|
});
|
|
260
264
|
});
|
|
261
265
|
|
|
262
266
|
it('does not open a type modal when blocked submission is invoked programmatically', () => {
|
|
263
|
-
const networkShow = spyOn(GceNetworkLoadBalancerModal, 'show');
|
|
264
|
-
const proxyShow = spyOn(GceProxyLoadBalancerModal, 'show');
|
|
265
|
-
const httpShow = spyOn(GceHttpLoadBalancerModal, 'show');
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
267
|
+
const networkShow = vi.spyOn(GceNetworkLoadBalancerModal, 'show').mockReturnValue(undefined);
|
|
268
|
+
const proxyShow = vi.spyOn(GceProxyLoadBalancerModal, 'show').mockReturnValue(undefined);
|
|
269
|
+
const httpShow = vi.spyOn(GceHttpLoadBalancerModal, 'show').mockReturnValue(undefined);
|
|
270
|
+
const regionalExternalNetworkShow = vi
|
|
271
|
+
.spyOn(GceRegionalExternalNetworkLoadBalancerModal, 'show')
|
|
272
|
+
.mockReturnValue(undefined);
|
|
273
|
+
render(
|
|
274
|
+
<GceLoadBalancerChoiceModal
|
|
275
|
+
application={application}
|
|
276
|
+
forPipelineConfig={true}
|
|
277
|
+
isNew={false}
|
|
278
|
+
loadBalancer={{ name: 'fnord', loadBalancerType: 'http' }}
|
|
279
|
+
/>,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
fireEvent.click(screen.getByRole('button', { name: /Configure Load Balancer/ }));
|
|
274
283
|
|
|
275
284
|
expect(networkShow).not.toHaveBeenCalled();
|
|
276
285
|
expect(proxyShow).not.toHaveBeenCalled();
|
|
277
286
|
expect(httpShow).not.toHaveBeenCalled();
|
|
287
|
+
expect(regionalExternalNetworkShow).not.toHaveBeenCalled();
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('exposes EXTERNAL_MANAGED and REGIONAL_EXTERNAL_NETWORK choices', () => {
|
|
291
|
+
expect(GCE_LOAD_BALANCER_CHOICES.map(({ type }) => type)).toEqual([
|
|
292
|
+
'NETWORK',
|
|
293
|
+
'INTERNAL',
|
|
294
|
+
'TCP',
|
|
295
|
+
'SSL',
|
|
296
|
+
'HTTP',
|
|
297
|
+
'INTERNAL_MANAGED',
|
|
298
|
+
'EXTERNAL_MANAGED',
|
|
299
|
+
'REGIONAL_EXTERNAL_NETWORK',
|
|
300
|
+
]);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('routes EXTERNAL_MANAGED to the HTTP modal', () => {
|
|
304
|
+
expect(getGceLoadBalancerModal('EXTERNAL_MANAGED' as any)).toBe(GceHttpLoadBalancerModal);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('routes REGIONAL_EXTERNAL_NETWORK to its dedicated modal', () => {
|
|
308
|
+
expect(getGceLoadBalancerModal('REGIONAL_EXTERNAL_NETWORK' as any)).toBe(
|
|
309
|
+
GceRegionalExternalNetworkLoadBalancerModal,
|
|
310
|
+
);
|
|
278
311
|
});
|
|
279
312
|
});
|
|
313
|
+
|
|
314
|
+
function choiceButton(label: string): HTMLButtonElement {
|
|
315
|
+
const button = screen.getByText(label, { selector: '.load-balancer-label' }).closest('button');
|
|
316
|
+
if (!(button instanceof HTMLButtonElement)) {
|
|
317
|
+
throw new Error(`No choice button found for ${label}`);
|
|
318
|
+
}
|
|
319
|
+
return button;
|
|
320
|
+
}
|
|
@@ -7,6 +7,7 @@ import type { GceLoadBalancerEditorMode, GceLoadBalancerType } from '../common';
|
|
|
7
7
|
import { GceProxyLoadBalancerModal } from '../common/GceProxyLoadBalancerModal';
|
|
8
8
|
import { GceHttpLoadBalancerModal } from '../http/GceHttpLoadBalancerModal';
|
|
9
9
|
import { GceNetworkLoadBalancerModal } from '../network/GceNetworkLoadBalancerModal';
|
|
10
|
+
import { GceRegionalExternalNetworkLoadBalancerModal } from '../network/GceRegionalExternalNetworkLoadBalancerModal';
|
|
10
11
|
|
|
11
12
|
import './GceLoadBalancerChoiceModal.less';
|
|
12
13
|
|
|
@@ -31,6 +32,16 @@ export const GCE_LOAD_BALANCER_CHOICES: IGceLoadBalancerChoice[] = [
|
|
|
31
32
|
label: 'Internal HTTP(S)',
|
|
32
33
|
description: 'Regional internal managed HTTP(S) load balancer.',
|
|
33
34
|
},
|
|
35
|
+
{
|
|
36
|
+
type: 'EXTERNAL_MANAGED',
|
|
37
|
+
label: 'External managed HTTP(S)',
|
|
38
|
+
description: 'Regional external managed HTTP(S) load balancer.',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'REGIONAL_EXTERNAL_NETWORK',
|
|
42
|
+
label: 'Regional external network',
|
|
43
|
+
description: 'Regional external pass-through network load balancer.',
|
|
44
|
+
},
|
|
34
45
|
];
|
|
35
46
|
|
|
36
47
|
export interface IGceLoadBalancerChoiceModalProps extends IModalComponentProps {
|
|
@@ -47,7 +58,8 @@ interface IGceLoadBalancerChoiceModalState {
|
|
|
47
58
|
|
|
48
59
|
export function getGceLoadBalancerModal(type: GceLoadBalancerType): IGceLoadBalancerModal {
|
|
49
60
|
if (type === 'NETWORK') return GceNetworkLoadBalancerModal;
|
|
50
|
-
if (type === 'HTTP' || type === 'INTERNAL_MANAGED') return GceHttpLoadBalancerModal;
|
|
61
|
+
if (type === 'HTTP' || type === 'INTERNAL_MANAGED' || type === 'EXTERNAL_MANAGED') return GceHttpLoadBalancerModal;
|
|
62
|
+
if (type === 'REGIONAL_EXTERNAL_NETWORK') return GceRegionalExternalNetworkLoadBalancerModal;
|
|
51
63
|
return GceProxyLoadBalancerModal;
|
|
52
64
|
}
|
|
53
65
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import { shallow } from 'enzyme';
|
|
3
3
|
|
|
4
|
-
import { normalizeGceLoadBalancerCommand } from './gceLoadBalancerModels';
|
|
5
4
|
import {
|
|
6
5
|
applyGceProxyTypeConstraints,
|
|
7
6
|
GCE_PROXY_TYPE_CONFIG,
|
|
@@ -9,6 +8,7 @@ import {
|
|
|
9
8
|
getGceProxyResourceOptions,
|
|
10
9
|
validateGceProxyLoadBalancerCommand,
|
|
11
10
|
} from './GceProxyLoadBalancerEditor';
|
|
11
|
+
import { normalizeGceLoadBalancerCommand } from './gceLoadBalancerModels';
|
|
12
12
|
|
|
13
13
|
describe('GceProxyLoadBalancerEditor', () => {
|
|
14
14
|
it('defines frontend and backend protocols that match each Clouddriver proxy contract', () => {
|
|
@@ -50,7 +50,7 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
50
50
|
expect(constrained.listeners[0].protocol).toBe('SSL');
|
|
51
51
|
expect(constrained.listeners[0].certificate).toEqual({ name: 'cert-a' });
|
|
52
52
|
expect(constrained.backendServices[0]).toEqual(
|
|
53
|
-
|
|
53
|
+
expect.objectContaining({
|
|
54
54
|
affinityCookieTtlSec: 300,
|
|
55
55
|
connectionDrainingTimeoutSec: 45,
|
|
56
56
|
portName: 'https',
|
|
@@ -107,7 +107,10 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
107
107
|
{ name: 'address-b', account: 'account-b', region: 'global' },
|
|
108
108
|
],
|
|
109
109
|
backendServices: [{ name: 'backend-a', account: 'account-a', region: 'global' }],
|
|
110
|
-
certificates: [
|
|
110
|
+
certificates: [
|
|
111
|
+
{ name: 'cert-a', account: 'account-a' },
|
|
112
|
+
{ name: 'regional-cert', account: 'account-a', region: 'us-central1' },
|
|
113
|
+
],
|
|
111
114
|
healthChecks: [{ name: 'check-a', account: 'account-a' }],
|
|
112
115
|
networks: [],
|
|
113
116
|
regions: [{ name: 'global' }],
|
|
@@ -203,15 +206,15 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
203
206
|
'create',
|
|
204
207
|
),
|
|
205
208
|
);
|
|
206
|
-
const
|
|
207
|
-
<GceProxyLoadBalancerEditor command={command} data={emptyData()} onChange={
|
|
209
|
+
const { container } = render(
|
|
210
|
+
<GceProxyLoadBalancerEditor command={command} data={emptyData()} onChange={vi.fn()} />,
|
|
208
211
|
);
|
|
209
212
|
|
|
210
|
-
expect(
|
|
211
|
-
expect(
|
|
212
|
-
expect(
|
|
213
|
-
expect(
|
|
214
|
-
expect(
|
|
213
|
+
expect(field(container, 'certificate')).toBeInTheDocument();
|
|
214
|
+
expect(field(container, 'portName')).toBeInTheDocument();
|
|
215
|
+
expect(field(container, 'connectionDrainingTimeoutSec')).toBeInTheDocument();
|
|
216
|
+
expect(field(container, 'network')).not.toBeInTheDocument();
|
|
217
|
+
expect(field(container, 'subnet')).not.toBeInTheDocument();
|
|
215
218
|
});
|
|
216
219
|
|
|
217
220
|
it('renders regional network and subnet fields for INTERNAL without unsupported draining controls', () => {
|
|
@@ -221,14 +224,14 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
221
224
|
'create',
|
|
222
225
|
),
|
|
223
226
|
);
|
|
224
|
-
const
|
|
225
|
-
<GceProxyLoadBalancerEditor command={command} data={emptyData()} onChange={
|
|
227
|
+
const { container } = render(
|
|
228
|
+
<GceProxyLoadBalancerEditor command={command} data={emptyData()} onChange={vi.fn()} />,
|
|
226
229
|
);
|
|
227
230
|
|
|
228
|
-
expect(
|
|
229
|
-
expect(
|
|
230
|
-
expect(
|
|
231
|
-
expect(
|
|
231
|
+
expect(field(container, 'network')).toBeInTheDocument();
|
|
232
|
+
expect(field(container, 'subnet')).toBeInTheDocument();
|
|
233
|
+
expect(field(container, 'connectionDrainingTimeoutSec')).not.toBeInTheDocument();
|
|
234
|
+
expect(field(container, 'certificate')).not.toBeInTheDocument();
|
|
232
235
|
});
|
|
233
236
|
|
|
234
237
|
it('keeps generated listener and backend service names aligned when the load balancer name changes', () => {
|
|
@@ -238,12 +241,14 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
238
241
|
'create',
|
|
239
242
|
),
|
|
240
243
|
);
|
|
241
|
-
const onChange =
|
|
242
|
-
const
|
|
244
|
+
const onChange = vi.fn();
|
|
245
|
+
const { container } = render(
|
|
246
|
+
<GceProxyLoadBalancerEditor command={command} data={emptyData()} onChange={onChange} />,
|
|
247
|
+
);
|
|
243
248
|
|
|
244
|
-
|
|
249
|
+
fireEvent.change(fieldControl(container, 'name'), { target: { value: 'app-main' } });
|
|
245
250
|
|
|
246
|
-
const updated = onChange.
|
|
251
|
+
const updated = onChange.mock.lastCall[0];
|
|
247
252
|
expect(updated.name).toBe('app-main');
|
|
248
253
|
expect(updated.listeners[0].name).toBe('app-main');
|
|
249
254
|
expect(updated.backendServices[0].name).toBe('app-main');
|
|
@@ -256,7 +261,7 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
256
261
|
'create',
|
|
257
262
|
),
|
|
258
263
|
);
|
|
259
|
-
const onChange =
|
|
264
|
+
const onChange = vi.fn();
|
|
260
265
|
const data = {
|
|
261
266
|
...emptyData(),
|
|
262
267
|
addresses: [
|
|
@@ -269,11 +274,11 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
269
274
|
},
|
|
270
275
|
],
|
|
271
276
|
};
|
|
272
|
-
const
|
|
277
|
+
const { container } = render(<GceProxyLoadBalancerEditor command={command} data={data} onChange={onChange} />);
|
|
273
278
|
|
|
274
|
-
|
|
279
|
+
fireEvent.change(fieldControl(container, 'address'), { target: { value: 'reserved-address' } });
|
|
275
280
|
|
|
276
|
-
expect(onChange.
|
|
281
|
+
expect(onChange.mock.lastCall[0].listeners[0].address).toEqual(data.addresses[0]);
|
|
277
282
|
});
|
|
278
283
|
|
|
279
284
|
it('replaces stale inline state with the complete normalized reader health check', () => {
|
|
@@ -290,7 +295,7 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
290
295
|
'edit',
|
|
291
296
|
),
|
|
292
297
|
);
|
|
293
|
-
const onChange =
|
|
298
|
+
const onChange = vi.fn();
|
|
294
299
|
const data = {
|
|
295
300
|
...emptyData(),
|
|
296
301
|
healthChecks: [
|
|
@@ -312,17 +317,17 @@ describe('GceProxyLoadBalancerEditor', () => {
|
|
|
312
317
|
},
|
|
313
318
|
],
|
|
314
319
|
};
|
|
315
|
-
const
|
|
320
|
+
const { container } = render(<GceProxyLoadBalancerEditor command={command} data={data} onChange={onChange} />);
|
|
316
321
|
|
|
317
|
-
|
|
322
|
+
fireEvent.change(fieldControl(container, 'healthCheck'), { target: { value: 'new-check' } });
|
|
318
323
|
|
|
319
|
-
const updated = onChange.
|
|
324
|
+
const updated = onChange.mock.lastCall[0];
|
|
320
325
|
expect(updated.backendServices[0].healthCheck).toEqual({
|
|
321
326
|
name: 'new-check',
|
|
322
327
|
selfLink: 'projects/test/global/healthChecks/new-check',
|
|
323
328
|
});
|
|
324
329
|
expect(updated.healthChecks).toEqual([
|
|
325
|
-
|
|
330
|
+
expect.objectContaining({
|
|
326
331
|
checkIntervalSec: 15,
|
|
327
332
|
healthCheckType: 'HTTP',
|
|
328
333
|
healthyThreshold: 2,
|
|
@@ -351,3 +356,15 @@ function emptyData(): any {
|
|
|
351
356
|
subnets: [],
|
|
352
357
|
};
|
|
353
358
|
}
|
|
359
|
+
|
|
360
|
+
function field(container: HTMLElement, name: string): HTMLElement | null {
|
|
361
|
+
return container.querySelector(`[data-field="${name}"]`);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function fieldControl(container: HTMLElement, name: string): HTMLInputElement | HTMLSelectElement {
|
|
365
|
+
const control = field(container, name)?.querySelector('input, select');
|
|
366
|
+
if (!(control instanceof HTMLInputElement || control instanceof HTMLSelectElement)) {
|
|
367
|
+
throw new Error(`No control found for ${name}`);
|
|
368
|
+
}
|
|
369
|
+
return control;
|
|
370
|
+
}
|
|
@@ -133,7 +133,8 @@ export function getGceProxyResourceOptions(
|
|
|
133
133
|
references(backend),
|
|
134
134
|
),
|
|
135
135
|
certificates: mergeGceResourceOptions(
|
|
136
|
-
|
|
136
|
+
// Global SSL proxies cannot use the regional certificates cached for regional HTTPS proxies.
|
|
137
|
+
accountScoped(data.certificates as IGceProxyDataItem[]).filter((item) => !item.region),
|
|
137
138
|
references(listener?.certificate),
|
|
138
139
|
),
|
|
139
140
|
healthChecks: mergeGceResourceOptions(
|