From 236b755205a30aafcb8c0780d85c87b3591ab596 Mon Sep 17 00:00:00 2001 From: hlts2 Date: Mon, 22 Jun 2026 14:41:02 +0900 Subject: [PATCH 1/2] fix(kubernetes): block metrics-server install on Talos clusters The marketplace metrics-server is only enabled for k3s clusters, so installing it on a Talos cluster silently reports success without actually installing anything. Validate the resolved canonical app name after checkAppPlan (which resolves partial matches like "metrics" -> "metrics-server") and exit early when the app is unsupported on the target cluster type. Signed-off-by: hlts2 --- cmd/kubernetes/kubernetes_app_add.go | 2 +- utility/kubernetes.go | 40 +++++++++++++++++++---- utility/kubernetes_test.go | 48 ++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/cmd/kubernetes/kubernetes_app_add.go b/cmd/kubernetes/kubernetes_app_add.go index 48dba63d..14468c20 100644 --- a/cmd/kubernetes/kubernetes_app_add.go +++ b/cmd/kubernetes/kubernetes_app_add.go @@ -43,7 +43,7 @@ var kubernetesAppAddCmd = &cobra.Command{ os.Exit(1) } - result := utility.RequestedSplit(appList, args[0]) + result := utility.RequestedSplit(appList, args[0], kubernetesFindCluster.ClusterType) configKubernetes := &civogo.KubernetesClusterConfig{ Applications: result, } diff --git a/utility/kubernetes.go b/utility/kubernetes.go index 1ceb94db..2378ec2c 100644 --- a/utility/kubernetes.go +++ b/utility/kubernetes.go @@ -128,7 +128,7 @@ func writeConfig(path string, data []byte, suppressMessage bool, mergeConfigs bo // checkAppPlan is the function to verify if the application to be installed in the cluster // has a plan or not, in case it has a plan but does not specify it, // we use the first one in the list -func checkAppPlan(appList []civogo.KubernetesMarketplaceApplication, requested string) (string, error) { +func checkAppPlan(appList []civogo.KubernetesMarketplaceApplication, requested string) (int, string, error) { foundIndex := -1 parts := strings.SplitN(requested, ":", 2) appName := parts[0] @@ -161,10 +161,10 @@ func checkAppPlan(appList []civogo.KubernetesMarketplaceApplication, requested s _, found := find(allPlan, planName) if !found { YellowConfirm("the plan you gave doesn't exist for %s; we've picked a default one for you\n", appName) - return fmt.Sprintf("%s:%s", appName, appList[foundIndex].Plans[0].Label), nil + return foundIndex, fmt.Sprintf("%s:%s", appName, appList[foundIndex].Plans[0].Label), nil } - return requested, nil + return foundIndex, requested, nil } if planName != "" { @@ -172,18 +172,24 @@ func checkAppPlan(appList []civogo.KubernetesMarketplaceApplication, requested s os.Exit(1) } - return requested, nil + return foundIndex, requested, nil } // RequestedSplit is a function to split all app requested to be installed -func RequestedSplit(appList []civogo.KubernetesMarketplaceApplication, requested string) string { +func RequestedSplit(appList []civogo.KubernetesMarketplaceApplication, requested, clusterType string) string { allsplit := strings.Split(requested, ",") allApp := []string{} for i := range allsplit { - checkApp, err := checkAppPlan(appList, allsplit[i]) + foundIdx, checkApp, err := checkAppPlan(appList, allsplit[i]) if err != nil { fmt.Print(err) + continue + } + + if !isAppCompatibleWithClusterType(appList[foundIdx].Name, clusterType) { + Error("%s cannot be installed via the marketplace on %s clusters", appList[foundIdx].Name, clusterType) + os.Exit(1) } allApp = append(allApp, checkApp) @@ -192,6 +198,28 @@ func RequestedSplit(appList []civogo.KubernetesMarketplaceApplication, requested return strings.Join(allApp, ",") } +// unsupportedAppsByClusterType lists marketplace apps that cannot be +// installed on a given cluster type. +// +// TODO: this table is hardcoded because civogo.KubernetesMarketplaceApplication +// does not expose the app's supported cluster_type. The marketplace manifest +// already declares it (e.g. +// https://github.com/civo/kubernetes-marketplace/blob/master/metrics-server/manifest.yaml), +// so once that field is surfaced through the API/civogo, this comparison should +// be replaced by checking the application's own cluster_type and this table removed. +var unsupportedAppsByClusterType = map[string][]string{ + "talos": {"metrics-server"}, +} + +func isAppCompatibleWithClusterType(requestedApp, clusterType string) bool { + for _, app := range unsupportedAppsByClusterType[clusterType] { + if app == requestedApp { + return false + } + } + return true +} + // RemoveApplicationFromInstalledList is a function to split all currently installed apps and remove one being uninstalled func RemoveApplicationFromInstalledList(current []civogo.KubernetesInstalledApplication, uninstall string) string { appsToRemove := strings.Split(uninstall, ",") diff --git a/utility/kubernetes_test.go b/utility/kubernetes_test.go index 7e261b49..a7939ef6 100644 --- a/utility/kubernetes_test.go +++ b/utility/kubernetes_test.go @@ -71,3 +71,51 @@ func TestRemoveApplicationFromInstalledListWithMultiple(t *testing.T) { // t.Errorf("expected 'mysql', got '%s'", ret) // } // } + +func TestIsAppCompatibleWithClusterType(t *testing.T) { + tests := []struct { + name string + requestedApp string + clusterType string + want bool + }{ + { + name: "metrics-server is not compatible with talos", + requestedApp: "metrics-server", + clusterType: "talos", + want: false, + }, + { + name: "metrics-server is compatible with k3s", + requestedApp: "metrics-server", + clusterType: "k3s", + want: true, + }, + { + name: "other app is compatible with talos", + requestedApp: "traefik2-nodeport", + clusterType: "talos", + want: true, + }, + { + name: "other app is compatible with k3s", + requestedApp: "traefik2-nodeport", + clusterType: "k3s", + want: true, + }, + { + name: "unknown cluster type has no restrictions", + requestedApp: "metrics-server", + clusterType: "", + want: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isAppCompatibleWithClusterType(tt.requestedApp, tt.clusterType); got != tt.want { + t.Errorf("isAppCompatibleWithClusterType(%q, %q) = %v, want %v", tt.requestedApp, tt.clusterType, got, tt.want) + } + }) + } +} From 41df7c229c47e5f26ad830bc0babafbd89beb2c6 Mon Sep 17 00:00:00 2001 From: hlts2 Date: Mon, 22 Jun 2026 14:56:26 +0900 Subject: [PATCH 2/2] chore: resolve golvuncheck vulnerabilities Signed-off-by: hlts2 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 95a8b6a4..af6de171 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -go 1.26.3 +go 1.26.4 module github.com/civo/cli