From 5fd562b96a11e8e3799b4e1029ad4a5487dc948d Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 30 Sep 2025 14:41:36 +0530 Subject: [PATCH 1/8] =?UTF-8?q?ui:=20introduce=20section-level=20=E2=80=9C?= =?UTF-8?q?advisories=E2=80=9D=20with=20quick-fix=20actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change adds a lightweight “advisories” mechanism to section configs and ships the first advisory to help operators satisfy some of the CKS prerequisites. Signed-off-by: Abhishek Kumar --- ui/public/locales/en.json | 17 +++ ui/src/api/index.js | 4 + ui/src/components/view/AdvisoriesView.vue | 159 +++++++++++++++++++ ui/src/config/router.js | 5 + ui/src/config/section/compute.js | 176 ++++++++++++++++++++++ ui/src/views/AutogenView.vue | 7 +- 6 files changed, 367 insertions(+), 1 deletion(-) create mode 100644 ui/src/components/view/AdvisoriesView.vue diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 1dd29d5a9297..30e8afd6fd03 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -287,8 +287,10 @@ "label.add.isolated.network": "Add Isolated Network", "label.add.kubernetes.cluster": "Add Kubernetes Cluster", "label.add.acl.name": "ACL name", +"label.add.latest.kubernetes.iso": "Add latest Kubernetes ISO", "label.add.ldap.account": "Add LDAP Account", "label.add.logical.router": "Add Logical Router to this Network", +"label.add.minimum.required.compute.offering": "Add minimum required Compute Offering", "label.add.more": "Add more", "label.add.nodes": "Add Nodes to Kubernetes Cluster", "label.add.netscaler.device": "Add Netscaler Device", @@ -1077,6 +1079,7 @@ "label.firewallruleuuid": "Firewall Rule", "label.firstname": "First name", "label.firstname.lower": "firstname", +"label.fix.configuration": "Fix configuration", "label.fix.errors": "Fix errors", "label.fixed": "Fixed Offering", "label.for": "for", @@ -1110,6 +1113,9 @@ "label.globo.dns.configuration": "GloboDNS configuration", "label.glustervolume": "Volume", "label.go.back": "Go back", +"label.go.to.compute.offerings": "Go to Compute Offerings", +"label.go.to.global.settings": "Go to Global Settings", +"label.go.to.kubernetes.isos": "Go to Kubernetes ISOs", "label.gpu": "GPU", "label.gpucardid": "GPU Card", "label.gpucardname": "GPU Card", @@ -3010,12 +3016,17 @@ "message.add.ip.v6.firewall.rule.failed": "Failed to add IPv6 firewall rule", "message.add.ip.v6.firewall.rule.processing": "Adding IPv6 firewall rule...", "message.add.ip.v6.firewall.rule.success": "Added IPv6 firewall rule", +"message.advisory.cks.endpoint.url.not.configured": "Endpoint URL which will be used by Kubernetes clusters is not configured correctly", +"message.advisory.cks.min.offering": "No suitable Compute Offering found for Kubernetes cluster nodes with minimum required resources (2 vCPU, 2 GB RAM)", +"message.advisory.cks.version.check": "No Kubernetes version found that can be used to deploy a Kubernetes cluster", "message.redeliver.webhook.delivery": "Redeliver this Webhook delivery", "message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule", "message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...", "message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule", "message.remove.sslcert.failed": "Failed to remove SSL certificate from load balancer", "message.remove.sslcert.processing": "Removing SSL certificate from load balancer...", +"message.add.latest.kubernetes.iso.failed": "Failed to add latest Kubernetes ISO", +"message.add.minimum.required.compute.offering.kubernetes.cluster.failed": "Failed to add minimum required Compute Offering for Kubernetes cluster nodes", "message.add.netris.controller": "Add Netris Provider", "message.add.nsx.controller": "Add NSX Provider", "message.add.network": "Add a new network for Zone: ", @@ -3056,9 +3067,13 @@ "message.add.vpn.gateway": "Please confirm that you want to add a VPN Gateway.", "message.add.vpn.gateway.failed": "Adding VPN gateway failed", "message.add.vpn.gateway.processing": "Adding VPN gateway...", +"message.added.latest.kubernetes.iso": "Latest Kubernetes ISO added successfully", +"message.added.minimum.required.compute.offering.kubernetes.cluster": "Minimum required Compute Offering for Kubernetes cluster nodes added successfully", "message.added.vpc.offering": "Added VPC offering", "message.adding.firewall.policy": "Adding Firewall Policy", "message.adding.host": "Adding host", +"message.adding.latest.kubernetes.iso": "Adding latest Kubernetes ISO", +"message.adding.minimum.required.compute.offering.kubernetes.cluster": "Adding minimum required Compute Offering for Kubernetes cluster nodes", "message.adding.netscaler.device": "Adding Netscaler device", "message.adding.netscaler.provider": "Adding Netscaler provider", "message.adding.nodes.to.cluster": "Adding nodes to Kubernetes cluster", @@ -3106,6 +3121,8 @@ "message.config.health.monitor.failed": "Configure Health Monitor failed", "message.config.sticky.policy.failed": "Failed to configure sticky policy.", "message.config.sticky.policy.processing": "Updating sticky policy...", +"message.config.updated": "Configuration updated successfully.", +"message.config.update.failed": "Failed to update configuration.", "message.configure.network.ip.and.mac": "Please configure the IP address and mac address of networks if needed.", "message.configure.network.select.default.network": "Please configure the IP address and mac address of networks if needed. Please select a network as the default network.", "message.configuring.guest.traffic": "Configuring guest traffic", diff --git a/ui/src/api/index.js b/ui/src/api/index.js index 0c8e8e9696c3..5ec73a0b20e1 100644 --- a/ui/src/api/index.js +++ b/ui/src/api/index.js @@ -140,3 +140,7 @@ export function oauthlogin (arg) { } }) } + +export function getBaseUrl () { + return vueProps.axios.defaults.baseURL +} diff --git a/ui/src/components/view/AdvisoriesView.vue b/ui/src/components/view/AdvisoriesView.vue new file mode 100644 index 000000000000..810892d891e1 --- /dev/null +++ b/ui/src/components/view/AdvisoriesView.vue @@ -0,0 +1,159 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + + + + diff --git a/ui/src/config/router.js b/ui/src/config/router.js index 582fbaaf2f35..16947f7567c5 100644 --- a/ui/src/config/router.js +++ b/ui/src/config/router.js @@ -81,6 +81,7 @@ function generateRouterMap (section) { filters: child.filters, params: child.params ? child.params : {}, columns: child.columns, + advisories: child.advisories, details: child.details, searchFilters: child.searchFilters, related: child.related, @@ -180,6 +181,10 @@ function generateRouterMap (section) { map.meta.columns = section.columns } + if (section.advisories) { + map.meta.advisories = section.advisories + } + if (section.actions) { map.meta.actions = section.actions } diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 7688566df423..c21e188115b1 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -18,6 +18,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' import { isZoneCreated } from '@/utils/zone' +import { getAPI, postAPI, getBaseUrl } from '@/api' export default { name: 'compute', @@ -581,6 +582,181 @@ export default { } ], resourceType: 'KubernetesCluster', + advisories: [ + { + id: 'cks-min-offering', + severity: 'warning', + message: 'message.advisory.cks.min.offering', + docsHelp: 'plugins/cloudstack-kubernetes-service.html', + dismissOnConditionFail: true, + condition: async (store) => { + if (!('listServiceOfferings' in store.getters.apis)) { + return false + } + const params = { + cpunumber: 2, + memory: 2048, + issystem: false + } + try { + const json = await getAPI('listServiceOfferings', params) + const offerings = json?.listserviceofferingsresponse?.serviceoffering || [] + return !offerings.some(o => !o.iscustomized) + } catch (error) {} + return false + }, + actions: [ + { + primary: true, + label: 'label.add.minimum.required.compute.offering', + loadingLabel: 'message.adding.minimum.required.compute.offering.kubernetes.cluster', + show: (store) => { return ('createServiceOffering' in store.getters.apis) }, + run: async () => { + const params = { + name: 'CKS Instance', + displaytext: 'CKS Instance', + cpunumber: 2, + cpuspeed: 1000, + memory: 2048, + iscustomized: false, + issystem: false + } + try { + const json = await postAPI('createServiceOffering', params) + if (json?.createserviceofferingresponse?.serviceoffering) { + return true + } + } catch (error) {} + return false + }, + successMessage: 'message.added.minimum.required.compute.offering.kubernetes.cluster', + errorMessage: 'message.add.minimum.required.compute.offering.kubernetes.cluster.failed' + }, + { + label: 'label.go.to.compute.offerings', + show: (store) => { return ('listServiceOfferings' in store.getters.apis) }, + run: (store, router) => { + router.push({ name: 'computeoffering' }) + return false + } + } + ] + }, + { + id: 'cks-version-check', + severity: 'warning', + message: 'message.advisory.cks.version.check', + docsHelp: 'plugins/cloudstack-kubernetes-service.html', + dismissOnConditionFail: true, + condition: async (store) => { + const api = 'listKubernetesSupportedVersions' + if (!(api in store.getters.apis)) { + return false + } + try { + const json = await getAPI(api, {}) + const versions = json?.listkubernetessupportedversionsresponse?.version || [] + return versions.length === 0 + } catch (error) {} + return false + }, + actions: [ + { + primary: true, + label: 'label.add.latest.kubernetes.iso', + loadingLabel: 'message.adding.latest.kubernetes.iso', + show: (store) => { return ('addKubernetesSupportedVersion' in store.getters.apis) }, + run: async () => { + const params = { + semanticversion: '1.33.1', + url: 'https://download.cloudstack.org/cks/setup-v1.33.1-calico-x86_64.iso', + displaytext: 'CKS Instance', + mincpunumber: 2, + minmemory: 2048 + } + try { + const json = await postAPI('addKubernetesSupportedVersion', params) + if (json?.addkubernetessupportedversionresponse?.kubernetessupportedversion) { + return true + } + } catch (error) {} + return false + }, + successMessage: 'message.added.latest.kubernetes.iso', + errorMessage: 'message.add.latest.kubernetes.iso.failed' + }, + { + label: 'label.go.to.kubernetes.isos', + show: true, + run: (store, router) => { + router.push({ name: 'kubernetesiso' }) + return false + } + } + ] + }, + { + id: 'cks-endpoint-url', + severity: 'warning', + message: 'message.advisory.cks.endpoint.url.not.configured', + docsHelp: 'plugins/cloudstack-kubernetes-service.html', + dismissOnConditionFail: true, + condition: async (store) => { + if (!['Admin'].includes(store.getters.userInfo.roletype)) { + return false + } + let url = '' + const baseUrl = getBaseUrl() + if (baseUrl.startsWith('/')) { + url = window.location.origin + baseUrl + } + if (!url || url.startsWith('http://localhost')) { + return false + } + const params = { + name: 'endpoint.url' + } + const json = await getAPI('listConfigurations', params) + const configuration = json?.listconfigurationsresponse?.configuration || {} + return !configuration.value || configuration.value.startsWith('http://localhost') + }, + actions: [ + { + primary: true, + label: 'label.fix.configuration', + show: (store) => { return ('updateConfiguration' in store.getters.apis) }, + run: async () => { + let url = '' + const baseUrl = getBaseUrl() + if (baseUrl.startsWith('/')) { + url = window.location.origin + baseUrl + } + var params = { + name: 'endpoint.url', + value: url + } + try { + const json = await postAPI('updateConfiguration', params) + if (json?.updateconfigurationresponse?.configuration) { + return true + } + } catch (error) {} + return false + }, + successMessage: 'message.config.updated', + errorMessage: 'message.config.update.failed' + }, + { + label: 'label.go.to.global.settings', + show: (store) => { return ('listConfigurations' in store.getters.apis) }, + run: (store, router) => { + router.push({ name: 'globalsetting' }) + return false + } + } + ] + } + ], actions: [ { api: 'createKubernetesCluster', diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue index f55767ded2a0..55d4f5df70f3 100644 --- a/ui/src/views/AutogenView.vue +++ b/ui/src/views/AutogenView.vue @@ -540,6 +540,9 @@ class="row-element" v-else > + Date: Tue, 30 Sep 2025 19:03:34 +0530 Subject: [PATCH 2/8] fix Signed-off-by: Abhishek Kumar --- ui/src/config/section/compute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index c21e188115b1..f0abda350974 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -655,7 +655,7 @@ export default { } try { const json = await getAPI(api, {}) - const versions = json?.listkubernetessupportedversionsresponse?.version || [] + const versions = json?.listkubernetessupportedversionsresponse?.kubernetessupportedversion || [] return versions.length === 0 } catch (error) {} return false From e39cdae5d5f1804cdd083834454f721119d12552 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 30 Sep 2025 19:05:45 +0530 Subject: [PATCH 3/8] fix endpoint.url check Signed-off-by: Abhishek Kumar --- ui/src/config/section/compute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index f0abda350974..426d5839ecbc 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -717,7 +717,7 @@ export default { name: 'endpoint.url' } const json = await getAPI('listConfigurations', params) - const configuration = json?.listconfigurationsresponse?.configuration || {} + const configuration = json?.listconfigurationsresponse?.configuration?.[0] || {} return !configuration.value || configuration.value.startsWith('http://localhost') }, actions: [ From c58198a1a2bb548e7080af87baf5fabd350a0987 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 30 Sep 2025 19:19:10 +0530 Subject: [PATCH 4/8] label consistency Signed-off-by: Abhishek Kumar --- ui/public/locales/en.json | 6 +++--- ui/src/config/section/compute.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 30e8afd6fd03..da1f3888eb65 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1079,8 +1079,8 @@ "label.firewallruleuuid": "Firewall Rule", "label.firstname": "First name", "label.firstname.lower": "firstname", -"label.fix.configuration": "Fix configuration", "label.fix.errors": "Fix errors", +"label.fix.global.setting": "Fix Global Setting", "label.fixed": "Fixed Offering", "label.for": "for", "label.forcks": "For CKS", @@ -3121,8 +3121,6 @@ "message.config.health.monitor.failed": "Configure Health Monitor failed", "message.config.sticky.policy.failed": "Failed to configure sticky policy.", "message.config.sticky.policy.processing": "Updating sticky policy...", -"message.config.updated": "Configuration updated successfully.", -"message.config.update.failed": "Failed to update configuration.", "message.configure.network.ip.and.mac": "Please configure the IP address and mac address of networks if needed.", "message.configure.network.select.default.network": "Please configure the IP address and mac address of networks if needed. Please select a network as the default network.", "message.configuring.guest.traffic": "Configuring guest traffic", @@ -3511,6 +3509,8 @@ "message.failed.to.remove": "Failed to remove", "message.forgot.password.success": "An email has been sent to your email address with instructions on how to reset your password.", "message.generate.keys": "Please confirm that you would like to generate new API/Secret keys for this User.", +"message.global.setting.updated": "Global Setting updated successfully.", +"message.global.setting.update.failed": "Failed to update Global Setting.", "message.chart.statistic.info": "The shown charts are self-adjustable, that means, if the value gets close to the limit or overpass it, it will grow to adjust the shown value", "message.chart.statistic.info.hypervisor.additionals": "The metrics data depend on the hypervisor plugin used for each hypervisor. The behavior can vary across different hypervisors. For instance, with KVM, metrics are real-time statistics provided by libvirt. In contrast, with VMware, the metrics are averaged data for a given time interval controlled by configuration.", "message.guest.traffic.in.advanced.zone": "Guest Network traffic is communication between end-user Instances. Specify a range of VLAN IDs or VXLAN Network identifiers (VNIs) to carry guest traffic for each physical Network.", diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 426d5839ecbc..03b33b2d98eb 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -723,7 +723,7 @@ export default { actions: [ { primary: true, - label: 'label.fix.configuration', + label: 'label.fix.global.setting', show: (store) => { return ('updateConfiguration' in store.getters.apis) }, run: async () => { let url = '' @@ -743,8 +743,8 @@ export default { } catch (error) {} return false }, - successMessage: 'message.config.updated', - errorMessage: 'message.config.update.failed' + successMessage: 'message.global.setting.updated', + errorMessage: 'message.global.setting.update.failed' }, { label: 'label.go.to.global.settings', From 32681bb0583c21b867c0d9d11195474ddf80491f Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 1 Oct 2025 13:10:09 +0530 Subject: [PATCH 5/8] Update ui/src/components/view/AdvisoriesView.vue Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- ui/src/components/view/AdvisoriesView.vue | 24 ++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ui/src/components/view/AdvisoriesView.vue b/ui/src/components/view/AdvisoriesView.vue index 810892d891e1..c668ec023801 100644 --- a/ui/src/components/view/AdvisoriesView.vue +++ b/ui/src/components/view/AdvisoriesView.vue @@ -62,22 +62,24 @@ export default { computed: { }, methods: { - evaluateAdvisories () { + async evaluateAdvisories () { this.advisories = [] const metaAdvisories = this.$route.meta.advisories || [] const dismissedAdvisories = this.$localStorage.get(DISMISSED_ADVISORIES_KEY) || [] - for (const advisory of metaAdvisories) { + const advisoryPromises = metaAdvisories.map(async advisory => { if (dismissedAdvisories.includes(advisory.id)) { - continue + return null } - Promise.resolve(advisory.condition(this.$store)).then(active => { - if (active) { - this.advisories.push(advisory) - } else if (advisory.dismissOnConditionFail) { - this.dismissAdvisory(advisory.id, true) - } - }) - } + const active = await Promise.resolve(advisory.condition(this.$store)) + if (active) { + return advisory + } else if (advisory.dismissOnConditionFail) { + this.dismissAdvisory(advisory.id, true) + } + return null + }) + const results = await Promise.all(advisoryPromises) + this.advisories = results.filter(a => a !== null) }, onAlertClose (advisory) { this.dismissAdvisory(advisory.id) From 53893c4fd00fe5185e61d80612f7d2b752908cbb Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 1 Oct 2025 15:24:44 +0530 Subject: [PATCH 6/8] improvements Signed-off-by: Abhishek Kumar --- ui/src/config/section/compute.js | 18 ++++--- ui/src/utils/acsrepo/index.js | 84 ++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 ui/src/utils/acsrepo/index.js diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 03b33b2d98eb..62273269d247 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -19,6 +19,7 @@ import { shallowRef, defineAsyncComponent } from 'vue' import store from '@/store' import { isZoneCreated } from '@/utils/zone' import { getAPI, postAPI, getBaseUrl } from '@/api' +import { getLatestKubernetesIsoParams } from '@/utils/acsrepo' export default { name: 'compute', @@ -614,7 +615,6 @@ export default { run: async () => { const params = { name: 'CKS Instance', - displaytext: 'CKS Instance', cpunumber: 2, cpuspeed: 1000, memory: 2048, @@ -667,13 +667,15 @@ export default { loadingLabel: 'message.adding.latest.kubernetes.iso', show: (store) => { return ('addKubernetesSupportedVersion' in store.getters.apis) }, run: async () => { - const params = { - semanticversion: '1.33.1', - url: 'https://download.cloudstack.org/cks/setup-v1.33.1-calico-x86_64.iso', - displaytext: 'CKS Instance', - mincpunumber: 2, - minmemory: 2048 + let arch = 'x86_64' + if ('listClusters' in store.getters.apis) { + try { + const json = await getAPI('listClusters', { allocationstate: 'Enabled', page: 1, pagesize: 1 }) + const cluster = json?.listclustersresponse?.cluster?.[0] || {} + arch = cluster.architecture || 'x86_64' + } catch (error) {} } + const params = await getLatestKubernetesIsoParams(arch) try { const json = await postAPI('addKubernetesSupportedVersion', params) if (json?.addkubernetessupportedversionresponse?.kubernetessupportedversion) { @@ -731,7 +733,7 @@ export default { if (baseUrl.startsWith('/')) { url = window.location.origin + baseUrl } - var params = { + const params = { name: 'endpoint.url', value: url } diff --git a/ui/src/utils/acsrepo/index.js b/ui/src/utils/acsrepo/index.js new file mode 100644 index 000000000000..0f9ea39b3d03 --- /dev/null +++ b/ui/src/utils/acsrepo/index.js @@ -0,0 +1,84 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +const BASE_KUBERNETES_ISO_URL = 'https://download.cloudstack.org/cks/' + +function getDefaultLatestKubernetesIsoParams (arch) { + return { + name: 'v1.33.1-calico-' + arch, + semanticversion: '1.33.1', + url: BASE_KUBERNETES_ISO_URL + 'setup-v1.33.1-calico-' + arch + '.iso', + arch: arch, + mincpunumber: 2, + minmemory: 2048 + } +} + +/** + * Returns the latest Kubernetes ISO info for the given architecture. + * Falls back to a hardcoded default if fetching fails. + * @param {string} arch + * @returns {Promise<{name: string, semanticversion: string, url: string, arch: string}>} + */ +export async function getLatestKubernetesIsoParams (arch) { + arch = arch || 'x86_64' + try { + const html = await fetch(BASE_KUBERNETES_ISO_URL, { cache: 'no-store' }).then(r => r.text()) + + // Grab all .iso hrefs from the index page + const hrefs = [...html.matchAll(/href="([^"]+\.iso)"/gi)].map(m => m[1]) + + // Prefer files that explicitly include the arch (e.g. ...-x86_64.iso) + let isoHrefs = hrefs.filter(h => new RegExp(`${arch}\\.iso$`, 'i').test(h)) + + // Fallback: older files without arch suffix (e.g. setup-1.28.4.iso) + if (isoHrefs.length === 0) { + isoHrefs = hrefs.filter(h => /setup-\d+\.\d+\.\d+\.iso$/i.test(h)) + } + + // Map to { name, semanticversion, url, arch } + const entries = isoHrefs.map(h => { + const m = h.match(/setup-(?:v)?(\d+\.\d+\.\d+)(?:-calico)?(?:-(x86_64|arm64))?/i) + return m + ? { + name: h.replace('.iso', ''), + semanticversion: m[1], + url: new URL(h, BASE_KUBERNETES_ISO_URL).toString(), + arch: m[2] || arch, + mincpunumber: 2, + minmemory: 2048 + } + : null + }).filter(Boolean) + + if (entries.length === 0) throw new Error('No matching ISOs found') + + // Semver-style sort descending + entries.sort((a, b) => { + const pa = a.semanticversion.split('.').map(Number) + const pb = b.semanticversion.split('.').map(Number) + for (let i = 0; i < 3; i++) { + if ((pb[i] ?? 0) !== (pa[i] ?? 0)) return (pb[i] ?? 0) - (pa[i] ?? 0) + } + return 0 + }) + + return entries[0] + } catch { + return { ...getDefaultLatestKubernetesIsoParams(arch) } + } +} From 553ebd31c7c754547e5287bc7ed9c7cc5d1ec5d9 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 1 Oct 2025 15:26:54 +0530 Subject: [PATCH 7/8] remove comments Signed-off-by: Abhishek Kumar --- ui/src/utils/acsrepo/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/ui/src/utils/acsrepo/index.js b/ui/src/utils/acsrepo/index.js index 0f9ea39b3d03..809bd7f17483 100644 --- a/ui/src/utils/acsrepo/index.js +++ b/ui/src/utils/acsrepo/index.js @@ -39,7 +39,6 @@ export async function getLatestKubernetesIsoParams (arch) { try { const html = await fetch(BASE_KUBERNETES_ISO_URL, { cache: 'no-store' }).then(r => r.text()) - // Grab all .iso hrefs from the index page const hrefs = [...html.matchAll(/href="([^"]+\.iso)"/gi)].map(m => m[1]) // Prefer files that explicitly include the arch (e.g. ...-x86_64.iso) @@ -50,7 +49,6 @@ export async function getLatestKubernetesIsoParams (arch) { isoHrefs = hrefs.filter(h => /setup-\d+\.\d+\.\d+\.iso$/i.test(h)) } - // Map to { name, semanticversion, url, arch } const entries = isoHrefs.map(h => { const m = h.match(/setup-(?:v)?(\d+\.\d+\.\d+)(?:-calico)?(?:-(x86_64|arm64))?/i) return m @@ -67,7 +65,6 @@ export async function getLatestKubernetesIsoParams (arch) { if (entries.length === 0) throw new Error('No matching ISOs found') - // Semver-style sort descending entries.sort((a, b) => { const pa = a.semanticversion.split('.').map(Number) const pb = b.semanticversion.split('.').map(Number) From cc644eb21e69ef071d27ae568b8ec85bf6e7b8fa Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 2 Oct 2025 12:44:40 +0530 Subject: [PATCH 8/8] allow disabling Signed-off-by: Abhishek Kumar --- ui/src/config/router.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/src/config/router.js b/ui/src/config/router.js index 16947f7567c5..04788dc3e693 100644 --- a/ui/src/config/router.js +++ b/ui/src/config/router.js @@ -81,7 +81,7 @@ function generateRouterMap (section) { filters: child.filters, params: child.params ? child.params : {}, columns: child.columns, - advisories: child.advisories, + advisories: !vueProps.$config.advisoriesDisabled ? child.advisories : undefined, details: child.details, searchFilters: child.searchFilters, related: child.related, @@ -181,7 +181,7 @@ function generateRouterMap (section) { map.meta.columns = section.columns } - if (section.advisories) { + if (!vueProps.$config.advisoriesDisabled && section.advisories) { map.meta.advisories = section.advisories }