@spinnaker/cloudrun 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.
Files changed (97) hide show
  1. package/dist/cloudrun.module.d.ts +1 -1
  2. package/dist/common/ComponentUrlDetails.d.ts +7 -0
  3. package/dist/common/LoadBalancerMessage.d.ts +4 -0
  4. package/dist/index.d.ts +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/instance/details/CloudrunInstanceDetails.d.ts +23 -0
  8. package/dist/loadBalancer/configure/wizard/CloudrunLoadBalancerModal.d.ts +32 -0
  9. package/dist/loadBalancer/details/CloudrunLoadBalancerActions.d.ts +3 -0
  10. package/dist/loadBalancer/details/sections.d.ts +7 -0
  11. package/dist/loadBalancer/details/useCloudrunLoadBalancerDetails.d.ts +4 -0
  12. package/dist/loadBalancer/index.d.ts +4 -2
  13. package/dist/loadBalancer/loadBalancerTransformer.d.ts +2 -6
  14. package/dist/manifest/manifest.service.d.ts +5 -0
  15. package/dist/manifest/manifestCommandBuilder.service.d.ts +1 -1
  16. package/dist/pipeline/stages/deployManifest/manifestStatus/DeployStatus.d.ts +1 -0
  17. package/dist/pipeline/stages/deployManifest/manifestStatus/ManifestDetailsLink.d.ts +6 -3
  18. package/dist/pipeline/stages/editLoadBalancer/CloudrunEditLoadBalancerExecutionDetails.d.ts +7 -0
  19. package/dist/pipeline/stages/editLoadBalancer/CloudrunEditLoadBalancerStageConfig.d.ts +30 -0
  20. package/dist/pipeline/stages/editLoadBalancer/cloudrunEditLoadBalancerStage.d.ts +2 -1
  21. package/dist/serverGroup/configure/serverGroupCommandBuilder.service.d.ts +4 -6
  22. package/dist/serverGroup/configure/wizard/BasicSettings.d.ts +5 -2
  23. package/dist/serverGroup/configure/wizard/serverGroupWizard.d.ts +18 -6
  24. package/dist/serverGroup/details/CloudrunServerGroupActions.d.ts +6 -0
  25. package/dist/serverGroup/details/cloudrunServerGroupDetailsGetter.d.ts +3 -0
  26. package/dist/serverGroup/details/sections.d.ts +5 -0
  27. package/dist/serverGroup/index.d.ts +3 -1
  28. package/dist/serverGroup/serverGroupTransformer.service.d.ts +1 -5
  29. package/package.json +4 -11
  30. package/src/cloudrun.module.spec.ts +22 -0
  31. package/src/cloudrun.module.ts +54 -59
  32. package/src/common/ComponentUrlDetails.tsx +25 -0
  33. package/src/common/LoadBalancerMessage.tsx +17 -0
  34. package/src/index.ts +2 -1
  35. package/src/instance/details/CloudrunInstanceDetails.tsx +164 -0
  36. package/src/loadBalancer/configure/wizard/CloudrunLoadBalancerModal.spec.tsx +85 -0
  37. package/src/loadBalancer/configure/wizard/CloudrunLoadBalancerModal.tsx +395 -0
  38. package/src/loadBalancer/details/CloudrunLoadBalancerActions.tsx +66 -0
  39. package/src/loadBalancer/details/sections.spec.ts +19 -0
  40. package/src/loadBalancer/details/sections.tsx +91 -0
  41. package/src/loadBalancer/details/useCloudrunLoadBalancerDetails.ts +56 -0
  42. package/src/loadBalancer/index.ts +4 -2
  43. package/src/loadBalancer/loadBalancerTransformer.ts +3 -13
  44. package/src/manifest/manifest.service.ts +18 -1
  45. package/src/manifest/manifestCommandBuilder.service.ts +46 -53
  46. package/src/pipeline/stages/deployManifest/manifestStatus/DeployStatus.tsx +33 -4
  47. package/src/pipeline/stages/deployManifest/manifestStatus/ManifestDetailsLink.spec.tsx +35 -0
  48. package/src/pipeline/stages/deployManifest/manifestStatus/ManifestDetailsLink.tsx +10 -5
  49. package/src/pipeline/stages/editLoadBalancer/CloudrunEditLoadBalancerExecutionDetails.tsx +70 -0
  50. package/src/pipeline/stages/editLoadBalancer/CloudrunEditLoadBalancerStageConfig.tsx +245 -0
  51. package/src/pipeline/stages/editLoadBalancer/cloudrunEditLoadBalancerStage.spec.ts +205 -0
  52. package/src/pipeline/stages/editLoadBalancer/cloudrunEditLoadBalancerStage.ts +14 -71
  53. package/src/serverGroup/configure/serverGroupCommandBuilder.service.ts +5 -15
  54. package/src/serverGroup/configure/wizard/BasicSettings.tsx +10 -8
  55. package/src/serverGroup/configure/wizard/serverGroupWizard.tsx +59 -15
  56. package/src/serverGroup/details/CloudrunServerGroupActions.tsx +80 -0
  57. package/src/serverGroup/details/cloudrunServerGroupDetailsGetter.spec.ts +46 -0
  58. package/src/serverGroup/details/cloudrunServerGroupDetailsGetter.ts +53 -0
  59. package/src/serverGroup/details/sections.tsx +60 -0
  60. package/src/serverGroup/index.ts +3 -1
  61. package/src/serverGroup/routerConsumers.spec.tsx +152 -0
  62. package/src/serverGroup/serverGroupTransformer.service.ts +2 -12
  63. package/dist/common/componentUrlDetails.component.d.ts +0 -1
  64. package/dist/common/conditionalDescriptionListItem.component.d.ts +0 -1
  65. package/dist/common/loadBalancerMessage.component.d.ts +0 -1
  66. package/dist/instance/details/details.controller.d.ts +0 -1
  67. package/dist/loadBalancer/configure/wizard/allocationConfigurationRow.component.d.ts +0 -1
  68. package/dist/loadBalancer/configure/wizard/basicSettings.component.d.ts +0 -1
  69. package/dist/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.d.ts +0 -1
  70. package/dist/loadBalancer/configure/wizard/wizard.controller.d.ts +0 -2
  71. package/dist/loadBalancer/details/details.controller.d.ts +0 -1
  72. package/dist/pipeline/pipeline.module.d.ts +0 -1
  73. package/dist/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.d.ts +0 -1
  74. package/dist/serverGroup/details/details.controller.d.ts +0 -1
  75. package/src/common/componentUrlDetails.component.ts +0 -22
  76. package/src/common/conditionalDescriptionListItem.component.ts +0 -37
  77. package/src/common/loadBalancerMessage.component.html +0 -11
  78. package/src/common/loadBalancerMessage.component.ts +0 -13
  79. package/src/instance/details/details.controller.ts +0 -84
  80. package/src/instance/details/details.html +0 -68
  81. package/src/loadBalancer/configure/wizard/allocationConfigurationRow.component.ts +0 -70
  82. package/src/loadBalancer/configure/wizard/basicSettings.component.html +0 -44
  83. package/src/loadBalancer/configure/wizard/basicSettings.component.ts +0 -86
  84. package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.html +0 -45
  85. package/src/loadBalancer/configure/wizard/stageAllocationConfigurationRow.component.ts +0 -84
  86. package/src/loadBalancer/configure/wizard/wizard.controller.ts +0 -157
  87. package/src/loadBalancer/configure/wizard/wizard.html +0 -39
  88. package/src/loadBalancer/configure/wizard/wizard.less +0 -9
  89. package/src/loadBalancer/details/details.controller.ts +0 -140
  90. package/src/loadBalancer/details/details.html +0 -89
  91. package/src/pipeline/pipeline.module.ts +0 -6
  92. package/src/pipeline/stages/editLoadBalancer/editLoadBalancerExecutionDetails.html +0 -33
  93. package/src/pipeline/stages/editLoadBalancer/editLoadBalancerStage.html +0 -51
  94. package/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.controller.ts +0 -65
  95. package/src/pipeline/stages/editLoadBalancer/loadBalancerChoice.modal.html +0 -53
  96. package/src/serverGroup/details/details.controller.ts +0 -134
  97. package/src/serverGroup/details/details.html +0 -94
