Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ vendor
*.swo
*~
dev/

.tiltbuild

# kind configs
config/kind/mgmt-kubeconfig-external
config/kind/mgmt-kubeconfig-internal
config/kind/worker-kubeconfig
17 changes: 16 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,21 @@ check-license: addlicense ## Check that every file has a license header present.

check: add-license lint test

.PHONY: kind-setup
kind-setup:
kind create cluster --name mgmt
kind create cluster --name worker

.PHONY: export-kubeconfig
export-kubeconfig:
kind get kubeconfig --name mgmt > ./config/kind/mgmt-kubeconfig-external # for applying crds to mgmt cluster via tilt
kind get kubeconfig --name mgmt --internal > ./config/kind/mgmt-kubeconfig-internal # for ccm config (it needs access to mgmt cluster)
kind get kubeconfig --name worker > ./config/kind/worker-kubeconfig # for applying crds to worker cluster via tilt

.PHONY: tilt-up
tilt-up: kind-setup export-kubeconfig
KUBECONFIG=./config/kind/mgmt-kubeconfig:./config/kind/worker-kubeconfig tilt up

##@ Build

.PHONY: build
Expand All @@ -78,7 +93,7 @@ run: fmt vet ## Run a metal cloud controller from your host.
go run ./cmd/metal-cloud-controller-manager/main.go

.PHONY: docker-build
docker-build: test ## Build docker image with the metal cloud controller.
docker-build: ## Build docker image with the metal cloud controller.
$(CONTAINER_TOOL) build -t ${CONTROLLER_IMG} .

.PHONY: docker-push
Expand Down
54 changes: 54 additions & 0 deletions Tiltfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
allow_k8s_contexts(['kind-mgmt', 'kind-worker'])

mgmt_ctx = 'kind-mgmt'
worker_ctx = 'kind-worker'

mgmt_kubeconfig = './config/kind/mgmt-kubeconfig-external'
worker_kubeconfig = './config/kind/worker-kubeconfig'

def mgmt_kubectl(args):
return local('kubectl --kubeconfig=' + mgmt_kubeconfig + ' --context=' + mgmt_ctx + ' ' + args)

def worker_kubectl(args):
return local('kubectl --kubeconfig=' + worker_kubeconfig + ' --context=' + worker_ctx + ' ' + args)

METAL_OPERATOR_REF = "v0.4.0"
mgmt_kubectl('apply -f https://raw.githubusercontent.com/ironcore-dev/metal-operator/' + METAL_OPERATOR_REF + '/config/crd/bases/metal.ironcore.dev_serverclaims.yaml')
mgmt_kubectl('apply -f https://raw.githubusercontent.com/ironcore-dev/metal-operator/' + METAL_OPERATOR_REF + '/config/crd/bases/metal.ironcore.dev_servermaintenances.yaml')
mgmt_kubectl('apply -f https://raw.githubusercontent.com/ironcore-dev/metal-operator/' + METAL_OPERATOR_REF + '/config/crd/bases/metal.ironcore.dev_servers.yaml')
mgmt_kubectl('wait --for=condition=Established --timeout=60s crd/serverclaims.metal.ironcore.dev')
mgmt_kubectl('wait --for=condition=Established --timeout=60s crd/servermaintenances.metal.ironcore.dev')
mgmt_kubectl('wait --for=condition=Established --timeout=60s crd/servers.metal.ironcore.dev')
mgmt_kubectl('apply -f config/kind/crs/server.yaml')
mgmt_kubectl('apply -f config/kind/crs/serverclaim.yaml')

worker_kubectl('apply -k config/kind') # kustomize
worker_kubectl('apply -f config/kind/crs/node.yaml')

local_resource(
"manager-binary",
cmd = 'mkdir -p .tiltbuild; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o .tiltbuild/manager ./cmd/metal-cloud-controller-manager/main.go',
deps = ["pkg", "cmd", "go.mod", "go.sum"]
)

docker_build(
ref = "controller",
context = "./.tiltbuild/",
dockerfile_contents = """
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY manager /metal-cloud-controller-manager
USER 65532:65532
ENTRYPOINT ["/metal-cloud-controller-manager"]
""",
only = "manager"
)

k8s_yaml(kustomize('config/kind'))

k8s_resource(
'cloud-controller-manager',
labels=['CCM'],
port_forwards='10258:10258',
extra_pod_selectors=[{'app.kubernetes.io/name': 'cloud-controller-manager'}]
)
3 changes: 3 additions & 0 deletions Tiltfile.license
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SPDX-FileCopyrightText: 2026 SAP SE

SPDX-License-Identifier: Apache-2.0
6 changes: 6 additions & 0 deletions config/kind/cloud-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
clusterName: kind-ccm
networking:
configureNodeAddresses: true
ipamKind:
apiGroup: metal.ironcore.dev
kind: ServerClaim
8 changes: 8 additions & 0 deletions config/kind/crs/node.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Node
metadata:
name: metal-node-1
labels:
kubernetes.io/hostname: metal-node-1
spec:
providerID: metal://default/server-1
6 changes: 6 additions & 0 deletions config/kind/crs/server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: metal.ironcore.dev/v1alpha1
kind: Server
metadata:
name: physical-server-1
spec:
systemUUID: "some-uuid"
13 changes: 13 additions & 0 deletions config/kind/crs/serverclaim.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: metal.ironcore.dev/v1alpha1
kind: ServerClaim
metadata:
name: server-1
namespace: default
spec:
image: "test-image"
power: "On"
serverRef:
name: physical-server-1
serverSelector:
matchLabels:
machine: "true"
22 changes: 22 additions & 0 deletions config/kind/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: kube-system

