@spinnaker/kubernetes 2026.2.2 → 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.
Files changed (158) hide show
  1. package/dist/index.d.ts +1 -2
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/instance/details/KubernetesInstanceDetails.d.ts +54 -0
  5. package/dist/instance/details/kubernetesInstanceDetails.utils.d.ts +13 -0
  6. package/dist/instance/index.d.ts +1 -1
  7. package/dist/kubernetes.module.d.ts +5 -1
  8. package/dist/loadBalancer/transformer.d.ts +1 -1
  9. package/dist/manifest/AnnotationCustomSections.d.ts +3 -2
  10. package/dist/manifest/ManifestKindSearch.d.ts +1 -1
  11. package/dist/manifest/delete/Delete.d.ts +0 -1
  12. package/dist/manifest/delete/DeleteModal.d.ts +1 -1
  13. package/dist/manifest/delete/delete.types.d.ts +4 -0
  14. package/dist/manifest/index.d.ts +3 -6
  15. package/dist/manifest/manifest.service.d.ts +5 -0
  16. package/dist/manifest/manifestCommandBuilder.service.d.ts +1 -1
  17. package/dist/manifest/rollout/PauseRollout.d.ts +0 -1
  18. package/dist/manifest/rollout/ResumeRollout.d.ts +0 -1
  19. package/dist/manifest/rollout/RollingRestart.d.ts +4 -4
  20. package/dist/manifest/rollout/UndoRollout.d.ts +0 -1
  21. package/dist/manifest/scale/Scale.d.ts +1 -2
  22. package/dist/manifest/scale/ScaleModal.d.ts +3 -3
  23. package/dist/manifest/scale/ScaleSettingsForm.d.ts +7 -1
  24. package/dist/manifest/selector/ManifestSelector.d.ts +1 -1
  25. package/dist/manifest/traffic/ManifestTrafficService.d.ts +2 -2
  26. package/dist/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.d.ts +1 -1
  27. package/dist/pipelines/stages/deployManifest/manifestStatus/DeployStatus.d.ts +1 -0
  28. package/dist/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.d.ts +6 -3
  29. package/dist/pipelines/stages/traffic/disableManifest.stage.d.ts +2 -1
  30. package/dist/pipelines/stages/traffic/enableManifest.stage.d.ts +2 -1
  31. package/dist/rawResource/index.d.ts +3 -1
  32. package/dist/rawResource/rawResource.dataSource.d.ts +1 -1
  33. package/dist/rawResource/rawResource.states.d.ts +4 -1
  34. package/dist/resources/resources.state.d.ts +2 -1
  35. package/dist/securityGroup/details/KubernetesSecurityGroupDetails.d.ts +49 -0
  36. package/dist/securityGroup/index.d.ts +3 -1
  37. package/dist/securityGroup/transformer.d.ts +4 -1
  38. package/dist/serverGroup/serverGroupCommandBuilder.d.ts +1 -1
  39. package/dist/serverGroup/serverGroupTransformer.d.ts +1 -1
  40. package/dist/serverGroupManager/details/ServerGroupManagerDetails.d.ts +7 -3
  41. package/package.json +3 -12
  42. package/src/help/kubernetes.help.ts +2 -0
  43. package/src/index.ts +2 -2
  44. package/src/instance/details/KubernetesInstanceDetails.spec.tsx +451 -0
  45. package/src/instance/details/KubernetesInstanceDetails.tsx +342 -0
  46. package/src/instance/details/kubernetesInstanceDetails.utils.ts +77 -0
  47. package/src/instance/index.ts +1 -1
  48. package/src/kubernetes.module.spec.ts +25 -0
  49. package/src/kubernetes.module.ts +23 -87
  50. package/src/loadBalancer/details/KubernetesLoadBalancerActions.tsx +30 -35
  51. package/src/loadBalancer/details/sections/LoadBalancerAnnotationCustomSection.tsx +2 -3
  52. package/src/loadBalancer/transformer.ts +1 -1
  53. package/src/manifest/AnnotationCustomSections.spec.tsx +178 -0
  54. package/src/manifest/AnnotationCustomSections.tsx +179 -2
  55. package/src/manifest/ManifestKindSearch.tsx +1 -1
  56. package/src/manifest/delete/Delete.tsx +0 -9
  57. package/src/manifest/delete/DeleteModal.tsx +69 -72
  58. package/src/manifest/delete/delete.types.ts +4 -0
  59. package/src/manifest/index.ts +3 -6
  60. package/src/manifest/manifest.service.ts +18 -1
  61. package/src/manifest/manifestCommandBuilder.service.spec.ts +41 -0
  62. package/src/manifest/manifestCommandBuilder.service.ts +45 -59
  63. package/src/manifest/rollout/PauseRollout.tsx +1 -9
  64. package/src/manifest/rollout/ResumeRollout.tsx +2 -10
  65. package/src/manifest/rollout/RollingRestart.tsx +16 -22
  66. package/src/manifest/rollout/UndoRollout.tsx +1 -10
  67. package/src/manifest/rollout/UndoRolloutModal.tsx +5 -1
  68. package/src/manifest/scale/Scale.tsx +12 -11
  69. package/src/manifest/scale/ScaleModal.tsx +43 -31
  70. package/src/manifest/scale/ScaleSettingsForm.tsx +8 -1
  71. package/src/manifest/selector/ManifestSelector.spec.tsx +89 -46
  72. package/src/manifest/selector/ManifestSelector.tsx +4 -5
  73. package/src/manifest/traffic/ManifestTrafficService.ts +2 -2
  74. package/src/manifest/wizard/ManifestWizard.tsx +1 -1
  75. package/src/pipelines/stages/deleteManifest/DeleteManifestOptionsForm.tsx +1 -1
  76. package/src/pipelines/stages/deleteManifest/DeleteManifestStageConfig.tsx +1 -1
  77. package/src/pipelines/stages/deployManifest/DeployManifestStageForm.tsx +16 -0
  78. package/src/pipelines/stages/deployManifest/ManifestCopier.spec.ts +52 -77
  79. package/src/pipelines/stages/deployManifest/ManifestDeploymentOptions.spec.tsx +2 -0
  80. package/src/pipelines/stages/deployManifest/manifestStatus/DeployStatus.tsx +39 -4
  81. package/src/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.spec.tsx +35 -0
  82. package/src/pipelines/stages/deployManifest/manifestStatus/ManifestDetailsLink.tsx +10 -5
  83. package/src/pipelines/stages/traffic/ManifestTrafficStageRegistration.spec.ts +34 -0
  84. package/src/pipelines/stages/traffic/disableManifest.stage.ts +17 -19
  85. package/src/pipelines/stages/traffic/enableManifest.stage.ts +17 -19
  86. package/src/rawResource/index.ts +18 -1
  87. package/src/rawResource/rawResource.dataSource.ts +5 -8
  88. package/src/rawResource/rawResource.states.ts +50 -51
  89. package/src/resources/resources.state.spec.ts +138 -0
  90. package/src/resources/resources.state.ts +30 -35
  91. package/src/securityGroup/details/KubernetesSecurityGroupDetails.spec.tsx +292 -0
  92. package/src/securityGroup/details/KubernetesSecurityGroupDetails.tsx +304 -0
  93. package/src/securityGroup/index.ts +3 -1
  94. package/src/securityGroup/transformer.ts +2 -10
  95. package/src/serverGroup/details/KubernetesServerGroupActions.tsx +31 -61
  96. package/src/serverGroup/details/kubernetesServerGroupDetailsGetter.ts +1 -1
  97. package/src/serverGroup/details/sections/ServerGroupAnnotationCustomSection.tsx +2 -3
  98. package/src/serverGroup/serverGroupCommandBuilder.ts +1 -1
  99. package/src/serverGroup/serverGroupTransformer.ts +1 -1
  100. package/src/serverGroupManager/details/ServerGroupManagerActions.tsx +48 -128
  101. package/src/serverGroupManager/details/ServerGroupManagerDetails.spec.ts +12 -0
  102. package/src/serverGroupManager/details/ServerGroupManagerDetails.tsx +19 -8
  103. package/src/serverGroupManager/details/sections/ServerGroupManagerAnnotationCustomSection.tsx +2 -3
  104. package/src/serverGroupManager/details/useKubernetesServerGroupManagerDetails.spec.tsx +112 -0
  105. package/src/serverGroupManager/details/useKubernetesServerGroupManagerDetails.tsx +50 -29
  106. package/dist/instance/details/details.controller.d.ts +0 -1
  107. package/dist/manifest/annotationCustomSections.component.d.ts +0 -3
  108. package/dist/manifest/delete/delete.controller.d.ts +0 -12
  109. package/dist/manifest/delete/deleteOptionsForm.component.d.ts +0 -1
  110. package/dist/manifest/editor/json/jsonEditor.component.d.ts +0 -1
  111. package/dist/manifest/manifestEvents.component.d.ts +0 -1
  112. package/dist/manifest/manifestImageDetails.component.d.ts +0 -1
  113. package/dist/manifest/manifestLabels.component.d.ts +0 -1
  114. package/dist/manifest/manifestQos.component.d.ts +0 -1
  115. package/dist/manifest/manifestResources.component.d.ts +0 -1
  116. package/dist/manifest/rollout/pause.controller.d.ts +0 -1
  117. package/dist/manifest/rollout/resume.controller.d.ts +0 -1
  118. package/dist/manifest/rollout/undo.controller.d.ts +0 -5
  119. package/dist/manifest/scale/scale.controller.d.ts +0 -8
  120. package/dist/manifest/scale/scaleSettingsForm.component.d.ts +0 -1
  121. package/dist/manifest/selector/selector.component.d.ts +0 -1
  122. package/dist/manifest/status/condition.component.d.ts +0 -1
  123. package/dist/manifest/status/status.component.d.ts +0 -1
  124. package/dist/rawResource/rawResource.module.d.ts +0 -1
  125. package/dist/reactShims/index.d.ts +0 -1
  126. package/dist/reactShims/kubernetes.react.injector.d.ts +0 -11
  127. package/dist/reactShims/kubernetes.react.module.d.ts +0 -1
  128. package/dist/securityGroup/details/details.controller.d.ts +0 -1
  129. package/src/instance/details/details.controller.ts +0 -186
  130. package/src/instance/details/details.html +0 -80
  131. package/src/manifest/annotationCustomSections.component.ts +0 -114
  132. package/src/manifest/delete/delete.controller.ts +0 -80
  133. package/src/manifest/delete/delete.html +0 -42
  134. package/src/manifest/delete/deleteOptionsForm.component.ts +0 -52
  135. package/src/manifest/editor/json/jsonEditor.component.ts +0 -12
  136. package/src/manifest/manifestEvents.component.ts +0 -12
  137. package/src/manifest/manifestImageDetails.component.ts +0 -12
  138. package/src/manifest/manifestLabels.component.ts +0 -12
  139. package/src/manifest/manifestQos.component.ts +0 -12
  140. package/src/manifest/manifestResources.component.ts +0 -12
  141. package/src/manifest/rollout/pause.controller.ts +0 -66
  142. package/src/manifest/rollout/pause.html +0 -28
  143. package/src/manifest/rollout/resume.controller.ts +0 -66
  144. package/src/manifest/rollout/resume.html +0 -28
  145. package/src/manifest/rollout/undo.controller.ts +0 -74
  146. package/src/manifest/rollout/undo.html +0 -44
  147. package/src/manifest/scale/scale.controller.ts +0 -75
  148. package/src/manifest/scale/scale.html +0 -32
  149. package/src/manifest/scale/scaleSettingsForm.component.ts +0 -11
  150. package/src/manifest/selector/selector.component.ts +0 -17
  151. package/src/manifest/status/condition.component.ts +0 -12
  152. package/src/manifest/status/status.component.ts +0 -11
  153. package/src/rawResource/rawResource.module.ts +0 -8
  154. package/src/reactShims/index.ts +0 -1
  155. package/src/reactShims/kubernetes.react.injector.ts +0 -30
  156. package/src/reactShims/kubernetes.react.module.ts +0 -12
  157. package/src/securityGroup/details/details.controller.ts +0 -118
  158. package/src/securityGroup/details/details.html +0 -91