@@ -1,7 +1,22 @@
1
1
  import React from 'react';
2
- import type { Application, IModalComponentProps, IStage } from '@spinnaker/core';
3
- //import type { IModalInstanceService } from 'angular-ui-bootstrap';
4
- import { noop, ReactInjector, ReactModal, TaskMonitor, WizardModal, WizardPage } from '@spinnaker/core';
2
+
3
+ import type {
4
+ Application,
5
+ DeckRuntimeServices,
6
+ IModalComponentProps,
7
+ IRouterInjectedProps,
8
+ IStage,
9
+ } from '@spinnaker/core';
10
+ import {
11
+ DeckRuntimeContext,
12
+ noop,
13
+ ReactModal,
14
+ TaskMonitor,
15
+ withRouter,
16
+ WizardModal,
17
+ WizardPage,
18
+ } from '@spinnaker/core';
19
+
5
20
  import { WizardServerGroupBasicSettings } from './BasicSettings';
6
21
  import { WizardServerGroupConfigFilesSettings } from './ConfigFiles';
7
22
  import type { ICloudrunServerGroupCommandData } from '../serverGroupCommandBuilder.service';
@@ -12,6 +27,8 @@ export interface ICloudrunServerGroupModalProps extends IModalComponentProps {
12
27
  application: Application;
13
28
  command: ICloudrunServerGroupCommandData;
14
29
  isNew?: boolean;
30
+ closeModal?: (command: any) => void;
31
+ dismissModal?: () => void;
15
32
  }
