Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

Commit

Permalink
feat: implement node Reset
Browse files Browse the repository at this point in the history
Add a new context menu item that opens reset modal dialog.
Add a new modal window with reset options, which provides a way to
toggle graceful, reboot flags.

Signed-off-by: Artem Chernyshev <[email protected]>
  • Loading branch information
Unix4ever authored and talos-bot committed Jul 23, 2021
1 parent bcb7d23 commit e5b6f29
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 60 deletions.
16 changes: 3 additions & 13 deletions frontend/src/components/LogView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
<div class="flex-1 pb-3 logs">
<div class="flex border-b border-talos-gray-300 dark:border-talos-gray-600 px-4 py-2 gap-1">
<div class="flex-1">{{ logs.length }} lines</div>
<div class="flex items-center justify-center gap-2 text-talos-gray-800 hover:text-talos-gray-600 dark:text-talos-gray-400 dark:hover:text-talos-gray-300">
<Switch
v-model="follow"
class="inline-flex justify-center items-center w-5 h-5 rounded-md border-2 border-talos-gray-800 dark:border-talos-gray-400 outline-none"
>
<check-icon v-if="follow" class="w-4 h-4 inline-block"/>
</Switch>
<div @click="() => { follow = !follow }" class="cursor-pointer uppercase text-sm select-none font-bold">Follow Logs</div>
</div>
<t-checkbox v-model="follow" label="follow logs"/>
</div>
<div class="flex-1 flex flex-col overflow-auto w-full h-full text-xs" ref="logView" style="font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace">
<div v-for="line, index in logs" :key="index" class="log-line">
Expand All @@ -28,9 +20,8 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
</template>

<script type="ts">
import { Switch } from '@headlessui/vue';
import TCheckbox from './TCheckbox.vue';
import { ref, onMounted, onUpdated, watch } from 'vue';
import { CheckIcon } from '@heroicons/vue/solid';

export default {
props: {
Expand All @@ -41,8 +32,7 @@ export default {
},

components: {
CheckIcon,
Switch,
TCheckbox,
},

setup() {
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/components/NodeListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
>Reboot</a
>
</menu-item>
<menu-item v-slot="{ active }">
<a
v-on:click="resetNode"
:class="{ active }"
>Reset</a
>
</menu-item>
</template>
</t-dropdown>
</div>
Expand Down Expand Up @@ -188,13 +195,23 @@ export default {
})
};

const resetNode = async () => {
router.replace({
query: {
modal: "reset",
node: ip.value,
}
});
};

return {
os,
ip,
status,
roles,
isTalos,
rebootNode,
resetNode,
getQuery,
}
}
Expand Down
77 changes: 30 additions & 47 deletions frontend/src/components/TCheckbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,43 @@ License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<template>
<button type="button"
class="focus:outline-none focus:shadow-outline"
:class="{primary: primary, secondary: !primary, small: small, xs: xs}">
<div class="container space-x-1">
</div>
</button>
<div class="flex items-center gap-2 text-talos-gray-800 hover:text-talos-gray-600 dark:text-talos-gray-400 dark:hover:text-talos-gray-300">
<Switch
v-model="checked"
class="inline-flex justify-center items-center w-5 h-5 rounded-md border-2 border-talos-gray-800 dark:border-talos-gray-400 outline-none"
>
<check-icon v-if="checked" class="w-4 h-4 inline-block"/>
</Switch>
<div @click="() => { checked = !checked }" class="cursor-pointer uppercase text-sm select-none font-bold">{{ label }}</div>
</div>
</template>

<script lang="ts">
import { Options, Vue } from 'vue-class-component';

@Options({
import { computed } from 'vue';
import { Switch } from '@headlessui/vue';
import { CheckIcon } from '@heroicons/vue/solid';

export default {
components: {
Switch,
CheckIcon,
},
props: {
primary: Boolean,
small: Boolean,
xs: Boolean,
modelValue: Boolean,
label: String,
},
emits: [
'update:modelValue'
],
setup(props, { emit }) {
const checked = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
});

data() {
return {
colors: this.primary ? this.primaryClasses : this.secondaryClasses,
checked,
};
}
})
export default class TButton extends Vue {}
};
</script>

