This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 18075ae4a96 Add support for Headlamp dashboard for kubernetes; 
deprecate legacy kubernetes dashboard (#12776)
18075ae4a96 is described below

commit 18075ae4a96be1b545c8d8a5a73004911c6079e7
Author: Pearl Dsilva <[email protected]>
AuthorDate: Mon Mar 30 10:04:45 2026 -0400

    Add support for Headlamp dashboard for kubernetes; deprecate legacy 
kubernetes dashboard (#12776)
---
 .../cluster/utils/KubernetesClusterUtil.java       | 13 ++++++-
 .../src/main/resources/conf/k8s-control-node.yml   | 19 ++++++++--
 scripts/util/create-kubernetes-binaries-iso.sh     | 15 ++++----
 ui/src/views/compute/KubernetesServiceTab.vue      | 44 ++++++++++++++++++----
 4 files changed, 72 insertions(+), 19 deletions(-)

diff --git 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java
 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java
index 00625f6e076..023fc9ec7fa 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java
+++ 
b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/utils/KubernetesClusterUtil.java
@@ -171,11 +171,20 @@ public class KubernetesClusterUtil {
         // Check if dashboard service is up running.
         while (System.currentTimeMillis() < timeoutTime) {
             if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug(String.format("Checking dashboard service for the 
Kubernetes cluster: %s to come up", kubernetesCluster));
+                LOGGER.debug(String.format("Checking dashboard service 
(Kubernetes Dashboard or Headlamp) for the Kubernetes cluster: %s to come up", 
kubernetesCluster));
             }
+            // Check for Headlamp (new dashboard) in kube-system namespace
+            if (isKubernetesClusterAddOnServiceRunning(kubernetesCluster, 
ipAddress, port, user, sshKeyFile, "kube-system", "headlamp")) {
+                if (LOGGER.isInfoEnabled()) {
+                    LOGGER.info(String.format("Headlamp dashboard service for 
the Kubernetes cluster %s is in running state", kubernetesCluster));
+                }
+                running = true;
+                break;
+            }
+            // For backward compatibility, check for Kubernetes Dashboard in 
kubernetes-dashboard namespace
             if (isKubernetesClusterAddOnServiceRunning(kubernetesCluster, 
ipAddress, port, user, sshKeyFile, "kubernetes-dashboard", 
"kubernetes-dashboard")) {
                 if (LOGGER.isInfoEnabled()) {
-                    LOGGER.info(String.format("Dashboard service for the 
Kubernetes cluster %s is in running state", kubernetesCluster));
+                    LOGGER.info(String.format("Kubernetes Dashboard service 
for the Kubernetes cluster %s is in running state", kubernetesCluster));
                 }
                 running = true;
                 break;
diff --git 
a/plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-control-node.yml
 
b/plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-control-node.yml
index 70291dd1c35..4db349ac778 100644
--- 
a/plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-control-node.yml
+++ 
b/plugins/integrations/kubernetes-service/src/main/resources/conf/k8s-control-node.yml
@@ -331,16 +331,29 @@ write_files:
         if [[ ${EXTERNAL_CNI_PLUGIN} == false ]]; then
           /opt/bin/kubectl apply -f ${K8S_CONFIG_SCRIPTS_COPY_DIR}/network.yaml
         fi
-        /opt/bin/kubectl apply -f ${K8S_CONFIG_SCRIPTS_COPY_DIR}/dashboard.yaml
+        if [ -f "${K8S_CONFIG_SCRIPTS_COPY_DIR}/headlamp.yaml" ]; then
+          echo "Installing Headlamp dashboard from ISO"
+          /opt/bin/kubectl apply -f 
${K8S_CONFIG_SCRIPTS_COPY_DIR}/headlamp.yaml
+        elif [ -f "${K8S_CONFIG_SCRIPTS_COPY_DIR}/dashboard.yaml" ]; then
+          echo "Installing Kubernetes Dashboard from ISO"
+          /opt/bin/kubectl apply -f 
${K8S_CONFIG_SCRIPTS_COPY_DIR}/dashboard.yaml
+          /opt/bin/kubectl create rolebinding admin-binding --role=admin 
--user=admin || true
+          /opt/bin/kubectl create clusterrolebinding cluster-admin-binding 
--clusterrole=cluster-admin --user=admin || true
+          /opt/bin/kubectl create clusterrolebinding kubernetes-dashboard-ui 
--clusterrole=cluster-admin 
--serviceaccount=kubernetes-dashboard:kubernetes-dashboard || true
+        else
+          echo "Warning: No dashboard YAML found in ISO (neither headlamp.yaml 
nor dashboard.yaml)"
+        fi
         rm -rf "${K8S_CONFIG_SCRIPTS_COPY_DIR}"
       else
+        ### Online installation - use Headlamp by default ###
         /opt/bin/kubectl apply -f 
"https://cloud.weave.works/k8s/net?k8s-version=$(/opt/bin/kubectl version | 
base64 | tr -d '\n')"
-        /opt/bin/kubectl apply -f 
https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta6/aio/deploy/recommended.yaml
+        /opt/bin/kubectl apply -f 
https://raw.githubusercontent.com/kubernetes-sigs/headlamp/v0.40.1/kubernetes-headlamp.yaml
+        /opt/bin/kubectl create serviceaccount headlamp-admin -n kube-system 
|| true
+        /opt/bin/kubectl create clusterrolebinding headlamp-admin 
--clusterrole=cluster-admin --serviceaccount=kube-system:headlamp-admin || true
       fi
 
       /opt/bin/kubectl create rolebinding admin-binding --role=admin 
--user=admin || true
       /opt/bin/kubectl create clusterrolebinding cluster-admin-binding 
--clusterrole=cluster-admin --user=admin || true
-      /opt/bin/kubectl create clusterrolebinding kubernetes-dashboard-ui 
--clusterrole=cluster-admin 
--serviceaccount=kubernetes-dashboard:kubernetes-dashboard || true
 
       sudo touch /home/cloud/success
       echo "true" > /home/cloud/success
diff --git a/scripts/util/create-kubernetes-binaries-iso.sh 
b/scripts/util/create-kubernetes-binaries-iso.sh
index ebaf072771c..00234f20511 100755
--- a/scripts/util/create-kubernetes-binaries-iso.sh
+++ b/scripts/util/create-kubernetes-binaries-iso.sh
@@ -19,8 +19,8 @@
 set -e
 
 if [ $# -lt 6 ]; then
-    echo "Invalid input. Valid usage: ./create-kubernetes-binaries-iso.sh 
OUTPUT_PATH KUBERNETES_VERSION CNI_VERSION CRICTL_VERSION 
WEAVENET_NETWORK_YAML_CONFIG DASHBOARD_YAML_CONFIG BUILD_NAME [ARCH] 
[ETCD_VERSION]"
-    echo "eg: ./create-kubernetes-binaries-iso.sh ./ 1.11.4 0.7.1 1.11.1 
https://github.com/weaveworks/weave/releases/download/latest_release/weave-daemonset-k8s-1.11.yaml
 
https://raw.githubusercontent.com/kubernetes/dashboard/v1.10.0/src/deploy/recommended/kubernetes-dashboard.yaml
 setup-v1.11.4 amd64"
+    echo "Invalid input. Valid usage: ./create-kubernetes-binaries-iso.sh 
OUTPUT_PATH KUBERNETES_VERSION CNI_VERSION CRICTL_VERSION 
WEAVENET_NETWORK_YAML_CONFIG HEADLAMP_DASHBOARD_VERSION BUILD_NAME [ARCH] 
[ETCD_VERSION]"
+    echo "eg: ./create-kubernetes-binaries-iso.sh ./ 1.11.4 0.7.1 1.11.1 
https://github.com/weaveworks/weave/releases/download/latest_release/weave-daemonset-k8s-1.11.yaml
 0.40.1 setup-v1.11.4 amd64"
     exit 1
 fi
 
@@ -96,10 +96,11 @@ echo "Downloading network config ${NETWORK_CONFIG_URL}"
 network_conf_file="${working_dir}/network.yaml"
 curl -sSL ${NETWORK_CONFIG_URL} -o ${network_conf_file}
 
-DASHBORAD_CONFIG_URL="${6}"
-echo "Downloading dashboard config ${DASHBORAD_CONFIG_URL}"
-dashboard_conf_file="${working_dir}/dashboard.yaml"
-curl -sSL ${DASHBORAD_CONFIG_URL} -o ${dashboard_conf_file}
+HEADLAMP_DASHBOARD_VERSION="${6}"
+HEADLAMP_DASHBOARD_URL="https://raw.githubusercontent.com/kubernetes-sigs/headlamp/v${HEADLAMP_DASHBOARD_VERSION}/kubernetes-headlamp.yaml";
+echo "Downloading Headlamp manifest from ${HEADLAMP_DASHBOARD_URL}"
+headlamp_conf_file="${working_dir}/headlamp.yaml"
+curl -sSL ${HEADLAMP_DASHBOARD_URL} -o ${headlamp_conf_file}
 
 # TODO : Change the url once merged
 
AUTOSCALER_URL="https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/cloudstack/examples/cluster-autoscaler-standard.yaml";
@@ -135,7 +136,7 @@ mkdir -p "${working_dir}/docker"
 output=`${k8s_dir}/kubeadm config images list --kubernetes-version=${RELEASE}`
 
 # Don't forget about the yaml images !
-for i in ${network_conf_file} ${dashboard_conf_file}
+for i in ${network_conf_file} ${headlamp_conf_file}
 do
   images=`grep "image:" $i | cut -d ':' -f2- | tr -d ' ' | tr -d "'"`
   output=`printf "%s\n" ${output} ${images}`
diff --git a/ui/src/views/compute/KubernetesServiceTab.vue 
b/ui/src/views/compute/KubernetesServiceTab.vue
index fc8f9c60213..3c45feea063 100644
--- a/ui/src/views/compute/KubernetesServiceTab.vue
+++ b/ui/src/views/compute/KubernetesServiceTab.vue
@@ -66,32 +66,62 @@
           </a-timeline>
         </a-card>
         <a-card :title="$t('label.kubernetes.dashboard')">
+          <p><strong>Note:</strong> CloudStack Kubernetes clusters use 
<strong>Headlamp</strong> dashboard (deployed in <code>kube-system</code> 
namespace). For backward compatibility with older clusters using Kubernetes 
Dashboard, please check your cluster configuration.</p>
           <a-timeline>
             <a-timeline-item>
               <p>
-                {{ $t('label.run.proxy.locally') }}<br><br>
-                <code><b>kubectl --kubeconfig /custom/path/kube.conf 
proxy</b></code>
+                <strong>Access Headlamp Dashboard (new 
clusters)</strong><br><br>
+                <strong>Step 1:</strong> Run port-forward command:<br>
+                <code><b>kubectl --kubeconfig /custom/path/kube.conf 
port-forward -n kube-system service/headlamp 8080:80</b></code><br><br>
+                <strong>Step 2:</strong> Open in your browser:<br>
+                <a 
href="http://localhost:8080";><code>http://localhost:8080</code></a>
               </p>
             </a-timeline-item>
             <a-timeline-item>
               <p>
-                {{ $t('label.open.url') }}<br><br>
+                <strong>Access Kubernetes Dashboard (legacy 
clusters)</strong><br><br>
+                <strong>Step 1:</strong> {{ $t('label.run.proxy.locally') 
}}<br>
+                <code><b>kubectl --kubeconfig /custom/path/kube.conf 
proxy</b></code><br><br>
+                <strong>Step 2:</strong> {{ $t('label.open.url') }}<br>
                 <a 
href="http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/";><code>http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/</code></a>
               </p>
             </a-timeline-item>
             <a-timeline-item>
+              <p>
+                <strong>Create Access Token for Headlamp (new 
clusters)</strong>
+              </p>
               <p v-html="$t('label.kubernetes.dashboard.create.token')"></p>
               <p 
v-html="$t('label.kubernetes.dashboard.create.token.desc')"></p>
-              <a-textarea :value="'kubectl --kubeconfig /custom/path/kube.conf 
apply -f - <<EOF\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: 
kubernetes-dashboard-admin-user\n  namespace: 
kubernetes-dashboard\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: 
ClusterRoleBinding\nmetadata:\n  name: 
kubernetes-dashboard-admin-user\nroleRef:\n  apiGroup: 
rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: 
cluster-admin\nsubjects:\n- kind: ServiceAccount\n  name: kubernete [...]
+              <a-textarea :value="'kubectl --kubeconfig /custom/path/kube.conf 
apply -f - <<EOF\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: 
headlamp-admin\n  namespace: kube-system\n---\napiVersion: 
rbac.authorization.k8s.io/v1\nkind: ClusterRoleBinding\nmetadata:\n  name: 
headlamp-admin\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: 
ClusterRole\n  name: cluster-admin\nsubjects:\n- kind: ServiceAccount\n  name: 
headlamp-admin\n  namespace: kube-system\n---\napiVe [...]
+              <br><br>
+              <p>{{ $t('label.token.for.dashboard.login') }}:</p>
+              <code><b>kubectl --kubeconfig /custom/path/kube.conf describe 
secret headlamp-admin-token -n kube-system</b></code>
+            </a-timeline-item>
+            <a-timeline-item>
+              <p>
+                <strong>Create Access Token for Kubernetes Dashboard (legacy 
clusters)</strong>
+              </p>
+              <p 
v-html="$t('label.kubernetes.dashboard.create.token.desc')"></p>
+              <a-textarea :value="'kubectl --kubeconfig /custom/path/kube.conf 
apply -f - <<EOF\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: 
kubernetes-dashboard-admin-user\n  namespace: 
kubernetes-dashboard\n---\napiVersion: rbac.authorization.k8s.io/v1\nkind: 
ClusterRoleBinding\nmetadata:\n  name: 
kubernetes-dashboard-admin-user\nroleRef:\n  apiGroup: 
rbac.authorization.k8s.io\n  kind: ClusterRole\n  name: 
cluster-admin\nsubjects:\n- kind: ServiceAccount\n  name: kubernete [...]
+              <br><br>
+              <p>{{ $t('label.token.for.dashboard.login') }}:</p>
+              <code><b>kubectl --kubeconfig /custom/path/kube.conf describe 
secret kubernetes-dashboard-token -n kubernetes-dashboard</b></code>
             </a-timeline-item>
             <a-timeline-item>
               <p>
-                {{ $t('label.token.for.dashboard.login') }}<br><br>
-                <code><b>kubectl --kubeconfig /custom/path/kube.conf describe 
secret $(kubectl --kubeconfig /custom/path/kube.conf get secrets -n 
kubernetes-dashboard | grep kubernetes-dashboard-token | awk '{print $1}') -n 
kubernetes-dashboard</b></code>
+                <strong>Important Notes:</strong><br>
+                • <strong>Port-forwarding is recommended for Headlamp</strong> 
- simpler and more reliable than kubectl proxy<br>
+                • Token is only needed if accessing Headlamp via NodePort or 
LoadBalancer with external access<br>
+                • For Kubernetes 1.24+, service account tokens are no longer 
auto-generated - use the Secret resource shown above or <code>kubectl create 
token</code> command<br>
+                • <strong>Cluster-admin role grants full control</strong> - 
use with caution and only for trusted administrators<br>
+                • Keep the port-forward command running while using the 
dashboard (press Ctrl+C to stop)
               </p>
             </a-timeline-item>
           </a-timeline>
-          <p>{{ $t('label.more.access.dashboard.ui') }}, <a 
href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui";>https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui</a></p>
+          <p>{{ $t('label.more.access.dashboard.ui') }}:
+            <a href="https://headlamp.dev/docs/latest/";>Headlamp 
Documentation</a> |
+            <a 
href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui";>Kubernetes
 Dashboard (Legacy)</a>
+          </p>
         </a-card>
         <a-card :title="$t('label.access.kubernetes.nodes')">
           <p v-html="$t('label.kubernetes.access.details')"></p>

Reply via email to