mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2026-10-01 03:36:04 +08:00
Compare commits
47
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f03fc0805 | ||
|
|
6d4a7cd1b1 | ||
|
|
f69d445ef7 | ||
|
|
d358a0a618 | ||
|
|
47841e49b1 | ||
|
|
57c7f0f856 | ||
|
|
161adde1da | ||
|
|
28ece801d5 | ||
|
|
a8cf6f5947 | ||
|
|
28e4991bd0 | ||
|
|
0d94e66b8f | ||
|
|
871ad2e88d | ||
|
|
03d1174d8e | ||
|
|
e236ab2d5f | ||
|
|
0b6569ff64 | ||
|
|
259988f230 | ||
|
|
bbbdd1a881 | ||
|
|
83edd23ada | ||
|
|
a6f2301498 | ||
|
|
0171f70b36 | ||
|
|
578fb72cbb | ||
|
|
45773987f3 | ||
|
|
f2b98b851d | ||
|
|
42d793b50e | ||
|
|
e2474ebf8d | ||
|
|
ad37d502f7 | ||
|
|
666fe7b3b8 | ||
|
|
81f1a56434 | ||
|
|
d6bf6f1b55 | ||
|
|
2774b1c30c | ||
|
|
8b35ea1eaa | ||
|
|
e86df77224 | ||
|
|
6d0ed11057 | ||
|
|
7d17baf774 | ||
|
|
69e257d41c | ||
|
|
e5d4d0556d | ||
|
|
e2773f8c69 | ||
|
|
b7b259c699 | ||
|
|
687943ae92 | ||
|
|
4d48e75447 | ||
|
|
94429d6b64 | ||
|
|
990c06ba98 | ||
|
|
a207ec078b | ||
|
|
8d4524c4c6 | ||
|
|
f04c0c6c9b | ||
|
|
ab77b21aa8 | ||
|
|
c86b4b0466 |
@@ -13,3 +13,4 @@ dist-pkg
|
||||
.DS_Store
|
||||
pkg/harvester/index.ts
|
||||
pkg/harvester/store/*
|
||||
scripts/pkgfile.js
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
pull_request_rules:
|
||||
- name: Automatically open backport PR to release-harvester-v1.5
|
||||
conditions:
|
||||
- base=main
|
||||
actions:
|
||||
backport:
|
||||
branches:
|
||||
- "release-harvester-v1.5"
|
||||
assignees:
|
||||
- "{{ author }}"
|
||||
labels:
|
||||
- "v1.5 backport PR"
|
||||
|
||||
- name: Ask to resolve conflict
|
||||
conditions:
|
||||
- conflict
|
||||
actions:
|
||||
comment:
|
||||
message: This pull request is now in conflict. Could you fix it @{{author}}? 🙏
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Build Dashboard (Branch)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'release-harvester-v*'
|
||||
- '*-dev'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'release-harvester-v*'
|
||||
- '*-dev'
|
||||
types:
|
||||
- merged
|
||||
|
||||
jobs:
|
||||
build-validation:
|
||||
name: Build Test
|
||||
uses: ./.github/workflows/test.yaml
|
||||
build:
|
||||
name: Build and Upload Package
|
||||
uses: ./.github/workflows/build-and-upload.yaml
|
||||
needs:
|
||||
- build-validation
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
with:
|
||||
CI_BRANCH: ${{github.ref_name}}
|
||||
@@ -0,0 +1,21 @@
|
||||
name: Build Dashboard (Release)
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v[1-9].*
|
||||
|
||||
jobs:
|
||||
build-validation:
|
||||
name: Build Test
|
||||
uses: ./.github/workflows/test.yaml
|
||||
build:
|
||||
name: Build and Upload Package
|
||||
uses: ./.github/workflows/build-and-upload.yaml
|
||||
needs:
|
||||
- build-validation
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
id-token: write
|
||||
with:
|
||||
CI_BUILD_TAG: ${{github.ref_name}}
|
||||
@@ -0,0 +1,165 @@
|
||||
name: Build Harvester Dashboard and Upload
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
CI_BRANCH:
|
||||
required: false
|
||||
type: string
|
||||
CI_BUILD_TAG:
|
||||
required: false
|
||||
type: string
|
||||
|
||||
env:
|
||||
GOOGLE_AUTH: ''
|
||||
DOCKER_USERNAME: ''
|
||||
DOCKER_PASSWORD: ''
|
||||
CI_BUILD_TAG: ${{inputs.CI_BUILD_TAG}}
|
||||
CI_BRANCH: ${{inputs.CI_BRANCH}}
|
||||
GIT_REPO: ${{github.repository}}
|
||||
GIT_COMMIT: ${{github.sha}}
|
||||
REPO: ${{github.event.repository.name || ''}}
|
||||
|
||||
jobs:
|
||||
build-and-upload-hosted:
|
||||
name: Build & Upload Hosted
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@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
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
# Build a directory containing the dashboard that can be used with ui-dashboard-index
|
||||
- id: build-hosted
|
||||
name: Build Hosted
|
||||
run: ./scripts/build-hosted
|
||||
|
||||
- id: upload-gate
|
||||
name: Upload Gate (superseded by a newer build?)
|
||||
run: ./scripts/build-upload-gate
|
||||
|
||||
- name: Get gcs auth
|
||||
uses: rancher-eio/read-vault-secrets@main
|
||||
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'
|
||||
with:
|
||||
credentials_json: "${{ env.GOOGLE_AUTH }}"
|
||||
|
||||
- name: Upload build
|
||||
uses: 'google-github-actions/upload-cloud-storage@v2'
|
||||
# https://github.com/google-github-actions/upload-cloud-storage
|
||||
with:
|
||||
path: ${{steps.build-hosted.outputs.BUILD_HOSTED_DIR}}
|
||||
destination: releases.rancher.com/harvester-ui/dashboard/${{ steps.build-hosted.outputs.BUILD_HOSTED_LOCATION }}
|
||||
parent: false
|
||||
headers: |-
|
||||
cache-control: no-cache,must-revalidate
|
||||
process_gcloudignore: false
|
||||
|
||||
build-and-upload-embedded:
|
||||
name: Build & Upload Embedded
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@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
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
# Build a tar that will be picked up by rancher builds and embedded into it
|
||||
- id: build-embedded
|
||||
name: Build Embedded
|
||||
run: ./scripts/build-embedded
|
||||
env:
|
||||
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
|
||||
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'
|
||||
with:
|
||||
credentials_json: "${{ env.GOOGLE_AUTH }}"
|
||||
|
||||
- name: Upload tar
|
||||
uses: 'google-github-actions/upload-cloud-storage@v2'
|
||||
with:
|
||||
path: ${{steps.build-embedded.outputs.BUILD_EMBEDED_TGZ}}
|
||||
destination: releases.rancher.com/harvester-ui/dashboard
|
||||
parent: false
|
||||
headers: |-
|
||||
cache-control: no-cache,must-revalidate
|
||||
process_gcloudignore: false
|
||||
|
||||
build-and-upload-harvester-plugin:
|
||||
name: Build & Upload Plugin
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@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
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- id: ci-build-pkg
|
||||
name: Build pkg
|
||||
run: ./scripts/ci-build-pkg.sh harvester
|
||||
|
||||
- id: upload-gate
|
||||
name: Upload Gate
|
||||
run: ./scripts/build-upload-gate
|
||||
|
||||
- name: Get gcs auth
|
||||
uses: rancher-eio/read-vault-secrets@main
|
||||
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'
|
||||
with:
|
||||
credentials_json: "${{ env.GOOGLE_AUTH }}"
|
||||
|
||||
- name: Upload plugin tar
|
||||
uses: 'google-github-actions/upload-cloud-storage@v2'
|
||||
with:
|
||||
path: dist-pkg/${{steps.ci-build-pkg.outputs.PKG_TARBALL}}
|
||||
destination: releases.rancher.com/harvester-ui/plugin
|
||||
parent: false
|
||||
headers: |-
|
||||
cache-control: no-cache,must-revalidate
|
||||
process_gcloudignore: false
|
||||
|
||||
- name: Upload plugin directory
|
||||
uses: 'google-github-actions/upload-cloud-storage@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}}
|
||||
parent: false
|
||||
headers: |-
|
||||
cache-control: no-cache,must-revalidate
|
||||
process_gcloudignore: false
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
workflow_call: # This tells GH that the workflow is reusable
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
@@ -192,7 +192,8 @@ export default {
|
||||
|
||||
<div v-if="openVlan">
|
||||
<LabeledInput
|
||||
v-model.number="parsedDefaultValue.vlan"
|
||||
v-model:value.number="parsedDefaultValue.vlan"
|
||||
type="number"
|
||||
class="mb-20"
|
||||
:mode="mode"
|
||||
required
|
||||
|
||||
@@ -17,12 +17,7 @@ const featuresV132 = [
|
||||
'improveMaintenanceMode',
|
||||
];
|
||||
|
||||
// TODO: change to https://github.com/harvester/dashboard/releases/tag/v1.4.0 after v1.4.0 release
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc5
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc4
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc3
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc2
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.0-rc1
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.0
|
||||
const featuresV140 = [
|
||||
...featuresV132,
|
||||
'cpuPinning',
|
||||
@@ -34,9 +29,16 @@ const featuresV140 = [
|
||||
'improveMaintenanceMode',
|
||||
];
|
||||
|
||||
// TODO: add v1.4.1 official release note
|
||||
// https://github.com/harvester/dashboard/releases/tag/v1.4.1-rc1
|
||||
const featuresV141 = [
|
||||
...featuresV140
|
||||
];
|
||||
|
||||
export const RELEASE_FEATURES = {
|
||||
'v1.3.0': featuresV130,
|
||||
'v1.3.1': featuresV131,
|
||||
'v1.3.2': featuresV132,
|
||||
'v1.4.0': featuresV140,
|
||||
'v1.4.1': featuresV141,
|
||||
};
|
||||
|
||||
@@ -74,7 +74,6 @@ export default {
|
||||
if (this.isCreate) {
|
||||
this.value.metadata.namespace = 'default';
|
||||
}
|
||||
|
||||
set(this.value, 'spec', this.value.spec || {});
|
||||
|
||||
const providers = PROVIDERS.map((provider) => ({
|
||||
@@ -83,14 +82,6 @@ export default {
|
||||
label: this.t(provider.labelKey)
|
||||
}));
|
||||
|
||||
if (this.mode !== _VIEW) {
|
||||
this.value['spec'] = this.value.spec || {};
|
||||
|
||||
providers.forEach((provider) => {
|
||||
this.value.spec[provider.name] = this.value.spec[provider.name] || clone(provider.default);
|
||||
});
|
||||
}
|
||||
|
||||
const selectedProviders = providers.filter((provider) => {
|
||||
const specProvider = this.value.spec[provider.name];
|
||||
const correctedSpecProvider = provider.name === 'forward' ? specProvider?.servers?.[0] || {} : specProvider;
|
||||
@@ -98,7 +89,12 @@ export default {
|
||||
return !isEmpty(correctedSpecProvider) && !isEqual(correctedSpecProvider, provider.default);
|
||||
});
|
||||
|
||||
const selectedProvider = selectedProviders?.[0]?.value || providers[0].value;
|
||||
const selectedProvider = selectedProviders?.[0]?.value || providers[0].value; // selected provider name
|
||||
const selectedProviderDefault = providers.find((p) => p.name === selectedProvider)?.default || providers[0].default;
|
||||
|
||||
if (this.mode !== _VIEW) {
|
||||
this.$set(this.value.spec, selectedProvider, this.value.spec[selectedProvider] || clone(selectedProviderDefault));
|
||||
}
|
||||
|
||||
return {
|
||||
bufferYaml: '',
|
||||
@@ -129,6 +125,18 @@ export default {
|
||||
outputTypeOptions() {
|
||||
return OUTPUT_TYPE;
|
||||
},
|
||||
outputProvider: {
|
||||
get() {
|
||||
return this.selectedProvider;
|
||||
},
|
||||
|
||||
set(newProvider) {
|
||||
this.selectedProvider = newProvider;
|
||||
const providerDefaultSpec = this.providers.find((p) => p.name === newProvider)?.default || {};
|
||||
|
||||
this.value.spec = { [newProvider]: this.value.spec[newProvider] || clone(providerDefaultSpec) };
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
@@ -142,8 +150,6 @@ export default {
|
||||
this.$refs.tabbed.select(provider.name);
|
||||
},
|
||||
willSave() {
|
||||
this.value.spec = { [this.selectedProvider]: this.value.spec[this.selectedProvider] };
|
||||
|
||||
const bufferJson = jsyaml.load(this.bufferYaml);
|
||||
|
||||
if (!isEmpty(bufferJson)) {
|
||||
@@ -236,7 +242,7 @@ export default {
|
||||
<div class="row">
|
||||
<div class="col span-6">
|
||||
<LabeledSelect
|
||||
v-model:value="selectedProvider"
|
||||
v-model:value="outputProvider"
|
||||
label="Output"
|
||||
:options="providers"
|
||||
:mode="mode"
|
||||
|
||||
@@ -10,17 +10,6 @@ import { clone } from '@shell/utils/object';
|
||||
import { uniq } from '@shell/utils/array';
|
||||
import { DATA_ENGINE_V1 } from '../../../models/harvester/persistentvolumeclaim';
|
||||
|
||||
// UI components for Longhorn storage class parameters
|
||||
const DEFAULT_PARAMETERS = [
|
||||
'numberOfReplicas',
|
||||
'staleReplicaTimeout',
|
||||
'diskSelector',
|
||||
'nodeSelector',
|
||||
'migratable',
|
||||
'encrypted',
|
||||
'dataEngine',
|
||||
];
|
||||
|
||||
const {
|
||||
CSI_PROVISIONER_SECRET_NAME,
|
||||
CSI_PROVISIONER_SECRET_NAMESPACE,
|
||||
@@ -141,11 +130,17 @@ export default {
|
||||
get() {
|
||||
const parameters = clone(this.value?.parameters) || {};
|
||||
|
||||
DEFAULT_PARAMETERS.forEach((key) => {
|
||||
delete parameters[key];
|
||||
});
|
||||
// UI components for Longhorn storage class parameters
|
||||
const defaultParameters = [
|
||||
'numberOfReplicas',
|
||||
'staleReplicaTimeout',
|
||||
'diskSelector',
|
||||
'nodeSelector',
|
||||
'migratable',
|
||||
...(this.value.volumeEncryptionFeatureEnabled ? ['encrypted', 'dataEngine'] : []),
|
||||
];
|
||||
|
||||
Object.values(CSI_SECRETS).forEach((key) => {
|
||||
[...defaultParameters, ...Object.values(CSI_SECRETS)].forEach((key) => {
|
||||
delete parameters[key];
|
||||
});
|
||||
|
||||
@@ -311,16 +306,16 @@ export default {
|
||||
</LabeledSelect>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-20">
|
||||
<RadioGroup
|
||||
v-model:value="value.parameters.migratable"
|
||||
name="layer3NetworkMode"
|
||||
:label="t('harvester.storage.parameters.migratable.label')"
|
||||
:mode="mode"
|
||||
:options="migratableOptions"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="value.volumeEncryptionFeatureEnabled">
|
||||
<div class="row mt-20">
|
||||
<RadioGroup
|
||||
v-model:value="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:value="volumeEncryption"
|
||||
|
||||
@@ -245,6 +245,8 @@ export default {
|
||||
async saveImage(buttonCb) {
|
||||
this.value.spec.displayName = (this.value.spec.displayName || '').trim();
|
||||
|
||||
if (this.isEdit) return await this.handleEditImage(buttonCb);
|
||||
|
||||
if (this.value.spec.sourceType === UPLOAD && this.isCreate) {
|
||||
try {
|
||||
this.value.spec.url = '';
|
||||
@@ -269,6 +271,23 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
async handleEditImage(buttonCb) {
|
||||
try {
|
||||
const data = [{
|
||||
op: 'replace', path: '/metadata/labels', value: this.value.metadata.labels
|
||||
}, {
|
||||
op: 'replace', path: '/metadata/annotations', value: this.value.metadata.annotations
|
||||
}];
|
||||
|
||||
await this.value.patch(data);
|
||||
buttonCb(true);
|
||||
this.done();
|
||||
} catch (e) {
|
||||
this.errors = exceptionToErrorsArray(e);
|
||||
buttonCb(false);
|
||||
}
|
||||
},
|
||||
|
||||
setImageLabels(str) {
|
||||
const suffixName = str?.split('/')?.pop() || str;
|
||||
|
||||
@@ -530,6 +549,7 @@ export default {
|
||||
:mode="mode"
|
||||
:pad-left="false"
|
||||
:read-allowed="false"
|
||||
:value-can-be-empty="true"
|
||||
@focusKey="focusKey"
|
||||
@update:value="value.setLabels($event)"
|
||||
>
|
||||
@@ -552,7 +572,7 @@ export default {
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
spellcheck="false"
|
||||
@update:value="queueUpdate"
|
||||
@input="queueUpdate"
|
||||
/>
|
||||
</template>
|
||||
</KeyValue>
|
||||
|
||||
@@ -29,7 +29,7 @@ import SSHKey from './kubevirt.io.virtualmachine/VirtualMachineSSHKey';
|
||||
export default {
|
||||
name: 'HarvesterEditVMTemplate',
|
||||
|
||||
emits: ['update:value'],
|
||||
emits: ['update:templateValue'],
|
||||
|
||||
components: {
|
||||
Tab,
|
||||
@@ -205,7 +205,7 @@ export default {
|
||||
if (tab.name === 'advanced') {
|
||||
this.$refs.yamlEditor?.refresh();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -222,11 +222,11 @@ export default {
|
||||
@finish="saveVMT"
|
||||
>
|
||||
<NameNsDescription
|
||||
:value="value"
|
||||
:value="templateValue"
|
||||
:mode="realTemplateMode"
|
||||
name-label="harvester.vmTemplate.nameNsDescription.name"
|
||||
:namespaced="true"
|
||||
@update:value="$emit('update:value', $event)"
|
||||
@update:value="$emit('update:templateValue', $event)"
|
||||
/>
|
||||
|
||||
<Checkbox
|
||||
|
||||
+5
-1
@@ -95,7 +95,11 @@ export default {
|
||||
devices: {
|
||||
handler(v) {
|
||||
this.rows = v;
|
||||
this.filterRows = this.rows;
|
||||
if (this.parentSriov) {
|
||||
this.filterRows = this.rows.filter((row) => row.labels[this.parentSriovLabel] === this.parentSriov);
|
||||
} else {
|
||||
this.filterRows = this.rows;
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
|
||||
@@ -72,17 +72,23 @@ export default {
|
||||
return stats;
|
||||
},
|
||||
|
||||
units() {
|
||||
allocatedUnits() {
|
||||
const exponent = exponentNeeded(this.storageStats.total, 1024);
|
||||
|
||||
return `${ UNITS[exponent] }iB`;
|
||||
},
|
||||
|
||||
usedUnits() {
|
||||
const exponent = exponentNeeded(this.storageStats.maximum, 1024);
|
||||
|
||||
return `${ UNITS[exponent] }iB`;
|
||||
},
|
||||
|
||||
used() {
|
||||
formatUsed() {
|
||||
let out = this.formatter(this.storageStats.used);
|
||||
|
||||
if (!Number.parseFloat(out) > 0) {
|
||||
out = this.formatter(this.storageStats.used, { canRoundToZero: false });
|
||||
out = this.formatter(this.storageStats.used, { canRoundToZero: true });
|
||||
}
|
||||
|
||||
return out;
|
||||
@@ -92,7 +98,7 @@ export default {
|
||||
let out = this.formatter(this.storageStats.scheduled);
|
||||
|
||||
if (!Number.parseFloat(out) > 0) {
|
||||
out = this.formatter(this.storageStats.scheduled, { canRoundToZero: false });
|
||||
out = this.formatter(this.storageStats.scheduled, { canRoundToZero: true });
|
||||
}
|
||||
|
||||
return out;
|
||||
@@ -100,9 +106,9 @@ export default {
|
||||
|
||||
usedAmountTemplateValues() {
|
||||
return {
|
||||
used: this.used,
|
||||
used: this.formatUsed,
|
||||
total: this.formatter(this.storageStats.maximum),
|
||||
unit: this.units,
|
||||
unit: this.usedUnits,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -110,7 +116,7 @@ export default {
|
||||
return {
|
||||
used: this.formatAllocated,
|
||||
total: this.formatter(this.storageStats.total),
|
||||
unit: this.units,
|
||||
unit: this.allocatedUnits,
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -141,7 +147,7 @@ export default {
|
||||
<ConsumptionGauge
|
||||
:capacity="storageStats.total"
|
||||
:used="storageStats.scheduled"
|
||||
:units="units"
|
||||
:units="allocatedUnits"
|
||||
:number-formatter="formatter"
|
||||
:resource-name="resourceName"
|
||||
>
|
||||
@@ -161,7 +167,7 @@ export default {
|
||||
<ConsumptionGauge
|
||||
:capacity="storageStats.maximum"
|
||||
:used="storageStats.used"
|
||||
:units="units"
|
||||
:units="usedUnits"
|
||||
:number-formatter="formatter"
|
||||
:resource-name="showAllocated ? '' : resourceName"
|
||||
:class="{
|
||||
|
||||
@@ -14,16 +14,35 @@ export function getVersion(v) {
|
||||
}
|
||||
|
||||
try {
|
||||
// v1.4.1-rc.1 => v1.4.1, v1.4.1-dev-20241222 => v1.4.1
|
||||
return `v${ semver.major(v) }.${ semver.minor(v) }.${ semver.patch(v) }`;
|
||||
} catch (error) {
|
||||
// fallback to the latest version
|
||||
return Object.keys(RELEASE_FEATURES).sort((a, b) => semver.compare(a, b)).pop();
|
||||
return latestVersion(Object.keys(RELEASE_FEATURES));
|
||||
}
|
||||
}
|
||||
|
||||
function latestVersion(versions) {
|
||||
return versions.sort((a, b) => semver.compare(a, b)).pop();
|
||||
}
|
||||
|
||||
// v1.3.3 => latest v1.3.x, v1.4.2 => latest v1.4.x
|
||||
function latestMinorVersion(v) {
|
||||
const minor = `v${ semver.major(v) }.${ semver.minor(v) }`;
|
||||
const minorVersions = Object.keys(RELEASE_FEATURES).filter((version) => version.startsWith(minor));
|
||||
|
||||
return latestVersion(minorVersions);
|
||||
}
|
||||
|
||||
export const featureEnabled = (featureKey, serverVersion) => {
|
||||
const version = getVersion(serverVersion);
|
||||
const releasedFeatures = RELEASE_FEATURES[version] || [];
|
||||
let releasedFeatures = RELEASE_FEATURES[version];
|
||||
|
||||
if (!releasedFeatures) {
|
||||
const fallback = latestMinorVersion(version);
|
||||
|
||||
releasedFeatures = RELEASE_FEATURES[fallback];
|
||||
}
|
||||
|
||||
return releasedFeatures.includes(featureKey);
|
||||
};
|
||||
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -x
|
||||
|
||||
BUILD_DEBUG="${BUILD_DEBUG:-}"
|
||||
if [[ -n "${BUILD_DEBUG}" ]]; then
|
||||
set -x
|
||||
env
|
||||
fi
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
echo "Bootstrapping..."
|
||||
yarn --pure-lockfile install
|
||||
|
||||
source scripts/version
|
||||
echo "BRANCH: ${COMMIT_BRANCH:-<none>}"
|
||||
echo "TAG: ${GIT_TAG:-<none>}"
|
||||
DIR=${GIT_TAG:-$COMMIT_BRANCH}
|
||||
|
||||
if [[ "${DIR}" == "main" ]]; then
|
||||
DIR="latest"
|
||||
fi
|
||||
|
||||
OUTPUT_DIR=dist/${DIR}-embedded
|
||||
|
||||
echo "Building..."
|
||||
COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=$OUTPUT_DIR ROUTER_BASE='/dashboard/' RESOURCE_BASE='/dashboard/' RANCHER_ENV=harvester yarn run build
|
||||
|
||||
if [ -v EMBED_PKG ]; then
|
||||
echo "Build and embed plugin from: $EMBED_PKG"
|
||||
PKG_FILE_NAME=${EMBED_PKG##*/}
|
||||
echo PKG_FILE_NAME: $PKG_FILE_NAME
|
||||
|
||||
PKG_NAME="${PKG_FILE_NAME/.tar.gz/""}"
|
||||
echo "Plugin name: '$PKG_NAME'"
|
||||
|
||||
# Fetch file, unpack and move to dist
|
||||
curl $EMBED_PKG --output $PKG_FILE_NAME
|
||||
OUTPUT_DIR_PKG=$OUTPUT_DIR/$PKG_NAME
|
||||
mkdir -p $OUTPUT_DIR_PKG
|
||||
tar xvfz $PKG_FILE_NAME -C $OUTPUT_DIR/$PKG_NAME
|
||||
echo "Plugin contents that will be served from $PKG_NAME"
|
||||
ls -alR $OUTPUT_DIR/$PKG_NAME
|
||||
fi
|
||||
|
||||
echo "Destroying..."
|
||||
find $OUTPUT_DIR -name "index.html" -mindepth 2 -exec rm {} \;
|
||||
find $OUTPUT_DIR -type d -empty -depth -exec rmdir {} \;
|
||||
|
||||
TARBALL=${DIR}.tar.gz
|
||||
TARBALL_FILE_PATH=dist/${TARBALL}
|
||||
|
||||
ENV_OUTPUT="${GITHUB_OUTPUT:-"temp-env"}"
|
||||
echo "BUILD_EMBEDED_TGZ=${TARBALL_FILE_PATH}" >> "$ENV_OUTPUT"
|
||||
|
||||
echo "Compressing to ${TARBALL}..."
|
||||
tar -czf ${TARBALL_FILE_PATH} $OUTPUT_DIR/
|
||||
|
||||
echo "Cleaning up..."
|
||||
rm -r $OUTPUT_DIR
|
||||
|
||||
ls -alR dist/
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
BUILD_DEBUG="${BUILD_DEBUG:-}"
|
||||
if [[ -n "${BUILD_DEBUG}" ]]; then
|
||||
set -x
|
||||
env
|
||||
fi
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
echo "Bootstrapping..."
|
||||
yarn --pure-lockfile install
|
||||
|
||||
source scripts/version
|
||||
echo "BRANCH: ${COMMIT_BRANCH:-<none>}"
|
||||
echo "TAG: ${GIT_TAG:-<none>}"
|
||||
DIR=${GIT_TAG:-$COMMIT_BRANCH}
|
||||
|
||||
if [[ "${DIR}" == "main" ]]; then
|
||||
DIR="latest"
|
||||
fi
|
||||
|
||||
BASE=${BASE:-https://releases.rancher.com/harvester-ui/dashboard/${DIR}}
|
||||
|
||||
echo "Building for ${BASE}..."
|
||||
|
||||
OUTPUT_DIR=dist/${DIR}
|
||||
|
||||
ENV_OUTPUT="${GITHUB_OUTPUT:-"temp-env"}"
|
||||
echo "BUILD_HOSTED_DIR=${OUTPUT_DIR}" >> "$ENV_OUTPUT"
|
||||
echo "BUILD_HOSTED_LOCATION=${DIR}" >> "$ENV_OUTPUT"
|
||||
|
||||
COMMIT=${COMMIT} VERSION=${VERSION} OUTPUT_DIR=${OUTPUT_DIR} ROUTER_BASE="/dashboard/" RESOURCE_BASE="${BASE}" RANCHER_ENV=harvester yarn run build
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
BASE_DIR="$( cd $SCRIPT_DIR && cd ../.. & pwd)"
|
||||
SHELL_DIR=$BASE_DIR/shell/
|
||||
EXIT_CODE=0
|
||||
FORMATS="umd-min"
|
||||
|
||||
while getopts "df:" opt; do
|
||||
case $opt in
|
||||
d)
|
||||
FORMATS="umd"
|
||||
;;
|
||||
f)
|
||||
FORMATS=$OPTARG
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
# Use shell folder in node modules when we have @rancher/shell installed as a node module
|
||||
# rather than the use-case of the mono-repo with the shell folder at the top-level
|
||||
if [ ! -d ${SHELL_DIR} ]; then
|
||||
SHELL_DIR=$BASE_DIR/node_modules/@rancher/shell/
|
||||
SHELL_DIR=$(cd -P ${SHELL_DIR} && pwd)
|
||||
fi
|
||||
|
||||
CREATE_TARBALL=${2}
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
VERSION=$(cd pkg/$1; node -p -e "require('./package.json').version")
|
||||
fi
|
||||
|
||||
NAME=${1}-${VERSION}
|
||||
PKG_DIST=${BASE_DIR}/dist-pkg/${NAME}
|
||||
|
||||
if [ -d "${BASE_DIR}/pkg/${1}" ]; then
|
||||
echo "Building UI Package $1"
|
||||
echo " Package name: ${NAME}"
|
||||
echo " Package version: ${VERSION}"
|
||||
echo " Output formats: ${FORMATS}"
|
||||
echo " Output directory: ${PKG_DIST}"
|
||||
rm -rf ${PKG_DIST}
|
||||
mkdir -p ${PKG_DIST}
|
||||
|
||||
pushd pkg/${1}
|
||||
|
||||
# Check that the .shell link exists and points to the correct place
|
||||
if [ -e ".shell" ]; then
|
||||
LINK=$(readlink .shell)
|
||||
if [ "${LINK}" != "${SHELL_DIR}" ]; then
|
||||
echo ".shell symlink exists but does not point to expected location - please check and fix"
|
||||
popd
|
||||
exit -1
|
||||
fi
|
||||
else
|
||||
ln -s ${SHELL_DIR} .shell
|
||||
fi
|
||||
|
||||
FILE=index.js
|
||||
if [ -f ./index.ts ]; then
|
||||
FILE=index.ts
|
||||
fi
|
||||
|
||||
if [ -n "$COMMIT" ]; then
|
||||
echo ${COMMIT} > ${PKG_DIST}/version
|
||||
fi
|
||||
|
||||
${BASE_DIR}/node_modules/.bin/vue-cli-service build --name ${NAME} --target lib ${FILE} --dest ${PKG_DIST} --formats ${FORMATS} --filename ${NAME}
|
||||
EXIT_CODE=$?
|
||||
cp -f ./package.json ${PKG_DIST}/package.json
|
||||
node ${SCRIPT_DIR}/pkgfile.js ${PKG_DIST}/package.json
|
||||
rm -rf ${PKG_DIST}/*.bak
|
||||
rm .shell
|
||||
|
||||
popd
|
||||
fi
|
||||
|
||||
if [ $EXIT_CODE -ne 0 ]; then
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
|
||||
|
||||
if [ -n "${CREATE_TARBALL}" ]; then
|
||||
echo $COMMIT $COMMIT_BRANCH > ${PKG_DIST}/version-commit.txt
|
||||
|
||||
TARBALL=${NAME}.tar.gz
|
||||
|
||||
pushd ${PKG_DIST}
|
||||
|
||||
rm -f ../$TARBALL
|
||||
|
||||
echo "Compressing to ${TARBALL}..."
|
||||
|
||||
tar -czf ../${TARBALL} .
|
||||
|
||||
popd
|
||||
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cd $(dirname $0)/..
|
||||
|
||||
BUILD_DEBUG="${BUILD_DEBUG:-}"
|
||||
if [[ -n "${BUILD_DEBUG}" ]]; then
|
||||
set -x
|
||||
env
|
||||
fi
|
||||
|
||||
if [[ -n "$CI_BUILD_TAG" ]]; then
|
||||
echo "Build has been triggered by a tag. Skipping gate"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -z "$CI_BRANCH" ]]; then
|
||||
echo "No branch detected. Skipping gate"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -z "$GIT_REPO" ]]; then
|
||||
echo "No repository detected. Unable to gate"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$GIT_COMMIT" ]]; then
|
||||
echo "No commit detected. Unable to gate"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Repo: $GIT_REPO"
|
||||
echo "Branch: $CI_BRANCH"
|
||||
echo "Build Commit: $GIT_COMMIT"
|
||||
|
||||
tmp=$(mktemp -u)
|
||||
STATUS_CODE=$(curl -w "%{http_code}" -s -o $tmp https://api.github.com/repos/$GIT_REPO/branches/$CI_BRANCH)
|
||||
|
||||
if [ "$STATUS_CODE" == "403" ]; then
|
||||
RATE_LIMIT_REMAINING=$(curl -s https://api.github.com/rate_limit | ./scripts/jq-nano - rate remaining)
|
||||
echo "Remaining GITHUB requests available: $RATE_LIMIT_REMAINING"
|
||||
RATE_LIMIT_REMAINING=${RATE_LIMIT_REMAINING:-0}
|
||||
if ((RATE_LIMIT_REMAINING < 1)); then
|
||||
echo "Github Rate Limit has been hit, skipping gate"
|
||||
exit 0
|
||||
fi
|
||||
# Fall through to the normal path like any other failed status code
|
||||
fi
|
||||
|
||||
LATEST_BRANCH_COMMIT=$(cat $tmp | ./scripts/jq-nano - commit sha)
|
||||
rm $tmp
|
||||
echo "Latest Branch Commit: $LATEST_BRANCH_COMMIT"
|
||||
if [ -z "$LATEST_BRANCH_COMMIT" ]; then
|
||||
echo "Unable to determine latest commit, failing gate"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$LATEST_BRANCH_COMMIT" == "$GIT_COMMIT" ]; then
|
||||
echo "Build was created from latest commit, passed upload gate"
|
||||
else
|
||||
echo "Build was not created from latest commit, failing gate"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
yarn --pure-lockfile install
|
||||
|
||||
source scripts/version
|
||||
|
||||
if [[ $COMMIT_BRANCH == "main" ]]; then
|
||||
VERSION="latest"
|
||||
else
|
||||
VERSION=$COMMIT_BRANCH
|
||||
fi
|
||||
|
||||
echo "CI_BUILD_TAG: ${CI_BUILD_TAG}"
|
||||
echo "GIT_TAG: ${GIT_TAG}"
|
||||
echo "TAG_VERSION: ${TAG_VERSION}"
|
||||
|
||||
echo "CI Build Args"
|
||||
echo "COMMIT: ${COMMIT}"
|
||||
echo "COMMIT_BRANCH: ${COMMIT_BRANCH}"
|
||||
echo "VERSION: ${VERSION}"
|
||||
|
||||
if [ -n "$GIT_TAG" ]; then
|
||||
COMMIT=$COMMIT COMMIT_BRANCH=$COMMIT_BRANCH VERSION=$CI_BUILD_TAG ./scripts/build-pkg.sh ${1} "true"
|
||||
else
|
||||
COMMIT=$COMMIT COMMIT_BRANCH=$COMMIT_BRANCH VERSION=$VERSION ./scripts/build-pkg.sh ${1} "true"
|
||||
fi
|
||||
|
||||
EXIT_CODE=$?
|
||||
|
||||
if [ -n "$GIT_TAG" ]; then
|
||||
export PKG_NAME=${1}-${CI_BUILD_TAG}
|
||||
export PKG_TARBALL=${PKG_NAME}.tar.gz
|
||||
else
|
||||
export PKG_NAME=${1}-${VERSION} # harvester-release-harvester-v1.5 or harvester-latest
|
||||
export PKG_TARBALL=${PKG_NAME}.tar.gz # harvester-release-harvester-v1.5.tar.gz or harvester-latest.tar.gz
|
||||
fi
|
||||
|
||||
echo "CI Build Artifacts"
|
||||
echo "Package Directory: ${PKG_NAME}"
|
||||
echo "Package Tarball: ${PKG_TARBALL}"
|
||||
|
||||
ENV_OUTPUT="${GITHUB_OUTPUT:-"temp-env"}"
|
||||
echo "PKG_TARBALL=${PKG_TARBALL}" >> "$ENV_OUTPUT"
|
||||
echo "PKG_NAME=${PKG_NAME}" >> "$ENV_OUTPUT"
|
||||
|
||||
exit $EXIT_CODE
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const readline = require('readline');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const file = args.shift();
|
||||
|
||||
let text;
|
||||
|
||||
if ( file === '-' ) {
|
||||
process.stdin.resume();
|
||||
text = fs.readFileSync(0, "utf8");
|
||||
} else {
|
||||
text = fs.readFileSync(file, "utf8");
|
||||
}
|
||||
|
||||
const data = JSON.parse(text);
|
||||
|
||||
let out = data;
|
||||
while ( args.length ) {
|
||||
out = out[args.shift()];
|
||||
}
|
||||
|
||||
console.log(out);
|
||||
@@ -0,0 +1,24 @@
|
||||
const fs = require('fs');
|
||||
|
||||
console.log('Updating package file'); // eslint-disable-line no-console
|
||||
|
||||
if (process.argv.length !== 3) {
|
||||
console.error('No enough args'); // eslint-disable-line no-console
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const pkgFilepath = process.argv[process.argv.length - 1];
|
||||
const pkg = require(pkgFilepath);
|
||||
|
||||
// Ensure we have the keys we need
|
||||
|
||||
pkg.files = ['**/*'];
|
||||
pkg.rancher = true;
|
||||
pkg.main = `${ pkg.name }-${ pkg.version }.umd.min.js`;
|
||||
|
||||
delete pkg.scripts;
|
||||
delete pkg.browserslist;
|
||||
|
||||
|
||||
fs.writeFileSync(pkgFilepath, JSON.stringify(pkg, null, 2));
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
shopt -s extglob
|
||||
|
||||
if [ -n "$(git status --porcelain --untracked-files=no)" ]; then
|
||||
DIRTY="-dirty"
|
||||
fi
|
||||
|
||||
COMMIT=$(git rev-parse --short HEAD)
|
||||
COMMIT_DATE=$(git --no-pager log -1 --format='%ct')
|
||||
COMMIT_BRANCH=$(git rev-parse --abbrev-ref HEAD | sed -E 's/[^a-zA-Z0-9.-]+/-/g')
|
||||
GIT_TAG=${CI_BUILD_TAG:-$(git tag -l --contains HEAD | head -n 1)}
|
||||
LAST_TAG=${GIT_TAG:-'v0.0.0'}
|
||||
|
||||
if [[ -z "$DIRTY" && -n "$GIT_TAG" ]]; then
|
||||
# human readable version used on rancher dashboard about page
|
||||
export DASHBOARD_VERSION=$GIT_TAG
|
||||
# computer readable version
|
||||
VERSION=$GIT_TAG
|
||||
else
|
||||
# human readable version used on rancher dashboard about page
|
||||
export DASHBOARD_VERSION="${COMMIT_BRANCH} ${COMMIT}"
|
||||
# computer readable version
|
||||
VERSION="${COMMIT}${DIRTY}"
|
||||
fi
|
||||
|
||||
# Chart tag.
|
||||
if [[ -z "${DIRTY}" && -n "${GIT_TAG}" ]]; then
|
||||
CHART_VERSION="${GIT_TAG}"
|
||||
elif [[ "$DIRTY" ]]; then
|
||||
CHART_VERSION="${LAST_TAG}${DIRTY}.${COMMIT}"
|
||||
else
|
||||
CHART_VERSION="${LAST_TAG}-${COMMIT_DATE}.${COMMIT}.${COMMIT_BRANCH}"
|
||||
fi
|
||||
|
||||
# Drop the v prefix for Chart Version to follow existing pattern.
|
||||
CHART_VERSION="$(echo ${CHART_VERSION} | sed -E 's/^v//' | sed -e 's/^\(.\{55\}\).*/\1/')"
|
||||
|
||||
# Chart Repo - Target Repo for releases.
|
||||
case $CHART_VERSION in
|
||||
*-alpha*) # All alpha tags
|
||||
CHART_REPO="alpha" ;;
|
||||
*-rc*) # All rc tags
|
||||
CHART_REPO="latest" ;;
|
||||
+([0-9]).+([0-9]).+([0-9])) # All release tags
|
||||
CHART_REPO="latest" ;;
|
||||
*) # Anything else - Future use
|
||||
CHART_REPO="dev" ;;
|
||||
esac
|
||||
|
||||
if [ -z "$ARCH" ]; then
|
||||
ARCH=amd64
|
||||
fi
|
||||
|
||||
echo "ARCH: $ARCH"
|
||||
echo "CHART_REPO: $CHART_REPO"
|
||||
echo "CHART_VERSION: $CHART_VERSION"
|
||||
echo "VERSION: $VERSION"
|
||||
Reference in New Issue
Block a user