@@ -0,0 +1,292 @@
1
+ import { shallow } from 'enzyme';
2
+ import React from 'react';
3
+ import { MenuItem } from 'react-bootstrap';
4
+
5
+ import {
6
+ AccountTag,
7
+ AddEntityTagLinks,
8
+ CloudProviderLogo,
9
+ CollapsibleSection,
10
+ EntityNotifications,
11
+ ManifestReader,
12
+ SETTINGS,
13
+ } from '@spinnaker/core';
14
+
15
+ import {
16
+ KubernetesSecurityGroupActions,
17
+ KubernetesSecurityGroupDetailsComponent as KubernetesSecurityGroupDetails,
18
+ } from './KubernetesSecurityGroupDetails';
19
+ import type { IKubernetesSecurityGroupDetailsProps } from './KubernetesSecurityGroupDetails';
20
+ import { KubernetesV2SecurityGroupTransformer } from '../transformer';
21
+ import { AnnotationCustomSections } from '../../manifest/AnnotationCustomSections';
22
+ import { DeleteModal } from '../../manifest/delete/DeleteModal';
23
+ import { KubernetesManifestCommandBuilder } from '../../manifest/manifestCommandBuilder.service';
24
+ import { ManifestLabels } from '../../manifest/ManifestLabels';
25
+ import { ManifestWizard } from '../../manifest/wizard/ManifestWizard';
26
+
27
+ describe('<KubernetesSecurityGroupDetails />', () => {
28
+ let originalAdHocInfraWritesEnabled: boolean;
29
+ let originalEntityTags: boolean;
30
+ let props: IKubernetesSecurityGroupDetailsProps;
31
+ let securityGroupReader: any;
32
+
33
+ beforeEach(() => {
34
+ originalAdHocInfraWritesEnabled = SETTINGS.kubernetesAdHocInfraWritesEnabled;
35
+ originalEntityTags = SETTINGS.feature.entityTags;
36
+ SETTINGS.kubernetesAdHocInfraWritesEnabled = true;
37
+ SETTINGS.feature.entityTags = false;
38
+
39
+ securityGroupReader = {
40
+ getSecurityGroupDetails: jasmine
41
+ .createSpy('getSecurityGroupDetails')
42
+ .and.returnValue(Promise.resolve(securityGroup())),
43
+ };
44
+ props = {
45
+ app: appWithSecurityGroups(),
46
+ resolvedSecurityGroup: {
47
+ accountId: 'k8s-local',
48
+ name: 'networkPolicy backend-security-policy',
49
+ region: 'dev',
50
+ },
51
+ securityGroupReader,
52
+ } as IKubernetesSecurityGroupDetailsProps;
53
+
54
+ spyOn(ManifestReader, 'getManifest').and.returnValue(Promise.resolve(manifestDetails()) as any);
55
+ });
56
+
57
+ it('replaces missing details through the injected state service', () => {
58
+ const stateService = { go: jasmine.createSpy('go'), params: {} };
59
+ const component = new KubernetesSecurityGroupDetails({
60
+ ...props,
61
+ router: {},
62
+ stateParams: {},
63
+ stateService,
64
+ } as any);
65
+
66
+ (component as any).autoClose();
67
+
68
+ expect(stateService.params.allowModalToStayOpen).toBe(true);
69
+ expect(stateService.go).toHaveBeenCalledWith('^', null, { location: 'replace' });
70
+ });
71
+
72
+ afterEach(() => {
73
+ SETTINGS.kubernetesAdHocInfraWritesEnabled = originalAdHocInfraWritesEnabled;
74
+ SETTINGS.feature.entityTags = originalEntityTags;
75
+ });
76
+
77
+ it('loads security group and manifest details before rendering the React sections', async () => {
78
+ const component = shallow(<KubernetesSecurityGroupDetails {...props} />);
79
+
80
+ await settle();
81
+ component.update();
82
+
83
+ expect(securityGroupReader.getSecurityGroupDetails).toHaveBeenCalledWith(
84
+ props.app,
85
+ 'k8s-local',
86
+ 'kubernetes',
87
+ 'dev',
88
+ '',
89
+ 'networkPolicy backend-security-policy',
90
+ );
91
+ expect(ManifestReader.getManifest).toHaveBeenCalledWith(
92
+ 'k8s-local',
93
+ 'dev',
94
+ 'networkPolicy backend-security-policy',
95
+ );
96
+ expect(component.find(CloudProviderLogo).prop('provider')).toBe('kubernetes');
97
+ expect(component.find('h3').text()).toContain('backend-security-policy');
98
+ expect(component.find(KubernetesSecurityGroupActions).prop('securityGroup')).toEqual(securityGroup());
99
+ expect(component.find(CollapsibleSection).map((section) => section.prop('heading'))).toEqual([
100
+ 'Information',
101
+ 'Labels',
102
+ ]);
103
+ expect(component.find(AccountTag).prop('account')).toBe('k8s-local');
104
+ expect(component.find(AnnotationCustomSections).prop('manifest')).toEqual(manifestDetails().manifest);
105
+ expect(component.find(AnnotationCustomSections).prop('resource')).toEqual(securityGroup());
106
+ expect(component.find(ManifestLabels).prop('manifest')).toEqual(manifestDetails().manifest);
107
+ });
108
+
109
+ it('auto-closes when the security group cannot be found', async () => {
110
+ const autoClose = jasmine.createSpy('autoClose');
111
+ securityGroupReader.getSecurityGroupDetails.and.returnValue(Promise.resolve(null));
112
+ const component = shallow(<KubernetesSecurityGroupDetails {...props} autoClose={autoClose} />);
113
+
114
+ await settle();
115
+ component.update();
116
+
117
+ expect(autoClose).toHaveBeenCalled();
118
+ });
119
+
120
+ it('renders entity tag integrations when enabled', async () => {
121
+ SETTINGS.feature.entityTags = true;
122
+ const component = shallow(<KubernetesSecurityGroupDetails {...props} />);
123
+
124
+ await settle();
125
+ component.update();
126
+
127
+ expect(component.find(EntityNotifications).prop('entity')).toEqual(securityGroup());
128
+ });
129
+
130
+ it('loads standalone security group details when the securityGroups data source is absent', async () => {
131
+ const app = {
132
+ ...appWithSecurityGroups(),
133
+ isStandalone: true,
134
+ getDataSource: () => undefined,
135
+ };
136
+ const component = shallow(<KubernetesSecurityGroupDetails {...props} app={app} />);
137
+
138
+ await settle();
139
+ component.update();
140
+
141
+ expect(securityGroupReader.getSecurityGroupDetails).toHaveBeenCalledWith(
142
+ app,
143
+ 'k8s-local',
144
+ 'kubernetes',
145
+ 'dev',
146
+ '',
147
+ 'networkPolicy backend-security-policy',
148
+ );
149
+ expect(component.find('h3').text()).toContain('backend-security-policy');
150
+ });
151
+ });
152
+
153
+ describe('<KubernetesSecurityGroupActions />', () => {
154
+ let originalAdHocInfraWritesEnabled: boolean;
155
+ let originalEntityTags: boolean;
156
+
157
+ beforeEach(() => {
158
+ originalAdHocInfraWritesEnabled = SETTINGS.kubernetesAdHocInfraWritesEnabled;
159
+ originalEntityTags = SETTINGS.feature.entityTags;
160
+ SETTINGS.kubernetesAdHocInfraWritesEnabled = true;
161
+ SETTINGS.feature.entityTags = false;
162
+ });
163
+
164
+ afterEach(() => {
165
+ SETTINGS.kubernetesAdHocInfraWritesEnabled = originalAdHocInfraWritesEnabled;
166
+ SETTINGS.feature.entityTags = originalEntityTags;
167
+ });
168
+
169
+ it('opens the delete modal from the actions menu', () => {
170
+ const component = shallow(
171
+ <KubernetesSecurityGroupActions
172
+ app={appWithSecurityGroups()}
173
+ manifest={manifestDetails()}
174
+ securityGroup={securityGroup()}
175
+ />,
176
+ );
177
+
178
+ expect(component.find(DeleteModal).prop('isOpen')).toBe(false);
179
+
180
+ component.find(MenuItem).at(0).prop('onClick')({} as any);
181
+
182
+ expect(component.find(DeleteModal).prop('isOpen')).toBe(true);
183
+ expect(component.find(DeleteModal).prop('resource')).toEqual(securityGroup());
184
+ });
185
+
186
+ it('opens the manifest wizard from the actions menu', async () => {
187
+ const command = { manifest: {} };
188
+ spyOn(KubernetesManifestCommandBuilder, 'buildNewManifestCommand').and.returnValue(Promise.resolve(command) as any);
189
+ spyOn(ManifestWizard, 'show');
190
+ const app = appWithSecurityGroups();
191
+ const component = shallow(
192
+ <KubernetesSecurityGroupActions app={app} manifest={manifestDetails()} securityGroup={securityGroup()} />,
193
+ );
194
+
195
+ component.find(MenuItem).at(1).prop('onClick')({} as any);
196
+ await settle();
197
+
198
+ expect(KubernetesManifestCommandBuilder.buildNewManifestCommand).toHaveBeenCalledWith(
199
+ app,
200
+ manifestDetails().manifest,
201
+ securityGroup().moniker,
202
+ 'k8s-local',
203
+ );
204
+ expect(ManifestWizard.show).toHaveBeenCalledWith({
205
+ title: 'Edit Manifest',
206
+ application: app,
207
+ command,
208
+ });
209
+ });
210
+
211
+ it('renders entity tag links when enabled', () => {
212
+ SETTINGS.feature.entityTags = true;
213
+ const app = appWithSecurityGroups();
214
+ const component = shallow(
215
+ <KubernetesSecurityGroupActions app={app} manifest={manifestDetails()} securityGroup={securityGroup()} />,
216
+ );
217
+
218
+ expect(component.find(AddEntityTagLinks).prop('component')).toEqual(securityGroup());
219
+ expect(component.find(AddEntityTagLinks).prop('application')).toBe(app);
220
+ expect(component.find(AddEntityTagLinks).prop('entityType')).toBe('securityGroup');
221
+ });
222
+
223
+ it('does not render action controls when ad-hoc infrastructure writes are disabled', () => {
224
+ SETTINGS.kubernetesAdHocInfraWritesEnabled = false;
225
+
226
+ const component = shallow(
227
+ <KubernetesSecurityGroupActions
228
+ app={appWithSecurityGroups()}
229
+ manifest={manifestDetails()}
230
+ securityGroup={securityGroup()}
231
+ />,
232
+ );
233
+
234
+ expect(component.isEmptyRender()).toBe(true);
235
+ expect(component.find('Dropdown').exists()).toBe(false);
236
+ expect(component.find(MenuItem).exists()).toBe(false);
237
+ expect(component.find(DeleteModal).exists()).toBe(false);
238
+ });
239
+ });
240
+
241
+ describe('KubernetesV2SecurityGroupTransformer', () => {
242
+ it('normalizes Kubernetes security groups without modifying them', async () => {
243
+ const group = securityGroup();
244
+
245
+ await expectAsync(new KubernetesV2SecurityGroupTransformer().normalizeSecurityGroup(group)).toBeResolvedTo(group);
246
+ });
247
+ });
248
+
249
+ const settle = () => new Promise((resolve) => setTimeout(resolve));
250
+
251
+ const appWithSecurityGroups = () =>
252
+ ({
253
+ isStandalone: false,
254
+ getDataSource: () => ({
255
+ ready: () => Promise.resolve(),
256
+ onRefresh: () => () => null,
257
+ }),
258
+ securityGroups: {
259
+ refresh: jasmine.createSpy('refreshSecurityGroups'),
260
+ },
261
+ } as any);
262
+
263
+ const securityGroup = (overrides: any = {}) =>
264
+ ({
265
+ account: 'k8s-local',
266
+ apiVersion: 'networking.k8s.io/v1',
267
+ cloudProvider: 'kubernetes',
268
+ createdTime: 1753718892000,
269
+ displayName: 'backend-security-policy',
270
+ kind: 'networkPolicy',
271
+ moniker: { app: 'kubernetesapp', cluster: 'networkPolicy backend-security-policy' },
272
+ name: 'networkPolicy backend-security-policy',
273
+ namespace: 'dev',
274
+ region: 'dev',
275
+ ...overrides,
276
+ } as any);
277
+
278
+ const manifestDetails = () =>
279
+ ({
280
+ account: 'k8s-local',
281
+ manifest: {
282
+ metadata: {
283
+ annotations: {
284
+ 'strategy.spinnaker.io/deployment-info': 'Account: {{ resource.account }}',
285
+ },
286
+ labels: {
287
+ 'app.kubernetes.io/name': 'kubernetesapp',
288
+ },
289
+ name: 'backend-security-policy',
290
+ },
291
+ },
292
+ } as any);
@@ -0,0 +1,304 @@
1
+ import React from 'react';
2
+ import { Dropdown, MenuItem } from 'react-bootstrap';
3
+
4
+ import type {
5
+ Application,
6
+ IManifest,
7
+ IOverridableProps,
8
+ IRouterInjectedProps,
9
+ ISecurityGroupDetail,
10
+ SecurityGroupReader,
11
+ } from '@spinnaker/core';
12
+ import {
13
+ AccountTag,
14
+ AddEntityTagLinks,
15
+ CloudProviderLogo,
16
+ CollapsibleSection,
17
+ DeckRuntimeContext,
18
+ EntityNotifications,
19
+ FirewallLabels,
20
+ ManifestReader,
21
+ robotToHuman,
22
+ SETTINGS,
23
+ timestamp,
24
+ useModal,
25
+ withRouter,
26
+ } from '@spinnaker/core';
27
+
28
+ import type { IKubernetesSecurityGroup } from '../../interfaces';
29
+ import { AnnotationCustomSections } from '../../manifest/AnnotationCustomSections';
30
+ import { ManifestLabels } from '../../manifest/ManifestLabels';
31
+ import { DeleteModal } from '../../manifest/delete/DeleteModal';
32
+ import { KubernetesManifestCommandBuilder } from '../../manifest/manifestCommandBuilder.service';
33
+ import { ManifestWizard } from '../../manifest/wizard/ManifestWizard';
34
+
35
+ interface ISecurityGroupFromStateParams {
36
+ accountId: string;
37
+ name: string;
38
+ region: string;
39
+ }
40
+
41
+ export interface IKubernetesSecurityGroupDetailsProps extends IOverridableProps {
42
+ app: Application;
43
+ autoClose?: () => void;
44
+ resolvedSecurityGroup: ISecurityGroupFromStateParams;
45
+ securityGroupReader?: SecurityGroupReader;
46
+ }
47
+
48
+ interface IKubernetesSecurityGroupDetailsState {
49
+ loading: boolean;
50
+ manifest?: IManifest;
51
+ securityGroup?: IKubernetesSecurityGroup;
52
+ }
53
+
54
+ export interface IKubernetesSecurityGroupActionsProps {
55
+ app: Application;
56
+ manifest: IManifest;
57
+ securityGroup: IKubernetesSecurityGroup;
58
+ }
59
+
60
+ export class KubernetesSecurityGroupDetailsComponent extends React.Component<
61
+ IKubernetesSecurityGroupDetailsProps & IRouterInjectedProps,
62
+ IKubernetesSecurityGroupDetailsState
63
+ > {
64
+ public static contextType = DeckRuntimeContext;
65
+ public declare context: React.ContextType<typeof DeckRuntimeContext>;
66
+
67
+ public state: IKubernetesSecurityGroupDetailsState = {
68
+ loading: true,
69
+ };
70
+
71
+ private isUnmounted = false;
72
+ private dataSourceReady = false;
73
+ private loadRequestId = 0;
74
+ private unsubscribeFromRefresh: () => void;
75
+
76
+ public componentDidMount(): void {
77
+ const dataSource = this.props.app.getDataSource('securityGroups');
78
+
79
+ if (!dataSource) {
80
+ this.dataSourceReady = true;
81
+ this.loadSecurityGroup();
82
+ return;
83
+ }
84
+
85
+ dataSource
86
+ .ready()
87
+ .then(() => {
88
+ if (this.isUnmounted) {
89
+ return;
90
+ }
91
+
92
+ this.dataSourceReady = true;
93
+ this.loadSecurityGroup();
94
+ this.unsubscribeFromRefresh = dataSource.onRefresh(this.loadSecurityGroup);
95
+ })
96
+ .catch(this.autoClose);
97
+ }
98
+
99
+ public componentDidUpdate(prevProps: IKubernetesSecurityGroupDetailsProps): void {
100
+ if (this.dataSourceReady && this.getCoordinatesKey(prevProps) !== this.getCoordinatesKey(this.props)) {
101
+ this.loadSecurityGroup();
102
+ }
103
+ }
104
+
105
+ public componentWillUnmount(): void {
106
+ this.isUnmounted = true;
107
+ if (this.unsubscribeFromRefresh) {
108
+ this.unsubscribeFromRefresh();
109
+ }
110
+ }
111
+
112
+ private getSecurityGroupReader(): SecurityGroupReader {
113
+ return this.props.securityGroupReader || this.context.services.securityGroupReader;
114
+ }
115
+
116
+ private getCoordinatesKey(props: IKubernetesSecurityGroupDetailsProps): string {
117
+ const { accountId, name, region } = props.resolvedSecurityGroup;
118
+ return [accountId, region, name].join(':');
119
+ }
120
+
121
+ private loadSecurityGroup = (): void => {
122
+ const { app, resolvedSecurityGroup } = this.props;
123
+ const { accountId, name, region } = resolvedSecurityGroup;
124
+ const requestId = ++this.loadRequestId;
125
+
126
+ this.setState({ loading: true });
127
+
128
+ Promise.all([
129
+ this.getSecurityGroupReader().getSecurityGroupDetails(app, accountId, 'kubernetes', region, '', name),
130
+ ManifestReader.getManifest(accountId, region, name),
131
+ ])
132
+ .then(([securityGroup, manifest]: [ISecurityGroupDetail, IManifest]) => {
133
+ if (!this.isCurrentLoad(requestId)) {
134
+ return;
135
+ }
136
+
137
+ if (!securityGroup) {
138
+ this.autoClose();
139
+ return;
140
+ }
141
+
142
+ this.setState({
143
+ loading: false,
144
+ manifest,
145
+ securityGroup: securityGroup as IKubernetesSecurityGroup,
146
+ });
147
+ })
148
+ .catch(() => {
149
+ if (this.isCurrentLoad(requestId)) {
150
+ this.autoClose();
151
+ }
152
+ });
153
+ };
154
+
155
+ private isCurrentLoad(requestId: number): boolean {
156
+ return !this.isUnmounted && requestId === this.loadRequestId;
157
+ }
158
+
159
+ private autoClose = (): void => {
160
+ if (this.isUnmounted) {
161
+ return;
162
+ }
163
+
164
+ if (this.props.autoClose) {
165
+ this.props.autoClose();
166
+ return;
167
+ }
168
+
169
+ this.props.stateService.params.allowModalToStayOpen = true;
170
+ this.props.stateService.go('^', null, { location: 'replace' });
171
+ };
172
+
173
+ private closeDetails = (): void => {
174
+ this.props.stateService.go('^');
175
+ };
176
+
177
+ public render(): JSX.Element {
178
+ const { app } = this.props;
179
+ const { loading, manifest, securityGroup } = this.state;
180
+
181
+ if (loading || !manifest || !securityGroup) {
182
+ return (
183
+ <div className="details-panel">
184
+ <div className="header">
185
+ <div className="close-button">
186
+ <a className="btn btn-link" onClick={this.closeDetails}>
187
+ <span className="glyphicon glyphicon-remove" />
188
+ </a>
189
+ </div>
190
+ <h4 className="text-center">Loading...</h4>
191
+ </div>
192
+ </div>
193
+ );
194
+ }
195
+
196
+ const manifestBody = manifest.manifest;
197
+
198
+ return (
199
+ <div className="details-panel">
200
+ <div className="header">
201
+ <div className="close-button">
202
+ <a className="btn btn-link" onClick={this.closeDetails}>
203
+ <span className="glyphicon glyphicon-remove" />
204
+ </a>
205
+ </div>
206
+ <div className="header-text horizontal middle">
207
+ <CloudProviderLogo provider="kubernetes" height="36px" width="36px" />
208
+ <h3 className="horizontal middle space-between flex-1">
209
+ {securityGroup.displayName}
210
+ {SETTINGS.feature.entityTags && (
211
+ <EntityNotifications
212
+ entity={securityGroup}
213
+ application={app}
214
+ placement="bottom"
215
+ hOffsetPercent="90%"
216
+ entityType="securityGroup"
217
+ pageLocation="details"
218
+ onUpdate={() => app.securityGroups.refresh()}
219
+ />
220
+ )}
221
+ </h3>
222
+ </div>
223
+ <div className="actions">
224
+ <KubernetesSecurityGroupActions app={app} manifest={manifest} securityGroup={securityGroup} />
225
+ </div>
226
+ </div>
227
+
228
+ <div className="content">
229
+ <CollapsibleSection heading="Information" defaultExpanded={true}>
230
+ <dl className="dl-horizontal dl-narrow">
231
+ <dt>Created</dt>
232
+ <dd>{timestamp(securityGroup.createdTime)}</dd>
233
+ <dt>Account</dt>
234
+ <dd>
235
+ <AccountTag account={securityGroup.account} />
236
+ </dd>
237
+ <dt>Namespace</dt>
238
+ <dd>{securityGroup.namespace}</dd>
239
+ <dt>Kind</dt>
240
+ <dd>{securityGroup.kind}</dd>
241
+ </dl>
242
+ </CollapsibleSection>
243
+
244
+ <AnnotationCustomSections manifest={manifestBody} resource={securityGroup} />
245
+
246
+ <CollapsibleSection heading="Labels" defaultExpanded={true}>
247
+ <ManifestLabels manifest={manifestBody} />
248
+ </CollapsibleSection>
249
+ </div>
250
+ </div>
251
+ );
252
+ }
253
+ }
254
+
255
+ export const KubernetesSecurityGroupDetails = withRouter(KubernetesSecurityGroupDetailsComponent);
256
+
257
+ export function KubernetesSecurityGroupActions({ app, manifest, securityGroup }: IKubernetesSecurityGroupActionsProps) {
258
+ const deleteModal = useModal();
259
+ const showEntityTags = SETTINGS.feature && SETTINGS.feature.entityTags;
260
+
261
+ if (!SETTINGS.kubernetesAdHocInfraWritesEnabled) {
262
+ return null;
263
+ }
264
+
265
+ const editSecurityGroup = (): void => {
266
+ KubernetesManifestCommandBuilder.buildNewManifestCommand(
267
+ app,
268
+ manifest.manifest,
269
+ securityGroup.moniker,
270
+ securityGroup.account,
271
+ ).then((builtCommand) => {
272
+ ManifestWizard.show({ title: 'Edit Manifest', application: app, command: builtCommand });
273
+ });
274
+ };
275
+
276
+ return (
277
+ <>
278
+ <Dropdown className="dropdown" id="security-group-actions-dropdown">
279
+ <Dropdown.Toggle className="btn btn-sm btn-primary dropdown-toggle">
280
+ {robotToHuman(securityGroup.kind)} Actions
281
+ </Dropdown.Toggle>
282
+ <Dropdown.Menu>
283
+ <MenuItem onClick={deleteModal.show}>Delete {FirewallLabels.get('Firewall')}</MenuItem>
284
+ <MenuItem onClick={editSecurityGroup}>Edit {FirewallLabels.get('Firewall')}</MenuItem>
285
+ {showEntityTags && (
286
+ <AddEntityTagLinks
287
+ component={securityGroup}
288
+ application={app}
289
+ entityType="securityGroup"
290
+ onUpdate={() => app.securityGroups.refresh()}
291
+ />
292
+ )}
293
+ </Dropdown.Menu>
294
+ </Dropdown>
295
+ <DeleteModal
296
+ application={app}
297
+ resource={securityGroup}
298
+ manifestController={undefined}
299
+ isOpen={deleteModal.open}
300
+ dismissModal={deleteModal.close}
301
+ />
302
+ </>
303
+ );
304
+ }
@@ -1 +1,3 @@
1
- export * from './details/details.controller';
1
+ export * from './details/KubernetesSecurityGroupDetails';
2
+ export * from './securityGroup.reader';
3
+ export * from './transformer';
@@ -1,15 +1,7 @@
1
- import { module } from 'angular';
2
-
3
1
  import type { ISecurityGroup } from '@spinnaker/core';
4
2
 
5
- class KubernetesV2SecurityGroupTransformer {
6
- public normalizeSecurityGroup(securityGroup: ISecurityGroup): PromiseLike<ISecurityGroup> {
3
+ export class KubernetesV2SecurityGroupTransformer {
4
+ public normalizeSecurityGroup(securityGroup: ISecurityGroup): Promise<ISecurityGroup> {
7
5
  return Promise.resolve(securityGroup);
8
6
  }
9
7
  }
10
-
11
- export const KUBERNETES_SECURITY_GROUP_TRANSFORMER = 'spinnaker.kubernetes.securityGroupTransformer';
12
- module(KUBERNETES_SECURITY_GROUP_TRANSFORMER, []).service(
13
- 'kubernetesV2SecurityGroupTransformer',
14
- KubernetesV2SecurityGroupTransformer,
15
- );