Skip to content

Commit

Permalink
add encrypt image and decrypt image actions
Browse files Browse the repository at this point in the history
Signed-off-by: andy.lee <andy.lee@suse.com>
  • Loading branch information
a110605 committed Sep 24, 2024
1 parent f50a27f commit 38cc611
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 25 deletions.
58 changes: 54 additions & 4 deletions pkg/harvester/detail/harvesterhci.io.virtualmachineimage/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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 '-';
Expand All @@ -80,7 +113,7 @@ export default {
},
imageName() {
return this.value?.metadata?.annotations?.[HCI.IMAGE_NAME] || '-';
return this.value?.metadata?.annotations?.[HCI_ANNOTATIONS.IMAGE_NAME] || '-';
},
}
};
Expand Down Expand Up @@ -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') }}
Expand All @@ -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">
&mdash;
</span>
</div>
</div>
<div v-if="errorMessage !== '-'" class="row">
<div class="col span-12">
<div>
Expand Down
54 changes: 35 additions & 19 deletions pkg/harvester/edit/harvesterhci.io.virtualmachineimage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,40 @@ export default {
this.$set(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.$set(this.value, 'spec', { sourceType: DOWNLOAD });
this.$set(this.value, 'spec', { sourceType: sourceType || DOWNLOAD });
}
if (image && cryptoOperation) {
this.$set(this.value.spec, 'securityParameters', {
cryptoOperation,
sourceImageName: image.metadata.name,
sourceImageNamespace: image.metadata.namespace
});
}
if (!this.value.metadata.name) {
this.value.metadata.generateName = 'image-';
}
return {
selectedImage: null,
selectedImage: image || null,
images: [],
url: this.value.spec.url,
files: [],
Expand Down Expand Up @@ -145,22 +165,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;
}
Expand All @@ -184,15 +209,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();
Expand Down Expand Up @@ -448,7 +464,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"
Expand Down
6 changes: 4 additions & 2 deletions pkg/harvester/l10n/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ harvester:
warning: Warning
error: Error
action:
createVM: Create a Virtual Machine
createVM: Create Virtual Machine
start: Start
restart: Restart
softreboot: Soft Reboot
Expand All @@ -135,6 +135,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
Expand Down Expand Up @@ -734,7 +736,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
Expand Down
48 changes: 48 additions & 0 deletions pkg/harvester/models/harvesterhci.io.virtualmachineimage.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,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,
Expand All @@ -68,6 +82,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) {
Vue.set(this.metadata, 'labels', { [HCI_ANNOTATIONS.OS_TYPE]: '', [HCI_ANNOTATIONS.IMAGE_SUFFIX]: '' });
Expand Down Expand Up @@ -152,6 +196,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);
Expand Down

0 comments on commit 38cc611

Please sign in to comment.