mirror of https://github.com/rancher/dashboard.git
337 lines
8.1 KiB
Vue
337 lines
8.1 KiB
Vue
<script>
|
|
import jsyaml from 'js-yaml';
|
|
import merge from 'lodash/merge';
|
|
import Loading from '@/components/Loading';
|
|
import NameNsDescription from '@/components/form/NameNsDescription';
|
|
import LabeledSelect from '@/components/form/LabeledSelect';
|
|
import Markdown from '@/components/Markdown';
|
|
import { CATALOG } from '@/config/types';
|
|
import { defaultAsyncData } from '@/components/ResourceDetail';
|
|
import {
|
|
REPO_TYPE, REPO, CHART, VERSION, NAMESPACE, NAME, DESCRIPTION as DESCRIPTION_QUERY, STEP
|
|
} from '@/config/query-params';
|
|
import Wizard from '@/components/Wizard';
|
|
import YamlEditor from '@/components/YamlEditor';
|
|
import { DESCRIPTION as DESCRIPTION_ANNOTATION } from '@/config/labels-annotations';
|
|
import { exceptionToErrorsArray } from '@/utils/error';
|
|
|
|
export default {
|
|
name: 'ChartInstall',
|
|
|
|
components: {
|
|
LabeledSelect,
|
|
Loading,
|
|
Markdown,
|
|
NameNsDescription,
|
|
Wizard,
|
|
YamlEditor,
|
|
},
|
|
|
|
props: {
|
|
mode: {
|
|
type: String,
|
|
required: true,
|
|
},
|
|
|
|
value: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
|
|
// originalValue: {
|
|
// type: Object,
|
|
// default: null,
|
|
// },
|
|
},
|
|
|
|
async fetch() {
|
|
const query = this.$route.query;
|
|
|
|
await this.$store.dispatch('catalog/load');
|
|
|
|
const repoType = query[REPO_TYPE];
|
|
const repoName = query[REPO];
|
|
|
|
this.repo = this.$store.getters['catalog/repo']({ repoType, repoName });
|
|
|
|
const chartName = query[CHART];
|
|
const version = query[VERSION];
|
|
|
|
if ( this.repo && !this.chart && chartName ) {
|
|
this.chart = this.$store.getters['catalog/chart']({
|
|
repoType, repoName, chartName
|
|
});
|
|
}
|
|
|
|
if ( !this.chart ) {
|
|
throw new Error('Chart not found');
|
|
}
|
|
|
|
if ( this.chart.targetNamespace ) {
|
|
this.forceNamespace = this.chart.targetNamespace;
|
|
} else if ( query[NAMESPACE] ) {
|
|
this.forceNamespace = query[NAMESPACE];
|
|
} else {
|
|
this.forceNamespace = null;
|
|
}
|
|
|
|
if ( this.chart.targetName ) {
|
|
this.value.metadata.name = this.chart.targetName;
|
|
this.nameDisabled = true;
|
|
} else if ( query[NAME] ) {
|
|
this.value.metadata.name = query[name];
|
|
} else {
|
|
this.nameDisabled = false;
|
|
}
|
|
|
|
if ( query[DESCRIPTION_QUERY] ) {
|
|
this.value.setAnnotation(DESCRIPTION_ANNOTATION, query[DESCRIPTION_QUERY]);
|
|
}
|
|
|
|
if ( version ) {
|
|
this.version = this.$store.getters['catalog/version']({
|
|
repoType, repoName, chartName, version
|
|
});
|
|
|
|
this.versionInfo = await this.$store.dispatch('catalog/getVersionInfo', {
|
|
repoType, repoName, chartName, version
|
|
});
|
|
|
|
const component = this.version?.annotations?.[CATALOG.COMPONENT];
|
|
|
|
if ( component ) {
|
|
if ( this.$store.getters['catalog/haveComponent'](component) ) {
|
|
this.valuesComponent = this.$store.getters['catalog/importComponent'](component);
|
|
} else {
|
|
this.valuesComponent = null;
|
|
}
|
|
} else {
|
|
this.valuesComponent = null;
|
|
}
|
|
|
|
if ( !this.value.values ) {
|
|
this.mergeValues(this.versionInfo.values);
|
|
this.valuesYaml = jsyaml.safeDump(this.value.values);
|
|
}
|
|
}
|
|
},
|
|
|
|
asyncData(ctx) {
|
|
return defaultAsyncData(ctx, 'chartInstallAction');
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
repo: null,
|
|
chart: null,
|
|
version: null,
|
|
versionInfo: null,
|
|
valuesYaml: null,
|
|
|
|
forceNamespace: null,
|
|
nameDisabled: false,
|
|
|
|
errors: null,
|
|
valuesComponent: null,
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
showReadme() {
|
|
return !!this.versionInfo?.readme;
|
|
},
|
|
|
|
showNameEditor() {
|
|
return !this.nameDisabled || !this.forceNamespace;
|
|
},
|
|
|
|
showVersions() {
|
|
return this.chart?.versions.length > 1;
|
|
},
|
|
|
|
steps() {
|
|
return [
|
|
{
|
|
name: 'name',
|
|
label: 'Name & Version',
|
|
ready: !!this.chart,
|
|
},
|
|
{
|
|
name: 'values',
|
|
label: 'Chart Options',
|
|
ready: !!this.versionInfo,
|
|
},
|
|
{
|
|
name: 'advanced',
|
|
label: 'Advanced Options',
|
|
ready: !!this.versionInfo,
|
|
},
|
|
];
|
|
},
|
|
},
|
|
|
|
watch: { '$route.query': '$fetch' },
|
|
|
|
mounted() {
|
|
const query = this.$route.query;
|
|
|
|
if ( query[STEP] >= 2 && !this.value.metadata?.name ) {
|
|
// If you reload the page, go back to 1 because the name and other stuff has been lost...
|
|
this.$router.applyQuery({ [STEP]: 1 });
|
|
} else if ( ( !query[STEP] || query[STEP] === 1 ) && !this.showReadme && !this.showNameEditor && !this.showVersions ) {
|
|
// If there's nothing on page 1, go to page 2
|
|
this.$router.applyQuery({ [STEP]: 2 });
|
|
}
|
|
|
|
// For easy access debugging...
|
|
if ( typeof window !== 'undefined' ) {
|
|
window.v = v;
|
|
window.c = this;
|
|
}
|
|
},
|
|
|
|
methods: {
|
|
selectVersion(version) {
|
|
this.$router.applyQuery({ [VERSION]: version });
|
|
},
|
|
|
|
mergeValues(neu) {
|
|
this.value.values = merge({}, neu, this.value.values || {});
|
|
},
|
|
|
|
yamlChanged(value) {
|
|
try {
|
|
jsyaml.safeLoad(value);
|
|
|
|
this.valuesYaml = value;
|
|
} catch (e) {
|
|
}
|
|
},
|
|
|
|
async finish(btnCb) {
|
|
try {
|
|
this.errors = null;
|
|
const obj = this.installInput();
|
|
const res = await this.repo.doAction('install', obj);
|
|
|
|
this.operation = await this.$store.dispatch('cluster/find', {
|
|
type: CATALOG.OPERATION,
|
|
id: `${ res.operationNamespace }/${ res.operationName }`
|
|
});
|
|
|
|
try {
|
|
await this.operation.waitForLink('logs');
|
|
this.operation.openLogs();
|
|
} catch (e) {
|
|
// The wait times out eventually, move on...
|
|
}
|
|
|
|
btnCb(true);
|
|
|
|
this.$router.replace({
|
|
name: `c-cluster-product-resource`,
|
|
params: {
|
|
product: this.$store.getters['productId'],
|
|
cluster: this.$store.getters['clusterId'],
|
|
resource: CATALOG.RELEASE,
|
|
}
|
|
});
|
|
} catch (err) {
|
|
this.errors = exceptionToErrorsArray(err);
|
|
btnCb(false);
|
|
}
|
|
},
|
|
|
|
installInput() {
|
|
const out = JSON.parse(JSON.stringify(this.value));
|
|
|
|
out.chartName = this.chart.chartName;
|
|
out.version = this.$route.query.version;
|
|
out.releaseName = out.metadata.name;
|
|
out.namespace = out.metadata.namespace;
|
|
out.description = out.metadata?.annotations?.[DESCRIPTION_ANNOTATION];
|
|
|
|
delete out.metadata;
|
|
|
|
// @TODO only save values that differ from defaults?
|
|
out.values = jsyaml.safeLoad(this.valuesYaml);
|
|
|
|
return out;
|
|
},
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<Loading v-if="$fetchState.pending" />
|
|
<Wizard
|
|
v-else-if="chart"
|
|
:steps="steps"
|
|
:show-banner="false"
|
|
:edit-first-step="true"
|
|
:errors="errors"
|
|
@finish="finish($event)"
|
|
>
|
|
<template #name>
|
|
<div v-if="showReadme" class="row">
|
|
<div class="col span-12">
|
|
<Markdown v-model="versionInfo.readme" class="readme" />
|
|
</div>
|
|
</div>
|
|
|
|
<NameNsDescription
|
|
v-if="showNameEditor"
|
|
v-model="value"
|
|
:mode="mode"
|
|
:name-disabled="nameDisabled"
|
|
:force-namespace="forceNamespace"
|
|
/>
|
|
|
|
<div v-if="showVersions" class="row">
|
|
<div class="col span-6">
|
|
<LabeledSelect
|
|
label="Chart Version"
|
|
:value="$route.query.version"
|
|
option-label="version"
|
|
option-key="version"
|
|
:reduce="opt=>opt.version"
|
|
:options="chart.versions"
|
|
@input="selectVersion($event)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-if="versionInfo" #values>
|
|
<component
|
|
:is="valuesComponent"
|
|
v-if="valuesComponent"
|
|
v-model="value.values"
|
|
/>
|
|
<YamlEditor
|
|
v-else
|
|
class="yaml-editor"
|
|
:value="valuesYaml"
|
|
@onInput="yamlChanged"
|
|
/>
|
|
</template>
|
|
|
|
<template #advanced>
|
|
Advanced helm options coming soon..
|
|
</template>
|
|
</Wizard>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.yaml-editor {
|
|
flex: 1;
|
|
min-height: 400px;
|
|
}
|
|
|
|
.readme {
|
|
max-height: calc(100vh - 520px);
|
|
margin-bottom: 20px;
|
|
overflow: auto;
|
|
}
|
|
</style>
|