@spinnaker/kubernetes 2025.1.4 → 2025.2.1
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.js +37010 -4266
- package/dist/index.js.map +1 -1
- package/dist/interfaces/infrastructure.types.d.ts +4 -0
- package/dist/loadBalancer/details/KubernetesLoadBalancerActions.d.ts +7 -0
- package/dist/loadBalancer/details/sections/IKubernetesLoadBalancerDetailsSectionProps.d.ts +5 -0
- package/dist/loadBalancer/details/sections/LoadBalancerAnnotationCustomSection.d.ts +3 -0
- package/dist/loadBalancer/details/sections/LoadBalancerEventsSection.d.ts +3 -0
- package/dist/loadBalancer/details/sections/LoadBalancerInformationSection.d.ts +3 -0
- package/dist/loadBalancer/details/sections/LoadBalancerLabelsSection.d.ts +3 -0
- package/dist/loadBalancer/details/sections/LoadBalancerStatusSection.d.ts +3 -0
- package/dist/loadBalancer/details/sections/index.d.ts +6 -0
- package/dist/loadBalancer/details/useKubernetesLoadBalancerDetails.d.ts +3 -0
- package/dist/loadBalancer/index.d.ts +4 -1
- package/dist/loadBalancer/transformer.d.ts +5 -1
- package/dist/manifest/delete/Delete.d.ts +10 -0
- package/dist/manifest/delete/DeleteModal.d.ts +13 -0
- package/dist/manifest/rollout/PauseRollout.d.ts +9 -0
- package/dist/manifest/rollout/ResumeRollout.d.ts +9 -0
- package/dist/manifest/rollout/RollingRestart.d.ts +1 -0
- package/dist/manifest/rollout/UndoRollout.d.ts +9 -0
- package/dist/manifest/rollout/UndoRolloutModal.d.ts +12 -0
- package/dist/manifest/rollout/undo.controller.d.ts +4 -0
- package/dist/manifest/scale/Scale.d.ts +10 -0
- package/dist/manifest/scale/ScaleModal.d.ts +13 -0
- package/package.json +2 -2
- package/src/interfaces/infrastructure.types.ts +11 -0
- package/src/kubernetes.module.ts +30 -7
- package/src/loadBalancer/details/KubernetesLoadBalancerActions.tsx +68 -0
- package/src/loadBalancer/details/sections/IKubernetesLoadBalancerDetailsSectionProps.ts +6 -0
- package/src/loadBalancer/details/sections/LoadBalancerAnnotationCustomSection.tsx +9 -0
- package/src/loadBalancer/details/sections/LoadBalancerEventsSection.tsx +15 -0
- package/src/loadBalancer/details/sections/LoadBalancerInformationSection.tsx +28 -0
- package/src/loadBalancer/details/sections/LoadBalancerLabelsSection.tsx +15 -0
- package/src/loadBalancer/details/sections/LoadBalancerStatusSection.tsx +138 -0
- package/src/loadBalancer/details/sections/index.ts +6 -0
- package/src/loadBalancer/details/useKubernetesLoadBalancerDetails.ts +52 -0
- package/src/loadBalancer/index.ts +4 -1
- package/src/loadBalancer/transformer.ts +2 -13
- package/src/manifest/delete/Delete.tsx +34 -0
- package/src/manifest/delete/DeleteModal.tsx +152 -0
- package/src/manifest/editor/json/JsonEditor.tsx +3 -4
- package/src/manifest/rollout/PauseRollout.tsx +46 -0
- package/src/manifest/rollout/ResumeRollout.tsx +46 -0
- package/src/manifest/rollout/RollingRestart.tsx +21 -13
- package/src/manifest/rollout/UndoRollout.tsx +33 -0
- package/src/manifest/rollout/UndoRolloutModal.tsx +123 -0
- package/src/manifest/rollout/undo.controller.ts +1 -1
- package/src/manifest/scale/Scale.tsx +34 -0
- package/src/manifest/scale/ScaleModal.tsx +115 -0
- package/src/manifest/selector/ManifestSelector.spec.tsx +2 -3
- package/src/manifest/selector/ManifestSelector.tsx +1 -2
- package/src/rawResource/rawResource.dataSource.ts +20 -24
- package/src/securityGroup/transformer.ts +1 -5
- package/dist/loadBalancer/details/details.controller.d.ts +0 -1
- package/src/loadBalancer/details/details.controller.ts +0 -101
- package/src/loadBalancer/details/details.html +0 -187
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import type { IManifest, IUseDetailsHookProps, UseDetailsHook, UseDetailsResult } from '@spinnaker/core';
|
|
4
|
+
import { ManifestReader, useData, useDataSource } from '@spinnaker/core';
|
|
5
|
+
|
|
6
|
+
import type { IKubernetesLoadBalancer } from '../../interfaces';
|
|
7
|
+
|
|
8
|
+
export const useKubernetesLoadBalancerDetails: UseDetailsHook<IKubernetesLoadBalancer> = (
|
|
9
|
+
props: IUseDetailsHookProps,
|
|
10
|
+
): UseDetailsResult<IKubernetesLoadBalancer> => {
|
|
11
|
+
const { loadBalancerParams, app, autoClose } = props;
|
|
12
|
+
const { name, accountId, region } = loadBalancerParams;
|
|
13
|
+
|
|
14
|
+
const dataSource = app.getDataSource('loadBalancers');
|
|
15
|
+
const {
|
|
16
|
+
data: loadBalancers,
|
|
17
|
+
loaded: isLoadBalancersLoaded,
|
|
18
|
+
refresh: refreshLoadBalancers,
|
|
19
|
+
error: loadBalancersError,
|
|
20
|
+
} = useDataSource<IKubernetesLoadBalancer[]>(dataSource);
|
|
21
|
+
|
|
22
|
+
const { result: manifest, status: manifestStatus, refresh: refreshManifest, error: manifestError } = useData(
|
|
23
|
+
() => ManifestReader.getManifest(accountId, region, name),
|
|
24
|
+
{} as IManifest,
|
|
25
|
+
[],
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const loading = !isLoadBalancersLoaded || manifestStatus !== 'RESOLVED';
|
|
29
|
+
|
|
30
|
+
const error = loadBalancersError || manifestError || undefined;
|
|
31
|
+
|
|
32
|
+
const data = useMemo<IKubernetesLoadBalancer | undefined>(() => {
|
|
33
|
+
if (loading || error) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const details = loadBalancers.find(
|
|
37
|
+
(lb: IKubernetesLoadBalancer) => lb.name === name && lb.account === accountId && lb.region === region,
|
|
38
|
+
);
|
|
39
|
+
if (!details) {
|
|
40
|
+
autoClose();
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
return { ...details, manifest };
|
|
44
|
+
}, [isLoadBalancersLoaded, loadBalancers, manifestStatus, manifest]);
|
|
45
|
+
|
|
46
|
+
const refetch = async () => {
|
|
47
|
+
refreshLoadBalancers();
|
|
48
|
+
refreshManifest();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return { data, loading, error, refetch };
|
|
52
|
+
};
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import type { IQService } from 'angular';
|
|
2
|
-
import { module } from 'angular';
|
|
3
1
|
import { camelCase, chain } from 'lodash';
|
|
4
2
|
|
|
5
3
|
import type { IInstanceCounts, IServerGroup } from '@spinnaker/core';
|
|
6
4
|
|
|
7
5
|
import type { IKubernetesLoadBalancer } from '../interfaces';
|
|
8
6
|
|
|
9
|
-
class
|
|
10
|
-
public static $inject = ['$q'];
|
|
11
|
-
constructor(private $q: IQService) {}
|
|
12
|
-
|
|
7
|
+
export class KubernetesLoadBalancerTransformer {
|
|
13
8
|
public normalizeLoadBalancer(loadBalancer: IKubernetesLoadBalancer): PromiseLike<IKubernetesLoadBalancer> {
|
|
14
9
|
loadBalancer.provider = loadBalancer.type;
|
|
15
10
|
loadBalancer.instances = [];
|
|
@@ -20,7 +15,7 @@ class KubernetesV2LoadBalancerTransformer {
|
|
|
20
15
|
instance.cloudProvider = loadBalancer.provider;
|
|
21
16
|
});
|
|
22
17
|
});
|
|
23
|
-
return
|
|
18
|
+
return Promise.resolve(loadBalancer);
|
|
24
19
|
}
|
|
25
20
|
|
|
26
21
|
private buildInstanceCounts(serverGroups: IServerGroup[]): IInstanceCounts {
|
|
@@ -52,9 +47,3 @@ class KubernetesV2LoadBalancerTransformer {
|
|
|
52
47
|
return instanceCounts;
|
|
53
48
|
}
|
|
54
49
|
}
|
|
55
|
-
|
|
56
|
-
export const KUBERNETES_LOAD_BALANCER_TRANSFORMER = 'spinnaker.kubernetes.loadBalancerTransformer';
|
|
57
|
-
module(KUBERNETES_LOAD_BALANCER_TRANSFORMER, []).service(
|
|
58
|
-
'kubernetesV2LoadBalancerTransformer',
|
|
59
|
-
KubernetesV2LoadBalancerTransformer,
|
|
60
|
-
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { MenuItem } from 'react-bootstrap';
|
|
4
|
+
import { react2angular } from 'react2angular';
|
|
5
|
+
|
|
6
|
+
import type { Application } from '@spinnaker/core';
|
|
7
|
+
import { withErrorBoundary } from '@spinnaker/core';
|
|
8
|
+
import { useModal } from '@spinnaker/core';
|
|
9
|
+
|
|
10
|
+
import { DeleteModal } from './DeleteModal';
|
|
11
|
+
import type { IAnyKubernetesResource } from '../../interfaces';
|
|
12
|
+
|
|
13
|
+
export interface IDeleteProps {
|
|
14
|
+
application: Application;
|
|
15
|
+
resource: IAnyKubernetesResource;
|
|
16
|
+
manifestController: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Delete(props: IDeleteProps) {
|
|
20
|
+
const { open, show, close } = useModal();
|
|
21
|
+
const handleClick = () => show();
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<MenuItem onClick={handleClick}>Delete</MenuItem>
|
|
25
|
+
<DeleteModal isOpen={open} dismissModal={close} {...props} />
|
|
26
|
+
</>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const KUBERNETES_DELETE = 'spinnaker.kubernetes.delete';
|
|
31
|
+
module(KUBERNETES_DELETE, []).component(
|
|
32
|
+
'kubernetesDelete',
|
|
33
|
+
react2angular(withErrorBoundary(Delete, 'kubernetesDelete'), ['application', 'resource', 'manifestController']),
|
|
34
|
+
);
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { UISref } from '@uirouter/react';
|
|
2
|
+
import { UIRouterContextComponent } from '@uirouter/react-hybrid';
|
|
3
|
+
import type { FormikProps } from 'formik';
|
|
4
|
+
import { Form } from 'formik';
|
|
5
|
+
import React, { useState } from 'react';
|
|
6
|
+
import { Button, Modal } from 'react-bootstrap';
|
|
7
|
+
|
|
8
|
+
import type { Application, IModalComponentProps, IModalProps } from '@spinnaker/core';
|
|
9
|
+
import {
|
|
10
|
+
ManifestWriter,
|
|
11
|
+
ModalClose,
|
|
12
|
+
robotToHuman,
|
|
13
|
+
SpinFormik,
|
|
14
|
+
SubmitButton,
|
|
15
|
+
TaskMonitorWrapper,
|
|
16
|
+
TaskReason,
|
|
17
|
+
UserVerification,
|
|
18
|
+
useTaskMonitor,
|
|
19
|
+
ValidationMessage,
|
|
20
|
+
} from '@spinnaker/core';
|
|
21
|
+
|
|
22
|
+
import type { IDeleteOptions } from './delete.controller';
|
|
23
|
+
import type { IAnyKubernetesResource } from '../../interfaces';
|
|
24
|
+
import DeleteManifestOptionsForm from '../../pipelines/stages/deleteManifest/DeleteManifestOptionsForm';
|
|
25
|
+
|
|
26
|
+
export interface IKubernetesDeleteModalProps
|
|
27
|
+
extends Pick<IModalProps, 'isOpen'>,
|
|
28
|
+
Pick<IModalComponentProps, 'dismissModal'> {
|
|
29
|
+
application: Application;
|
|
30
|
+
resource: IAnyKubernetesResource;
|
|
31
|
+
manifestController: string | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface IKubernetesDeleteValues extends IDeleteOptions {
|
|
35
|
+
reason?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function DeleteModal({
|
|
39
|
+
application,
|
|
40
|
+
resource,
|
|
41
|
+
manifestController,
|
|
42
|
+
isOpen,
|
|
43
|
+
dismissModal,
|
|
44
|
+
}: IKubernetesDeleteModalProps) {
|
|
45
|
+
const initialValues: IKubernetesDeleteValues = {
|
|
46
|
+
cascading: true,
|
|
47
|
+
};
|
|
48
|
+
const [verified, setVerified] = useState<boolean>(false);
|
|
49
|
+
const taskMonitor = useTaskMonitor(
|
|
50
|
+
{
|
|
51
|
+
application,
|
|
52
|
+
title: `Deleting ${resource.name}`,
|
|
53
|
+
onTaskComplete: () => application.serverGroups.refresh(true),
|
|
54
|
+
},
|
|
55
|
+
dismissModal,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const submit = (values: IKubernetesDeleteValues): void => {
|
|
59
|
+
const payload = {
|
|
60
|
+
cloudProvider: 'kubernetes',
|
|
61
|
+
manifestName: resource.name,
|
|
62
|
+
location: resource.namespace,
|
|
63
|
+
account: resource.account,
|
|
64
|
+
reason: values.reason,
|
|
65
|
+
options: {
|
|
66
|
+
gracePeriodSeconds: values.gracePeriodSeconds,
|
|
67
|
+
orphanDependants: !values.cascading,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
return taskMonitor.submit(() => ManifestWriter.deleteManifest(payload, application));
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const onOptionChange = (formik: FormikProps<IKubernetesDeleteValues>, values: IKubernetesDeleteValues): void => {
|
|
74
|
+
formik.setFieldValue('gracePeriodSeconds', values.gracePeriodSeconds);
|
|
75
|
+
formik.setFieldValue('cascading', values.cascading);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<UIRouterContextComponent>
|
|
80
|
+
<Modal show={isOpen} onHide={dismissModal}>
|
|
81
|
+
<TaskMonitorWrapper monitor={taskMonitor} />
|
|
82
|
+
<SpinFormik<IKubernetesDeleteValues>
|
|
83
|
+
initialValues={initialValues}
|
|
84
|
+
onSubmit={submit}
|
|
85
|
+
render={(formik) => (
|
|
86
|
+
<>
|
|
87
|
+
<ModalClose dismiss={dismissModal} />
|
|
88
|
+
<Modal.Header>
|
|
89
|
+
<Modal.Title>
|
|
90
|
+
Delete {robotToHuman(resource.name)} in {resource.namespace}
|
|
91
|
+
</Modal.Title>
|
|
92
|
+
</Modal.Header>
|
|
93
|
+
{manifestController && (
|
|
94
|
+
<div className="alert alert-warning">
|
|
95
|
+
Manifest is controlled by{' '}
|
|
96
|
+
<UISref
|
|
97
|
+
to="^.serverGroupManager"
|
|
98
|
+
params={{
|
|
99
|
+
accountId: resource.account,
|
|
100
|
+
region: resource.region,
|
|
101
|
+
serverGroupManager: manifestController,
|
|
102
|
+
provider: 'kubernetes',
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<a> {robotToHuman(manifestController)} </a>
|
|
106
|
+
</UISref>{' '}
|
|
107
|
+
and may be recreated after deletion.
|
|
108
|
+
</div>
|
|
109
|
+
)}
|
|
110
|
+
<Modal.Body>
|
|
111
|
+
<Form className="form-horizontal">
|
|
112
|
+
<DeleteManifestOptionsForm
|
|
113
|
+
onOptionsChange={(options: IDeleteOptions) => onOptionChange(formik, options)}
|
|
114
|
+
options={{
|
|
115
|
+
gracePeriodSeconds: formik.values.gracePeriodSeconds,
|
|
116
|
+
cascading: formik.values.cascading,
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
<TaskReason reason={formik.values.reason} onChange={(val) => formik.setFieldValue('reason', val)} />
|
|
120
|
+
</Form>
|
|
121
|
+
{formik.status?.error && (
|
|
122
|
+
<div className="sp-margin-xl-top">
|
|
123
|
+
<ValidationMessage
|
|
124
|
+
type="error"
|
|
125
|
+
message={
|
|
126
|
+
<span className="flex-container-v">
|
|
127
|
+
<span className="text-bold">Something went wrong:</span>
|
|
128
|
+
{formik.status.error.message && <span>{formik.status.error.message}</span>}
|
|
129
|
+
</span>
|
|
130
|
+
}
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
</Modal.Body>
|
|
135
|
+
<Modal.Footer>
|
|
136
|
+
<UserVerification account={resource.account} onValidChange={setVerified} />
|
|
137
|
+
<Button onClick={dismissModal}>Cancel</Button>
|
|
138
|
+
<SubmitButton
|
|
139
|
+
onClick={() => submit(formik.values)}
|
|
140
|
+
isDisabled={!formik.isValid || formik.isSubmitting || !verified}
|
|
141
|
+
isFormSubmit={true}
|
|
142
|
+
submitting={formik.isSubmitting}
|
|
143
|
+
label={`Delete ${resource.name}`}
|
|
144
|
+
/>
|
|
145
|
+
</Modal.Footer>
|
|
146
|
+
</>
|
|
147
|
+
)}
|
|
148
|
+
/>
|
|
149
|
+
</Modal>
|
|
150
|
+
</UIRouterContextComponent>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import 'brace/mode/json';
|
|
2
2
|
import 'brace/theme/textmate';
|
|
3
|
-
import { $log } from 'ngimport';
|
|
4
3
|
import React from 'react';
|
|
5
4
|
import AceEditor from 'react-ace';
|
|
6
5
|
|
|
@@ -17,12 +16,12 @@ export class JsonEditor extends React.Component<IJsonEditorProps> {
|
|
|
17
16
|
const obj = JSON.parse(raw);
|
|
18
17
|
this.props.onChange
|
|
19
18
|
? this.props.onChange(raw, obj)
|
|
20
|
-
:
|
|
19
|
+
: console.warn('No `onChange` handler provided for JSON editor.');
|
|
21
20
|
} catch (e) {
|
|
22
21
|
this.props.onChange
|
|
23
22
|
? this.props.onChange(raw, null)
|
|
24
|
-
:
|
|
25
|
-
|
|
23
|
+
: console.warn('No `onChange` handler provided for JSON editor.');
|
|
24
|
+
console.warn(`Error loading JSON from string ${raw}: `, e);
|
|
26
25
|
}
|
|
27
26
|
};
|
|
28
27
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { MenuItem } from 'react-bootstrap';
|
|
4
|
+
import { react2angular } from 'react2angular';
|
|
5
|
+
|
|
6
|
+
import type { Application } from '@spinnaker/core';
|
|
7
|
+
import { ConfirmationModalService, ManifestWriter, robotToHuman, withErrorBoundary } from '@spinnaker/core';
|
|
8
|
+
|
|
9
|
+
import type { IAnyKubernetesResource } from '../../interfaces';
|
|
10
|
+
|
|
11
|
+
export interface IPauseRolloutProps {
|
|
12
|
+
application: Application;
|
|
13
|
+
resource: IAnyKubernetesResource;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function PauseRollout({ application, resource }: IPauseRolloutProps) {
|
|
17
|
+
const handleClick = () => {
|
|
18
|
+
ConfirmationModalService.confirm({
|
|
19
|
+
account: resource.account,
|
|
20
|
+
askForReason: true,
|
|
21
|
+
header: `Pause rollout of ${robotToHuman(resource.name)} in ${resource.namespace}`,
|
|
22
|
+
submitMethod: (params: { reason?: string }) => {
|
|
23
|
+
const payload = {
|
|
24
|
+
cloudProvider: 'kubernetes',
|
|
25
|
+
manifestName: resource.name,
|
|
26
|
+
location: resource.namespace,
|
|
27
|
+
account: resource.account,
|
|
28
|
+
reason: params.reason,
|
|
29
|
+
};
|
|
30
|
+
return ManifestWriter.pauseRolloutManifest(payload, application);
|
|
31
|
+
},
|
|
32
|
+
taskMonitorConfig: {
|
|
33
|
+
application,
|
|
34
|
+
title: `Pause rollout of ${resource.name} in ${resource.namespace}`,
|
|
35
|
+
onTaskComplete: () => application.serverGroups.refresh(true),
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
return <MenuItem onClick={handleClick}>Pause Rollout</MenuItem>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const KUBERNETES_PAUSE_ROLLOUT = 'spinnaker.kubernetes.pause.rollout';
|
|
43
|
+
module(KUBERNETES_PAUSE_ROLLOUT, []).component(
|
|
44
|
+
'kubernetesPauseRollout',
|
|
45
|
+
react2angular(withErrorBoundary(PauseRollout, 'kubernetesPauseRollout'), ['application', 'resource']),
|
|
46
|
+
);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { MenuItem } from 'react-bootstrap';
|
|
4
|
+
import { react2angular } from 'react2angular';
|
|
5
|
+
|
|
6
|
+
import type { Application } from '@spinnaker/core';
|
|
7
|
+
import { ConfirmationModalService, ManifestWriter, robotToHuman, withErrorBoundary } from '@spinnaker/core';
|
|
8
|
+
|
|
9
|
+
import type { IAnyKubernetesResource } from '../../interfaces';
|
|
10
|
+
|
|
11
|
+
export interface IResumeRolloutProps {
|
|
12
|
+
application: Application;
|
|
13
|
+
resource: IAnyKubernetesResource;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function ResumeRollout({ application, resource }: IResumeRolloutProps) {
|
|
17
|
+
const handleClick = () => {
|
|
18
|
+
ConfirmationModalService.confirm({
|
|
19
|
+
account: resource.account,
|
|
20
|
+
askForReason: true,
|
|
21
|
+
header: `Resume rollout of ${robotToHuman(resource.name)} in ${resource.namespace}`,
|
|
22
|
+
submitMethod: (params: { reason?: string }) => {
|
|
23
|
+
const payload = {
|
|
24
|
+
cloudProvider: 'kubernetes',
|
|
25
|
+
manifestName: resource.name,
|
|
26
|
+
location: resource.namespace,
|
|
27
|
+
account: resource.account,
|
|
28
|
+
reason: params.reason,
|
|
29
|
+
};
|
|
30
|
+
return ManifestWriter.pauseRolloutManifest(payload, application);
|
|
31
|
+
},
|
|
32
|
+
taskMonitorConfig: {
|
|
33
|
+
application,
|
|
34
|
+
title: `Resume rollout of ${resource.name} in ${resource.namespace}`,
|
|
35
|
+
onTaskComplete: () => application.serverGroups.refresh(true),
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
return <MenuItem onClick={handleClick}>Resume Rollout</MenuItem>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const KUBERNETES_RESUME_ROLLOUT = 'spinnaker.kubernetes.resume.rollout';
|
|
43
|
+
module(KUBERNETES_RESUME_ROLLOUT, []).component(
|
|
44
|
+
'kubernetesResumeRollout',
|
|
45
|
+
react2angular(withErrorBoundary(ResumeRollout, 'kubernetesResumeRollout'), ['application', 'resource']),
|
|
46
|
+
);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
1
2
|
import React from 'react';
|
|
3
|
+
import { MenuItem } from 'react-bootstrap';
|
|
4
|
+
import { react2angular } from 'react2angular';
|
|
2
5
|
|
|
3
6
|
import type { Application } from '@spinnaker/core';
|
|
4
|
-
import { ConfirmationModalService, ManifestWriter } from '@spinnaker/core';
|
|
7
|
+
import { ConfirmationModalService, ManifestWriter, withErrorBoundary } from '@spinnaker/core';
|
|
5
8
|
|
|
6
9
|
import type { IKubernetesServerGroupManager } from '../../interfaces';
|
|
7
10
|
|
|
@@ -15,32 +18,37 @@ interface IRollingRestartParameters {
|
|
|
15
18
|
cloudProvider: string;
|
|
16
19
|
location: string;
|
|
17
20
|
manifestName: string;
|
|
21
|
+
reason?: string;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export function RollingRestart({ application, serverGroupManager }: IRollingRestartProps) {
|
|
21
25
|
function rollingRestart() {
|
|
22
|
-
const rollingRestartParameters: IRollingRestartParameters = {
|
|
23
|
-
account: serverGroupManager.account,
|
|
24
|
-
cloudProvider: 'kubernetes',
|
|
25
|
-
location: serverGroupManager.namespace,
|
|
26
|
-
manifestName: serverGroupManager.name,
|
|
27
|
-
};
|
|
28
26
|
ConfirmationModalService.confirm({
|
|
29
27
|
account: serverGroupManager.account,
|
|
30
28
|
askForReason: true,
|
|
31
29
|
header: `Initiate rolling restart of ${serverGroupManager.name}`,
|
|
32
|
-
submitMethod: () => {
|
|
30
|
+
submitMethod: (params: { reason?: string }) => {
|
|
31
|
+
const rollingRestartParameters: IRollingRestartParameters = {
|
|
32
|
+
account: serverGroupManager.account,
|
|
33
|
+
cloudProvider: 'kubernetes',
|
|
34
|
+
location: serverGroupManager.namespace,
|
|
35
|
+
manifestName: serverGroupManager.name,
|
|
36
|
+
reason: params.reason,
|
|
37
|
+
};
|
|
33
38
|
return ManifestWriter.rollingRestartManifest(rollingRestartParameters, application);
|
|
34
39
|
},
|
|
35
40
|
taskMonitorConfig: {
|
|
36
41
|
application,
|
|
37
42
|
title: `Rolling restart of ${serverGroupManager.name}`,
|
|
43
|
+
onTaskComplete: () => application.serverGroups.refresh(true),
|
|
38
44
|
},
|
|
39
45
|
});
|
|
40
46
|
}
|
|
41
|
-
return
|
|
42
|
-
<li>
|
|
43
|
-
<a onClick={rollingRestart}>Rolling Restart</a>
|
|
44
|
-
</li>
|
|
45
|
-
);
|
|
47
|
+
return <MenuItem onClick={rollingRestart}>Rolling Restart</MenuItem>;
|
|
46
48
|
}
|
|
49
|
+
|
|
50
|
+
export const KUBERNETES_ROLLING_RESTART = 'spinnaker.kubernetes.v2.rolling.restart';
|
|
51
|
+
module(KUBERNETES_ROLLING_RESTART, []).component(
|
|
52
|
+
'kubernetesRollingRestart',
|
|
53
|
+
react2angular(withErrorBoundary(RollingRestart, 'kubernetesRollingRestart'), ['application', 'serverGroupManager']),
|
|
54
|
+
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { MenuItem } from 'react-bootstrap';
|
|
4
|
+
import { react2angular } from 'react2angular';
|
|
5
|
+
|
|
6
|
+
import type { Application } from '@spinnaker/core';
|
|
7
|
+
import { withErrorBoundary } from '@spinnaker/core';
|
|
8
|
+
import { useModal } from '@spinnaker/core';
|
|
9
|
+
|
|
10
|
+
import { UndoRolloutModal } from './UndoRolloutModal';
|
|
11
|
+
import type { IAnyKubernetesResource } from '../../interfaces';
|
|
12
|
+
|
|
13
|
+
export interface IUndoRolloutProps {
|
|
14
|
+
application: Application;
|
|
15
|
+
resource: IAnyKubernetesResource;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function UndoRollout(props: IUndoRolloutProps) {
|
|
19
|
+
const { open, show, close } = useModal();
|
|
20
|
+
const handleClick = () => show();
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<MenuItem onClick={handleClick}>Undo Rolloutaaaa</MenuItem>
|
|
24
|
+
<UndoRolloutModal isOpen={open} dismissModal={close} {...props} />
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const KUBERNETES_UNDO_ROLLOUT = 'spinnaker.kubernetes.undo.rollout';
|
|
30
|
+
module(KUBERNETES_UNDO_ROLLOUT, []).component(
|
|
31
|
+
'kubernetesUndoRollout',
|
|
32
|
+
react2angular(withErrorBoundary(UndoRollout, 'kubernetesUndoRollout'), ['application', 'resource']),
|
|
33
|
+
);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Form } from 'formik';
|
|
2
|
+
import { orderBy } from 'lodash';
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
import { Button, Modal } from 'react-bootstrap';
|
|
5
|
+
import type { Option } from 'react-select';
|
|
6
|
+
|
|
7
|
+
import type { Application, IModalComponentProps, IModalProps, IServerGroupManager } from '@spinnaker/core';
|
|
8
|
+
import {
|
|
9
|
+
ManifestWriter,
|
|
10
|
+
ModalClose,
|
|
11
|
+
NameUtils,
|
|
12
|
+
ReactSelectInput,
|
|
13
|
+
robotToHuman,
|
|
14
|
+
SpinFormik,
|
|
15
|
+
SubmitButton,
|
|
16
|
+
TaskMonitorWrapper,
|
|
17
|
+
TaskReason,
|
|
18
|
+
UserVerification,
|
|
19
|
+
useTaskMonitor,
|
|
20
|
+
} from '@spinnaker/core';
|
|
21
|
+
|
|
22
|
+
import type { IAnyKubernetesResource } from '../../interfaces';
|
|
23
|
+
import type { IRolloutRevision } from './undo.controller';
|
|
24
|
+
|
|
25
|
+
export interface IKubernetesUndoRolloutModalProps
|
|
26
|
+
extends Pick<IModalProps, 'isOpen'>,
|
|
27
|
+
Pick<IModalComponentProps, 'dismissModal'> {
|
|
28
|
+
application: Application;
|
|
29
|
+
resource: IAnyKubernetesResource;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface IKubernetesUndoRolloutValues {
|
|
33
|
+
revision?: number;
|
|
34
|
+
reason?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const fetchRevisions = (serverGroupManager: IServerGroupManager): IRolloutRevision[] => {
|
|
38
|
+
const [, ...rest] = orderBy(serverGroupManager.serverGroups, ['moniker.sequence'], ['desc']);
|
|
39
|
+
return rest.map((serverGroup, index) => ({
|
|
40
|
+
label: `${NameUtils.getSequence(serverGroup.moniker.sequence)}${index > 0 ? '' : ' - previous revision'}`,
|
|
41
|
+
revision: serverGroup.moniker.sequence,
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function UndoRolloutModal({ application, resource, isOpen, dismissModal }: IKubernetesUndoRolloutModalProps) {
|
|
46
|
+
const initialValues: IKubernetesUndoRolloutValues = {};
|
|
47
|
+
const revisions = fetchRevisions(resource as IServerGroupManager);
|
|
48
|
+
const [verified, setVerified] = useState<boolean>(false);
|
|
49
|
+
const taskMonitor = useTaskMonitor(
|
|
50
|
+
{
|
|
51
|
+
application,
|
|
52
|
+
title: `Undo rollout of ${resource.name} in ${resource.namespace}`,
|
|
53
|
+
onTaskComplete: () => application.serverGroups.refresh(true),
|
|
54
|
+
},
|
|
55
|
+
dismissModal,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const submit = (values: IKubernetesUndoRolloutValues): void => {
|
|
59
|
+
const payload = {
|
|
60
|
+
cloudProvider: 'kubernetes',
|
|
61
|
+
manifestName: resource.name,
|
|
62
|
+
location: resource.namespace,
|
|
63
|
+
account: resource.account,
|
|
64
|
+
reason: values.reason,
|
|
65
|
+
revision: values.revision,
|
|
66
|
+
};
|
|
67
|
+
return taskMonitor.submit(() => ManifestWriter.undoRolloutManifest(payload, application));
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<Modal show={isOpen} onHide={dismissModal}>
|
|
72
|
+
<TaskMonitorWrapper monitor={taskMonitor} />
|
|
73
|
+
<SpinFormik<IKubernetesUndoRolloutValues>
|
|
74
|
+
initialValues={initialValues}
|
|
75
|
+
onSubmit={submit}
|
|
76
|
+
render={(formik) => (
|
|
77
|
+
<>
|
|
78
|
+
<ModalClose dismiss={dismissModal} />
|
|
79
|
+
<Modal.Header>
|
|
80
|
+
<Modal.Title>
|
|
81
|
+
Undo rollout of {robotToHuman(resource.name)} in {resource.namespace}
|
|
82
|
+
</Modal.Title>
|
|
83
|
+
</Modal.Header>
|
|
84
|
+
<Modal.Body>
|
|
85
|
+
<Form className="form-horizontal">
|
|
86
|
+
<div className="form-group form-inline">
|
|
87
|
+
<label className="col-md-3 sm-label-right">
|
|
88
|
+
<span className="label-text">Revision </span>
|
|
89
|
+
</label>
|
|
90
|
+
<div className="col-md-7">
|
|
91
|
+
<ReactSelectInput<number>
|
|
92
|
+
clearable={false}
|
|
93
|
+
value={formik.values.revision}
|
|
94
|
+
options={revisions.map((revision) => ({
|
|
95
|
+
label: revision.label,
|
|
96
|
+
value: revision.revision,
|
|
97
|
+
}))}
|
|
98
|
+
onChange={(option: Option<number>) => {
|
|
99
|
+
formik.setFieldValue('revision', option.target.value);
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
<TaskReason reason={formik.values.reason} onChange={(val) => formik.setFieldValue('reason', val)} />
|
|
105
|
+
</Form>
|
|
106
|
+
</Modal.Body>
|
|
107
|
+
<Modal.Footer>
|
|
108
|
+
<UserVerification account={resource.account} onValidChange={setVerified} />
|
|
109
|
+
<Button onClick={dismissModal}>Cancel</Button>
|
|
110
|
+
<SubmitButton
|
|
111
|
+
onClick={() => submit(formik.values)}
|
|
112
|
+
isDisabled={!formik.isValid || formik.isSubmitting || !verified}
|
|
113
|
+
isFormSubmit={true}
|
|
114
|
+
submitting={formik.isSubmitting}
|
|
115
|
+
label={`Undo rollout of ${resource.name}`}
|
|
116
|
+
/>
|
|
117
|
+
</Modal.Footer>
|
|
118
|
+
</>
|
|
119
|
+
)}
|
|
120
|
+
/>
|
|
121
|
+
</Modal>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { module } from 'angular';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { MenuItem } from 'react-bootstrap';
|
|
4
|
+
import { react2angular } from 'react2angular';
|
|
5
|
+
|
|
6
|
+
import type { Application } from '@spinnaker/core';
|
|
7
|
+
import { withErrorBoundary } from '@spinnaker/core';
|
|
8
|
+
import { useModal } from '@spinnaker/core';
|
|
9
|
+
|
|
10
|
+
import { ScaleModal } from './ScaleModal';
|
|
11
|
+
import type { IAnyKubernetesResource } from '../../interfaces';
|
|
12
|
+
|
|
13
|
+
export interface IScaleProps {
|
|
14
|
+
application: Application;
|
|
15
|
+
resource: IAnyKubernetesResource;
|
|
16
|
+
currentReplicas: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Scale(props: IScaleProps) {
|
|
20
|
+
const { open, show, close } = useModal();
|
|
21
|
+
const handleClick = () => show();
|
|
22
|
+
return (
|
|
23
|
+
<>
|
|
24
|
+
<MenuItem onClick={handleClick}>Scale</MenuItem>
|
|
25
|
+
<ScaleModal isOpen={open} dismissModal={close} {...props} />
|
|
26
|
+
</>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const KUBERNETES_SCALE = 'spinnaker.kubernetes.scale';
|
|
31
|
+
module(KUBERNETES_SCALE, []).component(
|
|
32
|
+
'kubernetesScale',
|
|
33
|
+
react2angular(withErrorBoundary(Scale, 'kubernetesScale'), ['application', 'resource', 'currentReplicas']),
|
|
34
|
+
);
|