@spinnaker/kubernetes 2026.2.3 → 2026.3.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/index.d.ts +1 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/instance/details/KubernetesInstanceDetails.d.ts +54 -0
- package/dist/instance/details/kubernetesInstanceDetails.utils.d.ts +13 -0
- package/dist/instance/index.d.ts +1 -1
- package/dist/kubernetes.module.d.ts +5 -1
- package/dist/loadBalancer/transformer.d.ts +1 -1
- package/dist/manifest/AnnotationCustomSections.d.ts +3 -2
- package/dist/manifest/ManifestKindSearch.d.ts +1 -1
- package/dist/manifest/delete/Delete.d.ts +0 -1
- package/dist/manifest/delete/DeleteModal.d.ts +1 -1
- package/dist/manifest/delete/delete.types.d.ts +4 -0
- package/dist/manifest/index.d.ts +3 -6
- package/dist/manifest/manifest.service.d.ts +5 -0
- package/dist/manifest/manifestCommandBuilder.service.d.ts +1 -1
- package/dist/manifest/rollout/PauseRollout.d.ts +0 -1
- package/dist/manifest/rollout/ResumeRollout.d.ts +0 -1
- package/dist/manifest/rollout/RollingRestart.d.ts +4 -4
- package/dist/manifest/rollout/UndoRollout.d.ts +0 -1
- package/dist/manifest/scale/Scale.d.ts +1 -2
- package/dist/manifest/scale/ScaleModal.d.ts +3 -3
- package/dist/manifest/scale/ScaleSettingsForm.d.ts +7 -1
- package/dist/manifest/selector/ManifestSelector.d.ts +1 -1
- package/dist/manifest/traffic/ManifestTrafficService.d.ts +2 -2
- package/dist/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.d.ts +1 -1
- package/dist/pipelines/stages/deployManifest/manifestStatus/DeployStatus.d.ts +1 -0
- package/dist/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.d.ts +6 -3
- package/dist/pipelines/stages/traffic/disableManifest.stage.d.ts +2 -1
- package/dist/pipelines/stages/traffic/enableManifest.stage.d.ts +2 -1
- package/dist/rawResource/index.d.ts +3 -1
- package/dist/rawResource/rawResource.dataSource.d.ts +1 -1
- package/dist/rawResource/rawResource.states.d.ts +4 -1
- package/dist/resources/resources.state.d.ts +2 -1
- package/dist/securityGroup/details/KubernetesSecurityGroupDetails.d.ts +49 -0
- package/dist/securityGroup/index.d.ts +3 -1
- package/dist/securityGroup/transformer.d.ts +4 -1
- package/dist/serverGroup/serverGroupCommandBuilder.d.ts +1 -1
- package/dist/serverGroup/serverGroupTransformer.d.ts +1 -1
- package/dist/serverGroupManager/details/ServerGroupManagerDetails.d.ts +7 -3
- package/package.json +3 -12
- package/src/help/kubernetes.help.ts +2 -0
- package/src/index.ts +2 -2
- package/src/instance/details/KubernetesInstanceDetails.spec.tsx +451 -0
- package/src/instance/details/KubernetesInstanceDetails.tsx +342 -0
- package/src/instance/details/kubernetesInstanceDetails.utils.ts +77 -0
- package/src/instance/index.ts +1 -1
- package/src/kubernetes.module.spec.ts +25 -0
- package/src/kubernetes.module.ts +23 -87
- package/src/loadBalancer/details/KubernetesLoadBalancerActions.tsx +30 -35
- package/src/loadBalancer/details/sections/LoadBalancerAnnotationCustomSection.tsx +2 -3
- package/src/loadBalancer/transformer.ts +1 -1
- package/src/manifest/AnnotationCustomSections.spec.tsx +178 -0
- package/src/manifest/AnnotationCustomSections.tsx +179 -2
- package/src/manifest/ManifestKindSearch.tsx +1 -1
- package/src/manifest/delete/Delete.tsx +0 -9
- package/src/manifest/delete/DeleteModal.tsx +69 -72
- package/src/manifest/delete/delete.types.ts +4 -0
- package/src/manifest/index.ts +3 -6
- package/src/manifest/manifest.service.ts +18 -1
- package/src/manifest/manifestCommandBuilder.service.spec.ts +41 -0
- package/src/manifest/manifestCommandBuilder.service.ts +45 -59
- package/src/manifest/rollout/PauseRollout.tsx +1 -9
- package/src/manifest/rollout/ResumeRollout.tsx +2 -10
- package/src/manifest/rollout/RollingRestart.tsx +16 -22
- package/src/manifest/rollout/UndoRollout.tsx +1 -10
- package/src/manifest/rollout/UndoRolloutModal.tsx +5 -1
- package/src/manifest/scale/Scale.tsx +12 -11
- package/src/manifest/scale/ScaleModal.tsx +43 -31
- package/src/manifest/scale/ScaleSettingsForm.tsx +8 -1
- package/src/manifest/selector/ManifestSelector.spec.tsx +89 -46
- package/src/manifest/selector/ManifestSelector.tsx +4 -5
- package/src/manifest/traffic/ManifestTrafficService.ts +2 -2
- package/src/manifest/wizard/ManifestWizard.tsx +1 -1
- package/src/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.tsx +1 -1
- package/src/pipelines/stages/deleteManifest/DeleteManifestStageConfig.tsx +1 -1
- package/src/pipelines/stages/deployManifest/DeployManifestStageForm.tsx +16 -0
- package/src/pipelines/stages/deployManifest/ManifestCopier.spec.ts +52 -77
- package/src/pipelines/stages/deployManifest/ManifestDeploymentOptions.spec.tsx +2 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/DeployStatus.tsx +39 -4
- package/src/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.spec.tsx +35 -0
- package/src/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.tsx +10 -5
- package/src/pipelines/stages/traffic/ManifestTrafficStageRegistration.spec.ts +34 -0
- package/src/pipelines/stages/traffic/disableManifest.stage.ts +17 -19
- package/src/pipelines/stages/traffic/enableManifest.stage.ts +17 -19
- package/src/rawResource/index.ts +18 -1
- package/src/rawResource/rawResource.dataSource.ts +5 -8
- package/src/rawResource/rawResource.states.ts +50 -51
- package/src/resources/resources.state.spec.ts +138 -0
- package/src/resources/resources.state.ts +30 -35
- package/src/securityGroup/details/KubernetesSecurityGroupDetails.spec.tsx +292 -0
- package/src/securityGroup/details/KubernetesSecurityGroupDetails.tsx +304 -0
- package/src/securityGroup/index.ts +3 -1
- package/src/securityGroup/transformer.ts +2 -10
- package/src/serverGroup/details/KubernetesServerGroupActions.tsx +31 -61
- package/src/serverGroup/details/kubernetesServerGroupDetailsGetter.ts +1 -1
- package/src/serverGroup/details/sections/ServerGroupAnnotationCustomSection.tsx +2 -3
- package/src/serverGroup/serverGroupCommandBuilder.ts +1 -1
- package/src/serverGroup/serverGroupTransformer.ts +1 -1
- package/src/serverGroupManager/details/ServerGroupManagerActions.tsx +48 -128
- package/src/serverGroupManager/details/ServerGroupManagerDetails.spec.ts +12 -0
- package/src/serverGroupManager/details/ServerGroupManagerDetails.tsx +19 -8
- package/src/serverGroupManager/details/sections/ServerGroupManagerAnnotationCustomSection.tsx +2 -3
- package/src/serverGroupManager/details/useKubernetesServerGroupManagerDetails.spec.tsx +112 -0
- package/src/serverGroupManager/details/useKubernetesServerGroupManagerDetails.tsx +50 -29
- package/dist/instance/details/details.controller.d.ts +0 -1
- package/dist/manifest/annotationCustomSections.component.d.ts +0 -3
- package/dist/manifest/delete/delete.controller.d.ts +0 -12
- package/dist/manifest/delete/deleteOptionsForm.component.d.ts +0 -1
- package/dist/manifest/editor/json/jsonEditor.component.d.ts +0 -1
- package/dist/manifest/manifestEvents.component.d.ts +0 -1
- package/dist/manifest/manifestImageDetails.component.d.ts +0 -1
- package/dist/manifest/manifestLabels.component.d.ts +0 -1
- package/dist/manifest/manifestQos.component.d.ts +0 -1
- package/dist/manifest/manifestResources.component.d.ts +0 -1
- package/dist/manifest/rollout/pause.controller.d.ts +0 -1
- package/dist/manifest/rollout/resume.controller.d.ts +0 -1
- package/dist/manifest/rollout/undo.controller.d.ts +0 -5
- package/dist/manifest/scale/scale.controller.d.ts +0 -8
- package/dist/manifest/scale/scaleSettingsForm.component.d.ts +0 -1
- package/dist/manifest/selector/selector.component.d.ts +0 -1
- package/dist/manifest/status/condition.component.d.ts +0 -1
- package/dist/manifest/status/status.component.d.ts +0 -1
- package/dist/rawResource/rawResource.module.d.ts +0 -1
- package/dist/reactShims/index.d.ts +0 -1
- package/dist/reactShims/kubernetes.react.injector.d.ts +0 -11
- package/dist/reactShims/kubernetes.react.module.d.ts +0 -1
- package/dist/securityGroup/details/details.controller.d.ts +0 -1
- package/src/instance/details/details.controller.ts +0 -186
- package/src/instance/details/details.html +0 -80
- package/src/manifest/annotationCustomSections.component.ts +0 -114
- package/src/manifest/delete/delete.controller.ts +0 -80
- package/src/manifest/delete/delete.html +0 -42
- package/src/manifest/delete/deleteOptionsForm.component.ts +0 -52
- package/src/manifest/editor/json/jsonEditor.component.ts +0 -12
- package/src/manifest/manifestEvents.component.ts +0 -12
- package/src/manifest/manifestImageDetails.component.ts +0 -12
- package/src/manifest/manifestLabels.component.ts +0 -12
- package/src/manifest/manifestQos.component.ts +0 -12
- package/src/manifest/manifestResources.component.ts +0 -12
- package/src/manifest/rollout/pause.controller.ts +0 -66
- package/src/manifest/rollout/pause.html +0 -28
- package/src/manifest/rollout/resume.controller.ts +0 -66
- package/src/manifest/rollout/resume.html +0 -28
- package/src/manifest/rollout/undo.controller.ts +0 -74
- package/src/manifest/rollout/undo.html +0 -44
- package/src/manifest/scale/scale.controller.ts +0 -75
- package/src/manifest/scale/scale.html +0 -32
- package/src/manifest/scale/scaleSettingsForm.component.ts +0 -11
- package/src/manifest/selector/selector.component.ts +0 -17
- package/src/manifest/status/condition.component.ts +0 -12
- package/src/manifest/status/status.component.ts +0 -11
- package/src/rawResource/rawResource.module.ts +0 -8
- package/src/reactShims/index.ts +0 -1
- package/src/reactShims/kubernetes.react.injector.ts +0 -30
- package/src/reactShims/kubernetes.react.module.ts +0 -12
- package/src/securityGroup/details/details.controller.ts +0 -118
- package/src/securityGroup/details/details.html +0 -91
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { mount } from 'enzyme';
|
|
2
|
+
import type { ReactWrapper } from 'enzyme';
|
|
2
3
|
import React from 'react';
|
|
4
|
+
import { act } from 'react-dom/test-utils';
|
|
3
5
|
import type { Option } from 'react-select';
|
|
4
6
|
import Select, { Creatable } from 'react-select';
|
|
5
7
|
|
|
@@ -21,16 +23,40 @@ import LabelEditor from './labelEditor/LabelEditor';
|
|
|
21
23
|
import Spy = jasmine.Spy;
|
|
22
24
|
|
|
23
25
|
describe('<ManifestSelector />', () => {
|
|
26
|
+
let accountService: Spy;
|
|
27
|
+
let mountedComponents: Array<ReactWrapper<ManifestSelector>>;
|
|
24
28
|
let searchService: Spy;
|
|
25
29
|
|
|
26
30
|
beforeEach(() => {
|
|
31
|
+
mountedComponents = [];
|
|
27
32
|
searchService = spyOn(ManifestKindSearchService, 'search').and.returnValue(Promise.resolve([]));
|
|
28
|
-
spyOn(AccountService, 'getAllAccountDetailsForProvider').and.returnValue(Promise.resolve([]));
|
|
33
|
+
accountService = spyOn(AccountService, 'getAllAccountDetailsForProvider').and.returnValue(Promise.resolve([]));
|
|
29
34
|
});
|
|
30
35
|
|
|
36
|
+
afterEach(async () => {
|
|
37
|
+
await act(async () => {
|
|
38
|
+
await Promise.all(searchService.calls.all().map(({ returnValue }) => returnValue));
|
|
39
|
+
mountedComponents.splice(0).forEach((wrapper) => wrapper.unmount());
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const component = async (selector: any, props: any = {}) => {
|
|
44
|
+
const wrapper = mount<ManifestSelector>(
|
|
45
|
+
(<ManifestSelector onChange={noop} selector={selector} {...props} />) as any,
|
|
46
|
+
);
|
|
47
|
+
mountedComponents.push(wrapper);
|
|
48
|
+
|
|
49
|
+
await act(async () => {
|
|
50
|
+
await accountService.calls.mostRecent().returnValue;
|
|
51
|
+
await Promise.resolve();
|
|
52
|
+
});
|
|
53
|
+
wrapper.update();
|
|
54
|
+
return wrapper;
|
|
55
|
+
};
|
|
56
|
+
|
|
31
57
|
describe('initialization', () => {
|
|
32
|
-
it('renders namespace from input props', () => {
|
|
33
|
-
const wrapper = component({
|
|
58
|
+
it('renders namespace from input props', async () => {
|
|
59
|
+
const wrapper = await component({
|
|
34
60
|
manifestName: 'configMap my-config-map',
|
|
35
61
|
account: 'my-account',
|
|
36
62
|
location: 'default',
|
|
@@ -40,8 +66,8 @@ describe('<ManifestSelector />', () => {
|
|
|
40
66
|
expect((namespace.props().value as Option).value).toEqual('default');
|
|
41
67
|
});
|
|
42
68
|
|
|
43
|
-
it('renders kind from input props', () => {
|
|
44
|
-
const wrapper = component({
|
|
69
|
+
it('renders kind from input props', async () => {
|
|
70
|
+
const wrapper = await component({
|
|
45
71
|
manifestName: 'configMap my-config-map',
|
|
46
72
|
account: 'my-account',
|
|
47
73
|
location: 'default',
|
|
@@ -51,8 +77,8 @@ describe('<ManifestSelector />', () => {
|
|
|
51
77
|
expect((kind.props().value as Option).value).toEqual('configMap');
|
|
52
78
|
});
|
|
53
79
|
|
|
54
|
-
it('renders name from input props', () => {
|
|
55
|
-
const wrapper = component({
|
|
80
|
+
it('renders name from input props', async () => {
|
|
81
|
+
const wrapper = await component({
|
|
56
82
|
manifestName: 'configMap my-config-map',
|
|
57
83
|
account: 'my-account',
|
|
58
84
|
location: 'default',
|
|
@@ -62,8 +88,8 @@ describe('<ManifestSelector />', () => {
|
|
|
62
88
|
expect((name.props().value as Option).value).toEqual('my-config-map');
|
|
63
89
|
});
|
|
64
90
|
|
|
65
|
-
it('renders kinds from input props', () => {
|
|
66
|
-
const wrapper = component({
|
|
91
|
+
it('renders kinds from input props', async () => {
|
|
92
|
+
const wrapper = await component({
|
|
67
93
|
account: 'my-account',
|
|
68
94
|
kinds: ['configMap', 'deployment'],
|
|
69
95
|
location: 'default',
|
|
@@ -74,7 +100,7 @@ describe('<ManifestSelector />', () => {
|
|
|
74
100
|
expect(kinds.props().value).toEqual(['configMap', 'deployment']);
|
|
75
101
|
});
|
|
76
102
|
|
|
77
|
-
it('renders labels from input props', () => {
|
|
103
|
+
it('renders labels from input props', async () => {
|
|
78
104
|
const labelSelectors: IManifestLabelSelector[] = [
|
|
79
105
|
{
|
|
80
106
|
key: 'label-key',
|
|
@@ -82,7 +108,7 @@ describe('<ManifestSelector />', () => {
|
|
|
82
108
|
values: ['label-value'],
|
|
83
109
|
},
|
|
84
110
|
];
|
|
85
|
-
const wrapper = component({
|
|
111
|
+
const wrapper = await component({
|
|
86
112
|
account: 'my-account',
|
|
87
113
|
kinds: ['configMap', 'deployment'],
|
|
88
114
|
labelSelectors: {
|
|
@@ -102,8 +128,8 @@ describe('<ManifestSelector />', () => {
|
|
|
102
128
|
application: { getDataSource: () => ({ data }) },
|
|
103
129
|
});
|
|
104
130
|
|
|
105
|
-
it("includes cluster if selected kind matches the cluster's server groups' kind", () => {
|
|
106
|
-
const wrapper = component(
|
|
131
|
+
it("includes cluster if selected kind matches the cluster's server groups' kind", async () => {
|
|
132
|
+
const wrapper = await component(
|
|
107
133
|
{
|
|
108
134
|
kind: 'replicaSet',
|
|
109
135
|
account: 'my-account',
|
|
@@ -124,8 +150,8 @@ describe('<ManifestSelector />', () => {
|
|
|
124
150
|
expect(cluster.props().clusters).toEqual(['replicaSet my-replica-set']);
|
|
125
151
|
});
|
|
126
152
|
|
|
127
|
-
it("does not include cluster if selected kind does not match cluster's server groups' kind", () => {
|
|
128
|
-
const wrapper = component(
|
|
153
|
+
it("does not include cluster if selected kind does not match cluster's server groups' kind", async () => {
|
|
154
|
+
const wrapper = await component(
|
|
129
155
|
{
|
|
130
156
|
kind: 'statefulSet',
|
|
131
157
|
account: 'my-account',
|
|
@@ -146,8 +172,8 @@ describe('<ManifestSelector />', () => {
|
|
|
146
172
|
expect(cluster.props().clusters).toEqual([]);
|
|
147
173
|
});
|
|
148
174
|
|
|
149
|
-
it('handles case in which a cluster has two different kinds of server groups', () => {
|
|
150
|
-
const wrapper = component(
|
|
175
|
+
it('handles case in which a cluster has two different kinds of server groups', async () => {
|
|
176
|
+
const wrapper = await component(
|
|
151
177
|
{
|
|
152
178
|
kind: 'statefulSet',
|
|
153
179
|
account: 'my-account',
|
|
@@ -174,8 +200,8 @@ describe('<ManifestSelector />', () => {
|
|
|
174
200
|
expect(cluster.props().clusters).toEqual(['my-cluster']);
|
|
175
201
|
});
|
|
176
202
|
|
|
177
|
-
it("does not include cluster if the cluster's server groups are managed", () => {
|
|
178
|
-
const wrapper = component(
|
|
203
|
+
it("does not include cluster if the cluster's server groups are managed", async () => {
|
|
204
|
+
const wrapper = await component(
|
|
179
205
|
{
|
|
180
206
|
kind: 'replicaSet',
|
|
181
207
|
account: 'my-account',
|
|
@@ -197,8 +223,8 @@ describe('<ManifestSelector />', () => {
|
|
|
197
223
|
expect(cluster.props().clusters).toEqual([]);
|
|
198
224
|
});
|
|
199
225
|
|
|
200
|
-
it('filters clusters by account', () => {
|
|
201
|
-
const wrapper = component(
|
|
226
|
+
it('filters clusters by account', async () => {
|
|
227
|
+
const wrapper = await component(
|
|
202
228
|
{
|
|
203
229
|
kind: 'replicaSet',
|
|
204
230
|
account: 'my-other-account',
|
|
@@ -219,8 +245,8 @@ describe('<ManifestSelector />', () => {
|
|
|
219
245
|
expect(cluster.props().clusters).toEqual([]);
|
|
220
246
|
});
|
|
221
247
|
|
|
222
|
-
it('filters clusters by namespace', () => {
|
|
223
|
-
const wrapper = component(
|
|
248
|
+
it('filters clusters by namespace', async () => {
|
|
249
|
+
const wrapper = await component(
|
|
224
250
|
{
|
|
225
251
|
kind: 'replicaSet',
|
|
226
252
|
account: 'my-account',
|
|
@@ -244,8 +270,8 @@ describe('<ManifestSelector />', () => {
|
|
|
244
270
|
});
|
|
245
271
|
|
|
246
272
|
describe('change handlers', () => {
|
|
247
|
-
it('calls the search service after updating the `Kind` field', () => {
|
|
248
|
-
const wrapper = component({
|
|
273
|
+
it('calls the search service after updating the `Kind` field', async () => {
|
|
274
|
+
const wrapper = await component({
|
|
249
275
|
manifestName: 'configMap my-config-map',
|
|
250
276
|
account: 'my-account',
|
|
251
277
|
location: 'default',
|
|
@@ -256,8 +282,8 @@ describe('<ManifestSelector />', () => {
|
|
|
256
282
|
expect(searchService).toHaveBeenCalledWith('deployment', 'default', 'my-account');
|
|
257
283
|
});
|
|
258
284
|
|
|
259
|
-
it('calls the search service after updating the `Namespace` field', () => {
|
|
260
|
-
const wrapper = component({
|
|
285
|
+
it('calls the search service after updating the `Namespace` field', async () => {
|
|
286
|
+
const wrapper = await component({
|
|
261
287
|
manifestName: 'configMap my-config-map',
|
|
262
288
|
account: 'my-account',
|
|
263
289
|
location: 'default',
|
|
@@ -268,8 +294,8 @@ describe('<ManifestSelector />', () => {
|
|
|
268
294
|
expect(searchService).toHaveBeenCalledWith('configMap', 'kube-system', 'my-account');
|
|
269
295
|
});
|
|
270
296
|
|
|
271
|
-
it('calls the search service after updating the `Account` field', () => {
|
|
272
|
-
const wrapper = component({
|
|
297
|
+
it('calls the search service after updating the `Account` field', async () => {
|
|
298
|
+
const wrapper = await component({
|
|
273
299
|
manifestName: 'configMap my-config-map',
|
|
274
300
|
account: 'my-account',
|
|
275
301
|
location: 'default',
|
|
@@ -286,8 +312,28 @@ describe('<ManifestSelector />', () => {
|
|
|
286
312
|
expect(searchService).toHaveBeenCalledWith('configMap', 'default', 'my-other-account');
|
|
287
313
|
});
|
|
288
314
|
|
|
289
|
-
it('
|
|
290
|
-
const wrapper = component({
|
|
315
|
+
it('waits for complete manifest search criteria', async () => {
|
|
316
|
+
const wrapper = await component({ mode: SelectorMode.Static });
|
|
317
|
+
wrapper.setState({
|
|
318
|
+
accounts: [{ name: 'my-account', namespaces: ['default'] }],
|
|
319
|
+
} as IManifestSelectorState);
|
|
320
|
+
searchService.calls.reset();
|
|
321
|
+
|
|
322
|
+
const account = wrapper.find(AccountSelectInput).first();
|
|
323
|
+
account.props().onChange(createFakeReactSyntheticEvent({ value: 'my-account' }));
|
|
324
|
+
expect(searchService).not.toHaveBeenCalled();
|
|
325
|
+
|
|
326
|
+
const namespace = wrapper.find({ label: 'Namespace' }).find(Creatable).first();
|
|
327
|
+
namespace.props().onChange({ value: 'default', label: 'default' });
|
|
328
|
+
expect(searchService).not.toHaveBeenCalled();
|
|
329
|
+
|
|
330
|
+
const kind = wrapper.find({ label: 'Kind' }).find(Creatable).first();
|
|
331
|
+
kind.props().onChange({ value: 'deployment', label: 'deployment' });
|
|
332
|
+
expect(searchService).toHaveBeenCalledOnceWith('deployment', 'default', 'my-account');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('clears namespace when changing account if account does not have selected namespace', async () => {
|
|
336
|
+
const wrapper = await component({
|
|
291
337
|
manifestName: 'configMap my-config-map',
|
|
292
338
|
account: 'my-account',
|
|
293
339
|
location: 'default',
|
|
@@ -306,8 +352,8 @@ describe('<ManifestSelector />', () => {
|
|
|
306
352
|
});
|
|
307
353
|
|
|
308
354
|
describe('mode change', () => {
|
|
309
|
-
it('handles kind during static -> dynamic mode transition', () => {
|
|
310
|
-
const wrapper = component(
|
|
355
|
+
it('handles kind during static -> dynamic mode transition', async () => {
|
|
356
|
+
const wrapper = await component(
|
|
311
357
|
{
|
|
312
358
|
manifestName: 'configMap my-config-map',
|
|
313
359
|
account: 'my-account',
|
|
@@ -320,8 +366,8 @@ describe('<ManifestSelector />', () => {
|
|
|
320
366
|
expect(wrapper.state().selector.kind).toEqual('configMap');
|
|
321
367
|
});
|
|
322
368
|
|
|
323
|
-
it('handles kind during static -> label mode transition', () => {
|
|
324
|
-
const wrapper = component(
|
|
369
|
+
it('handles kind during static -> label mode transition', async () => {
|
|
370
|
+
const wrapper = await component(
|
|
325
371
|
{
|
|
326
372
|
manifestName: 'configMap my-config-map',
|
|
327
373
|
account: 'my-account',
|
|
@@ -336,8 +382,8 @@ describe('<ManifestSelector />', () => {
|
|
|
336
382
|
expect(wrapper.state().selector.manifestName).toBeNull();
|
|
337
383
|
});
|
|
338
384
|
|
|
339
|
-
it('handles kind during dynamic -> static mode transition', () => {
|
|
340
|
-
const wrapper = component(
|
|
385
|
+
it('handles kind during dynamic -> static mode transition', async () => {
|
|
386
|
+
const wrapper = await component(
|
|
341
387
|
{
|
|
342
388
|
account: 'my-account',
|
|
343
389
|
location: 'default',
|
|
@@ -352,8 +398,8 @@ describe('<ManifestSelector />', () => {
|
|
|
352
398
|
expect(wrapper.state().selector.manifestName).toEqual('configMap');
|
|
353
399
|
});
|
|
354
400
|
|
|
355
|
-
it('handles kind during dynamic -> label mode transition', () => {
|
|
356
|
-
const wrapper = component(
|
|
401
|
+
it('handles kind during dynamic -> label mode transition', async () => {
|
|
402
|
+
const wrapper = await component(
|
|
357
403
|
{
|
|
358
404
|
account: 'my-account',
|
|
359
405
|
location: 'default',
|
|
@@ -369,8 +415,8 @@ describe('<ManifestSelector />', () => {
|
|
|
369
415
|
expect(wrapper.state().selector.manifestName).toBeNull();
|
|
370
416
|
});
|
|
371
417
|
|
|
372
|
-
it('handles kind during label -> static mode transition', () => {
|
|
373
|
-
const wrapper = component(
|
|
418
|
+
it('handles kind during label -> static mode transition', async () => {
|
|
419
|
+
const wrapper = await component(
|
|
374
420
|
{
|
|
375
421
|
account: 'my-account',
|
|
376
422
|
location: 'default',
|
|
@@ -388,8 +434,8 @@ describe('<ManifestSelector />', () => {
|
|
|
388
434
|
expect(wrapper.state().selector.kinds).toBeNull();
|
|
389
435
|
});
|
|
390
436
|
|
|
391
|
-
it('handles kind during label -> dynamic mode transition', () => {
|
|
392
|
-
const wrapper = component(
|
|
437
|
+
it('handles kind during label -> dynamic mode transition', async () => {
|
|
438
|
+
const wrapper = await component(
|
|
393
439
|
{
|
|
394
440
|
account: 'my-account',
|
|
395
441
|
location: 'default',
|
|
@@ -408,6 +454,3 @@ describe('<ManifestSelector />', () => {
|
|
|
408
454
|
});
|
|
409
455
|
});
|
|
410
456
|
});
|
|
411
|
-
|
|
412
|
-
const component = (selector: any, props: any = {}) =>
|
|
413
|
-
mount<ManifestSelector>((<ManifestSelector onChange={noop} selector={selector} {...props} />) as any);
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
AccountSelectInput,
|
|
11
11
|
AccountService,
|
|
12
12
|
AppListExtractor,
|
|
13
|
-
NgReact,
|
|
14
13
|
noop,
|
|
15
14
|
ScopeClusterSelector,
|
|
16
15
|
SETTINGS,
|
|
17
16
|
StageConfigField,
|
|
18
17
|
StageConstants,
|
|
18
|
+
TargetSelect,
|
|
19
19
|
} from '@spinnaker/core';
|
|
20
20
|
|
|
21
21
|
import type { IManifestLabelSelector } from './IManifestLabelSelector';
|
|
@@ -164,7 +164,7 @@ export class ManifestSelector extends React.Component<IManifestSelectorProps, IM
|
|
|
164
164
|
|
|
165
165
|
public componentWillUnmount = () => this.destroy$.next();
|
|
166
166
|
|
|
167
|
-
public loadAccounts = ():
|
|
167
|
+
public loadAccounts = (): Promise<void> => {
|
|
168
168
|
return AccountService.getAllAccountDetailsForProvider('kubernetes').then((accounts) => {
|
|
169
169
|
const selector = this.state.selector;
|
|
170
170
|
const kind = parseSpinnakerName(selector.manifestName).kind;
|
|
@@ -244,8 +244,8 @@ export class ManifestSelector extends React.Component<IManifestSelectorProps, IM
|
|
|
244
244
|
|
|
245
245
|
private isExpression = (value: string): boolean => (typeof value === 'string' ? value.includes('${') : false);
|
|
246
246
|
|
|
247
|
-
private search = (kind: string, namespace: string, account: string):
|
|
248
|
-
if (this.isExpression(account)) {
|
|
247
|
+
private search = (kind: string, namespace: string, account: string): Promise<string[]> => {
|
|
248
|
+
if (!kind || !namespace || !account || this.isExpression(account)) {
|
|
249
249
|
return Promise.resolve([]);
|
|
250
250
|
}
|
|
251
251
|
return ManifestKindSearchService.search(kind, namespace, account).then((results) =>
|
|
@@ -310,7 +310,6 @@ export class ManifestSelector extends React.Component<IManifestSelectorProps, IM
|
|
|
310
310
|
};
|
|
311
311
|
|
|
312
312
|
public render() {
|
|
313
|
-
const { TargetSelect } = NgReact;
|
|
314
313
|
const selectedMode = this.getSelectedMode();
|
|
315
314
|
const modes = this.props.modes || [selectedMode];
|
|
316
315
|
const { selector, accounts, kinds, namespaces, resources, loading } = this.state;
|
|
@@ -7,7 +7,7 @@ export class ManifestTrafficService {
|
|
|
7
7
|
public static readonly ENABLE_MANIFEST_OPERATION = 'enableManifest';
|
|
8
8
|
public static readonly DISABLE_MANIFEST_OPERATION = 'disableManifest';
|
|
9
9
|
|
|
10
|
-
public static enable = (manifest: IManifest, application: Application, reason?: string):
|
|
10
|
+
public static enable = (manifest: IManifest, application: Application, reason?: string): Promise<ITask> => {
|
|
11
11
|
return TaskExecutor.executeTask({
|
|
12
12
|
job: [
|
|
13
13
|
{
|
|
@@ -24,7 +24,7 @@ export class ManifestTrafficService {
|
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
public static disable = (manifest: IManifest, application: Application, reason?: string):
|
|
27
|
+
public static disable = (manifest: IManifest, application: Application, reason?: string): Promise<ITask> => {
|
|
28
28
|
return TaskExecutor.executeTask({
|
|
29
29
|
job: [
|
|
30
30
|
{
|
|
@@ -47,7 +47,7 @@ export class ManifestWizard extends React.Component<IKubernetesManifestModalProp
|
|
|
47
47
|
taskMonitor: new TaskMonitor({
|
|
48
48
|
application: props.application,
|
|
49
49
|
title: `${this.props.isNew ? 'Deploying' : 'Updating'} your manifest`,
|
|
50
|
-
|
|
50
|
+
onDismiss: () => this.props.dismissModal(),
|
|
51
51
|
}),
|
|
52
52
|
};
|
|
53
53
|
}
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { noop, StageConfigField } from '@spinnaker/core';
|
|
4
4
|
|
|
5
|
-
import type { IDeleteOptions } from '../../../manifest/delete/delete.
|
|
5
|
+
import type { IDeleteOptions } from '../../../manifest/delete/delete.types';
|
|
6
6
|
|
|
7
7
|
import './deleteManifestOptionsForm.less';
|
|
8
8
|
|
|
@@ -4,7 +4,7 @@ import React from 'react';
|
|
|
4
4
|
import type { Application, IStage, IStageConfigProps } from '@spinnaker/core';
|
|
5
5
|
|
|
6
6
|
import DeleteManifestOptionsForm from './DeleteManifestOptionsForm';
|
|
7
|
-
import type { IDeleteOptions } from '../../../manifest/delete/delete.
|
|
7
|
+
import type { IDeleteOptions } from '../../../manifest/delete/delete.types';
|
|
8
8
|
import type { IManifestSelector } from '../../../manifest/selector/IManifestSelector';
|
|
9
9
|
import { SelectorMode } from '../../../manifest/selector/IManifestSelector';
|
|
10
10
|
import { ManifestSelector } from '../../../manifest/selector/ManifestSelector';
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
import {
|
|
13
13
|
ArtifactTypePatterns,
|
|
14
14
|
CheckboxInput,
|
|
15
|
+
NumberInput,
|
|
15
16
|
RadioButtonInput,
|
|
16
17
|
SETTINGS,
|
|
17
18
|
StageArtifactSelectorDelegate,
|
|
@@ -254,6 +255,21 @@ export class DeployManifestStageForm extends React.Component<
|
|
|
254
255
|
onConfigChange={(config) => this.props.formik.setFieldValue('trafficManagement', config)}
|
|
255
256
|
selectedAccount={stage.account}
|
|
256
257
|
/>
|
|
258
|
+
<hr />
|
|
259
|
+
<h4>Stability Timeout</h4>
|
|
260
|
+
<StageConfigField label="Timeout (minutes)" helpKey="kubernetes.manifest.stableManifestTimeout">
|
|
261
|
+
<NumberInput
|
|
262
|
+
value={stage.stableManifestTimeoutMinutes ?? ''}
|
|
263
|
+
min={1}
|
|
264
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
265
|
+
const val = e.target.value;
|
|
266
|
+
this.props.formik.setFieldValue('stableManifestTimeoutMinutes', val === '' ? undefined : Number(val));
|
|
267
|
+
}}
|
|
268
|
+
/>
|
|
269
|
+
<div className="form-inline">
|
|
270
|
+
<span className="input-label sp-padding-xs-left">Leave blank to use the default (30 minutes).</span>
|
|
271
|
+
</div>
|
|
272
|
+
</StageConfigField>
|
|
257
273
|
</div>
|
|
258
274
|
);
|
|
259
275
|
}
|
|
@@ -1,86 +1,13 @@
|
|
|
1
|
-
import type { IQService, IScope } from 'angular';
|
|
2
|
-
import { mock } from 'angular';
|
|
3
|
-
|
|
4
1
|
import type { Application } from '@spinnaker/core';
|
|
5
|
-
import {
|
|
2
|
+
import { noop } from '@spinnaker/core';
|
|
6
3
|
import { ManifestCopier } from './ManifestCopier';
|
|
7
4
|
|
|
8
5
|
describe('<ManifestCopier />', () => {
|
|
9
6
|
let application: Application;
|
|
10
7
|
|
|
11
|
-
beforeEach(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// The application model implicitly depends on a bunch of Angular things, which is why
|
|
15
|
-
// we need the Angular mock environment (even though we're testing a React component).
|
|
16
|
-
application = ApplicationModelBuilder.createApplicationForTests(
|
|
17
|
-
'app',
|
|
18
|
-
{
|
|
19
|
-
key: 'serverGroups',
|
|
20
|
-
loader: () =>
|
|
21
|
-
$q.resolve([
|
|
22
|
-
// Replica sets in same cluster, no manager.
|
|
23
|
-
{
|
|
24
|
-
name: 'replicaSet my-replicaSet-v002',
|
|
25
|
-
region: 'default',
|
|
26
|
-
category: 'serverGroup',
|
|
27
|
-
account: 'my-k8s-account',
|
|
28
|
-
cloudProvider: 'kubernetes',
|
|
29
|
-
cluster: 'replicaSet my-replicaSet',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
name: 'replicaSet my-replicaSet-v001',
|
|
33
|
-
region: 'default',
|
|
34
|
-
category: 'serverGroup',
|
|
35
|
-
account: 'my-k8s-account',
|
|
36
|
-
cloudProvider: 'kubernetes',
|
|
37
|
-
cluster: 'replicaSet my-replicaSet',
|
|
38
|
-
},
|
|
39
|
-
// Replica set managed by deployment.
|
|
40
|
-
{
|
|
41
|
-
name: 'replicaSet my-managed-replicaSet-v001',
|
|
42
|
-
region: 'default',
|
|
43
|
-
category: 'serverGroup',
|
|
44
|
-
account: 'my-k8s-account',
|
|
45
|
-
cloudProvider: 'kubernetes',
|
|
46
|
-
cluster: 'deployment my-deployment',
|
|
47
|
-
serverGroupManagers: [{ name: 'deployment my-deployment' }],
|
|
48
|
-
},
|
|
49
|
-
]),
|
|
50
|
-
onLoad: (_app: Application, data: any) => $q.resolve(data),
|
|
51
|
-
defaultData: [],
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
key: 'serverGroupManagers',
|
|
55
|
-
loader: () =>
|
|
56
|
-
$q.resolve([
|
|
57
|
-
{
|
|
58
|
-
name: 'deployment my-deployment',
|
|
59
|
-
region: 'default',
|
|
60
|
-
account: 'my-k8s-account',
|
|
61
|
-
cloudProvider: 'kubernetes',
|
|
62
|
-
},
|
|
63
|
-
]),
|
|
64
|
-
onLoad: (_app: Application, data: any) => $q.resolve(data),
|
|
65
|
-
defaultData: [],
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
key: 'securityGroups',
|
|
69
|
-
loader: () => $q.resolve([]),
|
|
70
|
-
onLoad: (_app: Application, data: any) => $q.resolve(data),
|
|
71
|
-
defaultData: [],
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
key: 'loadBalancers',
|
|
75
|
-
loader: () => $q.resolve([]),
|
|
76
|
-
onLoad: (_app: Application, data: any) => $q.resolve(data),
|
|
77
|
-
defaultData: [],
|
|
78
|
-
},
|
|
79
|
-
);
|
|
80
|
-
application.refresh();
|
|
81
|
-
$scope.$digest();
|
|
82
|
-
}),
|
|
83
|
-
);
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
application = buildApplication();
|
|
10
|
+
});
|
|
84
11
|
|
|
85
12
|
describe('dropdown grouping & ordering', () => {
|
|
86
13
|
it('sorts deployments to the top of the list', () => {
|
|
@@ -108,3 +35,51 @@ const buildProps = (application: Application) => ({
|
|
|
108
35
|
onManifestSelected: noop,
|
|
109
36
|
show: true,
|
|
110
37
|
});
|
|
38
|
+
|
|
39
|
+
const buildApplication = (): Application => {
|
|
40
|
+
const dataSources = {
|
|
41
|
+
serverGroups: [
|
|
42
|
+
// Replica sets in same cluster, no manager.
|
|
43
|
+
{
|
|
44
|
+
name: 'replicaSet my-replicaSet-v002',
|
|
45
|
+
region: 'default',
|
|
46
|
+
category: 'serverGroup',
|
|
47
|
+
account: 'my-k8s-account',
|
|
48
|
+
cloudProvider: 'kubernetes',
|
|
49
|
+
cluster: 'replicaSet my-replicaSet',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'replicaSet my-replicaSet-v001',
|
|
53
|
+
region: 'default',
|
|
54
|
+
category: 'serverGroup',
|
|
55
|
+
account: 'my-k8s-account',
|
|
56
|
+
cloudProvider: 'kubernetes',
|
|
57
|
+
cluster: 'replicaSet my-replicaSet',
|
|
58
|
+
},
|
|
59
|
+
// Replica set managed by deployment.
|
|
60
|
+
{
|
|
61
|
+
name: 'replicaSet my-managed-replicaSet-v001',
|
|
62
|
+
region: 'default',
|
|
63
|
+
category: 'serverGroup',
|
|
64
|
+
account: 'my-k8s-account',
|
|
65
|
+
cloudProvider: 'kubernetes',
|
|
66
|
+
cluster: 'deployment my-deployment',
|
|
67
|
+
serverGroupManagers: [{ name: 'deployment my-deployment' }],
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
serverGroupManagers: [
|
|
71
|
+
{
|
|
72
|
+
name: 'deployment my-deployment',
|
|
73
|
+
region: 'default',
|
|
74
|
+
account: 'my-k8s-account',
|
|
75
|
+
cloudProvider: 'kubernetes',
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
securityGroups: [],
|
|
79
|
+
loadBalancers: [],
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return ({
|
|
83
|
+
getDataSource: (key: keyof typeof dataSources) => ({ data: dataSources[key] }),
|
|
84
|
+
} as unknown) as Application;
|
|
85
|
+
};
|
|
@@ -5,6 +5,7 @@ import { StageConfigField } from '@spinnaker/core';
|
|
|
5
5
|
|
|
6
6
|
import type { IManifestDeploymentOptionsProps } from './ManifestDeploymentOptions';
|
|
7
7
|
import { defaultTrafficManagementConfig, ManifestDeploymentOptions } from './ManifestDeploymentOptions';
|
|
8
|
+
import { ManifestKindSearchService } from '../../../manifest/ManifestKindSearch';
|
|
8
9
|
|
|
9
10
|
describe('<ManifestDeploymentOptions />', () => {
|
|
10
11
|
const onConfigChangeSpy = jasmine.createSpy('onConfigChangeSpy');
|
|
@@ -12,6 +13,7 @@ describe('<ManifestDeploymentOptions />', () => {
|
|
|
12
13
|
let props: IManifestDeploymentOptionsProps;
|
|
13
14
|
|
|
14
15
|
beforeEach(() => {
|
|
16
|
+
spyOn(ManifestKindSearchService, 'search').and.returnValue(Promise.resolve([]));
|
|
15
17
|
props = {
|
|
16
18
|
accounts: [],
|
|
17
19
|
config: defaultTrafficManagementConfig,
|
|
@@ -2,11 +2,17 @@ import { get } from 'lodash';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
|
|
4
4
|
import type { IExecutionDetailsSectionProps, IManifest } from '@spinnaker/core';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
CollapsibleElement,
|
|
7
|
+
ExecutionDetailsSection,
|
|
8
|
+
ManifestYaml,
|
|
9
|
+
SETTINGS,
|
|
10
|
+
StageFailureMessage,
|
|
11
|
+
} from '@spinnaker/core';
|
|
6
12
|
|
|
7
13
|
import { ManifestStatus } from './ManifestStatus';
|
|
8
|
-
import type { IStageManifest } from '../../../../manifest/manifest.service';
|
|
9
|
-
import { KubernetesManifestService } from '../../../../manifest/manifest.service';
|
|
14
|
+
import type { IStageManifest, IStoredManifestReference } from '../../../../manifest/manifest.service';
|
|
15
|
+
import { isStoredManifestReference, KubernetesManifestService } from '../../../../manifest/manifest.service';
|
|
10
16
|
|
|
11
17
|
import './DeployStatus.less';
|
|
12
18
|
|
|
@@ -38,7 +44,9 @@ export class DeployStatus extends React.Component<IExecutionDetailsSectionProps,
|
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
public componentDidUpdate(_prevProps: IExecutionDetailsSectionProps, prevState: IDeployStatusState) {
|
|
41
|
-
const manifests: IStageManifest[] = get(this.props.stage, ['context', 'outputs.manifests'], [])
|
|
47
|
+
const manifests: IStageManifest[] = get(this.props.stage, ['context', 'outputs.manifests'], [])
|
|
48
|
+
.filter((m) => !!m)
|
|
49
|
+
.filter((m) => !isStoredManifestReference(m));
|
|
42
50
|
const manifestIds = manifests.map((m) => KubernetesManifestService.manifestIdentifier(m)).sort();
|
|
43
51
|
if (prevState.manifestIds.join('') !== manifestIds.join('')) {
|
|
44
52
|
this.unsubscribeAll();
|
|
@@ -71,6 +79,16 @@ export class DeployStatus extends React.Component<IExecutionDetailsSectionProps,
|
|
|
71
79
|
this.state.subscriptions.forEach(({ unsubscribe }) => unsubscribe());
|
|
72
80
|
}
|
|
73
81
|
|
|
82
|
+
// Manifests the artifact-store entity storage feature replaced with a reference instead
|
|
83
|
+
// of expanding them before deck saw them. We can't determine their kind/name/namespace
|
|
84
|
+
// (and so can't subscribe to live status for them) without fetching the content, so we
|
|
85
|
+
// let the user load them on demand instead, same as a baked manifest's YAML.
|
|
86
|
+
private get storedManifestRefs(): IStoredManifestReference[] {
|
|
87
|
+
return get(this.props.stage, ['context', 'outputs.manifests'], [])
|
|
88
|
+
.filter((m: unknown) => !!m)
|
|
89
|
+
.filter(isStoredManifestReference);
|
|
90
|
+
}
|
|
91
|
+
|
|
74
92
|
private stageManifestToIManifest(manifest: IStageManifest, account: string): IManifest {
|
|
75
93
|
return {
|
|
76
94
|
name: get(manifest, 'metadata.name', ''),
|
|
@@ -113,6 +131,23 @@ export class DeployStatus extends React.Component<IExecutionDetailsSectionProps,
|
|
|
113
131
|
</div>
|
|
114
132
|
</div>
|
|
115
133
|
)}
|
|
134
|
+
{!!this.storedManifestRefs.length && (
|
|
135
|
+
<div className="row">
|
|
136
|
+
<div className="col-md-12">
|
|
137
|
+
<div className="well alert alert-info">
|
|
138
|
+
{this.storedManifestRefs.map((ref, i) => (
|
|
139
|
+
<div key={i}>
|
|
140
|
+
<ManifestYaml
|
|
141
|
+
linkName="Load Manifest Details"
|
|
142
|
+
modalTitle="Manifest"
|
|
143
|
+
manifestUri={ref.reference.replace(/^ref?:\/\//, '')}
|
|
144
|
+
/>
|
|
145
|
+
</div>
|
|
146
|
+
))}
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
)}
|
|
116
151
|
</ExecutionDetailsSection>
|
|
117
152
|
</div>
|
|
118
153
|
);
|