mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2025-12-13 21:21:44 +00:00
add encrypt image and decrypt image actions
Signed-off-by: andy.lee <andy.lee@suse.com>
This commit is contained in:
parent
8117e99b54
commit
228369ad88
@ -2,7 +2,8 @@
|
||||
import CopyToClipboardText from '@shell/components/CopyToClipboardText';
|
||||
import LabelValue from '@shell/components/LabelValue';
|
||||
import { DESCRIPTION } from '@shell/config/labels-annotations';
|
||||
import { HCI } from '@pkg/harvester/config/labels-annotations';
|
||||
import { HCI as HCI_ANNOTATIONS } from '@pkg/harvester/config/labels-annotations';
|
||||
import { HCI } from '../../types';
|
||||
import Tabbed from '@shell/components/Tabbed';
|
||||
import Tab from '@shell/components/Tabbed/Tab';
|
||||
import { findBy } from '@shell/utils/array';
|
||||
@ -31,10 +32,14 @@ export default {
|
||||
const inStore = this.$store.getters['currentProduct'].inStore;
|
||||
|
||||
this.secrets = await this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET });
|
||||
this.images = await this.$store.dispatch(`${ inStore }/findAll`, { type: HCI.IMAGE });
|
||||
},
|
||||
|
||||
data() {
|
||||
return { secrets: [] };
|
||||
return {
|
||||
secrets: [],
|
||||
images: []
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
@ -64,6 +69,34 @@ export default {
|
||||
return this.value?.spec?.sourceType === 'upload';
|
||||
},
|
||||
|
||||
sourceImage() {
|
||||
const { sourceImageName, sourceImageNamespace } = this.value?.spec?.securityParameters || {};
|
||||
|
||||
if (sourceImageNamespace && sourceImageName) {
|
||||
const imageId = `${ sourceImageNamespace }/${ sourceImageName }`;
|
||||
|
||||
return this.images.find(image => image.id === imageId);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
sourceImageLink() {
|
||||
return this.sourceImage?.detailLocation;
|
||||
},
|
||||
|
||||
sourceImageId() {
|
||||
if (this.sourceImage) {
|
||||
return this.sourceImage.displayNameWithNamespace;
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
|
||||
isEncryptedOrDecrypted() {
|
||||
return ['encrypt', 'decrypt'].includes(this.value?.spec?.securityParameters?.cryptoOperation);
|
||||
},
|
||||
|
||||
encryptionSecret() {
|
||||
if (!this.value.isEncrypted) {
|
||||
return '-';
|
||||
@ -80,7 +113,7 @@ export default {
|
||||
},
|
||||
|
||||
imageName() {
|
||||
return this.value?.metadata?.annotations?.[HCI.IMAGE_NAME] || '-';
|
||||
return this.value?.metadata?.annotations?.[HCI_ANNOTATIONS.IMAGE_NAME] || '-';
|
||||
},
|
||||
}
|
||||
};
|
||||
@ -144,7 +177,7 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="value.isEncrypted" class="row">
|
||||
<div v-if="value.isEncrypted" class="row mb-20">
|
||||
<div class="col span-12">
|
||||
<div class="text-label">
|
||||
{{ t('harvester.image.encryptionSecret') }}
|
||||
@ -161,6 +194,23 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isEncryptedOrDecrypted" class="row mb-20">
|
||||
<div class="col span-12">
|
||||
<div class="text-label">
|
||||
{{ t('harvester.image.sourceImage') }}
|
||||
</div>
|
||||
<n-link v-if="sourceImageId && sourceImageLink" :to="sourceImageLink">
|
||||
{{ sourceImageId }}
|
||||
</n-link>
|
||||
<span v-else-if="sourceImageId">
|
||||
{{ sourceImageId }}
|
||||
</span>
|
||||
<span v-else class="text-muted">
|
||||
—
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="errorMessage !== '-'" class="row">
|
||||
<div class="col span-12">
|
||||
<div>
|
||||
|
||||
@ -62,12 +62,32 @@ export default {
|
||||
|
||||
this['storageClassName'] = this.storageClassName || defaultStorage?.metadata?.name || 'longhorn';
|
||||
this.images = this.$store.getters[`${ inStore }/all`](HCI.IMAGE);
|
||||
this.selectedImage = this.images.find(i => i.name === this.value.name) || null;
|
||||
|
||||
const { securityParameters } = this.value.spec;
|
||||
|
||||
// edit and view mode should show the source image
|
||||
if (securityParameters) {
|
||||
// image ns/name = image.id
|
||||
const sourceImage = `${ securityParameters.sourceImageNamespace }/${ securityParameters.sourceImageName }`;
|
||||
|
||||
this.selectedImage = this.images.find(image => image.id === sourceImage);
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
// pass from Encrypt Image / Decrypt Image actions
|
||||
const { image, sourceType, cryptoOperation } = this.$route.query || {};
|
||||
|
||||
if ( !this.value.spec ) {
|
||||
this.value['spec'] = { sourceType: DOWNLOAD };
|
||||
this.value['spec'] = { sourceType: sourceType || DOWNLOAD };
|
||||
}
|
||||
|
||||
if (image && cryptoOperation) {
|
||||
this.value.spec.securityParameters = {
|
||||
cryptoOperation,
|
||||
sourceImageName: image.metadata.name,
|
||||
sourceImageNamespace: image.metadata.namespace
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.value.metadata.name) {
|
||||
@ -75,7 +95,7 @@ export default {
|
||||
}
|
||||
|
||||
return {
|
||||
selectedImage: null,
|
||||
selectedImage: image || null,
|
||||
images: [],
|
||||
url: this.value.spec.url,
|
||||
files: [],
|
||||
@ -147,22 +167,27 @@ export default {
|
||||
options = this.images.filter(image => image.isEncrypted);
|
||||
}
|
||||
|
||||
return options.map(image => image.spec.displayName);
|
||||
return options.map(image => image.displayNameWithNamespace);
|
||||
},
|
||||
sourceImageName: {
|
||||
sourceImage: {
|
||||
get() {
|
||||
return this.selectedImage?.spec.displayName;
|
||||
if (this.selectedImage) {
|
||||
return this.selectedImage.displayNameWithNamespace;
|
||||
}
|
||||
|
||||
return '';
|
||||
},
|
||||
set(imageDisplayName) {
|
||||
this.selectedImage = this.images.find(i => i.spec.displayName === imageDisplayName);
|
||||
set(neu) {
|
||||
this.selectedImage = this.images.find(i => i.displayNameWithNamespace === neu);
|
||||
// sourceImageName should bring the name of the image
|
||||
this.value.spec.securityParameters.sourceImageName = this.selectedImage?.metadata.name || '';
|
||||
this.value.spec.securityParameters.sourceImageNamespace = this.selectedImage?.metadata.namespace || '';
|
||||
}
|
||||
},
|
||||
sourceType: {
|
||||
get() {
|
||||
if (this.value.spec.sourceType === CLONE) {
|
||||
return this.value.spec.securityParameters.cryptoOperation;
|
||||
return this.value.spec?.securityParameters?.cryptoOperation;
|
||||
} else {
|
||||
return this.value.spec.sourceType;
|
||||
}
|
||||
@ -186,15 +211,6 @@ export default {
|
||||
},
|
||||
|
||||
watch: {
|
||||
'value.metadata.namespace'(neu) {
|
||||
if (this.value.spec.sourceType === CLONE) {
|
||||
this.$set(this.value.spec, 'securityParameters', {
|
||||
cryptoOperation: this.value.spec.securityParameters.cryptoOperation,
|
||||
sourceImageName: '',
|
||||
sourceImageNamespace: neu
|
||||
});
|
||||
}
|
||||
},
|
||||
'value.spec.url'(neu) {
|
||||
const url = neu.trim();
|
||||
|
||||
@ -451,7 +467,7 @@ export default {
|
||||
|
||||
<LabeledSelect
|
||||
v-if="value.spec.sourceType === 'clone'"
|
||||
v-model="sourceImageName"
|
||||
v-model="sourceImage"
|
||||
:options="sourceImageOptions"
|
||||
:label="t('harvester.image.sourceImage')"
|
||||
:mode="mode"
|
||||
|
||||
@ -153,7 +153,7 @@ harvester:
|
||||
warning: Warning
|
||||
error: Error
|
||||
action:
|
||||
createVM: Create a Virtual Machine
|
||||
createVM: Create Virtual Machine
|
||||
start: Start
|
||||
restart: Restart
|
||||
softreboot: Soft Reboot
|
||||
@ -162,6 +162,8 @@ harvester:
|
||||
deepClone: Clone
|
||||
shallowClone: Clone Template
|
||||
unpause: Unpause
|
||||
encryptImage: Encrypt Image
|
||||
decryptImage: Decrypt Image
|
||||
ejectCDROM: Eject CD-ROM
|
||||
editVMQuota: Edit VM Quota
|
||||
launchFormTemplate: Launch instance from template
|
||||
@ -778,7 +780,7 @@ harvester:
|
||||
urlTip: 'Supports the <code>raw</code> and <code>qcow2</code> image formats which are supported by <a href="https://www.qemu.org/docs/master/system/images.html#disk-image-file-formats" target="_blank">qemu</a>. Bootable ISO images can also be used and are treated like <code>raw</code> images.'
|
||||
fileName: File Name
|
||||
uploadFile: Upload File
|
||||
source: Source
|
||||
source: Source Type
|
||||
sourceType:
|
||||
download: URL
|
||||
upload: File
|
||||
|
||||
@ -57,6 +57,20 @@ export default class HciVmImage extends HarvesterResource {
|
||||
label: this.t('harvester.action.createVM'),
|
||||
disabled: !this.isReady,
|
||||
},
|
||||
{
|
||||
action: 'encryptImage',
|
||||
enabled: !this.isEncrypted,
|
||||
icon: 'icon icon-lock',
|
||||
label: this.t('harvester.action.encryptImage'),
|
||||
disabled: !this.isReady,
|
||||
},
|
||||
{
|
||||
action: 'decryptImage',
|
||||
enabled: this.isEncrypted,
|
||||
icon: 'icon icon-unlock',
|
||||
label: this.t('harvester.action.decryptImage'),
|
||||
disabled: !this.isReady,
|
||||
},
|
||||
{
|
||||
action: 'download',
|
||||
enabled: this.links?.download,
|
||||
@ -67,6 +81,36 @@ export default class HciVmImage extends HarvesterResource {
|
||||
];
|
||||
}
|
||||
|
||||
encryptImage() {
|
||||
const router = this.currentRouter();
|
||||
|
||||
router.push({
|
||||
name: `${ HARVESTER_PRODUCT }-c-cluster-resource-create`,
|
||||
params: { resource: HCI.IMAGE },
|
||||
query: {
|
||||
image: this,
|
||||
fromPage: HCI.IMAGE,
|
||||
sourceType: 'clone',
|
||||
cryptoOperation: 'encrypt'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
decryptImage() {
|
||||
const router = this.currentRouter();
|
||||
|
||||
router.push({
|
||||
name: `${ HARVESTER_PRODUCT }-c-cluster-resource-create`,
|
||||
params: { resource: HCI.IMAGE },
|
||||
query: {
|
||||
image: this,
|
||||
fromPage: HCI.IMAGE,
|
||||
sourceType: 'clone',
|
||||
cryptoOperation: 'decrypt'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
applyDefaults(resources = this, realMode) {
|
||||
if (realMode !== _CLONE) {
|
||||
this.metadata['labels'] = { [HCI_ANNOTATIONS.OS_TYPE]: '', [HCI_ANNOTATIONS.IMAGE_SUFFIX]: '' };
|
||||
@ -151,6 +195,10 @@ export default class HciVmImage extends HarvesterResource {
|
||||
!!this.spec.securityParameters?.sourceImageNamespace;
|
||||
}
|
||||
|
||||
get displayNameWithNamespace() {
|
||||
return `${ this.metadata.namespace }/${ this.spec.displayName }`;
|
||||
}
|
||||
|
||||
get imageMessage() {
|
||||
if (this.uploadError) {
|
||||
return ucFirst(this.uploadError);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user