ui/lib/istio/addon/components/istio-catalog/component.js

984 lines
36 KiB
JavaScript

/* eslint-disable ember/no-arrow-function-computed-properties */
import Component from '@ember/component';
import layout from './template';
import {
get, computed, setProperties, set, defineProperty, observer
} from '@ember/object';
import { inject as service } from '@ember/service';
import CrudCatalog from 'shared/mixins/crud-catalog';
import { requiredError } from 'shared/utils/util';
import { convertToMillis, ucFirst } from 'shared/utils/util';
import { parseSi } from 'shared/utils/parse-unit'
import ReservationCheck from 'shared/mixins/reservation-check';
import { alias } from '@ember/object/computed'
import CatalogUpgrade from 'shared/mixins/catalog-upgrade';
import { allSettled } from 'rsvp';
import Semver from 'semver';
import { isEmpty } from '@ember/utils';
const GATEWAY_TYPE = ['NodePort', 'LoadBalancer'];
const PERSISTENCE_KEYS = ['existingClaim', 'size', 'storageClass']
const DEFAULT_HTTP2_PORT = 31380;
const DEFAULT_HTTPS_PORT = 31390;
const GATEWAY_ENABLED = 'gateways.enabled'
const HTTP2_PORT = 'gateways.istio-ingressgateway.ports[0].nodePort'
const HTTPS_PORT = 'gateways.istio-ingressgateway.ports[1].nodePort'
const LB_IP = 'gateways.istio-ingressgateway.loadBalancerIP'
const LB_SOURCE_RANGES = 'gateways.istio-ingressgateway.loadBalancerSourceRanges'
const PILOT_REQUEST_CPU = 'pilot.resources.requests.cpu'
const PILOT_REQUEST_MEM = 'pilot.resources.requests.memory'
const PILOT_LIMIT_CPU = 'pilot.resources.limits.cpu'
const PILOT_LIMIT_MEM = 'pilot.resources.limits.memory'
const PILOT_NODE_SELECTOR_PREFIX = 'pilot.nodeSelector.'
const PILOT_TOLERATION = 'pilot.tolerations'
const MIXER_REQUEST_CPU = 'mixer.telemetry.resources.requests.cpu'
const MIXER_REQUEST_MEM = 'mixer.telemetry.resources.requests.memory'
const MIXER_LIMIT_CPU = 'mixer.telemetry.resources.limits.cpu'
const MIXER_LIMIT_MEM = 'mixer.telemetry.resources.limits.memory'
const MIXER_NODE_SELECTOR_PREFIX = 'mixer.nodeSelector.'
const MIXER_TOLERATION = 'mixer.tolerations'
const POLICY_REQUEST_CPU = 'mixer.policy.resources.requests.cpu'
const POLICY_REQUEST_MEM = 'mixer.policy.resources.requests.memory'
const POLICY_LIMIT_CPU = 'mixer.policy.resources.limits.cpu'
const POLICY_LIMIT_MEM = 'mixer.policy.resources.limits.memory'
const GATEWAY_REQUEST_CPU = 'gateways.istio-ingressgateway.resources.requests.cpu'
const GATEWAY_REQUEST_MEM = 'gateways.istio-ingressgateway.resources.requests.memory'
const GATEWAY_LIMIT_CPU = 'gateways.istio-ingressgateway.resources.limits.cpu'
const GATEWAY_LIMIT_MEM = 'gateways.istio-ingressgateway.resources.limits.memory'
const GATEWAY_NODE_SELECTOR_PREFIX = 'gateways.istio-ingressgateway.nodeSelector.'
const GATEWAY_TOLERATION = 'gateways.istio-ingressgateway.tolerations'
const TRACING_REQUEST_CPU = 'tracing.jaeger.resources.requests.cpu'
const TRACING_REQUEST_MEM = 'tracing.jaeger.resources.requests.memory'
const TRACING_LIMIT_CPU = 'tracing.jaeger.resources.limits.cpu'
const TRACING_LIMIT_MEM = 'tracing.jaeger.resources.limits.memory'
const TRACING_NODE_SELECTOR_PREFIX = 'tracing.nodeSelector.'
const TRACING_TOLERATION = 'tracing.tolerations'
const MEMBERS = 'global.members'
const MEMBER_USER = 'User'
const MEMBER_GROUP = 'Group'
const MEMBER_SYSYTEM = 'system:authenticated'
const ANSWER_TO_CONFIG = {
'tracing.enabled': 'tracingEnabled',
[PILOT_REQUEST_CPU]: 'pilotRequestCpu',
[PILOT_REQUEST_MEM]: 'pilotRequestMemory',
[PILOT_LIMIT_MEM]: 'pilotLimitMemory',
[PILOT_LIMIT_CPU]: 'pilotLimitCpu',
[GATEWAY_ENABLED]: 'gatewayEnabled',
'gateways.istio-ingressgateway.type': 'gatewayType',
[LB_IP]: 'loadBalancerIP',
[MIXER_REQUEST_MEM]: 'mixerTelemetryRequestMemory',
[MIXER_LIMIT_MEM]: 'mixerTelemetryLimitMemory',
[MIXER_REQUEST_CPU]: 'mixerTelemetryRequestCpu',
[MIXER_LIMIT_CPU]: 'mixerTelemetryLimitCpu',
'pilot.traceSampling': 'traceSampling',
'mixer.policy.enabled': 'mixerPolicyEnabled',
'mtls.enabled': 'mtlsEnabled',
[TRACING_REQUEST_CPU]: 'tracingRequestCpu',
[TRACING_REQUEST_MEM]: 'tracingRequestMemory',
[TRACING_LIMIT_CPU]: 'tracingLimitCpu',
[TRACING_LIMIT_MEM]: 'tracingLimitMemory',
[GATEWAY_REQUEST_CPU]: 'gatewayRequestCpu',
[GATEWAY_REQUEST_MEM]: 'gatewayRequestMemory',
[GATEWAY_LIMIT_CPU]: 'gatewayLimitCpu',
[GATEWAY_LIMIT_MEM]: 'gatewayLimitMemory',
[POLICY_REQUEST_MEM]: 'mixerPolicyRequestMemory',
[POLICY_LIMIT_MEM]: 'mixerPolicyLimitMemory',
[POLICY_REQUEST_CPU]: 'mixerPolicyRequestCpu',
[POLICY_LIMIT_CPU]: 'mixerPolicyLimitCpu',
}
const HIDDEN_KEYS = {
'enableCRDs': true,
'mixer.enabled': true,
'pilot.enabled': true,
'security.enabled': true,
'nodeagent.enabled': false,
'istio_cni.enabled': false,
'istiocoredns.enabled': false,
'sidecarInjectorWebhook.enabled': true,
'kiali.enabled': true,
'galley.enabled': true,
'certmanager.enabled': false,
'global.rancher.domain': '',
'global.rancher.clusterId': '',
'global.monitoring.type': 'cluster-monitoring',
}
const NODE_PORT_KEYS = {
'gateways.istio-ingressgateway.ports[0].name': 'http2',
'gateways.istio-ingressgateway.ports[0].port': 80,
'gateways.istio-ingressgateway.ports[1].name': 'https',
'gateways.istio-ingressgateway.ports[1].port': 443,
}
const WORKLOADS = ['mixerTelemetry', 'tracing', 'gateway', 'pilot', 'mixerPolicy']
const ISTIO_TEMPLATE = 'system-library-rancher-istio';
const NODE_EXPORTER_CPU = 100;
const NODE_EXPORTER_MEMORY = 30;
const MONITORING_MIN_CPU = 650
const MONITORING_MIN_MEMORY = 650
const PROMETHEUS_DEFAULT_CPU = 750
const PROMETHEUS_DEFAULT_MEMORY = 750
const MONITORING_CLUSTER_CPU = PROMETHEUS_DEFAULT_CPU + MONITORING_MIN_CPU;
const MONITORING_CLUSTER_MEMORY = PROMETHEUS_DEFAULT_MEMORY + MONITORING_MIN_MEMORY;
const ISTIO_CLUSTER_CPU = 500;
const ISTIO_CLUSTER_MEMORY = 500;
const ISTIO_BREAKING_VERSION = '1.4.2';
export default Component.extend(CrudCatalog, ReservationCheck, CatalogUpgrade, {
settings: service(),
scope: service(),
intl: service(),
grafana: service(),
modalService: service('modal'),
layout,
answers: null,
appName: 'cluster-istio',
nsName: 'istio-system',
templateId: ISTIO_TEMPLATE,
templateName: 'rancher-istio',
level: alias('scope.currentPageScope'),
cluster: alias('scope.currentCluster'),
enableClusterMonitoring: alias('scope.currentCluster.enableClusterMonitoring'),
init() {
this._super(...arguments);
this.initConfig();
this.initWorkloads();
if ( get(this, 'enabled') ) {
this.initAnswers();
} else {
set(this, 'customAnswers', { ...HIDDEN_KEYS });
}
},
actions: {
save(cb) {
if (!get(this, 'enableClusterMonitoring')) {
this.enableMonitoring()
}
set(this, 'errors', [])
const errors = this.validate() || []
if (errors.length > 0) {
set(this, 'errors', errors)
cb()
return
}
let answers = {
'global.rancher.domain': window.location.host,
'global.rancher.clusterId': get(this, 'cluster.id'),
};
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 PILOT_REQUEST_CPU:
case PILOT_LIMIT_CPU:
case MIXER_REQUEST_CPU:
case MIXER_LIMIT_CPU:
case TRACING_REQUEST_CPU:
case TRACING_LIMIT_CPU:
case GATEWAY_REQUEST_CPU:
case GATEWAY_LIMIT_CPU:
case POLICY_REQUEST_CPU:
case POLICY_LIMIT_CPU:
answers[key] = `${ value }m`
break;
case PILOT_REQUEST_MEM:
case PILOT_LIMIT_MEM:
case MIXER_REQUEST_MEM:
case MIXER_LIMIT_MEM:
case TRACING_REQUEST_MEM:
case TRACING_LIMIT_MEM:
case GATEWAY_REQUEST_MEM:
case GATEWAY_LIMIT_MEM:
case POLICY_REQUEST_MEM:
case POLICY_LIMIT_MEM:
answers[key] = `${ value }Mi`
break;
default:
answers[key] = value
}
});
['tracing', 'gateway', 'pilot', 'mixer'].map((component) => {
(get(this, `${ component }NodeSelectors`) || []).map((selector) => {
let { key, value } = selector
if (key.includes('.')) {
key = key.replace(/\./g, '\\.')
}
if (component === 'gateway') {
answers[`gateways.istio-ingressgateway.nodeSelector.${ key }`] = value
} else {
answers[`${ component }.nodeSelector.${ key }`] = value
}
});
})
if (get(this, 'config.gatewayEnabled') && get(this, 'config.gatewayType') === 'LoadBalancer') {
(get(this, 'loadBalancerSourceRanges') || []).map((value, idx) => {
answers[`${ LB_SOURCE_RANGES }[${ idx }]`] = value
});
}
if (get(this, 'config.gatewayEnabled') && get(this, 'config.gatewayType') === 'NodePort') {
(Object.keys(NODE_PORT_KEYS) || []).map((key) => {
answers[key] = NODE_PORT_KEYS[key]
})
answers[HTTP2_PORT] = get(this, 'config.http2Port')
answers[HTTPS_PORT] = get(this, 'config.httpsPort')
}
const users = get(this, 'globalStore').all('user');
if (get(this, 'allowSystemGroup')) {
answers[`${ MEMBERS }[0].kind`] = MEMBER_GROUP
answers[`${ MEMBERS }[0].name`] = MEMBER_SYSYTEM
} else {
(get(this, 'members') || []).map((m = {}, idx) => {
const { principalType, id } = m
let name = id
if (principalType === 'user') {
const filtered = users.filter((u = {}) => u.principalIds.includes(id)).get('firstObject')
if (filtered) {
name = get(filtered, 'id')
} else {
return
}
}
answers[`${ MEMBERS }[${ idx }].kind`] = ucFirst(principalType)
answers[`${ MEMBERS }[${ idx }].name`] = name
})
}
['gateway', 'pilot', 'mixer', 'tracing'].map((component) => {
(get(this, `${ component }Tolerations`) || []).map((t, index) => {
Object.keys(t).map((key) => {
if (t[key]) {
if (component === 'gateway') {
answers[`gateways.istio-ingressgateway.tolerations[${ index }].${ key }`] = t[key]
} else {
answers[`${ component }.tolerations[${ index }].${ key }`] = t[key]
}
}
})
});
})
this.save(cb, answers);
},
addAuthorizedPrincipal(principal) {
if (principal) {
let { members = [], globalStore } = this
members.pushObject(globalStore.createRecord(principal));
set(this, 'members', members);
}
},
removeMember(member) {
let { members = [] } = this;
members.removeObject(member);
},
disable() {
const { app, modalService } = this
modalService.toggleModal('modal-delete-istio', {
escToClose: true,
istioApp: app,
});
},
},
workloadEnabledChange: observer('config.tracingEnabled', 'config.gatewayEnabled', 'config.mixerPolicyEnabled', function() {
['tracing', 'gateway'].map((w) => {
if (!get(this, `config.${ w }Enabled`)) {
set(this, `${ w }NodeSelectors`, [])
}
})
this.notifyPropertyChange('requestsCpu')
this.notifyPropertyChange('requestsMemory')
this.notifyPropertyChange('saveDisabled')
}),
gatewayTypeContent: computed(() => {
return GATEWAY_TYPE.map((value) => ({
label: value,
value
}))
}),
kialiUrl: computed('cluster.id', 'templateVersion', function() {
const {
cluster: { id: clusterId },
templateVersion
} = this;
let kialiPort = '-http:80';
if (!isEmpty(templateVersion) && Semver.gte(templateVersion, ISTIO_BREAKING_VERSION) ) {
kialiPort = ':20001';
}
return `/k8s/clusters/${ clusterId }/api/v1/namespaces/istio-system/services/http:kiali${ kialiPort }/proxy/`
}),
jaegerUrl: computed('cluster.id', function() {
return `/k8s/clusters/${ get(this, 'cluster.id') }/api/v1/namespaces/istio-system/services/http:tracing:80/proxy/jaeger/search`
}),
prometheusUrl: computed('cluster.id', function() {
return `/k8s/clusters/${ get(this, 'cluster.id') }/api/v1/namespaces/cattle-prometheus/services/http:access-prometheus:80/proxy/`
}),
saveDisabled: computed('mixerTelemetryWarning', 'enabled', 'istioWarning', 'pilotWarning', 'tracingWarning', 'gatewayWarning', 'mixerPolicyWarning', 'mixerWarning', function() {
return [...WORKLOADS, 'mixer'].reduce((out, w) => {
if (['gateway', 'tracing', 'mixerPolicy'].includes(w) && !get(this, `config.${ w }Enabled`)) {
return out || (get(this, `${ w }Warning`) || false)
} else if (w === 'mixerTelemetry') {
return out || (get(this, 'mixerTelemetryWarning') || false) || (get(this, 'mixerWarning') )
} else {
return out || (get(this, `${ w }Warning`) || false)
}
}, false) || (get(this, 'istioWarning') || false)
}),
canReuse: computed('monitoringApp.externalIdInfo.version', function() {
const version = get(this, 'monitoringApp.externalIdInfo.version')
const cannotReuseVersion = '0.0.3'
return Semver.gt(Semver.coerce(version), Semver.coerce(cannotReuseVersion))
}),
istioWarning: computed('insufficientCpu', 'insufficientMemory', function() {
let {
insufficientCpu, insufficientMemory, intl, minCpu, minMemory, enabled
} = this
const prefix = 'clusterIstioPage.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,
})
}
return;
}),
enabled: computed('app.state', function() {
return !!get(this, 'app') && get(this, 'app.state') !== 'removing'
}),
nsNeedMove: computed('namespace.projectId', 'project.id', function() {
const { namespace = {}, project = {} } = this
return namespace.projectId !== project.id
}),
requestsCpu: computed('config.mixerTelemetryRequestCpu', 'config.pilotRequestCpu', 'config.gatewayRequestCpu', 'config.tracingRequestCpu', 'config.mixerPolicyRequestCpu', function() {
return WORKLOADS
.filter((w) => {
if (['gateway', 'tracing', 'mixerPolicy'].includes(w) && !get(this, `config.${ w }Enabled`)) {
return false
}
return true
})
.reduce((all, w) => {
return all + parseInt(get(this, `config.${ w }RequestCpu`) || 0)
}, 0)
}),
requestsMemory: computed('config.mixerTelemetryRequestMemory', 'config.pilotRequestMemory', 'config.gatewayRequestMemory', 'config.tracingRequestMemory', 'config.mixerPolicyRequestMemory', function() {
return WORKLOADS
.filter((w) => {
if (['gateway', 'tracing', 'mixerPolicy'].includes(w) && !get(this, `config.${ w }Enabled`)) {
return false
}
return true
})
.reduce((all, w) => {
return all + parseInt(get(this, `config.${ w }RequestMemory`) || 0)
}, 0)
}),
mixerSchedulableNodes: computed('mixerNodeSelectors.[]', 'cluster.nodes.@each.{allocatable,requested}', 'config.mixerPolicyEnabled', function() {
return this.getSchedulableNodes('mixer')
}),
insufficientMixerCpu: computed('mixerSchedulableNodes.@each.{allocatable,requested}', 'config.mixerPolicyEnabled', 'config.mixerTelemetryRequestCpu', 'config.mixerPolicyRequestCpu', 'cluster.nodes.@each.{allocatable,requested}', function() {
let reservation
if (get(this, 'config.mixerPolicyEnabled')) {
reservation = Math.max(parseInt(get(this, 'config.mixerTelemetryRequestCpu') || '0'), parseInt(get(this, 'config.mixerPolicyRequestCpu') || '0'))
} else {
reservation = parseInt(get(this, 'config.mixerTelemetryRequestCpu') || '0')
}
return this.getComponentInsufficient('mixer', 'cpu', reservation)
}),
insufficientMixerMemory: computed('mixerSchedulableNodes.@each.{allocatable,requested}', 'config.mixerPolicyEnabled', 'config.mixerTelemetryRequestMemory', 'config.mixerPolicyRequestMemory', 'cluster.nodes.@each.{allocatable,requested}', function() {
let reservation
if (get(this, 'config.mixerPolicyEnabled')) {
reservation = Math.max(parseInt(get(this, 'config.mixerTelemetryRequestMemory') || '0'), parseInt(get(this, 'config.mixerPolicyRequestMemory') || '0'))
} else {
reservation = parseInt(get(this, 'config.mixerTelemetryRequestMemory') || '0')
}
return this.getComponentInsufficient('mixer', 'memory', reservation)
}),
mixerWarning: computed('config.{mixerPolicyEnabled,mixerPolicyRequestCpu,mixerPolicyRequestMemory,mixerTelemetryRequestCpu,mixerTelemetryRequestMemory}', 'insufficientMixerCpu', 'insufficientMixerMemory', 'insufficientMixerTelemetryCpu', 'insufficientMixerTelemetryMemory', 'mixerNodeSelectors.[]', function() {
if ((get(this, 'mixerNodeSelectors') || []).length === 0 ) {
return
}
const displayName = get(this, 'config.mixerPolicyEnabled') ? get(this, 'intl').t('clusterIstioPage.telemetryAndPolicy') : undefined
let componentCpu
let componentMemory
if (get(this, 'config.mixerPolicyEnabled')) {
componentCpu = Math.max(parseInt(get(this, 'config.mixerTelemetryRequestCpu') || '0'), parseInt(get(this, 'config.mixerPolicyRequestCpu') || '0'))
componentMemory = Math.max(parseInt(get(this, 'config.mixerTelemetryRequestMemory') || '0'), parseInt(get(this, 'config.mixerPolicyRequestMemory') || '0'))
} else {
componentCpu = parseInt(get(this, 'config.mixerTelemetryRequestCpu') || '0')
componentMemory = parseInt(get(this, 'config.mixerTelemetryRequestMemory') || '0')
}
return this.getComponentWarning('mixer', componentCpu, componentMemory, displayName)
}),
istioVersions: computed('availableVersions', 'templateLables', function() {
const { availableVersions = [], templateLables = {} } = this
return availableVersions.map((v) => {
const key = `rancher.istio.v${ v.value }`
return {
label: `${ v.label } (Istio ${ templateLables[key] })`,
value: v.value,
}
})
}),
monitoringApps: computed('monitoringApp', function() {
return [get(this, 'monitoringApp')]
}),
clusterLevelMinCpu: computed('cluster.{enableClusterMonitoring,nodes}', 'enableClusterMonitoring', function() {
if (get(this, 'enableClusterMonitoring')) {
return ISTIO_CLUSTER_CPU
} else {
const allNodes = get(this, 'cluster.nodes') || [];
const schedulableNodes = allNodes.filterBy('isUnschedulable', false);
return ISTIO_CLUSTER_CPU + MONITORING_CLUSTER_CPU + get(schedulableNodes, 'length') * NODE_EXPORTER_CPU;
}
}),
clusterLevelMinMemory: computed('cluster.enableClusterMonitoring', 'enableClusterMonitoring', 'scope.currentCluster.nodes', function() {
if (get(this, 'enableClusterMonitoring')) {
return ISTIO_CLUSTER_MEMORY
} else {
const allNodes = get(this, 'scope.currentCluster.nodes') || [];
const schedulableNodes = allNodes.filterBy('isUnschedulable', false);
return ISTIO_CLUSTER_MEMORY + MONITORING_CLUSTER_MEMORY + get(schedulableNodes, 'length') * NODE_EXPORTER_MEMORY;
}
}),
canEnable: computed('cluster.isWindows', function() {
if (get(this, 'cluster.isWindows')) {
return false
} else {
return true
}
}),
willSavePersistence(answers, component) {
PERSISTENCE_KEYS.map((k) => {
const key = `${ component }.persistence.${ 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)
}
})
},
validate() {
const errors = [];
['pilot', 'mixerTelemetry'].map((w) => {
errors.pushObjects(this.validateLimitAndRequest(w))
})
if (get(this, 'config.gatewayEnabled')) {
errors.pushObjects(this.validateLimitAndRequest('gateway'))
}
if (get(this, 'config.tracingEnabled')) {
errors.pushObjects(this.validateLimitAndRequest('tracing'))
}
if (get(this, 'config.mixerPolicyEnabled')) {
errors.pushObjects(this.validateLimitAndRequest('mixerPolicy'))
}
['traceSampling'].map((field) => {
if (!get(this, `config.${ field }`)) {
errors.pushObject(requiredError(`clusterIstioPage.config.${ field }.label`))
}
})
if (get(this, 'config.gatewayEnabled') && get(this, 'config.gatewayType') === 'NodePort') {
['http2Port', 'httpsPort'].map((field) => {
if (!get(this, `config.${ field }`)) {
errors.pushObject(requiredError(`clusterIstioPage.config.${ field }.label`))
}
})
}
return errors
},
validatePV(component) {
const { intl, storageClasses = [] } = 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, `${ component }.persistence.storageClass`)) {
const emptyError = intl.t('globalRegistryPage.config.storageClass.emptyError')
errors.pushObject(emptyError)
}
if (!get(this, `${ component }.persistence.size`)) {
errors.pushObject(intl.t('globalRegistryPage.config.storageClass.sizeRequired', { component: ucFirst(component) }))
}
} else if (!get(this, `${ component }.persistence.existingClaim`)){
errors.pushObject(requiredError(`clusterIstioPage.existingClaim.label`, { component: ucFirst(component) }))
}
return errors
},
initConfig() {
const config = {
tracingEnabled: true,
kialiEnabled: true,
autoInject: true,
mtlsEnabled: false,
gatewayType: 'NodePort',
gatewayEnabled: false,
http2Port: DEFAULT_HTTP2_PORT,
httpsPort: DEFAULT_HTTPS_PORT,
mixerTelemetryRequestCpu: 1000,
mixerTelemetryLimitCpu: 4800,
mixerTelemetryRequestMemory: 1024,
mixerTelemetryLimitMemory: 4096,
traceSampling: 1,
mixerPolicyEnabled: true,
pilotRequestCpu: 500,
pilotRequestMemory: 2048,
pilotLimitCpu: 1000,
pilotLimitMemory: 4096,
gatewayRequestCpu: 100,
gatewayLimitCpu: 2000,
gatewayRequestMemory: 128,
gatewayLimitMemory: 1024,
tracingRequestCpu: 100,
tracingLimitCpu: 500,
tracingRequestMemory: 100,
tracingLimitMemory: 1024,
mixerPolicyRequestCpu: 1000,
mixerPolicyLimitCpu: 4800,
mixerPolicyRequestMemory: 1024,
mixerPolicyLimitMemory: 4096,
}
setProperties(this, {
config,
allowSystemGroup: false,
});
},
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)
}))
});
},
initAnswers() {
let customAnswers = {};
const answers = get(this, 'app.answers') || {};
const answerKeys = Object.keys(ANSWER_TO_CONFIG) || []
const mixerNodeSelector = {};
const pilotNodeSelector = {}
const gatewayNodeSelector = {}
const tracingNodeSelector = {}
const loadBalancerSourceRanges = [];
Object.keys(answers).filter((key) => key.startsWith(MIXER_NODE_SELECTOR_PREFIX) ).map((k) => {
let value = answers[k] || '';
const key = k.replace(MIXER_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
mixerNodeSelector[key] = value
});
Object.keys(answers).filter((key) => key.startsWith(PILOT_NODE_SELECTOR_PREFIX) ).map((k) => {
let value = answers[k] || '';
const key = k.replace(PILOT_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
pilotNodeSelector[key] = value
});
Object.keys(answers).filter((key) => key.startsWith(GATEWAY_NODE_SELECTOR_PREFIX) ).map((k) => {
let value = answers[k] || '';
const key = k.replace(GATEWAY_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
gatewayNodeSelector[key] = value
});
Object.keys(answers).filter((key) => key.startsWith(TRACING_NODE_SELECTOR_PREFIX) ).map((k) => {
let value = answers[k] || '';
const key = k.replace(TRACING_NODE_SELECTOR_PREFIX, '').replace(/\\\./g, '.')
tracingNodeSelector[key] = value
});
Object.keys(answers).filter((key) => key.startsWith(`${ LB_SOURCE_RANGES }[`) ).map((k) => {
loadBalancerSourceRanges.pushObject(answers[k])
})
const pilotTolerations = []
const mixerTolerations = []
const gatewayTolerations = []
const tracingTolerations = []
const pilotTolerationKeys = Object.keys(answers).filter((key) => key.startsWith(PILOT_TOLERATION) )
const pilotTolerationIndexs = pilotTolerationKeys.map((k) => {
return k.replace(`${ PILOT_TOLERATION }[`, '').split('].').get('firstObject')
}).uniq()
pilotTolerationIndexs.map((idx) => {
pilotTolerations.pushObject({
key: answers[`${ PILOT_TOLERATION }[${ idx }].key`] || '',
operator: answers[`${ PILOT_TOLERATION }[${ idx }].operator`] || '',
value: answers[`${ PILOT_TOLERATION }[${ idx }].value`] || '',
effect: answers[`${ PILOT_TOLERATION }[${ idx }].effect`] || '',
tolerationSeconds: answers[`${ PILOT_TOLERATION }[${ idx }].tolerationSeconds`] || '',
})
})
const mixerTolerationKeys = Object.keys(answers).filter((key) => key.startsWith(MIXER_TOLERATION) )
const mixerTolerationIndexs = mixerTolerationKeys.map((k) => {
return k.replace(`${ MIXER_TOLERATION }[`, '').split('].').get('firstObject')
}).uniq()
mixerTolerationIndexs.map((idx) => {
mixerTolerations.pushObject({
key: answers[`${ MIXER_TOLERATION }[${ idx }].key`] || '',
operator: answers[`${ MIXER_TOLERATION }[${ idx }].operator`] || '',
value: answers[`${ MIXER_TOLERATION }[${ idx }].value`] || '',
effect: answers[`${ MIXER_TOLERATION }[${ idx }].effect`] || '',
tolerationSeconds: answers[`${ MIXER_TOLERATION }[${ idx }].tolerationSeconds`] || '',
})
})
const gatewayTolerationKeys = Object.keys(answers).filter((key) => key.startsWith(GATEWAY_TOLERATION) )
const gatewayTolerationIndexs = gatewayTolerationKeys.map((k) => {
return k.replace(`${ GATEWAY_TOLERATION }[`, '').split('].').get('firstObject')
}).uniq()
gatewayTolerationIndexs.map((idx) => {
gatewayTolerations.pushObject({
key: answers[`${ GATEWAY_TOLERATION }[${ idx }].key`] || '',
operator: answers[`${ GATEWAY_TOLERATION }[${ idx }].operator`] || '',
value: answers[`${ GATEWAY_TOLERATION }[${ idx }].value`] || '',
effect: answers[`${ GATEWAY_TOLERATION }[${ idx }].effect`] || '',
tolerationSeconds: answers[`${ GATEWAY_TOLERATION }[${ idx }].tolerationSeconds`] || '',
})
})
const tracingTolerationKeys = Object.keys(answers).filter((key) => key.startsWith(TRACING_TOLERATION) )
const tracingTolerationIndexs = tracingTolerationKeys.map((k) => {
return k.replace(`${ TRACING_TOLERATION }[`, '').split('].').get('firstObject')
}).uniq()
tracingTolerationIndexs.map((idx) => {
tracingTolerations.pushObject({
key: answers[`${ TRACING_TOLERATION }[${ idx }].key`] || '',
operator: answers[`${ TRACING_TOLERATION }[${ idx }].operator`] || '',
value: answers[`${ TRACING_TOLERATION }[${ idx }].value`] || '',
effect: answers[`${ TRACING_TOLERATION }[${ idx }].effect`] || '',
tolerationSeconds: answers[`${ TRACING_TOLERATION }[${ idx }].tolerationSeconds`] || '',
})
})
this.updateCpuMemoryPreRequest()
setProperties(this, {
mixerNodeSelector,
pilotNodeSelector,
gatewayNodeSelector,
tracingNodeSelector,
loadBalancerSourceRanges,
'config.http2Port': answers[HTTP2_PORT] || DEFAULT_HTTP2_PORT,
'config.httpsPort': answers[HTTPS_PORT] || DEFAULT_HTTPS_PORT,
pilotTolerations,
mixerTolerations,
gatewayTolerations,
tracingTolerations,
})
this.initMembers()
Object.keys(answers).forEach((key = '') => {
if (key.startsWith(MIXER_NODE_SELECTOR_PREFIX)
|| key.startsWith(`${ LB_SOURCE_RANGES }[`)
|| key.startsWith(PILOT_NODE_SELECTOR_PREFIX)
|| key.startsWith(GATEWAY_NODE_SELECTOR_PREFIX)
|| key.startsWith(TRACING_NODE_SELECTOR_PREFIX)
|| key.startsWith(`${ MEMBERS }[`)
|| key.startsWith(`${ PILOT_TOLERATION }`)
|| key.startsWith(`${ MIXER_TOLERATION }`)
|| key.startsWith(`${ GATEWAY_TOLERATION }`)
|| key.startsWith(`${ TRACING_TOLERATION }`)
) {
return
}
if (Object.keys(NODE_PORT_KEYS).includes(key) || key === HTTP2_PORT || key === HTTPS_PORT) {
return
}
if (answerKeys.includes(key)) {
let value
switch (key) {
case PILOT_REQUEST_CPU:
case MIXER_REQUEST_CPU:
case MIXER_LIMIT_CPU:
case PILOT_LIMIT_CPU:
case TRACING_REQUEST_CPU:
case TRACING_LIMIT_CPU:
case GATEWAY_REQUEST_CPU:
case GATEWAY_LIMIT_CPU:
case POLICY_REQUEST_CPU:
case POLICY_LIMIT_CPU:
value = convertToMillis(answers[key] || '0')
break;
case PILOT_REQUEST_MEM:
case PILOT_LIMIT_MEM:
case MIXER_REQUEST_MEM:
case MIXER_LIMIT_MEM:
case TRACING_REQUEST_MEM:
case TRACING_LIMIT_MEM:
case GATEWAY_REQUEST_MEM:
case GATEWAY_LIMIT_MEM:
case POLICY_REQUEST_MEM:
case POLICY_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];
setProperties(this, { customAnswers, })
});
},
getEnalbedWorkloads(answers) {
const out = []
if (answers['pilot.enabled'] === 'true') {
out.push('pilot')
}
if (answers['mixer.enabled'] === 'true') {
out.push('mixer.telemetry')
}
if (answers['mixer.policy.enabled'] === 'true') {
out.push('mixer.policy')
}
if (answers['gateways.enabled'] === 'true') {
out.push('gateways.istio-ingressgateway')
}
if (answers['tracing.enabled'] === 'true') {
out.push('tracing')
}
return out;
},
doneSaving() {
this.updateCpuMemoryPreRequest()
},
initMembers() {
const { answers = {} } = get(this, 'app');
const { globalStore } = this
if (answers[`${ MEMBERS }[0].name`] === MEMBER_SYSYTEM && answers[`${ MEMBERS }[0].kind`] === 'Group') {
set(this, 'allowSystemGroup', true)
} else {
set(this, 'allowSystemGroup', false)
const users = globalStore.all('user')
const keys = Object.keys(answers).filter((key = '') => /^global.members\[\d\].name$/g.test(key))
const members = (keys || [])
.filter((key) => {
const kindKey = key.replace('.name', '.kind')
const kind = answers[kindKey]
return !(kind === MEMBER_GROUP && answers[key] === MEMBER_SYSYTEM)
})
.map((key) => {
const kindKey = key.replace('.name', '.kind')
const kind = answers[kindKey]
let id = answers[key]
if (kind === MEMBER_USER) {
const filtered = users.filter((u) => (u.principalIds || []).includes(`local://${ id }`)).get('firstObject')
if (filtered && filtered.principalIds) {
const principalIds = filtered.principalIds || []
if (principalIds.length > 1) {
id = principalIds.filter((f) => f !== `local://${ id }`).get('firstObject')
} else {
id = filtered.principalIds.get('firstObject')
}
}
}
return get(this, 'globalStore').createRecord({
type: 'member',
id,
})
})
const membersPromises = (members || []).map((m) => globalStore.find('principal', m.id));
allSettled(membersPromises).then((res) => {
if (this.isDestroyed || this.isDestroying) {
return;
}
set(this, 'members', res.map((xhr) => {
if (xhr.state === 'fulfilled') {
return xhr.value;
}
}))
})
}
},
enableMonitoring() {
const resource = get(this, 'cluster');
let answers = {}
answers['operator-init.enabled'] = 'true';
answers['exporter-node.enabled'] = 'true';
answers['exporter-node.ports.metrics.port'] = '9796';
answers['exporter-kubelets.https'] = `${ !(get(this, 'scope.currentCluster.isGKE') || get(this, 'scope.currentCluster.isAKS')) }`;
answers['exporter-node.resources.limits.cpu'] = '200m';
answers['exporter-node.resources.limits.memory'] = '200Mi';
answers['operator.resources.limits.memory'] = '500Mi';
answers['prometheus.retention'] = '12h';
answers['grafana.persistence.enabled'] = 'false';
answers['prometheus.persistence.enabled'] = 'false';
answers['prometheus.persistence.storageClass'] = 'default';
answers['grafana.persistence.storageClass'] = 'default';
answers['grafana.persistence.size'] = '10Gi';
answers['prometheus.persistence.size'] = '50Gi';
answers['prometheus.resources.core.requests.cpu'] = '750m';
answers['prometheus.resources.core.limits.cpu'] = '1000m';
answers['prometheus.resources.core.requests.memory'] = '750Mi';
answers['prometheus.resources.core.limits.memory'] = '1000Mi';
answers['prometheus.persistent.useReleaseName'] = 'true'
return resource.doAction('enableMonitoring', { answers })
},
});