Skip to content

Commit

Permalink
Add volume and image encryption feature
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 13, 2024
1 parent cb5f45f commit 3e19e57
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 29 deletions.
9 changes: 9 additions & 0 deletions pkg/harvester/config/harvester-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,12 @@ export const ADD_ONS = {
RANCHER_MONITORING: 'rancher-monitoring',
VM_IMPORT_CONTROLLER: 'vm-import-controller',
};

export const CSI_SECRETS = {
CSI_PROVISIONER_SECRET_NAME: 'csi.storage.k8s.io/provisioner-secret-name',
CSI_PROVISIONER_SECRET_NAMESPACE: 'csi.storage.k8s.io/provisioner-secret-namespace',
CSI_NODE_PUBLISH_SECRET_NAME: 'csi.storage.k8s.io/node-publish-secret-name',
CSI_NODE_PUBLISH_SECRET_NAMESPACE: 'csi.storage.k8s.io/node-publish-secret-namespace',
CSI_NODE_STAGE_SECRET_NAME: 'csi.storage.k8s.io/node-stage-secret-name',
CSI_NODE_STAGE_SECRET_NAMESPACE: 'csi.storage.k8s.io/node-stage-secret-namespace',
};
2 changes: 0 additions & 2 deletions pkg/harvester/config/table-headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,13 @@ export const IMAGE_DOWNLOAD_SIZE = {
labelKey: 'tableHeaders.size',
value: 'downSize',
sort: 'status.size',
width: 120
};

export const IMAGE_VIRTUAL_SIZE = {
name: 'virtualSize',
labelKey: 'harvester.tableHeaders.virtualSize',
value: 'virtualSize',
sort: 'status.virtualSize',
width: 120
};

