RKE2 backup & restore

This commit is contained in:
Vincent Fiduccia 2021-05-19 17:18:39 -07:00
parent e7f1fbc9dd
commit f95f4e802a
No known key found for this signature in database
GPG Key ID: 2B29AD6BB2BB2582
5 changed files with 49 additions and 31 deletions

View File

@ -6,6 +6,8 @@ import Banner from '@/components/Banner';
import Date from '@/components/formatter/Date.vue'; import Date from '@/components/formatter/Date.vue';
import RadioGroup from '@/components/form/RadioGroup.vue'; import RadioGroup from '@/components/form/RadioGroup.vue';
import { exceptionToErrorsArray } from '@/utils/error'; import { exceptionToErrorsArray } from '@/utils/error';
import { CAPI } from '@/config/types';
import { set } from '@/utils/object';
export default { export default {
components: { components: {
@ -20,7 +22,7 @@ export default {
return { return {
errors: [], errors: [],
labels: {}, labels: {},
restoreMode: 'etcd', restoreMode: 'all',
moveTo: this.workspace, moveTo: this.workspace,
loaded: false, loaded: false,
allWorkspaces: [], allWorkspaces: [],
@ -62,14 +64,22 @@ export default {
async apply(buttonDone) { async apply(buttonDone) {
try { try {
await this.$store.dispatch('rancher/request', { if ( this.isRke2 ) {
url: `/v3/clusters/${ escape(this.snapshot.clusterId) }?action=restoreFromEtcdBackup`, const cluster = this.$store.getters['management/byId'](CAPI.RANCHER_CLUSTER, this.snapshot.clusterId);
method: 'post',
data: { set(cluster, 'spec.rkeConfig.etcdSnapshotRestore', { name: this.snapshot.name });
etcdBackupId: this.snapshot.id,
restoreRkeConfig: this.restoreMode, await cluster.save();
}, } else {
}); await this.$store.dispatch('rancher/request', {
url: `/v3/clusters/${ escape(this.snapshot.clusterId) }?action=restoreFromEtcdBackup`,
method: 'post',
data: {
etcdBackupId: this.snapshot.id,
restoreRkeConfig: this.restoreMode,
},
});
}
buttonDone(true); buttonDone(true);
this.close(); this.close();
@ -103,15 +113,17 @@ export default {
<h3>Snapshot Date</h3> <h3>Snapshot Date</h3>
<div><Date :value="snapshot.createdAt || snapshot.created" /></div> <div><Date :value="snapshot.createdAt || snapshot.created" /></div>
<div class="spacer" /> <template v-if="!isRke2">
<div class="spacer" />
<RadioGroup <RadioGroup
v-model="restoreMode" v-model="restoreMode"
name="restoreMode" name="restoreMode"
label="Restore Type" label="Restore Type"
:labels="['Only etcd', 'Kubernetes version and etcd', 'Cluster config, Kubernetes version and etcd']" :labels="['Only etcd', 'Kubernetes version and etcd', 'Cluster config, Kubernetes version and etcd']"
:options="['etcd', 'kubernetesVersion', 'all']" :options="['etcd', 'kubernetesVersion', 'all']"
/> />
</template>
</form> </form>
</div> </div>
@ -122,12 +134,9 @@ export default {
<AsyncButton <AsyncButton
mode="restore" mode="restore"
:disabled="isRke2"
@click="apply" @click="apply"
/> />
<Banner v-if="isRke2" color="warning" label="@TODO The actual rke2 restore action..." />
<Banner v-for="(err, i) in errors" :key="i" color="error" :label="err" /> <Banner v-for="(err, i) in errors" :key="i" color="error" :label="err" />
</div> </div>
</Card> </Card>

View File

@ -11,6 +11,7 @@ import {
} from '@/config/table-headers'; } from '@/config/table-headers';
import CustomCommand from '@/edit/provisioning.cattle.io.cluster/CustomCommand'; import CustomCommand from '@/edit/provisioning.cattle.io.cluster/CustomCommand';
import AsyncButton from '@/components/AsyncButton.vue'; import AsyncButton from '@/components/AsyncButton.vue';
import { set } from '@/utils/object';
export default { export default {
components: { components: {
@ -191,8 +192,9 @@ export default {
btnCb(true); btnCb(true);
}, 1000); }, 1000);
} else { } else {
btnCb(false); set(this.value.spec.rkeConfig, 'etcdSnapshotCreate', {});
this.$store.dispatch('growl/fromError', { title: '@TODO Actual RKE2 snapshot create API call' }); await this.value.save();
btnCb(true);
} }
} catch (err) { } catch (err) {
this.$store.dispatch('growl/fromError', { title: 'Error creating snapshot', err }); this.$store.dispatch('growl/fromError', { title: 'Error creating snapshot', err });

View File

@ -313,9 +313,9 @@ export default {
v-model="provisioner" v-model="provisioner"
class="rke-switch" class="rke-switch"
off-value="rke1" off-value="rke1"
off-label="RKE" off-label="RKE1"
on-value="rke2" on-value="rke2"
on-label="RKE2" on-label="RKE2/K3s"
/> />
</div> </div>
{{ obj.label }} {{ obj.label }}

View File

@ -42,14 +42,21 @@ export default async function({
} }
// Initial ?setup=admin-password can technically be on any route // Initial ?setup=admin-password can technically be on any route
const initialPass = route.query[SETUP]; const initialPass = route.query[SETUP];
const firstLogin = await store.dispatch('rancher/find', { let firstLogin = false;
type: 'setting',
id: 'first-login', try {
opt: { url: `/v3/settings/first-login` } const res = await store.dispatch('rancher/find', {
}); type: 'setting',
id: 'first-login',
opt: { url: `/v3/settings/first-login` }
});
firstLogin = res?.value === 'true';
} catch (e) {
}
// TODO show error if firstLogin and default pass doesn't work // TODO show error if firstLogin and default pass doesn't work
if (firstLogin && firstLogin.value === 'true' ) { if ( firstLogin ) {
const ok = await tryInitialSetup(store, initialPass); const ok = await tryInitialSetup(store, initialPass);
if (ok) { if (ok) {

View File

@ -73,7 +73,7 @@ export default {
function responseObject(res) { function responseObject(res) {
let out = res.data; let out = res.data;
if ( res.status === 201 ) { if ( res.status === 204 || out === null ) {
out = {}; out = {};
} }