@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
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
GceLoadBalancerProtocol,
|
|
5
|
+
IGceLoadBalancerCommand,
|
|
6
|
+
IGceLoadBalancerData,
|
|
7
|
+
IGceLoadBalancerDataItem,
|
|
8
|
+
IGceLoadBalancerHealthCheck,
|
|
9
|
+
IGceResourceReference,
|
|
10
|
+
} from '../common';
|
|
11
|
+
import { mergeGceResourceOptions } from '../common';
|
|
12
|
+
|
|
13
|
+
export type GceRegionalExternalNetworkSessionAffinity =
|
|
14
|
+
| 'NONE'
|
|
15
|
+
| 'CLIENT_IP'
|
|
16
|
+
| 'CLIENT_IP_PROTO'
|
|
17
|
+
| 'CLIENT_IP_PORT_PROTO';
|
|
18
|
+
|
|
19
|
+
export type IGceRegionalExternalNetworkLoadBalancerCommand = IGceLoadBalancerCommand & {
|
|
20
|
+
loadBalancerType: 'REGIONAL_EXTERNAL_NETWORK';
|
|
21
|
+
ports: string[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
interface IGceRegionalExternalNetworkDataItem extends IGceLoadBalancerDataItem {
|
|
25
|
+
account?: string;
|
|
26
|
+
address?: string;
|
|
27
|
+
addressType?: string;
|
|
28
|
+
credentials?: string;
|
|
29
|
+
networkTier?: string;
|
|
30
|
+
region?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface IGceRegionalExternalNetworkLoadBalancerOptions {
|
|
34
|
+
accounts: IGceRegionalExternalNetworkDataItem[];
|
|
35
|
+
addresses: IGceRegionalExternalNetworkDataItem[];
|
|
36
|
+
regions: IGceRegionalExternalNetworkDataItem[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface IGceRegionalExternalNetworkLoadBalancerEditorProps {
|
|
40
|
+
command: IGceRegionalExternalNetworkLoadBalancerCommand;
|
|
41
|
+
data: IGceLoadBalancerData;
|
|
42
|
+
onChange: (command: IGceRegionalExternalNetworkLoadBalancerCommand) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const SESSION_AFFINITIES: GceRegionalExternalNetworkSessionAffinity[] = [
|
|
46
|
+
'NONE',
|
|
47
|
+
'CLIENT_IP',
|
|
48
|
+
'CLIENT_IP_PROTO',
|
|
49
|
+
'CLIENT_IP_PORT_PROTO',
|
|
50
|
+
];
|
|
51
|
+
const PROTOCOLS: GceLoadBalancerProtocol[] = ['TCP', 'UDP'];
|
|
52
|
+
const NETWORK_TIERS = ['PREMIUM', 'STANDARD'];
|
|
53
|
+
const MAX_PORTS = 5;
|
|
54
|
+
|
|
55
|
+
export function createGceRegionalExternalNetworkHealthCheck(name: string): IGceLoadBalancerHealthCheck {
|
|
56
|
+
return {
|
|
57
|
+
healthCheckType: 'TCP',
|
|
58
|
+
name,
|
|
59
|
+
port: 80,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function buildGceRegionalExternalNetworkLoadBalancerOptions(
|
|
64
|
+
command: IGceRegionalExternalNetworkLoadBalancerCommand,
|
|
65
|
+
data: IGceLoadBalancerData,
|
|
66
|
+
): IGceRegionalExternalNetworkLoadBalancerOptions {
|
|
67
|
+
const accountMatches = (item: IGceRegionalExternalNetworkDataItem): boolean =>
|
|
68
|
+
!item.account && !item.credentials ? true : (item.account || item.credentials) === command.credentials;
|
|
69
|
+
const locationMatches = (item: IGceRegionalExternalNetworkDataItem): boolean =>
|
|
70
|
+
accountMatches(item) && (!item.region || item.region === command.region);
|
|
71
|
+
const listener = command.listeners[0];
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
accounts: mergeGceResourceOptions(data.accounts, command.credentials ? [{ name: command.credentials }] : []),
|
|
75
|
+
addresses: mergeGceResourceOptions(
|
|
76
|
+
(data.addresses as IGceRegionalExternalNetworkDataItem[])
|
|
77
|
+
.filter(locationMatches)
|
|
78
|
+
.filter((item) => item.addressType === 'EXTERNAL'),
|
|
79
|
+
references(listener?.address),
|
|
80
|
+
),
|
|
81
|
+
regions: mergeGceResourceOptions(data.regions, command.region ? [{ name: command.region }] : []),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function validateGceRegionalExternalNetworkLoadBalancerCommand(
|
|
86
|
+
command: IGceRegionalExternalNetworkLoadBalancerCommand,
|
|
87
|
+
): string[] {
|
|
88
|
+
const errors: string[] = [];
|
|
89
|
+
const backend = command.backendServices[0];
|
|
90
|
+
const ports = command.ports?.length ? command.ports : splitPorts(command.listeners[0]?.portRange);
|
|
91
|
+
|
|
92
|
+
if (!command.name.trim()) errors.push('Name is required.');
|
|
93
|
+
if (!command.credentials) errors.push('Account is required.');
|
|
94
|
+
if (!command.region) errors.push('Region is required.');
|
|
95
|
+
if (command.listeners[0]?.protocol !== 'TCP' && command.listeners[0]?.protocol !== 'UDP') {
|
|
96
|
+
errors.push('Protocol must be TCP or UDP.');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!ports.length) {
|
|
100
|
+
errors.push('Ports must be between 1 and 65535.');
|
|
101
|
+
} else {
|
|
102
|
+
if (ports.length > MAX_PORTS) {
|
|
103
|
+
errors.push('REGIONAL_EXTERNAL_NETWORK load balancers accept between one and five ports.');
|
|
104
|
+
}
|
|
105
|
+
if (ports.some((port) => !validPort(port))) {
|
|
106
|
+
errors.push('Ports must be between 1 and 65535.');
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!backend?.name?.trim()) errors.push('Backend service name is required.');
|
|
111
|
+
if (!hasHealthCheck(backend)) errors.push('Each backend service requires a health check.');
|
|
112
|
+
if (!healthCheckName(backend, command.healthChecks[0])) errors.push('Health check name is required.');
|
|
113
|
+
const backendHealthCheck =
|
|
114
|
+
backend?.healthCheck && typeof backend.healthCheck === 'object'
|
|
115
|
+
? (backend.healthCheck as IGceLoadBalancerHealthCheck)
|
|
116
|
+
: command.healthChecks[0];
|
|
117
|
+
if (!validPort(backendHealthCheck?.port)) {
|
|
118
|
+
errors.push('Health check port must be between 1 and 65535.');
|
|
119
|
+
}
|
|
120
|
+
const originalListener = command.original?.listeners[0];
|
|
121
|
+
if (
|
|
122
|
+
command.mode === 'edit' &&
|
|
123
|
+
originalListener &&
|
|
124
|
+
(command.listeners[0]?.protocol !== originalListener.protocol ||
|
|
125
|
+
ports.join(',') !== splitPorts(originalListener.portRange).join(','))
|
|
126
|
+
) {
|
|
127
|
+
errors.push('Protocol and ports cannot be changed while editing a REGIONAL_EXTERNAL_NETWORK load balancer.');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const sessionAffinity = String(backend?.sessionAffinity || 'NONE').toUpperCase();
|
|
131
|
+
if (!SESSION_AFFINITIES.includes(sessionAffinity as GceRegionalExternalNetworkSessionAffinity)) {
|
|
132
|
+
errors.push('Session affinity must be NONE, CLIENT_IP, CLIENT_IP_PROTO, or CLIENT_IP_PORT_PROTO.');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return errors;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function GceRegionalExternalNetworkLoadBalancerEditor({
|
|
139
|
+
command,
|
|
140
|
+
data,
|
|
141
|
+
onChange,
|
|
142
|
+
}: IGceRegionalExternalNetworkLoadBalancerEditorProps): JSX.Element {
|
|
143
|
+
const options = buildGceRegionalExternalNetworkLoadBalancerOptions(command, data);
|
|
144
|
+
const listener = command.listeners[0] || { name: command.name, portRange: '', protocol: 'TCP' };
|
|
145
|
+
const backend = command.backendServices[0] || { name: command.name, sessionAffinity: 'NONE' };
|
|
146
|
+
const healthCheck =
|
|
147
|
+
(typeof backend.healthCheck === 'object' ? backend.healthCheck : undefined) ||
|
|
148
|
+
command.healthChecks[0] ||
|
|
149
|
+
createGceRegionalExternalNetworkHealthCheck(command.name);
|
|
150
|
+
const editing = command.mode === 'edit';
|
|
151
|
+
const portsValue = (command.ports?.length ? command.ports : splitPorts(listener.portRange)).join(',');
|
|
152
|
+
|
|
153
|
+
const update = (updates: Partial<IGceRegionalExternalNetworkLoadBalancerCommand>): void =>
|
|
154
|
+
onChange({ ...command, ...updates } as IGceRegionalExternalNetworkLoadBalancerCommand);
|
|
155
|
+
// Reserved addresses belong to one account and region, so a scope change drops the selection.
|
|
156
|
+
const updateScope = (updates: Partial<IGceRegionalExternalNetworkLoadBalancerCommand>): void =>
|
|
157
|
+
update({ ...updates, listeners: [{ ...listener, address: undefined }] });
|
|
158
|
+
const updateListener = (updates: Partial<typeof listener>): void =>
|
|
159
|
+
update({ listeners: [{ ...listener, ...updates }] });
|
|
160
|
+
const updateBackend = (updates: Record<string, unknown>): void =>
|
|
161
|
+
update({ backendServices: [{ ...backend, ...updates }] });
|
|
162
|
+
const updateHealthCheck = (updates: Partial<IGceLoadBalancerHealthCheck>): void => {
|
|
163
|
+
const nextHealthCheck = { ...healthCheck, ...updates };
|
|
164
|
+
update({
|
|
165
|
+
backendServices: [{ ...backend, healthCheck: (nextHealthCheck as unknown) as IGceResourceReference }],
|
|
166
|
+
healthChecks: [nextHealthCheck],
|
|
167
|
+
});
|
|
168
|
+
};
|
|
169
|
+
const updateName = (name: string): void =>
|
|
170
|
+
update({
|
|
171
|
+
name,
|
|
172
|
+
listeners: [{ ...listener, name: listener.name === command.name ? name : listener.name }],
|
|
173
|
+
backendServices: [{ ...backend, name: backend.name === command.name ? name : backend.name }],
|
|
174
|
+
});
|
|
175
|
+
const updatePorts = (value: string): void => {
|
|
176
|
+
const ports = value.split(',');
|
|
177
|
+
update({
|
|
178
|
+
ports,
|
|
179
|
+
listeners: [{ ...listener, portRange: ports.join(',') }],
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
const updateAddress = (addressValue: string): void => {
|
|
183
|
+
const selected = (data.addresses as IGceRegionalExternalNetworkDataItem[]).find(
|
|
184
|
+
(item) => item.address === addressValue || item.name === addressValue,
|
|
185
|
+
);
|
|
186
|
+
const address: IGceResourceReference | undefined = addressValue
|
|
187
|
+
? {
|
|
188
|
+
...(selected || {}),
|
|
189
|
+
address: selected?.address || addressValue,
|
|
190
|
+
name: selected?.address || selected?.name || addressValue,
|
|
191
|
+
}
|
|
192
|
+
: undefined;
|
|
193
|
+
update({
|
|
194
|
+
listeners: [{ ...listener, address }],
|
|
195
|
+
networkTier: selected?.networkTier || (addressValue ? 'PREMIUM' : command.networkTier),
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<div className="form-horizontal gce-regional-external-network-load-balancer-editor">
|
|
201
|
+
{textField('Name', 'name', command.name, updateName, editing)}
|
|
202
|
+
{selectField(
|
|
203
|
+
'Account',
|
|
204
|
+
'credentials',
|
|
205
|
+
command.credentials,
|
|
206
|
+
options.accounts,
|
|
207
|
+
(credentials) => updateScope({ credentials }),
|
|
208
|
+
editing,
|
|
209
|
+
)}
|
|
210
|
+
{selectField('Region', 'region', command.region, options.regions, (region) => updateScope({ region }), editing)}
|
|
211
|
+
{selectField(
|
|
212
|
+
'IP address',
|
|
213
|
+
'address',
|
|
214
|
+
String(listener.address?.address || listener.address?.name || ''),
|
|
215
|
+
options.addresses,
|
|
216
|
+
updateAddress,
|
|
217
|
+
editing,
|
|
218
|
+
(item) => item.address || item.name,
|
|
219
|
+
)}
|
|
220
|
+
{selectField(
|
|
221
|
+
'Network tier',
|
|
222
|
+
'networkTier',
|
|
223
|
+
command.networkTier || 'PREMIUM',
|
|
224
|
+
NETWORK_TIERS.map((name) => ({ name })),
|
|
225
|
+
(networkTier) => update({ networkTier }),
|
|
226
|
+
editing || Boolean(listener.address),
|
|
227
|
+
({ name }) => name,
|
|
228
|
+
false,
|
|
229
|
+
)}
|
|
230
|
+
{selectField(
|
|
231
|
+
'Protocol',
|
|
232
|
+
'protocol',
|
|
233
|
+
listener.protocol,
|
|
234
|
+
PROTOCOLS.map((name) => ({ name })),
|
|
235
|
+
(protocol) => updateListener({ protocol: protocol as GceLoadBalancerProtocol }),
|
|
236
|
+
editing,
|
|
237
|
+
({ name }) => name,
|
|
238
|
+
false,
|
|
239
|
+
)}
|
|
240
|
+
{textField('Ports', 'ports', portsValue, updatePorts, editing)}
|
|
241
|
+
{selectField(
|
|
242
|
+
'Session affinity',
|
|
243
|
+
'sessionAffinity',
|
|
244
|
+
String(backend.sessionAffinity || 'NONE'),
|
|
245
|
+
SESSION_AFFINITIES.map((name) => ({ name })),
|
|
246
|
+
(sessionAffinity) => updateBackend({ sessionAffinity }),
|
|
247
|
+
false,
|
|
248
|
+
({ name }) => name,
|
|
249
|
+
false,
|
|
250
|
+
)}
|
|
251
|
+
|
|
252
|
+
<h4>Health Check</h4>
|
|
253
|
+
<div className="form-group" data-field="healthCheck">
|
|
254
|
+
{textField('Name', 'healthCheckName', healthCheck.name || '', (name) => updateHealthCheck({ name }))}
|
|
255
|
+
{numberField('Port', 'healthCheckPort', healthCheck.port, (port) => updateHealthCheck({ port }))}
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function textField(
|
|
262
|
+
label: string,
|
|
263
|
+
field: string,
|
|
264
|
+
value: string,
|
|
265
|
+
onChange: (value: string) => void,
|
|
266
|
+
disabled = false,
|
|
267
|
+
): JSX.Element {
|
|
268
|
+
return (
|
|
269
|
+
<div className="form-group" data-field={field}>
|
|
270
|
+
<label className="col-md-3 sm-label-right" htmlFor={controlId(field)}>
|
|
271
|
+
{label}
|
|
272
|
+
</label>
|
|
273
|
+
<div className="col-md-7">
|
|
274
|
+
<input
|
|
275
|
+
className="form-control input-sm"
|
|
276
|
+
disabled={disabled}
|
|
277
|
+
id={controlId(field)}
|
|
278
|
+
onChange={(event) => onChange(event.target.value)}
|
|
279
|
+
value={value || ''}
|
|
280
|
+
/>
|
|
281
|
+
</div>
|
|
282
|
+
</div>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function numberField(
|
|
287
|
+
label: string,
|
|
288
|
+
field: string,
|
|
289
|
+
value: unknown,
|
|
290
|
+
onChange: (value: number | string | undefined) => void,
|
|
291
|
+
): JSX.Element {
|
|
292
|
+
return (
|
|
293
|
+
<div className="form-group" data-field={field}>
|
|
294
|
+
<label className="col-md-3 sm-label-right" htmlFor={controlId(field)}>
|
|
295
|
+
{label}
|
|
296
|
+
</label>
|
|
297
|
+
<div className="col-md-7">
|
|
298
|
+
<input
|
|
299
|
+
className="form-control input-sm"
|
|
300
|
+
id={controlId(field)}
|
|
301
|
+
inputMode="numeric"
|
|
302
|
+
onChange={(event) =>
|
|
303
|
+
onChange(
|
|
304
|
+
event.target.value === ''
|
|
305
|
+
? undefined
|
|
306
|
+
: /^\d+$/.test(event.target.value)
|
|
307
|
+
? Number(event.target.value)
|
|
308
|
+
: event.target.value,
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
type="text"
|
|
312
|
+
value={value === undefined || value === null ? '' : String(value)}
|
|
313
|
+
/>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function selectField(
|
|
320
|
+
label: string,
|
|
321
|
+
field: string,
|
|
322
|
+
value: string | undefined,
|
|
323
|
+
options: readonly IGceRegionalExternalNetworkDataItem[],
|
|
324
|
+
onChange: (value: string) => void,
|
|
325
|
+
disabled = false,
|
|
326
|
+
getValue: (item: IGceRegionalExternalNetworkDataItem) => string = ({ name }) => name,
|
|
327
|
+
includeEmptyOption = true,
|
|
328
|
+
): JSX.Element {
|
|
329
|
+
return (
|
|
330
|
+
<div className="form-group" data-field={field}>
|
|
331
|
+
<label className="col-md-3 sm-label-right" htmlFor={controlId(field)}>
|
|
332
|
+
{label}
|
|
333
|
+
</label>
|
|
334
|
+
<div className="col-md-7">
|
|
335
|
+
<select
|
|
336
|
+
className="form-control input-sm"
|
|
337
|
+
disabled={disabled}
|
|
338
|
+
id={controlId(field)}
|
|
339
|
+
onChange={(event) => onChange(event.target.value)}
|
|
340
|
+
value={value || ''}
|
|
341
|
+
>
|
|
342
|
+
{includeEmptyOption && <option value="">Select...</option>}
|
|
343
|
+
{options.map((option) => {
|
|
344
|
+
const optionValue = getValue(option);
|
|
345
|
+
return (
|
|
346
|
+
<option key={optionValue} value={optionValue}>
|
|
347
|
+
{optionValue}
|
|
348
|
+
</option>
|
|
349
|
+
);
|
|
350
|
+
})}
|
|
351
|
+
</select>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function controlId(field: string): string {
|
|
358
|
+
return `gce-regional-external-network-${field}`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function references<T extends IGceLoadBalancerDataItem>(referenceValue?: T): T[] {
|
|
362
|
+
return referenceValue?.name || (referenceValue as IGceRegionalExternalNetworkDataItem | undefined)?.address
|
|
363
|
+
? [referenceValue]
|
|
364
|
+
: [];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function splitPorts(value: string | undefined): string[] {
|
|
368
|
+
return String(value || '').split(',');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function validPort(value: unknown): boolean {
|
|
372
|
+
const text = String(value ?? '');
|
|
373
|
+
if (!/^\d+$/.test(text)) return false;
|
|
374
|
+
const port = Number(text);
|
|
375
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function hasHealthCheck(backend?: { healthCheck?: unknown }): boolean {
|
|
379
|
+
const healthCheck = backend?.healthCheck;
|
|
380
|
+
if (!healthCheck) return false;
|
|
381
|
+
if (typeof healthCheck === 'string') return Boolean(healthCheck);
|
|
382
|
+
if (typeof healthCheck === 'object') {
|
|
383
|
+
const record = healthCheck as Record<string, unknown>;
|
|
384
|
+
return Boolean(record.healthCheckType || record.port || record.name);
|
|
385
|
+
}
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function healthCheckName(
|
|
390
|
+
backend: { healthCheck?: unknown } | undefined,
|
|
391
|
+
commandHealthCheck: IGceLoadBalancerHealthCheck | undefined,
|
|
392
|
+
): string {
|
|
393
|
+
const backendHealthCheck = backend?.healthCheck;
|
|
394
|
+
const name =
|
|
395
|
+
typeof backendHealthCheck === 'string'
|
|
396
|
+
? backendHealthCheck
|
|
397
|
+
: backendHealthCheck !== null && typeof backendHealthCheck === 'object'
|
|
398
|
+
? (backendHealthCheck as IGceResourceReference).name
|
|
399
|
+
: commandHealthCheck?.name;
|
|
400
|
+
return String(name || '').trim();
|
|
401
|
+
}
|