@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,5 +1,6 @@
|
|
|
1
|
+
import type { Mock } from 'vitest';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import {
|
|
3
|
+
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
|
3
4
|
|
|
4
5
|
import { AccountService, ConfirmationModalService, DeckRuntimeContext, SecurityGroupWriter } from '@spinnaker/core';
|
|
5
6
|
|
|
@@ -11,11 +12,11 @@ describe('GceSecurityGroupDetails', () => {
|
|
|
11
12
|
const RuntimeWrapper = ({ children }: React.PropsWithChildren<{}>) => (
|
|
12
13
|
<DeckRuntimeContext.Provider value={{ services: runtimeServices } as any}>{children}</DeckRuntimeContext.Provider>
|
|
13
14
|
);
|
|
14
|
-
const
|
|
15
|
+
const renderWithRuntime = (component: React.ReactElement) => render(<RuntimeWrapper>{component}</RuntimeWrapper>);
|
|
15
16
|
|
|
16
17
|
beforeEach(() => {
|
|
17
18
|
runtimeServices = {};
|
|
18
|
-
spyOn(AccountService, 'challengeDestructiveActions').
|
|
19
|
+
vi.spyOn(AccountService, 'challengeDestructiveActions').mockResolvedValue(false);
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
const firstRoute = {
|
|
@@ -48,83 +49,72 @@ describe('GceSecurityGroupDetails', () => {
|
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
async function flush(): Promise<void> {
|
|
52
|
-
await Promise.resolve();
|
|
53
|
-
await Promise.resolve();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
52
|
it('reloads the current firewall details after the security group data source refreshes', async () => {
|
|
57
53
|
let refreshDetails: (() => void) | undefined;
|
|
58
54
|
const app = {
|
|
59
55
|
securityGroups: {
|
|
60
|
-
onRefresh:
|
|
56
|
+
onRefresh: vi.fn().mockImplementation((callback: () => void) => {
|
|
61
57
|
refreshDetails = callback;
|
|
62
|
-
return
|
|
58
|
+
return vi.fn();
|
|
63
59
|
}),
|
|
64
60
|
},
|
|
65
61
|
};
|
|
66
62
|
const reader = {
|
|
67
|
-
getSecurityGroupDetails:
|
|
68
|
-
.
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
Promise.resolve(details('first-firewall', 'after refresh')),
|
|
72
|
-
),
|
|
63
|
+
getSecurityGroupDetails: vi
|
|
64
|
+
.fn()
|
|
65
|
+
.mockReturnValueOnce(Promise.resolve(details('first-firewall', 'before refresh')))
|
|
66
|
+
.mockReturnValueOnce(Promise.resolve(details('first-firewall', 'after refresh'))),
|
|
73
67
|
};
|
|
74
68
|
runtimeServices.securityGroupReader = reader;
|
|
75
|
-
const
|
|
69
|
+
const rendered = renderWithRuntime(<GceSecurityGroupDetails app={app as any} resolvedSecurityGroup={firstRoute} />);
|
|
76
70
|
|
|
77
|
-
await
|
|
78
|
-
wrapper.update();
|
|
79
|
-
expect(wrapper.text()).toContain('before refresh');
|
|
71
|
+
await waitFor(() => expect(screen.getByText('before refresh')).toBeInTheDocument());
|
|
80
72
|
expect(refreshDetails).toBeDefined();
|
|
81
73
|
|
|
82
|
-
refreshDetails?.();
|
|
83
|
-
await flush();
|
|
84
|
-
wrapper.update();
|
|
74
|
+
await act(async () => refreshDetails?.());
|
|
85
75
|
|
|
86
76
|
expect(reader.getSecurityGroupDetails).toHaveBeenCalledTimes(2);
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
await waitFor(() => expect(screen.getByText('after refresh')).toBeInTheDocument());
|
|
78
|
+
rendered.unmount();
|
|
89
79
|
});
|
|
90
80
|
|
|
91
81
|
it('clears prior details and actions on route change and ignores an out-of-order response', async () => {
|
|
92
82
|
const secondRequest = deferred<any>();
|
|
93
83
|
const thirdRequest = deferred<any>();
|
|
94
84
|
const reader = {
|
|
95
|
-
getSecurityGroupDetails:
|
|
96
|
-
.
|
|
97
|
-
.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
thirdRequest.promise,
|
|
101
|
-
),
|
|
85
|
+
getSecurityGroupDetails: vi
|
|
86
|
+
.fn()
|
|
87
|
+
.mockReturnValueOnce(Promise.resolve(details('first-firewall', 'first details')))
|
|
88
|
+
.mockReturnValueOnce(secondRequest.promise)
|
|
89
|
+
.mockReturnValueOnce(thirdRequest.promise),
|
|
102
90
|
};
|
|
103
91
|
runtimeServices.securityGroupReader = reader;
|
|
104
|
-
const app = { securityGroups: { onRefresh: () =>
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
await
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
await
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
92
|
+
const app = { securityGroups: { onRefresh: () => vi.fn() } };
|
|
93
|
+
const rendered = renderWithRuntime(<GceSecurityGroupDetails app={app as any} resolvedSecurityGroup={firstRoute} />);
|
|
94
|
+
|
|
95
|
+
await waitFor(() => expect(screen.getByText('first details')).toBeInTheDocument());
|
|
96
|
+
expect(screen.getByRole('button', { name: /Firewall Actions/ })).toBeInTheDocument();
|
|
97
|
+
|
|
98
|
+
rendered.rerender(
|
|
99
|
+
<RuntimeWrapper>
|
|
100
|
+
<GceSecurityGroupDetails app={app as any} resolvedSecurityGroup={{ ...firstRoute, name: 'second-firewall' }} />
|
|
101
|
+
</RuntimeWrapper>,
|
|
102
|
+
);
|
|
103
|
+
expect(screen.queryByRole('button', { name: /Firewall Actions/ })).not.toBeInTheDocument();
|
|
104
|
+
expect(screen.queryByText('first details')).not.toBeInTheDocument();
|
|
105
|
+
|
|
106
|
+
rendered.rerender(
|
|
107
|
+
<RuntimeWrapper>
|
|
108
|
+
<GceSecurityGroupDetails app={app as any} resolvedSecurityGroup={{ ...firstRoute, name: 'third-firewall' }} />
|
|
109
|
+
</RuntimeWrapper>,
|
|
110
|
+
);
|
|
111
|
+
await act(async () => secondRequest.resolve(details('second-firewall', 'stale second details')));
|
|
112
|
+
expect(screen.queryByText('stale second details')).not.toBeInTheDocument();
|
|
113
|
+
|
|
114
|
+
await act(async () => thirdRequest.resolve(details('third-firewall', 'current third details')));
|
|
115
|
+
await waitFor(() => expect(screen.getByText('current third details')).toBeInTheDocument());
|
|
116
|
+
expect(screen.getByRole('button', { name: /Firewall Actions/ })).toBeInTheDocument();
|
|
117
|
+
rendered.unmount();
|
|
128
118
|
});
|
|
129
119
|
});
|
|
130
120
|
|
|
@@ -133,11 +123,11 @@ describe('GceSecurityGroupActions', () => {
|
|
|
133
123
|
const RuntimeWrapper = ({ children }: React.PropsWithChildren<{}>) => (
|
|
134
124
|
<DeckRuntimeContext.Provider value={{ services: runtimeServices }}>{children}</DeckRuntimeContext.Provider>
|
|
135
125
|
);
|
|
136
|
-
const
|
|
126
|
+
const renderActions = (component: React.ReactElement) => render(<RuntimeWrapper>{component}</RuntimeWrapper>);
|
|
137
127
|
|
|
138
128
|
const app = {
|
|
139
129
|
name: 'my-app',
|
|
140
|
-
securityGroups: { refresh:
|
|
130
|
+
securityGroups: { refresh: vi.fn() },
|
|
141
131
|
};
|
|
142
132
|
const resolvedSecurityGroup = {
|
|
143
133
|
accountId: 'my-account',
|
|
@@ -156,24 +146,26 @@ describe('GceSecurityGroupActions', () => {
|
|
|
156
146
|
};
|
|
157
147
|
|
|
158
148
|
it('opens edit and clone modals and confirms deletion with complete firewall identity', () => {
|
|
159
|
-
spyOn(GceSecurityGroupModal, 'show');
|
|
160
|
-
spyOn(ConfirmationModalService, 'confirm');
|
|
161
|
-
spyOn(SecurityGroupWriter, 'deleteSecurityGroup').
|
|
162
|
-
const
|
|
149
|
+
vi.spyOn(GceSecurityGroupModal, 'show').mockReturnValue(undefined);
|
|
150
|
+
vi.spyOn(ConfirmationModalService, 'confirm').mockReturnValue(undefined);
|
|
151
|
+
vi.spyOn(SecurityGroupWriter, 'deleteSecurityGroup').mockReturnValue(Promise.resolve({} as any));
|
|
152
|
+
const rendered = renderActions(
|
|
163
153
|
<GceSecurityGroupActions
|
|
164
154
|
app={app as any}
|
|
165
155
|
resolvedSecurityGroup={resolvedSecurityGroup}
|
|
166
156
|
securityGroup={securityGroup}
|
|
167
157
|
/>,
|
|
168
158
|
);
|
|
169
|
-
const items =
|
|
159
|
+
const items = Array.from(rendered.container.querySelectorAll<HTMLButtonElement>('button[data-action]'));
|
|
170
160
|
|
|
171
|
-
expect(items.map((item) => item.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
161
|
+
expect(items.map((item) => item.textContent?.trim())).toEqual([
|
|
162
|
+
'Edit Inbound Rules',
|
|
163
|
+
'Clone Firewall',
|
|
164
|
+
'Delete Firewall',
|
|
165
|
+
]);
|
|
166
|
+
items.forEach((item) => fireEvent.click(item));
|
|
175
167
|
|
|
176
|
-
const firewallWithIdentity =
|
|
168
|
+
const firewallWithIdentity = expect.objectContaining({
|
|
177
169
|
accountId: 'my-account',
|
|
178
170
|
id: 'my-firewall',
|
|
179
171
|
name: 'my-firewall',
|
|
@@ -197,25 +189,25 @@ describe('GceSecurityGroupActions', () => {
|
|
|
197
189
|
runtimeServices,
|
|
198
190
|
);
|
|
199
191
|
expect(ConfirmationModalService.confirm).toHaveBeenCalledWith(
|
|
200
|
-
|
|
192
|
+
expect.objectContaining({
|
|
201
193
|
account: 'my-account',
|
|
202
194
|
buttonText: 'Delete my-firewall',
|
|
203
195
|
header: 'Really delete my-firewall?',
|
|
204
|
-
taskMonitorConfig:
|
|
196
|
+
taskMonitorConfig: expect.objectContaining({ application: app as any, title: 'Deleting my-firewall' }),
|
|
205
197
|
}),
|
|
206
198
|
);
|
|
207
199
|
|
|
208
|
-
const confirmation = (ConfirmationModalService.confirm as
|
|
200
|
+
const confirmation = (ConfirmationModalService.confirm as Mock).mock.lastCall[0];
|
|
209
201
|
confirmation.submitMethod();
|
|
210
202
|
expect(SecurityGroupWriter.deleteSecurityGroup).toHaveBeenCalledWith(
|
|
211
203
|
firewallWithIdentity,
|
|
212
204
|
app as any,
|
|
213
|
-
|
|
205
|
+
expect.objectContaining({ cloudProvider: 'gce', securityGroupName: 'my-firewall' }),
|
|
214
206
|
);
|
|
215
207
|
});
|
|
216
208
|
|
|
217
209
|
it('disables host-project shared-VPC actions and explains why they are read-only', () => {
|
|
218
|
-
const
|
|
210
|
+
const rendered = renderActions(
|
|
219
211
|
<GceSecurityGroupActions
|
|
220
212
|
app={app as any}
|
|
221
213
|
resolvedSecurityGroup={resolvedSecurityGroup}
|
|
@@ -223,10 +215,9 @@ describe('GceSecurityGroupActions', () => {
|
|
|
223
215
|
/>,
|
|
224
216
|
);
|
|
225
217
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
);
|
|
218
|
+
const items = Array.from(rendered.container.querySelectorAll<HTMLButtonElement>('button[data-action]'));
|
|
219
|
+
expect(items).toHaveLength(3);
|
|
220
|
+
items.forEach((item) => expect(item).toBeDisabled());
|
|
221
|
+
expect(screen.getByText('You cannot modify shared VPC host project firewall rules.')).toBeInTheDocument();
|
|
231
222
|
});
|
|
232
223
|
});
|
|
@@ -11,25 +11,25 @@ describe('GceServerGroupCommandBuilder', () => {
|
|
|
11
11
|
let xpnNamingService;
|
|
12
12
|
|
|
13
13
|
beforeEach(() => {
|
|
14
|
-
spyOn(AccountService, 'listAccounts').
|
|
15
|
-
spyOn(ExpectedArtifactService, 'getExpectedArtifactsAvailableToStage').
|
|
16
|
-
instanceTypeService =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
instanceTypeService.getCategories.
|
|
14
|
+
vi.spyOn(AccountService, 'listAccounts').mockResolvedValue([{ name: 'test-account' }]);
|
|
15
|
+
vi.spyOn(ExpectedArtifactService, 'getExpectedArtifactsAvailableToStage').mockReturnValue([]);
|
|
16
|
+
instanceTypeService = {
|
|
17
|
+
getCategories: vi.fn(),
|
|
18
|
+
getInstanceTypeDetails: vi.fn(),
|
|
19
|
+
getCategoryForInstanceType: vi.fn(),
|
|
20
|
+
};
|
|
21
|
+
instanceTypeService.getCategories.mockResolvedValue([
|
|
22
22
|
{ type: 'custom', families: [{ instanceTypes: [{ name: 'n1-standard-1' }] }] },
|
|
23
23
|
]);
|
|
24
|
-
instanceTypeService.getInstanceTypeDetails.
|
|
24
|
+
instanceTypeService.getInstanceTypeDetails.mockResolvedValue({
|
|
25
25
|
storage: { localSSDSupported: false, size: 10, count: 1 },
|
|
26
26
|
});
|
|
27
|
-
instanceTypeService.getCategoryForInstanceType.
|
|
28
|
-
customInstanceBuilder =
|
|
29
|
-
customInstanceBuilder.parseInstanceTypeString.
|
|
30
|
-
xpnNamingService =
|
|
31
|
-
xpnNamingService.deriveProjectId.
|
|
32
|
-
xpnNamingService.decorateXpnResourceIfNecessary.
|
|
27
|
+
instanceTypeService.getCategoryForInstanceType.mockResolvedValue('custom');
|
|
28
|
+
customInstanceBuilder = { parseInstanceTypeString: vi.fn() };
|
|
29
|
+
customInstanceBuilder.parseInstanceTypeString.mockReturnValue({});
|
|
30
|
+
xpnNamingService = { deriveProjectId: vi.fn(), decorateXpnResourceIfNecessary: vi.fn() };
|
|
31
|
+
xpnNamingService.deriveProjectId.mockReturnValue('test-project');
|
|
32
|
+
xpnNamingService.decorateXpnResourceIfNecessary.mockImplementation((_projectId, resource) => resource);
|
|
33
33
|
|
|
34
34
|
const promiseService = { all: (values) => Promise.all(values), resolve: (value) => Promise.resolve(value) };
|
|
35
35
|
builder = new GceServerGroupCommandBuilder(
|
|
@@ -153,6 +153,26 @@ describe('GceServerGroupCommandBuilder', () => {
|
|
|
153
153
|
expect(serverGroup.instanceFlexibilityPolicy.instanceSelections.preferred.machineTypes).toEqual(['n2-standard-8']);
|
|
154
154
|
});
|
|
155
155
|
|
|
156
|
+
it('keeps Clouddriver-managed load balancer metadata out of clone instance metadata', async () => {
|
|
157
|
+
const promiseService = { all: (values) => Promise.all(values), resolve: (value) => Promise.resolve(value) };
|
|
158
|
+
const defaultHiddenKeysBuilder = new GceServerGroupCommandBuilder(
|
|
159
|
+
promiseService,
|
|
160
|
+
instanceTypeService,
|
|
161
|
+
customInstanceBuilder,
|
|
162
|
+
xpnNamingService,
|
|
163
|
+
);
|
|
164
|
+
const serverGroup = buildServerGroup();
|
|
165
|
+
serverGroup.launchConfig.instanceTemplate.properties.metadata.items = [
|
|
166
|
+
{ key: 'load-balancer-names', value: 'listener-a,listener-b' },
|
|
167
|
+
{ key: 'regional-external-load-balancer-names', value: 'listener-a,listener-b' },
|
|
168
|
+
{ key: 'team', value: 'payments' },
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
const command = await defaultHiddenKeysBuilder.buildServerGroupCommandFromExisting(application(), serverGroup);
|
|
172
|
+
|
|
173
|
+
expect(command.instanceMetadata).toEqual({ team: 'payments' });
|
|
174
|
+
});
|
|
175
|
+
|
|
156
176
|
it('preserves absent versus explicitly empty clone flexibility policies', async () => {
|
|
157
177
|
const absent = buildServerGroup();
|
|
158
178
|
const empty = buildServerGroup();
|
|
@@ -428,6 +428,7 @@ export class GceServerGroupConfigurationService {
|
|
|
428
428
|
command.loadBalancers,
|
|
429
429
|
command.credentials,
|
|
430
430
|
newLoadBalancerObjects,
|
|
431
|
+
command.region,
|
|
431
432
|
);
|
|
432
433
|
const matched = _.intersection(command.backingData.filtered.loadBalancers, command.loadBalancers);
|
|
433
434
|
const removed = _.xor(matched, command.loadBalancers);
|
|
@@ -452,7 +453,12 @@ export class GceServerGroupConfigurationService {
|
|
|
452
453
|
const lbIndex = command.backingData.filtered.loadBalancerIndex;
|
|
453
454
|
|
|
454
455
|
const backendServices = command.loadBalancers.reduce((backendServices, lbName) => {
|
|
455
|
-
|
|
456
|
+
// Clouddriver attaches every EXTERNAL_MANAGED backend service; its regional names must not
|
|
457
|
+
// enter the global backend-service-names metadata.
|
|
458
|
+
if (
|
|
459
|
+
gceHttpLoadBalancerUtils.isHttpLoadBalancer(lbIndex[lbName]) &&
|
|
460
|
+
lbIndex[lbName].loadBalancerType !== 'EXTERNAL_MANAGED'
|
|
461
|
+
) {
|
|
456
462
|
backendServices[lbName] = _.intersection(lbIndex[lbName].backendServices, backendsFromMetadata);
|
|
457
463
|
}
|
|
458
464
|
return backendServices;
|
|
@@ -1,9 +1,79 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { nativePromiseService } from '@spinnaker/core';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import {
|
|
6
|
+
GCE_DISTRIBUTION_POLICY_TARGET_SHAPES,
|
|
7
|
+
GceServerGroupConfigurationService,
|
|
8
|
+
} from './serverGroupConfiguration.service';
|
|
9
|
+
|
|
10
|
+
describe('GceServerGroupConfigurationService', () => {
|
|
11
|
+
let service;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
service = new GceServerGroupConfigurationService(nativePromiseService, {
|
|
15
|
+
securityGroupReader: {},
|
|
16
|
+
loadBalancerReader: {},
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('configureLoadBalancerOptions', () => {
|
|
21
|
+
it('scopes EXTERNAL_MANAGED listener normalization by account and region without seeding global backends', () => {
|
|
22
|
+
const command = {
|
|
23
|
+
credentials: 'account-a',
|
|
24
|
+
region: 'europe-west1',
|
|
25
|
+
loadBalancers: ['external-listener'],
|
|
26
|
+
backendServiceMetadata: ['europe-backend'],
|
|
27
|
+
backingData: {
|
|
28
|
+
loadBalancers: [
|
|
29
|
+
{
|
|
30
|
+
accounts: [
|
|
31
|
+
{
|
|
32
|
+
name: 'account-a',
|
|
33
|
+
regions: [
|
|
34
|
+
{
|
|
35
|
+
loadBalancers: [
|
|
36
|
+
{
|
|
37
|
+
account: 'account-a',
|
|
38
|
+
listeners: [{ name: 'external-listener' }],
|
|
39
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
40
|
+
name: 'external-listener',
|
|
41
|
+
provider: 'gce',
|
|
42
|
+
region: 'us-central1',
|
|
43
|
+
urlMapName: 'app-main',
|
|
44
|
+
backendServices: ['central-backend', 'other-backend'],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
account: 'account-a',
|
|
48
|
+
listeners: [{ name: 'external-listener' }],
|
|
49
|
+
loadBalancerType: 'EXTERNAL_MANAGED',
|
|
50
|
+
name: 'external-listener',
|
|
51
|
+
provider: 'gce',
|
|
52
|
+
region: 'europe-west1',
|
|
53
|
+
urlMapName: 'app-main',
|
|
54
|
+
backendServices: ['europe-backend'],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
filtered: {},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
service.configureLoadBalancerOptions(command);
|
|
68
|
+
|
|
69
|
+
expect(command.loadBalancers).toEqual(['app-main (account-a/europe-west1/EXTERNAL_MANAGED)']);
|
|
70
|
+
expect(command.backendServices).toBeUndefined();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('target shapes', () => {
|
|
75
|
+
it('exposes BALANCED and ANY_SINGLE_ZONE alongside ANY and EVEN', () => {
|
|
76
|
+
expect(GCE_DISTRIBUTION_POLICY_TARGET_SHAPES).toEqual(['ANY', 'EVEN', 'BALANCED', 'ANY_SINGLE_ZONE']);
|
|
77
|
+
});
|
|
8
78
|
});
|
|
9
79
|
});
|