Skip to content

Commit 3891ded

Browse files
committed
introduce k8s-idiomatic Go SDK for Device API
Signed-off-by: Dan Huenecke <dhuenecke@nvidia.com>
1 parent ef820c3 commit 3891ded

75 files changed

Lines changed: 6293 additions & 145 deletions

Some content is hidden

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

.coderabbit.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,9 @@ reviews:
1616
profile: chill
1717
auto_review:
1818
enabled: true
19+
path_filters:
20+
- "!api/gen/**" # Ignore generated protobuf bindings
21+
- "!api/**/zz_generated.*" # Ignore generated deepcopy and conversion code
22+
- "!client-go/client/**" # Ignore generated clientset
23+
- "!client-go/listers/**" # Ignore generated listers
24+
- "!client-go/informers/**" # Ignore generated informers

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*.so
1616
*.dylib
1717
api/bin/*
18+
client-go/bin/*
1819

1920
# Test binary, built with `go test -c`
2021
*.test

.versions.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ go_tools:
4646
setup_envtest: 'latest'
4747
goimports: 'v0.30.0'
4848
crane: 'v0.20.2'
49+
goverter: 'v1.9.2'
50+
kubernetes_code_gen: 'v0.34.1'
4951

5052
# Protocol Buffers / gRPC
5153
protobuf:

api/Makefile

Lines changed: 120 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,32 @@
1616
# Configuration
1717
# ==============================================================================
1818

19+
# Helpers for string manipulation
20+
COMMA := ,
21+
EMPTY :=
22+
SPACE := $(EMPTY) $(EMPTY)
23+
24+
# Modules
25+
MODULE_NAME := github.com/nvidia/nvsentinel/api
26+
27+
# API Versions (space-separated)
28+
API_VERSIONS := device/v1alpha1
29+
30+
# Helper for tools that need comma-separated Group/Versions
31+
API_VERSIONS_COMMA := $(subst $(SPACE),$(COMMA),$(API_VERSIONS))
32+
33+
# Helper for tools that need full package paths
34+
API_PKGS := $(foreach version,$(API_VERSIONS),$(MODULE_NAME)/$(version))
35+
36+
#Helper for tools that need comma-separated package paths
37+
API_PKGS_COMMA := $(subst $(SPACE),$(COMMA),$(API_PKGS))
38+
39+
# Common
40+
BOILERPLATE := hack/boilerplate.go.txt
41+
1942
# Directories
20-
PROTO_DIR := proto
21-
GEN_DIR := gen/go
43+
PROTO_DIR := proto
44+
GEN_DIR := gen/go
2245

2346
# Shell setup
2447
SHELL = /usr/bin/env bash -o pipefail
@@ -29,9 +52,17 @@ LOCALBIN ?= $(shell pwd)/bin
2952
$(LOCALBIN):
3053
mkdir -p $(LOCALBIN)
3154

55+
# Go Version
56+
GO_VERSION := $(shell grep "^toolchain " go.mod | awk '{print $$2}')
57+
ifeq ($(GO_VERSION),)
58+
GO_VERSION := $(shell go env GOVERSION)
59+
endif
60+
3261
# Tool Binaries
33-
PROTOC_GEN_GO ?= $(LOCALBIN)/protoc-gen-go
34-
PROTOC_GEN_GO_GRPC ?= $(LOCALBIN)/protoc-gen-go-grpc
62+
PROTOC_GEN_GO ?= $(LOCALBIN)/protoc-gen-go
63+
PROTOC_GEN_GO_GRPC ?= $(LOCALBIN)/protoc-gen-go-grpc
64+
DEEPCOPY_GEN ?= $(LOCALBIN)/deepcopy-gen
65+
GOVERTER ?= $(LOCALBIN)/goverter
3566

3667
# Tool Versions (load from ../.versions.yaml)
3768
# Requires yq to be installed: brew install yq (macOS) or see https://github.com/mikefarah/yq
@@ -42,15 +73,17 @@ endif
4273

4374
VERSIONS_FILE := ../.versions.yaml
4475

45-
PROTOC_GEN_GO_VERSION := $(shell $(YQ) '.protobuf.protoc_gen_go' $(VERSIONS_FILE))
46-
PROTOC_GEN_GO_GRPC_VERSION := $(shell $(YQ) '.protobuf.protoc_gen_go_grpc' $(VERSIONS_FILE))
76+
PROTOC_GEN_GO_VERSION := $(shell $(YQ) '.protobuf.protoc_gen_go' $(VERSIONS_FILE))
77+
PROTOC_GEN_GO_GRPC_VERSION := $(shell $(YQ) '.protobuf.protoc_gen_go_grpc' $(VERSIONS_FILE))
78+
KUBERNETES_CODEGEN_VERSION := $(shell $(YQ) '.go_tools.kubernetes_code_gen' $(VERSIONS_FILE))
79+
GOVERTER_VERSION := $(shell $(YQ) '.go_tools.goverter' $(VERSIONS_FILE))
4780

4881
# ==============================================================================
4982
# Targets
5083
# ==============================================================================
5184

5285
.PHONY: all
53-
all: protos-generate build
86+
all: code-gen test build ## Run code generation, compile all code, and execute tests.
5487

5588
##@ General
5689

@@ -60,42 +93,100 @@ help: ## Display this help.
6093

6194
##@ Development
6295

96+
.PHONY: code-gen
97+
code-gen: install-tools protos-generate deepcopy-gen conversion-gen ## Run all code generation targets.
98+
@echo "Synchronizing module dependencies..."
99+
go mod tidy
100+
63101
.PHONY: build
64-
build:
102+
build: code-gen ## Compile all Go code after generation to verify type safety.
103+
@echo "Compiling..."
65104
go build -v ./...
66105

106+
.PHONY: test
107+
test: code-gen ## Run unit tests and generate coverage after code generation.
108+
@echo "Testing..."
109+
go test -v $$(go list ./... | grep -v /gen/) -coverprofile cover.out
110+
67111
.PHONY: protos-generate
68-
protos-generate: $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) protos-clean ## Generate Go code from Proto definitions.
69-
@echo "Generating Proto code..."
112+
protos-generate: $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) clean-protos ## Generate Protobuf Go bindings.
113+
@echo "Generating protobuf bindings for $(API_VERSIONS_COMMA)..."
70114
@mkdir -p $(GEN_DIR)
71-
cd proto && protoc \
72-
-I . \
73-
-I ../$(THIRD_PARTY_DIR) \
74-
--plugin="protoc-gen-go=$(PROTOC_GEN_GO)" \
75-
--plugin="protoc-gen-go-grpc=$(PROTOC_GEN_GO_GRPC)" \
76-
--go_out=../$(GEN_DIR) \
77-
--go_opt=paths=source_relative \
78-
--go-grpc_out=../$(GEN_DIR) \
79-
--go-grpc_opt=paths=source_relative \
80-
device/v1alpha1/*.proto
81-
@echo "Cleaning up dependencies..."
82-
go mod tidy
83-
@echo "Done."
84-
85-
.PHONY: protos-clean
86-
protos-clean: ## Remove generated code.
87-
@echo "Cleaning old generated Proto code..."
115+
@$(foreach version,$(API_VERSIONS), \
116+
cd $(PROTO_DIR) && protoc \
117+
-I . \
118+
--plugin="protoc-gen-go=$(PROTOC_GEN_GO)" \
119+
--plugin="protoc-gen-go-grpc=$(PROTOC_GEN_GO_GRPC)" \
120+
--go_out="../$(GEN_DIR)" \
121+
--go_opt=paths="source_relative" \
122+
--go-grpc_out="../$(GEN_DIR)" \
123+
--go-grpc_opt=paths="source_relative" \
124+
$(version)/*.proto; \
125+
)
126+
127+
.PHONY: deepcopy-gen
128+
deepcopy-gen: $(DEEPCOPY_GEN) clean-deepcopy ## Generate deepcopy code.
129+
@echo "Generating deepcopy code for $(API_VERSIONS_COMMA)..."
130+
$(DEEPCOPY_GEN) \
131+
--bounding-dirs="$(API_PKGS_COMMA)" \
132+
--go-header-file="$(BOILERPLATE)" \
133+
--output-file="zz_generated.deepcopy.go" \
134+
$(API_PKGS)
135+
136+
.PHONY: conversion-gen
137+
conversion-gen: $(GOVERTER) clean-conversion ## Generate conversion code.
138+
@echo "Generating conversion code for $(API_VERSIONS_COMMA)..."
139+
@$(foreach version,$(API_VERSIONS), \
140+
$(GOVERTER) gen ./$(version); \
141+
)
142+
143+
.PHONY: clean
144+
clean: clean-protos clean-conversion clean-deepcopy ## Remove all generated code.
145+
146+
.PHONY: purge
147+
purge: clean clean-bin ## Remove all generated code and downloaded tools.
148+
149+
.PHONY: clean-protos
150+
clean-protos: ## Remove generated protobuf Go bindings.
151+
@echo "Removing generated protobuf Go bindings..."
88152
rm -rf $(GEN_DIR)
89-
@echo "Done."
153+
154+
.PHONY: clean-deepcopy
155+
clean-deepcopy: ## Remove generated deepcopy code.
156+
@echo "Removing generated deepcopy code..."
157+
find . -name "zz_generated.deepcopy.go" -delete
158+
159+
.PHONY: clean-conversion
160+
clean-conversion: ## Remove generated conversion code.
161+
@echo "Removing generated conversion code..."
162+
find . -name "zz_generated.conversion.go" -delete
163+
164+
.PHONY: clean-bin
165+
clean-bin: ## Remove all downloaded tools in localbin.
166+
@echo "Removing downloaded build tools from $(LOCALBIN)..."
167+
rm -rf $(LOCALBIN)
90168

91169
##@ Build Dependencies
92170

171+
.PHONY: install-tools
172+
install-tools: install-tools-echo $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) $(DEEPCOPY_GEN) $(GOVERTER) ## Ensure all necessary build tools are downloaded and installed.
173+
174+
.PHONY: install-tools-echo
175+
install-tools-echo:
176+
@echo "Installing dependencies..."
177+
93178
$(PROTOC_GEN_GO):
94179
$(call go-install-tool,$(PROTOC_GEN_GO),google.golang.org/protobuf/cmd/protoc-gen-go,$(PROTOC_GEN_GO_VERSION))
95180

96181
$(PROTOC_GEN_GO_GRPC):
97182
$(call go-install-tool,$(PROTOC_GEN_GO_GRPC),google.golang.org/grpc/cmd/protoc-gen-go-grpc,$(PROTOC_GEN_GO_GRPC_VERSION))
98183

184+
$(DEEPCOPY_GEN):
185+
$(call go-install-tool,$(DEEPCOPY_GEN),k8s.io/code-generator/cmd/deepcopy-gen,$(KUBERNETES_CODEGEN_VERSION))
186+
187+
$(GOVERTER):
188+
$(call go-install-tool,$(GOVERTER),github.com/jmattheis/goverter/cmd/goverter,$(GOVERTER_VERSION))
189+
99190
# go-install-tool macro
100191
# $1 - target path with name of binary
101192
# $2 - package url which can be installed
@@ -107,7 +198,7 @@ mkdir -p $(LOCALBIN); \
107198
package=$(2)@$(3) ;\
108199
echo "Downloading $${package}" ;\
109200
rm -f $(1) || true ;\
110-
GOBIN=$(LOCALBIN) go install $${package} ;\
201+
GOBIN=$(LOCALBIN) GOTOOLCHAIN=$(GO_VERSION) go install $${package} ;\
111202
mv $(1) $(1)-$(3) ;\
112203
} ;\
113204
ln -sf $(1)-$(3) $(1)

api/device/v1alpha1/converter.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package v1alpha1
16+
17+
import (
18+
pb "github.com/nvidia/nvsentinel/api/gen/go/device/v1alpha1"
19+
"google.golang.org/protobuf/types/known/timestamppb"
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
// Converter is the interface used to generate type conversion methods between the
24+
// Kubernetes Resource Model structs and the Protobuf message structs.
25+
//
26+
// goverter:converter
27+
// goverter:output:file ./zz_generated.conversion.go
28+
// goverter:extend FromProtobufTypeMeta FromProtobufListTypeMeta FromProtobufTimestamp ToProtobufTimestamp
29+
// goverter:useZeroValueOnPointerInconsistency
30+
type Converter interface {
31+
// FromProtobuf converts a protobuf Gpu message into a GPU object.
32+
//
33+
// goverter:map . TypeMeta | FromProtobufTypeMeta
34+
// goverter:map Metadata ObjectMeta
35+
FromProtobuf(source *pb.Gpu) GPU
36+
37+
// ToProtobuf converts a GPU object into a protobuf Gpu message.
38+
//
39+
// goverter:map ObjectMeta Metadata
40+
// goverter:ignore state sizeCache unknownFields
41+
ToProtobuf(source GPU) *pb.Gpu
42+
43+
// FromProtobufList converts a protobuf GpuList message into a GPUList object.
44+
//
45+
// goverter:map . TypeMeta | FromProtobufListTypeMeta
46+
// goverter:map Metadata ListMeta
47+
FromProtobufList(source *pb.GpuList) *GPUList
48+
49+
// ToProtobufList converts a GPUList object into a protobuf GpuList message.
50+
//
51+
// goverter:map ListMeta Metadata
52+
// goverter:ignore state sizeCache unknownFields
53+
ToProtobufList(source *GPUList) *pb.GpuList
54+
55+
// FromProtobufObjectMeta converts a protobuf ObjectMeta into a metav1.ObjectMeta object.
56+
//
57+
// goverter:ignore Namespace GenerateName UID Generation CreationTimestamp DeletionTimestamp DeletionGracePeriodSeconds Labels Annotations OwnerReferences Finalizers ManagedFields SelfLink
58+
FromProtobufObjectMeta(source *pb.ObjectMeta) metav1.ObjectMeta
59+
60+
// ToProtobufObjectMeta converts a metav1.ObjectMeta into a protobuf Object message.
61+
//
62+
// goverter:ignore state sizeCache unknownFields
63+
ToProtobufObjectMeta(source metav1.ObjectMeta) *pb.ObjectMeta
64+
65+
// FromProtobufListMeta converts a protobuf ListMeta into a metav1.ListMeta object.
66+
//
67+
// goverter:ignore SelfLink Continue RemainingItemCount
68+
FromProtobufListMeta(source *pb.ListMeta) metav1.ListMeta
69+
70+
// ToProtobufListMeta converts a metav1.ListMeta into a protobuf ListMeta message.
71+
//
72+
// goverter:ignore state sizeCache unknownFields
73+
ToProtobufListMeta(source metav1.ListMeta) *pb.ListMeta
74+
75+
// FromProtobufSpec converts a protobuf GpuSpec message into a GPUSpec object.
76+
//
77+
// goverter:map Uuid UUID
78+
FromProtobufSpec(source *pb.GpuSpec) GPUSpec
79+
80+
// ToProtobufSpec converts a GPUSpec object into a protobuf GpuSpec message.
81+
//
82+
// goverter:map UUID Uuid
83+
// goverter:ignore state sizeCache unknownFields
84+
ToProtobufSpec(source GPUSpec) *pb.GpuSpec
85+
86+
// FromProtobufStatus converts a protobuf GpuStatus message into a GPUStatus object.
87+
FromProtobufStatus(source *pb.GpuStatus) GPUStatus
88+
89+
// ToProtobufStatus converts a GPUStatus object into a protobuf GpuStatus message.
90+
//
91+
// goverter:ignore state sizeCache unknownFields
92+
ToProtobufStatus(source GPUStatus) *pb.GpuStatus
93+
94+
// FromProtobufCondition converts a protobuf Condition message into a metav1.Condition object.
95+
//
96+
// goverter:ignore ObservedGeneration
97+
// Note: ObservedGeneration is specific to k8s and not found in the protobuf Condition message.
98+
FromProtobufCondition(source *pb.Condition) metav1.Condition
99+
100+
// ToProtobufCondition converts a metav1.Condition object into a protobuf Condition message.
101+
//
102+
// goverter:ignore state sizeCache unknownFields
103+
ToProtobufCondition(source metav1.Condition) *pb.Condition
104+
}
105+
106+
// FromProtobufTypeMeta generates the standard TypeMeta for the root GPU resource.
107+
func FromProtobufTypeMeta(_ *pb.Gpu) metav1.TypeMeta {
108+
return metav1.TypeMeta{
109+
Kind: "GPU",
110+
APIVersion: SchemeGroupVersion.String(),
111+
}
112+
}
113+
114+
// FromProtobufListTypeMeta generates the standard TypeMeta for the GPUList resource.
115+
func FromProtobufListTypeMeta(_ *pb.GpuList) metav1.TypeMeta {
116+
return metav1.TypeMeta{
117+
Kind: "GPUList",
118+
APIVersion: SchemeGroupVersion.String(),
119+
}
120+
}
121+
122+
// FromProtobufTimestamp converts a protobuf Timestamp message to a metav1.Time.
123+
func FromProtobufTimestamp(source *timestamppb.Timestamp) metav1.Time {
124+
if source == nil {
125+
return metav1.Time{}
126+
}
127+
return metav1.NewTime(source.AsTime())
128+
}
129+
130+
// ToProtobufTimestamp converts a metav1.Time to a protobuf Timestamp message.
131+
func ToProtobufTimestamp(source metav1.Time) *timestamppb.Timestamp {
132+
return timestamppb.New(source.Time)
133+
}

api/device/v1alpha1/doc.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// +groupName=device.nvidia.com
16+
17+
// Package v1alpha1 contains the API Schema definitions for the device.nvidia.com v1alpha1 API group.
18+
// This package includes types for GPU resources and conversions between Kubernetes and protobuf representations.
19+
package v1alpha1

0 commit comments

Comments
 (0)