From d843c111f5d1b76d8b1e5d9d765409983307bbbf Mon Sep 17 00:00:00 2001 From: mperea Date: Sun, 12 Oct 2025 17:56:18 +0200 Subject: [PATCH] cluster-autoscaler/cloudstack: Add project-id support for multi-project environments This commit adds support for the optional project-id parameter in CloudStack cloud provider configuration. This is required for clusters running in CloudStack projects, as the listKubernetesClusters API returns empty results when querying by cluster ID without the projectid parameter in multi-project environments. Changes: - Add ProjectID field to service.Config struct - Pass ProjectID from cloud-config to service configuration - Include projectid parameter in all CKS API calls when configured: * listKubernetesClusters (GetClusterDetails) * scaleKubernetesCluster (ScaleCluster and RemoveNodesFromCluster) - Update README.md to document the optional project-id parameter The project-id parameter is optional and backwards compatible. If not specified, the API uses the default project associated with the credentials. --- .../cloudprovider/cloudstack/README.md | 4 +++ .../cloudstack/cloudstack_option.go | 1 + .../cloudprovider/cloudstack/service/cks.go | 33 ++++++++++++++----- .../cloudstack/service/client.go | 1 + 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/cluster-autoscaler/cloudprovider/cloudstack/README.md b/cluster-autoscaler/cloudprovider/cloudstack/README.md index 1b05a2293424..c2063377455e 100644 --- a/cluster-autoscaler/cloudprovider/cloudstack/README.md +++ b/cluster-autoscaler/cloudprovider/cloudstack/README.md @@ -28,9 +28,13 @@ that is suitable for your environment. api-url = api-key = secret-key = +project-id = # Optional: required for multi-project environments ``` The access token needs to be able to execute the `listKubernetesClusters` and `scaleKubernetesCluster` APIs. +**Note:** The `project-id` parameter is optional but required when working with clusters in CloudStack projects. +If not specified, the API will use the default project associated with the API credentials. + To create the secret, use the following command: ```bash kubectl -n kube-system create secret generic cloudstack-secret --from-file=cloud-config diff --git a/cluster-autoscaler/cloudprovider/cloudstack/cloudstack_option.go b/cluster-autoscaler/cloudprovider/cloudstack/cloudstack_option.go index c5790a297071..84793b2c0b31 100644 --- a/cluster-autoscaler/cloudprovider/cloudstack/cloudstack_option.go +++ b/cluster-autoscaler/cloudprovider/cloudstack/cloudstack_option.go @@ -97,6 +97,7 @@ func createConfig(opts ...option) (*managerConfig, error) { APIKey: config.Global.APIKey, SecretKey: config.Global.SecretKey, Endpoint: config.Global.APIURL, + ProjectID: config.Global.ProjectID, } } diff --git a/cluster-autoscaler/cloudprovider/cloudstack/service/cks.go b/cluster-autoscaler/cloudprovider/cloudstack/service/cks.go index 5c9920ad786f..130535de8b60 100644 --- a/cluster-autoscaler/cloudprovider/cloudstack/service/cks.go +++ b/cluster-autoscaler/cloudprovider/cloudstack/service/cks.go @@ -74,7 +74,8 @@ type VirtualMachine struct { // cksService implements the CKSService interface type cksService struct { - client APIClient + client APIClient + projectID string } func virtaulMachinesToMap(vms []*VirtualMachine) map[string]*VirtualMachine { @@ -89,9 +90,14 @@ func virtaulMachinesToMap(vms []*VirtualMachine) map[string]*VirtualMachine { func (service *cksService) GetClusterDetails(clusterID string) (*Cluster, error) { var out ListClusterResponse - _, err := service.client.NewRequest("listKubernetesClusters", map[string]string{ + params := map[string]string{ "id": clusterID, - }, &out) + } + if service.projectID != "" { + params["projectid"] = service.projectID + } + + _, err := service.client.NewRequest("listKubernetesClusters", params, &out) if err != nil { return nil, fmt.Errorf("Unable to fetch cluster details : %v", err) @@ -108,10 +114,15 @@ func (service *cksService) GetClusterDetails(clusterID string) (*Cluster, error) func (service *cksService) ScaleCluster(clusterID string, workerCount int) (*Cluster, error) { var out ClusterResponse - _, err := service.client.NewRequest("scaleKubernetesCluster", map[string]string{ + params := map[string]string{ "id": clusterID, "size": strconv.Itoa(workerCount), - }, &out) + } + if service.projectID != "" { + params["projectid"] = service.projectID + } + + _, err := service.client.NewRequest("scaleKubernetesCluster", params, &out) if err != nil { return nil, fmt.Errorf("Unable to scale cluster : %v", err) @@ -123,10 +134,15 @@ func (service *cksService) ScaleCluster(clusterID string, workerCount int) (*Clu func (service *cksService) RemoveNodesFromCluster(clusterID string, nodeIDs ...string) (*Cluster, error) { var out ClusterResponse - _, err := service.client.NewRequest("scaleKubernetesCluster", map[string]string{ + params := map[string]string{ "id": clusterID, "nodeids": strings.Join(nodeIDs[:], ","), - }, &out) + } + if service.projectID != "" { + params["projectid"] = service.projectID + } + + _, err := service.client.NewRequest("scaleKubernetesCluster", params, &out) if err != nil { return nil, fmt.Errorf("Unable to delete %v from cluster : %v", nodeIDs, err) } @@ -143,6 +159,7 @@ func (service *cksService) Close() { func NewCKSService(config *Config) CKSService { client := NewAPIClient(config) return &cksService{ - client: client, + client: client, + projectID: config.ProjectID, } } diff --git a/cluster-autoscaler/cloudprovider/cloudstack/service/client.go b/cluster-autoscaler/cloudprovider/cloudstack/service/client.go index a794047674ca..e5e374165258 100644 --- a/cluster-autoscaler/cloudprovider/cloudstack/service/client.go +++ b/cluster-autoscaler/cloudprovider/cloudstack/service/client.go @@ -49,6 +49,7 @@ type Config struct { APIKey string SecretKey string Endpoint string + ProjectID string Timeout int PollInterval int }