resources:
- ../default

patches:
- target:
kind: Deployment
name: manager
path: manager-patch.yaml

secretGenerator:
- name: metal-cloud-config
namespace: kube-system
files:
- cloud-config=cloud-config.yaml
- kubeconfig=mgmt-kubeconfig-internal

generatorOptions:
disableNameSuffixHash: true
29 changes: 29 additions & 0 deletions config/kind/manager-patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: manager
spec:
strategy:
type: Recreate
template:
spec:
containers:
- name: manager
image: controller
imagePullPolicy: IfNotPresent
args:
- --cloud-provider=metal
- --cloud-config=/etc/kubernetes/cloud-config/cloud-config
- --metal-kubeconfig=/etc/kubernetes/cloud-config/kubeconfig
- --concurrent-service-syncs=10
- --leader-elect=false
- --secure-port=10258
- --v=2
volumeMounts:
- mountPath: /etc/kubernetes/cloud-config
name: cloud-config
readOnly: true
volumes:
- name: cloud-config
secret:
secretName: metal-cloud-config
18 changes: 9 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ go 1.25.6

require (
github.com/ironcore-dev/controller-utils v0.11.0
github.com/ironcore-dev/metal-operator v0.3.0
github.com/ironcore-dev/metal-operator v0.4.0
github.com/onsi/ginkgo/v2 v2.28.1
github.com/onsi/gomega v1.39.1
github.com/pkg/errors v0.9.1
github.com/spf13/pflag v1.0.10
k8s.io/api v0.35.0
k8s.io/apimachinery v0.35.0
k8s.io/client-go v0.35.0
k8s.io/cloud-provider v0.34.1
k8s.io/cloud-provider v0.35.0
k8s.io/component-base v0.35.0
k8s.io/controller-manager v0.34.1
k8s.io/controller-manager v0.35.0
k8s.io/klog/v2 v2.130.1
sigs.k8s.io/cluster-api v1.10.4
sigs.k8s.io/controller-runtime v0.23.1
Expand Down Expand Up @@ -80,7 +80,7 @@ require (
github.com/prometheus/common v0.67.1 // indirect
github.com/prometheus/procfs v0.19.1 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/stmcginnis/gofish v0.20.0 // indirect
github.com/stmcginnis/gofish v0.21.4 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.etcd.io/etcd/api/v3 v3.6.5 // indirect
Expand All @@ -89,12 +89,12 @@ require (
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel v1.40.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
go.opentelemetry.io/otel/sdk v1.40.0 // indirect
go.opentelemetry.io/otel/trace v1.40.0 // indirect
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
Expand All @@ -121,7 +121,7 @@ require (
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
k8s.io/apiextensions-apiserver v0.35.0 // indirect
k8s.io/apiserver v0.35.0 // indirect
k8s.io/component-helpers v0.34.1 // indirect
k8s.io/component-helpers v0.35.0 // indirect
k8s.io/kms v0.35.0 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
Expand Down
40 changes: 20 additions & 20 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/ironcore-dev/controller-utils v0.11.0 h1:vQhZgPxxFwmSi+fSlPEuwCmI5sOP7QwjX97DL4jew/c=
github.com/ironcore-dev/controller-utils v0.11.0/go.mod h1:kPIgIjGNMA5zUlwH04rCdDbYnvvDtd79z3Rgav1Yrpg=
github.com/ironcore-dev/metal-operator v0.3.0 h1:hHK4rmEH2ZHmZ3GYI8U0D6zbdE627AFp1hyEDwnLLuw=
github.com/ironcore-dev/metal-operator v0.3.0/go.mod h1:9zPEgLN9bn379RfZmaR2nvxOSLM1sBuhVCUE36uZjNw=
github.com/ironcore-dev/metal-operator v0.4.0 h1:D/5+eFe2n1Ro81ysWWv+RnyAHtB8pjrQMJzp8FhiYH8=
github.com/ironcore-dev/metal-operator v0.4.0/go.mod h1:n7TDdJYvW1/4fqSjW5+uX5WRP671wGKyVELcTd8KWto=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
Expand Down Expand Up @@ -185,8 +185,8 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stmcginnis/gofish v0.20.0 h1:hH2V2Qe898F2wWT1loApnkDUrXXiLKqbSlMaH3Y1n08=
github.com/stmcginnis/gofish v0.20.0/go.mod h1:PzF5i8ecRG9A2ol8XT64npKUunyraJ+7t0kYMpQAtqU=
github.com/stmcginnis/gofish v0.21.4 h1:daexK8sh31CgeSMkPUNs21HWHHA9ecCPJPyLCTxukCg=
github.com/stmcginnis/gofish v0.21.4/go.mod h1:PzF5i8ecRG9A2ol8XT64npKUunyraJ+7t0kYMpQAtqU=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down Expand Up @@ -236,20 +236,20 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE=
go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
Expand Down Expand Up @@ -344,14 +344,14 @@ k8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4=
k8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds=
k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=
k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=
k8s.io/cloud-provider v0.34.1 h1:FS+4C1vq9pIngd/5LR5Jha1sEbn+fo0HJitgZmUyBNc=
k8s.io/cloud-provider v0.34.1/go.mod h1:ghyQYfQIWZAXKNS+TEgEiQ8wPuhzIVt3wFO6rKqS/rQ=
k8s.io/cloud-provider v0.35.0 h1:syiBCQbKh2gho/S1BkIl006Dc44pV8eAtGZmv5NMe7M=
k8s.io/cloud-provider v0.35.0/go.mod h1:7grN+/Nt5Hf7tnSGPT3aErt4K7aQpygyCrGpbrQbzNc=
k8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94=
k8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0=
k8s.io/component-helpers v0.34.1 h1:gWhH3CCdwAx5P3oJqZKb4Lg5FYZTWVbdWtOI8n9U4XY=
k8s.io/component-helpers v0.34.1/go.mod h1:4VgnUH7UA/shuBur+OWoQC0xfb69sy/93ss0ybZqm3c=
k8s.io/controller-manager v0.34.1 h1:c9Cmun/zF740kmdRQWPGga+4MglT5SlrwsCXDS/KtJI=
k8s.io/controller-manager v0.34.1/go.mod h1:fGiJDhi3OSzSAB4f40ZkJLAqMQSag9RM+7m5BRhBO3Q=
k8s.io/component-helpers v0.35.0 h1:wcXv7HJRksgVjM4VlXJ1CNFBpyDHruRI99RrBtrJceA=
k8s.io/component-helpers v0.35.0/go.mod h1:ahX0m/LTYmu7fL3W8zYiIwnQ/5gT28Ex4o2pymF63Co=
k8s.io/controller-manager v0.35.0 h1:KteodmfVIRzfZ3RDaxhnHb72rswBxEngvdL9vuZOA9A=
k8s.io/controller-manager v0.35.0/go.mod h1:1bVuPNUG6/dpWpevsJpXioS0E0SJnZ7I/Wqc9Awyzm4=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kms v0.35.0 h1:/x87FED2kDSo66csKtcYCEHsxF/DBlNl7LfJ1fVQs1o=
Expand Down
46 changes: 37 additions & 9 deletions pkg/cloudprovider/metal/instances_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import (
"context"
"errors"
"fmt"
"net/url"
"strings"

metalv1alpha1 "github.com/ironcore-dev/metal-operator/api/v1alpha1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
cloudprovider "k8s.io/cloud-provider"
"k8s.io/klog/v2"
capiv1beta1 "sigs.k8s.io/cluster-api/exp/ipam/api/v1beta1"
Expand Down Expand Up @@ -120,7 +122,7 @@ func (o *metalInstancesV2) InstanceMetadata(ctx context.Context, node *corev1.No

providerID := node.Spec.ProviderID
if providerID == "" {
providerID = fmt.Sprintf("%s://%s/%s", ProviderName, o.metalNamespace, serverClaim.Name)
providerID = buildProviderID(o.metalNamespace, serverClaim.Name)
}

instanceType, ok := server.Labels[metalv1alpha1.AnnotationInstanceType]
Expand Down Expand Up @@ -286,13 +288,39 @@ func (o *metalInstancesV2) getServerClaimFromProviderID(ctx context.Context, pro
return serverClaim, nil
}

func buildProviderID(namespace, name string) string {
u := url.URL{
Scheme: ProviderName,
Host: namespace,
Path: name,
}
return u.String()
}

func getObjectKeyFromProviderID(providerID string) (client.ObjectKey, error) {
parts := strings.Split(strings.TrimPrefix(providerID, fmt.Sprintf("%s://", ProviderName)), "/")
if len(parts) != 2 {
return client.ObjectKey{}, fmt.Errorf("invalid format of ProviderID %s", providerID)
}
return client.ObjectKey{
Namespace: parts[0],
Name: parts[1],
}, nil
if providerID == "" {
return types.NamespacedName{}, errors.New("empty providerID")
}

u, err := url.Parse(providerID)
if err != nil {
return types.NamespacedName{}, fmt.Errorf("parse provider ID %q: %w", providerID, err)
}

if u.Scheme != ProviderName {
return types.NamespacedName{}, fmt.Errorf("unsupported provider scheme: %q", u.Scheme)
}

namespace := u.Host
name := strings.TrimPrefix(u.Path, "/")

if namespace == "" || name == "" {
return types.NamespacedName{}, errors.New("missing namespace or name in provider ID")
}

if strings.Contains(name, "/") {
return types.NamespacedName{}, errors.New("invalid provider ID format: name cannot contain slashes")
}

return types.NamespacedName{Namespace: namespace, Name: name}, nil
}
Loading
Loading