mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-01 11:46:06 +08:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbc415a2b4 | ||
|
|
c12d4b5fba | ||
|
|
dc91be11f9 | ||
|
|
e5b72499b5 | ||
|
|
e66037b007 | ||
|
|
c676cad057 | ||
|
|
254abd4648 | ||
|
|
1df1d3534a | ||
|
|
ff1e6e6e7f | ||
|
|
622242e4a5 |
@@ -4,7 +4,7 @@ description: Setup node and install dependencies
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
@@ -12,6 +12,6 @@ jobs:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: rancher/gh-issue-mgr/auto-assign-action@main
|
||||
- uses: rancher/gh-issue-mgr/auto-assign-action@b70f0bdf12a03e5e3f33e4f92ccb6c89deb3ebd9 # main
|
||||
with:
|
||||
configuration-path: .github/auto-assign-config.yaml
|
||||
configuration-path: .github/auto-assign-config.yaml
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
ref: ${{ github.base_ref }}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
ref: ${{ github.base_ref }}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Check package version
|
||||
env:
|
||||
|
||||
@@ -25,12 +25,12 @@ jobs:
|
||||
name: Build & Upload Hosted
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
# Note - Cannot use the setup action here as it uses a different yarn install arg
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
@@ -45,19 +45,20 @@ jobs:
|
||||
run: ./scripts/build-upload-gate
|
||||
|
||||
- name: Get gcs auth
|
||||
uses: rancher-eio/read-vault-secrets@main
|
||||
uses: rancher-eio/read-vault-secrets@0da85151ad1f19ed7986c41587e45aac1ace74b6 # v3
|
||||
with:
|
||||
secrets: |
|
||||
secret/data/github/repo/${{ github.repository }}/google-auth/harvester/credentials token | GOOGLE_AUTH ;
|
||||
|
||||
- name: Apply gcs auth
|
||||
# https://github.com/google-github-actions/auth
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2
|
||||
|
||||
with:
|
||||
credentials_json: "${{ env.GOOGLE_AUTH }}"
|
||||
|
||||
- name: Upload build
|
||||
uses: 'google-github-actions/upload-cloud-storage@v2'
|
||||
uses: google-github-actions/upload-cloud-storage@c0f6160ff80057923ff50e5e567695cea181ec23 # v2
|
||||
# https://github.com/google-github-actions/upload-cloud-storage
|
||||
with:
|
||||
path: ${{steps.build-hosted.outputs.BUILD_HOSTED_DIR}}
|
||||
@@ -71,12 +72,12 @@ jobs:
|
||||
name: Build & Upload Embedded
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
# Note - Cannot use the setup action here as it uses a different yarn install arg
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
@@ -89,19 +90,19 @@ jobs:
|
||||
DISABLED_EMBED_PKG: https://releases.rancher.com/harvester-ui/plugin/harvester-1.0.3.tar.gz
|
||||
|
||||
- name: Get gcs auth
|
||||
uses: rancher-eio/read-vault-secrets@main
|
||||
uses: rancher-eio/read-vault-secrets@0da85151ad1f19ed7986c41587e45aac1ace74b6 # v3
|
||||
with:
|
||||
secrets: |
|
||||
secret/data/github/repo/${{ github.repository }}/google-auth/harvester/credentials token | GOOGLE_AUTH ;
|
||||
|
||||
- name: Apply gcs auth
|
||||
# https://github.com/google-github-actions/auth
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2
|
||||
with:
|
||||
credentials_json: "${{ env.GOOGLE_AUTH }}"
|
||||
|
||||
- name: Upload tar
|
||||
uses: 'google-github-actions/upload-cloud-storage@v2'
|
||||
uses: google-github-actions/upload-cloud-storage@c0f6160ff80057923ff50e5e567695cea181ec23 # v2
|
||||
with:
|
||||
path: ${{steps.build-embedded.outputs.BUILD_EMBEDED_TGZ}}
|
||||
destination: releases.rancher.com/harvester-ui/dashboard
|
||||
@@ -114,12 +115,12 @@ jobs:
|
||||
name: Build & Upload Plugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
# Note - Cannot use the setup action here as it uses a different yarn install arg
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
@@ -133,19 +134,19 @@ jobs:
|
||||
run: ./scripts/build-upload-gate
|
||||
|
||||
- name: Get gcs auth
|
||||
uses: rancher-eio/read-vault-secrets@main
|
||||
uses: rancher-eio/read-vault-secrets@0da85151ad1f19ed7986c41587e45aac1ace74b6 # v3
|
||||
with:
|
||||
secrets: |
|
||||
secret/data/github/repo/${{ github.repository }}/google-auth/harvester/credentials token | GOOGLE_AUTH ;
|
||||
|
||||
- name: Apply gcs auth
|
||||
# https://github.com/google-github-actions/auth
|
||||
uses: 'google-github-actions/auth@v2'
|
||||
uses: google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed # v2
|
||||
with:
|
||||
credentials_json: "${{ env.GOOGLE_AUTH }}"
|
||||
|
||||
- name: Upload plugin tar
|
||||
uses: 'google-github-actions/upload-cloud-storage@v2'
|
||||
uses: google-github-actions/upload-cloud-storage@c0f6160ff80057923ff50e5e567695cea181ec23 # v2
|
||||
with:
|
||||
path: dist-pkg/${{steps.ci-build-pkg.outputs.PKG_TARBALL}}
|
||||
destination: releases.rancher.com/harvester-ui/plugin
|
||||
@@ -155,7 +156,7 @@ jobs:
|
||||
process_gcloudignore: false
|
||||
|
||||
- name: Upload plugin directory
|
||||
uses: 'google-github-actions/upload-cloud-storage@v2'
|
||||
uses: google-github-actions/upload-cloud-storage@c0f6160ff80057923ff50e5e567695cea181ec23 # v2
|
||||
with:
|
||||
path: dist-pkg/${{steps.ci-build-pkg.outputs.PKG_NAME}}
|
||||
destination: releases.rancher.com/harvester-ui/plugin/${{steps.ci-build-pkg.outputs.PKG_NAME}}
|
||||
|
||||
@@ -27,14 +27,14 @@ jobs:
|
||||
build-status: ${{ job.status }}
|
||||
steps:
|
||||
- name: Read Secrets
|
||||
uses: rancher-eio/read-vault-secrets@main
|
||||
uses: rancher-eio/read-vault-secrets@0da85151ad1f19ed7986c41587e45aac1ace74b6 # v3
|
||||
with:
|
||||
secrets: |
|
||||
secret/data/github/repo/${{ github.repository }}/dockerhub/rancher/credentials username | DOCKER_USERNAME ;
|
||||
secret/data/github/repo/${{ github.repository }}/dockerhub/rancher/credentials password | DOCKER_PASSWORD ;
|
||||
|
||||
- name: Checkout repository (normal flow)
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Enable Corepack
|
||||
run: corepack enable
|
||||
@@ -45,18 +45,18 @@ jobs:
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
with:
|
||||
username: ${{ env.DOCKER_USERNAME }}
|
||||
password: ${{ env.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Setup Helm
|
||||
uses: azure/setup-helm@v3
|
||||
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3
|
||||
with:
|
||||
version: v3.8.0
|
||||
|
||||
- name: Setup Nodejs with yarn caching
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: yarn
|
||||
@@ -67,4 +67,4 @@ jobs:
|
||||
- name: Build and push UI image
|
||||
run: |
|
||||
publish="yarn publish-pkgs -cp -r ${{ inputs.registry_target }} -o ${{ inputs.registry_user }}"
|
||||
$publish
|
||||
$publish
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
target_branch: ${{ steps.get-version.outputs.target_branch }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Determine target branch
|
||||
id: get-version
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
version: ${{ steps.get_version.outputs.version }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Extract version from package.json
|
||||
id: get_version
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Setup environment
|
||||
run: |
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
yarn install --frozen-lockfile
|
||||
|
||||
- name: Setup Helm
|
||||
uses: azure/setup-helm@v3
|
||||
uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3
|
||||
with:
|
||||
version: v3.8.0
|
||||
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
yarn publish-pkgs -s ${{ github.repository }} -b ${{ needs.setup-target-branch.outputs.target_branch }} -t harvester-${{ needs.extract-version.outputs.version }}
|
||||
|
||||
- name: Upload charts artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: charts
|
||||
path: tmp
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout release branch
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
ref: '${{ github.ref_name }}'
|
||||
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
echo "LAST_COMMIT=${LAST_COMMIT}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout target branch
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
ref: '${{ needs.setup-target-branch.outputs.target_branch }}'
|
||||
|
||||
@@ -121,7 +121,7 @@ jobs:
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: charts
|
||||
|
||||
@@ -132,7 +132,7 @@ jobs:
|
||||
git push origin ${{ needs.setup-target-branch.outputs.target_branch }}
|
||||
|
||||
- name: Run Helm chart releaser
|
||||
uses: helm/chart-releaser-action@v1.7.0
|
||||
uses: helm/chart-releaser-action@cae68fefc6b5f367a0275617c9f83181ba54714f # v1.7.0
|
||||
with:
|
||||
charts_dir: ./charts
|
||||
env:
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
release_tag: ${{ steps.determine_tag.outputs.release_tag }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- name: Determine release tag
|
||||
id: determine_tag
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- name: Check package version
|
||||
env:
|
||||
TAG_VERSION: ${{ github.event.release.tag_name }}
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
needs:
|
||||
- setup-release-tag
|
||||
- check-version
|
||||
uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@master
|
||||
uses: rancher/dashboard/.github/workflows/build-extension-charts.yml@9eb70a732e9be146722e1dbab431338366c2afc6 # creators-pkg-v3.0.10
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- name: Check package version
|
||||
env:
|
||||
TAG_VERSION: ${{github.ref_name}}
|
||||
|
||||
@@ -20,13 +20,13 @@ jobs:
|
||||
# The FOSSA token is shared between all repos in Harvester's GH org. It can
|
||||
# be used directly and there is no need to request specific access to EIO.
|
||||
- name: Read FOSSA token
|
||||
uses: rancher-eio/read-vault-secrets@main
|
||||
uses: rancher-eio/read-vault-secrets@0da85151ad1f19ed7986c41587e45aac1ace74b6 # v3
|
||||
with:
|
||||
secrets: |
|
||||
secret/data/github/org/harvester/fossa/credentials token | FOSSA_API_KEY_PUSH_ONLY
|
||||
|
||||
- name: FOSSA scan
|
||||
uses: fossas/fossa-action@main
|
||||
uses: fossas/fossa-action@c414b9ad82eaad041e47a7cf62a4f02411f427a0 # v1.8.0
|
||||
with:
|
||||
api-key: ${{ env.FOSSA_API_KEY_PUSH_ONLY }}
|
||||
# Only runs the scan and do not provide/returns any results back to the
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
ref: ${{ github.base_ref }}
|
||||
- name: Setup Nodejs and yarn install
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0 # Need full history for commit-lint
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
@a110605
|
||||
@houhoucoop
|
||||
@@ -7,7 +7,7 @@ The Harvester UI Extension is a Rancher extension that provides the user interfa
|
||||
|
||||
## Installation
|
||||
|
||||
For detailed installation instructions, please refer to the [official Harvester documentation](https://docs.harvesterhci.io/v1.5/rancher/harvester-ui-extension#installation-on-rancher-210).
|
||||
For Harvester UI extension installation instructions, please refer to the page **Rancher Integration** -> **Harvester UI Extension** in [official Harvester documentation](https://docs.harvesterhci.io).
|
||||
|
||||
|
||||
## Development Setup
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "harvester-ui-extension",
|
||||
"version": "1.8.0-rc2",
|
||||
"version": "1.8.0-rc4",
|
||||
"private": false,
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
|
||||
@@ -7,8 +7,7 @@ The Harvester UI Extension is a Rancher extension that provides the user interfa
|
||||
|
||||
## Installation
|
||||
|
||||
For detailed installation instructions, please refer to the [official Harvester documentation](https://docs.harvesterhci.io/v1.5/rancher/harvester-ui-extension#installation-on-rancher-210).
|
||||
|
||||
For Harvester UI extension installation instructions, please refer to the page **Rancher Integration** -> **Harvester UI Extension** in [official Harvester documentation](https://docs.harvesterhci.io).
|
||||
|
||||
## Development Setup
|
||||
|
||||
|
||||
@@ -45,8 +45,8 @@ export default {
|
||||
|
||||
const cpu = { ...this.resources?.cpu };
|
||||
|
||||
if (cpu.v1 !== null) cpu.v1 = String(cpu.v1);
|
||||
if (cpu.v2 !== null) cpu.v2 = String(cpu.v2);
|
||||
if (cpu.v1 !== null && cpu.v1 !== undefined) cpu.v1 = String(cpu.v1);
|
||||
if (cpu.v2 !== null && cpu.v2 !== undefined) cpu.v2 = String(cpu.v2);
|
||||
|
||||
this.value.value = JSON.stringify({ ...this.resources, cpu });
|
||||
},
|
||||
|
||||
@@ -65,6 +65,7 @@ const FEATURE_FLAGS = {
|
||||
'vGPUAsPCIDevice',
|
||||
'instanceManagerResourcesSetting',
|
||||
'rwxNetworkSetting',
|
||||
'createPVCWithDataVolume'
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -80,4 +80,5 @@ export const HCI = {
|
||||
VOLUME_SNAPSHOT_CLASS: 'cdi.harvesterhci.io/storageProfileVolumeSnapshotClass',
|
||||
MAC_ADDRESS: 'harvesterhci.io/mac-address',
|
||||
NODE_UPGRADE_PAUSE_MAP: 'harvesterhci.io/node-upgrade-pause-map',
|
||||
CDI_POPULATOR_KIND: 'cdi.kubevirt.io/storage.populator.kind',
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ export const HCI_ALLOWED_SETTINGS = {
|
||||
kind: 'custom', from: 'import', canReset: true
|
||||
},
|
||||
[HCI_SETTING.RWX_NETWORK]: {
|
||||
kind: 'custom', from: 'import', canReset: true, featureFlag: 'rwxNetworkSetting'
|
||||
kind: 'json', from: 'import', canReset: true, featureFlag: 'rwxNetworkSetting'
|
||||
},
|
||||
[HCI_SETTING.VM_FORCE_RESET_POLICY]: { kind: 'json', from: 'import' },
|
||||
[HCI_SETTING.SSL_CERTIFICATES]: { kind: 'json', from: 'import' },
|
||||
|
||||
@@ -41,3 +41,8 @@ export const VMIMPORT_SOURCE_KINDS = {
|
||||
OPENSTACK: 'OpenstackSource',
|
||||
OVA: 'OvaSource',
|
||||
};
|
||||
|
||||
export const CDI_POPULATOR_KIND = {
|
||||
VOLUME_IMPORT_SOURCE: 'VolumeImportSource',
|
||||
VOLUME_CLONE_SOURCE: 'VolumeCloneSource',
|
||||
};
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
import { STORAGE_CLASS } from '@shell/config/types';
|
||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||
import { sortBy } from '@shell/utils/sort';
|
||||
import { isInternalStorageClass } from '../utils/storage-class';
|
||||
|
||||
import { Card } from '@components/Card';
|
||||
import { Banner } from '@components/Banner';
|
||||
import { LabeledInput } from '@components/Form/LabeledInput';
|
||||
import AsyncButton from '@shell/components/AsyncButton';
|
||||
import LabeledSelect from '@shell/components/form/LabeledSelect';
|
||||
|
||||
export default {
|
||||
name: 'HarvesterDataMigrationDialog',
|
||||
|
||||
emits: ['close'],
|
||||
|
||||
components: {
|
||||
AsyncButton, Banner, Card, LabeledInput, LabeledSelect
|
||||
},
|
||||
|
||||
props: {
|
||||
resources: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
async fetch() {
|
||||
this.storageClasses = await this.$store.dispatch('harvester/findAll', { type: STORAGE_CLASS });
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
targetVolumeName: '',
|
||||
targetStorageClassName: '',
|
||||
errors: [],
|
||||
storageClasses: [],
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters({ t: 'i18n/t' }),
|
||||
|
||||
actionResource() {
|
||||
return this.resources[0];
|
||||
},
|
||||
|
||||
storageClassOptions() {
|
||||
return sortBy(
|
||||
this.storageClasses
|
||||
.filter((sc) => !isInternalStorageClass(sc.metadata?.name))
|
||||
.map((sc) => ({
|
||||
label: sc.metadata?.name,
|
||||
value: sc.metadata?.name
|
||||
})),
|
||||
'label'
|
||||
);
|
||||
},
|
||||
|
||||
disableSave() {
|
||||
return !this.targetVolumeName || !this.targetStorageClassName;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
close() {
|
||||
this.targetVolumeName = '';
|
||||
this.targetStorageClassName = '';
|
||||
this.errors = [];
|
||||
this.$emit('close');
|
||||
},
|
||||
|
||||
async apply(buttonDone) {
|
||||
if (!this.actionResource) {
|
||||
buttonDone(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.targetVolumeName) {
|
||||
const name = this.t('harvester.modal.dataMigration.fields.targetVolumeName.label');
|
||||
|
||||
this['errors'] = [this.t('validation.required', { key: name })];
|
||||
buttonDone(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.targetStorageClassName) {
|
||||
const name = this.t('harvester.modal.dataMigration.fields.targetStorageClassName.label');
|
||||
|
||||
this['errors'] = [this.t('validation.required', { key: name })];
|
||||
buttonDone(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.actionResource.doAction('dataMigration', {
|
||||
targetVolumeName: this.targetVolumeName,
|
||||
targetStorageClassName: this.targetStorageClassName
|
||||
}, {}, false);
|
||||
|
||||
buttonDone(true);
|
||||
this.close();
|
||||
} catch (err) {
|
||||
const error = err?.data || err;
|
||||
|
||||
this['errors'] = exceptionToErrorsArray(error);
|
||||
buttonDone(false);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Card :show-highlight-border="false">
|
||||
<template #title>
|
||||
{{ t('harvester.modal.dataMigration.title') }}
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<LabeledInput
|
||||
v-model:value="targetVolumeName"
|
||||
:label="t('harvester.modal.dataMigration.fields.targetVolumeName.label')"
|
||||
:placeholder="t('harvester.modal.dataMigration.fields.targetVolumeName.placeholder')"
|
||||
class="mb-20"
|
||||
required
|
||||
/>
|
||||
|
||||
<LabeledSelect
|
||||
v-model:value="targetStorageClassName"
|
||||
:label="t('harvester.modal.dataMigration.fields.targetStorageClassName.label')"
|
||||
:placeholder="t('harvester.modal.dataMigration.fields.targetStorageClassName.placeholder')"
|
||||
:options="storageClassOptions"
|
||||
required
|
||||
/>
|
||||
|
||||
<Banner
|
||||
v-for="(err, i) in errors"
|
||||
:key="i"
|
||||
color="error"
|
||||
:label="err"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template
|
||||
#actions
|
||||
class="actions"
|
||||
>
|
||||
<div class="buttons">
|
||||
<button
|
||||
class="btn role-secondary mr-10"
|
||||
@click="close"
|
||||
>
|
||||
{{ t('generic.cancel') }}
|
||||
</button>
|
||||
|
||||
<AsyncButton
|
||||
mode="apply"
|
||||
:disabled="disableSave"
|
||||
@click="apply"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.actions {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -9,8 +9,11 @@ import { LabeledInput } from '@components/Form/LabeledInput';
|
||||
import NameNsDescription from '@shell/components/form/NameNsDescription';
|
||||
import Conditions from '@shell/components/form/Conditions';
|
||||
import { Banner } from '@components/Banner';
|
||||
import { Checkbox } from '@components/Form/Checkbox';
|
||||
import jsyaml from 'js-yaml';
|
||||
import { exceptionToErrorsArray } from '@shell/utils/error';
|
||||
import { allHash } from '@shell/utils/promise';
|
||||
import { get } from '@shell/utils/object';
|
||||
import { clone, get } from '@shell/utils/object';
|
||||
import { STORAGE_CLASS, LONGHORN, PV } from '@shell/config/types';
|
||||
import { sortBy } from '@shell/utils/sort';
|
||||
import { saferDump } from '@shell/utils/create-yaml';
|
||||
@@ -33,6 +36,7 @@ export default {
|
||||
|
||||
components: {
|
||||
Banner,
|
||||
Checkbox,
|
||||
Tab,
|
||||
UnitInput,
|
||||
CruResource,
|
||||
@@ -90,14 +94,34 @@ export default {
|
||||
source,
|
||||
storage,
|
||||
imageId,
|
||||
snapshots: [],
|
||||
images: [],
|
||||
showAdvanced: false,
|
||||
createWithDataVolume: false,
|
||||
snapshots: [],
|
||||
images: [],
|
||||
GIBIBYTE
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.registerBeforeHook(this.willSave, 'willSave');
|
||||
|
||||
if (this.mode === _CREATE) {
|
||||
const originalSaveYaml = this.value.saveYaml?.bind(this.value);
|
||||
|
||||
this.value.saveYaml = async(yaml) => {
|
||||
if (this.createWithDataVolume && this.isBlank) {
|
||||
const parsed = jsyaml.load(yaml);
|
||||
const dvObj = { ...parsed, type: 'cdi.kubevirt.io.datavolume' };
|
||||
const dataVolume = await this.$store.dispatch('harvester/create', dvObj);
|
||||
|
||||
await dataVolume.save();
|
||||
|
||||
return dataVolume;
|
||||
}
|
||||
|
||||
return originalSaveYaml(yaml);
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
@@ -135,6 +159,10 @@ export default {
|
||||
return Object.values(VOLUME_MODE);
|
||||
},
|
||||
|
||||
accessModeOptions() {
|
||||
return ['ReadWriteOnce', 'ReadWriteMany', 'ReadOnlyMany'];
|
||||
},
|
||||
|
||||
imageOption() {
|
||||
return sortBy(
|
||||
this.images
|
||||
@@ -275,6 +303,10 @@ export default {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('lhV2VolExpansion');
|
||||
},
|
||||
|
||||
isCreatePVCWithDataVolumeFeatureEnabled() {
|
||||
return this.$store.getters['harvester-common/getFeatureEnabled']('createPVCWithDataVolume');
|
||||
},
|
||||
|
||||
isResizeDisabled() {
|
||||
return (
|
||||
!this.isLHV2VolExpansionFeatureEnabled &&
|
||||
@@ -341,6 +373,58 @@ export default {
|
||||
|
||||
return readWriteOnce ? ['ReadWriteOnce'] : ['ReadWriteMany'];
|
||||
},
|
||||
buildDataVolumeObj() {
|
||||
const storage = {
|
||||
storageClassName: this.value.spec.storageClassName,
|
||||
resources: { requests: { storage: this.storage } },
|
||||
};
|
||||
|
||||
if (this.showAdvanced && this.value.spec.accessModes?.length > 0) {
|
||||
storage.accessModes = this.value.spec.accessModes;
|
||||
}
|
||||
|
||||
if (this.showAdvanced && this.value.spec.volumeMode) {
|
||||
storage.volumeMode = this.value.spec.volumeMode;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'cdi.kubevirt.io.datavolume',
|
||||
apiVersion: 'cdi.kubevirt.io/v1beta1',
|
||||
kind: 'DataVolume',
|
||||
metadata: {
|
||||
name: this.value.metadata.name,
|
||||
namespace: this.value.metadata.namespace,
|
||||
annotations: this.value.metadata.annotations || {},
|
||||
labels: this.value.metadata.labels || {},
|
||||
},
|
||||
spec: {
|
||||
source: { blank: {} },
|
||||
storage,
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
async save(buttonDone) {
|
||||
if (this.isCreate && this.isBlank && this.createWithDataVolume) {
|
||||
try {
|
||||
this.update();
|
||||
const dvObj = this.buildDataVolumeObj();
|
||||
const dataVolume = await this.$store.dispatch('harvester/create', dvObj);
|
||||
|
||||
await dataVolume.save();
|
||||
buttonDone(true);
|
||||
this.done();
|
||||
} catch (err) {
|
||||
const error = err?.data || err;
|
||||
|
||||
this['errors'] = exceptionToErrorsArray(error);
|
||||
buttonDone(false);
|
||||
}
|
||||
} else {
|
||||
await CreateEditView.methods.save.call(this, buttonDone);
|
||||
}
|
||||
},
|
||||
|
||||
willSave() {
|
||||
this.update();
|
||||
},
|
||||
@@ -383,9 +467,17 @@ export default {
|
||||
this.update();
|
||||
},
|
||||
generateYaml() {
|
||||
const out = saferDump(this.value);
|
||||
this.update();
|
||||
|
||||
return out;
|
||||
if (this.isCreate && this.isBlank && this.createWithDataVolume) {
|
||||
return saferDump(this.buildDataVolumeObj());
|
||||
}
|
||||
|
||||
const plain = clone(this.value);
|
||||
|
||||
delete plain.saveYaml;
|
||||
|
||||
return saferDump(plain);
|
||||
},
|
||||
}
|
||||
};
|
||||
@@ -458,18 +550,6 @@ export default {
|
||||
@update:value="update"
|
||||
/>
|
||||
|
||||
<LabeledSelect
|
||||
v-if="showVolumeMode"
|
||||
v-model:value="value.spec.volumeMode"
|
||||
:label="t('harvester.volume.volumeMode')"
|
||||
:options="volumeModeOptions"
|
||||
required
|
||||
:disabled="!isCreate"
|
||||
:mode="mode"
|
||||
class="mb-20"
|
||||
@update:value="update"
|
||||
/>
|
||||
|
||||
<UnitInput
|
||||
v-model:value="storage"
|
||||
:label="t('harvester.volume.size')"
|
||||
@@ -490,6 +570,44 @@ export default {
|
||||
>
|
||||
<span>{{ t('harvester.volume.longhorn.disableResize') }}</span>
|
||||
</Banner>
|
||||
|
||||
<div class="row mb-20">
|
||||
<Checkbox
|
||||
v-if="isCreate && isBlank && isCreatePVCWithDataVolumeFeatureEnabled"
|
||||
v-model:value="createWithDataVolume"
|
||||
:label="t('harvester.volume.createWithDataVolume')"
|
||||
tooltip-key="harvester.volume.createWithDataVolumeTooltip"
|
||||
/>
|
||||
</div>
|
||||
<a
|
||||
v-if="isCreate && isCreatePVCWithDataVolumeFeatureEnabled"
|
||||
role="button"
|
||||
class="hand"
|
||||
@click="showAdvanced = !showAdvanced"
|
||||
>
|
||||
{{ showAdvanced ? t('harvester.volume.hideAdvanced') : t('harvester.volume.showAdvanced') }}
|
||||
</a>
|
||||
|
||||
<LabeledSelect
|
||||
v-if="showAdvanced"
|
||||
v-model:value="value.spec.accessModes"
|
||||
:label="t('harvester.volume.accessModes')"
|
||||
:options="accessModeOptions"
|
||||
:multiple="true"
|
||||
:mode="mode"
|
||||
class="mb-20 mt-20"
|
||||
@update:value="update"
|
||||
/>
|
||||
|
||||
<LabeledSelect
|
||||
v-if="showAdvanced"
|
||||
v-model:value="value.spec.volumeMode"
|
||||
:label="t('harvester.volume.volumeMode')"
|
||||
:options="volumeModeOptions"
|
||||
:mode="mode"
|
||||
class="mb-20"
|
||||
@update:value="update"
|
||||
/>
|
||||
</Tab>
|
||||
<Tab
|
||||
v-if="!isCreate"
|
||||
|
||||
@@ -132,6 +132,15 @@ harvester:
|
||||
targetVolume:
|
||||
label: Target Volume
|
||||
placeholder: Select a target volume
|
||||
dataMigration:
|
||||
title: Data Migration
|
||||
fields:
|
||||
targetVolumeName:
|
||||
label: Target Volume Name
|
||||
placeholder: Enter a target volume name
|
||||
targetStorageClassName:
|
||||
label: Target Storage Class
|
||||
placeholder: Select a storage class
|
||||
migration:
|
||||
failedMessage: Latest migration failed!
|
||||
title: Migration
|
||||
@@ -243,6 +252,7 @@ harvester:
|
||||
abortMigration: Abort Migration
|
||||
storageMigration: Storage Migration
|
||||
cancelStorageMigration: Cancel Storage Migration
|
||||
dataMigration: Data Migration
|
||||
createTemplate: Generate Template
|
||||
enableMaintenance: Enable Maintenance Mode
|
||||
disableMaintenance: Disable Maintenance Mode
|
||||
@@ -912,6 +922,11 @@ harvester:
|
||||
conditions: Conditions
|
||||
size: Size
|
||||
volumeMode: Volume Mode
|
||||
accessModes: Access Modes
|
||||
createWithDataVolume: Create with DataVolume
|
||||
createWithDataVolumeTooltip: Create Volume with Kubevirt/Containerized Data Importer way. It can fill accessMode/volumeMode automatically.
|
||||
showAdvanced: Show Advanced Options
|
||||
hideAdvanced: Hide Advanced Options
|
||||
source: Source
|
||||
kind: Kind
|
||||
sourceOptions:
|
||||
|
||||
@@ -70,8 +70,13 @@ export default {
|
||||
return schema;
|
||||
},
|
||||
filterRows() {
|
||||
// we only show the non golden image PVCs in the list
|
||||
return this.rows.filter((pvc) => !pvc?.isGoldenImageVolume);
|
||||
return this.rows.filter((pvc) => {
|
||||
if (pvc?.isGoldenImageVolume || pvc?.isCDIPopulatorVolume) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
},
|
||||
headers() {
|
||||
return [
|
||||
|
||||
@@ -9,6 +9,7 @@ import { colorForState } from '@shell/plugins/dashboard-store/resource-class';
|
||||
import { HCI, VOLUME_SNAPSHOT } from '../../types';
|
||||
import HarvesterResource from '../harvester';
|
||||
import { PRODUCT_NAME as HARVESTER_PRODUCT } from '../../config/harvester';
|
||||
import { CDI_POPULATOR_KIND } from '../../config/types';
|
||||
import { LVM_DRIVER } from './storage.k8s.io.storageclass';
|
||||
|
||||
const DEGRADED_ERRORS = ['replica scheduling failed', 'precheck new replica failed'];
|
||||
@@ -44,7 +45,7 @@ export default class HciPv extends HarvesterResource {
|
||||
const exportImageAction = {
|
||||
action: 'exportImage',
|
||||
enabled: this.hasAction('export') && !this.isEncrypted,
|
||||
icon: 'icon icon-copy',
|
||||
icon: 'icon icon-external-link',
|
||||
label: this.t('harvester.action.exportImage')
|
||||
};
|
||||
const takeSnapshotAction = {
|
||||
@@ -77,10 +78,23 @@ export default class HciPv extends HarvesterResource {
|
||||
icon: 'icon icon-backup',
|
||||
label: this.t('harvester.action.cancelExpand')
|
||||
},
|
||||
{
|
||||
action: 'dataMigration',
|
||||
enabled: this.hasAction('dataMigration') && this.createPVCWithDataVolumeFeatureEnabled,
|
||||
icon: 'icon icon-copy',
|
||||
label: this.t('harvester.action.dataMigration')
|
||||
},
|
||||
...out
|
||||
];
|
||||
}
|
||||
|
||||
dataMigration(resources = this) {
|
||||
this.$dispatch('promptModal', {
|
||||
resources,
|
||||
component: 'HarvesterDataMigrationDialog'
|
||||
});
|
||||
}
|
||||
|
||||
exportImage(resources = this) {
|
||||
this.$dispatch('promptModal', {
|
||||
resources,
|
||||
@@ -339,10 +353,20 @@ export default class HciPv extends HarvesterResource {
|
||||
return this?.metadata?.annotations?.[HCI_ANNOTATIONS.GOLDEN_IMAGE] === 'true';
|
||||
}
|
||||
|
||||
get isCDIPopulatorVolume() {
|
||||
const kind = this?.metadata?.annotations?.[HCI_ANNOTATIONS.CDI_POPULATOR_KIND];
|
||||
|
||||
return kind === CDI_POPULATOR_KIND.VOLUME_IMPORT_SOURCE || kind === CDI_POPULATOR_KIND.VOLUME_CLONE_SOURCE;
|
||||
}
|
||||
|
||||
get thirdPartyStorageFeatureEnabled() {
|
||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('thirdPartyStorage');
|
||||
}
|
||||
|
||||
get createPVCWithDataVolumeFeatureEnabled() {
|
||||
return this.$rootGetters['harvester-common/getFeatureEnabled']('createPVCWithDataVolume');
|
||||
}
|
||||
|
||||
get resourceExternalLink() {
|
||||
const host = window.location.host;
|
||||
const { params } = this.currentRoute();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "harvester",
|
||||
"description": "Rancher UI Extension for Harvester",
|
||||
"version": "1.8.0-rc2",
|
||||
"version": "1.8.0-rc4",
|
||||
"private": false,
|
||||
"rancher": {
|
||||
"annotations": {
|
||||
@@ -29,4 +29,4 @@
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
# Usage: update package.json and pkg/harvester/package.json to desired version
|
||||
# Example: ./scripts/bump_version.sh v1.8.0-rc3
|
||||
|
||||
VERSION="$1"
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "Usage: $0 <version>"
|
||||
echo "Example: $0 v1.8.0-rc3"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Strip leading 'v' if present
|
||||
VERSION="${VERSION#v}"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
FILES=(
|
||||
"$ROOT_DIR/package.json"
|
||||
"$ROOT_DIR/pkg/harvester/package.json"
|
||||
)
|
||||
|
||||
for FILE in "${FILES[@]}"; do
|
||||
if [ ! -f "$FILE" ]; then
|
||||
echo "File not found: $FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use node to update version in-place while preserving formatting
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const raw = fs.readFileSync('$FILE', 'utf8');
|
||||
const updated = raw.replace(/\"version\":\s*\"[^\"]*\"/, '\"version\": \"$VERSION\"');
|
||||
fs.writeFileSync('$FILE', updated);
|
||||
"
|
||||
|
||||
echo "Updated $FILE -> $VERSION"
|
||||
done
|
||||
@@ -13844,9 +13844,9 @@ yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2:
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yaml@^2.5.1:
|
||||
version "2.8.2"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5"
|
||||
integrity sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==
|
||||
version "2.8.3"
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d"
|
||||
integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==
|
||||
|
||||
yargs-parser@^18.1.2:
|
||||
version "18.1.3"
|
||||
|
||||
Reference in New Issue
Block a user