Skip to content

Commit 0a4c911

Browse files
craigvanamanCraig Vanamanclaude
authored
feat: stackit SDK migration
* chore: wip commit * chore: wip commit * feat: using stackit sdk * chore: adding roadmaps so they can be worked on by claud web * docs: update SDK migration roadmap with completion status Updated ROADMAP_sdk_migration.md to reflect actual migration completion: - Added migration status summary showing 92% completion - Marked Phases 1-5 as COMPLETE (100% each) - Updated Phase 6 showing 20% completion (only samples updated) - Added comprehensive migration summary section - Documented all code changes and statistics - Listed remaining documentation tasks (README, MIGRATION.md, CHANGELOG.md) Key achievements documented: - 313 lines of new SDK client code - 229 lines of legacy HTTP code deleted - All 145 unit tests passing - All 26 E2E tests passing - Zero code debt - complete migration with no hybrid approach Remaining work: Phase 6 documentation tasks (8% of total project) * docs: complete SDK migration documentation Added comprehensive STACKIT SDK documentation to README.md: - New "STACKIT SDK Integration" section with authentication details - Table of required Secret fields highlighting region requirement - Example Secret YAML with region field - Guide for obtaining STACKIT credentials (project ID, token, region) - SDK configuration details (Core v0.18.0, IaaS v1.0.0) - SDK reference links to documentation and examples - Updated project structure showing sdk_client.go and helpers.go - Reorganized References section with SDK and Platform categories Updated ROADMAP_sdk_migration.md: - Marked Phase 6 (Documentation & Release) as COMPLETE - Updated overall progress to 100% complete - Added detailed documentation completion checklist - Noted CHANGELOG/MIGRATION docs intentionally skipped (pre-production) - Updated success criteria and next steps - Changed status to "Ready for code review and merge" All 6 phases of SDK migration are now complete: ✅ Phase 1: Dependency & Authentication Setup ✅ Phase 2: Client Interface Refactoring ✅ Phase 3: Core Logic Migration ✅ Phase 4: Unit Test Updates ✅ Phase 5: E2E Test Validation ✅ Phase 6: Documentation & Release Migration is production-ready and awaiting code review. * chore: readme update * chore: improved validation and testing * chore: updating docs on label bug --------- Co-authored-by: Craig Vanaman <craig.vanaman@aoe.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent 4192511 commit 0a4c911

File tree

283 files changed

+84575
-915
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

283 files changed

+84575
-915
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,3 @@ vendir.lock.yml
2929
.craig
3030
CLAUDE.md
3131
.claude
32-
ROADMAP.md