16
33
 
17
34
  export interface ICloudrunServerGroupModalState {
@@ -20,21 +37,31 @@ export interface ICloudrunServerGroupModalState {
20
37
  taskMonitor: TaskMonitor;
21
38
  }
22
39
 
23
- export class ServerGroupWizard extends React.Component<ICloudrunServerGroupModalProps, ICloudrunServerGroupModalState> {
24
- public static defaultProps: Partial<ICloudrunServerGroupModalProps> = {
40
+ export class ServerGroupWizardComponent extends React.Component<
41
+ ICloudrunServerGroupModalProps & IRouterInjectedProps,
42
+ ICloudrunServerGroupModalState
43
+ > {
44
+ public static contextType = DeckRuntimeContext;
45
+ public declare context: React.ContextType<typeof DeckRuntimeContext>;
46
+
47
+ public static defaultProps: Partial<ICloudrunServerGroupModalProps & IRouterInjectedProps> = {
25
48
  closeModal: noop,
26
49
  dismissModal: noop,
27
50
  };
28
51
 
29
52
  private _isUnmounted = false;
53
+ private applicationRefreshUnsubscribe?: () => void;
30
54
 
31
55
  /* private serverGroupWriter: ServerGroupWriter; */
32
- public static show(props: ICloudrunServerGroupModalProps): Promise<ICloudrunServerGroupCommandData> {
56
+ public static show(
57
+ props: ICloudrunServerGroupModalProps,
58
+ runtimeServices: DeckRuntimeServices,
59
+ ): Promise<ICloudrunServerGroupCommandData> {
33
60
  const modalProps = { dialogClassName: 'wizard-modal modal-lg' };
34
- return ReactModal.show(ServerGroupWizard, props, modalProps);
61
+ return ReactModal.show(ServerGroupWizard, props, modalProps, runtimeServices);
35
62
  }
36
63
 
37
- constructor(props: ICloudrunServerGroupModalProps) {
64
+ constructor(props: ICloudrunServerGroupModalProps & IRouterInjectedProps) {
38
65
  super(props);
39
66
  if (!props.command) {
40
67
  CloudrunServerGroupCommandBuilder.buildNewServerGroupCommand(props.application, 'cloudrun', 'create').then(
@@ -53,18 +80,20 @@ export class ServerGroupWizard extends React.Component<ICloudrunServerGroupModal
53
80
  title: `${
54
81
  props.command.command.viewState.submitButtonLabel === 'Create' ? 'Creating' : 'Updating'
55
82
  } your Server Group`,
56
- modalInstance: TaskMonitor.modalInstanceEmulation(() => this.props.dismissModal()),
83
+ onDismiss: () => this.props.dismissModal(),
57
84
  onTaskComplete: this.onTaskComplete,
58
85
  }),
59
86
  };
60
87
  }
61
88
 
62
89
  private onTaskComplete = () => {
90
+ this.clearApplicationRefreshSubscription();
91
+ this.applicationRefreshUnsubscribe = this.props.application.serverGroups.onNextRefresh(this.onApplicationRefresh);
63
92
  this.props.application.serverGroups.refresh();
64
- this.props.application.serverGroups.onNextRefresh(null, this.onApplicationRefresh);
65
93
  };
66
94
 
67
95
  protected onApplicationRefresh = (): void => {
96
+ this.clearApplicationRefreshSubscription();
68
97
  if (this._isUnmounted) {
69
98
  return;
70
99
  }
@@ -82,23 +111,33 @@ export class ServerGroupWizard extends React.Component<ICloudrunServerGroupModal
82
111
  provider: 'cloudrun',
83
112
  };
84
113
  let transitionTo = '^.^.^.clusters.serverGroup';
85
- if (ReactInjector.$state.includes('**.clusters.serverGroup')) {
114
+ if (this.props.stateService.includes('**.clusters.serverGroup')) {
86
115
  // clone via details, all view
87
116
  transitionTo = '^.serverGroup';
88
117
  }
89
- if (ReactInjector.$state.includes('**.clusters.cluster.serverGroup')) {
118
+ if (this.props.stateService.includes('**.clusters.cluster.serverGroup')) {
90
119
  // clone or create with details open
91
120
  transitionTo = '^.^.serverGroup';
92
121
  }
93
- if (ReactInjector.$state.includes('**.clusters')) {
122
+ if (this.props.stateService.includes('**.clusters')) {
94
123
  // create new, no details open
95
124
  transitionTo = '.serverGroup';
96
125
  }
97
- ReactInjector.$state.go(transitionTo, newStateParams);
126
+ this.props.stateService.go(transitionTo, newStateParams);
98
127
  }
99
128
  }
100
129
  };
101
130
 
131
+ public componentWillUnmount(): void {
132
+ this._isUnmounted = true;
133
+ this.clearApplicationRefreshSubscription();
134
+ }
135
+
136
+ private clearApplicationRefreshSubscription = (): void => {
137
+ this.applicationRefreshUnsubscribe?.();
138
+ this.applicationRefreshUnsubscribe = undefined;
139
+ };
140
+
102
141
  private submit = (c: ICloudrunServerGroupCommandData): void => {
103
142
  const command: any = CloudrunServerGroupCommandBuilder.copyAndCleanCommand(c.command);
104
143
  const forPipelineConfig = command.viewState.mode === 'editPipeline' || command.viewState.mode === 'createPipeline';
@@ -106,7 +145,8 @@ export class ServerGroupWizard extends React.Component<ICloudrunServerGroupModal
106
145
  this.props.closeModal && this.props.closeModal(command);
107
146
  } else {
108
147
  //command.viewState.mode = 'create';
109
- const submitMethod = () => ReactInjector.serverGroupWriter.cloneServerGroup(command, this.props.application);
148
+ const submitMethod = () =>
149
+ this.context.services.serverGroupWriter.cloneServerGroup(command, this.props.application);
110
150
  this.state.taskMonitor.submit(submitMethod);
111
151
  return null;
112
152
  }
@@ -148,3 +188,7 @@ export class ServerGroupWizard extends React.Component<ICloudrunServerGroupModal
148
188
  );
149
189
  }
150
190
  }
191
+
192
+ export const ServerGroupWizard = Object.assign(withRouter(ServerGroupWizardComponent), {
193
+ show: ServerGroupWizardComponent.show,
194
+ });
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import { Dropdown, MenuItem } from 'react-bootstrap';
3
+
4
+ import type { IRouterInjectedProps, IServerGroupActionsProps } from '@spinnaker/core';
5
+ import { ConfirmationModalService, useDeckRuntimeServices, withRouter } from '@spinnaker/core';
6
+
7
+ import { CloudrunHealth } from '../../common/cloudrunHealth';
8
+ import type { ICloudrunServerGroup } from '../../interfaces';
9
+
10
+ export function CloudrunServerGroupActionsComponent({
11
+ app,
12
+ serverGroup,
13
+ stateService,
14
+ }: IServerGroupActionsProps & IRouterInjectedProps) {
15
+ const { serverGroupWriter } = useDeckRuntimeServices();
16
+ const cloudrunServerGroup = serverGroup as ICloudrunServerGroup;
17
+ const canDestroyServerGroup = Boolean(
18
+ cloudrunServerGroup && !cloudrunServerGroup.tags?.isLatest && cloudrunServerGroup.disabled,
19
+ );
20
+ const destroyServerGroup = () => {
21
+ const stateParams = {
22
+ name: cloudrunServerGroup.name,
23
+ accountId: cloudrunServerGroup.account,
24
+ region: cloudrunServerGroup.region,
25
+ };
26
+ const taskMonitor = {
27
+ application: app,
28
+ title: `Destroying ${cloudrunServerGroup.name}`,
29
+ onTaskComplete: () => {
30
+ if (stateService.includes('**.serverGroup', stateParams)) {
31
+ stateService.go('^');
32
+ }
33
+ },
34
+ };
35
+ const confirmationModalParams = {
36
+ header: `Really destroy ${cloudrunServerGroup.name}?`,
37
+ buttonText: `Destroy ${cloudrunServerGroup.name}`,
38
+ account: cloudrunServerGroup.account,
39
+ taskMonitorConfig: taskMonitor,
40
+ submitMethod: (params: any) => serverGroupWriter.destroyServerGroup(cloudrunServerGroup, app, params),
41
+ askForReason: true,
42
+ platformHealthOnlyShowOverride: app.attributes.platformHealthOnlyShowOverride,
43
+ platformHealthType: CloudrunHealth.PLATFORM,
44
+ interestingHealthProviderNames: [] as string[],
45
+ };
46
+
47
+ if (app.attributes.platformHealthOnlyShowOverride && app.attributes.platformHealthOnly) {
48
+ confirmationModalParams.interestingHealthProviderNames = [CloudrunHealth.PLATFORM];
49
+ }
50
+
51
+ ConfirmationModalService.confirm(confirmationModalParams);
52
+ };
53
+
54
+ return (
55
+ <Dropdown className="dropdown" id="cloudrun-server-group-actions-dropdown">
56
+ <Dropdown.Toggle className="btn btn-sm btn-primary dropdown-toggle">Server Group Actions</Dropdown.Toggle>
57
+ <Dropdown.Menu>
58
+ {cloudrunServerGroup.tags?.isLatest && (
59
+ <MenuItem
60
+ disabled={true}
61
+ title="You cannot destroy the latest server group (revision). You may be able to delete this server group's load balancer."
62
+ >
63
+ Destroy
64
+ </MenuItem>
65
+ )}
66
+ {canDestroyServerGroup && <MenuItem onClick={destroyServerGroup}>Destroy</MenuItem>}
67
+ {!canDestroyServerGroup && !cloudrunServerGroup.tags?.isLatest && (
68
+ <MenuItem
69
+ disabled={true}
70
+ title="You cannot destroy this server group while it is receiving traffic. You may be able to delete this server group's load balancer."
71
+ >
72
+ Destroy
73
+ </MenuItem>
74
+ )}
75
+ </Dropdown.Menu>
76
+ </Dropdown>
77
+ );
78
+ }
79
+
80
+ export const CloudrunServerGroupActions = withRouter(CloudrunServerGroupActionsComponent);
@@ -0,0 +1,46 @@
1
+ import { ServerGroupReader } from '@spinnaker/core';
2
+
3
+ import { cloudrunServerGroupDetailsGetter } from './cloudrunServerGroupDetailsGetter';
4
+
5
+ describe('cloudrunServerGroupDetailsGetter', () => {
6
+ const props: any = {
7
+ app: {
8
+ name: 'fnord',
9
+ serverGroups: { data: [] },
10
+ loadBalancers: { data: [] },
11
+ },
12
+ serverGroup: {
13
+ name: 'service-v000',
14
+ accountId: 'test',
15
+ region: 'us-central1',
16
+ },
17
+ };
18
+
19
+ it('auto-closes and completes when server group lookup fails', (done) => {
20
+ const autoClose = jasmine.createSpy('autoClose');
21
+ spyOn(ServerGroupReader, 'getServerGroup').and.returnValue(Promise.reject(new Error('not found')) as any);
22
+
23
+ cloudrunServerGroupDetailsGetter(props, autoClose).subscribe({
24
+ next: () => fail('should not emit a server group'),
25
+ error: () => fail('should not emit an error'),
26
+ complete: () => {
27
+ expect(autoClose).toHaveBeenCalled();
28
+ done();
29
+ },
30
+ });
31
+ });
32
+
33
+ it('auto-closes and completes when lookup returns no server group', (done) => {
34
+ const autoClose = jasmine.createSpy('autoClose');
35
+ spyOn(ServerGroupReader, 'getServerGroup').and.returnValue(Promise.resolve(null) as any);
36
+
37
+ cloudrunServerGroupDetailsGetter(props, autoClose).subscribe({
38
+ next: () => fail('should not emit a server group'),
39
+ error: () => fail('should not emit an error'),
40
+ complete: () => {
41
+ expect(autoClose).toHaveBeenCalled();
42
+ done();
43
+ },
44
+ });
45
+ });
46
+ });
@@ -0,0 +1,53 @@
1
+ import { isEmpty } from 'lodash';
2
+ import { Observable } from 'rxjs';
3
+
4
+ import type { IServerGroup, IServerGroupDetailsProps } from '@spinnaker/core';
5
+ import { ServerGroupReader } from '@spinnaker/core';
6
+
7
+ export const cloudrunServerGroupDetailsGetter = (
8
+ props: IServerGroupDetailsProps,
9
+ autoClose: () => void,
10
+ ): Observable<IServerGroup> => {
11
+ const { app, serverGroup } = props;
12
+ return new Observable<IServerGroup>((observer) => {
13
+ ServerGroupReader.getServerGroup(app.name, serverGroup.accountId, serverGroup.region, serverGroup.name).then(
14
+ (serverGroupDetails: IServerGroup) => {
15
+ let fromApp = app.serverGroups.data.find((toCheck: IServerGroup) => {
16
+ return (
17
+ toCheck.name === serverGroup.name &&
18
+ toCheck.account === serverGroup.accountId &&
19
+ toCheck.region === serverGroup.region
20
+ );
21
+ });
22
+
23
+ if (!fromApp) {
24
+ app.loadBalancers.data.some((loadBalancer: any) => {
25
+ if (loadBalancer.account !== serverGroup.accountId) {
26
+ return false;
27
+ }
28
+ return loadBalancer.serverGroups.some((toCheck: IServerGroup) => {
29
+ if (toCheck.name === serverGroup.name) {
30
+ fromApp = toCheck;
31
+ return true;
32
+ }
33
+ return false;
34
+ });
35
+ });
36
+ }
37
+
38
+ const mergedServerGroup = { ...serverGroupDetails, ...fromApp };
39
+ if (isEmpty(mergedServerGroup)) {
40
+ autoClose();
41
+ observer.complete();
42
+ return;
43
+ }
44
+
45
+ observer.next(mergedServerGroup);
46
+ },
47
+ () => {
48
+ autoClose();
49
+ observer.complete();
50
+ },
51
+ );
52
+ });
53
+ };
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+
3
+ import type { IServerGroupDetailsSectionProps } from '@spinnaker/core';
4
+ import { AccountTag, CollapsibleSection, HealthCounts, timestamp } from '@spinnaker/core';
5
+
6
+ export function CloudrunServerGroupInformationSection({ serverGroup }: IServerGroupDetailsSectionProps) {
7
+ return (
8
+ <CollapsibleSection heading="Server Group Information" defaultExpanded={true}>
9
+ <dl className="dl-horizontal dl-narrow">
10
+ <dt>Created</dt>
11
+ <dd>{timestamp(serverGroup.createdTime)}</dd>
12
+ <dt>In</dt>
13
+ <dd>
14
+ <AccountTag account={serverGroup.account} />
15
+ </dd>
16
+ <dt>Region</dt>
17
+ <dd>{serverGroup.region}</dd>
18
+ </dl>
19
+ </CollapsibleSection>
20
+ );
21
+ }
22
+
23
+ export function CloudrunServerGroupSizeSection({ serverGroup }: IServerGroupDetailsSectionProps) {
24
+ const capacity = serverGroup.capacity || ({} as any);
25
+ const current = serverGroup.instances?.length || 0;
26
+ return (
27
+ <CollapsibleSection heading="Size" defaultExpanded={true}>
28
+ <dl className="dl-horizontal dl-narrow">
29
+ {capacity.min === capacity.max ? (
30
+ <>
31
+ <dt>Min/Max</dt>
32
+ <dd>{capacity.min}</dd>
33
+ </>
34
+ ) : (
35
+ <>
36
+ <dt>Min</dt>
37
+ <dd>{capacity.min}</dd>
38
+ <dt>Max</dt>
39
+ <dd>{capacity.max}</dd>
40
+ </>
41
+ )}
42
+ <dt>Current</dt>
43
+ <dd>{current}</dd>
44
+ </dl>
45
+ </CollapsibleSection>
46
+ );
47
+ }
48
+
49
+ export function CloudrunServerGroupHealthSection({ serverGroup }: IServerGroupDetailsSectionProps) {
50
+ return (
51
+ <CollapsibleSection heading="Health" defaultExpanded={true}>
52
+ <dl className="dl-horizontal dl-narrow">
53
+ <dt>Instances</dt>
54
+ <dd>
55
+ <HealthCounts container={serverGroup.instanceCounts} className="pull-left" />
56
+ </dd>
57
+ </dl>
58
+ </CollapsibleSection>
59
+ );
60
+ }
@@ -1,3 +1,5 @@
1
1
  export * from './configure/serverGroupCommandBuilder.service';
2
2
  export * from './serverGroupTransformer.service';
3
- export * from './details/details.controller';
3
+ export * from './details/CloudrunServerGroupActions';
4
+ export * from './details/cloudrunServerGroupDetailsGetter';
5
+ export * from './details/sections';
@@ -0,0 +1,152 @@
1
+ import { mount, shallow } from 'enzyme';
2
+ import React from 'react';
3
+ import { MenuItem } from 'react-bootstrap';
4
+
5
+ import { ConfirmationModalService, DeckRuntimeContext, ServerGroupNamePreview } from '@spinnaker/core';
6
+
7
+ import { ServerGroupBasicSettingsComponent } from './configure/wizard/BasicSettings';
8
+ import { ServerGroupWizardComponent } from './configure/wizard/serverGroupWizard';
9
+ import { CloudrunServerGroupActionsComponent } from './details/CloudrunServerGroupActions';
10
+
11
+ describe('Cloud Run server group router consumers', () => {
12
+ it('opens the latest server group through the injected state service', () => {
13
+ const go = jasmine.createSpy('go');
14
+ const component = shallow(
15
+ <ServerGroupBasicSettingsComponent
16
+ {...({ router: {}, stateParams: {}, stateService: { go, is: () => true } } as any)}
17
+ accounts={[]}
18
+ app={
19
+ {
20
+ clusters: [],
21
+ name: 'app',
22
+ serverGroups: {
23
+ data: [{ account: 'test', cluster: 'app-main', createdTime: 1, name: 'app-main-v001', region: 'us' }],
24
+ },
25
+ } as any
26
+ }
27
+ detailsChanged={() => undefined}
28
+ formik={
29
+ {
30
+ values: {
31
+ command: {
32
+ credentials: 'test',
33
+ region: 'us',
34
+ selectedProvider: 'cloudrun',
35
+ viewState: { mode: 'create' },
36
+ },
37
+ stack: 'main',
38
+ },
39
+ } as any
40
+ }
41
+ onAccountSelect={() => undefined}
42
+ onEnterStack={() => undefined}
43
+ selectedAccount="test"
44
+ />,
45
+ );
46
+
47
+ component.find(ServerGroupNamePreview).prop('navigateToLatestServerGroup')();
48
+
49
+ expect(go).toHaveBeenCalledWith('.serverGroup', {
50
+ accountId: 'test',
51
+ provider: 'cloudrun',
52
+ region: 'us',
53
+ serverGroup: 'app-main-v001',
54
+ });
55
+ });
56
+
57
+ it('closes destroyed server group details through the injected state service', () => {
58
+ const go = jasmine.createSpy('go');
59
+ const confirm = spyOn(ConfirmationModalService, 'confirm');
60
+ const component = mount(
61
+ <DeckRuntimeContext.Provider value={{ services: { serverGroupWriter: {} } } as any}>
62
+ <CloudrunServerGroupActionsComponent
63
+ {...({ router: {}, stateParams: {}, stateService: { go, includes: () => true } } as any)}
64
+ app={{ attributes: {} } as any}
65
+ serverGroup={
66
+ {
67
+ account: 'test',
68
+ disabled: true,
69
+ name: 'app-v001',
70
+ region: 'us',
71
+ tags: { isLatest: false },
72
+ } as any
73
+ }
74
+ />
75
+ </DeckRuntimeContext.Provider>,
76
+ );
77
+
78
+ component.find(MenuItem).first().prop('onClick')();
79
+ confirm.calls.mostRecent().args[0].taskMonitorConfig.onTaskComplete();
80
+
81
+ expect(go).toHaveBeenCalledWith('^');
82
+ });
83
+
84
+ it('opens a newly created server group through the injected state service', () => {
85
+ const go = jasmine.createSpy('go');
86
+ const component = new ServerGroupWizardComponent({
87
+ application: { serverGroups: {} },
88
+ closeModal: () => undefined,
89
+ command: {
90
+ command: { credentials: 'test', region: 'us', viewState: { submitButtonLabel: 'Create' } },
91
+ },
92
+ dismissModal: () => undefined,
93
+ router: {},
94
+ stateParams: {},
95
+ stateService: { go, includes: (state: string) => state === '**.clusters' },
96
+ title: 'Create server group',
97
+ } as any);
98
+ component.state.taskMonitor = {
99
+ task: {
100
+ execution: {
101
+ stages: [{ context: { 'deploy.server.groups': { us: 'app-v001' } }, type: 'cloneServerGroup' }],
102
+ },
103
+ },
104
+ } as any;
105
+
106
+ (component as any).onApplicationRefresh();
107
+
108
+ expect(go).toHaveBeenCalledWith('.serverGroup', {
109
+ accountId: 'test',
110
+ provider: 'cloudrun',
111
+ region: 'us',
112
+ serverGroup: 'app-v001',
113
+ });
114
+ });
115
+
116
+ it('owns the server group refresh subscription across replacement and unmount', () => {
117
+ const firstUnsubscribe = jasmine.createSpy('firstUnsubscribe');
118
+ const secondUnsubscribe = jasmine.createSpy('secondUnsubscribe');
119
+ const callbacks: Array<() => void> = [];
120
+ const onNextRefresh = jasmine.createSpy('onNextRefresh').and.callFake((callback: () => void) => {
121
+ callbacks.push(callback);
122
+ return callbacks.length === 1 ? firstUnsubscribe : secondUnsubscribe;
123
+ });
124
+ const refresh = jasmine.createSpy('refresh');
125
+ const go = jasmine.createSpy('go');
126
+ const component = new ServerGroupWizardComponent({
127
+ application: { serverGroups: { onNextRefresh, refresh } },
128
+ closeModal: jasmine.createSpy('closeModal'),
129
+ command: { command: { credentials: 'test', region: 'us', viewState: { submitButtonLabel: 'Create' } } },
130
+ dismissModal: jasmine.createSpy('dismissModal'),
131
+ router: {},
132
+ stateParams: {},
133
+ stateService: { go, includes: () => false },
134
+ title: 'Create server group',
135
+ } as any) as any;
136
+
137
+ component.onTaskComplete();
138
+
139
+ expect(onNextRefresh.calls.first().invocationOrder).toBeLessThan(refresh.calls.first().invocationOrder);
140
+
141
+ component.onTaskComplete();
142
+
143
+ expect(firstUnsubscribe).toHaveBeenCalledTimes(1);
144
+
145
+ component.componentWillUnmount();
146
+ callbacks[1]();
147
+
148
+ expect(secondUnsubscribe).toHaveBeenCalledTimes(1);
149
+ expect(component.applicationRefreshUnsubscribe).toBeUndefined();
150
+ expect(go).not.toHaveBeenCalled();
151
+ });
152
+ });
@@ -1,14 +1,10 @@
1
- import { module } from 'angular';
2
1
  import type { IServerGroup } from '@spinnaker/core';
3
2
 
4
3
  import type { ICloudrunServerGroupCommand } from '../serverGroup/configure/serverGroupCommandBuilder.service';
5
4
 
6
5
  export class CloudrunV2ServerGroupTransformer {
7
- public static $inject = ['$q'];
8
- constructor(private $q: ng.IQService) {}
9
-
10
- public normalizeServerGroup(serverGroup: IServerGroup): PromiseLike<IServerGroup> {
11
- return this.$q.resolve(serverGroup);
6
+ public normalizeServerGroup(serverGroup: IServerGroup): Promise<IServerGroup> {
7
+ return Promise.resolve(serverGroup);
12
8
  }
13
9
 
14
10
  public convertServerGroupCommandToDeployConfiguration(command: ICloudrunServerGroupCommand): any {
@@ -53,9 +49,3 @@ export class CloudrunDeployDescription {
53
49
  this.configArtifacts = [];
54
50
  }
55
51
  }
56
-
57
- export const CLOUDRUN_SERVER_GROUP_TRANSFORMER = 'spinnaker.cloudrun.serverGroup.transformer.service';
58
- module(CLOUDRUN_SERVER_GROUP_TRANSFORMER, []).service(
59
- 'cloudrunV2ServerGroupTransformer',
60
- CloudrunV2ServerGroupTransformer,
61
- );
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_COMPONENT_URL_DETAILS = "spinnaker.cloudrun.componentUrlDetails.component";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_CONDITIONAL_DESCRIPTION_LIST_ITEM = "spinnaker.cloudrun.conditionalDescriptionListItem";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_LOAD_BALANCER_CREATE_MESSAGE = "spinnaker.cloudrun.loadBalancer.createMessage.component";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_INSTANCE_DETAILS_CTRL = "spinnaker.cloudrun.instanceDetails.controller";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_ALLOCATION_CONFIGURATION_ROW = "spinnaker.cloudrun.allocationConfigurationRow.component";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_LOAD_BALANCER_BASIC_SETTINGS = "spinnaker.cloudrun.loadBalancerSettings.component";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_STAGE_ALLOCATION_CONFIGURATION_ROW = "spinnaker.cloudrun.stageAllocationConfigurationRow.component";
@@ -1,2 +0,0 @@
1
- import './wizard.less';
2
- export declare const CLOUDRUN_LOAD_BALANCER_WIZARD_CTRL = "spinnaker.cloudrun.loadBalancer.wizard.controller";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_LOAD_BALANCER_DETAILS_CTRL = "spinnaker.cloudrun.loadBalancerDetails.controller";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_PIPELINE_MODULE = "spinnaker.cloudrun.pipeline.module";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_LOAD_BALANCER_CHOICE_MODAL_CTRL = "spinnaker.Cloudrun.loadBalancerChoiceModal.controller";
@@ -1 +0,0 @@
1
- export declare const CLOUDRUN_SERVER_GROUP_DETAILS_CTRL = "spinnaker.cloudrun.serverGroup.details.controller";
@@ -1,22 +0,0 @@
1
- import type { IComponentOptions } from 'angular';
2
- import { module } from 'angular';
3
-
4
- const cloudrunComponentUrlDetailsComponent: IComponentOptions = {
5
- bindings: { component: '<' },
6
- template: `
7
- <dt>HTTPS</dt>
8
- <dl class="small">
9
- <a href="{{$ctrl.component.url}}" target="_blank">{{$ctrl.component.url}}</a>
10
- <copy-to-clipboard class="copy-to-clipboard copy-to-clipboard-sm"
11
- tool-tip="'Copy URL to clipboard'"
12
- text="$ctrl.component.httpsUrl"></copy-to-clipboard>
13
- </dl>
14
- `,
15
- };
16
-
17
- export const CLOUDRUN_COMPONENT_URL_DETAILS = 'spinnaker.cloudrun.componentUrlDetails.component';
18
-
19
- module(CLOUDRUN_COMPONENT_URL_DETAILS, []).component(
20
- 'cloudrunComponentUrlDetails',
21
- cloudrunComponentUrlDetailsComponent,
22
- );