From 988c2fd7a0f09a02a3017cc20b4dbdb50a8228f8 Mon Sep 17 00:00:00 2001 From: avelichk Date: Wed, 18 Mar 2020 23:49:47 +0000 Subject: [PATCH 1/3] Support Metrics Collector for HP Jobs --- pkg/ui/v1alpha3/frontend/package.json | 10 +- .../frontend/src/actions/generalActions.js | 7 + .../frontend/src/actions/hpCreateActions.js | 75 +++ .../Common/Create/Params/MetricsCollector.jsx | 633 ++++++++++++++++++ .../frontend/src/components/HP/Create/HP.jsx | 3 +- .../src/components/HP/Create/HPParameters.jsx | 81 ++- .../frontend/src/constants/constants.js | 16 + .../v1alpha3/frontend/src/reducers/general.js | 9 + .../frontend/src/reducers/hpCreate.js | 141 ++++ 9 files changed, 965 insertions(+), 10 deletions(-) create mode 100644 pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx create mode 100644 pkg/ui/v1alpha3/frontend/src/constants/constants.js diff --git a/pkg/ui/v1alpha3/frontend/package.json b/pkg/ui/v1alpha3/frontend/package.json index 96b856fca5f..e1ccd98fc46 100644 --- a/pkg/ui/v1alpha3/frontend/package.json +++ b/pkg/ui/v1alpha3/frontend/package.json @@ -9,6 +9,7 @@ "@material-ui/icons": "^3.0.2", "@material-ui/styles": "^3.0.0-alpha.10", "@svgr/webpack": "4.1.0", + "ace-builds": "^1.4.8", "antd": "^3.13.6", "axios": "^0.18.0", "babel-core": "7.0.0-bridge.0", @@ -41,7 +42,9 @@ "jest-pnp-resolver": "1.0.2", "jest-resolve": "23.6.0", "jest-watch-typeahead": "^0.2.1", + "js-yaml": "^3.13.1", "mini-css-extract-plugin": "0.5.0", + "monaco-editor": "^0.17.1", "optimize-css-assets-webpack-plugin": "5.0.1", "plotly.js": "^1.45.0", "pnp-webpack-plugin": "1.2.1", @@ -50,11 +53,12 @@ "postcss-preset-env": "6.5.0", "postcss-safe-parser": "4.0.1", "react": "^16.8.3", - "react-ace": "^6.4.0", + "react-ace": "^8.0.0", "react-app-polyfill": "^0.2.2", "react-dev-utils": "^8.0.0", "react-dom": "^16.8.3", "react-html-parser": "^2.0.2", + "react-monaco-editor": "^0.28.0", "react-plotly.js": "^2.3.0", "react-redux": "^6.0.1", "react-router": "^4.3.1", @@ -71,9 +75,7 @@ "webpack": "4.28.3", "webpack-dev-server": "3.1.14", "webpack-manifest-plugin": "2.0.4", - "workbox-webpack-plugin": "3.6.3", - "monaco-editor": "^0.17.1", - "react-monaco-editor": "^0.28.0" + "workbox-webpack-plugin": "3.6.3" }, "scripts": { "start": "node --max-old-space-size=4096 scripts/start.js", diff --git a/pkg/ui/v1alpha3/frontend/src/actions/generalActions.js b/pkg/ui/v1alpha3/frontend/src/actions/generalActions.js index 277bf980cfd..5d3422f7813 100644 --- a/pkg/ui/v1alpha3/frontend/src/actions/generalActions.js +++ b/pkg/ui/v1alpha3/frontend/src/actions/generalActions.js @@ -74,3 +74,10 @@ export const CLOSE_DIALOG_EXPERIMENT = 'CLOSE_DIALOG_EXPERIMENT'; export const closeDialogExperiment = () => ({ type: CLOSE_DIALOG_EXPERIMENT, }); + +export const VALIDATION_ERROR = 'VALIDATION_ERROR'; + +export const validationError = message => ({ + type: VALIDATION_ERROR, + message, +}); diff --git a/pkg/ui/v1alpha3/frontend/src/actions/hpCreateActions.js b/pkg/ui/v1alpha3/frontend/src/actions/hpCreateActions.js index 1799a7a22d5..49446b2b413 100644 --- a/pkg/ui/v1alpha3/frontend/src/actions/hpCreateActions.js +++ b/pkg/ui/v1alpha3/frontend/src/actions/hpCreateActions.js @@ -146,3 +146,78 @@ export const submitHPJob = data => ({ type: SUBMIT_HP_JOB_REQUEST, data, }); + +export const CHANGE_MC_KIND_HP = 'CHANGE_MC_KIND_HP'; + +export const changeMCKindHP = kind => ({ + type: CHANGE_MC_KIND_HP, + kind, +}); + +export const CHANGE_MC_FILE_SYSTEM_HP = 'CHANGE_MC_FILE_SYSTEM_HP'; + +export const changeMCFileSystemHP = (kind, path) => ({ + type: CHANGE_MC_FILE_SYSTEM_HP, + kind, + path, +}); + +export const CHANGE_MC_HTTP_GET_HP = 'CHANGE_MC_HTTP_GET_HP'; + +export const changeMCHttpGetHP = (port, path, scheme, host) => ({ + type: CHANGE_MC_HTTP_GET_HP, + port, + path, + scheme, + host, +}); + +export const ADD_MC_HTTP_GET_HEADER_HP = 'ADD_MC_HTTP_GET_HEADER_HP'; + +export const addMCHttpGetHeaderHP = () => ({ + type: ADD_MC_HTTP_GET_HEADER_HP, +}); + +export const CHANGE_MC_HTTP_GET_HEADER_HP = 'CHANGE_MC_HTTP_GET_HEADER_HP'; + +export const changeMCHttpGetHeaderHP = (fieldName, value, index) => ({ + type: CHANGE_MC_HTTP_GET_HEADER_HP, + fieldName, + value, + index, +}); + +export const DELETE_MC_HTTP_GET_HEADER_HP = 'DELETE_MC_HTTP_GET_HEADER_HP'; + +export const deleteMCHttpGetHeaderHP = index => ({ + type: DELETE_MC_HTTP_GET_HEADER_HP, + index, +}); + +export const ADD_MC_METRICS_FORMAT_HP = 'ADD_MC_METRICS_FORMAT_HP'; + +export const addMCMetricsFormatHP = () => ({ + type: ADD_MC_METRICS_FORMAT_HP, +}); + +export const CHANGE_MC_METRIC_FORMAT_HP = 'CHANGE_MC_METRIC_FORMAT_HP'; + +export const changeMCMetricsFormatHP = (format, index) => ({ + type: CHANGE_MC_METRIC_FORMAT_HP, + format, + index, +}); + +export const DELETE_MC_METRIC_FORMAT_HP = 'DELETE_MC_METRIC_FORMAT_HP'; + +export const deleteMCMetricsFormatHP = index => ({ + type: DELETE_MC_METRIC_FORMAT_HP, + index, +}); + +export const CHANGE_MC_CUSTOM_CONTAINER_HP = 'CHANGE_MC_CUSTOM_CONTAINER_HP'; + +export const changeMCCustomContainerHP = yamlContainer => ({ + type: CHANGE_MC_CUSTOM_CONTAINER_HP, + yamlContainer, +}); diff --git a/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx b/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx new file mode 100644 index 00000000000..8d62095806d --- /dev/null +++ b/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx @@ -0,0 +1,633 @@ +import React from 'react'; +import { connect } from 'react-redux'; + +import AceEditor from 'react-ace'; +import 'ace-builds/src-noconflict/theme-sqlserver'; +import 'ace-builds/src-noconflict/mode-yaml'; + +import withStyles from '@material-ui/styles/withStyles'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import Tooltip from '@material-ui/core/Tooltip'; +import HelpOutlineIcon from '@material-ui/icons/HelpOutline'; +import FormControl from '@material-ui/core/FormControl'; +import InputLabel from '@material-ui/core/InputLabel'; +import Select from '@material-ui/core/Select'; +import OutlinedInput from '@material-ui/core/OutlinedInput'; +import MenuItem from '@material-ui/core/MenuItem'; +import TextField from '@material-ui/core/TextField'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import DeleteIcon from '@material-ui/icons/Delete'; +import Divider from '@material-ui/core/Divider'; + +import * as constants from '../../../../constants/constants'; +import { + changeMCKindHP, + changeMCFileSystemHP, + addMCMetricsFormatHP, + changeMCMetricsFormatHP, + deleteMCMetricsFormatHP, + changeMCHttpGetHP, + addMCHttpGetHeaderHP, + changeMCHttpGetHeaderHP, + deleteMCHttpGetHeaderHP, + changeMCCustomContainerHP, +} from '../../../../actions/hpCreateActions'; + +const module = 'general'; +const hpModule = 'hpCreate'; +const nasModule = 'nasCreate'; + +const styles = theme => ({ + help: { + padding: 4 / 2, + verticalAlign: 'middle', + marginRight: 5, + }, + formSelect: { + width: '70%', + }, + textField: { + width: '95%', + }, + grid: { + marginTop: 20, + marginBottom: 30, + }, + textsList: { + marginBottom: 15, + }, + headerButton: { + marginTop: 10, + }, +}); + +class MetricsCollectorSpec extends React.Component { + onMCKindChange = event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCKindHP(event.target.value) + : this.props.changeMCKindHP(event.target.value); + }; + + onMCFileSystemKindChange = event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCFileSystemHP( + event.target.value, + this.props.mcSpecHP.source.fileSystemPath.path, + ) + : this.props.changeMCFileSystemHP( + event.target.value, + this.props.mcSpecHP.source.fileSystemPath.path, + ); + }; + + onMCFileSystemPathChange = event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCFileSystemHP( + this.props.mcSpecHP.source.fileSystemPath.kind, + event.target.value, + ) + : this.props.changeMCFileSystemHP( + this.props.mcSpecHP.source.fileSystemPath.kind, + event.target.value, + ); + }; + + onMCMetricsFormatAdd = () => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.addMCMetricsFormatHP() + : this.props.addMCMetricsFormatHP(); + }; + + onMCMetricsFormatChange = index => event => { + this.props.jobType == constants.JOB_TYPE_HP + ? this.props.changeMCMetricsFormatHP(event.target.value, index) + : this.props.changeMCMetricsFormatHP(event.target.value, index); + }; + + onMCMetricsFormatDelete = index => event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.deleteMCMetricsFormatHP(index) + : this.props.deleteMCMetricsFormatHP(index); + }; + + onMCHttpGetPortChange = event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCHttpGetHP( + event.target.value, + this.props.mcSpecHP.source.httpGet.path, + this.props.mcSpecHP.source.httpGet.scheme, + this.props.mcSpecHP.source.httpGet.host, + ) + : this.props.changeMCHttpGetHP( + event.target.value, + this.props.mcSpecHP.source.httpGet.path, + this.props.mcSpecHP.source.httpGet.scheme, + this.props.mcSpecHP.source.httpGet.host, + ); + }; + + onMCHttpGetPathChange = event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCHttpGetHP( + this.props.mcSpecHP.source.httpGet.port, + event.target.value, + this.props.mcSpecHP.source.httpGet.scheme, + this.props.mcSpecHP.source.httpGet.host, + ) + : this.props.changeMCHttpGetHP( + this.props.mcSpecHP.source.httpGet.port, + event.target.value, + this.props.mcSpecHP.source.httpGet.scheme, + this.props.mcSpecHP.source.httpGet.host, + ); + }; + + onMCHttpGetSchemeChange = event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCHttpGetHP( + this.props.mcSpecHP.source.httpGet.port, + this.props.mcSpecHP.source.httpGet.path, + event.target.value, + this.props.mcSpecHP.source.httpGet.host, + ) + : this.props.changeMCHttpGetHP( + this.props.mcSpecHP.source.httpGet.port, + this.props.mcSpecHP.source.httpGet.path, + event.target.value, + this.props.mcSpecHP.source.httpGet.host, + ); + }; + + onMCHttpGetHostChange = event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCHttpGetHP( + this.props.mcSpecHP.source.httpGet.port, + this.props.mcSpecHP.source.httpGet.path, + this.props.mcSpecHP.source.httpGet.scheme, + event.target.value, + ) + : this.props.changeMCHttpGetHP( + this.props.mcSpecHP.source.httpGet.port, + this.props.mcSpecHP.source.httpGet.path, + this.props.mcSpecHP.source.httpGet.scheme, + event.target.value, + ); + }; + + onMCHttpGetHeaderAdd = () => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.addMCHttpGetHeaderHP() + : this.props.addMCHttpGetHeaderHP(); + }; + + onMCHttpGetHeaderChange = (fieldName, index) => event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCHttpGetHeaderHP(fieldName, event.target.value, index) + : this.props.changeMCHttpGetHeaderHP(fieldName, event.target.value, index); + }; + + onMCHttpGetHeaderDelete = index => event => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.deleteMCHttpGetHeaderHP(index) + : this.props.deleteMCHttpGetHeaderHP(index); + }; + + onMCCustomContainerChange = yamlContainer => { + this.props.jobType === constants.JOB_TYPE_HP + ? this.props.changeMCCustomContainerHP(yamlContainer) + : this.props.changeMCCustomContainerHP(yamlContainer); + }; + render() { + const { classes } = this.props; + return ( +
+ + + + + + + {'Kind'} + + + + + Kind + + + + + {((this.props.jobType === constants.JOB_TYPE_HP && + (this.props.mcSpecHP.collector.kind === constants.MC_KIND_FILE || + this.props.mcSpecHP.collector.kind === constants.MC_KIND_TENSORFLOW_EVENT || + this.props.mcSpecHP.collector.kind === constants.MC_KIND_CUSTOM)) || + (this.props.jobType === constants.JOB_TYPE_NAS && + (this.props.mcSpecNAS.collector.kind === constants.MC_KIND_FILE || + this.props.mcSpecNAS.collector.kind === constants.MC_KIND_TENSORFLOW_EVENT || + this.props.mcSpecNAS.collector.kind === constants.MC_KIND_CUSTOM))) && ( + + + + + + + {'File System Kind and Path'} + + + + + File System Kind + + + + {((this.props.jobType === constants.JOB_TYPE_HP && + this.props.mcSpecHP.source.fileSystemPath.kind != constants.MC_FILE_SYSTEM_NO_KIND) || + (this.props.jobType === constants.JOB_TYPE_NAS && + this.props.mcSpecHP.source.fileSystemPath.kind != + constants.MC_FILE_SYSTEM_NO_KIND)) && ( + + + + )} + + )} + {((this.props.jobType === constants.JOB_TYPE_HP && + this.props.mcSpecHP.collector.kind === constants.MC_KIND_PROMETHEUS) || + (this.props.jobType === constants.JOB_TYPE_NAS && + this.props.mcSpecNAS.collector.kind === constants.MC_KIND_PROMETHEUS)) && ( +
+ + + + + + + {'HttpGet Port and Path'} + + + + + + + + + + + + + + + + {'HttpGet Scheme and Host (optional)'} + + + + + Scheme + + + + + + + + + + + + + + {'HttpGet Headers (optional)'} + + + + + {this.props.jobType === constants.JOB_TYPE_HP + ? this.props.mcSpecHP.source.httpGet.httpHeaders.map((header, index) => { + return ( +
+ + + + + + + + + + + + + + +
+ ); + }) + : this.props.mcSpecNAS.source.httpGet.httpHeaders.map((header, index) => { + return ( +
+ + + + + + + + + + + + + + +
+ ); + })} +
+ )} + {((this.props.jobType === constants.JOB_TYPE_HP && + this.props.mcSpecHP.collector.kind === constants.MC_KIND_CUSTOM) || + (this.props.jobType === constants.JOB_TYPE_NAS && + this.props.mcSpecNAS.collector.kind === constants.MC_KIND_CUSTOM)) && ( + + + + + + + {'Yaml for the Custom Container'} + + + + + + + )} + + + + + + + + {this.props.jobType === constants.JOB_TYPE_HP && + this.props.mcSpecHP.source != undefined && + this.props.mcSpecHP.source.filter != undefined && + this.props.mcSpecHP.source.filter.metricsFormat.map((format, index) => { + return ( +
+ + + + + + + + + + + +
+ ); + })} + {this.props.jobType === constants.JOB_TYPE_NAS && + this.props.mcSpecNAS.source != undefined && + this.props.mcSpecNAS.source.filter != undefined && + this.props.mcSpecNAS.source.filter.metricsFormat.map((format, index) => { + return ( +
+ + + + + + + + + + + +
+ ); + })} +
+ ); + } +} + +const mapStateToProps = state => { + return { + mcSpecHP: state[hpModule].mcSpec, + mcSpecNAS: state[nasModule].mcSpec, + mcCustomContainerYamlHP: state[hpModule].mcCustomContainerYaml, + mcCustomContainerYamlNAS: state[nasModule].mcCustomContainerYaml, + mcKindsList: state[module].mcKindsList, + mcFileSystemKindsList: state[module].mcFileSystemKindsList, + mcURISchemesList: state[module].mcURISchemesList, + }; +}; + +export default connect(mapStateToProps, { + changeMCKindHP, + changeMCFileSystemHP, + addMCMetricsFormatHP, + changeMCMetricsFormatHP, + deleteMCMetricsFormatHP, + changeMCHttpGetHP, + addMCHttpGetHeaderHP, + changeMCHttpGetHeaderHP, + deleteMCHttpGetHeaderHP, + changeMCCustomContainerHP, +})(withStyles(styles)(MetricsCollectorSpec)); diff --git a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HP.jsx b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HP.jsx index da0c716c5fb..b00fad5ae13 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HP.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HP.jsx @@ -20,7 +20,8 @@ const HP = props => { return (
- + + {/* TODO: CHANGE IT! */} diff --git a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx index dc25584f28a..db7de1b8e11 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx @@ -1,5 +1,8 @@ import React from 'react'; -import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; + +import jsyaml from 'js-yaml'; + import withStyles from '@material-ui/styles/withStyles'; import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; @@ -10,11 +13,12 @@ import CommonParametersSpec from './Params/CommonSpec'; import Objective from './Params/Objective'; import TrialSpecParam from './Params/Trial'; import Parameters from './Params/Parameters'; +import Algorithm from './Params/Algorithm'; +import MetricsCollectorSpec from '../../Common/Create/Params/MetricsCollector'; import { submitHPJob } from '../../../actions/hpCreateActions'; - -import { connect } from 'react-redux'; -import Algorithm from './Params/Algorithm'; +import { validationError } from '../../../actions/generalActions'; +import * as constants from '../../../constants/constants'; const module = 'hpCreate'; @@ -122,6 +126,67 @@ const HPParameters = props => { data.spec.parameters = []; addParameter(props.parameters, data.spec.parameters); + // Metrics Collector + let newMCSpec = JSON.parse(JSON.stringify(props.mcSpec)); + + // Delete empty metrics format + if (newMCSpec.source.filter.metricsFormat.length === 0) { + delete newMCSpec.source.filter; + } + + if (newMCSpec.collector.kind === constants.MC_KIND_STDOUT) { + // Delete fileSystemPath and httpGet + delete newMCSpec.source.fileSystemPath; + delete newMCSpec.source.httpGet; + } + + if ( + newMCSpec.collector.kind === constants.MC_KIND_FILE || + newMCSpec.collector.kind === constants.MC_KIND_TENSORFLOW_EVENT || + newMCSpec.collector.kind === constants.MC_KIND_CUSTOM + ) { + // Delete httpGet + delete newMCSpec.source.httpGet; + // Delete empty fileSystemPath + if (newMCSpec.source.fileSystemPath.kind === constants.MC_FILE_SYSTEM_NO_KIND) { + delete newMCSpec.source.fileSystemPath; + } + } + + if (newMCSpec.collector.kind === constants.MC_KIND_PROMETHEUS) { + // Delete file System Path + delete newMCSpec.source.fileSystemPath; + // Delete empty host + if (newMCSpec.source.httpGet.host === '') { + delete newMCSpec.source.httpGet.host; + } + // Delete empty headers + if (newMCSpec.source.httpGet.httpHeaders.length === 0) { + delete newMCSpec.source.httpGet.httpHeaders; + } + } + + // Delete empty source + if (newMCSpec.source != undefined && Object.keys(newMCSpec.source).length === 0) { + delete newMCSpec.source; + } + + // Add Custom Container YAML to the Metrics Collector + if ( + newMCSpec.collector.kind === constants.MC_KIND_CUSTOM && + props.mcCustomContainerYaml != '' + ) { + try { + let mcCustomContainerJson = jsyaml.load(props.mcCustomContainerYaml); + newMCSpec.collector.customCollector = mcCustomContainerJson; + } catch { + props.validationError('Metrics Collector Custom Container is not valid YAML!'); + return; + } + } + + data.spec.metricsCollectorSpec = newMCSpec; + //TODO: Add support not only for default ConfigMap for Trial-Templates data.spec.trialTemplate = { goTemplate: { @@ -153,6 +218,8 @@ const HPParameters = props => { {SectionInTypography('Parameters', classes)} + {SectionInTypography('Metrics Collector Spec', classes)} + {SectionInTypography('Trial Spec', classes)} @@ -181,6 +248,8 @@ const mapStateToProps = state => ({ parameters: state[module].parameters, trial: state[module].trial, trialNamespace: state[module].trialNamespace, + mcSpec: state[module].mcSpec, + mcCustomContainerYaml: state[module].mcCustomContainerYaml, }); //TODO: Added validation and remove it @@ -191,4 +260,6 @@ const mapStateToProps = state => ({ // metricsName: PropTypes.arrayOf(PropTypes.string), // } -export default connect(mapStateToProps, { submitHPJob })(withStyles(styles)(HPParameters)); +export default connect(mapStateToProps, { submitHPJob, validationError })( + withStyles(styles)(HPParameters), +); diff --git a/pkg/ui/v1alpha3/frontend/src/constants/constants.js b/pkg/ui/v1alpha3/frontend/src/constants/constants.js new file mode 100644 index 00000000000..9365f080a6b --- /dev/null +++ b/pkg/ui/v1alpha3/frontend/src/constants/constants.js @@ -0,0 +1,16 @@ +// Here we store global constants for React frontend + +export const JOB_TYPE_HP = 'hp'; +export const JOB_TYPE_NAS = 'nas'; + +export const MC_KIND_STDOUT = 'StdOut'; +export const MC_KIND_FILE = 'File'; +export const MC_KIND_TENSORFLOW_EVENT = 'TensorFlowEvent'; +export const MC_KIND_PROMETHEUS = 'PrometheusMetric'; +export const MC_KIND_CUSTOM = 'Custom'; + +export const MC_FILE_SYSTEM_KIND_FILE = 'File'; +export const MC_FILE_SYSTEM_KIND_DIRECTORY = 'Directory'; +export const MC_FILE_SYSTEM_NO_KIND = 'No File System'; + +export const MC_HTTP_GET_HTTP_SCHEME = 'HTTP'; diff --git a/pkg/ui/v1alpha3/frontend/src/reducers/general.js b/pkg/ui/v1alpha3/frontend/src/reducers/general.js index f4a561c4308..3da3788b12a 100644 --- a/pkg/ui/v1alpha3/frontend/src/reducers/general.js +++ b/pkg/ui/v1alpha3/frontend/src/reducers/general.js @@ -14,6 +14,9 @@ const initialState = { globalNamespace: '', experiment: {}, dialogExperimentOpen: false, + mcKindsList: ['StdOut', 'File', 'TensorFlowEvent', 'PrometheusMetric', 'Custom', 'None'], + mcFileSystemKindsList: ['No File System', 'File', 'Directory'], + mcURISchemesList: ['HTTP', 'HTTPS'], }; const generalReducer = (state = initialState, action) => { @@ -137,6 +140,12 @@ const generalReducer = (state = initialState, action) => { ...state, dialogExperimentOpen: false, }; + case actions.VALIDATION_ERROR: + return { + ...state, + snackOpen: true, + snackText: action.message, + }; default: return state; } diff --git a/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js b/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js index 195d1e8ed69..e9f37acf58b 100644 --- a/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js +++ b/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js @@ -1,4 +1,5 @@ import * as actions from '../actions/hpCreateActions'; +import * as constants from '../constants/constants'; const initialState = { loading: false, @@ -97,6 +98,17 @@ const initialState = { trial: 'defaultTrialTemplate.yaml', currentYaml: '', trialNamespace: 'kubeflow', + mcSpec: { + collector: { + kind: 'StdOut', + }, + source: { + filter: { + metricsFormat: [], + }, + }, + }, + mcCustomContainerYaml: '', }; const filterValue = (obj, key) => { @@ -245,6 +257,135 @@ const hpCreateReducer = (state = initialState, action) => { ...state, trialNamespace: action.namespace, }; + // Collector Kind change + case actions.CHANGE_MC_KIND_HP: + let newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + newMCSpec.collector.kind = action.kind; + + if ( + action.kind === constants.MC_KIND_FILE || + action.kind === constants.MC_KIND_TENSORFLOW_EVENT || + action.kind === constants.MC_KIND_CUSTOM + ) { + let newKind; + switch (action.kind) { + case constants.MC_KIND_FILE: + newKind = constants.MC_FILE_SYSTEM_KIND_FILE; + break; + + case constants.MC_KIND_TENSORFLOW_EVENT: + newKind = constants.MC_FILE_SYSTEM_KIND_DIRECTORY; + break; + + default: + newKind = constants.MC_FILE_SYSTEM_NO_KIND; + } + // File or TF Event collector Kind + newMCSpec.source.fileSystemPath = { + kind: newKind, + path: '', + }; + } else if (action.kind === constants.MC_KIND_PROMETHEUS) { + // Prometheus collector Kind + newMCSpec.source.httpGet = { + port: '', + path: '', + scheme: constants.MC_HTTP_GET_HTTP_SCHEME, + host: '', + httpHeaders: [], + }; + } + + return { + ...state, + mcSpec: newMCSpec, + mcCustomContainerYaml: '', + }; + // File System Path change + case actions.CHANGE_MC_FILE_SYSTEM_HP: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + newMCSpec.source.fileSystemPath.kind = action.kind; + newMCSpec.source.fileSystemPath.path = action.path; + return { + ...state, + mcSpec: newMCSpec, + }; + // HTTPGet settings + case actions.CHANGE_MC_HTTP_GET_HP: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + + newMCSpec.source.httpGet.port = action.port; + newMCSpec.source.httpGet.path = action.path; + newMCSpec.source.httpGet.scheme = action.scheme; + newMCSpec.source.httpGet.host = action.host; + + return { + ...state, + mcSpec: newMCSpec, + }; + // HTTPGet Headers + case actions.ADD_MC_HTTP_GET_HEADER_HP: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + let currentHeaders = newMCSpec.source.httpGet.httpHeaders.slice(); + let newHeader = { name: '', value: '' }; + currentHeaders.push(newHeader); + newMCSpec.source.httpGet.httpHeaders = currentHeaders; + return { + ...state, + mcSpec: newMCSpec, + }; + case actions.CHANGE_MC_HTTP_GET_HEADER_HP: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + currentHeaders = newMCSpec.source.httpGet.httpHeaders.slice(); + currentHeaders[action.index][action.fieldName] = action.value; + newMCSpec.source.httpGet.httpHeaders = currentHeaders; + return { + ...state, + mcSpec: newMCSpec, + }; + case actions.DELETE_MC_HTTP_GET_HEADER_HP: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + currentHeaders = newMCSpec.source.httpGet.httpHeaders.slice(); + currentHeaders.splice(action.index, 1); + newMCSpec.source.httpGet.httpHeaders = currentHeaders; + return { + ...state, + mcSpec: newMCSpec, + }; + // Custom container + case actions.CHANGE_MC_CUSTOM_CONTAINER_HP: + return { + ...state, + mcCustomContainerYaml: action.yamlContainer, + }; + // Metrics Format + case actions.ADD_MC_METRICS_FORMAT_HP: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + let currentFormats = newMCSpec.source.filter.metricsFormat.slice(); + currentFormats.push(''); + newMCSpec.source.filter.metricsFormat = currentFormats; + return { + ...state, + mcSpec: newMCSpec, + }; + case actions.CHANGE_MC_METRIC_FORMAT_HP: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + currentFormats = newMCSpec.source.filter.metricsFormat.slice(); + currentFormats[action.index] = action.format; + newMCSpec.source.filter.metricsFormat = currentFormats; + return { + ...state, + mcSpec: newMCSpec, + }; + case actions.DELETE_MC_METRIC_FORMAT_HP: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + currentFormats = newMCSpec.source.filter.metricsFormat.slice(); + currentFormats.splice(action.index, 1); + newMCSpec.source.filter.metricsFormat = currentFormats; + return { + ...state, + mcSpec: newMCSpec, + }; default: return state; } From 14cbcdedfe9f02dcd1408f315d0c33c07cb1899e Mon Sep 17 00:00:00 2001 From: avelichk Date: Thu, 19 Mar 2020 01:46:06 +0000 Subject: [PATCH 2/3] Support metrics collector in NAS --- .../frontend/src/actions/nasCreateActions.js | 77 +++++++++- .../Common/Create/Params/MetricsCollector.jsx | 83 +++++++---- .../frontend/src/components/HP/Create/HP.jsx | 3 +- .../src/components/HP/Create/HPParameters.jsx | 10 +- .../components/NAS/Create/NASParameters.jsx | 86 ++++++++++- .../frontend/src/constants/constants.js | 4 + .../frontend/src/reducers/hpCreate.js | 16 +- .../frontend/src/reducers/nasCreate.js | 141 ++++++++++++++++++ 8 files changed, 372 insertions(+), 48 deletions(-) diff --git a/pkg/ui/v1alpha3/frontend/src/actions/nasCreateActions.js b/pkg/ui/v1alpha3/frontend/src/actions/nasCreateActions.js index a3947945bc4..08dc6d9e45e 100644 --- a/pkg/ui/v1alpha3/frontend/src/actions/nasCreateActions.js +++ b/pkg/ui/v1alpha3/frontend/src/actions/nasCreateActions.js @@ -190,7 +190,7 @@ export const changeTrial = trial => ({ trial, }); -export const CHANGE_TRIAL_NAMESPACE_NAS = 'CHANGE_TRIAL_NAMESPACE_HP'; +export const CHANGE_TRIAL_NAMESPACE_NAS = 'CHANGE_TRIAL_NAMESPACE_NAS'; export const changeTrialNamespace = namespace => ({ type: CHANGE_TRIAL_NAMESPACE_NAS, @@ -211,3 +211,78 @@ export const CLOSE_SNACKBAR = 'CLOSE_SNACKBAR'; export const closeSnackbar = () => ({ type: CLOSE_SNACKBAR, }); + +export const CHANGE_MC_KIND_NAS = 'CHANGE_MC_KIND_NAS'; + +export const changeMCKindNAS = kind => ({ + type: CHANGE_MC_KIND_NAS, + kind, +}); + +export const CHANGE_MC_FILE_SYSTEM_NAS = 'CHANGE_MC_FILE_SYSTEM_NAS'; + +export const changeMCFileSystemNAS = (kind, path) => ({ + type: CHANGE_MC_FILE_SYSTEM_NAS, + kind, + path, +}); + +export const CHANGE_MC_HTTP_GET_NAS = 'CHANGE_MC_HTTP_GET_NAS'; + +export const changeMCHttpGetNAS = (port, path, scheme, host) => ({ + type: CHANGE_MC_HTTP_GET_NAS, + port, + path, + scheme, + host, +}); + +export const ADD_MC_HTTP_GET_HEADER_NAS = 'ADD_MC_HTTP_GET_HEADER_NAS'; + +export const addMCHttpGetHeaderNAS = () => ({ + type: ADD_MC_HTTP_GET_HEADER_NAS, +}); + +export const CHANGE_MC_HTTP_GET_HEADER_NAS = 'CHANGE_MC_HTTP_GET_HEADER_NAS'; + +export const changeMCHttpGetHeaderNAS = (fieldName, value, index) => ({ + type: CHANGE_MC_HTTP_GET_HEADER_NAS, + fieldName, + value, + index, +}); + +export const DELETE_MC_HTTP_GET_HEADER_NAS = 'DELETE_MC_HTTP_GET_HEADER_NAS'; + +export const deleteMCHttpGetHeaderNAS = index => ({ + type: DELETE_MC_HTTP_GET_HEADER_NAS, + index, +}); + +export const ADD_MC_METRICS_FORMAT_NAS = 'ADD_MC_METRICS_FORMAT_NAS'; + +export const addMCMetricsFormatNAS = () => ({ + type: ADD_MC_METRICS_FORMAT_NAS, +}); + +export const CHANGE_MC_METRIC_FORMAT_NAS = 'CHANGE_MC_METRIC_FORMAT_NAS'; + +export const changeMCMetricsFormatNAS = (format, index) => ({ + type: CHANGE_MC_METRIC_FORMAT_NAS, + format, + index, +}); + +export const DELETE_MC_METRIC_FORMAT_NAS = 'DELETE_MC_METRIC_FORMAT_NAS'; + +export const deleteMCMetricsFormatNAS = index => ({ + type: DELETE_MC_METRIC_FORMAT_NAS, + index, +}); + +export const CHANGE_MC_CUSTOM_CONTAINER_NAS = 'CHANGE_MC_CUSTOM_CONTAINER_NAS'; + +export const changeMCCustomContainerNAS = yamlContainer => ({ + type: CHANGE_MC_CUSTOM_CONTAINER_NAS, + yamlContainer, +}); diff --git a/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx b/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx index 8d62095806d..ecda5864f0c 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx @@ -35,6 +35,19 @@ import { changeMCCustomContainerHP, } from '../../../../actions/hpCreateActions'; +import { + changeMCKindNAS, + changeMCFileSystemNAS, + addMCMetricsFormatNAS, + changeMCMetricsFormatNAS, + deleteMCMetricsFormatNAS, + changeMCHttpGetNAS, + addMCHttpGetHeaderNAS, + changeMCHttpGetHeaderNAS, + deleteMCHttpGetHeaderNAS, + changeMCCustomContainerNAS, +} from '../../../../actions/nasCreateActions'; + const module = 'general'; const hpModule = 'hpCreate'; const nasModule = 'nasCreate'; @@ -67,7 +80,7 @@ class MetricsCollectorSpec extends React.Component { onMCKindChange = event => { this.props.jobType === constants.JOB_TYPE_HP ? this.props.changeMCKindHP(event.target.value) - : this.props.changeMCKindHP(event.target.value); + : this.props.changeMCKindNAS(event.target.value); }; onMCFileSystemKindChange = event => { @@ -76,9 +89,9 @@ class MetricsCollectorSpec extends React.Component { event.target.value, this.props.mcSpecHP.source.fileSystemPath.path, ) - : this.props.changeMCFileSystemHP( + : this.props.changeMCFileSystemNAS( event.target.value, - this.props.mcSpecHP.source.fileSystemPath.path, + this.props.mcSpecNAS.source.fileSystemPath.path, ); }; @@ -88,8 +101,8 @@ class MetricsCollectorSpec extends React.Component { this.props.mcSpecHP.source.fileSystemPath.kind, event.target.value, ) - : this.props.changeMCFileSystemHP( - this.props.mcSpecHP.source.fileSystemPath.kind, + : this.props.changeMCFileSystemNAS( + this.props.mcSpecNAS.source.fileSystemPath.kind, event.target.value, ); }; @@ -97,19 +110,19 @@ class MetricsCollectorSpec extends React.Component { onMCMetricsFormatAdd = () => { this.props.jobType === constants.JOB_TYPE_HP ? this.props.addMCMetricsFormatHP() - : this.props.addMCMetricsFormatHP(); + : this.props.addMCMetricsFormatNAS(); }; onMCMetricsFormatChange = index => event => { this.props.jobType == constants.JOB_TYPE_HP ? this.props.changeMCMetricsFormatHP(event.target.value, index) - : this.props.changeMCMetricsFormatHP(event.target.value, index); + : this.props.changeMCMetricsFormatNAS(event.target.value, index); }; onMCMetricsFormatDelete = index => event => { this.props.jobType === constants.JOB_TYPE_HP ? this.props.deleteMCMetricsFormatHP(index) - : this.props.deleteMCMetricsFormatHP(index); + : this.props.deleteMCMetricsFormatNAS(index); }; onMCHttpGetPortChange = event => { @@ -120,11 +133,11 @@ class MetricsCollectorSpec extends React.Component { this.props.mcSpecHP.source.httpGet.scheme, this.props.mcSpecHP.source.httpGet.host, ) - : this.props.changeMCHttpGetHP( + : this.props.changeMCHttpGetNAS( event.target.value, - this.props.mcSpecHP.source.httpGet.path, - this.props.mcSpecHP.source.httpGet.scheme, - this.props.mcSpecHP.source.httpGet.host, + this.props.mcSpecNAS.source.httpGet.path, + this.props.mcSpecNAS.source.httpGet.scheme, + this.props.mcSpecNAS.source.httpGet.host, ); }; @@ -136,11 +149,11 @@ class MetricsCollectorSpec extends React.Component { this.props.mcSpecHP.source.httpGet.scheme, this.props.mcSpecHP.source.httpGet.host, ) - : this.props.changeMCHttpGetHP( - this.props.mcSpecHP.source.httpGet.port, + : this.props.changeMCHttpGetNAS( + this.props.mcSpecNAS.source.httpGet.port, event.target.value, - this.props.mcSpecHP.source.httpGet.scheme, - this.props.mcSpecHP.source.httpGet.host, + this.props.mcSpecNAS.source.httpGet.scheme, + this.props.mcSpecNAS.source.httpGet.host, ); }; @@ -152,11 +165,11 @@ class MetricsCollectorSpec extends React.Component { event.target.value, this.props.mcSpecHP.source.httpGet.host, ) - : this.props.changeMCHttpGetHP( - this.props.mcSpecHP.source.httpGet.port, - this.props.mcSpecHP.source.httpGet.path, + : this.props.changeMCHttpGetNAS( + this.props.mcSpecNAS.source.httpGet.port, + this.props.mcSpecNAS.source.httpGet.path, event.target.value, - this.props.mcSpecHP.source.httpGet.host, + this.props.mcSpecNAS.source.httpGet.host, ); }; @@ -168,10 +181,10 @@ class MetricsCollectorSpec extends React.Component { this.props.mcSpecHP.source.httpGet.scheme, event.target.value, ) - : this.props.changeMCHttpGetHP( - this.props.mcSpecHP.source.httpGet.port, - this.props.mcSpecHP.source.httpGet.path, - this.props.mcSpecHP.source.httpGet.scheme, + : this.props.changeMCHttpGetNAS( + this.props.mcSpecNAS.source.httpGet.port, + this.props.mcSpecNAS.source.httpGet.path, + this.props.mcSpecNAS.source.httpGet.scheme, event.target.value, ); }; @@ -179,25 +192,25 @@ class MetricsCollectorSpec extends React.Component { onMCHttpGetHeaderAdd = () => { this.props.jobType === constants.JOB_TYPE_HP ? this.props.addMCHttpGetHeaderHP() - : this.props.addMCHttpGetHeaderHP(); + : this.props.addMCHttpGetHeaderNAS(); }; onMCHttpGetHeaderChange = (fieldName, index) => event => { this.props.jobType === constants.JOB_TYPE_HP ? this.props.changeMCHttpGetHeaderHP(fieldName, event.target.value, index) - : this.props.changeMCHttpGetHeaderHP(fieldName, event.target.value, index); + : this.props.changeMCHttpGetHeaderNAS(fieldName, event.target.value, index); }; onMCHttpGetHeaderDelete = index => event => { this.props.jobType === constants.JOB_TYPE_HP ? this.props.deleteMCHttpGetHeaderHP(index) - : this.props.deleteMCHttpGetHeaderHP(index); + : this.props.deleteMCHttpGetHeaderNAS(index); }; onMCCustomContainerChange = yamlContainer => { this.props.jobType === constants.JOB_TYPE_HP ? this.props.changeMCCustomContainerHP(yamlContainer) - : this.props.changeMCCustomContainerHP(yamlContainer); + : this.props.changeMCCustomContainerNAS(yamlContainer); }; render() { const { classes } = this.props; @@ -303,7 +316,7 @@ class MetricsCollectorSpec extends React.Component { {((this.props.jobType === constants.JOB_TYPE_HP && this.props.mcSpecHP.source.fileSystemPath.kind != constants.MC_FILE_SYSTEM_NO_KIND) || (this.props.jobType === constants.JOB_TYPE_NAS && - this.props.mcSpecHP.source.fileSystemPath.kind != + this.props.mcSpecNAS.source.fileSystemPath.kind != constants.MC_FILE_SYSTEM_NO_KIND)) && ( { return (
- - {/* TODO: CHANGE IT! */} + diff --git a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx index db7de1b8e11..882d14dc5f7 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/HP/Create/HPParameters.jsx @@ -130,11 +130,17 @@ const HPParameters = props => { let newMCSpec = JSON.parse(JSON.stringify(props.mcSpec)); // Delete empty metrics format - if (newMCSpec.source.filter.metricsFormat.length === 0) { + if ( + newMCSpec.source.filter.metricsFormat.length === 0 || + newMCSpec.collector.kind === constants.MC_KIND_NONE + ) { delete newMCSpec.source.filter; } - if (newMCSpec.collector.kind === constants.MC_KIND_STDOUT) { + if ( + newMCSpec.collector.kind === constants.MC_KIND_STDOUT || + newMCSpec.collector.kind === constants.MC_KIND_NONE + ) { // Delete fileSystemPath and httpGet delete newMCSpec.source.fileSystemPath; delete newMCSpec.source.httpGet; diff --git a/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/NASParameters.jsx b/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/NASParameters.jsx index 1573b23ce0a..9ac0c6ffe86 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/NASParameters.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/NAS/Create/NASParameters.jsx @@ -1,6 +1,8 @@ import React from 'react'; +import { connect } from 'react-redux'; + +import jsyaml from 'js-yaml'; -import PropTypes from 'prop-types'; import withStyles from '@material-ui/styles/withStyles'; import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid'; @@ -11,11 +13,12 @@ import CommonParametersSpec from './Params/CommonSpec'; import Objective from './Params/Objective'; import Algorithm from './Params/Algorithm'; import TrialSpecParam from './Params/Trial'; +import NASConfig from './Params/NASConfig'; +import MetricsCollectorSpec from '../../Common/Create/Params/MetricsCollector'; import { submitNASJob } from '../../../actions/nasCreateActions'; - -import { connect } from 'react-redux'; -import NASConfig from './Params/NASConfig'; +import { validationError } from '../../../actions/generalActions'; +import * as constants from '../../../constants/constants'; const module = 'nasCreate'; @@ -137,6 +140,73 @@ const NASParameters = props => { data.spec.nasConfig.operations = []; addOperations(props.operations, data.spec.nasConfig.operations); + // Metrics Collector + let newMCSpec = JSON.parse(JSON.stringify(props.mcSpec)); + + // Delete empty metrics format + if ( + newMCSpec.source.filter.metricsFormat.length === 0 || + newMCSpec.collector.kind === constants.MC_KIND_NONE + ) { + delete newMCSpec.source.filter; + } + + if ( + newMCSpec.collector.kind === constants.MC_KIND_STDOUT || + newMCSpec.collector.kind === constants.MC_KIND_NONE + ) { + // Delete fileSystemPath and httpGet + delete newMCSpec.source.fileSystemPath; + delete newMCSpec.source.httpGet; + } + + if ( + newMCSpec.collector.kind === constants.MC_KIND_FILE || + newMCSpec.collector.kind === constants.MC_KIND_TENSORFLOW_EVENT || + newMCSpec.collector.kind === constants.MC_KIND_CUSTOM + ) { + // Delete httpGet + delete newMCSpec.source.httpGet; + // Delete empty fileSystemPath + if (newMCSpec.source.fileSystemPath.kind === constants.MC_FILE_SYSTEM_NO_KIND) { + delete newMCSpec.source.fileSystemPath; + } + } + + if (newMCSpec.collector.kind === constants.MC_KIND_PROMETHEUS) { + // Delete file System Path + delete newMCSpec.source.fileSystemPath; + // Delete empty host + if (newMCSpec.source.httpGet.host === '') { + delete newMCSpec.source.httpGet.host; + } + // Delete empty headers + if (newMCSpec.source.httpGet.httpHeaders.length === 0) { + delete newMCSpec.source.httpGet.httpHeaders; + } + } + + // Delete empty source + if (newMCSpec.source != undefined && Object.keys(newMCSpec.source).length === 0) { + delete newMCSpec.source; + } + + // Add Custom Container YAML to the Metrics Collector + if ( + newMCSpec.collector.kind === constants.MC_KIND_CUSTOM && + props.mcCustomContainerYaml != '' + ) { + try { + let mcCustomContainerJson = jsyaml.load(props.mcCustomContainerYaml); + newMCSpec.collector.customCollector = mcCustomContainerJson; + } catch { + props.validationError('Metrics Collector Custom Container is not valid YAML!'); + return; + } + } + + data.spec.metricsCollectorSpec = newMCSpec; + //TODO: Add support not only for default ConfigMap for Trial-Templates data.spec.trialTemplate = { goTemplate: { @@ -166,6 +236,8 @@ const NASParameters = props => { {SectionInTypography('NAS Config', classes)} + {SectionInTypography('Metrics Collector Spec', classes)} + {SectionInTypography('Trial Spec', classes)}
@@ -196,6 +268,8 @@ const mapStateToProps = state => ({ operations: state[module].operations, trial: state[module].trial, trialNamespace: state[module].trialNamespace, + mcSpec: state[module].mcSpec, + mcCustomContainerYaml: state[module].mcCustomContainerYaml, }); //TODO: Added validation and remove it @@ -207,4 +281,6 @@ const mapStateToProps = state => ({ // metricsName: PropTypes.arrayOf(PropTypes.string), // } -export default connect(mapStateToProps, { submitNASJob })(withStyles(styles)(NASParameters)); +export default connect(mapStateToProps, { submitNASJob, validationError })( + withStyles(styles)(NASParameters), +); diff --git a/pkg/ui/v1alpha3/frontend/src/constants/constants.js b/pkg/ui/v1alpha3/frontend/src/constants/constants.js index 9365f080a6b..5d9c839e13d 100644 --- a/pkg/ui/v1alpha3/frontend/src/constants/constants.js +++ b/pkg/ui/v1alpha3/frontend/src/constants/constants.js @@ -8,9 +8,13 @@ export const MC_KIND_FILE = 'File'; export const MC_KIND_TENSORFLOW_EVENT = 'TensorFlowEvent'; export const MC_KIND_PROMETHEUS = 'PrometheusMetric'; export const MC_KIND_CUSTOM = 'Custom'; +export const MC_KIND_NONE = 'None'; export const MC_FILE_SYSTEM_KIND_FILE = 'File'; export const MC_FILE_SYSTEM_KIND_DIRECTORY = 'Directory'; export const MC_FILE_SYSTEM_NO_KIND = 'No File System'; export const MC_HTTP_GET_HTTP_SCHEME = 'HTTP'; + +export const MC_PROMETHEUS_DEFAULT_PORT = 8080; +export const MC_PROMETHEUS_DEFAULT_PATH = '/metrics'; diff --git a/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js b/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js index e9f37acf58b..4cc043c99fd 100644 --- a/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js +++ b/pkg/ui/v1alpha3/frontend/src/reducers/hpCreate.js @@ -257,7 +257,7 @@ const hpCreateReducer = (state = initialState, action) => { ...state, trialNamespace: action.namespace, }; - // Collector Kind change + // Metrics Collector Kind change case actions.CHANGE_MC_KIND_HP: let newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); newMCSpec.collector.kind = action.kind; @@ -280,16 +280,16 @@ const hpCreateReducer = (state = initialState, action) => { default: newKind = constants.MC_FILE_SYSTEM_NO_KIND; } - // File or TF Event collector Kind + // File or TF Event Kind newMCSpec.source.fileSystemPath = { kind: newKind, path: '', }; } else if (action.kind === constants.MC_KIND_PROMETHEUS) { - // Prometheus collector Kind + // Prometheus Kind newMCSpec.source.httpGet = { - port: '', - path: '', + port: constants.MC_PROMETHEUS_DEFAULT_PORT, + path: constants.MC_PROMETHEUS_DEFAULT_PATH, scheme: constants.MC_HTTP_GET_HTTP_SCHEME, host: '', httpHeaders: [], @@ -323,7 +323,7 @@ const hpCreateReducer = (state = initialState, action) => { ...state, mcSpec: newMCSpec, }; - // HTTPGet Headers + // Collector HTTPGet Headers case actions.ADD_MC_HTTP_GET_HEADER_HP: newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); let currentHeaders = newMCSpec.source.httpGet.httpHeaders.slice(); @@ -352,13 +352,13 @@ const hpCreateReducer = (state = initialState, action) => { ...state, mcSpec: newMCSpec, }; - // Custom container + // Collector Custom container case actions.CHANGE_MC_CUSTOM_CONTAINER_HP: return { ...state, mcCustomContainerYaml: action.yamlContainer, }; - // Metrics Format + // Collector Metrics Format case actions.ADD_MC_METRICS_FORMAT_HP: newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); let currentFormats = newMCSpec.source.filter.metricsFormat.slice(); diff --git a/pkg/ui/v1alpha3/frontend/src/reducers/nasCreate.js b/pkg/ui/v1alpha3/frontend/src/reducers/nasCreate.js index 731ed8a54fc..b174e005bb6 100644 --- a/pkg/ui/v1alpha3/frontend/src/reducers/nasCreate.js +++ b/pkg/ui/v1alpha3/frontend/src/reducers/nasCreate.js @@ -1,4 +1,5 @@ import * as actions from '../actions/nasCreateActions'; +import * as constants from '../constants/constants'; const initialState = { commonParametersMetadata: [ @@ -349,6 +350,17 @@ const initialState = { currentYaml: '', snackText: '', snackOpen: false, + mcSpec: { + collector: { + kind: 'StdOut', + }, + source: { + filter: { + metricsFormat: [], + }, + }, + }, + mcCustomContainerYaml: '', }; const filterValue = (obj, key) => { @@ -557,6 +569,135 @@ const nasCreateReducer = (state = initialState, action) => { ...state, snackOpen: false, }; + // Metrics Collector Kind change + case actions.CHANGE_MC_KIND_NAS: + let newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + newMCSpec.collector.kind = action.kind; + + if ( + action.kind === constants.MC_KIND_FILE || + action.kind === constants.MC_KIND_TENSORFLOW_EVENT || + action.kind === constants.MC_KIND_CUSTOM + ) { + let newKind; + switch (action.kind) { + case constants.MC_KIND_FILE: + newKind = constants.MC_FILE_SYSTEM_KIND_FILE; + break; + + case constants.MC_KIND_TENSORFLOW_EVENT: + newKind = constants.MC_FILE_SYSTEM_KIND_DIRECTORY; + break; + + default: + newKind = constants.MC_FILE_SYSTEM_NO_KIND; + } + // File or TF Event Kind + newMCSpec.source.fileSystemPath = { + kind: newKind, + path: '', + }; + } else if (action.kind === constants.MC_KIND_PROMETHEUS) { + // Prometheus Kind + newMCSpec.source.httpGet = { + port: constants.MC_PROMETHEUS_DEFAULT_PORT, + path: constants.MC_PROMETHEUS_DEFAULT_PATH, + scheme: constants.MC_HTTP_GET_HTTP_SCHEME, + host: '', + httpHeaders: [], + }; + } + + return { + ...state, + mcSpec: newMCSpec, + mcCustomContainerYaml: '', + }; + // File System Path change + case actions.CHANGE_MC_FILE_SYSTEM_NAS: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + newMCSpec.source.fileSystemPath.kind = action.kind; + newMCSpec.source.fileSystemPath.path = action.path; + return { + ...state, + mcSpec: newMCSpec, + }; + // HTTPGet settings + case actions.CHANGE_MC_HTTP_GET_NAS: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + + newMCSpec.source.httpGet.port = action.port; + newMCSpec.source.httpGet.path = action.path; + newMCSpec.source.httpGet.scheme = action.scheme; + newMCSpec.source.httpGet.host = action.host; + + return { + ...state, + mcSpec: newMCSpec, + }; + // Collector HTTPGet Headers + case actions.ADD_MC_HTTP_GET_HEADER_NAS: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + let currentHeaders = newMCSpec.source.httpGet.httpHeaders.slice(); + let newHeader = { name: '', value: '' }; + currentHeaders.push(newHeader); + newMCSpec.source.httpGet.httpHeaders = currentHeaders; + return { + ...state, + mcSpec: newMCSpec, + }; + case actions.CHANGE_MC_HTTP_GET_HEADER_NAS: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + currentHeaders = newMCSpec.source.httpGet.httpHeaders.slice(); + currentHeaders[action.index][action.fieldName] = action.value; + newMCSpec.source.httpGet.httpHeaders = currentHeaders; + return { + ...state, + mcSpec: newMCSpec, + }; + case actions.DELETE_MC_HTTP_GET_HEADER_NAS: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + currentHeaders = newMCSpec.source.httpGet.httpHeaders.slice(); + currentHeaders.splice(action.index, 1); + newMCSpec.source.httpGet.httpHeaders = currentHeaders; + return { + ...state, + mcSpec: newMCSpec, + }; + // Collector Custom container + case actions.CHANGE_MC_CUSTOM_CONTAINER_NAS: + return { + ...state, + mcCustomContainerYaml: action.yamlContainer, + }; + // Collector Metrics Format + case actions.ADD_MC_METRICS_FORMAT_NAS: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + let currentFormats = newMCSpec.source.filter.metricsFormat.slice(); + currentFormats.push(''); + newMCSpec.source.filter.metricsFormat = currentFormats; + return { + ...state, + mcSpec: newMCSpec, + }; + case actions.CHANGE_MC_METRIC_FORMAT_NAS: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + currentFormats = newMCSpec.source.filter.metricsFormat.slice(); + currentFormats[action.index] = action.format; + newMCSpec.source.filter.metricsFormat = currentFormats; + return { + ...state, + mcSpec: newMCSpec, + }; + case actions.DELETE_MC_METRIC_FORMAT_NAS: + newMCSpec = JSON.parse(JSON.stringify(state.mcSpec)); + currentFormats = newMCSpec.source.filter.metricsFormat.slice(); + currentFormats.splice(action.index, 1); + newMCSpec.source.filter.metricsFormat = currentFormats; + return { + ...state, + mcSpec: newMCSpec, + }; default: return state; } From eb39bb5b1f8f951bcb1f5c206f88c55c201bcdcf Mon Sep 17 00:00:00 2001 From: avelichk Date: Thu, 19 Mar 2020 01:56:32 +0000 Subject: [PATCH 3/3] Change label for HP metrics regex --- .../src/components/Common/Create/Params/MetricsCollector.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx b/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx index ecda5864f0c..f900967f181 100644 --- a/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx +++ b/pkg/ui/v1alpha3/frontend/src/components/Common/Create/Params/MetricsCollector.jsx @@ -567,7 +567,7 @@ class MetricsCollectorSpec extends React.Component {