Add pkg/harvester components + shell portings - 1

Signed-off-by: Francesco Torchia <francesco.torchia@suse.com>
This commit is contained in:
Francesco Torchia
2024-10-23 17:00:46 +02:00
parent f8408469f7
commit 4f2688f6ab
265 changed files with 42316 additions and 82 deletions
@@ -0,0 +1,58 @@
<script>
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { RadioGroup } from '@components/Form/Radio';
import CreateEditView from '@shell/mixins/create-edit-view';
export default {
name: 'EditGenericAddon',
components: {
Tabbed,
Tab,
RadioGroup,
},
mixins: [CreateEditView],
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
},
};
</script>
<template>
<Tabbed :side-tabs="true">
<Tab
name="basic"
:label="t('generic.basic')"
:weight="99"
>
<RadioGroup
v-model="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
</Tab>
</Tabbed>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>
@@ -0,0 +1,58 @@
<script>
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { RadioGroup } from '@components/Form/Radio';
import CreateEditView from '@shell/mixins/create-edit-view';
export default {
name: 'EditSeederAddon',
components: {
Tabbed,
Tab,
RadioGroup,
},
mixins: [CreateEditView],
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
},
};
</script>
<template>
<Tabbed :side-tabs="true">
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="99"
>
<RadioGroup
v-model="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
</Tab>
</Tabbed>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>
@@ -0,0 +1,47 @@
<script>
import CreateEditView from '@shell/mixins/create-edit-view';
import CruResource from '@shell/components/CruResource';
export default {
name: 'EditAddon',
components: { CruResource },
mixins: [CreateEditView],
props: {
value: {
type: Object,
required: true,
}
},
computed: {
currentComponent() {
const name = this.value.metadata.name;
try {
return require(`./${ name }.vue`).default;
} catch {
return require(`./generic.vue`).default;
}
},
},
};
</script>
<template>
<CruResource
:resource="value"
:mode="mode"
:errors="errors"
@finish="save"
>
<component
:is="currentComponent"
:value="value"
:register-before-hook="registerBeforeHook"
:mode="mode"
/>
</CruResource>
</template>
@@ -0,0 +1,156 @@
<script>
import merge from 'lodash/merge';
import jsyaml from 'js-yaml';
import { clone } from '@shell/utils/object';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { RadioGroup } from '@components/Form/Radio';
import { LabeledInput } from '@components/Form/LabeledInput';
import { Banner } from '@components/Banner';
import CreateEditView from '@shell/mixins/create-edit-view';
const DEFAULT_VALUE = { image: { repo: 'rancher/harvester-nvidia-driver-toolkit' } };
export default {
name: 'EditAddonNvidiaDriverToolkit',
components: {
Banner,
LabeledInput,
RadioGroup,
Tabbed,
Tab,
},
mixins: [CreateEditView],
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
},
data() {
return {
initSpec: clone(this.value.spec),
valuesContentJson: this.parseValuesContent()
};
},
watch: {
valuesContentJson: {
handler(neu) {
this.update(neu);
},
deep: true,
}
},
computed: {
parsingSpecError() {
return this.valuesContentJson && (this.valuesContentJson.image === undefined || this.valuesContentJson.driverLocation === undefined);
}
},
methods: {
parseValuesContent() {
try {
return merge({}, DEFAULT_VALUE, jsyaml.load(this.value.spec.valuesContent));
} catch (err) {
this.$store.dispatch('growl/fromError', {
title: this.$store.getters['i18n/t']('generic.notification.title.error'),
err: err.data || err,
}, { root: true });
return DEFAULT_VALUE;
}
},
toggleEnable(v) {
this.resetSpec();
this.value.spec.enabled = v;
},
resetSpec() {
this.value.spec = clone(this.initSpec);
this.valuesContentJson = this.parseValuesContent();
},
update(values) {
this.value.spec.valuesContent = jsyaml.dump(values);
}
}
};
</script>
<template>
<div>
<Banner
v-if="parsingSpecError"
color="error"
:label="t('harvester.addons.nvidiaDriverToolkit.parsingSpecError', null, { raw: true })"
/>
<Tabbed v-else :side-tabs="true">
<Tab
name="basic"
:label="t('harvester.addons.nvidiaDriverToolkit.titles.basic')"
:weight="1"
>
<RadioGroup
:value="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
@input="toggleEnable"
/>
<div v-if="value.spec.enabled">
<div v-if="valuesContentJson.image" class="row mb-15">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.image.repo"
:mode="mode"
:required="true"
label-key="harvester.addons.nvidiaDriverToolkit.image.repository"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.image.tag"
:mode="mode"
:required="true"
class="col span-6"
label-key="harvester.addons.nvidiaDriverToolkit.image.tag"
/>
</div>
</div>
<div class="row mb-15">
<LabeledInput
v-model="valuesContentJson.driverLocation"
:mode="mode"
:required="true"
label-key="harvester.addons.nvidiaDriverToolkit.driver.location"
/>
</div>
</div>
</Tab>
</Tabbed>
</div>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>
@@ -0,0 +1,58 @@
<script>
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { RadioGroup } from '@components/Form/Radio';
import CreateEditView from '@shell/mixins/create-edit-view';
export default {
name: 'EditAddonPCI',
components: {
Tabbed,
Tab,
RadioGroup,
},
mixins: [CreateEditView],
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
},
};
</script>
<template>
<Tabbed :side-tabs="true">
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="99"
>
<RadioGroup
v-model="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
</Tab>
</Tabbed>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>
@@ -0,0 +1,191 @@
<script>
import merge from 'lodash/merge';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { RadioGroup } from '@components/Form/Radio';
import { LabeledInput } from '@components/Form/LabeledInput';
import CreateEditView from '@shell/mixins/create-edit-view';
import jsyaml from 'js-yaml';
const DEFAUL_VALUE = {
fluentbit: {
resources: {
limits: {
cpu: '200m',
memory: '200Mi'
},
requests: {
cpu: '50m',
memory: '50Mi'
}
}
},
fluentd: {
resources: {
limits: {
cpu: '1000m',
memory: '800Mi'
},
requests: {
cpu: '100m',
memory: '20Mi'
}
}
},
};
export default {
name: 'EditAddonLogging',
components: {
Tabbed,
Tab,
RadioGroup,
LabeledInput,
},
mixins: [CreateEditView],
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
},
data() {
let valuesContentJson = DEFAUL_VALUE;
try {
valuesContentJson = merge({}, DEFAUL_VALUE, jsyaml.load(this.value.spec.valuesContent));
} catch (err) {
valuesContentJson = DEFAUL_VALUE;
this.$store.dispatch('growl/fromError', {
title: this.$store.getters['i18n/t']('generic.notification.title.error'),
err: err.data || err,
}, { root: true });
}
return { valuesContentJson };
},
watch: {
valuesContentJson: {
handler(neu) {
this.$set(this.value.spec, 'valuesContent', jsyaml.dump(neu));
},
deep: true,
immediate: true
},
},
};
</script>
<template>
<Tabbed :side-tabs="true">
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="99"
>
<RadioGroup
v-model="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
</Tab>
<Tab v-if="value.spec.enabled" name="fluentbit" :label="t('harvester.logging.configuration.section.fluentbit')" :weight="-1">
<div class="row mt-20">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.fluentbit.resources.requests.cpu"
:label="t('monitoring.prometheus.config.requests.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.fluentbit.resources.requests.memory"
:label="t('monitoring.prometheus.config.requests.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-20">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.fluentbit.resources.limits.cpu"
:label="t('monitoring.prometheus.config.limits.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.fluentbit.resources.limits.memory"
:label="t('monitoring.prometheus.config.limits.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
</Tab>
<Tab v-if="value.spec.enabled" name="fluentd" :label="t('harvester.logging.configuration.section.fluentd')" :weight="-1">
<div class="row mt-20">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.fluentd.resources.requests.cpu"
:label="t('monitoring.prometheus.config.requests.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.fluentd.resources.requests.memory"
:label="t('monitoring.prometheus.config.requests.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-20">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.fluentd.resources.limits.cpu"
:label="t('monitoring.prometheus.config.limits.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.fluentd.resources.limits.memory"
:label="t('monitoring.prometheus.config.limits.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
</Tab>
</Tabbed>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>
@@ -0,0 +1,523 @@
<script>
import merge from 'lodash/merge';
import isEmpty from 'lodash/isEmpty';
import jsyaml from 'js-yaml';
import { allHash } from '@shell/utils/promise';
import { findBy } from '@shell/utils/array';
import { LabeledInput } from '@components/Form/LabeledInput';
import { RadioGroup } from '@components/Form/Radio';
import LazyImage from '@shell/components/LazyImage';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import CreateEditView from '@shell/mixins/create-edit-view';
import { ENDPOINTS } from '@shell/config/types';
const CATTLE_MONITORING_NAMESPACE = 'cattle-monitoring-system';
const DEFAUL_VALUE = {
prometheus: {
prometheusSpec: {
resources: {
limits: {
cpu: '1000m',
memory: '3000Mi'
},
requests: {
cpu: '750m',
memory: '750Mi'
}
},
evaluationInterval: '1m',
scrapeInterval: '1m',
retention: '5d',
retentionSize: '50GiB',
},
},
'prometheus-node-exporter': {
resources: {
limits: {
cpu: '200m',
memory: '180Mi'
},
requests: {
cpu: '100m',
memory: '30Mi'
}
}
},
grafana: {
resources: {
limits: {
cpu: '200m',
memory: '500Mi'
},
requests: {
cpu: '100m',
memory: '200Mi'
}
}
},
alertmanager: {
enabled: false,
alertmanagerSpec: {
retention: '120h',
resources: {
limits: {
cpu: '1000m',
memory: '600Mi'
},
requests: {
cpu: '100m',
memory: '100Mi'
}
}
},
}
};
export default {
name: 'EditAddonMonitoring',
components: {
LabeledInput, RadioGroup, LazyImage, Tabbed, Tab
},
mixins: [CreateEditView],
async fetch() {
const { $store, externalLinks } = this;
if (!$store.getters['harvester/schemaFor'](ENDPOINTS)) {
return;
}
const hash = await allHash({ endpoints: $store.dispatch('harvester/findAll', { type: ENDPOINTS }) });
if (!isEmpty(hash.endpoints)) {
const amMatch = externalLinks.alertmanager;
const grafanaMatch = externalLinks.grafana;
const promeMatch = externalLinks.prometheus;
const alertmanager = findBy(
hash.endpoints,
'id',
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-alertmanager`
);
const grafana = findBy(
hash.endpoints,
'id',
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-grafana`
);
const prometheus = findBy(
hash.endpoints,
'id',
`${ CATTLE_MONITORING_NAMESPACE }/rancher-monitoring-prometheus`
);
if (!isEmpty(alertmanager) && !isEmpty(alertmanager.subsets)) {
amMatch.enabled = true;
}
if (!isEmpty(grafana) && !isEmpty(grafana.subsets)) {
grafanaMatch.enabled = true;
}
if (!isEmpty(prometheus) && !isEmpty(prometheus.subsets)) {
promeMatch.enabled = true;
}
}
},
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
},
data() {
const grafanaSrc = require('~shell/assets/images/vendor/grafana.svg');
const prometheusSrc = require('~shell/assets/images/vendor/prometheus.svg');
const currentCluster = this.$store.getters['currentCluster'];
let valuesContentJson = DEFAUL_VALUE;
try {
valuesContentJson = merge({}, DEFAUL_VALUE, jsyaml.load(this.value.spec.valuesContent));
} catch (err) {
valuesContentJson = DEFAUL_VALUE;
this.$store.dispatch('growl/fromError', {
title: this.$store.getters['i18n/t']('generic.notification.title.error'),
err: err.data || err,
}, { root: true });
}
return {
valuesContentJson,
externalLinks: {
alertmanager: {
enabled: false,
iconSrc: prometheusSrc,
label: 'monitoring.overview.linkedList.alertManager.label',
description:
'monitoring.overview.linkedList.alertManager.description',
link: `/k8s/clusters/${ currentCluster.id }/api/v1/namespaces/${ CATTLE_MONITORING_NAMESPACE }/services/http:rancher-monitoring-alertmanager:9093/proxy`,
},
grafana: {
enabled: false,
iconSrc: grafanaSrc,
label: 'monitoring.overview.linkedList.grafana.label',
description: 'monitoring.overview.linkedList.grafana.description',
link: `/k8s/clusters/${ currentCluster.id }/api/v1/namespaces/${ CATTLE_MONITORING_NAMESPACE }/services/http:rancher-monitoring-grafana:80/proxy`,
},
prometheus: {
enabled: false,
iconSrc: prometheusSrc,
label: 'monitoring.overview.linkedList.prometheusPromQl.label',
description:
'monitoring.overview.linkedList.prometheusPromQl.description',
link: `/k8s/clusters/${ currentCluster.id }/api/v1/namespaces/${ CATTLE_MONITORING_NAMESPACE }/services/http:rancher-monitoring-prometheus:9090/proxy`,
},
}
};
},
computed: {
prometheusNodeExporter() {
return this.valuesContentJson['prometheus-node-exporter'];
},
},
watch: {
valuesContentJson: {
handler(neu) {
this.$set(this.value.spec, 'valuesContent', jsyaml.dump(neu));
},
deep: true,
immediate: true
},
},
};
</script>
<template>
<Tabbed :side-tabs="true">
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="99"
>
<RadioGroup
v-model="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
</Tab>
<Tab v-if="value.spec.enabled" name="prometheus" :label="t('harvester.setting.harvesterMonitoring.section.prometheus')" :weight="-1">
<a
v-clean-tooltip="!externalLinks.prometheus.enabled ? t('monitoring.overview.linkedList.na') : undefined"
:disabled="!externalLinks.prometheus.enabled"
:href="externalLinks.prometheus.link"
target="_blank"
rel="noopener noreferrer"
class="subtype-banner m-0 mt-10 mb-10"
>
<div class="subtype-content">
<div class="title">
<div class="subtype-logo round-image">
<LazyImage :src="externalLinks.prometheus.iconSrc" />
</div>
<h5>
<span>
<t :k="externalLinks.prometheus.label" />
</span>
</h5>
<div class="flex-right">
<i class="icon icon-external-link mr-10" />
</div>
</div>
</div>
</a>
<div class="row">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.prometheus.prometheusSpec.scrapeInterval"
:label="t('monitoring.prometheus.config.scrape')"
:tooltip="t('harvester.setting.harvesterMonitoring.tips.scrape')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.prometheus.prometheusSpec.evaluationInterval"
:label="t('monitoring.prometheus.config.evaluation')"
:tooltip="t('harvester.setting.harvesterMonitoring.tips.evaluation')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.prometheus.prometheusSpec.retention"
:label="t('monitoring.prometheus.config.retention')"
:tooltip="t('harvester.setting.harvesterMonitoring.tips.retention')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.prometheus.prometheusSpec.retentionSize"
:label="t('monitoring.prometheus.config.retentionSize')"
:tooltip="t('harvester.setting.harvesterMonitoring.tips.retentionSize')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-12 mt-5">
<h4 class="mb-0">
{{ t('monitoring.prometheus.config.resourceLimits') }}
</h4>
</div>
</div>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.prometheus.prometheusSpec.resources.requests.cpu"
:label="t('monitoring.prometheus.config.requests.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.prometheus.prometheusSpec.resources.requests.memory"
:label="t('monitoring.prometheus.config.requests.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.prometheus.prometheusSpec.resources.limits.cpu"
:label="t('monitoring.prometheus.config.limits.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.prometheus.prometheusSpec.resources.limits.memory"
:label="t('monitoring.prometheus.config.limits.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
</Tab>
<Tab v-if="value.spec.enabled" name="nodeExporter" :label="t('harvester.setting.harvesterMonitoring.section.prometheusNodeExporter')" :weight="-2">
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="prometheusNodeExporter.resources.limits.cpu"
:label="t('monitoring.prometheus.config.limits.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="prometheusNodeExporter.resources.limits.memory"
:label="t('monitoring.prometheus.config.limits.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="prometheusNodeExporter.resources.requests.cpu"
:label="t('monitoring.prometheus.config.requests.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="prometheusNodeExporter.resources.requests.memory"
:label="t('monitoring.prometheus.config.requests.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
</Tab>
<Tab v-if="value.spec.enabled && valuesContentJson.grafana.resources" name="grafana" :label="t('harvester.setting.harvesterMonitoring.section.grafana')" :weight="-3">
<a
v-clean-tooltip="!externalLinks.grafana.enabled ? t('monitoring.overview.linkedList.na') : undefined"
:disabled="!externalLinks.grafana.enabled"
:href="externalLinks.grafana.link"
target="_blank"
rel="noopener nofollow"
class="subtype-banner m-0 mt-10 mb-10"
>
<div class="subtype-content">
<div class="title">
<div class="subtype-logo round-image">
<LazyImage :src="externalLinks.grafana.iconSrc" />
</div>
<h5>
<span>
<t :k="externalLinks.grafana.label" />
</span>
</h5>
<div class="flex-right">
<i class="icon icon-external-link mr-10" />
</div>
</div>
</div>
</a>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.grafana.resources.requests.cpu"
:label="t('monitoring.prometheus.config.requests.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.grafana.resources.requests.memory"
:label="t('monitoring.prometheus.config.requests.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.grafana.resources.limits.cpu"
:label="t('monitoring.prometheus.config.limits.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.grafana.resources.limits.memory"
:label="t('monitoring.prometheus.config.limits.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
</Tab>
<Tab v-if="value.spec.enabled" name="alertmanager" :label="t('harvester.setting.harvesterMonitoring.section.alertmanager')" :weight="-4">
<RadioGroup
v-model="valuesContentJson.alertmanager.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
<a
v-if="valuesContentJson.alertmanager.enabled"
v-clean-tooltip="!externalLinks.alertmanager.enabled ? t('monitoring.overview.linkedList.na') : undefined"
:disabled="!externalLinks.alertmanager.enabled"
:href="externalLinks.alertmanager.link"
target="_blank"
rel="noopener noreferrer"
class="subtype-banner m-0 mt-10 mb-10"
>
<div class="subtype-content">
<div class="title">
<div class="subtype-logo round-image">
<LazyImage :src="externalLinks.alertmanager.iconSrc" />
</div>
<h5>
<span>
<t :k="externalLinks.alertmanager.label" />
</span>
</h5>
<div class="flex-right">
<i class="icon icon-external-link mr-10" />
</div>
</div>
</div>
</a>
<div v-if="valuesContentJson.alertmanager.enabled">
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.alertmanager.alertmanagerSpec.retention"
:label="t('monitoring.prometheus.config.retention')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.alertmanager.alertmanagerSpec.resources.limits.cpu"
:label="t('monitoring.prometheus.config.limits.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.alertmanager.alertmanagerSpec.resources.limits.memory"
:label="t('monitoring.prometheus.config.limits.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.alertmanager.alertmanagerSpec.resources.requests.cpu"
:label="t('monitoring.prometheus.config.requests.cpu')"
:required="true"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.alertmanager.alertmanagerSpec.resources.requests.memory"
:label="t('monitoring.prometheus.config.requests.memory')"
:required="true"
:mode="mode"
/>
</div>
</div>
</div>
</Tab>
</Tabbed>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>
@@ -0,0 +1,151 @@
<script>
import merge from 'lodash/merge';
import jsyaml from 'js-yaml';
import { LabeledInput } from '@components/Form/LabeledInput';
import { RadioGroup } from '@components/Form/Radio';
const DEFAUL_VALUE = {
hostname: '',
rancherVersion: '',
bootstrapPassword: '',
};
export default {
name: 'EditAddonVcluster',
components: { LabeledInput, RadioGroup },
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
registerBeforeHook: {
type: Function,
required: true,
},
},
data() {
let valuesContentJson = {};
try {
valuesContentJson = merge({}, DEFAUL_VALUE, jsyaml.load(this.value.spec.valuesContent));
} catch (err) {
valuesContentJson = DEFAUL_VALUE;
this.$store.dispatch('growl/fromError', {
title: this.$store.getters['i18n/t']('generic.notification.title.error'),
err: err.data || err,
}, { root: true });
}
return { valuesContentJson };
},
created() {
if (this.registerBeforeHook) {
this.registerBeforeHook(this.willSave, 'willSave');
}
},
methods: {
willSave() {
const errors = [];
if (!this.value.spec.enabled) {
return Promise.resolve();
}
if (!this.valuesContentJson.hostname) {
errors.push(this.t('validation.required', { key: this.t('harvester.addons.rancherVcluster.hostname') }, true));
}
if (!this.valuesContentJson.bootstrapPassword) {
errors.push(this.t('validation.required', { key: this.t('harvester.addons.rancherVcluster.password') }, true));
}
if (errors.length > 0) {
return Promise.reject(errors);
} else {
return Promise.resolve();
}
},
},
watch: {
valuesContentJson: {
handler(neu) {
this.$set(this.value.spec, 'valuesContent', jsyaml.dump(neu));
},
deep: true,
immediate: true
},
},
};
</script>
<template>
<div>
<div class="row">
<div class="col span-12">
<RadioGroup
v-model="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
</div>
</div>
<template v-if="value.spec.enabled">
<div class="row mb-20">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.hostname"
label-key="harvester.addons.rancherVcluster.hostname"
:required="true"
:mode="mode"
placeholder="rancher.$vip.nip.io"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.rancherVersion"
label-key="harvester.addons.rancherVcluster.rancherVersion"
:required="true"
:disabled="true"
/>
</div>
</div>
<div class="row mt-20">
<div class="col span-6">
<LabeledInput
v-model="valuesContentJson.bootstrapPassword"
label-key="harvester.addons.rancherVcluster.password"
:mode="mode"
:required="true"
type="password"
/>
</div>
</div>
</template>
</div>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>
@@ -0,0 +1,234 @@
<script>
import { LabeledInput } from '@components/Form/LabeledInput';
import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { RadioGroup } from '@components/Form/Radio';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import CreateEditView from '@shell/mixins/create-edit-view';
import { STORAGE_CLASS } from '@shell/config/types';
import { allHash } from '@shell/utils/promise';
import { set, get, clone } from '@shell/utils/object';
const VALUES_YAML_KEYS = [
'resources.requests.cpu',
'resources.requests.memory',
'resources.limits.cpu',
'resources.limits.memory',
'pvcClaim.enabled',
'pvcClaim.size',
'pvcClaim.storageClassName',
];
const DEFAULT_VALUES = {
'resources.requests.cpu': '0.5',
'resources.requests.memory': '2Gi',
'resources.limits.cpu': '2',
'resources.limits.memory': '4Gi',
'pvcClaim.enabled': false,
'pvcClaim.size': '200Gi',
'pvcClaim.storageClassName': '',
};
export default {
name: 'EditHarvesterLogging',
components: {
LabeledInput,
Tabbed,
Tab,
RadioGroup,
LabeledSelect,
},
mixins: [CreateEditView],
props: {
value: {
type: Object,
required: true,
},
mode: {
type: String,
required: true
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
const hash = { storages: this.$store.dispatch(`${ inStore }/findAll`, { type: STORAGE_CLASS }) };
await allHash(hash);
},
data() {
let valuesObj = {};
try {
valuesObj = JSON.parse(this.value?.spec?.valuesContent || '{}');
} catch (err) {}
const valuesContent = clone(valuesObj);
VALUES_YAML_KEYS.map((key) => {
if (!get(valuesObj, key)) {
set(valuesContent, key, DEFAULT_VALUES[key]);
}
});
return { valuesContent };
},
computed: {
storageClassOptions() {
const inStore = this.$store.getters['currentProduct'].inStore;
const storages = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS);
const out = storages.filter(s => !s.parameters?.backingImage).map((s) => {
const label = s.isDefault ? `${ s.name } (${ this.t('generic.default') })` : s.name;
return {
label,
value: s.name,
};
}) || [];
return out;
},
},
methods: {
update() {
set(this.value, 'spec.valuesContent', JSON.stringify(this.valuesContent));
},
setDefaultClassName() {
const inStore = this.$store.getters['currentProduct'].inStore;
const defaultStorage = this.$store.getters[`${ inStore }/all`](STORAGE_CLASS).find( s => s.isDefault);
this.$set(this.valuesContent.pvcClaim, 'storageClassName', this.valuesContent?.pvcClaim?.storageClassName || defaultStorage?.metadata?.name || 'longhorn');
this.update();
},
},
watch: {
'valuesContent.pvcClaim.enabled'(value) {
if (value) {
this.setDefaultClassName();
}
},
},
};
</script>
<template>
<Tabbed :side-tabs="true">
<Tab
name="basic"
:label="t('harvester.addons.vmImport.titles.basic')"
:weight="99"
>
<RadioGroup
v-model="value.spec.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
/>
<div v-if="value.spec.enabled">
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContent.resources.limits.cpu"
:label="t('monitoring.prometheus.config.limits.cpu')"
:required="true"
:mode="mode"
@input="update"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContent.resources.limits.memory"
:label="t('monitoring.prometheus.config.limits.memory')"
:required="true"
:mode="mode"
@input="update"
/>
</div>
</div>
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContent.resources.requests.cpu"
:label="t('monitoring.prometheus.config.requests.cpu')"
:required="true"
:mode="mode"
@input="update"
/>
</div>
<div class="col span-6">
<LabeledInput
v-model="valuesContent.resources.requests.memory"
:label="t('monitoring.prometheus.config.requests.memory')"
:required="true"
:mode="mode"
@input="update"
/>
</div>
</div>
<br />
<h2>{{ t('harvester.addons.vmImport.titles.pvc') }}</h2>
<div v-if="value.spec.enabled">
<RadioGroup
v-model="valuesContent.pvcClaim.enabled"
class="mb-20"
name="model"
:mode="mode"
:options="[true,false]"
:labels="[t('generic.enabled'), t('generic.disabled')]"
@input="update"
/>
<div v-if="valuesContent.pvcClaim.enabled">
<div class="row mt-10">
<div class="col span-6">
<LabeledInput
v-model="valuesContent.pvcClaim.size"
:label="t('harvester.volume.size')"
:required="true"
:mode="mode"
@input="update"
/>
</div>
<div class="col span-6">
<LabeledSelect
v-model="valuesContent.pvcClaim.storageClassName"
:options="storageClassOptions"
:label="t('harvester.storage.storageClass.label')"
:mode="mode"
class="mb-20"
@input="update"
/>
</div>
</div>
</div>
</div>
</div>
</Tab>
</Tabbed>
</template>
<style lang="scss" scoped>
::v-deep .radio-group {
display: flex;
.radio-container {
margin-right: 30px;
}
}
</style>