-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add vm config parser, unify vm and helm type app deployment (#503)
* refactor: Deploy page combine vm and helm type app * refactor: VM config translator, compat with edge case * Deploy page updated when changed runtime, version * Fix test code
- Loading branch information
Showing
25 changed files
with
1,156 additions
and
787 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
mock/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
const toString = Object.prototype.toString; | ||
const isObject = val => toString.call(val) === '[object Object]'; | ||
const isNumber = val => toString.call(val) === '[object Number]'; | ||
|
||
const validRenderTypes = [ | ||
'radio', | ||
// 'checkbox', | ||
'input', | ||
'select', | ||
'text', | ||
'slider', | ||
'number', | ||
'node-role', | ||
'yaml' | ||
]; | ||
|
||
const TEXT_KEYS = ['description']; // keys will be transformed to text area | ||
|
||
const NODE_MUST_HAVE_KEYS = ['cpu', 'memory']; | ||
|
||
// keys to check if object can be normalized to Section | ||
const featureKeys = ['key']; | ||
|
||
const sectionProto = { | ||
default: '', | ||
description: '', | ||
key: '', | ||
label: '', | ||
required: false, | ||
type: 'string', | ||
getRenderType() { | ||
if (validRenderTypes.includes(this.renderType)) { | ||
return this.renderType; | ||
} | ||
|
||
if (TEXT_KEYS.includes(this.key)) { | ||
return 'text'; | ||
} | ||
|
||
if ( | ||
this.type === 'string' && | ||
!this.items && | ||
!TEXT_KEYS.includes(this.key) && | ||
!(this.range || this.step) | ||
) { | ||
return 'input'; | ||
} | ||
|
||
if (this.type === 'integer' && Array.isArray(this.range)) { | ||
return 'radio'; | ||
} | ||
|
||
if ( | ||
this.type === 'integer' && | ||
isNumber(this.step) && | ||
isNumber(this.min) && | ||
isNumber(this.max) | ||
) { | ||
return 'slider'; | ||
} | ||
|
||
if (this.type === 'integer' && !this.step && (isNumber(this.min) || isNumber(this.max))) { | ||
return 'number'; | ||
} | ||
|
||
if (this.type === 'string' && Array.isArray(this.items)) { | ||
return 'select'; | ||
} | ||
|
||
if (this.type === 'array' && isNodeItem(this)) { | ||
return 'node-role'; | ||
} | ||
|
||
if (typeof this.type === 'string' && 'changeable' in this) { | ||
return Array.isArray(this.range) ? 'radio' : 'input'; | ||
} | ||
|
||
throw Error(`unknown render type for section: ${this}`); | ||
}, | ||
toString() { | ||
return JSON.stringify(...this); | ||
}, | ||
toJSON() { | ||
// transform to object that react can accept | ||
const ret = Object.assign( | ||
{ ...this }, | ||
{ | ||
default: this.default, | ||
description: this.description, | ||
label: this.label || this.key, | ||
required: typeof this.required === 'boolean' ? this.required : this.required !== 'false', | ||
type: this.type, | ||
renderType: this.getRenderType() | ||
} | ||
); | ||
|
||
ret.keyName = ret.originKey = ret.key; | ||
ret.defaultValue = ret.default; | ||
|
||
delete ret.key; | ||
delete ret.default; | ||
|
||
// generate unique keyName based on each level | ||
if (ret.keyPrefix) { | ||
ret.keyName = [ret.keyPrefix, ret.keyName].join('.'); | ||
delete ret.keyPrefix; | ||
} | ||
|
||
return ret; | ||
} | ||
}; | ||
|
||
export const isNodeItem = (item = {}) => { | ||
if (!Array.isArray(item.properties)) { | ||
return false; | ||
} | ||
|
||
const itemKeys = item.properties.map(o => o.key); | ||
|
||
return NODE_MUST_HAVE_KEYS.reduce((check, key) => { | ||
return check && itemKeys.includes(key); | ||
}, true); | ||
}; | ||
|
||
export const genPrefix = (prefix, base) => { | ||
if (typeof prefix === 'string' && prefix) { | ||
return [prefix, base].join('.'); | ||
} | ||
return base; | ||
}; | ||
|
||
const factory = (ownProps = {}, extendProps = {}) => { | ||
const wrapObj = obj => { | ||
const ownKeys = (obj && Object.getOwnPropertyNames(obj)) || []; | ||
const hasFeatureKeys = featureKeys.reduce((check, key) => { | ||
return check && ownKeys.includes(key); | ||
}, true); | ||
|
||
if (!hasFeatureKeys) { | ||
return obj; | ||
} | ||
|
||
const inst = Object.create(sectionProto); | ||
|
||
if (Array.isArray(obj.properties)) { | ||
obj.properties = factory(obj.properties, { | ||
keyPrefix: genPrefix(extendProps.keyPrefix, obj.key) | ||
}); | ||
} | ||
|
||
return Object.assign(inst, obj, extendProps); | ||
}; | ||
|
||
if (isObject(ownProps)) { | ||
return wrapObj(ownProps); | ||
} | ||
|
||
if (Array.isArray(ownProps)) { | ||
return ownProps.filter(Boolean).map(prop => wrapObj(prop)); | ||
} | ||
}; | ||
|
||
export default factory; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
import _ from 'lodash'; | ||
|
||
import factory, { isNodeItem } from './factory'; | ||
|
||
const defaultOptions = {}; | ||
|
||
const CLUSTER_KEY = 'cluster'; | ||
const NAME_KEY = 'name'; | ||
const DESC_KEY = 'description'; | ||
const SUBNET_KEY = 'subnet'; | ||
const ENV_KEY = 'env'; | ||
|
||
// setting placeholder | ||
const defaultNameSetting = { | ||
key: 'name', | ||
label: 'Name', | ||
type: 'string', | ||
default: '', | ||
required: false | ||
}; | ||
|
||
const defaultRuntimeSetting = { | ||
key: 'runtime', | ||
label: 'Runtime', | ||
renderType: 'radio', | ||
range: [] | ||
}; | ||
|
||
const defaultVersionSetting = { | ||
key: 'version', | ||
label: 'Version', | ||
items: [ | ||
// {name: 'x1', value: 'v1'}, | ||
] | ||
}; | ||
|
||
export default class VMConfigParser { | ||
constructor(config, options = {}) { | ||
this.config = config; | ||
this.options = _.extend(defaultOptions, options); | ||
|
||
this.runtimeConf = _.clone(defaultRuntimeSetting); | ||
this.versionConf = _.clone(defaultVersionSetting); | ||
this.vxnetConf = { items: [] }; | ||
} | ||
|
||
setConfig(config = {}) { | ||
if (typeof config === 'string') { | ||
config = JSON.parse(config); | ||
} | ||
|
||
if (!this.validConfig(config)) { | ||
throw Error('invalid config input'); | ||
} | ||
|
||
// config.json | ||
this.config = config; | ||
} | ||
|
||
validConfig(config) { | ||
return ( | ||
_.isObject(config) && typeof config.type === 'string' && Array.isArray(config.properties) | ||
); | ||
} | ||
|
||
isReady() { | ||
return !_.isEmpty(this.config); | ||
} | ||
|
||
getConfigByKey(config, key) { | ||
if (typeof config === 'string' && !key) { | ||
key = config; | ||
config = null; | ||
} | ||
|
||
if (typeof key !== 'string') { | ||
throw Error(`key should be string, but ${typeof key} given`); | ||
} | ||
|
||
config = config || this.config; | ||
if (_.isEmpty(config)) { | ||
return; | ||
} | ||
|
||
const properties = config.properties || config; | ||
|
||
if (_.isArray(properties)) { | ||
return _.find(properties, { key }); | ||
} | ||
if (_.isObject(properties)) { | ||
return _.extend({}, properties[key], { key }); | ||
} | ||
} | ||
|
||
getClusterSetting() { | ||
return this.getConfigByKey(CLUSTER_KEY); | ||
} | ||
|
||
/** | ||
* mock basic setting for helm app | ||
*/ | ||
getDefaultBasicSetting(override = {}) { | ||
const setting = []; | ||
const nameConf = _.clone(defaultNameSetting); | ||
setting.push(nameConf, this.runtimeConf, this.versionConf); | ||
|
||
if (!_.isEmpty(override)) { | ||
_.forEach(setting, setting => { | ||
if (setting.key === override.key) { | ||
_.extend(setting, override); | ||
} | ||
}); | ||
} | ||
|
||
return factory(setting, { keyPrefix: CLUSTER_KEY }); | ||
} | ||
|
||
getBasicSetting() { | ||
const clusterSetting = this.getClusterSetting(); | ||
const setting = []; | ||
const nameConf = this.getConfigByKey(clusterSetting, NAME_KEY); | ||
const descConf = this.getConfigByKey(clusterSetting, DESC_KEY); | ||
|
||
setting.push(nameConf, descConf, this.runtimeConf, this.versionConf); | ||
|
||
return factory(setting, { keyPrefix: CLUSTER_KEY }); | ||
} | ||
|
||
getNodeSetting() { | ||
const clusterSetting = this.getClusterSetting(); | ||
return factory(_.filter(clusterSetting.properties, isNodeItem), { keyPrefix: 'node' }); | ||
} | ||
|
||
getVxnetSetting() { | ||
return factory([this.vxnetConf], { keyPrefix: CLUSTER_KEY }); | ||
} | ||
|
||
getEnvSetting() { | ||
const envSetting = this.getConfigByKey(ENV_KEY); | ||
return factory(_.get(envSetting, 'properties', []), { keyPrefix: 'env', labelPrefix: 'env' }); | ||
} | ||
|
||
setRuntimes(runtimes = [], mergeProps) { | ||
this.runtimeConf.range = runtimes; | ||
!_.isEmpty(mergeProps) && _.extend(this.runtimeConf, mergeProps); | ||
} | ||
|
||
setVersions(versions = [], mergeProps) { | ||
this.versionConf.items = versions; | ||
!_.isEmpty(mergeProps) && _.extend(this.versionConf, mergeProps); | ||
} | ||
|
||
setSubnets(nets = [], mergeProps) { | ||
if (!this.vxnetConf.key) { | ||
const clusterSetting = this.getClusterSetting(); | ||
|
||
if (!_.isEmpty(clusterSetting)) { | ||
_.extend(this.vxnetConf, this.getConfigByKey(clusterSetting, SUBNET_KEY)); | ||
} | ||
} | ||
|
||
this.vxnetConf.items = nets; | ||
|
||
!_.isEmpty(mergeProps) && _.extend(this.vxnetConf, mergeProps); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.