ui/lib/global-admin/addon/components/registry-catalog/component.js

855 lines
30 KiB
JavaScript

import Component from '@ember/component';
import layout from './template';
import {
get, set, computed, setProperties, defineProperty, observer
} from '@ember/object';
import CrudCatalog from 'shared/mixins/crud-catalog';
import { inject as service } from '@ember/service';
import { requiredError } from 'shared/utils/util';
import C from 'ui/utils/constants';
import { ucFirst, convertToMillis } from 'shared/utils/util';
import ReservationCheck from 'shared/mixins/reservation-check';
import { parseSi } from 'shared/utils/parse-unit'
const INTERNAL = 'internal'
const INGRESS_HOSTS_CORE = 'expose.ingress.host';
const APP_VERSION = 'catalog://?catalog=system-library&template=harbor&version=1.7.5-rancher1';
const STORAGE_TYPE = ['filesystem', 's3'];
const S3_REGION = C.AWS_S3_REGIONS;
const ACCESS_MODE = ['ReadWriteOnce']
const RDISE_EXTENNAL_KEYS = ['host', 'port', 'jobserviceDatabaseIndex', 'registryDatabaseIndex', 'password']
const PERSISTENCE_KEYS = ['existingClaim', 'size', 'storageClass']
const DATABASE_EXTERNAL_KEYS = ['host', 'port', 'username', 'password', 'coreDatabase', 'clairDatabase', 'notaryServerDatabase', 'notarySignerDatabase', 'sslmode']
const S3_KEYS = ['region', 'bucket', 'accesskey', 'secretkey', 'regionendpoint']
const DATABASE_TYPE = [INTERNAL, 'external']
const SSL_MODE = C.POSTGRESQL_SSL_MODE;
const PERSISTENCE_COMPONENTS = ['redis', 'registry', 'database'];
const DATABASE_REQUEST_CPU = 'database.internal.resources.requests.cpu'
const DATABASE_REQUEST_MEM = 'database.internal.resources.requests.memory'
const DATABASE_LIMIT_CPU = 'database.internal.resources.limits.cpu'
const DATABASE_LIMIT_MEM = 'database.internal.resources.limits.memory'
const REDIS_REQUEST_CPU = 'redis.internal.resources.requests.cpu'
const REDIS_REQUEST_MEM = 'redis.internal.resources.requests.memory'
const REDIS_LIMIT_CPU = 'redis.internal.resources.limits.cpu'
const REDIS_LIMIT_MEM = 'redis.internal.resources.limits.memory'
const REGISTRY_REQUEST_CPU = 'registry.registry.resources.requests.cpu'
const REGISTRY_REQUEST_MEM = 'registry.registry.resources.requests.memory'
const REGISTRY_LIMIT_CPU = 'registry.registry.resources.limits.cpu'
const REGISTRY_LIMIT_MEM = 'registry.registry.resources.limits.memory'
const CLAIR_REQUEST_CPU = 'clair.resources.requests.cpu'
const CLAIR_REQUEST_MEM = 'clair.resources.requests.memory'
const CLAIR_LIMIT_CPU = 'clair.resources.limits.cpu'
const CLAIR_LIMIT_MEM = 'clair.resources.limits.memory'
const NOTARY_REQUEST_CPU = 'notary.server.resources.requests.cpu'
const NOTARY_REQUEST_MEM = 'notary.server.resources.requests.memory'
const NOTARY_LIMIT_CPU = 'notary.server.resources.limits.cpu'
const NOTARY_LIMIT_MEM = 'notary.server.resources.limits.memory'
const SIGNER_REQUEST_CPU = 'notary.signer.resources.requests.cpu'
const SIGNER_REQUEST_MEM = 'notary.signer.resources.requests.memory'
const SIGNER_LIMIT_CPU = 'notary.signer.resources.limits.cpu'
const SIGNER_LIMIT_MEM = 'notary.signer.resources.limits.memory'
const REGISTRY_NODE_SELECTOR_PREFIX = 'registry.nodeSelector.'
const DATABASE_NODE_SELECTOR_PREFIX = 'database.internal.nodeSelector.'
const REDIS_NODE_SELECTOR_PREFIX = 'redis.internal.nodeSelector.'
const CLAIR_NODE_SELECTOR_PREFIX = 'clair.nodeSelector.'
const NOTARY_NODE_SELECTOR_PREFIX = 'notary.nodeSelector.'
const WORKLOADS = ['database', 'redis', 'registry', 'clair', 'notaryServer', 'notarySigner']
const HIDDEN_KEYS = {
'externalURL': `https://${ window.location.host }`,
[INGRESS_HOSTS_CORE]: window.location.host,
'persistence.type': 'storageClass',
}
const ANSWER_TO_CONFIG = {
secretKey: 'secretKey',
'imageChartStorage.type': 'storageType',
'database.type': 'databaseType',
'redis.type': 'redisType',
'clair.enabled': 'clairEnabled',
'notary.enabled': 'notaryEnabled',
'harborAdminPassword': 'password',
[DATABASE_REQUEST_CPU]: 'databaseRequestCpu',
[DATABASE_REQUEST_MEM]: 'databaseRequestMemory',
[DATABASE_LIMIT_CPU]: 'databaseLimitCpu',
[DATABASE_LIMIT_MEM]: 'databaseLimitMemory',
[REDIS_REQUEST_CPU]: 'redisRequestCpu',
[REDIS_REQUEST_MEM]: 'redisRequestMemory',
[REDIS_LIMIT_CPU]: 'redisLimitCpu',
[REDIS_LIMIT_MEM]: 'redisLimitMemory',
[REGISTRY_REQUEST_CPU]: 'registryRequestCpu',
[REGISTRY_REQUEST_MEM]: 'registryRequestMemory',
[REGISTRY_LIMIT_CPU]: 'registryLimitCpu',
[REGISTRY_LIMIT_MEM]: 'registryLimitMemory',
[CLAIR_REQUEST_CPU]: 'clairRequestCpu',
[CLAIR_REQUEST_MEM]: 'clairRequestMemory',
[CLAIR_LIMIT_CPU]: 'clairLimitCpu',
[CLAIR_LIMIT_MEM]: 'clairLimitMemory',
[NOTARY_REQUEST_CPU]: 'notaryServerRequestCpu',
[NOTARY_REQUEST_MEM]: 'notaryServerRequestMemory',
[NOTARY_LIMIT_CPU]: 'notaryServerLimitCpu',
[NOTARY_LIMIT_MEM]: 'notaryServerLimitMemory',
[SIGNER_REQUEST_CPU]: 'notarySignerRequestCpu',
[SIGNER_REQUEST_MEM]: 'notarySignerRequestMemory',
[SIGNER_LIMIT_CPU]: 'notarySignerLimitCpu',
[SIGNER_LIMIT_MEM]: 'notarySignerLimitMemory',
}
export default Component.extend(CrudCatalog, ReservationCheck, {
intl: service(),
globalStore: service(),
growl: service(),
layout,
answers: null,
appName: 'global-registry',
nsName: 'cattle-system',
appVersion: APP_VERSION,
nsExists: true,
config: null,
level: 'cluster',
clusterLevelMinCpu: 500,
clusterLevelMinMemory: 500,
init() {
this._super(...arguments);
this.initWorkloads();
if (get(this, 'showForm')) {
this.initConfig()
} else {
this.clearTimeOut()
const { globalRegistryEnabled = {} } = this
setProperties(this, {
enabled: globalRegistryEnabled.value === 'true',
ready: true,
})
}
},
actions: {
save(cb) {
set(this, 'errors', [])
const errors = this.validate() || []
if (errors.length > 0) {
set(this, 'errors', errors)
cb()
return
}
let answers = { ...HIDDEN_KEYS };
const answerKeys = Object.keys(ANSWER_TO_CONFIG) || []
answerKeys.map((key) => {
const value = get(this, `config.${ ANSWER_TO_CONFIG[key] }`)
if ( value === undefined || value === '' ) {
return;
}
switch (key) {
case DATABASE_REQUEST_CPU:
case DATABASE_LIMIT_CPU:
case REDIS_REQUEST_CPU:
case REDIS_LIMIT_CPU:
case REGISTRY_REQUEST_CPU:
case REGISTRY_LIMIT_CPU:
case CLAIR_REQUEST_CPU:
case CLAIR_LIMIT_CPU:
case NOTARY_REQUEST_CPU:
case NOTARY_LIMIT_CPU:
case SIGNER_REQUEST_CPU:
case SIGNER_LIMIT_CPU:
answers[key] = `${ value }m`
break;
case DATABASE_REQUEST_MEM:
case DATABASE_LIMIT_MEM:
case REDIS_REQUEST_MEM:
case REDIS_LIMIT_MEM:
case REGISTRY_REQUEST_MEM:
case REGISTRY_LIMIT_MEM:
case CLAIR_REQUEST_MEM:
case CLAIR_LIMIT_MEM:
case NOTARY_REQUEST_MEM:
case NOTARY_LIMIT_MEM:
case SIGNER_REQUEST_MEM:
case SIGNER_LIMIT_MEM:
answers[key] = `${ value }Mi`
break;
default:
answers[key] = value
}
})
const {
databaseType, redisType, storageType
} = get(this, 'config')
if (storageType === 's3') {
S3_KEYS.map((k) => {
const key = `imageChartStorage.s3.${ k }`
answers[key] = get(this, key)
})
} else if (storageType === 'filesystem') {
this.willSavePersistence(answers, 'registry')
}
if (databaseType === 'external') {
DATABASE_EXTERNAL_KEYS.map((k) => {
const key = `database.external.${ k }`
answers[key] = get(this, key)
})
} else if (databaseType === INTERNAL) {
this.willSavePersistence(answers, 'database')
}
if (redisType === 'external') {
RDISE_EXTENNAL_KEYS.map((k) => {
const key = `redis.external.${ k }`
answers[key] = get(this, key)
})
answers['redis.external.coreDatabaseIndex'] = '0'
} else if (redisType === INTERNAL) {
this.willSavePersistence(answers, 'redis')
}
['registry', 'database', 'redis', 'clair', 'notary'].map((component) => {
(get(this, `${ component }NodeSelectors`) || []).map((selector) => {
let { key, value } = selector
if (key.includes('.')) {
key = key.replace(/\./g, '\\.')
}
if (component === 'registry') {
answers[`registry.nodeSelector.${ key }`] = value
} else if (['database', 'redis'].includes(component) && get(this, `config.${ component }Type`) === INTERNAL) {
answers[`${ component }.internal.nodeSelector.${ key }`] = value
} else if (['clair', 'notary'].includes(component) && get(this, `config.${ component }Enabled`)) {
answers[`${ component }.nodeSelector.${ key }`] = value
}
});
})
this.save(cb, answers);
},
disable() {
const url = get(this, 'app.links.self');
get(this, 'globalStore')
.rawRequest({
url,
method: 'DELETE',
})
.then(() => {
this.setGlobalRegistryEnabled('false', () => {
setTimeout(() => {
window.location.href = window.location.href;
}, 1000);
})
})
.catch((err) => {
get(this, 'growl').fromError(get(err, 'body.message'));
})
},
},
workloadEnabledChange: observer('config.clairEnabled', 'config.notaryEnabled', 'config.databaseType', 'config.redisType', 'config.storageType', function() {
['clair', 'notary'].map((w) => {
if (!get(this, `config.${ w }Enabled`)) {
set(this, `${ w }NodeSelectors`, [])
}
});
['database', 'redis'].map((w) => {
if (get(this, `config.${ w }Type`) === 'external') {
set(this, `${ w }NodeSelectors`, [])
}
});
set(this, `registryNodeSelectors`, [])
this.notifyPropertyChange('requestsCpu')
this.notifyPropertyChange('requestsMemory')
this.notifyPropertyChange('saveDisabled')
}),
dockerLogin: computed('app.answers', function() {
const url = (get(this, 'app.answers') || HIDDEN_KEYS)[INGRESS_HOSTS_CORE];
const username = get(this, 'showForm') ? 'admin' : 'USERNAME'
return `docker login --username=${ username } ${ url }`;
}),
dockerPull: computed('app.answers', function() {
const url = (get(this, 'app.answers') || HIDDEN_KEYS)[INGRESS_HOSTS_CORE];
return `docker pull ${ url }/REPO_NAME/IMAGE_NAME[:TAG]`;
}),
dockerTag: computed('app.answers', function() {
const url = (get(this, 'app.answers') || HIDDEN_KEYS)[INGRESS_HOSTS_CORE];
return `docker tag SOURCE_IMAGE[:TAG] ${ url }/REPO_NAME/IMAGE_NAME[:TAG]`;
}),
dockerPush: computed('app.answers', function() {
const url = (get(this, 'app.answers') || HIDDEN_KEYS)[INGRESS_HOSTS_CORE];
return `docker push ${ url }/REPO_NAME/IMAGE_NAME[:TAG]`;
}),
storageTypeContent: computed(() => {
return STORAGE_TYPE.map((value) => ({
label: value,
value
}))
}),
s3RegionContent: computed(() => {
return S3_REGION.map((value) => ({
label: value,
value
}))
}),
accessModeContent: computed(() => {
return ACCESS_MODE.map((value) => ({
label: value,
value
}))
}),
databaseTypeContent: computed(() => {
return DATABASE_TYPE.map((value) => ({
label: value,
value
}))
}),
sslmodeContent: computed(() => {
return SSL_MODE.map((value) => ({
label: value,
value
}))
}),
persistentVolumeChoices: computed('persistentVolumeClaims.[]', function() {
return (get(this, 'persistentVolumeClaims') || []).map((s) => ({
label: s.name,
value: s.id
}))
}),
requestsCpu: computed('config.redisRequestCpu', 'config.databaseRequestCpu', 'config.registryRequestCpu', 'config.notaryServerRequestCpu', 'config.calirRequestCpu', 'config.notarySignerRequestCpu', function() {
return WORKLOADS
.filter((w) => {
if (['notaryServer', 'notarySigner'].includes(w) && !get(this, `config.notaryEnabled`)) {
return false
} else if (w === 'clair' && !get(this, `config.clairEnabled`)) {
return false
} else if (['redis', 'database'].includes(w) && get(this, `config.${ w }Type`) === 'external') {
return false
}
return true
})
.reduce((all, w) => {
return all + parseInt(get(this, `config.${ w }RequestCpu`) || 0)
}, 0)
}),
requestsMemory: computed('config.redisRequestMemory', 'config.databaseRequestMemory', 'config.registryRequestMemory', 'config.clairRequestMemory', 'config.notaryServerRequestMemory', 'config.notarySignerRequestMemory', function() {
return WORKLOADS
.filter((w) => {
if (['notaryServer', 'notarySigner'].includes(w) && !get(this, `config.notaryEnabled`)) {
return false
} else if (w === 'clair' && !get(this, `config.clairEnabled`)) {
return false
} else if (['redis', 'database'].includes(w) && get(this, `config.${ w }Type`) === 'external') {
return false
}
return true
})
.reduce((all, w) => {
return all + parseInt(get(this, `config.${ w }RequestMemory`) || 0)
}, 0)
}),
globalRegistryWarning: computed('insufficientCpu', 'insufficientMemory', function() {
let {
insufficientCpu, insufficientMemory, intl, minCpu, minMemory, enabled
} = this
const prefix = 'globalRegistryPage.insufficientSize.total'
const action = enabled ? 'update' : 'enable'
if (insufficientCpu && insufficientMemory) {
return intl.t(`${ prefix }.all`, {
minCpu,
minMemory,
action,
})
} else if (insufficientCpu) {
return intl.t(`${ prefix }.cpu`, {
minCpu,
action,
})
} else if (insufficientMemory) {
return intl.t(`${ prefix }.memory`, {
minMemory,
action,
})
}
}),
saveDisabled: computed('globalRegistryWarning', 'databaseWarning', 'redisWarning', 'registryWarning', 'clairWarning', 'notaryWarning', 'notarySignerWarning', 'notaryServerWarning', function() {
return [...WORKLOADS, 'notary'].reduce((out, w) => {
if (w === 'clair' && get(this, `config.clairEnabled`)) {
return out || (get(this, `${ w }Warning`) || false)
} else if (['database', 'redis'].includes(w) && get(this, `config.${ w }Type`) === 'internal') {
return out || (get(this, `${ w }Warning`) || false)
} else if (['notary', 'notaryServer', 'notarySigner'].includes(w) && get(this, `config.notaryEnabled`)) {
return out || (get(this, `${ w }Warning`) || false)
} else {
return out || (get(this, `${ w }Warning`) || false)
}
}, false) || get(this, 'globalRegistryWarning')
}),
notarySchedulableNodes: computed('notaryNodeSelectors.[]', 'cluster.nodes.@each.{allocatable,requested}', 'config.notaryEnabled', function() {
if (!get(this, 'config.notaryEnabled')) {
return []
} else {
return this.getSchedulableNodes('notary')
}
}),
insufficientNotaryCpu: computed(`notarySchedulableNodes.@each.{allocatable,requested}`, `config.notaryServerRequestCpu`, 'config.notarySignerRequestCpu', 'cluster.nodes.@each.{allocatable,requested}', function() {
const reservation = Math.max(parseInt(get(this, 'config.notaryServerRequestCpu') || '0'), parseInt(get(this, 'config.notarySignerRequestCpu') || '0'))
return this.getComponentInsufficient('notary', 'cpu', reservation)
}),
insufficientNotaryMemory: computed(`notarySchedulableNodes.@each.{allocatable,requested}`, `config.notaryServerRequestMemory`, 'config.notarySignerRequestMemory', 'cluster.nodes.@each.{allocatable,requested}', function() {
const reservation = Math.max(parseInt(get(this, 'config.notaryServerRequestMemory') || '0'), parseInt(get(this, 'config.notarySignerRequestMemory') || '0'))
return this.getComponentInsufficient('notary', 'memory', reservation)
}),
notaryWarning: computed(`insufficientNotaryCpu`, `insufficientNotaryMemory`, function() {
if (!get(this, 'config.notaryEnabled')) {
return
}
const componentCpu = Math.max(parseInt(get(this, 'config.notaryServerRequestCpu') || '0'), parseInt(get(this, 'config.notarySignerRequestCpu') || '0'))
const componentMemory = Math.max(parseInt(get(this, 'config.notaryServerRequestMemory') || '0'), parseInt(get(this, 'config.notarySignerRequestMemory') || '0'))
return this.getComponentWarning('notary', componentCpu, componentMemory, get(this, 'intl').t('globalRegistryPage.notaryAndSigner'))
}),
initConfig() {
const customAnswers = {};
setProperties(this, {
redis: { external: {}, },
persistence: {
persistentVolumeClaim: {
registry: {},
redis: {},
database: {},
}
},
database: { external: {}, },
imageChartStorage: { s3: {}, },
secretKeyQuestion: {
minLength: 16,
maxLength: 16,
validChars: 'alphanum',
},
config: {
clairEnabled: true,
notaryEnabled: true,
databaseType: INTERNAL,
redisType: INTERNAL,
storageType: 'filesystem',
databaseRequestCpu: 100,
databaseRequestMemory: 256,
databaseLimitCpu: 500,
databaseLimitMemory: 2048,
redisRequestCpu: 100,
redisRequestMemory: 256,
redisLimitCpu: 500,
redisLimitMemory: 2048,
registryRequestCpu: 100,
registryRequestMemory: 256,
registryLimitCpu: 1000,
registryLimitMemory: 2048,
clairRequestCpu: 100,
clairRequestMemory: 256,
clairLimitCpu: 500,
clairLimitMemory: 2048,
notaryServerRequestCpu: 100,
notaryServerRequestMemory: 256,
notaryServerLimitCpu: 500,
notaryServerLimitMemory: 2048,
notarySignerRequestCpu: 100,
notarySignerRequestMemory: 256,
notarySignerLimitCpu: 500,
notarySignerLimitMemory: 2048,
},
'database.external.sslmode': 'disable',
'database.external.coreDatabase': 'registry',
'database.external.clairDatabase': 'clair',
'database.external.notaryServerDatabase': 'notary_server',
'database.external.notarySignerDatabase': 'notary_signer',
'database.external.port': '5432',
'redis.external.port': '6379',
'persistence.persistentVolumeClaim.registry.size': '100Gi',
'persistence.persistentVolumeClaim.redis.size': '5Gi',
'persistence.persistentVolumeClaim.database.size': '5Gi',
useRedisStorageClass: true,
useDatabaseStorageClass: true,
useRegistryStorageClass: true,
})
if ( get(this, 'enabled') ) {
const answers = get(this, 'app.answers') || {};
const s3Keys = S3_KEYS.map((k) => `imageChartStorage.s3.${ k }`)
const databaseExtenalKeys = DATABASE_EXTERNAL_KEYS.map((k) => `database.external.${ k }`)
const redisExtenalKeys = RDISE_EXTENNAL_KEYS.map((k) => `redis.external.${ k }`)
const answerKeys = Object.keys(ANSWER_TO_CONFIG) || []
const registryNodeSelector = {}
const databaseNodeSelector = {};
const redisNodeSelector = {}
const clairNodeSelector = {}
const notaryNodeSelector = {}
Object.keys(answers).filter((key) => key.startsWith(DATABASE_NODE_SELECTOR_PREFIX) ).map((k) => {
const value = answers[k] || '';
const key = k.replace(DATABASE_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
databaseNodeSelector[key] = value
})
Object.keys(answers).filter((key) => key.startsWith(REDIS_NODE_SELECTOR_PREFIX) ).map((k) => {
const value = answers[k] || '';
const key = k.replace(REDIS_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
redisNodeSelector[key] = value
});
Object.keys(answers).filter((key) => key.startsWith(REGISTRY_NODE_SELECTOR_PREFIX) ).map((k) => {
let value = answers[k] || '';
const key = k.replace(REGISTRY_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
registryNodeSelector[key] = value
});
Object.keys(answers).filter((key) => key.startsWith(CLAIR_NODE_SELECTOR_PREFIX) ).map((k) => {
let value = answers[k] || '';
const key = k.replace(CLAIR_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
clairNodeSelector[key] = value
});
Object.keys(answers).filter((key) => key.startsWith(NOTARY_NODE_SELECTOR_PREFIX) ).map((k) => {
let value = answers[k] || '';
const key = k.replace(NOTARY_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
notaryNodeSelector[key] = value
});
const workloads = this.getEnalbedWorkloads(answers)
const preRequestsCpu = workloads.reduce((all, current) => {
const value = answers[`${ current }.resources.requests.cpu`]
return value ? all + convertToMillis(value) : all
}, 0)
const preRequestsMemory = workloads.reduce((all, current) => {
const value = answers[`${ current }.resources.requests.memory`]
return value ? all + parseSi(value) / 1048576 : all
}, 0)
setProperties(this, {
registryNodeSelector,
databaseNodeSelector,
redisNodeSelector,
clairNodeSelector,
notaryNodeSelector,
preRequestsCpu,
preRequestsMemory,
})
let persistenceKeys = []
PERSISTENCE_COMPONENTS.map((component) => PERSISTENCE_KEYS.map((k) => {
persistenceKeys = [...persistenceKeys, `persistence.persistentVolumeClaim.${ component }.${ k }`]
}))
const arr = [...s3Keys, ...databaseExtenalKeys, ...redisExtenalKeys, ...persistenceKeys]
Object.keys(answers).forEach((key) => {
if (key.startsWith(REGISTRY_NODE_SELECTOR_PREFIX)
|| key.startsWith(DATABASE_NODE_SELECTOR_PREFIX)
|| key.startsWith(REDIS_NODE_SELECTOR_PREFIX)
|| key.startsWith(CLAIR_NODE_SELECTOR_PREFIX)
|| key.startsWith(NOTARY_NODE_SELECTOR_PREFIX)
) {
return
}
if (arr.includes(key)) {
return set(this, key, answers[key])
}
if (Object.keys(HIDDEN_KEYS).includes(key) || key === 'redis.external.coreDatabaseIndex') {
return
}
if (answerKeys.includes(key)) {
let value
switch (key) {
case DATABASE_REQUEST_CPU:
case DATABASE_LIMIT_CPU:
case REDIS_REQUEST_CPU:
case REDIS_LIMIT_CPU:
case REGISTRY_REQUEST_CPU:
case REGISTRY_LIMIT_CPU:
case CLAIR_REQUEST_CPU:
case CLAIR_LIMIT_CPU:
case NOTARY_REQUEST_CPU:
case NOTARY_LIMIT_CPU:
case SIGNER_REQUEST_CPU:
case SIGNER_LIMIT_CPU:
value = convertToMillis(answers[key] || '0')
break;
case DATABASE_REQUEST_MEM:
case DATABASE_LIMIT_MEM:
case REDIS_REQUEST_MEM:
case REDIS_LIMIT_MEM:
case REGISTRY_REQUEST_MEM:
case REGISTRY_LIMIT_MEM:
case CLAIR_REQUEST_MEM:
case CLAIR_LIMIT_MEM:
case NOTARY_REQUEST_MEM:
case NOTARY_LIMIT_MEM:
case SIGNER_REQUEST_MEM:
case SIGNER_LIMIT_MEM:
value = parseSi(answers[key] || '0', 1024) / 1048576
break;
default:
value = answers[key]
}
return set(this, `config.${ ANSWER_TO_CONFIG[key] }`, value)
}
customAnswers[key] = answers[key];
});
}
set(this, 'customAnswers', customAnswers);
},
validate() {
const errors = []
const intl = get(this, 'intl')
const customAnswers = get(this, 'customAnswers')
const exposeIngressHost = customAnswers[INGRESS_HOSTS_CORE] || window.location.host
if (/^[0-9.:]+$/.test(exposeIngressHost)) {
errors.pushObject(intl.t('globalRegistryPage.config.exposeIngressHost.error', { hostName: exposeIngressHost }))
}
if (!get(this, 'config.password')) {
errors.pushObject(requiredError('globalRegistryPage.setting.adminPassword.label'))
}
const secretKey = get(this, 'config.secretKey')
if (secretKey && secretKey.length !== 16) {
errors.pushObject(intl.t('globalRegistryPage.config.secretKey.error'))
}
if (get(this, 'config.storageType') === 'filesystem') {
errors.pushObjects(this.validatePV('registry'))
}
errors.pushObjects(this.validateLimitAndRequest('registry'))
if (get(this, 'config.databaseType') === INTERNAL) {
errors.pushObjects(this.validatePV('database'))
errors.pushObjects(this.validateLimitAndRequest('database'))
}
if (get(this, 'config.redisType') === INTERNAL) {
errors.pushObjects(this.validatePV('redis'))
errors.pushObjects(this.validateLimitAndRequest('redis'))
}
if (get(this, 'config.storageType') === 's3') {
S3_KEYS
.filter((k) => k !== 'regionendpoint' || k !== 'region')
.map((k) => {
const key = `imageChartStorage.s3.${ k }`
if (!get(this, key)) {
errors.pushObject(requiredError(`globalRegistryPage.config.s3.${ k }.label`))
}
})
}
if (get(this, 'config.databaseType') === 'external') {
DATABASE_EXTERNAL_KEYS.map((k) => {
const key = `database.external.${ k }`
if (!get(this, key)) {
errors.pushObject(requiredError(`globalRegistryPage.config.database.external.${ k }.label`))
}
})
}
if (get(this, 'config.redisType') === 'external') {
RDISE_EXTENNAL_KEYS
.filter((k) => k !== 'password')
.map((k) => {
const key = `redis.external.${ k }`
if (!get(this, key)) {
errors.pushObject(requiredError(`globalRegistryPage.config.redis.external.${ k }.label`))
}
})
}
if (get(this, 'config.notaryEnabled')) {
errors.pushObjects(this.validateLimitAndRequest('notaryServer'))
errors.pushObjects(this.validateLimitAndRequest('notarySigner'))
}
if (get(this, 'config.clairEnabled')) {
errors.pushObjects(this.validateLimitAndRequest('clair'))
}
return errors.uniq()
},
willSavePersistence(answers, component) {
PERSISTENCE_KEYS.map((k) => {
const key = `persistence.persistentVolumeClaim.${ component }.${ k }`
const useStorageClass = get(this, `use${ ucFirst(component) }StorageClass`)
if (['storageClass', 'size'].includes(k) && useStorageClass) {
answers[key] = get(this, key)
}
if (k === 'existingClaim' && !useStorageClass) {
answers[key] = get(this, key)
}
})
},
validatePV(component) {
const { storageClasses = [], intl } = this
const errors = []
const defaultStorageClasses = storageClasses.filter((s) => s.annotations && (s.annotations['storageclass.kubernetes.io/is-default-class'] === 'true' || s.annotations['storageclass.beta.kubernetes.io/is-default-class'] === 'true'))
if (get(this, `use${ ucFirst(component) }StorageClass`)) {
if (defaultStorageClasses.length === 0 && !get(this, `persistence.persistentVolumeClaim.${ component }.storageClass`)) {
const emptyError = intl.t('globalRegistryPage.config.storageClass.emptyError')
errors.pushObject(emptyError)
}
if (!get(this, `persistence.persistentVolumeClaim.${ component }.size`)) {
errors.pushObject(intl.t('globalRegistryPage.config.storageClass.sizeRequired', { component: ucFirst(component) }))
}
} else if (!get(this, `persistence.persistentVolumeClaim.${ component }.existingClaim`)){
errors.pushObject(requiredError(`clusterIstioPage.existingClaim.label`, { component: ucFirst(component) }))
}
return errors
},
doneSaving() {
this.setGlobalRegistryEnabled('true')
},
setGlobalRegistryEnabled(value, successCB) {
const globalStore = get(this, 'globalStore')
const globalRegistryEnabled = globalStore.all('setting').findBy('id', 'global-registry-enabled')
set(globalRegistryEnabled, 'value', value)
globalRegistryEnabled.save().then(() => {
if (successCB) {
successCB()
}
}).catch((err) => {
get(this, 'growl').fromError(get(this, 'intl').t('globalRegistryPage.globalRegistryEnabled.error'), err);
})
},
initWorkloads() {
WORKLOADS.map((w) => {
defineProperty(this, `${ w }SchedulableNodes`, computed(`${ w }NodeSelectors.[]`, 'cluster.nodes.@each.{allocatable,requested}', `config.${ w }Enabled`, () => {
return this.getSchedulableNodes(w)
}));
defineProperty(this, `insufficient${ ucFirst(w) }Cpu`, computed(`${ w }SchedulableNodes.@each.{allocatable,requested}`, `config.${ w }RequestCpu`, 'cluster.nodes.@each.{allocatable,requested}', () => {
return this.getComponentInsufficient(w, 'cpu')
}))
defineProperty(this, `insufficient${ ucFirst(w) }Memory`, computed(`${ w }SchedulableNodes.@each.{allocatable,requested}`, `config.${ w }RequestMemory`, 'cluster.nodes.@each.{allocatable,requested}', () => {
return this.getComponentInsufficient(w, 'memory')
}))
defineProperty(this, `${ w }Warning`, computed(`insufficient${ ucFirst(w) }Cpu`, `insufficient${ ucFirst(w) }Memory`, () => {
return this.getComponentWarning(w)
}))
});
},
getEnalbedWorkloads(answers) {
let out = ['registry.registry']
if (answers['redis.type'] === 'internal') {
out.push('redis.internal')
}
if (answers['database.type'] === 'internal') {
out.push('database.internal')
}
if (answers['notary.enabled'] === 'true') {
out = [...out, 'notary.server', 'notary.signer']
}
if (answers['clair.enabled'] === 'true') {
out.push('clair')
}
return out;
}
});