export const IMAGE_PROGRESS = {
Expand Down
43 changes: 41 additions & 2 deletions pkg/harvester/detail/harvesterhci.io.virtualmachineimage/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import Tabbed from '@shell/components/Tabbed';
import Tab from '@shell/components/Tabbed/Tab';
import { findBy } from '@shell/utils/array';
import { get } from '@shell/utils/object';
import { ucFirst } from '@shell/utils/string';
import Storage from './Storage';
import { SECRET } from '@shell/config/types';
export default {
components: {
Expand All @@ -26,8 +27,14 @@ export default {
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
this.secrets = await this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET });
},
data() {
return {};
return { secrets: [] };
},
computed: {
Expand Down Expand Up @@ -57,6 +64,21 @@ export default {
return this.value?.spec?.sourceType === 'upload';
},
encryptionSecret() {
if (!this.value.isEncrypted) {
return '-';
}
return this.value.encryptionSecret;
},
secretLink() {
return this.secrets.find(sc => sc.id === this.value.encryptionSecret)?.detailLocation;
},
isEncryptedString() {
return ucFirst(String(this.value.isEncrypted));
},
imageName() {
return this.value?.metadata?.annotations?.[HCI.IMAGE_NAME] || '-';
},
Expand Down Expand Up @@ -116,6 +138,23 @@ export default {
</div>
</div>
<div class="row">
<div class="col span-12">
<LabelValue :name="t('harvester.image.isEncryption')" :value="isEncryptedString" class="mb-20" />
</div>
</div>
<div v-if="value.isEncrypted" class="row">
<div class="col span-12">
<div class="text-label">
{{ t('harvester.image.encryptionSecret') }}
</div>
<n-link v-if="secretLink" :to="secretLink">
{{ encryptionSecret }}
</n-link>
</div>
</div>
<div v-if="errorMessage !== '-'" class="row">
<div class="col span-12">
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import KeyValue from '@shell/components/form/KeyValue';
import LabeledSelect from '@shell/components/form/LabeledSelect';
import { LabeledInput } from '@components/Form/LabeledInput';
import RadioGroup from '@components/Form/Radio/RadioGroup';
import { SECRET, NAMESPACE, LONGHORN } from '@shell/config/types';
import { _CREATE, _VIEW } from '@shell/config/query-params';
import { LONGHORN } from '@shell/config/types';
import { CSI_SECRETS } from '@pkg/harvester/config/harvester-map';
import { clone } from '@shell/utils/object';
import { uniq } from '@shell/utils/array';
Expand All @@ -15,8 +15,18 @@ const DEFAULT_PARAMETERS = [
'diskSelector',
'nodeSelector',
'migratable',
'encrypted',
];
const {
CSI_PROVISIONER_SECRET_NAME,
CSI_PROVISIONER_SECRET_NAMESPACE,
CSI_NODE_PUBLISH_SECRET_NAME,
CSI_NODE_PUBLISH_SECRET_NAMESPACE,
CSI_NODE_STAGE_SECRET_NAME,
CSI_NODE_STAGE_SECRET_NAMESPACE
} = CSI_SECRETS;
export default {
components: {
KeyValue,
Expand All @@ -40,18 +50,30 @@ export default {
},
},
async fetch() {
const inStore = this.$store.getters['currentProduct'].inStore;
const allNamespaces = await this.$store.dispatch(`${ inStore }/findAll`, { type: NAMESPACE });
this.secrets = await this.$store.dispatch(`${ inStore }/findAll`, { type: SECRET });
this.namespaces = allNamespaces.filter(ns => ns.isSystem === false).map(ns => ns.id); // only show non-system namespaces to user to select
},
data() {
if (this.realMode === _CREATE) {
this.$set(this.value, 'parameters', {
numberOfReplicas: '3',
staleReplicaTimeout: '30',
diskSelector: null,
nodeSelector: null,
encrypted: 'false',
migratable: 'true',
});
}
return {};
return {
secrets: [],
namespaces: [],
};
},
computed: {
Expand Down Expand Up @@ -97,6 +119,26 @@ export default {
}];
},
secretOptions() {
const selectedNS = this.secretNamespace;
return this.secrets.filter(secret => secret.namespace === selectedNS).map(secret => secret.name);
},
secretNameOptions() {
return this.namespaces;
},
volumeEncryptionOptions() {
return [{
label: this.t('generic.yes'),
value: 'true'
}, {
label: this.t('generic.no'),
value: 'false'
}];
},
parameters: {
get() {
const parameters = clone(this.value?.parameters) || {};
Expand All @@ -113,6 +155,49 @@ export default {
}
},
volumeEncryption: {
set(neu) {
this.$set(this.value, 'parameters', {
...this.value.parameters,
encrypted: neu
});
},
get() {
return this.value?.parameters?.encrypted || 'false';
}
},
secretName: {
get() {
return this.value.parameters[CSI_PROVISIONER_SECRET_NAME];
},
set(neu) {
this.$set(this.value, 'parameters', {
...this.value.parameters,
[CSI_PROVISIONER_SECRET_NAME]: neu,
[CSI_NODE_PUBLISH_SECRET_NAME]: neu,
[CSI_NODE_STAGE_SECRET_NAME]: neu
});
}
},
secretNamespace: {
get() {
return this.value.parameters[CSI_PROVISIONER_SECRET_NAMESPACE];
},
set(neu) {
this.$set(this.value, 'parameters', {
...this.value.parameters,
[CSI_PROVISIONER_SECRET_NAMESPACE]: neu,
[CSI_NODE_PUBLISH_SECRET_NAMESPACE]: neu,
[CSI_NODE_STAGE_SECRET_NAMESPACE]: neu
});
}
},
nodeSelector: {
get() {
const nodeSelector = this.value?.parameters?.nodeSelector;
Expand Down Expand Up @@ -221,14 +306,39 @@ export default {
</LabeledSelect>
</div>
</div>
<div class="row mt-10">
<div class="row mt-20">
<RadioGroup
v-model="value.parameters.migratable"
name="layer3NetworkMode"
:label="t('harvester.storage.parameters.migratable.label')"
:mode="mode"
:options="migratableOptions"
/>
</div>
<div class="row mt-20">
<RadioGroup
v-model="volumeEncryption"
name="volumeEncryption"
:label="t('harvester.storage.volumeEncryption')"
:mode="mode"
:options="volumeEncryptionOptions"
/>
</div>
<div v-if="value.parameters.encrypted === 'true'" class="row mt-20">
<div class="col span-6">
<RadioGroup
v-model="value.parameters.migratable"
name="layer3NetworkMode"
:label="t('harvester.storage.parameters.migratable.label')"
<LabeledSelect
v-model="secretNamespace"
:label="t('harvester.storage.secretNamespace')"
:options="secretNameOptions"
:mode="mode"
/>
</div>
<div class="col span-6">
<LabeledSelect
v-model="secretName"
:label="t('harvester.storage.secretName')"
:options="secretOptions"
:mode="mode"
:options="migratableOptions"
/>
</div>
</div>
Expand Down
Loading

0 comments on commit 3e19e57

Please sign in to comment.