@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,10 +1,25 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import { shallow } from 'enzyme';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CloudProviderRegistry,
|
|
6
|
+
ConfirmationModalService,
|
|
7
|
+
HelpContentsRegistry,
|
|
8
|
+
HelpTextExpandedContext,
|
|
9
|
+
InfrastructureCaches,
|
|
10
|
+
TaskExecutor,
|
|
11
|
+
} from '@spinnaker/core';
|
|
12
|
+
|
|
13
|
+
import { mockHttpClient } from '../../../../core/src/api/mock/mockHttpSupport';
|
|
5
14
|
|
|
6
15
|
import { GceLoadBalancerChoiceModal } from '../configure/choice/GceLoadBalancerChoiceModal';
|
|
7
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
GceLoadBalancerActions,
|
|
18
|
+
GceLoadBalancerBackendServicesSection,
|
|
19
|
+
GceLoadBalancerInformationSection,
|
|
20
|
+
GceLoadBalancerListenersSection,
|
|
21
|
+
loadGceLoadBalancerDetails,
|
|
22
|
+
} from './gceLoadBalancerDetails';
|
|
8
23
|
|
|
9
24
|
describe('GceLoadBalancerActions', () => {
|
|
10
25
|
const app = { name: 'fnord' } as any;
|
|
@@ -15,51 +30,440 @@ describe('GceLoadBalancerActions', () => {
|
|
|
15
30
|
name: 'fnord-main',
|
|
16
31
|
region: 'europe-west1',
|
|
17
32
|
} as any;
|
|
33
|
+
const managedLoadBalancer = {
|
|
34
|
+
...loadBalancer,
|
|
35
|
+
isManaged: true,
|
|
36
|
+
managedResourceSummary: { id: 'resource-a', isPaused: false, locations: { account: 'account-a', regions: [] } },
|
|
37
|
+
};
|
|
18
38
|
|
|
19
39
|
it('hides write actions when the Google provider is disabled', () => {
|
|
20
|
-
spyOn(CloudProviderRegistry, 'isDisabled').
|
|
40
|
+
vi.spyOn(CloudProviderRegistry, 'isDisabled').mockReturnValue(true);
|
|
21
41
|
|
|
22
|
-
const
|
|
42
|
+
const { container } = render(<GceLoadBalancerActions app={app} loadBalancer={loadBalancer} />);
|
|
23
43
|
|
|
24
|
-
expect(
|
|
44
|
+
expect(container).toBeEmptyDOMElement();
|
|
25
45
|
});
|
|
26
46
|
|
|
27
|
-
it('opens the current load balancer in edit mode through managed-resource gating', () => {
|
|
28
|
-
spyOn(CloudProviderRegistry, 'isDisabled').
|
|
29
|
-
const show = spyOn(GceLoadBalancerChoiceModal, 'show').
|
|
47
|
+
it('opens the current load balancer in edit mode through managed-resource gating', async () => {
|
|
48
|
+
vi.spyOn(CloudProviderRegistry, 'isDisabled').mockReturnValue(false);
|
|
49
|
+
const show = vi.spyOn(GceLoadBalancerChoiceModal, 'show').mockReturnValue(Promise.resolve() as any);
|
|
50
|
+
const confirm = vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(Promise.reject() as any);
|
|
30
51
|
|
|
31
|
-
const
|
|
32
|
-
|
|
52
|
+
const managed = render(<GceLoadBalancerActions app={app} loadBalancer={managedLoadBalancer} />);
|
|
53
|
+
fireEvent.click(screen.getByText('Edit Load Balancer'));
|
|
54
|
+
await vi.waitFor(() =>
|
|
55
|
+
expect(confirm).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ header: 'Pause Management?' })),
|
|
56
|
+
);
|
|
57
|
+
expect(show).not.toHaveBeenCalled();
|
|
58
|
+
managed.unmount();
|
|
33
59
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
60
|
+
render(<GceLoadBalancerActions app={app} loadBalancer={loadBalancer} />);
|
|
61
|
+
|
|
62
|
+
fireEvent.click(screen.getByText('Edit Load Balancer'));
|
|
63
|
+
await vi.waitFor(() =>
|
|
64
|
+
expect(show).toHaveBeenCalledExactlyOnceWith({
|
|
65
|
+
app,
|
|
66
|
+
application: app,
|
|
67
|
+
forPipelineConfig: false,
|
|
68
|
+
isNew: false,
|
|
69
|
+
loadBalancer,
|
|
70
|
+
mode: 'edit',
|
|
71
|
+
} as any),
|
|
72
|
+
);
|
|
45
73
|
});
|
|
46
74
|
|
|
47
|
-
it('keeps delete behind managed-resource gating and disables it while instances are attached', () => {
|
|
48
|
-
spyOn(CloudProviderRegistry, 'isDisabled').
|
|
75
|
+
it('keeps delete behind managed-resource gating and disables it while instances are attached', async () => {
|
|
76
|
+
vi.spyOn(CloudProviderRegistry, 'isDisabled').mockReturnValue(false);
|
|
77
|
+
const confirm = vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(Promise.reject() as any);
|
|
49
78
|
|
|
50
|
-
const editable =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.
|
|
54
|
-
|
|
55
|
-
|
|
79
|
+
const editable = render(<GceLoadBalancerActions app={app} loadBalancer={managedLoadBalancer} />);
|
|
80
|
+
fireEvent.click(editable.getByText('Delete Load Balancer'));
|
|
81
|
+
await vi.waitFor(() =>
|
|
82
|
+
expect(confirm).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({ header: 'Pause Management?' })),
|
|
83
|
+
);
|
|
84
|
+
editable.unmount();
|
|
85
|
+
confirm.mockClear();
|
|
56
86
|
|
|
57
|
-
|
|
87
|
+
render(
|
|
58
88
|
<GceLoadBalancerActions app={app} loadBalancer={{ ...loadBalancer, instances: [{ name: 'instance-a' }] }} />,
|
|
59
89
|
);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
90
|
+
const attachedDelete = screen.getByText('Delete Load Balancer');
|
|
91
|
+
expect(attachedDelete.closest('li')).toHaveClass('disabled');
|
|
92
|
+
fireEvent.click(attachedDelete);
|
|
93
|
+
await Promise.resolve();
|
|
94
|
+
expect(confirm).not.toHaveBeenCalled();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
describe('loadGceLoadBalancerDetails', () => {
|
|
99
|
+
it('matches scoped regional HTTP load balancers by raw urlMapName', async () => {
|
|
100
|
+
const normalizedLoadBalancer = {
|
|
101
|
+
account: 'test',
|
|
102
|
+
defaultService: { healthCheck: { name: 'hc-default' }, name: 'backend-default' },
|
|
103
|
+
hostRules: [],
|
|
104
|
+
listeners: [{ name: 'regional-listener' }],
|
|
105
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
106
|
+
name: 'regional-url-map (test/us-central1/EXTERNAL_MANAGED)',
|
|
107
|
+
provider: 'gce',
|
|
108
|
+
region: 'us-central1',
|
|
109
|
+
urlMapName: 'regional-url-map',
|
|
110
|
+
};
|
|
111
|
+
const loadBalancerReader = {
|
|
112
|
+
getLoadBalancerDetails: vi.fn().mockReturnValue(
|
|
113
|
+
Promise.resolve([
|
|
114
|
+
{
|
|
115
|
+
dnsname: '1.2.3.4',
|
|
116
|
+
listenerDescriptions: [{ listener: { loadBalancerPort: '443' } }],
|
|
117
|
+
},
|
|
118
|
+
]),
|
|
119
|
+
),
|
|
120
|
+
};
|
|
121
|
+
const accountService = {
|
|
122
|
+
getAccountDetails: vi.fn().mockReturnValue(Promise.resolve({ project: 'gce-project' })),
|
|
123
|
+
};
|
|
124
|
+
const autoClose = vi.fn();
|
|
125
|
+
|
|
126
|
+
const loadBalancer = await loadGceLoadBalancerDetails({
|
|
127
|
+
app: { loadBalancers: { data: [normalizedLoadBalancer] } } as any,
|
|
128
|
+
autoClose,
|
|
129
|
+
loadBalancerParams: {
|
|
130
|
+
accountId: 'test',
|
|
131
|
+
name: 'regional-url-map',
|
|
132
|
+
provider: 'gce',
|
|
133
|
+
region: 'us-central1',
|
|
134
|
+
vpcId: null,
|
|
135
|
+
},
|
|
136
|
+
loadBalancerReader: loadBalancerReader as any,
|
|
137
|
+
accountService: accountService as any,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(autoClose).not.toHaveBeenCalled();
|
|
141
|
+
expect(loadBalancerReader.getLoadBalancerDetails).toHaveBeenCalledWith(
|
|
142
|
+
'gce',
|
|
143
|
+
'test',
|
|
144
|
+
'us-central1',
|
|
145
|
+
'regional-listener',
|
|
146
|
+
);
|
|
147
|
+
expect(loadBalancer).toBe(normalizedLoadBalancer as any);
|
|
148
|
+
expect((loadBalancer as any).logsLink).toContain('regional-url-map');
|
|
149
|
+
expect((loadBalancer as any).logsLink).not.toContain('(test/us-central1/EXTERNAL_MANAGED)');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('prefers an exact name over a regional URL-map alias', async () => {
|
|
153
|
+
const autoClose = vi.fn();
|
|
154
|
+
const loadBalancerReader = {
|
|
155
|
+
getLoadBalancerDetails: vi.fn().mockReturnValue(Promise.resolve([])),
|
|
156
|
+
};
|
|
157
|
+
const accountService = {
|
|
158
|
+
getAccountDetails: vi.fn().mockReturnValue(Promise.resolve({})),
|
|
159
|
+
};
|
|
160
|
+
const internalManaged = {
|
|
161
|
+
account: 'test',
|
|
162
|
+
listeners: [{ name: 'internal-listener' }],
|
|
163
|
+
loadBalancerType: 'INTERNAL_MANAGED',
|
|
164
|
+
name: 'shared-map',
|
|
165
|
+
provider: 'gce',
|
|
166
|
+
region: 'us-central1',
|
|
167
|
+
urlMapName: 'shared-map',
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const loadBalancer = await loadGceLoadBalancerDetails({
|
|
171
|
+
app: {
|
|
172
|
+
loadBalancers: {
|
|
173
|
+
data: [
|
|
174
|
+
{
|
|
175
|
+
account: 'test',
|
|
176
|
+
listeners: [{ name: 'external-listener' }],
|
|
177
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
178
|
+
name: 'shared-map (test/us-central1/EXTERNAL_MANAGED)',
|
|
179
|
+
provider: 'gce',
|
|
180
|
+
region: 'us-central1',
|
|
181
|
+
urlMapName: 'shared-map',
|
|
182
|
+
},
|
|
183
|
+
internalManaged,
|
|
184
|
+
],
|
|
185
|
+
},
|
|
186
|
+
} as any,
|
|
187
|
+
autoClose,
|
|
188
|
+
loadBalancerParams: {
|
|
189
|
+
accountId: 'test',
|
|
190
|
+
name: 'shared-map',
|
|
191
|
+
provider: 'gce',
|
|
192
|
+
region: 'us-central1',
|
|
193
|
+
vpcId: null,
|
|
194
|
+
},
|
|
195
|
+
loadBalancerReader: loadBalancerReader as any,
|
|
196
|
+
accountService: accountService as any,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(autoClose).not.toHaveBeenCalled();
|
|
200
|
+
expect(loadBalancer).toBe(internalManaged as any);
|
|
201
|
+
expect(loadBalancerReader.getLoadBalancerDetails).toHaveBeenCalledWith(
|
|
202
|
+
'gce',
|
|
203
|
+
'test',
|
|
204
|
+
'us-central1',
|
|
205
|
+
'internal-listener',
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('renders REGIONAL_EXTERNAL_NETWORK addresses without an HTTP URL scheme', async () => {
|
|
210
|
+
const loadBalancer = {
|
|
211
|
+
account: 'test',
|
|
212
|
+
loadBalancerType: 'REGIONAL_EXTERNAL_NETWORK',
|
|
213
|
+
name: 'passthrough-lb',
|
|
214
|
+
provider: 'gce',
|
|
215
|
+
region: 'us-central1',
|
|
216
|
+
};
|
|
217
|
+
await loadGceLoadBalancerDetails({
|
|
218
|
+
accountService: { getAccountDetails: () => Promise.resolve({}) } as any,
|
|
219
|
+
app: { loadBalancers: { data: [loadBalancer] } } as any,
|
|
220
|
+
autoClose: vi.fn(),
|
|
221
|
+
loadBalancerParams: {
|
|
222
|
+
accountId: 'test',
|
|
223
|
+
name: 'passthrough-lb',
|
|
224
|
+
provider: 'gce',
|
|
225
|
+
region: 'us-central1',
|
|
226
|
+
vpcId: null,
|
|
227
|
+
},
|
|
228
|
+
loadBalancerReader: {
|
|
229
|
+
getLoadBalancerDetails: () => Promise.resolve([{ dnsname: '203.0.113.10' }]),
|
|
230
|
+
} as any,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// AccountTag lazily loads credentials; answer that request so the fail-closed test client allows it.
|
|
234
|
+
const http = mockHttpClient();
|
|
235
|
+
http.expectGET('/credentials').respond(200, []);
|
|
236
|
+
const { container } = render(<GceLoadBalancerInformationSection app={{}} loadBalancer={loadBalancer} />);
|
|
237
|
+
expect(screen.getByText('203.0.113.10')).toBeInTheDocument();
|
|
238
|
+
expect(container).not.toHaveTextContent('http://');
|
|
239
|
+
expect(container).not.toHaveTextContent('https://');
|
|
240
|
+
await http.flush();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('reads back the REGIONAL_EXTERNAL_NETWORK backend service and health check', async () => {
|
|
244
|
+
const loadBalancer = {
|
|
245
|
+
account: 'test',
|
|
246
|
+
backendService: { name: 'passthrough-backend', healthCheck: { name: 'tcp-hc' } },
|
|
247
|
+
loadBalancerType: 'REGIONAL_EXTERNAL_NETWORK',
|
|
248
|
+
name: 'passthrough-lb',
|
|
249
|
+
provider: 'gce',
|
|
250
|
+
region: 'us-central1',
|
|
251
|
+
};
|
|
252
|
+
await loadGceLoadBalancerDetails({
|
|
253
|
+
accountService: { getAccountDetails: () => Promise.resolve({}) } as any,
|
|
254
|
+
app: { loadBalancers: { data: [loadBalancer] } } as any,
|
|
255
|
+
autoClose: vi.fn(),
|
|
256
|
+
loadBalancerParams: {
|
|
257
|
+
accountId: 'test',
|
|
258
|
+
name: 'passthrough-lb',
|
|
259
|
+
provider: 'gce',
|
|
260
|
+
region: 'us-central1',
|
|
261
|
+
vpcId: null,
|
|
262
|
+
},
|
|
263
|
+
loadBalancerReader: {
|
|
264
|
+
getLoadBalancerDetails: () => Promise.resolve([{ dnsname: '203.0.113.10' }]),
|
|
265
|
+
} as any,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
render(<GceLoadBalancerBackendServicesSection app={{}} loadBalancer={loadBalancer} />);
|
|
269
|
+
expect(screen.getByText('passthrough-backend')).toBeInTheDocument();
|
|
270
|
+
expect(screen.getByText('tcp-hc')).toBeInTheDocument();
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('GceLoadBalancerActions delete behavior', () => {
|
|
275
|
+
const app = { name: 'fnord' } as any;
|
|
276
|
+
|
|
277
|
+
beforeEach(() => {
|
|
278
|
+
vi.spyOn(CloudProviderRegistry, 'isDisabled').mockReturnValue(false);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('deletes EXTERNAL_MANAGED load balancers using raw listener names and regional scope', async () => {
|
|
282
|
+
const confirmSpy = vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(Promise.resolve({}) as any);
|
|
283
|
+
vi.spyOn(InfrastructureCaches, 'clearCache').mockReturnValue(undefined);
|
|
284
|
+
const executeTaskSpy = vi.spyOn(TaskExecutor, 'executeTask').mockReturnValue(Promise.resolve({}) as any);
|
|
285
|
+
const loadBalancer = {
|
|
286
|
+
account: 'test-account',
|
|
287
|
+
instances: [],
|
|
288
|
+
listeners: [{ name: 'regional-listener-443' }],
|
|
289
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
290
|
+
name: 'regional-url-map (test-account/us-central1/EXTERNAL_MANAGED)',
|
|
291
|
+
provider: 'gce',
|
|
292
|
+
region: 'us-central1',
|
|
293
|
+
urlMapName: 'regional-url-map',
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
render(<GceLoadBalancerActions app={app} loadBalancer={loadBalancer} />);
|
|
297
|
+
fireEvent.click(screen.getByText('Delete Load Balancer'));
|
|
298
|
+
await vi.waitFor(() => expect(confirmSpy).toHaveBeenCalledTimes(1));
|
|
299
|
+
const modalParams = confirmSpy.mock.lastCall[0] as any;
|
|
300
|
+
await modalParams.submitMethod({ deleteHealthChecks: true });
|
|
301
|
+
|
|
302
|
+
expect(executeTaskSpy).toHaveBeenCalledWith({
|
|
303
|
+
application: app,
|
|
304
|
+
description: 'Delete load balancer: regional-url-map in test-account:us-central1',
|
|
305
|
+
job: [
|
|
306
|
+
expect.objectContaining({
|
|
307
|
+
cloudProvider: 'gce',
|
|
308
|
+
credentials: 'test-account',
|
|
309
|
+
deleteHealthChecks: true,
|
|
310
|
+
loadBalancerName: 'regional-listener-443',
|
|
311
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
312
|
+
region: 'us-central1',
|
|
313
|
+
regions: ['us-central1'],
|
|
314
|
+
type: 'deleteLoadBalancer',
|
|
315
|
+
}),
|
|
316
|
+
],
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('shows delete-health-check controls for EXTERNAL_MANAGED and REGIONAL_EXTERNAL_NETWORK load balancers', async () => {
|
|
321
|
+
const confirmSpy = vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(Promise.resolve({}) as any);
|
|
322
|
+
const deleteBodyHasHealthCheckOption = async (loadBalancer: any): Promise<boolean> => {
|
|
323
|
+
const callCount = confirmSpy.mock.calls.length;
|
|
324
|
+
const actions = render(<GceLoadBalancerActions app={app} loadBalancer={loadBalancer} />);
|
|
325
|
+
fireEvent.click(actions.getByText('Delete Load Balancer'));
|
|
326
|
+
await vi.waitFor(() => expect(confirmSpy).toHaveBeenCalledTimes(callCount + 1));
|
|
327
|
+
actions.unmount();
|
|
328
|
+
|
|
329
|
+
const body = render(<>{(confirmSpy.mock.lastCall[0] as any).bodyContent}</>);
|
|
330
|
+
const checkbox = body.queryByRole('checkbox', { name: 'Delete associated health checks' });
|
|
331
|
+
body.unmount();
|
|
332
|
+
return checkbox !== null;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const externalManaged = {
|
|
336
|
+
account: 'test-account',
|
|
337
|
+
instances: [],
|
|
338
|
+
listeners: [{ name: 'regional-listener' }],
|
|
339
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
340
|
+
name: 'regional-url-map (test-account/us-central1/EXTERNAL_MANAGED)',
|
|
341
|
+
provider: 'gce',
|
|
342
|
+
region: 'us-central1',
|
|
343
|
+
urlMapName: 'regional-url-map',
|
|
344
|
+
};
|
|
345
|
+
expect(await deleteBodyHasHealthCheckOption(externalManaged)).toBe(true);
|
|
346
|
+
|
|
347
|
+
const regionalExternalNetwork = {
|
|
348
|
+
account: 'test-account',
|
|
349
|
+
backendService: { healthCheck: { name: 'network-hc' } },
|
|
350
|
+
instances: [],
|
|
351
|
+
loadBalancerType: 'REGIONAL_EXTERNAL_NETWORK',
|
|
352
|
+
name: 'regional-network-lb',
|
|
353
|
+
provider: 'gce',
|
|
354
|
+
region: 'us-central1',
|
|
355
|
+
};
|
|
356
|
+
expect(await deleteBodyHasHealthCheckOption(regionalExternalNetwork)).toBe(true);
|
|
357
|
+
|
|
358
|
+
// INTERNAL deletes always remove unused health checks, so no option is offered.
|
|
359
|
+
expect(await deleteBodyHasHealthCheckOption({ ...regionalExternalNetwork, loadBalancerType: 'INTERNAL' })).toBe(
|
|
360
|
+
false,
|
|
361
|
+
);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe('GceLoadBalancerListenersSection', () => {
|
|
366
|
+
const app = { name: 'fnord' } as any;
|
|
367
|
+
|
|
368
|
+
beforeEach(() => {
|
|
369
|
+
vi.spyOn(HelpContentsRegistry, 'getHelpField').mockImplementation((id: string) => `help:${id}`);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// Expanded help renders the registered help contents inline, which makes the HelpField id observable.
|
|
373
|
+
function renderListeners(loadBalancer: any) {
|
|
374
|
+
return render(
|
|
375
|
+
<HelpTextExpandedContext.Provider value={true}>
|
|
376
|
+
<GceLoadBalancerListenersSection app={app} loadBalancer={loadBalancer} />
|
|
377
|
+
</HelpTextExpandedContext.Provider>,
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function definitions(container: HTMLElement): string[] {
|
|
382
|
+
return Array.from(container.querySelectorAll('dd')).map((node) => node.textContent);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
it('keeps the historical listener list for existing load balancer families', () => {
|
|
386
|
+
const network = renderListeners({
|
|
387
|
+
loadBalancerType: 'NETWORK',
|
|
388
|
+
listeners: [{ port: '8080' }],
|
|
389
|
+
elb: {
|
|
390
|
+
listenerDescriptions: [
|
|
391
|
+
{ listener: { instancePort: '8080', instanceProtocol: 'TCP', loadBalancerPort: '8080', protocol: 'TCP' } },
|
|
392
|
+
],
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
expect(network.getAllByRole('listitem').map((node) => node.textContent)).toEqual(['8080']);
|
|
397
|
+
expect(definitions(network.container)).toEqual([]);
|
|
398
|
+
network.unmount();
|
|
399
|
+
|
|
400
|
+
const http = renderListeners({
|
|
401
|
+
loadBalancerType: 'HTTP',
|
|
402
|
+
listeners: [{ port: '80' }, { port: '443', protocol: 'HTTPS' }],
|
|
403
|
+
provider: 'gce',
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
expect(http.getAllByRole('listitem').map((node) => node.textContent)).toEqual(['80', 'HTTPS:443']);
|
|
407
|
+
expect(http.queryByText(/^help:/)).not.toBeInTheDocument();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('renders REGIONAL_EXTERNAL_NETWORK listenerDescriptions without named-port help', () => {
|
|
411
|
+
const { container, queryByText } = renderListeners({
|
|
412
|
+
loadBalancerType: 'REGIONAL_EXTERNAL_NETWORK',
|
|
413
|
+
elb: {
|
|
414
|
+
listenerDescriptions: [
|
|
415
|
+
{ listener: { instancePort: '8080', instanceProtocol: 'TCP', loadBalancerPort: '8080', protocol: 'TCP' } },
|
|
416
|
+
],
|
|
417
|
+
},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
expect(definitions(container)).toEqual(['TCP:8080 → TCP:8080']);
|
|
421
|
+
expect(queryByText(/^help:/)).not.toBeInTheDocument();
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('prefers EXTERNAL_MANAGED elb.listenerDescriptions over normalized listeners', () => {
|
|
425
|
+
const { container, getByText } = renderListeners({
|
|
426
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
427
|
+
listeners: [{ port: '80' }, { port: '443' }],
|
|
428
|
+
provider: 'gce',
|
|
429
|
+
elb: {
|
|
430
|
+
listenerDescriptions: [
|
|
431
|
+
{
|
|
432
|
+
listener: {
|
|
433
|
+
instancePort: '8080',
|
|
434
|
+
instanceProtocol: 'HTTP',
|
|
435
|
+
loadBalancerPort: '443',
|
|
436
|
+
protocol: 'HTTPS',
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
},
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
expect(definitions(container)).toEqual(['HTTPS:443 → HTTP:8080']);
|
|
444
|
+
expect(getByText('help:gce.httpLoadBalancer.namedPort')).toBeInTheDocument();
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
it('falls back to normalized EXTERNAL_MANAGED listeners only when elb.listenerDescriptions are absent', () => {
|
|
448
|
+
const { container } = renderListeners({
|
|
449
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
450
|
+
listeners: [
|
|
451
|
+
{ port: '80', name: 'frontend-80' },
|
|
452
|
+
{ port: '443', certificate: 'projects/p/certificates/cert', name: 'frontend-443' },
|
|
453
|
+
],
|
|
454
|
+
provider: 'gce',
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
expect(definitions(container)).toEqual(['HTTP:80', 'HTTPS:443']);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('shows no listeners configured when a regional external network load balancer has no descriptions', () => {
|
|
461
|
+
const { container, getByText } = renderListeners({
|
|
462
|
+
loadBalancerType: 'REGIONAL_EXTERNAL_NETWORK',
|
|
463
|
+
listeners: [{ port: '8080' }],
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
expect(getByText('No listeners configured')).toBeInTheDocument();
|
|
467
|
+
expect(definitions(container)).toEqual([]);
|
|
64
468
|
});
|
|
65
469
|
});
|