argo-cd
162 строки · 6.4 Кб
1import {Checkbox, Select, Tooltip} from 'argo-ui';
2import * as classNames from 'classnames';
3import * as React from 'react';
4import * as ReactForm from 'react-form';
5
6import './application-sync-options.scss';
7
8export const REPLACE_WARNING = `The resources will be synced using 'kubectl replace/create' command that is a potentially destructive action and might cause resources recreation.`;
9export const FORCE_WARNING = `The resources will be synced using '--force' that is a potentially destructive action and will immediately remove resources from the API and bypasses graceful deletion. Immediate deletion of some resources may result in inconsistency or data loss.`;
10export const PRUNE_ALL_WARNING = `The resources will be synced using '--prune', and all resources are marked to be pruned. Only continue if you want to delete all of the Application's resources.`;
11
12export interface ApplicationSyncOptionProps {
13options: string[];
14onChanged: (updatedOptions: string[]) => any;
15id?: string;
16}
17
18function selectOption(name: string, label: string, defaultVal: string, values: string[], props: ApplicationSyncOptionProps) {
19const options = [...(props.options || [])];
20const prefix = `${name}=`;
21const index = options.findIndex(item => item.startsWith(prefix));
22const val = index < 0 ? defaultVal : options[index].substr(prefix.length);
23
24return (
25<div className='application-sync-options__select'>
26<label>{label}:</label>
27<Select
28value={val}
29options={values}
30onChange={opt => {
31const newValue = `${name}=${opt.value}`;
32if (index < 0) {
33props.onChanged(options.concat(newValue));
34} else {
35options[index] = newValue;
36props.onChanged(options);
37}
38}}
39/>
40</div>
41);
42}
43
44function booleanOption(name: string, label: string, defaultVal: boolean, props: ApplicationSyncOptionProps, invert: boolean, warning: string = null) {
45const options = [...(props.options || [])];
46const prefix = `${name}=`;
47const index = options.findIndex(item => item.startsWith(prefix));
48const checked = index < 0 ? defaultVal : options[index].substring(prefix.length) === (invert ? 'false' : 'true');
49return (
50<React.Fragment>
51<Checkbox
52id={`sync-option-${name}-${props.id}`}
53checked={checked}
54onChange={(val: boolean) => {
55if (index < 0) {
56props.onChanged(options.concat(`${name}=${invert ? !val : val}`));
57} else {
58options.splice(index, 1);
59props.onChanged(options);
60}
61}}
62/>
63<label htmlFor={`sync-option-${name}-${props.id}`}>{label}</label>{' '}
64{warning && (
65<>
66<Tooltip content={warning}>
67<i className='fa fa-exclamation-triangle' />
68</Tooltip>
69{checked && <div className='application-sync-options__warning'>{warning}</div>}
70</>
71)}
72</React.Fragment>
73);
74}
75
76enum ManualSyncFlags {
77Prune = 'Prune',
78DryRun = 'Dry Run',
79ApplyOnly = 'Apply Only',
80Force = 'Force'
81}
82
83export interface SyncFlags {
84Prune: boolean;
85DryRun: boolean;
86ApplyOnly: boolean;
87Force: boolean;
88}
89
90const syncOptions: Array<(props: ApplicationSyncOptionProps) => React.ReactNode> = [
91props => booleanOption('Validate', 'Skip Schema Validation', false, props, true),
92props => booleanOption('CreateNamespace', 'Auto-Create Namespace', false, props, false),
93props => booleanOption('PruneLast', 'Prune Last', false, props, false),
94props => booleanOption('ApplyOutOfSyncOnly', 'Apply Out of Sync Only', false, props, false),
95props => booleanOption('RespectIgnoreDifferences', 'Respect Ignore Differences', false, props, false),
96props => booleanOption('ServerSideApply', 'Server-Side Apply', false, props, false),
97props => selectOption('PrunePropagationPolicy', 'Prune Propagation Policy', 'foreground', ['foreground', 'background', 'orphan'], props)
98];
99
100const optionStyle = {marginTop: '0.5em'};
101
102export const ApplicationSyncOptions = (props: ApplicationSyncOptionProps) => (
103<div className='row application-sync-options'>
104{syncOptions.map((render, i) => (
105<div
106key={i}
107style={optionStyle}
108className={classNames('small-12', {
109'large-6': i < syncOptions.length - 1
110})}>
111{render(props)}
112</div>
113))}
114<div className='small-12' style={optionStyle}>
115{booleanOption('Replace', 'Replace', false, props, false, REPLACE_WARNING)}
116</div>
117</div>
118);
119
120export const ApplicationManualSyncFlags = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi; id?: string}) => {
121const {
122fieldApi: {getValue, setValue, setTouched}
123} = props;
124const val = getValue() || false;
125return (
126<div style={optionStyle}>
127{Object.keys(ManualSyncFlags).map(flag => (
128<React.Fragment key={flag}>
129<Checkbox
130id={`sync-option-${flag}-${props.id}`}
131checked={val[flag]}
132onChange={(newVal: boolean) => {
133setTouched(true);
134const update = {...val};
135update[flag] = newVal;
136setValue(update);
137}}
138/>
139<label htmlFor={`sync-option-${flag}-${props.id}`}>{ManualSyncFlags[flag as keyof typeof ManualSyncFlags]}</label>{' '}
140</React.Fragment>
141))}
142</div>
143);
144});
145
146export const ApplicationSyncOptionsField = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi}) => {
147const {
148fieldApi: {getValue, setValue, setTouched}
149} = props;
150const val = getValue() || [];
151return (
152<div className='argo-field' style={{borderBottom: '0'}}>
153<ApplicationSyncOptions
154options={val}
155onChanged={opts => {
156setTouched(true);
157setValue(opts);
158}}
159/>
160</div>
161);
162});
163