From 5cc85a264d807d2ac419021222df26fb18eb2b30 Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Wed, 10 Dec 2025 14:58:03 +0100 Subject: [PATCH 01/17] update mcm setup with the existing stackit setup # Conflicts: # go.mod # Conflicts: # go.mod # go.sum # Conflicts: # pkg/provider/apis/validation/validation.go # pkg/provider/core.go --- pkg/provider/apis/provider_spec.go | 3 +++ pkg/provider/core.go | 22 ++++++++++++++++++++++ pkg/provider/stackit_client.go | 3 ++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/pkg/provider/apis/provider_spec.go b/pkg/provider/apis/provider_spec.go index f8b91ba8..c2994e8b 100644 --- a/pkg/provider/apis/provider_spec.go +++ b/pkg/provider/apis/provider_spec.go @@ -32,6 +32,9 @@ type ProviderSpec struct { // Optional field. If not specified, the project's default security group will be used. SecurityGroups []string `json:"securityGroups,omitempty"` + // Region is the region where the server will be created. + Region string `json:"region,omitempty"` + // UserData is cloud-init script or user data for VM bootstrapping // Optional field. Can be used to override Secret.userData for this MachineClass. // If specified, takes precedence over Secret.userData. diff --git a/pkg/provider/core.go b/pkg/provider/core.go index d1b70bf6..cd77ffa4 100644 --- a/pkg/provider/core.go +++ b/pkg/provider/core.go @@ -53,6 +53,7 @@ func (p *Provider) CreateMachine(ctx context.Context, req *driver.CreateMachineR // Extract credentials from Secret projectID := string(req.Secret.Data["project-id"]) serviceAccountKey := string(req.Secret.Data["serviceaccount.json"]) + region := providerSpec.Region // Initialize client on first use (lazy initialization) if err := p.ensureClient(serviceAccountKey); err != nil { @@ -208,8 +209,15 @@ func (p *Provider) DeleteMachine(ctx context.Context, req *driver.DeleteMachineR return nil, status.Error(codes.InvalidArgument, "ProviderID is required") } + // Decode ProviderSpec from MachineClass + providerSpec, err := decodeProviderSpec(req.MachineClass) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + // Extract credentials from Secret serviceAccountKey := string(req.Secret.Data["serviceaccount.json"]) + region := providerSpec.Region // Initialize client on first use (lazy initialization) if err := p.ensureClient(serviceAccountKey); err != nil { @@ -274,8 +282,15 @@ func (p *Provider) GetMachineStatus(ctx context.Context, req *driver.GetMachineS return nil, status.Error(codes.NotFound, "machine does not have a ProviderID yet") } + // Decode ProviderSpec from MachineClass + providerSpec, err := decodeProviderSpec(req.MachineClass) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + // Extract credentials from Secret serviceAccountKey := string(req.Secret.Data["serviceaccount.json"]) + region := providerSpec.Region // Initialize client on first use (lazy initialization) if err := p.ensureClient(serviceAccountKey); err != nil { @@ -335,9 +350,16 @@ func (p *Provider) ListMachines(ctx context.Context, req *driver.ListMachinesReq klog.V(2).Infof("List machines request has been received for %q", req.MachineClass.Name) defer klog.V(2).Infof("List machines request has been processed for %q", req.MachineClass.Name) + // Decode ProviderSpec from MachineClass + providerSpec, err := decodeProviderSpec(req.MachineClass) + if err != nil { + return nil, status.Error(codes.Internal, err.Error()) + } + // Extract credentials from Secret projectID := string(req.Secret.Data["project-id"]) serviceAccountKey := string(req.Secret.Data["serviceaccount.json"]) + region := providerSpec.Region // Initialize client on first use (lazy initialization) if err := p.ensureClient(serviceAccountKey); err != nil { diff --git a/pkg/provider/stackit_client.go b/pkg/provider/stackit_client.go index dc339c74..861bad39 100644 --- a/pkg/provider/stackit_client.go +++ b/pkg/provider/stackit_client.go @@ -19,7 +19,8 @@ import ( // Note: region parameter is required by STACKIT SDK v1.0.0+ // It must be extracted from the Secret (e.g., "eu01-1", "eu01-2") type StackitClient interface { - // CreateServer creates a new server in STACKIT + // + //CreateServer creates a new server in STACKIT CreateServer(ctx context.Context, projectID, region string, req *CreateServerRequest) (*Server, error) // GetServer retrieves a server by ID from STACKIT GetServer(ctx context.Context, projectID, region, serverID string) (*Server, error) From 8e9dc83b7d2af3177017ba8ceac84f6a72bd4ebd Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Wed, 10 Dec 2025 15:13:43 +0100 Subject: [PATCH 02/17] validate provider spec region --- pkg/provider/apis/validation/validation.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/provider/apis/validation/validation.go b/pkg/provider/apis/validation/validation.go index c27bac58..d335301d 100644 --- a/pkg/provider/apis/validation/validation.go +++ b/pkg/provider/apis/validation/validation.go @@ -95,6 +95,12 @@ func ValidateProviderSpecNSecret(spec *api.ProviderSpec, secrets *corev1.Secret) errors = append(errors, fmt.Errorf("providerSpec.machineType has invalid format (expected format: c2i.2, m2i.8, etc.)")) } + if spec.Region == "" { + errors = append(errors, fmt.Errorf("providerSpec.region is required")) + } else if !isValidRegion(spec.Region) { + errors = append(errors, fmt.Errorf("providerSpec.region has invalid format (expected format: eu01-1, eu01-2, etc.)")) + } + // ImageID is required unless BootVolume.Source is specified hasBootVolumeSource := spec.BootVolume != nil && spec.BootVolume.Source != nil if spec.ImageID == "" && !hasBootVolumeSource { From 744a4689ee9c5d1308059e7e13a3f02641a1c2b9 Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Wed, 10 Dec 2025 15:18:12 +0100 Subject: [PATCH 03/17] minor nits --- pkg/provider/stackit_client.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/provider/stackit_client.go b/pkg/provider/stackit_client.go index 861bad39..dc339c74 100644 --- a/pkg/provider/stackit_client.go +++ b/pkg/provider/stackit_client.go @@ -19,8 +19,7 @@ import ( // Note: region parameter is required by STACKIT SDK v1.0.0+ // It must be extracted from the Secret (e.g., "eu01-1", "eu01-2") type StackitClient interface { - // - //CreateServer creates a new server in STACKIT + // CreateServer creates a new server in STACKIT CreateServer(ctx context.Context, projectID, region string, req *CreateServerRequest) (*Server, error) // GetServer retrieves a server by ID from STACKIT GetServer(ctx context.Context, projectID, region, serverID string) (*Server, error) From 01b6ebb0505ce5ca4053aadaf337db6274d0586b Mon Sep 17 00:00:00 2001 From: Felix Breuer Date: Thu, 11 Dec 2025 13:30:57 +0100 Subject: [PATCH 04/17] push to reg3 --- Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Makefile b/Makefile index 16560220..c4581e90 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,7 @@ include ./hack/tools.mk .PHONY: all all: verify +# TODO: remove the second target once published .PHONY: image image: $(KO) ## Builds a single binary specified by TARGET KO_DOCKER_REPO=$(REGISTRY)/$(REPO) \ @@ -29,6 +30,14 @@ image: $(KO) ## Builds a single binary specified by TARGET --platform linux/amd64,linux/arm64 \ ./cmd/machine-controller + KO_DOCKER_REPO=reg3.infra.ske.eu01.stackit.cloud/stackitcloud/machine-controller-manager-provider-stackit \ + $(KO) build --push=$(PUSH) \ + --image-label org.opencontainers.image.source="https://github.com/stackitcloud/machine-controller-manager-provider-stackit" \ + --sbom none -t $(VERSION) \ + --bare \ + --platform linux/amd64,linux/arm64 \ + ./cmd/machine-controller + .PHONY: clean-tools-bin clean-tools-bin: ## Empty the tools binary directory. rm -rf $(TOOLS_BIN_DIR)/* $(TOOLS_BIN_DIR)/.version_* From c615ff6eb7500a142a21b785c399e397cd36f4e6 Mon Sep 17 00:00:00 2001 From: Felix Breuer Date: Thu, 11 Dec 2025 13:58:22 +0100 Subject: [PATCH 05/17] remove double push --- Makefile | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Makefile b/Makefile index c4581e90..73ead887 100644 --- a/Makefile +++ b/Makefile @@ -30,14 +30,6 @@ image: $(KO) ## Builds a single binary specified by TARGET --platform linux/amd64,linux/arm64 \ ./cmd/machine-controller - KO_DOCKER_REPO=reg3.infra.ske.eu01.stackit.cloud/stackitcloud/machine-controller-manager-provider-stackit \ - $(KO) build --push=$(PUSH) \ - --image-label org.opencontainers.image.source="https://github.com/stackitcloud/machine-controller-manager-provider-stackit" \ - --sbom none -t $(VERSION) \ - --bare \ - --platform linux/amd64,linux/arm64 \ - ./cmd/machine-controller - .PHONY: clean-tools-bin clean-tools-bin: ## Empty the tools binary directory. rm -rf $(TOOLS_BIN_DIR)/* $(TOOLS_BIN_DIR)/.version_* From 91d9e9526de22b724e422d4d6ee6f016d880b08e Mon Sep 17 00:00:00 2001 From: Felix Breuer Date: Thu, 11 Dec 2025 14:05:02 +0100 Subject: [PATCH 06/17] remove comment --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 73ead887..16560220 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,6 @@ include ./hack/tools.mk .PHONY: all all: verify -# TODO: remove the second target once published .PHONY: image image: $(KO) ## Builds a single binary specified by TARGET KO_DOCKER_REPO=$(REGISTRY)/$(REPO) \ From e302a9510bca88b5728a7ed44398596439c55df1 Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Thu, 11 Dec 2025 14:50:18 +0100 Subject: [PATCH 07/17] update go.mod --- go.mod | 2 ++ 1 file changed, 2 insertions(+) diff --git a/go.mod b/go.mod index baf330bd..bbe0ba5f 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,8 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect + github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect From f416f9a50152bc202608a216e8f078f76d87c3b0 Mon Sep 17 00:00:00 2001 From: Felix Breuer Date: Fri, 12 Dec 2025 12:22:33 +0100 Subject: [PATCH 08/17] change provider to qa --- pkg/provider/sdk_client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/provider/sdk_client.go b/pkg/provider/sdk_client.go index 167cb05c..e2706042 100644 --- a/pkg/provider/sdk_client.go +++ b/pkg/provider/sdk_client.go @@ -50,7 +50,9 @@ var ( // - More secure than static tokens (short-lived, rotating) func createIAASClient(serviceAccountKey string) (*iaas.APIClient, error) { // Configure SDK with custom base URL if provided (for testing with mock server) - baseURL := os.Getenv("STACKIT_API_ENDPOINT") + // baseURL := os.Getenv("STACKIT_API_ENDPOINT") + // TODO: this should be configureable via ske-tages or ske-base + baseURL := "https://iaas.api.eu01.qa.stackit.cloud" noAuth := os.Getenv("STACKIT_NO_AUTH") == "true" var opts []config.ConfigurationOption From 13d65db65f1998a604a168f1624ae20373da0866 Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Fri, 12 Dec 2025 15:06:09 +0100 Subject: [PATCH 09/17] update dependency --- go.mod | 1 + go.sum | 14 ++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index bbe0ba5f..432f549b 100644 --- a/go.mod +++ b/go.mod @@ -67,6 +67,7 @@ require ( google.golang.org/protobuf v1.36.7 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.31.0 // indirect k8s.io/client-go v0.34.3 // indirect diff --git a/go.sum b/go.sum index b3d7dd31..367e0ec7 100644 --- a/go.sum +++ b/go.sum @@ -140,16 +140,12 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= -go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= -go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= -go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -206,6 +202,8 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -219,8 +217,8 @@ k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= k8s.io/cluster-bootstrap v0.31.0 h1:jj5t1PArBPddvDypdNpzqnZQ/+qnGxpJuTF7SX05h1Y= k8s.io/cluster-bootstrap v0.31.0/go.mod h1:6ujqWFrBV4amKe1ii/6BXgrd57bF/Q3gXebLJdmfSK4= -k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= -k8s.io/component-base v0.34.3/go.mod h1:5iIlD8wPfWE/xSHTRfbjuvUul2WZbI2nOUK65XL0E/c= +k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= +k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= From e3abe0d2629eb5dd269a4455b8b6ab0a08326162 Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Fri, 12 Dec 2025 15:17:35 +0100 Subject: [PATCH 10/17] add token endpoint --- pkg/provider/sdk_client.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/provider/sdk_client.go b/pkg/provider/sdk_client.go index e2706042..e98de200 100644 --- a/pkg/provider/sdk_client.go +++ b/pkg/provider/sdk_client.go @@ -52,7 +52,9 @@ func createIAASClient(serviceAccountKey string) (*iaas.APIClient, error) { // Configure SDK with custom base URL if provided (for testing with mock server) // baseURL := os.Getenv("STACKIT_API_ENDPOINT") // TODO: this should be configureable via ske-tages or ske-base + // TODO: the STACKIT_TOKEN_BASEURL env baseURL := "https://iaas.api.eu01.qa.stackit.cloud" + tokenEndpoint := "https://service-account.api.qa.stackit.cloud/token" noAuth := os.Getenv("STACKIT_NO_AUTH") == "true" var opts []config.ConfigurationOption @@ -74,6 +76,10 @@ func createIAASClient(serviceAccountKey string) (*iaas.APIClient, error) { opts = append(opts, config.WithEndpoint(baseURL)) } + if tokenEndpoint != "" { + opts = append(opts, config.WithTokenEndpoint(tokenEndpoint)) + } + iaasClient, err := iaas.NewAPIClient(opts...) if err != nil { return nil, fmt.Errorf("failed to create STACKIT SDK API client: %w", err) From 2818c21da7312bc623af3c31e46fb822d102ccc9 Mon Sep 17 00:00:00 2001 From: Felix Breuer Date: Fri, 12 Dec 2025 16:22:26 +0100 Subject: [PATCH 11/17] also reconcile openstack provider ID --- pkg/provider/helpers.go | 10 +++++++--- pkg/provider/sdk_client.go | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/provider/helpers.go b/pkg/provider/helpers.go index da6eb7a2..f7d91fbf 100644 --- a/pkg/provider/helpers.go +++ b/pkg/provider/helpers.go @@ -37,14 +37,18 @@ func encodeProviderSpecForResponse(spec *api.ProviderSpec) ([]byte, error) { func parseProviderID(providerID string) (projectID, serverID string, err error) { const prefix = "stackit://" - if !strings.HasPrefix(providerID, prefix) { - return "", "", fmt.Errorf("ProviderID must start with 'stackit://'") - } + // if !strings.HasPrefix(providerID, prefix) { + // return "", "", fmt.Errorf("ProviderID must start with 'stackit://'") + // } // Remove prefix and split by '/' remainder := strings.TrimPrefix(providerID, prefix) parts := strings.Split(remainder, "/") + if len(parts) == 3 { + return "", parts[3], nil + } + if len(parts) != 2 { return "", "", fmt.Errorf("ProviderID must have format 'stackit:///'") } diff --git a/pkg/provider/sdk_client.go b/pkg/provider/sdk_client.go index e98de200..9bdca334 100644 --- a/pkg/provider/sdk_client.go +++ b/pkg/provider/sdk_client.go @@ -51,7 +51,7 @@ var ( func createIAASClient(serviceAccountKey string) (*iaas.APIClient, error) { // Configure SDK with custom base URL if provided (for testing with mock server) // baseURL := os.Getenv("STACKIT_API_ENDPOINT") - // TODO: this should be configureable via ske-tages or ske-base + // TODO: this should be configureable via ske-stages or ske-base // TODO: the STACKIT_TOKEN_BASEURL env baseURL := "https://iaas.api.eu01.qa.stackit.cloud" tokenEndpoint := "https://service-account.api.qa.stackit.cloud/token" From 5239672757dcb69c23fab2436e3f27049534e2a1 Mon Sep 17 00:00:00 2001 From: Felix Breuer Date: Mon, 15 Dec 2025 10:59:51 +0100 Subject: [PATCH 12/17] log sec groups --- pkg/provider/core.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provider/core.go b/pkg/provider/core.go index cd77ffa4..041d4db6 100644 --- a/pkg/provider/core.go +++ b/pkg/provider/core.go @@ -174,7 +174,7 @@ func (p *Provider) CreateMachine(ctx context.Context, req *driver.CreateMachineR server, err := p.client.CreateServer(ctx, projectID, providerSpec.Region, createReq) if err != nil { klog.Errorf("Failed to create server for machine %q: %v", req.Machine.Name, err) - return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create server: %v", err)) + return nil, status.Error(codes.Internal, fmt.Sprintf("failed to create server: secgroups: %v err: %v", createReq.SecurityGroups, err)) } // Generate ProviderID in format: stackit:/// From b9204a9c3595c49f5fc770619f77c47d91582c6f Mon Sep 17 00:00:00 2001 From: Felix Breuer Date: Mon, 15 Dec 2025 12:09:54 +0100 Subject: [PATCH 13/17] allow deletion if provider id is empty --- pkg/provider/core.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/provider/core.go b/pkg/provider/core.go index 041d4db6..31bb3062 100644 --- a/pkg/provider/core.go +++ b/pkg/provider/core.go @@ -206,7 +206,10 @@ func (p *Provider) DeleteMachine(ctx context.Context, req *driver.DeleteMachineR // Validate ProviderID exists if req.Machine.Spec.ProviderID == "" { - return nil, status.Error(codes.InvalidArgument, "ProviderID is required") + // the server is already deleted, return success so the CRD can be cleaned up + // TODO: verify this + return nil, nil + // return nil, status.Error(codes.InvalidArgument, "ProviderID is required") } // Decode ProviderSpec from MachineClass From 193c7d7f9b6a83fe97ab3b0caab3de5ed96d550a Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Thu, 18 Dec 2025 09:28:14 +0100 Subject: [PATCH 14/17] rebase go.mod --- go.mod | 3 --- go.sum | 14 ++++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 432f549b..baf330bd 100644 --- a/go.mod +++ b/go.mod @@ -31,8 +31,6 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect - github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/google/uuid v1.6.0 // indirect @@ -67,7 +65,6 @@ require ( google.golang.org/protobuf v1.36.7 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiserver v0.31.0 // indirect k8s.io/client-go v0.34.3 // indirect diff --git a/go.sum b/go.sum index 367e0ec7..b3d7dd31 100644 --- a/go.sum +++ b/go.sum @@ -140,12 +140,16 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -202,8 +206,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -217,8 +219,8 @@ k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= k8s.io/cluster-bootstrap v0.31.0 h1:jj5t1PArBPddvDypdNpzqnZQ/+qnGxpJuTF7SX05h1Y= k8s.io/cluster-bootstrap v0.31.0/go.mod h1:6ujqWFrBV4amKe1ii/6BXgrd57bF/Q3gXebLJdmfSK4= -k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= -k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/component-base v0.34.3 h1:zsEgw6ELqK0XncCQomgO9DpUIzlrYuZYA0Cgo+JWpVk= +k8s.io/component-base v0.34.3/go.mod h1:5iIlD8wPfWE/xSHTRfbjuvUul2WZbI2nOUK65XL0E/c= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= From 40dc96bef57f72e3508d52a84e2e5516255910ae Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Fri, 19 Dec 2025 11:19:31 +0100 Subject: [PATCH 15/17] fix rebase --- pkg/provider/apis/provider_spec.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/provider/apis/provider_spec.go b/pkg/provider/apis/provider_spec.go index c2994e8b..f8b91ba8 100644 --- a/pkg/provider/apis/provider_spec.go +++ b/pkg/provider/apis/provider_spec.go @@ -32,9 +32,6 @@ type ProviderSpec struct { // Optional field. If not specified, the project's default security group will be used. SecurityGroups []string `json:"securityGroups,omitempty"` - // Region is the region where the server will be created. - Region string `json:"region,omitempty"` - // UserData is cloud-init script or user data for VM bootstrapping // Optional field. Can be used to override Secret.userData for this MachineClass. // If specified, takes precedence over Secret.userData. From f9a6670e2303289ed252cb781c11d080d44b56f5 Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Fri, 19 Dec 2025 11:21:07 +0100 Subject: [PATCH 16/17] fix rebase --- pkg/provider/apis/validation/validation.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/provider/apis/validation/validation.go b/pkg/provider/apis/validation/validation.go index d335301d..c27bac58 100644 --- a/pkg/provider/apis/validation/validation.go +++ b/pkg/provider/apis/validation/validation.go @@ -95,12 +95,6 @@ func ValidateProviderSpecNSecret(spec *api.ProviderSpec, secrets *corev1.Secret) errors = append(errors, fmt.Errorf("providerSpec.machineType has invalid format (expected format: c2i.2, m2i.8, etc.)")) } - if spec.Region == "" { - errors = append(errors, fmt.Errorf("providerSpec.region is required")) - } else if !isValidRegion(spec.Region) { - errors = append(errors, fmt.Errorf("providerSpec.region has invalid format (expected format: eu01-1, eu01-2, etc.)")) - } - // ImageID is required unless BootVolume.Source is specified hasBootVolumeSource := spec.BootVolume != nil && spec.BootVolume.Source != nil if spec.ImageID == "" && !hasBootVolumeSource { From 3774f297a40c171c4adab8cedc002d8f3a6bf22b Mon Sep 17 00:00:00 2001 From: Aniruddha Basak Date: Fri, 19 Dec 2025 11:22:53 +0100 Subject: [PATCH 17/17] fix rebase --- pkg/provider/core.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/pkg/provider/core.go b/pkg/provider/core.go index 31bb3062..55218407 100644 --- a/pkg/provider/core.go +++ b/pkg/provider/core.go @@ -53,7 +53,6 @@ func (p *Provider) CreateMachine(ctx context.Context, req *driver.CreateMachineR // Extract credentials from Secret projectID := string(req.Secret.Data["project-id"]) serviceAccountKey := string(req.Secret.Data["serviceaccount.json"]) - region := providerSpec.Region // Initialize client on first use (lazy initialization) if err := p.ensureClient(serviceAccountKey); err != nil { @@ -220,7 +219,6 @@ func (p *Provider) DeleteMachine(ctx context.Context, req *driver.DeleteMachineR // Extract credentials from Secret serviceAccountKey := string(req.Secret.Data["serviceaccount.json"]) - region := providerSpec.Region // Initialize client on first use (lazy initialization) if err := p.ensureClient(serviceAccountKey); err != nil { @@ -236,11 +234,6 @@ func (p *Provider) DeleteMachine(ctx context.Context, req *driver.DeleteMachineR return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid ProviderID format: %v", err)) } - providerSpec, err := decodeProviderSpec(req.MachineClass) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - // Call STACKIT API to delete server err = p.client.DeleteServer(ctx, projectID, providerSpec.Region, serverID) if err != nil { @@ -293,7 +286,6 @@ func (p *Provider) GetMachineStatus(ctx context.Context, req *driver.GetMachineS // Extract credentials from Secret serviceAccountKey := string(req.Secret.Data["serviceaccount.json"]) - region := providerSpec.Region // Initialize client on first use (lazy initialization) if err := p.ensureClient(serviceAccountKey); err != nil { @@ -310,12 +302,6 @@ func (p *Provider) GetMachineStatus(ctx context.Context, req *driver.GetMachineS return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid ProviderID format: %v", err)) } - // Decode ProviderSpec from MachineClass - providerSpec, err := decodeProviderSpec(req.MachineClass) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - // Call STACKIT API to get server status server, err := p.client.GetServer(ctx, projectID, providerSpec.Region, serverID) if err != nil { @@ -362,19 +348,12 @@ func (p *Provider) ListMachines(ctx context.Context, req *driver.ListMachinesReq // Extract credentials from Secret projectID := string(req.Secret.Data["project-id"]) serviceAccountKey := string(req.Secret.Data["serviceaccount.json"]) - region := providerSpec.Region // Initialize client on first use (lazy initialization) if err := p.ensureClient(serviceAccountKey); err != nil { return nil, status.Error(codes.Internal, fmt.Sprintf("failed to initialize STACKIT client: %v", err)) } - // Decode ProviderSpec from MachineClass - providerSpec, err := decodeProviderSpec(req.MachineClass) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } - // Call STACKIT API to list all servers servers, err := p.client.ListServers(ctx, projectID, providerSpec.Region) if err != nil {