mirror of https://github.com/rancher/dashboard.git
396 lines
12 KiB
Vue
396 lines
12 KiB
Vue
<script>
|
|
import { Checkbox } from '@components/Form/Checkbox';
|
|
import CreateEditView from '@shell/mixins/create-edit-view';
|
|
import CruResource from '@shell/components/CruResource';
|
|
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
|
import Tab from '@shell/components/Tabbed/Tab';
|
|
import { RadioGroup } from '@components/Form/Radio';
|
|
import LabeledInput from '@components/Form/LabeledInput/LabeledInput.vue';
|
|
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
|
import UnitInput from '@shell/components/form/UnitInput';
|
|
import uniq from 'lodash/uniq';
|
|
import { _CREATE, _EDIT, FOCUS, _VIEW } from '@shell/config/query-params';
|
|
import { STORAGE_CLASS, PV } from '@shell/config/types';
|
|
import StatusTable from '@shell/components/StatusTable';
|
|
import ResourceTabs from '@shell/components/form/ResourceTabs';
|
|
import Labels from '@shell/components/form/Labels';
|
|
import { Banner } from '@components/Banner';
|
|
import ResourceManager from '@shell/mixins/resource-manager';
|
|
|
|
const DEFAULT_STORAGE = '10Gi';
|
|
|
|
export default {
|
|
name: 'PersistentVolumeClaim',
|
|
emits: ['input'],
|
|
inheritAttrs: false,
|
|
components: {
|
|
Banner,
|
|
Checkbox,
|
|
CruResource,
|
|
LabeledInput,
|
|
LabeledSelect,
|
|
Labels,
|
|
NameNsDescription,
|
|
RadioGroup,
|
|
ResourceTabs,
|
|
StatusTable,
|
|
Tab,
|
|
UnitInput,
|
|
},
|
|
|
|
mixins: [CreateEditView, ResourceManager],
|
|
async fetch() {
|
|
const storageClasses = await this.$store.dispatch('cluster/findAll', { type: STORAGE_CLASS });
|
|
|
|
if (this.$store.getters['cluster/canList'](PV)) {
|
|
this.resourceManagerFetchSecondaryResources(this.secondaryResourceData);
|
|
}
|
|
|
|
this.storageClassOptions = storageClasses.map((s) => s.name).sort();
|
|
this.storageClassOptions.unshift(this.t('persistentVolumeClaim.useDefault'));
|
|
|
|
this.value.spec['storageClassName'] = this.value.spec.storageClassName || this.storageClassOptions[0];
|
|
},
|
|
data() {
|
|
const canListPersistentVolumes = this.$store.getters['cluster/canList'](PV);
|
|
const canListStorageClasses = this.$store.getters['cluster/canList'](STORAGE_CLASS);
|
|
const sourceOptions = [
|
|
{
|
|
label: this.t('persistentVolumeClaim.source.options.new'),
|
|
value: 'new'
|
|
},
|
|
{
|
|
label: this.t('persistentVolumeClaim.source.options.existing'),
|
|
value: 'existing'
|
|
}
|
|
];
|
|
|
|
const defaultAccessModes = ['ReadWriteOnce'];
|
|
|
|
this.value['spec'] = this.value.spec || {};
|
|
this.value.spec['resources'] = this.value.spec.resources || {};
|
|
this.value.spec.resources['requests'] = this.value.spec.resources.requests || {};
|
|
this.value.spec.resources.requests['storage'] = this.value.spec.resources.requests.storage || DEFAULT_STORAGE;
|
|
if (this.realMode === _CREATE) {
|
|
this.value.spec['accessModes'] = defaultAccessModes;
|
|
}
|
|
|
|
const defaultTab = this.$route.query[FOCUS] || null;
|
|
|
|
return {
|
|
secondaryResourceData: this.secondaryResourceDataConfig(),
|
|
sourceOptions,
|
|
source: this.value.spec.volumeName ? sourceOptions[1].value : sourceOptions[0].value,
|
|
immutableMode: this.realMode === _CREATE ? _CREATE : _VIEW,
|
|
persistentVolumeOptions: [],
|
|
persistentVolumes: [],
|
|
storageClassOptions: [],
|
|
defaultTab,
|
|
canListPersistentVolumes,
|
|
canListStorageClasses,
|
|
};
|
|
},
|
|
computed: {
|
|
readWriteOnce: {
|
|
get() {
|
|
return this.value.spec.accessModes.includes('ReadWriteOnce');
|
|
},
|
|
set(value) {
|
|
this.checkboxSetter('ReadWriteOnce', value);
|
|
}
|
|
},
|
|
readOnlyMany: {
|
|
get() {
|
|
return this.value.spec.accessModes.includes('ReadOnlyMany');
|
|
},
|
|
set(value) {
|
|
this.checkboxSetter('ReadOnlyMany', value);
|
|
}
|
|
},
|
|
readWriteMany: {
|
|
get() {
|
|
return this.value.spec.accessModes.includes('ReadWriteMany');
|
|
},
|
|
set(value) {
|
|
this.checkboxSetter('ReadWriteMany', value);
|
|
}
|
|
},
|
|
persistentVolume: {
|
|
get() {
|
|
return this.value.spec.volumeName;
|
|
},
|
|
set(value) {
|
|
const persistentVolume = this.persistentVolumes.find((pv) => pv.metadata.name === value);
|
|
|
|
this.value.spec['storageClassName'] = '';
|
|
|
|
if (persistentVolume) {
|
|
this.value.spec.resources.requests['storage'] = persistentVolume.spec.capacity?.storage;
|
|
if (persistentVolume.spec?.storageClassName) {
|
|
this.value.spec['storageClassName'] = persistentVolume.spec?.storageClassName ;
|
|
}
|
|
}
|
|
|
|
this.value.spec['volumeName'] = value;
|
|
}
|
|
},
|
|
storageAmountMode() {
|
|
if (this.isCreate) {
|
|
return _CREATE;
|
|
} else if (this.isEdit && this.value.expandable && this.value.bound) {
|
|
return _EDIT;
|
|
}
|
|
|
|
return _VIEW;
|
|
}
|
|
},
|
|
created() {
|
|
this.registerBeforeHook(this.willSave, 'willSave');
|
|
},
|
|
mounted() {
|
|
const focus = this.$refs.volumeSize?.focus;
|
|
|
|
if (this.defaultTab === 'volumeclaim' && focus) {
|
|
setTimeout(() => focus());
|
|
}
|
|
},
|
|
methods: {
|
|
secondaryResourceDataConfig() {
|
|
return {
|
|
namespace: this.value?.metadata?.namespace || null,
|
|
data: {
|
|
[PV]: {
|
|
applyTo: [
|
|
{ var: 'persistentVolumes' },
|
|
{
|
|
var: 'persistentVolumeOptions',
|
|
parsingFunc: (data) => {
|
|
return data
|
|
.map((s) => {
|
|
const status = s.status.phase === 'Available' ? '' : ` (${ s.status.phase })`;
|
|
|
|
return {
|
|
label: `${ s.metadata.name }${ status }`,
|
|
value: s.metadata.name
|
|
};
|
|
})
|
|
.sort((l, r) => l.label.localeCompare(r.label));
|
|
}
|
|
}
|
|
]
|
|
},
|
|
}
|
|
};
|
|
},
|
|
checkboxSetter(key, value) {
|
|
if (value) {
|
|
this.value.spec.accessModes.push(key);
|
|
this.value['accessModes'] = uniq(this.value.spec.accessModes);
|
|
} else {
|
|
const indexOf = this.value.spec.accessModes.indexOf(key);
|
|
|
|
if (indexOf >= 0) {
|
|
this.value.spec.accessModes.splice(indexOf, 1);
|
|
}
|
|
}
|
|
},
|
|
isPersistentVolumeSelectable(option) {
|
|
const persistentVolume = this.persistentVolumes.find((pv) => pv.metadata.name === option.value);
|
|
|
|
return persistentVolume.status.phase === 'Available';
|
|
},
|
|
updateDefaults(source) {
|
|
if (source === 'new') {
|
|
this.value.spec.resources.requests['storage'] = DEFAULT_STORAGE;
|
|
}
|
|
|
|
this['persistentVolume'] = null;
|
|
},
|
|
willSave() {
|
|
if (this.value.spec.storageClassName === this.t('persistentVolumeClaim.useDefault')) {
|
|
delete this.value.spec['storageClassName'];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<CruResource
|
|
:done-route="doneRoute"
|
|
:mode="mode"
|
|
:resource="value"
|
|
:subtypes="[]"
|
|
:validation-passed="true"
|
|
:errors="errors"
|
|
@error="e=>errors = e"
|
|
@finish="save"
|
|
@cancel="done"
|
|
>
|
|
<NameNsDescription
|
|
:value="value"
|
|
:mode="mode"
|
|
:register-before-hook="registerBeforeHook"
|
|
:namespaced="true"
|
|
/>
|
|
|
|
<ResourceTabs
|
|
:value="value"
|
|
:mode="mode"
|
|
:side-tabs="true"
|
|
:default-tab="defaultTab"
|
|
@update:value="$emit('input', $event)"
|
|
>
|
|
<Tab
|
|
name="volumeclaim"
|
|
:label="t('persistentVolumeClaim.volumeClaim.label')"
|
|
:weight="4"
|
|
>
|
|
<div class="row">
|
|
<div class="col span-6">
|
|
<RadioGroup
|
|
v-model:value="source"
|
|
name="source"
|
|
:mode="immutableMode"
|
|
:label="t('persistentVolumeClaim.source.label')"
|
|
:options="sourceOptions"
|
|
@update:value="updateDefaults"
|
|
/>
|
|
</div>
|
|
<div class="col span-6">
|
|
<div class="row">
|
|
<div
|
|
v-if="source === 'new'"
|
|
class="col span-12"
|
|
>
|
|
<LabeledSelect
|
|
v-if="canListStorageClasses"
|
|
v-model:value="value.spec.storageClassName"
|
|
:options="storageClassOptions"
|
|
:label="t('persistentVolumeClaim.volumeClaim.storageClass')"
|
|
:mode="immutableMode"
|
|
/>
|
|
<LabeledInput
|
|
v-else
|
|
v-model:value="value.spec.storageClassName"
|
|
:label="t('persistentVolumeClaim.volumeClaim.storageClass')"
|
|
:mode="immutableMode"
|
|
:tooltip="t('persistentVolumeClaim.volumeClaim.tooltips.noStorageClass')"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-else
|
|
class="col span-12"
|
|
>
|
|
<LabeledSelect
|
|
v-if="canListPersistentVolumes"
|
|
v-model:value="persistentVolume"
|
|
:options="persistentVolumeOptions"
|
|
:label="t('persistentVolumeClaim.volumeClaim.persistentVolume')"
|
|
:selectable="isPersistentVolumeSelectable"
|
|
:mode="immutableMode"
|
|
:loading="isLoadingSecondaryResources"
|
|
/>
|
|
<LabeledInput
|
|
v-else
|
|
v-model:value="persistentVolume"
|
|
:label="t('persistentVolumeClaim.volumeClaim.persistentVolume')"
|
|
:mode="immutableMode"
|
|
:tooltip="t('persistentVolumeClaim.volumeClaim.tooltips.noPersistentVolume')"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col span-12 mt-10">
|
|
<UnitInput
|
|
ref="volumeSize"
|
|
v-model:value="value.spec.resources.requests.storage"
|
|
:label="t('persistentVolumeClaim.volumeClaim.requestStorage')"
|
|
:mode="storageAmountMode"
|
|
:input-exponent="3"
|
|
:output-modifier="true"
|
|
:increment="1024"
|
|
:min="1"
|
|
:required="true"
|
|
/>
|
|
<Banner
|
|
v-if="isEdit && !value.expandable"
|
|
color="info"
|
|
class="mt-10"
|
|
>
|
|
{{ t('persistentVolumeClaim.expand.notSupported') }}
|
|
</Banner>
|
|
<Banner
|
|
v-else-if="isEdit && !value.bound"
|
|
color="info"
|
|
class="mt-10"
|
|
>
|
|
{{ t('persistentVolumeClaim.expand.notBound') }}
|
|
</Banner>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Tab>
|
|
<Tab
|
|
name="customize"
|
|
:label="t('persistentVolumeClaim.customize.label')"
|
|
:weight="3"
|
|
>
|
|
<div class="access">
|
|
<h3>{{ t('persistentVolumeClaim.accessModes') }}</h3>
|
|
<span class="text-error">*</span>
|
|
</div>
|
|
<div>
|
|
<Checkbox
|
|
v-model:value="readWriteOnce"
|
|
:label="t('persistentVolumeClaim.customize.accessModes.readWriteOnce')"
|
|
:mode="immutableMode"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Checkbox
|
|
v-model:value="readOnlyMany"
|
|
:label="t('persistentVolumeClaim.customize.accessModes.readOnlyMany')"
|
|
:mode="immutableMode"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<Checkbox
|
|
v-model:value="readWriteMany"
|
|
:label="t('persistentVolumeClaim.customize.accessModes.readWriteMany')"
|
|
:mode="immutableMode"
|
|
/>
|
|
</div>
|
|
</Tab>
|
|
<Tab
|
|
v-if="isView"
|
|
name="status"
|
|
:label="t('persistentVolumeClaim.status.label')"
|
|
:weight="2"
|
|
>
|
|
<StatusTable :resource="value" />
|
|
</Tab>
|
|
<Tab
|
|
name="labels-and-annotations"
|
|
label-key="generic.labelsAndAnnotations"
|
|
:weight="-1"
|
|
>
|
|
<Labels
|
|
default-container-class="labels-and-annotations-container"
|
|
:value="value"
|
|
:mode="mode"
|
|
:display-side-by-side="false"
|
|
/>
|
|
</Tab>
|
|
</ResourceTabs>
|
|
</CruResource>
|
|
</template>
|
|
|
|
<style lang='scss' scoped>
|
|
.access {
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
</style>
|