<style scoped>
button {
@apply px-3 py-2 text-sm font-semibold leading-5 transition-colors duration-200 rounded-md shadow-sm select-none;
}

.primary {
@apply text-white bg-blue-500 hover:bg-blue-700 focus:bg-blue-700 border border-transparent;
}

.secondary {
@apply text-black dark:text-white border border-talos-gray-300 dark:border-talos-gray-600 bg-talos-gray-50 dark:bg-talos-gray-800 hover:bg-gray-200 dark:hover:bg-talos-gray-700 focus:bg-talos-gray-200 dark:focus:bg-talos-gray-700;
}

.small {
@apply py-1;
}

.xs {
@apply p-1;
}

button > div.container {
vertical-align: middle;
}

button > div.container > * {
display: inline-block;
vertical-align: middle;
}
</style>
2 changes: 2 additions & 0 deletions frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import SidebarCluster from "../views/SidebarCluster.vue";
import SidebarNode from "../views/SidebarNode.vue";
import Settings from "../views/Settings.vue";
import Reboot from "../views/Reboot.vue";
import Reset from "../views/Reset.vue";
import Upgrade from "../views/Upgrade.vue";
import { useRoute } from 'vue-router';
import { context } from '../context';
Expand Down Expand Up @@ -123,6 +124,7 @@ const modals = {
settings: Settings,
reboot: Reboot,
upgrade: Upgrade,
reset: Reset,
};

export { modals };
Expand Down
107 changes: 107 additions & 0 deletions frontend/src/views/Reset.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<!--
This Runtime Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<template>
<div>
<div class="sm:flex sm:items-start p-5 max-w-xl">
<div class="flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-yellow-400 text-yellow-400 bg-opacity-20 sm:mx-0 sm:h-10 sm:w-10">
<exclamation-icon class="w-6 h-6" fill="currentColor"/>
</div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-talos-gray-900 dark:text-talos-gray-100" id="modal-title">Reset the Node {{ node }}</h3>
<p class="text-sm text-talos-gray-500 dark:text-talos-gray-300">This operation will reset the EPHEMERAL partition on the node.</p>
<div class="mt-2 flex flex-col gap-1">
<t-checkbox v-model="graceful" label="Graceful"/>
<t-checkbox v-model="reboot" label="Reboot"/>
</div>
</div>
</div>
<div class="bg-talos-gray-50 dark:bg-talos-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse gap-2 rounded-b-lg">
<t-button small primary :disabled="!node || state === 'Resetting'" @click="reset">{{ state }}</t-button>
<t-button small @click="close">Cancel</t-button>
</div>
</div>
</template>

<script lang="ts">
import TButton from '../components/TButton.vue';
import TCheckbox from '../components/TCheckbox.vue';
import { ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { MachineService, getCluster } from '../api/grpc';
import { Runtime } from '../api/common/theila.pb';
import {
ExclamationIcon,
} from '@heroicons/vue/outline';
import { showError } from '../modal';

export default {
components: {
TButton,
TCheckbox,
ExclamationIcon,
},

setup() {
const route = useRoute();
const router = useRouter();
const state = ref("Reset");
const graceful = ref(true);
const reboot = ref(true);

const close = () => {
router.replace({ query: {
modal: undefined,
node: undefined,
}});
};

return {
node: route.query.node,
state,
close,
graceful,
reboot,
reset: async () => {
state.value = "Resetting";

try {
const res = await MachineService.Reset({
reboot: reboot.value,
graceful: graceful.value,
systemPartitionsToWipe: [
{
label: "EPHEMERAL",
wipe: true,
}
],
}, {
runtime: Runtime.Talos,
metadata: {
nodes: [route.query.node],
...getCluster(route),
}
});

const errors: string[] = [];
for(const message of res.messages) {
if(message.metadata.error)
errors.push(`${message.metadata.hostname || route.query.node} ${message.metadata.error}`);
}

if(errors.length > 0)
throw new Error(errors.join(", "))

close();
} catch(e) {
close();

showError("Failed to Issue Reset", e.toString())
}
}
}
},
}
</script>

0 comments on commit e5b6f29

Please sign in to comment.