diff --git a/pkg/harvester/config/harvester-cluster.js b/pkg/harvester/config/harvester-cluster.js index 19c40ec0..b127a9f4 100644 --- a/pkg/harvester/config/harvester-cluster.js +++ b/pkg/harvester/config/harvester-cluster.js @@ -57,10 +57,12 @@ import { } from './table-headers'; import { ADD_ONS } from './harvester-map'; import { registerAddonSideNav } from '../utils/dynamic-nav'; +import { setStandalonePrime } from '../utils/l10n-globals'; import { setVendor } from '@shell/config/private-label'; -import { createCssVars } from '@shell/utils/color'; import { CONFIGURABLE_TABLES } from '@shell/store/features'; import { tableViewsOffFeature } from '../utils/table-views'; +import { SETTING } from '@shell/config/settings'; +import { primeBrandUpdater } from '../utils/prime-brand'; const TEMPLATE = HCI.VM_VERSION; const MONITORING_GROUP = 'Monitoring & Logging::Monitoring'; @@ -123,27 +125,9 @@ export function init($plugin, store) { const [isHarvesterPrime, isDark, privateLabel] = JSON.parse(value); const primeLogo = isDark ? require('../assets/images/dark/suse-virtualization.svg') : require('../assets/images/suse-virtualization.svg'); + setStandalonePrime(isHarvesterPrime); setVendor(privateLabel || ''); - let primaryColorStyle = document.getElementById('harvester-prime-primary-color'); - - // set harvester prime primary color style to #4dd192 (Cyan Green) - if (isHarvesterPrime) { - if (!primaryColorStyle) { - primaryColorStyle = document.createElement('style'); - primaryColorStyle.id = 'harvester-prime-primary-color'; - document.head.appendChild(primaryColorStyle); - primaryColorStyle.sheet.insertRule('body.theme-light, body.theme-dark {}', 0); - } - - const colors = createCssVars('#4dd192', isDark ? 'dark' : 'light'); - const rule = primaryColorStyle.sheet.cssRules[0]; - - Object.entries(colors).forEach(([property, color]) => rule.style.setProperty(property.trim(), color)); - } else { - primaryColorStyle?.remove(); - } - const primeLabelKey = privateLabel ? 'harvester.branding.primeLabel' : 'harvester.branding.primeEmptyLabel'; store.dispatch('setIsSingleProduct', { @@ -169,6 +153,19 @@ export function init($plugin, store) { }, { immediate: true } ); + + const updateBrand = primeBrandUpdater(); + + // The value is read too: a change event from the server is loaded into the same setting object + store.watch( + (_state, getters) => { + const brandSetting = getters['management/byId'](MANAGEMENT.SETTING, SETTING.BRAND); + + return [!!getters['harvester-common/isHarvesterPrime'], brandSetting, brandSetting?.value]; + }, + ([isHarvesterPrime, brandSetting]) => updateBrand(brandSetting, isHarvesterPrime), + { immediate: true } + ); } product({ diff --git a/pkg/harvester/index.ts b/pkg/harvester/index.ts index 8acd4354..8270d904 100644 --- a/pkg/harvester/index.ts +++ b/pkg/harvester/index.ts @@ -6,6 +6,9 @@ import harvesterCommonStore from './store/harvester-common'; import harvesterStore from './store/harvester-store'; import customValidators from './validators'; import { PRODUCT_NAME } from './config/harvester'; +import { + hostResolvesL10nGlobals, resolveL10nGlobals, standaloneProductName, PRODUCT_NAME_GLOBAL +} from './utils/l10n-globals'; import { defineAsyncComponent } from 'vue'; import './styles/vue-flow.scss'; @@ -17,6 +20,15 @@ export default function (plugin: IPlugin) { // Auto-import model, detail, edit from the folders importTypes(plugin); + if (isSingleVirtualCluster) { + // Standalone: the server-flavor setting picks the product name, see config/harvester-cluster + plugin.register('l10n-global', PRODUCT_NAME_GLOBAL, standaloneProductName); + } else if (!hostResolvesL10nGlobals(plugin.environment?.version)) { + // Hosts older than Rancher 2.16 would show `[[Harvester]]` literally, so resolve the tokens here. + // Mutating the module in place also updates the copy that importTypes registered. + resolveL10nGlobals(require('./l10n/en-us.yaml')); + } + // Provide plugin metadata from package.json plugin.metadata = require('./package.json'); diff --git a/pkg/harvester/l10n/en-us.yaml b/pkg/harvester/l10n/en-us.yaml index 6c7d04a0..2fce5798 100644 --- a/pkg/harvester/l10n/en-us.yaml +++ b/pkg/harvester/l10n/en-us.yaml @@ -25,8 +25,8 @@ generic: unsupported: serverVersion: 'Current version: {serverVersion}' - description: Harvester ui only supports Harvester cluster version greater or equal to 1.3.0. - learnMore: Learn more about Harvester from the Harvester Web Site or read the Harvester Docs + description: '[[Harvester]] ui only supports [[Harvester]] cluster version greater or equal to 1.3.0.' + learnMore: Learn more about [[Harvester]] from the [[Harvester]] Web Site or read the [[Harvester]] Docs nav: group: @@ -87,10 +87,10 @@ harvester: primeLabel: '{vendor}' primeEmptyLabel: '' logos: - tip: 'Upload a logo to replace the Harvester logo in the top-level navigation header. Image height should be 21 pixels with a max width of 200 pixels. Max file size is 20KB. Accepted formats: JPEG, PNG, SVG.' + tip: 'Upload a logo to replace the [[Harvester]] logo in the top-level navigation header. Image height should be 21 pixels with a max width of 200 pixels. Max file size is 20KB. Accepted formats: JPEG, PNG, SVG.' favicon: - tip: 'Upload an icon to replace the Harvester favicon in the browser tab. Max file size is 20KB' - productLabel: 'Harvester' + tip: 'Upload an icon to replace the [[Harvester]] favicon in the browser tab. Max file size is 20KB' + productLabel: '[[Harvester]]' modal: backup: success: 'Backup { backUpName } has been initiated.' @@ -182,7 +182,7 @@ harvester: delete: Delete bundle: title: Generate a Support Bundle - titleDescription: Collect system-related logs in Harvester to assist with troubleshooting and support. + titleDescription: Collect system-related logs in [[Harvester]] to assist with troubleshooting and support. tip: Each field below specifies an aspect of the support bundle. For detailed explanations, please refer to the documentation. url: Issue URL description: Description @@ -336,7 +336,7 @@ harvester: attachedVM: Attached Virtual Machine cpuManager: CPU
Manager diskState: Disk - routeConnectivityTooltip: Connectivity between the VM network and the management network, which the Harvester nodes are connected to. + routeConnectivityTooltip: Connectivity between the VM network and the management network, which the [[Harvester]] nodes are connected to. fingerprint: Fingerprint value: Value actions: Actions @@ -501,7 +501,7 @@ harvester: aclEmptyError: The fields in subnet access control list rule can not be empty. dashboard: label: Dashboard - header: "Harvester Cluster: {cluster}" + header: "[[Harvester]] Cluster: {cluster}" hardwareResourceGauge: cpu: CPU memory: Memory @@ -839,7 +839,7 @@ harvester: collapse: Hide VM-wide high-performance options blockMultiQueue: label: Virtio Block Multi-Queue - tip: Map disk I/O to multiple queues so it is processed across multiple CPUs. Applies to virtio disks and requires a fixed CPU allocation (Harvester always sets one). + tip: Map disk I/O to multiple queues so it is processed across multiple CPUs. Applies to virtio disks and requires a fixed CPU allocation ([[Harvester]] always sets one). noVirtioTip: Add at least one disk on the virtio bus to enable Block Multi-Queue. ioThreadsPolicy: label: I/O Threads Policy @@ -939,7 +939,7 @@ harvester: displayNamePlaceholder: Virtual machine alias name displayName: Display Name filesystem: - description: Harvester supports filesystem volumes for VM via virtiofs. + description: '[[Harvester]] supports filesystem volumes for VM via virtiofs.' type: Filesystem Type volume: Volume resource: Resource @@ -1193,8 +1193,8 @@ harvester: selectExisting: Select Existing Image createRepository: Creating Upgrade Repository succeeded: Succeeded - releaseTip: Please read the upgrade documentation carefully. You can view details on the Harvester Release Notes. - checkReady: I have read and understood the upgrade instructions related to this Harvester version. + releaseTip: Please read the upgrade documentation carefully. You can view details on the [[Harvester]] Release Notes. + checkReady: I have read and understood the upgrade instructions related to this [[Harvester]] version. pending: Pending upload: duplicatedFile: The file you are trying to upload already exists. @@ -1203,7 +1203,7 @@ harvester: os: OS kubernetes: Kubernetes monitoringChart: Monitoring Chart - harvesterChart: Harvester Chart + harvesterChart: '[[Harvester]] Chart' success: Success fail: Fail ongoing: On-going @@ -1243,7 +1243,7 @@ harvester: maxFailure: label: Max Failure count: Count - tooltip: Max number of consecutive failed backups that could be tolerated. If reach this threshold, Harvester controller will suspend the schedule job. This value should less than retain count + tooltip: Max number of consecutive failed backups that could be tolerated. If reach this threshold, [[Harvester]] controller will suspend the schedule job. This value should less than retain count virtualMachine: title: Virtual Machine Name placeholder: Select a virtual machine @@ -1257,7 +1257,7 @@ harvester: title: Restore Virtual Machine refreshInterval: label: Refresh Interval - tip: The interval for the backup target to sync data to harvester cluster. Set to 0 to disable refresh mechanism. + tip: The interval for the backup target to sync data to [[Harvester]] cluster. Set to 0 to disable refresh mechanism. backupTargetTip: The endpoint used to access the backupstore. NFS and S3 are supported. message: noSetting: @@ -1276,7 +1276,7 @@ harvester: actionLabel: Test connection waitingLabel: Testing connection... successLabel: Connection tested - successMessage: 'Harvester can successfully connect to the endpoint {endpoint}' + successMessage: '[[Harvester]] can successfully connect to the endpoint {endpoint}' restore: backup: Backup deletePreviousVolumes: Delete Previous Volumes @@ -1487,7 +1487,7 @@ harvester: placeholder: Select a provider network networkPolicy: label: Policies - banner: The network policies must be used for VMs attached to overlay networks. Please read the harvester document how the network policy works. + banner: The network policies must be used for VMs attached to overlay networks. Please read the [[Harvester]] documentation on how network policies work. network: label: Virtual Machine Networks tabs: @@ -1596,7 +1596,7 @@ harvester: autoRotateRKE2Certs: expiringInHours: Expiring in httpProxy: - warning: 'WARNING:
If you configure "http-proxy" and "https-proxy", you must also put Harvester node's CIDR into "no-proxy", otherwise the Harvester cluster will be broken.' + warning: 'WARNING:
If you configure "http-proxy" and "https-proxy", you must also put [[Harvester]] node's CIDR into "no-proxy", otherwise the [[Harvester]] cluster will be broken.' supportBundleImage: imagePullPolicy: Pull Policy repo: Repository @@ -1648,7 +1648,7 @@ harvester: strategyType: Strategy Type concurrency: Concurrency harvesterMonitoring: - label: Harvester Monitoring + label: '[[Harvester]] Monitoring' section: prometheus: Prometheus prometheusNodeExporter: Prometheus Node Exporter @@ -1663,10 +1663,10 @@ harvester: url: URL insecureSkipTLSVerify: Insecure Skip TLS Verify tip: - prefix: Harvester secures cluster registration via TLS by default. If opt out "Insecure Skip TLS Verify", you must provide custom CA certificates using the + prefix: '[[Harvester]] secures cluster registration via TLS by default. If opt out "Insecure Skip TLS Verify", you must provide custom CA certificates using the' middle: 'additional-ca' suffix: setting. - message: To completely unset the imported Harvester cluster, please also remove it on the Rancher Dashboard UI via the Virtualization Management page. + message: To completely unset the imported [[Harvester]] cluster, please also remove it on the Rancher Dashboard UI via the Virtualization Management page. ntpServers: isNotIPV4: The address you entered is not IPv4 or host. Please enter a valid IPv4 address or a host address. isDuplicate: There are duplicate NTP server configurations. @@ -1696,19 +1696,19 @@ harvester: networkData: Network Data support: - title: Harvester Support + title: '[[Harvester]] Support' kubeconfig: title: Download KubeConfig titleDescription: Download kubeconfig for debugging. internal: rancher: title: Access Embedded Rancher UI - titleDescription: You can only use the embedded Rancher UI for debugging and validation purposes. For more information about how Harvester integrates with Rancher, see the documentation. + titleDescription: You can only use the embedded Rancher UI for debugging and validation purposes. For more information about how [[Harvester]] integrates with Rancher, see the documentation. longhorn: title: Access Embedded Longhorn UI titleDescription: We only support to use the embedded Longhorn UI for debugging and validation purpose. community: - learnMore: Find out more about SUSE Harvester support + learnMore: Find out more about [[Harvester]] support pricing: Contact us for pricing namespace: @@ -1928,16 +1928,16 @@ harvester: addons: descriptions: - 'harvester-system/vm-import-controller': vm-import-controller is an add-on to help migrate virtual machine workloads from other source clusters to an existing Harvester cluster. - 'harvester-system/pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester virtual machine and guest clusters. - 'cattle-logging-system/rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the Harvester cluster and route them to many kinds of servers based on flows. + 'harvester-system/vm-import-controller': vm-import-controller is an add-on to help migrate virtual machine workloads from other source clusters to an existing [[Harvester]] cluster. + 'harvester-system/pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with [[Harvester]] virtual machine and guest clusters. + 'cattle-logging-system/rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the [[Harvester]] cluster and route them to many kinds of servers based on flows. 'harvester-system/rancher-vcluster': rancher-vcluster deploys a virtual cluster (vcluster) with Rancher installed. - 'cattle-monitoring-system/rancher-monitoring': rancher-monitoring is an add-on that collects Harvester cluster and virtual machine metrics and allows you to view the metrics on an embedded dashboard and send alert(s) to remote servers. - 'vm-import-controller': vm-import-controller is an add-on to help migrate virtual machine workloads from other source clusters to an existing Harvester cluster. - 'pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with Harvester virtual machines and guest clusters. - 'nvidia-driver-toolkit': 'nvidia-driver-toolkit is an add-on to enable vGPU devices and assign them to Harvester virtual machines.' - 'rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the Harvester cluster and route them to many kinds of servers based on flows. - 'rancher-monitoring': rancher-monitoring is an add-on to collect Harvester cluster and virtual machine metrics, view them on the embedded dashboard, and send alert(s) to remote servers. + 'cattle-monitoring-system/rancher-monitoring': rancher-monitoring is an add-on that collects [[Harvester]] cluster and virtual machine metrics and allows you to view the metrics on an embedded dashboard and send alert(s) to remote servers. + 'vm-import-controller': vm-import-controller is an add-on to help migrate virtual machine workloads from other source clusters to an existing [[Harvester]] cluster. + 'pcidevices-controller': pcidevices-controller is an add-on to help discover PCI devices for nodes in your cluster and allow users to prepare devices for PCI Passthrough, for use with [[Harvester]] virtual machines and guest clusters. + 'nvidia-driver-toolkit': 'nvidia-driver-toolkit is an add-on to enable vGPU devices and assign them to [[Harvester]] virtual machines.' + 'rancher-logging': rancher-logging is an add-on to collect versatile logs, events, and audits from the [[Harvester]] cluster and route them to many kinds of servers based on flows. + 'rancher-monitoring': rancher-monitoring is an add-on to collect [[Harvester]] cluster and virtual machine metrics, view them on the embedded dashboard, and send alert(s) to remote servers. 'rancher-vcluster': rancher-vcluster deploys a virtual cluster (vcluster) with Rancher installed. 'k3k': k3k is an add-on to deploy the k3k controller. 'rancher-k3k': rancher-k3k deploys a k3k virtual cluster with Rancher installed. @@ -2006,7 +2006,7 @@ harvester: providerSelect: Provider createNew: Create new provider requirementsTitle: Requirements - requirementsText: "To migrate virtual machines from VMware, you will need a vCenter Server running version 6.5 or higher with a user account that has at least read permissions, and the source environment must be network-accessible from Harvester Node." + requirementsText: "To migrate virtual machines from VMware, you will need a vCenter Server running version 6.5 or higher with a user account that has at least read permissions, and the source environment must be network-accessible from [[Harvester]] Node." name: Name urlLabel: vCenter/ESXi URL urlPlaceholder: "https://vcenter.example.com/sdk" @@ -2059,7 +2059,7 @@ harvester: datastore: Datastore dashboard: title: Virtual Machine Migration Overview - description: "Migrate virtual machines from VMware vSphere to Harvester using cold migration for data consistency. Create a migration plan to select VMs, map networks and storage, and prepare a migration to Harvester." + description: "Migrate virtual machines from VMware vSphere to [[Harvester]] using cold migration for data consistency. Create a migration plan to select VMs, map networks and storage, and prepare a migration to [[Harvester]]." createPlan: Create Migration tableTitle: Migrations columns: @@ -2079,22 +2079,22 @@ harvester: checkMigratedVm: Click to check migrated VM details configureMappings: title: Set Mappings - description: Map VMware networks and datastores to Harvester and Longhorn target resources + description: Map VMware networks and datastores to [[Harvester]] and Longhorn target resources save: Save Mappings and Continue noTemplate: Start from scratch removeMap: Remove mapping networkMapping: title: Network Mapping - description: Map VMware port groups to Harvester networks - placeholder: Choose a Harvester network + description: Map VMware port groups to [[Harvester]] networks + placeholder: Choose a [[Harvester]] network template: Use existing network mapping as template options: podNetworking: Management Network ignored: Ignored (No NIC will be created) storageMapping: title: Storage Mapping - description: Map VMware datastores to Harvester storage classes - placeholder: Choose a Harvester Storage Class + description: Map VMware datastores to [[Harvester]] storage classes + placeholder: Choose a [[Harvester]] Storage Class template: Use existing storage mapping as template storageDefaults: title: 'Storage defaults — {name}' @@ -2413,7 +2413,7 @@ harvester: vGpuDevices: vGPU Devices showMore: Show More parentSriov: Filter By Parent SR-IOV GPU - noPermission: Please contact your system administrator to add Harvester add-ons first. + noPermission: Please contact your system administrator to add [[Harvester]] add-ons first. goSetting: prefix: The nvidia-driver-toolkit add-on is not enabled, click middle: here @@ -2449,7 +2449,7 @@ harvester: vgpu: label: vGPU Devices - noPermission: Please contact system administrator to add Harvester add-ons first. + noPermission: Please contact system administrator to add [[Harvester]] add-ons first. detachWarning: title: Cannot Disable vGPU message: Please detach the device from the VM and save it first before disabling this vGPU device. @@ -2470,7 +2470,7 @@ harvester: usb: label: USB Devices - noPermission: Please contact system admin to add Harvester add-ons first + noPermission: Please contact system admin to add [[Harvester]] add-ons first goSetting: prefix: The pcidevices-controller add-on is not enabled, click middle: here @@ -2531,7 +2531,7 @@ typeDescription: # Should fit on one line. # If you link to anything external, it MUST have # target="_blank" rel="noopener noreferrer nofollow" - harvester: "Branding allows administrators to globally re-brand the UI by customizing the Harvester product name, logos and color scheme." + harvester: "Branding allows administrators to globally re-brand the UI by customizing the [[Harvester]] product name, logos and color scheme." advancedSettings: tags: @@ -2546,19 +2546,19 @@ advancedSettings: descriptions: 'harv-vlan': Default Network Interface name of the VLAN network. 'harv-backup-target': Custom backup target to store virtual machine backups. - 'branding': Branding allows administrators to globally re-brand the UI by customizing the Harvester product name, logos, and color scheme. + 'branding': Branding allows administrators to globally re-brand the UI by customizing the [[Harvester]] product name, logos, and color scheme. 'harv-csi-driver-config': Configure additional information for CSI drivers. 'harv-csi-online-expand-validation': Allow online volume expansion for specific CSI drivers. 'harv-containerd-registry': Containerd Registry Configuration to connect private registries. - 'harv-log-level': Configure Harvester server log level. Defaults to Info. - 'harv-server-version': Harvester server version. - 'harv-server-flavor': "Indicates the Harvester edition: Prime or Community." - 'harv-upgrade-checker-enabled': Specifies whether to enable Harvester upgrade check or not. Default is True. - 'harv-upgrade-checker-url': Default Harvester upgrade check url. Only used when the upgrade-checker-enabled is equal to True. - 'harv-ui-source': Configure how to load the UI source. The `External` option is used to load the UI from a ui-index URL, while the `Bundled` option loads the UI from the local Harvester server. + 'harv-log-level': Configure [[Harvester]] server log level. Defaults to Info. + 'harv-server-version': '[[Harvester]] server version.' + 'harv-server-flavor': "Indicates the [[Harvester]] edition: Prime or Community." + 'harv-upgrade-checker-enabled': Specifies whether to enable [[Harvester]] upgrade check or not. Default is True. + 'harv-upgrade-checker-url': Default [[Harvester]] upgrade check url. Only used when the upgrade-checker-enabled is equal to True. + 'harv-ui-source': Configure how to load the UI source. The `External` option is used to load the UI from a ui-index URL, while the `Bundled` option loads the UI from the local [[Harvester]] server. 'harv-ui-index': 'HTML index location for the UI.' 'harv-cluster-registration-url': Registration URL for multi-cluster management. - 'harv-http-proxy': 'HTTP proxy for Harvester to access external services.' + 'harv-http-proxy': 'HTTP proxy for [[Harvester]] to access external services.' 'harv-additional-ca': 'Custom CA root certificates for TLS validation.' 'harv-overcommit-config': 'Resource overcommit configuration.' 'harv-support-bundle-timeout': 'Support bundle timeout configuration in minutes, use 0 to disable the timeout.' @@ -2570,13 +2570,13 @@ advancedSettings: 'harv-storage-network': 'Longhorn storage-network setting.' 'harv-rwx-network': 'Configure RWX network behavior for shared or dedicated storage network usage.' 'harv-support-bundle-namespaces': Select additional namespaces to include in the support bundle. - 'harv-auto-disk-provision-paths': Specify the disks(using glob pattern) that Harvester will automatically add as virtual machine storage. + 'harv-auto-disk-provision-paths': Specify the disks(using glob pattern) that [[Harvester]] will automatically add as virtual machine storage. 'harv-support-bundle-image': Support bundle image configuration. Find different versions in rancher/support-bundle-kit. - 'harv-release-download-url': This setting allows you to configure the upgrade release download URL address. Harvester will get the ISO URL and checksum value from the ($URL/$VERSION/version.yaml) file hosted by the configured URL. + 'harv-release-download-url': This setting allows you to configure the upgrade release download URL address. [[Harvester]] will get the ISO URL and checksum value from the ($URL/$VERSION/version.yaml) file hosted by the configured URL. 'harv-default-vm-termination-grace-period-seconds': Configure the virtual machine termination grace period for virtual machine stop. 'harv-ntp-servers': Configure NTP server. You can configure multiple IPv4 addresses or host addresses. - 'harv-auto-rotate-rke2-certs': The certificate rotation mechanism relies on Rancher. Harvester will automatically update certificates generation to trigger rotation. - 'harv-kubeconfig-default-token-ttl-minutes': 'TTL (in minutes) applied on Harvester administration kubeconfig files. Default is 0, which means to never expire.' + 'harv-auto-rotate-rke2-certs': The certificate rotation mechanism relies on Rancher. [[Harvester]] will automatically update certificates generation to trigger rotation. + 'harv-kubeconfig-default-token-ttl-minutes': 'TTL (in minutes) applied on [[Harvester]] administration kubeconfig files. Default is 0, which means to never expire.' 'harv-longhorn-v2-data-engine-enabled': 'Enable the Longhorn V2 data engine. Default is false. ' 'harv-longhorn-v2-data-engine-hugepage-enabled': 'Enable hugepages when using the Longhorn V2 data engine. Default is true. Disabling hugepages reduces memory pressure on low-spec nodes and increases deployment flexibility. However, performance may be lower compared to running with hugepages.' 'harv-longhorn-v2-data-engine-memory-size': 'Configure the amount of memory allocated to the SPDK target daemon when using the Longhorn V2 data engine. Default is 2048 MiB.' diff --git a/pkg/harvester/utils/__tests__/l10n-globals.test.js b/pkg/harvester/utils/__tests__/l10n-globals.test.js new file mode 100644 index 00000000..38e3932e --- /dev/null +++ b/pkg/harvester/utils/__tests__/l10n-globals.test.js @@ -0,0 +1,101 @@ +import fs from 'fs'; +import path from 'path'; +import { load } from 'js-yaml'; +import { hostResolvesL10nGlobals, resolveL10nGlobals, setStandalonePrime, standaloneProductName } from '../l10n-globals'; + +describe('hostResolvesL10nGlobals', () => { + it('should be true from Rancher 2.16', () => { + expect(hostResolvesL10nGlobals('v2.16.0')).toBe(true); + expect(hostResolvesL10nGlobals('v2.17.1')).toBe(true); + }); + + it('should be true for 2.16 head builds', () => { + expect(hostResolvesL10nGlobals('v2.16.0-68ddd75-head')).toBe(true); + expect(hostResolvesL10nGlobals('v2.16-136b11a0062247c1b28f107d82283bdb29bc07ae-head')).toBe(true); + }); + + it('should be false before Rancher 2.16', () => { + expect(hostResolvesL10nGlobals('v2.15.1')).toBe(false); + expect(hostResolvesL10nGlobals('v2.14.6')).toBe(false); + }); + + it('should be false when the Rancher version is missing or unknown', () => { + expect(hostResolvesL10nGlobals('')).toBe(false); + expect(hostResolvesL10nGlobals(undefined)).toBe(false); + expect(hostResolvesL10nGlobals('dev')).toBe(false); + }); +}); + +describe('standaloneProductName', () => { + it('should be Harvester when the server flavor is not prime', () => { + setStandalonePrime(false); + + expect(standaloneProductName()).toBe('Harvester'); + }); + + it('should be SUSE Virtualization when the server flavor is prime', () => { + setStandalonePrime(true); + + expect(standaloneProductName()).toBe('SUSE Virtualization'); + setStandalonePrime(false); + }); + + it('should go back to Harvester when the server flavor changes from prime', () => { + setStandalonePrime(true); + setStandalonePrime(false); + + expect(standaloneProductName()).toBe('Harvester'); + }); +}); + +describe('resolveL10nGlobals', () => { + it('should replace tokens with their name when no value is given', () => { + const translations = { support: { title: '[[Harvester]] Support' } }; + + resolveL10nGlobals(translations); + + expect(translations).toStrictEqual({ support: { title: 'Harvester Support' } }); + }); + + it('should replace every token in a string with the given value', () => { + const translations = { message: 'The [[Harvester]] UI only supports [[Harvester]] v1.3.0 and later' }; + + resolveL10nGlobals(translations, { Harvester: 'SUSE Virtualization' }); + + expect(translations.message).toBe('The SUSE Virtualization UI only supports SUSE Virtualization v1.3.0 and later'); + }); + + it('should keep an escaped token as a literal', () => { + const translations = { message: 'Use \\[[Harvester]] in a string' }; + + resolveL10nGlobals(translations); + + expect(translations.message).toBe('Use [[Harvester]] in a string'); + }); + + it('should leave strings without tokens and non-string values untouched', () => { + const translations = { + plain: 'harvester-seeder is an add-on', + count: 3, + nested: { link: 'site' }, + }; + + resolveL10nGlobals(translations); + + expect(translations).toStrictEqual({ + plain: 'harvester-seeder is an add-on', + count: 3, + nested: { link: 'site' }, + }); + }); + + it('should leave no token in the extension translations', () => { + const translations = load(fs.readFileSync(path.join(__dirname, '../../l10n/en-us.yaml'), 'utf8')); + + expect(JSON.stringify(translations)).toContain('[[Harvester]]'); + + resolveL10nGlobals(translations); + + expect(JSON.stringify(translations)).not.toContain('[['); + }); +}); diff --git a/pkg/harvester/utils/__tests__/prime-brand.test.js b/pkg/harvester/utils/__tests__/prime-brand.test.js new file mode 100644 index 00000000..a9c7d1de --- /dev/null +++ b/pkg/harvester/utils/__tests__/prime-brand.test.js @@ -0,0 +1,54 @@ +import { primeBrandUpdater } from '../prime-brand'; + +describe('primeBrandUpdater', () => { + it('should switch the brand to suse on Prime', () => { + const setting = { value: 'harvester' }; + + primeBrandUpdater()(setting, true); + + expect(setting.value).toBe('suse'); + }); + + it('should keep the brand of the server when not on Prime', () => { + const setting = { value: 'harvester' }; + + primeBrandUpdater()(setting, false); + + expect(setting.value).toBe('harvester'); + }); + + it('should put the brand of the server back when the flavor stops being Prime', () => { + const setting = { value: 'harvester' }; + const update = primeBrandUpdater(); + + update(setting, true); + update(setting, false); + + expect(setting.value).toBe('harvester'); + }); + + it('should switch the brand to suse again when the server sends its brand back on Prime', () => { + const setting = { value: 'harvester' }; + const update = primeBrandUpdater(); + + update(setting, true); + setting.value = 'harvester'; + update(setting, true); + + expect(setting.value).toBe('suse'); + }); + + it('should keep an empty brand of the server when not on Prime', () => { + const setting = { value: '' }; + const update = primeBrandUpdater(); + + update(setting, true); + update(setting, false); + + expect(setting.value).toBe(''); + }); + + it('should do nothing before the setting is loaded', () => { + expect(() => primeBrandUpdater()(undefined, true)).not.toThrow(); + }); +}); diff --git a/pkg/harvester/utils/l10n-globals.js b/pkg/harvester/utils/l10n-globals.js new file mode 100644 index 00000000..9182bcd7 --- /dev/null +++ b/pkg/harvester/utils/l10n-globals.js @@ -0,0 +1,49 @@ +import semver from 'semver'; + +// Rancher 2.16 resolves `[[name]]` tokens in translations from values registered as 'l10n-global' +export const L10N_GLOBALS_MIN_RANCHER_VERSION = '2.16.0'; + +// Name of the product name token, `[[Harvester]]`, and its value on Prime +export const PRODUCT_NAME_GLOBAL = 'Harvester'; +export const PRIME_PRODUCT_NAME = 'SUSE Virtualization'; + +const TOKEN = /(\\?)\[\[([\w-]+)\]\]/g; + +// Standalone has no Rancher Prime, the server-flavor setting decides instead +let standalonePrime = false; + +export function setStandalonePrime(isPrime) { + standalonePrime = !!isPrime; +} + +export function standaloneProductName() { + return standalonePrime ? PRIME_PRODUCT_NAME : PRODUCT_NAME_GLOBAL; +} + +/** + * Whether the host Rancher resolves `[[name]]` tokens itself. + * A missing or unknown version counts as an older host. + */ +export function hostResolvesL10nGlobals(version) { + const v = semver.coerce(version); + + return !!v && semver.gte(v, L10N_GLOBALS_MIN_RANCHER_VERSION); +} + +/** + * Replace `[[name]]` tokens in every string of `translations`, in place, the same way Rancher 2.16 does: + * a known name gets its value, an unknown one falls back to the name and `\[[name]]` stays a literal `[[name]]` + */ +export function resolveL10nGlobals(translations, values = {}) { + Object.keys(translations || {}).forEach((key) => { + const value = translations[key]; + + if (typeof value === 'string') { + translations[key] = value.replace(TOKEN, (_, escaped, name) => (escaped ? `[[${ name }]]` : (values[name] ?? name))); + } else if (value && typeof value === 'object') { + resolveL10nGlobals(value, values); + } + }); + + return translations; +} diff --git a/pkg/harvester/utils/prime-brand.js b/pkg/harvester/utils/prime-brand.js new file mode 100644 index 00000000..555d5f0f --- /dev/null +++ b/pkg/harvester/utils/prime-brand.js @@ -0,0 +1,26 @@ +import { BRAND } from '@shell/config/types'; + +/** + * Standalone Prime takes the SUSE theme of Rancher Prime: the `ui-brand` setting decides the stylesheet the shell + * loads, and only the `suse` brand has one. Returns an updater that switches the copy of the setting in the store, + * the server keeps its value, and puts the server's brand back when the flavor stops being Prime. + */ +export function primeBrandUpdater() { + let serverBrand; + + return (brandSetting, isPrime) => { + if (!brandSetting) { + return; + } + + if (brandSetting.value !== BRAND.SUSE) { + serverBrand = brandSetting.value; + } + + const brand = isPrime ? BRAND.SUSE : (serverBrand ?? brandSetting.value); + + if (brandSetting.value !== brand) { + brandSetting.value = brand; + } + }; +}