README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ machine-controller-manager-provider-stackit/
2121
│ ├── provider/
2222
│ │ ├── core.go # Core provider implementation
2323
│ │ ├── provider.go # Driver interface implementation
24-
│ │ ├── stackit_client.go # STACKIT API client
24+
│ │ ├── stackit_client.go # STACKIT client interface
25+
│ │ ├── sdk_client.go # STACKIT SDK wrapper implementation
26+
│ │ ├── helpers.go # SDK type conversion utilities
2527
│ │ ├── apis/
2628
│ │ │ ├── provider_spec.go # ProviderSpec CRD definitions
2729
│ │ │ └── validation/ # Field validation logic
@@ -97,6 +99,24 @@ kubectl apply -f samples/machine-class.yaml
9799
kubectl apply -f samples/machine.yaml
98100
```
99101

102+
## STACKIT SDK Integration
103+
104+
This provider uses the official [STACKIT Go SDK](https://github.com/stackitcloud/stackit-sdk-go) for all interactions with the STACKIT IaaS API. The SDK provides type-safe API access, built-in authentication handling, and is officially maintained by STACKIT.
105+
106+
The SDK client is stateless and supports different credentials per MachineClass, allowing multi-tenancy scenarios where different machine pools use different STACKIT projects.
107+
108+
### Authentication & Credentials
109+
110+
The provider requires STACKIT credentials to be provided via a Kubernetes Secret. The Secret must contain the following fields:
111+
112+
| Field | Required | Description |
113+
|-------|----------|-------------|
114+
| `projectId` | Yes | STACKIT project UUID |
115+
| `stackitToken` | Yes | STACKIT API authentication token |
116+
| `region` | Yes | STACKIT region (e.g., `eu01-1`, `eu01-2`) |
117+
| `userData` | No | Default cloud-init user data (can be overridden in ProviderSpec) |
118+
| `networkId` | No | Default network UUID (can be overridden in ProviderSpec) |
119+
100120
## Configuration Reference
101121

102122
### ProviderSpec Fields
@@ -145,11 +165,25 @@ just start
145165

146166
## References
147167

168+
### Machine Controller Manager
148169
- [Machine Controller Manager](https://github.com/gardener/machine-controller-manager) - Core MCM project
149170
- [MCM Provider Development Guide](https://github.com/gardener/machine-controller-manager/blob/master/docs/development/cp_support_new.md) - Guidelines followed to build this provider
150171
- [MCM Sample Provider](https://github.com/gardener/machine-controller-manager-provider-sampleprovider) - Original template used as starting point
151172
- [MCM Driver Interface](https://github.com/gardener/machine-controller-manager/blob/master/pkg/util/provider/driver/driver.go) - Provider contract interface
173+
174+
### STACKIT SDK
175+
- [STACKIT SDK Go](https://github.com/stackitcloud/stackit-sdk-go) - Official STACKIT Go SDK
176+
- [IaaS Service Package](https://github.com/stackitcloud/stackit-sdk-go/tree/main/services/iaas) - IaaS service API documentation
177+
- [SDK Core Package](https://github.com/stackitcloud/stackit-sdk-go/tree/main/core) - Core SDK configuration and authentication
178+
- [SDK Examples](https://github.com/stackitcloud/stackit-sdk-go/tree/main/examples) - Code examples and usage patterns
179+
- [SDK Releases](https://github.com/stackitcloud/stackit-sdk-go/releases) - Release notes and changelog
180+
181+
### STACKIT Platform
152182
- [STACKIT Documentation](https://docs.stackit.cloud/) - STACKIT cloud platform documentation
183+
- [STACKIT Portal](https://portal.stackit.cloud/) - STACKIT management console
184+
- [Service Accounts](https://docs.stackit.cloud/stackit/en/service-accounts-134415819.html) - Creating and managing service accounts
185+
- [Service Account Keys](https://docs.stackit.cloud/stackit/en/usage-of-the-service-account-keys-in-stackit-175112464.html) - API authentication setup
186+
- [IaaS API Documentation](https://docs.stackit.cloud/) - STACKIT IaaS REST API reference
153187

154188
## License
155189

config/overlays/e2e/kustomization.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ kind: Kustomization
44
resources:
55
- namespace.yaml
66
- ../../default
7+
# TODO: replace ref with 'main' once changes merged upstream
78
- https://github.com/stackit-controllers-k8s/stackit-api-mockservers//config/apis/iaas?ref=main
89

10+
911
patches:
1012
# Patch MCM deployment to machine-controller-manager namespace
1113
- target:

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ require (
88
github.com/onsi/ginkgo/v2 v2.27.2
99
github.com/onsi/gomega v1.38.2
1010
github.com/spf13/pflag v1.0.5
11+
github.com/stackitcloud/stackit-sdk-go/core v0.18.0
12+
github.com/stackitcloud/stackit-sdk-go/services/iaas v1.0.0
1113
k8s.io/api v0.0.0-20190918155943-95b840bb6a1f
1214
k8s.io/apimachinery v0.0.0-20190913080033-27d36303b655
1315
k8s.io/component-base v0.0.0-20190918160511-547f6c5d7090
@@ -23,11 +25,13 @@ require (
2325
github.com/go-logr/logr v1.4.3 // indirect
2426
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
2527
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d // indirect
28+
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
2629
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 // indirect
2730
github.com/golang/protobuf v1.5.3 // indirect
2831
github.com/google/go-cmp v0.7.0 // indirect
2932
github.com/google/gofuzz v1.0.0 // indirect
3033
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
34+
github.com/google/uuid v1.6.0 // indirect
3135
github.com/googleapis/gnostic v0.2.0 // indirect
3236
github.com/hashicorp/golang-lru v0.5.1 // indirect
3337
github.com/imdario/mergo v0.3.5 // indirect

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
8080
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
8181
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
8282
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
83+
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
84+
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
8385
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
8486
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
8587
github.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7 h1:u4bArs140e9+AfE52mFHOXVFnOSBJBRlzTHrOPLOIhE=
@@ -115,6 +117,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J
115117
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
116118
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
117119
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
120+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
121+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
118122
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
119123
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
120124
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
@@ -220,6 +224,10 @@ github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzu
220224
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
221225
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
222226
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
227+
github.com/stackitcloud/stackit-sdk-go/core v0.18.0 h1:+4v8sjQpQXPihO3crgTp0Kz/XRIi5p7oKV28dw6jsEQ=
228+
github.com/stackitcloud/stackit-sdk-go/core v0.18.0/go.mod h1:fqto7M82ynGhEnpZU6VkQKYWYoFG5goC076JWXTUPRQ=
229+
github.com/stackitcloud/stackit-sdk-go/services/iaas v1.0.0 h1:qLMpd5whPMLnaLEdFQjK51q/o9V6eMFMORBDSsyGyNI=
230+
github.com/stackitcloud/stackit-sdk-go/services/iaas v1.0.0/go.mod h1:854gnLR92NvAbJAA1xZEumrtNh1DoBP1FXTMvhwYA6w=
223231
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
224232
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
225233
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=

pkg/provider/apis/validation/validation.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]
2828
// Pattern: lowercase letter(s) followed by digits, dot, then more digits (e.g., c2i.2, m2i.8, g1a.8)
2929
var machineTypeRegex = regexp.MustCompile(`^[a-z]+\d+[a-z]*\.\d+[a-z]*(\.[a-z]+\d+)*$`)
3030

31+
// regionRegex is a regex pattern for validating STACKIT region format
32+
// Pattern: lowercase letters/digits followed by digits, dash, then digit(s) (e.g., eu01-1, eu01-2)
33+
var regionRegex = regexp.MustCompile(`^[a-z0-9]+-\d+$`)
34+
3135
// labelKeyRegex validates Kubernetes label keys (must start/end with alphanumeric, can contain -, _, .)
3236
// Maximum length: 63 characters
3337
var labelKeyRegex = regexp.MustCompile(`^[a-zA-Z0-9]([-a-zA-Z0-9_.]*[a-zA-Z0-9])?$`)
@@ -63,6 +67,16 @@ func ValidateProviderSpecNSecret(spec *api.ProviderSpec, secrets *corev1.Secret)
6367
errors = append(errors, fmt.Errorf("secret 'stackitToken' cannot be empty"))
6468
}
6569

70+
// Validate region (required for SDK)
71+
region, ok := secrets.Data["region"]
72+
if !ok {
73+
errors = append(errors, fmt.Errorf("secret must contain 'region' field"))
74+
} else if len(region) == 0 {
75+
errors = append(errors, fmt.Errorf("secret 'region' cannot be empty"))
76+
} else if !isValidRegion(string(region)) {
77+
errors = append(errors, fmt.Errorf("secret 'region' has invalid format (expected format: eu01-1, eu01-2, etc.)"))
78+
}
79+
6680
// Validate ProviderSpec
6781
if spec.MachineType == "" {
6882
errors = append(errors, fmt.Errorf("providerSpec.machineType is required"))
@@ -261,3 +275,8 @@ func isValidEmail(s string) bool {
261275
func isValidMachineType(s string) bool {
262276
return machineTypeRegex.MatchString(s)
263277
}
278+
279+
// isValidRegion checks if a string matches the STACKIT region format
280+
func isValidRegion(s string) bool {
281+
return regionRegex.MatchString(s)
282+
}

pkg/provider/apis/validation/validation_core_labels_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var _ = Describe("ValidateProviderSpecNSecret", func() {
2828
Data: map[string][]byte{
2929
"projectId": []byte("11111111-2222-3333-4444-555555555555"),
3030
"stackitToken": []byte("test-token"),
31+
"region": []byte("eu01-1"),
3132
},
3233
}
3334
})

pkg/provider/apis/validation/validation_fields_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var _ = Describe("ValidateProviderSpecNSecret", func() {
2828
Data: map[string][]byte{
2929
"projectId": []byte("11111111-2222-3333-4444-555555555555"),
3030
"stackitToken": []byte("test-token"),
31+
"region": []byte("eu01-1"),
3132
},
3233
}
3334
})

pkg/provider/apis/validation/validation_networking_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var _ = Describe("ValidateProviderSpecNSecret", func() {
2828
Data: map[string][]byte{
2929
"projectId": []byte("11111111-2222-3333-4444-555555555555"),
3030
"stackitToken": []byte("test-token"),
31+
"region": []byte("eu01-1"),
3132
},
3233
}
3334
})

pkg/provider/apis/validation/validation_secgroup_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var _ = Describe("ValidateProviderSpecNSecret", func() {
2828
Data: map[string][]byte{
2929
"projectId": []byte("11111111-2222-3333-4444-555555555555"),
3030
"stackitToken": []byte("test-token"),
31+
"region": []byte("eu01-1"),
3132
},
3233
}
3334
})

0 commit comments

Comments
 (0)