mirror of https://github.com/rancher/dashboard.git
Add Save As RKE Template cluster action
This commit is contained in:
parent
1f0e95beff
commit
f02e35c76c
|
|
@ -77,13 +77,15 @@ button,
|
|||
.role-secondary {
|
||||
background: transparent;
|
||||
color: var(--primary) !important;
|
||||
border: solid thin var(--primary);
|
||||
border: solid 1px var(--primary);
|
||||
line-height: $btn-height - 2px;
|
||||
}
|
||||
|
||||
.role-tertiary {
|
||||
background: var(--accent-btn);
|
||||
border: solid thin var(--primary);
|
||||
border: solid 1px var(--primary);
|
||||
color: var(--primary);
|
||||
line-height: $btn-height - 2px;
|
||||
}
|
||||
|
||||
.role-link {
|
||||
|
|
|
|||
|
|
@ -2455,6 +2455,12 @@ promptRemove:
|
|||
promptRestore:
|
||||
title: Restore Snapshot
|
||||
|
||||
promptSaveAsRKETemplate:
|
||||
title: Create RKE Template from {cluster}
|
||||
name: Cluster Template Name
|
||||
description: Create a new RKE cluster template and initial revision from the current cluster configuration.
|
||||
warning: This will modify the current cluster, setting up the cluster to use the newly created cluster template and revision.
|
||||
|
||||
rancherAlertingDrivers:
|
||||
msTeams: Enable Microsoft Teams
|
||||
sms: Enable SMS
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
<script>
|
||||
import { mapState, mapGetters } from 'vuex';
|
||||
import { isArray } from '@/utils/array';
|
||||
import AsyncButton from '@/components/AsyncButton';
|
||||
import Card from '@/components/Card';
|
||||
import Banner from '@/components/Banner';
|
||||
import SaveAsRKETemplateDialog from '@/components/SaveAsRKETemplateDialog';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Card,
|
||||
AsyncButton,
|
||||
Banner,
|
||||
// Need to include all of the dialogs here
|
||||
// Would be nice if this could be done dynamically
|
||||
SaveAsRKETemplateDialog,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
errors: [],
|
||||
labels: {},
|
||||
restoreMode: 'all',
|
||||
moveTo: this.workspace,
|
||||
opened: false,
|
||||
allWorkspaces: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState('action-menu', ['showModal', 'modalData']),
|
||||
...mapGetters({ t: 'i18n/t' }),
|
||||
...mapGetters(['currentCluster']),
|
||||
|
||||
dynamic() {
|
||||
return this.modalComponent;
|
||||
},
|
||||
|
||||
resources() {
|
||||
let resources = this.modalData?.resources;
|
||||
|
||||
if (!isArray(resources)) {
|
||||
resources = [resources];
|
||||
}
|
||||
|
||||
return resources || [];
|
||||
},
|
||||
|
||||
component() {
|
||||
return this.modalData?.component;
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
showModal(show) {
|
||||
if (show) {
|
||||
this.opened = true;
|
||||
this.$modal.show('promptModal');
|
||||
} else {
|
||||
this.opened = false;
|
||||
this.$modal.hide('promptModal');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
close() {
|
||||
if (!this.opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.errors = [];
|
||||
this.labels = {};
|
||||
this.$store.commit('action-menu/togglePromptModal');
|
||||
},
|
||||
|
||||
modalClosed() {
|
||||
this.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<modal
|
||||
class="promptModal-modal"
|
||||
name="promptModal"
|
||||
styles="background-color: var(--nav-bg); border-radius: var(--border-radius); max-height: 100vh;"
|
||||
height="auto"
|
||||
:scrollable="true"
|
||||
@closed="modalClosed()"
|
||||
>
|
||||
<component v-if="opened && component" :is="component" @close="close()" :resources="resources"/>
|
||||
</modal>
|
||||
</template>
|
||||
|
||||
<style lang='scss'>
|
||||
.promptModal-modal {
|
||||
border-radius: var(--border-radius);
|
||||
overflow: scroll;
|
||||
max-height: 100vh;
|
||||
& ::-webkit-scrollbar-corner {
|
||||
background: rgba(0,0,0,0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
<script>
|
||||
import AsyncButton from '@/components/AsyncButton';
|
||||
import Card from '@/components/Card';
|
||||
import Banner from '@/components/Banner';
|
||||
import LabeledInput from '@/components/form/LabeledInput';
|
||||
import { exceptionToErrorsArray } from '@/utils/error';
|
||||
|
||||
const DEFAULT_REVISION = 'v1';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Card,
|
||||
AsyncButton,
|
||||
Banner,
|
||||
LabeledInput,
|
||||
},
|
||||
props: {
|
||||
resources: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return { errors: [], name: '' };
|
||||
},
|
||||
computed: {
|
||||
cluster() {
|
||||
if (this.resources?.length === 1) {
|
||||
const c = this.resources[0];
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
return {};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit('close');
|
||||
},
|
||||
|
||||
async apply(buttonDone) {
|
||||
try {
|
||||
await this.$store.dispatch('rancher/request', {
|
||||
url: `/v3/clusters/${ escape(this.cluster.name) }?action=saveAsTemplate`,
|
||||
method: 'post',
|
||||
data: {
|
||||
clusterTemplateName: this.name,
|
||||
clusterTemplateRevisionName: DEFAULT_REVISION
|
||||
},
|
||||
});
|
||||
|
||||
buttonDone(true);
|
||||
this.close();
|
||||
} catch (err) {
|
||||
this.errors = exceptionToErrorsArray(err);
|
||||
buttonDone(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
<Card class="prompt-restore" :show-highlight-border="false">
|
||||
<h4 slot="title" class="text-default-text" v-html="t('promptSaveAsRKETemplate.title', { cluster: cluster.displayName }, true)" />
|
||||
|
||||
<div slot="body" class="pl-10 pr-10">
|
||||
<form>
|
||||
<p class="pt-10 pb-10">{{ t('promptSaveAsRKETemplate.description') }}</P>
|
||||
<Banner color="warning" label-key="promptSaveAsRKETemplate.warning" />
|
||||
|
||||
<LabeledInput
|
||||
v-model="name"
|
||||
:label="t('promptSaveAsRKETemplate.name')"
|
||||
:required="true"
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div slot="actions">
|
||||
<button class="btn role-secondary mr-10" @click="close">
|
||||
{{ t('generic.cancel') }}
|
||||
</button>
|
||||
|
||||
<AsyncButton
|
||||
mode="create"
|
||||
:disabled="name.length <= 0"
|
||||
@click="apply"
|
||||
/>
|
||||
|
||||
<Banner v-for="(err, i) in errors" :key="i" color="error" :label="err" />
|
||||
</div>
|
||||
</Card>
|
||||
</template>
|
||||
<style lang='scss' scoped>
|
||||
.prompt-restore {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -8,6 +8,7 @@ import ActionMenu from '@/components/ActionMenu';
|
|||
import WindowManager from '@/components/nav/WindowManager';
|
||||
import PromptRemove from '@/components/PromptRemove';
|
||||
import PromptRestore from '@/components/PromptRestore';
|
||||
import PromptModal from '@/components/PromptModal';
|
||||
import AssignTo from '@/components/AssignTo';
|
||||
import Group from '@/components/nav/Group';
|
||||
import Header from '@/components/nav/Header';
|
||||
|
|
@ -32,6 +33,7 @@ export default {
|
|||
PromptRemove,
|
||||
PromptRestore,
|
||||
AssignTo,
|
||||
PromptModal,
|
||||
Header,
|
||||
ActionMenu,
|
||||
Group,
|
||||
|
|
@ -483,6 +485,7 @@ export default {
|
|||
<PromptRemove />
|
||||
<PromptRestore />
|
||||
<AssignTo />
|
||||
<PromptModal />
|
||||
<button v-if="dev" v-shortkey.once="['shift','l']" class="hide" @shortkey="toggleNoneLocale()" />
|
||||
<button v-if="dev" v-shortkey.once="['shift','t']" class="hide" @shortkey="toggleTheme()" />
|
||||
<button v-shortkey.once="['f8']" class="hide" @shortkey="wheresMyDebugger()" />
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import { ucFirst } from '@/utils/string';
|
|||
|
||||
export const DEFAULT_WORKSPACE = 'fleet-default';
|
||||
|
||||
const SAVE_AS_RKE_TEMPLATE_ACTION = '"saveAsTemplate';
|
||||
|
||||
export default {
|
||||
details() {
|
||||
const out = [
|
||||
|
|
@ -45,6 +47,20 @@ export default {
|
|||
enabled: this.$rootGetters['isRancher'],
|
||||
});
|
||||
|
||||
const canSaveAsTemplate = this.isRke1 && this.mgmt.status.driver === 'rancherKubernetesEngine' && !this.mgmt.spec.clusterTemplateName;
|
||||
|
||||
if (canSaveAsTemplate) {
|
||||
insertAt(out, 2, { divider: true });
|
||||
|
||||
insertAt(out, 3, {
|
||||
action: 'saveAsRKETemplate',
|
||||
label: 'Save as RKE Template',
|
||||
icon: 'icon icon-folder',
|
||||
bulkable: true,
|
||||
enabled: this.$rootGetters['isRancher'],
|
||||
});
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
|
|
@ -284,4 +300,13 @@ export default {
|
|||
return proxyFor(this.$ctx, x);
|
||||
});
|
||||
},
|
||||
|
||||
saveAsRKETemplate() {
|
||||
return (resources = this) => {
|
||||
this.$dispatch('promptModal', {
|
||||
resources,
|
||||
component: 'SaveAsRKETemplateDialog'
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -399,6 +399,10 @@ export default {
|
|||
commit('action-menu/toggleAssignTo', resources, { root: true });
|
||||
},
|
||||
|
||||
promptModal({ commit, state }, data ) {
|
||||
commit('action-menu/togglePromptModal', data, { root: true });
|
||||
},
|
||||
|
||||
async resourceAction({ getters, dispatch }, {
|
||||
resource, actionName, body, opt,
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@ export const state = function() {
|
|||
showPromptRestore: false,
|
||||
showAssignTo: false,
|
||||
showPromptUpdate: false,
|
||||
showModal: false,
|
||||
toMove: [],
|
||||
toRemove: [],
|
||||
toRestore: [],
|
||||
toAssign: [],
|
||||
toUpdate: [],
|
||||
modalData: {},
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -135,7 +137,24 @@ export const mutations = {
|
|||
}
|
||||
|
||||
state.toUpdate = resources;
|
||||
}
|
||||
},
|
||||
|
||||
togglePromptModal(state, data) {
|
||||
|
||||
console.log('Toggle prompt modal');
|
||||
console.log(data);
|
||||
|
||||
if (!data) {
|
||||
// Clearing the resources also hides the prompt
|
||||
state.showModal = false;
|
||||
} else {
|
||||
console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> SHOW >>> ');
|
||||
console.log(data.resources);
|
||||
state.showModal = true;
|
||||
}
|
||||
|
||||
state.modalData = data;
|
||||
}
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue