Compare commits

..

2 Commits

Author SHA1 Message Date
Florent Beauchamp
543f44d1d1 fix(@»en-orchestra/backups): better cleaning of broken vhd
must be merged after #6471 and all tests passing
2022-11-14 10:54:31 +01:00
Thierry Goettelmann
b0cb249ae9 docs(lite): update README about UiIcon (#6520) 2022-11-14 10:22:07 +01:00
19 changed files with 81 additions and 52 deletions

View File

@@ -3,7 +3,7 @@
const sum = require('lodash/sum')
const UUID = require('uuid')
const { asyncMap } = require('@xen-orchestra/async-map')
const { Constants, openVhd, VhdAbstract, VhdFile } = require('vhd-lib')
const { Constants, openVhd, VhdAbstract, VhdFile, BrokenVhdError } = require('vhd-lib')
const { isVhdAlias, resolveVhdAlias } = require('vhd-lib/aliases')
const { dirname, resolve } = require('path')
const { DISK_TYPES } = Constants
@@ -242,7 +242,7 @@ exports.cleanVm = async function cleanVm(
} catch (error) {
vhds.delete(path)
logWarn('VHD check error', { path, error })
if (error?.code === 'ERR_ASSERTION' && remove) {
if (error instanceof BrokenVhdError && remove) {
logInfo('deleting broken VHD', { path })
return VhdAbstract.unlink(handler, path)
}

View File

@@ -91,18 +91,21 @@ const fontSize = ref("2rem");
This project is using Font Awesome 6 Free.
Here is how to use an icon in your template.
Icons can be displayed with the `UiIcon` component.
Note: `FontAwesomeIcon` is a global component that does not need to be imported.
Passing `undefined` as `icon` prop will disable the component (no need to use an additional `v-if` condition).
Use the `busy` prop to display a loader icon.
```vue
<template>
<div>
<FontAwesomeIcon :icon="faDisplay" />
<UiIcon :icon="faDisplay" />
</div>
</template>
<script lang="ts" setup>
import UiIcon from "@/components/ui/UiIcon.vue"
import { faDisplay } from "@fortawesome/free-solid-svg-icons";
</script>
```
@@ -115,8 +118,6 @@ Here is the equivalent between font weight and style name.
| ---------- | ----------- |
| Solid | 900 |
| Regular | 400 |
| Light | 300 |
| Thin | 100 |
### CSS

View File

@@ -7,7 +7,7 @@
@remove="emit('removeSort', property)"
>
<span class="property">
<UiIcon :icon="isAscending ? faCaretUp : faCaretDown" />
<FontAwesomeIcon :icon="isAscending ? faCaretUp : faCaretDown" />
{{ property }}
</span>
</UiFilter>
@@ -57,11 +57,10 @@ import {
faSort,
} from "@fortawesome/free-solid-svg-icons";
import FormWidget from "@/components/FormWidget.vue";
import UiActionButton from "@/components/ui/UiActionButton.vue";
import UiButton from "@/components/ui/UiButton.vue";
import UiActionButton from "@/components/ui/UiActionButton.vue";
import UiFilter from "@/components/ui/UiFilter.vue";
import UiFilterGroup from "@/components/ui/UiFilterGroup.vue";
import UiIcon from "@/components/ui/UiIcon.vue";
import UiModal from "@/components/ui/UiModal.vue";
import useModal from "@/composables/modal.composable";

View File

@@ -2,7 +2,7 @@
<th>
<div class="content">
<span class="label">
<UiIcon :icon="icon" />
<FontAwesomeIcon v-if="icon" :icon="icon" />
<slot />
</span>
</div>
@@ -11,7 +11,6 @@
<script lang="ts" setup>
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import UiIcon from "@/components/ui/UiIcon.vue";
defineProps<{
icon?: IconDefinition;

View File

@@ -8,7 +8,7 @@
<span class="widget">
<span v-if="before || $slots.before" class="before">
<slot name="before">
<UiIcon v-if="isIcon(before)" :icon="before" fixed-width />
<FontAwesomeIcon v-if="isIcon(before)" :icon="before" fixed-width />
<template v-else>{{ before }}</template>
</slot>
</span>
@@ -17,7 +17,7 @@
</span>
<span v-if="after || $slots.after" class="after">
<slot name="after">
<UiIcon v-if="isIcon(after)" :icon="after" fixed-width />
<FontAwesomeIcon v-if="isIcon(after)" :icon="after" fixed-width />
<template v-else>{{ after }}</template>
</slot>
</span>
@@ -27,7 +27,6 @@
<script lang="ts" setup>
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import UiIcon from "@/components/ui/UiIcon.vue";
defineProps<{
before?: IconDefinition | string | object; // "object" added as workaround

View File

@@ -1,5 +1,5 @@
<template>
<UiIcon :class="className" :icon="icon" class="power-state-icon" />
<FontAwesomeIcon class="power-state-icon" :class="className" :icon="icon" />
</template>
<script lang="ts" setup>
@@ -11,7 +11,6 @@ import {
faQuestion,
faStop,
} from "@fortawesome/free-solid-svg-icons";
import UiIcon from "@/components/ui/UiIcon.vue";
import type { PowerState } from "@/libs/xen-api";
const props = defineProps<{
@@ -30,7 +29,7 @@ const icon = computed(() => icons[props.state] ?? faQuestion);
const className = computed(() => `state-${props.state.toLocaleLowerCase()}`);
</script>
<style lang="postcss" scoped>
<style scoped lang="postcss">
.power-state-icon {
color: var(--color-extra-blue-d60);

View File

@@ -1,6 +1,6 @@
<template>
<div class="title-bar">
<UiIcon :icon="icon" class="icon" />
<FontAwesomeIcon :icon="icon" class="icon" />
<div class="title">
<slot />
</div>
@@ -12,7 +12,6 @@
<script lang="ts" setup>
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import UiIcon from "@/components/ui/UiIcon.vue";
defineProps<{
icon: IconDefinition;

View File

@@ -1,14 +1,13 @@
<template>
<div class="infra-action">
<slot>
<UiIcon :icon="icon" fixed-width />
<FontAwesomeIcon :icon="icon" fixed-width />
</slot>
</div>
</template>
<script lang="ts" setup>
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import UiIcon from "@/components/ui/UiIcon.vue";
defineProps<{
icon?: IconDefinition;

View File

@@ -8,7 +8,7 @@
v-bind="$attrs"
>
<a :href="href" class="link" @click="navigate">
<UiIcon :icon="icon" class="icon" />
<FontAwesomeIcon :icon="icon" class="icon" />
<div class="text">
<slot />
</div>
@@ -23,7 +23,6 @@
<script lang="ts" setup>
import type { RouteLocationRaw } from "vue-router";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import UiIcon from "@/components/ui/UiIcon.vue";
defineProps<{
icon: IconDefinition;

View File

@@ -2,7 +2,7 @@
<li class="infra-loading-item">
<div class="infra-item-label-placeholder">
<div class="link-placeholder">
<UiIcon :icon="icon" class="icon" />
<FontAwesomeIcon :icon="icon" class="icon" />
<div class="loader">&nbsp;</div>
</div>
</div>
@@ -11,7 +11,6 @@
<script lang="ts" setup>
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import UiIcon from "@/components/ui/UiIcon.vue";
defineProps<{
icon: IconDefinition;

View File

@@ -4,14 +4,13 @@
<slot />
</span>
<span class="remove" @click.stop="emit('remove')">
<UiIcon :icon="faRemove" class="icon" />
<FontAwesomeIcon :icon="faRemove" class="icon" />
</span>
</span>
</template>
<script lang="ts" setup>
import { faRemove } from "@fortawesome/free-solid-svg-icons";
import UiIcon from "@/components/ui/UiIcon.vue";
const emit = defineEmits<{
(event: "edit"): void;

View File

@@ -1,10 +1,10 @@
<template>
<FontAwesomeIcon
v-if="icon !== undefined"
:fixed-width="fixedWidth"
:icon="icon"
:spin="busy"
class="ui-icon"
:fixed-width="fixedWidth"
/>
</template>
@@ -12,7 +12,6 @@
import { computed } from "vue";
import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import { faSpinner } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
const props = withDefaults(
defineProps<{

View File

@@ -8,11 +8,11 @@
>
<div class="container">
<span v-if="onClose" class="close-icon" @click="emit('close')">
<UiIcon :icon="faXmark" />
<FontAwesomeIcon :icon="faXmark" />
</span>
<div v-if="icon || $slots.icon" class="modal-icon">
<slot name="icon">
<UiIcon :icon="icon" />
<FontAwesomeIcon :icon="icon" />
</slot>
</div>
<UiTitle v-if="$slots.title" type="h4">
@@ -38,7 +38,6 @@ import type { IconDefinition } from "@fortawesome/fontawesome-common-types";
import { faXmark } from "@fortawesome/free-solid-svg-icons";
import { useMagicKeys, whenever } from "@vueuse/core";
import UiButtonGroup from "@/components/ui/UiButtonGroup.vue";
import UiIcon from "@/components/ui/UiIcon.vue";
import UiTitle from "@/components/ui/UiTitle.vue";
const props = withDefaults(

View File

@@ -3,11 +3,13 @@ import { createApp } from "vue";
import App from "@/App.vue";
import i18n from "@/i18n";
import router from "@/router";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
const app = createApp(App);
app.use(i18n);
app.use(createPinia());
app.use(router);
app.component("FontAwesomeIcon", FontAwesomeIcon);
app.mount("#root");

View File

@@ -0,0 +1,10 @@
'use strict'
class BrokenVhdError extends Error {
constructor(message, baseError) {
super(message)
this.code = 'BROKEN_VHD'
this.cause = baseError
}
}
module.exports = BrokenVhdError

View File

@@ -9,6 +9,7 @@ const assert = require('assert')
const { synchronized } = require('decorator-synchronized')
const promisify = require('promise-toolbox/promisify')
const zlib = require('zlib')
const BrokenVhdError = require('../BrokenVhdError')
const { debug } = createLogger('vhd-lib:VhdDirectory')
@@ -110,7 +111,14 @@ exports.VhdDirectory = class VhdDirectory extends VhdAbstract {
// EISDIR pathname refers to a directory and the access requested
// involved writing (that is, O_WRONLY or O_RDWR is set).
// reading the header ensure we have a well formed directory immediatly
await vhd.readHeaderAndFooter()
try {
await vhd.readHeaderAndFooter()
} catch (error) {
if (error.code === 'ERR_ASSERTION') {
throw new BrokenVhdError('Invalid header or footer', error)
}
throw error
}
return {
dispose: () => {},
value: vhd,
@@ -185,18 +193,8 @@ exports.VhdDirectory = class VhdDirectory extends VhdAbstract {
async readHeaderAndFooter() {
await this.#readChunkFilters()
let bufHeader, bufFooter
try {
bufHeader = (await this._readChunk('header')).buffer
bufFooter = (await this._readChunk('footer')).buffer
} catch (error) {
// emit an AssertionError if the VHD is broken to stay as close as possible to the VhdFile API
if (error.code === 'ENOENT') {
assert(false, 'Header And Footer should exists')
} else {
throw error
}
}
const bufHeader = (await this._readChunk('header')).buffer
const bufFooter = (await this._readChunk('footer')).buffer
const footer = unpackFooter(bufFooter)
const header = unpackHeader(bufHeader, footer)

View File

@@ -10,6 +10,7 @@ const {
} = require('../_constants')
const { computeBatSize, sectorsToBytes, unpackHeader, unpackFooter, BUF_BLOCK_UNUSED } = require('./_utils')
const { createLogger } = require('@xen-orchestra/log')
const BrokenVhdError = require('../BrokenVhdError')
const { fuFooter, fuHeader, checksumStruct } = require('../_structs')
const { set: mapSetBit } = require('../_bitmap')
const { VhdAbstract } = require('./VhdAbstract')
@@ -93,7 +94,14 @@ exports.VhdFile = class VhdFile extends VhdAbstract {
// EISDIR pathname refers to a directory and the access requested
// involved writing (that is, O_WRONLY or O_RDWR is set).
// reading the header ensure we have a well formed file immediatly
await vhd.readHeaderAndFooter(checkSecondFooter)
try {
await vhd.readHeaderAndFooter(checkSecondFooter)
} catch (error) {
if (error.code === 'ERR_ASSERTION') {
throw new BrokenVhdError('Invalid header or footer', error)
}
throw error
}
return {
dispose: () => handler.closeFile(fd),
value: vhd,

View File

@@ -1,5 +1,6 @@
'use strict'
exports.BrokenVhdError = require('./BrokenVhdError')
exports.chainVhd = require('./chain')
exports.checkFooter = require('./checkFooter')
exports.checkVhdChain = require('./checkChain')

View File

@@ -1,17 +1,37 @@
'use strict'
const { BrokenVhdError } = require('.')
const { resolveVhdAlias } = require('./aliases')
const { VhdDirectory } = require('./Vhd/VhdDirectory.js')
const { VhdFile } = require('./Vhd/VhdFile.js')
class AggregateError extends Error {
constructor(errors, message) {
super(message)
this.errors = errors
}
}
exports.openVhd = async function openVhd(handler, path, opts) {
const resolved = await resolveVhdAlias(handler, path)
try {
return await VhdFile.open(handler, resolved, opts)
} catch (e) {
if (e.code !== 'EISDIR') {
throw e
}
return await VhdDirectory.open(handler, resolved, opts)
} catch (vhdDirectoryError) {
// it's a directory, but it's an invalid vhd
if (vhdDirectoryError instanceof BrokenVhdError) {
throw vhdDirectoryError
}
// it's not a vhd directory, try to open it as a vhd file
try {
return await VhdFile.open(handler, resolved, opts)
} catch (vhdFileError) {
// this is really a file that looks like a vhd but is broken
if (vhdFileError instanceof BrokenVhdError) {
throw vhdFileError
}
// the errors are not vhd related, throw both error to keep a trace
throw new AggregateError([vhdDirectoryError, vhdFileError])
}
}
}