Skip to content

Commit

Permalink
chore: add new experiment form validation (chaos-mesh#819)
Browse files Browse the repository at this point in the history
Signed-off-by: Yue Yang <[email protected]>

Co-authored-by: ti-srebot <[email protected]>
  • Loading branch information
g1eny0ung and ti-srebot authored Aug 18, 2020
1 parent 0dbf446 commit 9de98e3
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 43 deletions.
2 changes: 2 additions & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"clsx": "^1.1.1",
"d3": "^5.16.0",
"dayjs": "^1.8.28",
"flat": "^5.0.2",
"formik": "^2.1.4",
"js-yaml": "^3.14.0",
"jsoneditor": "^9.0.3",
Expand Down Expand Up @@ -53,6 +54,7 @@
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@types/d3": "^5.7.2",
"@types/flat": "^5.0.1",
"@types/jest": "^25.2.1",
"@types/js-yaml": "^3.12.4",
"@types/jsoneditor": "^8.6.0",
Expand Down
38 changes: 20 additions & 18 deletions ui/src/components/NewExperiment/Stepper/Target/IO.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default function IO(props: StepperFormTargetProps) {
label="Action"
helperText="Please select an action"
onChange={handleActionChange}
onBlur={() => {}} // Delay the form validation with an empty func. If don’t do this, errors will appear early
>
{actions.map((option: string) => (
<MenuItem key={option} value={option}>
Expand All @@ -60,25 +61,26 @@ export default function IO(props: StepperFormTargetProps) {
))}
</SelectField>

{(values.target.io_chaos.action === 'delay' || values.target.io_chaos.action === 'mixed') && (
<TextField
id="target.io_chaos.delay"
name="target.io_chaos.delay"
label="Delay"
helperText="Optional. The value of delay of I/O operations. If it's empty, the operator will generate a value for it randomly."
/>
)}

{(values.target.io_chaos.action === 'errno' || values.target.io_chaos.action === 'mixed') && (
<TextField
id="target.io_chaos.errno"
name="target.io_chaos.errno"
label="Errno"
helperText="Optional. The error code returned by I/O operators. By default, it returns a random error code"
/>
)}

{values.target.io_chaos.action !== '' && (
<AdvancedOptions>
{values.target.io_chaos.action !== 'errno' && (
<TextField
id="target.io_chaos.delay"
name="target.io_chaos.delay"
label="Delay"
helperText="Optional. The value of delay of I/O operations. If it's empty, the operator will generate a value for it randomly."
/>
)}

{values.target.io_chaos.action !== 'delay' && (
<TextField
id="target.io_chaos.errno"
name="target.io_chaos.errno"
label="Errno"
helperText="Optional. The error code returned by I/O operators. By default, it returns a random error code"
/>
)}
<TextField
id="target.io_chaos.percent"
name="target.io_chaos.percent"
Expand All @@ -92,7 +94,7 @@ export default function IO(props: StepperFormTargetProps) {
id="target.io_chaos.path"
name="target.io_chaos.path"
label="Path"
helperText="The path of files for injecting"
helperText="The path of files for injecting. If it's empty, the IOChaos action will inject into all files."
/>
<AutocompleteMultipleField
id="target.io_chaos.methods"
Expand Down
42 changes: 41 additions & 1 deletion ui/src/components/NewExperiment/Stepper/Target/Network.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const actions = ['partition', 'loss', 'delay', 'duplicate', 'corrupt', 'bandwidt
const direction = ['from', 'to', 'both']

export default function Network(props: StepperFormTargetProps) {
const { values, setFieldValue, handleActionChange } = props
const { errors, touched, values, setFieldValue, handleActionChange } = props

const { namespaces } = useSelector((state: RootState) => state.experiments)

Expand Down Expand Up @@ -53,6 +53,7 @@ export default function Network(props: StepperFormTargetProps) {
label="Action"
helperText="Please select a NetworkChaos action"
onChange={handleActionChange}
onBlur={() => {}} // Delay the form validation with an empty func. If don’t do this, errors will appear early
>
{actions.map((option) => (
<MenuItem key={option} value={option}>
Expand All @@ -67,6 +68,7 @@ export default function Network(props: StepperFormTargetProps) {
name="target.network_chaos.direction"
label="Direction"
helperText="Specifies the partition direction"
error={errors.target?.network_chaos?.direction && touched.target?.network_chaos?.direction ? true : false}
>
{direction.map((option) => (
<MenuItem key={option} value={option}>
Expand All @@ -83,6 +85,11 @@ export default function Network(props: StepperFormTargetProps) {
name="target.network_chaos.bandwidth.rate"
label="Rate"
helperText="The rate allows bps, kbps, mbps, gbps, tbps unit. For example, bps means bytes per second"
error={
errors.target?.network_chaos?.bandwidth?.rate && touched.target?.network_chaos?.bandwidth?.rate
? true
: false
}
/>
<TextField
type="number"
Expand Down Expand Up @@ -125,6 +132,11 @@ export default function Network(props: StepperFormTargetProps) {
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
}}
error={
errors.target?.network_chaos?.corrupt?.corrupt && touched.target?.network_chaos?.corrupt?.corrupt
? true
: false
}
/>
<TextField
id="target.network_chaos.corrupt.correlation"
Expand All @@ -134,6 +146,11 @@ export default function Network(props: StepperFormTargetProps) {
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
}}
error={
errors.target?.network_chaos?.corrupt?.correlation && touched.target?.network_chaos?.corrupt?.correlation
? true
: false
}
/>
</>
)}
Expand All @@ -145,6 +162,12 @@ export default function Network(props: StepperFormTargetProps) {
name="target.network_chaos.delay.latency"
label="Latency"
helperText="The latency of delay"
autoFocus
error={
errors.target?.network_chaos?.delay?.latency && touched.target?.network_chaos?.delay?.latency
? true
: false
}
/>
<AdvancedOptions>
<TextField
Expand Down Expand Up @@ -176,6 +199,11 @@ export default function Network(props: StepperFormTargetProps) {
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
}}
error={
errors.target?.network_chaos?.duplicate?.duplicate && touched.target?.network_chaos?.duplicate?.duplicate
? true
: false
}
/>
<TextField
id="target.network_chaos.duplicate.correlation"
Expand All @@ -185,6 +213,12 @@ export default function Network(props: StepperFormTargetProps) {
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
}}
error={
errors.target?.network_chaos?.duplicate?.correlation &&
touched.target?.network_chaos?.duplicate?.correlation
? true
: false
}
/>
</>
)}
Expand All @@ -199,6 +233,7 @@ export default function Network(props: StepperFormTargetProps) {
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
}}
error={errors.target?.network_chaos?.loss?.loss && touched.target?.network_chaos?.loss?.loss ? true : false}
/>
<TextField
id="target.network_chaos.loss.correlation"
Expand All @@ -208,6 +243,11 @@ export default function Network(props: StepperFormTargetProps) {
InputProps={{
endAdornment: <InputAdornment position="end">%</InputAdornment>,
}}
error={
errors.target?.network_chaos?.loss?.correlation && touched.target?.network_chaos?.loss?.correlation
? true
: false
}
/>
</>
)}
Expand Down
4 changes: 3 additions & 1 deletion ui/src/components/NewExperiment/Stepper/Target/Pod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { toTitleCase } from 'lib/utils'
const actions = ['pod failure', 'pod kill', 'container kill']

export default function Pod(props: StepperFormTargetProps) {
const { values, handleActionChange } = props
const { errors, touched, values, handleActionChange } = props

return (
<>
Expand All @@ -19,6 +19,7 @@ export default function Pod(props: StepperFormTargetProps) {
helperText="Select a PodChaos action"
value={values.target.pod_chaos.action}
onChange={handleActionChange}
onBlur={() => {}} // Delay the form validation with an empty func. If don’t do this, errors will appear early
>
{actions.map((option: string) => (
<MenuItem key={option} value={option.split(' ').join('-')}>
Expand All @@ -33,6 +34,7 @@ export default function Pod(props: StepperFormTargetProps) {
name="target.pod_chaos.container_name"
label="Container Name"
helperText="Fill the container name you want to kill"
error={errors.target?.pod_chaos?.container_name && touched.target?.pod_chaos?.container_name ? true : false}
/>
)}
</>
Expand Down
3 changes: 3 additions & 0 deletions ui/src/components/NewExperiment/Stepper/Target/Time.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { StepperFormTargetProps } from 'components/NewExperiment/types'
import { resetOtherChaos } from 'lib/formikhelpers'

export default function Time(props: StepperFormTargetProps) {
const { errors, touched } = props

useEffect(() => {
resetOtherChaos(props, 'TimeChaos', false)
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand All @@ -18,6 +20,7 @@ export default function Time(props: StepperFormTargetProps) {
name="target.time_chaos.time_offset"
label="Offset"
helperText="The time offset"
error={errors.target?.time_chaos?.time_offset && touched.target?.time_chaos?.time_offset ? true : false}
/>

<AdvancedOptions>
Expand Down
96 changes: 88 additions & 8 deletions ui/src/components/NewExperiment/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Experiment } from './types'
import * as Yup from 'yup'

import { Experiment } from './types'

export const defaultExperimentSchema: Experiment = {
name: '',
namespace: 'default',
Expand All @@ -18,15 +19,15 @@ export const defaultExperimentSchema: Experiment = {
target: {
kind: 'PodChaos',
pod_chaos: {
action: '',
action: 'pod-failure',
container_name: '',
},
network_chaos: {
action: '',
direction: '',
bandwidth: {
buffer: 0,
limit: 0,
buffer: 1,
limit: 1,
minburst: 0,
peakrate: 0,
rate: '',
Expand Down Expand Up @@ -77,12 +78,12 @@ export const defaultExperimentSchema: Experiment = {
stressng_stressors: '',
stressors: {
cpu: {
workers: 0,
workers: 1,
load: 0,
options: [],
},
memory: {
workers: 0,
workers: 1,
options: [],
},
},
Expand All @@ -95,6 +96,85 @@ export const defaultExperimentSchema: Experiment = {
},
}

export const validationSchema = Yup.object().shape({
name: Yup.string().required(),
export const validationSchema = Yup.object({
name: Yup.string().required('The experiment name is required.'),
target: Yup.object().when('name', (name: string, schema: Yup.ObjectSchema) =>
name
? Yup.object({
kind: Yup.string(),
pod_chaos: Yup.object().when('kind', (kind: string, schema: Yup.ObjectSchema) =>
kind === 'PodChaos'
? Yup.object({
action: Yup.string(),
container_name: Yup.string().when('action', (action: string, schema: Yup.StringSchema) =>
action === 'container-kill' ? schema.required('The Container name is required.') : schema
),
})
: schema
),
network_chaos: Yup.object().when('kind', (kind: string, schema: Yup.ObjectSchema) =>
kind === 'NetworkChaos'
? Yup.object({
action: Yup.string().required('The NetworkChaos action is required.'),
direction: Yup.string().when('action', (action: string, schema: Yup.StringSchema) =>
action === 'partition' ? schema.required('The direction is required.') : schema
),
bandwidth: Yup.object().when('action', (action: string, schema: Yup.ObjectSchema) =>
action === 'bandwidth'
? Yup.object({
rate: Yup.string().required('The rate of bandwidth is required.'),
})
: schema
),
corrupt: Yup.object().when('action', (action: string, schema: Yup.ObjectSchema) =>
action === 'corrupt'
? Yup.object({
corrupt: Yup.string().required('The corrupt is required.'),
correlation: Yup.string().required('The correlation of corrupt is required.'),
})
: schema
),
delay: Yup.object().when('action', (action: string, schema: Yup.ObjectSchema) =>
action === 'delay'
? Yup.object({
latency: Yup.string().required('The latency of delay is required.'),
})
: schema
),
duplicate: Yup.object().when('action', (action: string, schema: Yup.ObjectSchema) =>
action === 'duplicate'
? Yup.object({
duplicate: Yup.string().required('The duplicate is required.'),
correlation: Yup.string().required('The correlation of duplicate is required.'),
})
: schema
),
loss: Yup.object().when('action', (action: string, schema: Yup.ObjectSchema) =>
action === 'loss'
? Yup.object({
loss: Yup.string().required('The loss is required.'),
correlation: Yup.string().required('The correlation of loss is required.'),
})
: schema
),
})
: schema
),
io_chaos: Yup.object().when('kind', (kind: string, schema: Yup.ObjectSchema) =>
kind === 'IoChaos'
? Yup.object({
action: Yup.string().required('The IOChaos action is required.'),
})
: schema
),
time_chaos: Yup.object().when('kind', (kind: string, schema: Yup.ObjectSchema) =>
kind === 'TimeChaos'
? Yup.object({
time_offset: Yup.string().required('The time offset is required.'),
})
: schema
),
})
: schema
),
})
Loading

0 comments on commit 9de98e3

Please sign in to comment.