Skip to content
Open
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
29 changes: 29 additions & 0 deletions Dockerfile.node-labeler
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2025 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

FROM --platform=$BUILDPLATFORM golang:1.25.5 AS builder

ARG STAGINGVERSION
ARG TARGETPLATFORM

WORKDIR /go/src/sigs.k8s.io/gcp-compute-persistent-disk-csi-driver
ADD . .
RUN GOARCH=$(echo $TARGETPLATFORM | cut -f2 -d '/') GCE_PD_CSI_STAGING_VERSION=$STAGINGVERSION make gce-pd-node-labeler

FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /go/src/sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/bin/gce-pd-node-labeler .
USER 65532:65532

ENTRYPOINT ["/gce-pd-node-labeler"]
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ REV=$(shell git describe --long --tags --match='v*' --dirty 2>/dev/null || git r
GCE_PD_CSI_STAGING_VERSION ?= ${REV}
STAGINGVERSION=${GCE_PD_CSI_STAGING_VERSION}
STAGINGIMAGE=${GCE_PD_CSI_STAGING_IMAGE}
LABELERSTAGINGIMAGE?=${STAGINGIMAGE}/gce-pd-node-labeler
DRIVERBINARY=gce-pd-csi-driver
LABELERBINARY=gce-pd-node-labeler
DRIVERWINDOWSBINARY=${DRIVERBINARY}.exe

DOCKER=DOCKER_CLI_EXPERIMENTAL=enabled docker
Expand All @@ -40,6 +42,10 @@ gce-pd-driver: require-GCE_PD_CSI_STAGING_VERSION
mkdir -p bin
CGO_ENABLED=0 go build -mod=vendor -gcflags=$(GCFLAGS) -ldflags "-extldflags=static -X main.version=$(STAGINGVERSION)" -o bin/${DRIVERBINARY} ./cmd/gce-pd-csi-driver/

gce-pd-node-labeler: require-GCE_PD_CSI_STAGING_VERSION
mkdir -p bin
CGO_ENABLED=0 go build -mod=vendor -gcflags=$(GCFLAGS) -ldflags "-extldflags=static -X main.version=$(STAGINGVERSION)" -o bin/${LABELERBINARY} ./cmd/gce-pd-node-labeler/

gce-pd-driver-windows: require-GCE_PD_CSI_STAGING_VERSION
ifeq (${GOARCH}, amd64)
mkdir -p bin
Expand Down Expand Up @@ -111,6 +117,18 @@ build-and-push-container-linux-arm64: require-GCE_PD_CSI_STAGING_IMAGE init-buil
--build-arg BUILDPLATFORM=linux \
--build-arg STAGINGVERSION=$(STAGINGVERSION) --push --provenance=false .

build-node-labeler-container-linux-amd64: require-GCE_PD_CSI_STAGING_IMAGE init-buildx
$(DOCKER) buildx build --file=Dockerfile.node-labeler --platform=linux/amd64 \
-t $(LABELERSTAGINGIMAGE):$(STAGINGVERSION)_linux_amd64 \
--build-arg BUILDPLATFORM=linux \
--build-arg STAGINGVERSION=$(STAGINGVERSION) --push --provenance=false .

build-node-labeler-container-linux-arm64: require-GCE_PD_CSI_STAGING_IMAGE init-buildx
$(DOCKER) buildx build --file=Dockerfile.node-labeler --platform=linux/arm64 \
-t $(LABELERSTAGINGIMAGE):$(STAGINGVERSION)_linux_arm64 \
--build-arg BUILDPLATFORM=linux \
--build-arg STAGINGVERSION=$(STAGINGVERSION) --push --provenance=false .

build-and-push-container-linux-debug: require-GCE_PD_CSI_STAGING_IMAGE init-buildx
$(DOCKER) buildx build --file=Dockerfile.debug --platform=linux \
-t $(STAGINGIMAGE):$(STAGINGVERSION)_linux \
Expand Down
126 changes: 126 additions & 0 deletions cmd/gce-pd-node-labeler/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
Copyright 2025 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package main is the GCE PD Node Labeler entrypoint.
package main

import (
"context"
"errors"
"flag"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/klog/v2"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/nodelabeler"
)

var (
configmapName = flag.String("configmap-name", "machine-pd-compatibility", "Name of the ConfigMap to use for machine & PD compatibility information.")
configmapNamespace = flag.String("configmap-namespace", "gce-pd-csi-driver", "Namespace of the ConfigMap to use for machine & PD compatibility information.")
)

func main() {
klog.InitFlags(nil)
flag.Parse()
log.SetLogger(klog.NewKlogr())

ctx := signals.SetupSignalHandler()

mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{})
if err != nil {
klog.Fatalf("Failed to create manager: %v", err)
}

reconciler := &nodelabeler.Reconciler{
Client: mgr.GetClient(),
ConfigMapName: *configmapName,
ConfigMapNamespace: *configmapNamespace,
}

if err := mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
if !mgr.GetCache().WaitForCacheSync(ctx) {
return errors.New("failed to wait for cache sync")
}
if err := reconciler.UpdateMachinePDCompatibility(ctx); err != nil {
klog.Errorf("Failed to load initial machine and PD compatibility map: %v", err)
return err
}
return nil
})); err != nil {
klog.Fatalf("Failed to add runnable to manager: %v", err)
}

mapFn := func(ctx context.Context, obj client.Object) []reconcile.Request {
cm, ok := obj.(*corev1.ConfigMap)
if !ok {
klog.Errorf("Expected a ConfigMap object but got %T", obj)
return nil
}
if err := reconciler.UpdateMachinePDCompatibility(ctx); err != nil {
klog.Errorf("Failed to update disk compatibility map from ConfigMap %s/%s: %v", cm.Namespace, cm.Name, err)
return nil
}

var nodes corev1.NodeList
if err := mgr.GetClient().List(ctx, &nodes); err != nil {
klog.Errorf("Failed to list nodes for configmap change reconciliation: %v", err)
return nil
}
requests := make([]reconcile.Request, len(nodes.Items))
for i, node := range nodes.Items {
requests[i] = reconcile.Request{
NamespacedName: types.NamespacedName{
Name: node.Name,
},
}
}
klog.Infof("ConfigMap changed, enqueuing reconciliation for %d nodes", len(requests))
return requests
}

err = builder.ControllerManagedBy(mgr).
For(&corev1.Node{}).
Watches(
&corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: *configmapName,
Namespace: *configmapNamespace,
},
},
handler.EnqueueRequestsFromMapFunc(mapFn),
builder.WithPredicates(predicate.NewPredicateFuncs(func(obj client.Object) bool {
return obj.GetName() == *configmapName && obj.GetNamespace() == *configmapNamespace
})),
).
Complete(reconciler)
if err != nil {
klog.Fatalf("Failed to build controller: %v", err)
}

klog.Info("Starting manager")
if err := mgr.Start(ctx); err != nil {
klog.Fatalf("Failed to start manager: %v", err)
}
}
Loading