Skip to content

Commit bc160e9

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

74 files changed

Lines changed: 6207 additions & 144 deletions

Some content is hidden

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

.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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ go_tools:
4646
setup_envtest: 'latest'
4747
goimports: 'v0.30.0'
4848
crane: 'v0.20.2'
49+
deepcopy_gen: 'v0.34.2'
50+
goverter: 'v1.9.2'
51+
kubernetes_code_gen: 'v0.34.2'
4952

5053
# Protocol Buffers / gRPC
5154
protobuf:

api/Makefile

Lines changed: 117 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,29 @@
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+
# Common
37+
BOILERPLATE := hack/boilerplate.go.txt
38+
1939
# Directories
20-
PROTO_DIR := proto
21-
GEN_DIR := gen/go
40+
PROTO_DIR := proto
41+
GEN_DIR := gen/go
2242

2343
# Shell setup
2444
SHELL = /usr/bin/env bash -o pipefail
@@ -29,9 +49,17 @@ LOCALBIN ?= $(shell pwd)/bin
2949
$(LOCALBIN):
3050
mkdir -p $(LOCALBIN)
3151

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

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

4371
VERSIONS_FILE := ../.versions.yaml
4472

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))
73+
PROTOC_GEN_GO_VERSION := $(shell $(YQ) '.protobuf.protoc_gen_go' $(VERSIONS_FILE))
74+
PROTOC_GEN_GO_GRPC_VERSION := $(shell $(YQ) '.protobuf.protoc_gen_go_grpc' $(VERSIONS_FILE))
75+
KUBERNETES_CODEGEN_VERSION := $(shell $(YQ) '.go_tools.kubernetes_code_gen' $(VERSIONS_FILE))
76+
GOVERTER_VERSION := $(shell $(YQ) '.go_tools.goverter' $(VERSIONS_FILE))
4777

4878
# ==============================================================================
4979
# Targets
5080
# ==============================================================================
5181

5282
.PHONY: all
53-
all: protos-generate build
83+
all: code-gen test build ## Run code generation, compile all code, and execute tests.
5484

5585
##@ General
5686

@@ -60,42 +90,100 @@ help: ## Display this help.
6090

6191
##@ Development
6292

93+
.PHONY: code-gen
94+
code-gen: install-tools protos-generate deepcopy-gen conversion-gen ## Run all code generation targets.
95+
@echo "Synchronizing module dependencies..."
96+
go mod tidy
97+
6398
.PHONY: build
64-
build:
99+
build: code-gen ## Compile all Go code after generation to verify type safety.
100+
@echo "Compiling..."
65101
go build -v ./...
66102

103+
.PHONY: test
104+
test: code-gen ## Run unit tests and generate coverage after code generation.
105+
@echo "Testing..."
106+
go test -v $$(go list ./... | grep -v /gen/) -coverprofile cover.out
107+
67108
.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..."
109+
protos-generate: $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) clean-protos ## Generate Protobuf Go bindings.
110+
@echo "Generating protobuf bindings for $(API_VERSIONS_COMMA)..."
70111
@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..."
112+
@$(foreach version,$(API_VERSIONS), \
113+
cd $(PROTO_DIR) && protoc \
114+
-I . \
115+
--plugin="protoc-gen-go=$(PROTOC_GEN_GO)" \
116+
--plugin="protoc-gen-go-grpc=$(PROTOC_GEN_GO_GRPC)" \
117+
--go_out="../$(GEN_DIR)" \
118+
--go_opt=paths="source_relative" \
119+
--go-grpc_out="../$(GEN_DIR)" \
120+
--go-grpc_opt=paths="source_relative" \
121+
$(version)/*.proto; \
122+
)
123+
124+
.PHONY: deepcopy-gen
125+
deepcopy-gen: $(DEEPCOPY_GEN) clean-deepcopy ## Generate deepcopy code.
126+
@echo "Generating deepcopy code for $(API_VERSIONS_COMMA)..."
127+
$(DEEPCOPY_GEN) \
128+
--bounding-dirs="$(API_PKGS_COMMA)" \
129+
--go-header-file="$(BOILERPLATE)" \
130+
--output-file="zz_generated.deepcopy.go" \
131+
$(API_PKGS)
132+
133+
.PHONY: conversion-gen
134+
conversion-gen: $(GOVERTER) clean-conversion ## Generate conversion code..
135+
@echo "Generating conversion code for $(API_VERSIONS_COMMA)..."
136+
@$(foreach version,$(API_VERSIONS), \
137+
$(GOVERTER) gen ./$(version); \
138+
)
139+
140+
.PHONY: clean
141+
clean: clean-protos clean-conversion clean-deepcopy ## Remove all generated code.
142+
143+
.PHONY: purge
144+
purge: clean clean-bin ## Remove all generated code and downloaded tools.
145+
146+
.PHONY: clean-protos
147+
clean-protos: ## Remove generated protobuf Go bindings.
148+
@echo "Removing generated protobuf Go bindings..."
88149
rm -rf $(GEN_DIR)
89-
@echo "Done."
150+
151+
.PHONY: clean-deepcopy
152+
clean-deepcopy: ## Remove generated deepcopy code.
153+
@echo "Removing generated deepcopy code..."
154+
find . -name "zz_generated.deepcopy.go" -delete
155+
156+
.PHONY: clean-conversion
157+
clean-conversion: ## Remove generated conversion code.
158+
@echo "Removing generated conversion code..."
159+
find . -name "zz_generated.conversion.go" -delete
160+
161+
.PHONY: clean-bin
162+
clean-bin: ## Remove all downloaded tools in localbin.
163+
@echo "Removing downloaded build tools from $(LOCALBIN)..."
164+
rm -rf $(LOCALBIN)
90165

91166
##@ Build Dependencies
92167

168+
.PHONY: install-tools
169+
install-tools: install-tools-echo $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_GRPC) $(DEEPCOPY_GEN) $(GOVERTER) ## Ensure all necessary build tools are downloaded and installed.
170+
171+
.PHONY: install-tools-echo
172+
install-tools-echo:
173+
@echo "Installing dependencies..."
174+
93175
$(PROTOC_GEN_GO):
94176
$(call go-install-tool,$(PROTOC_GEN_GO),google.golang.org/protobuf/cmd/protoc-gen-go,$(PROTOC_GEN_GO_VERSION))
95177

96178
$(PROTOC_GEN_GO_GRPC):
97179
$(call go-install-tool,$(PROTOC_GEN_GO_GRPC),google.golang.org/grpc/cmd/protoc-gen-go-grpc,$(PROTOC_GEN_GO_GRPC_VERSION))
98180

181+
$(DEEPCOPY_GEN):
182+
$(call go-install-tool,$(DEEPCOPY_GEN),k8s.io/code-generator/cmd/deepcopy-gen,$(KUBERNETES_CODEGEN_VERSION))
183+
184+
$(GOVERTER):
185+
$(call go-install-tool,$(GOVERTER),github.com/jmattheis/goverter/cmd/goverter,$(GOVERTER_VERSION))
186+
99187
# go-install-tool macro
100188
# $1 - target path with name of binary
101189
# $2 - package url which can be installed
@@ -107,7 +195,7 @@ mkdir -p $(LOCALBIN); \
107195
package=$(2)@$(3) ;\
108196
echo "Downloading $${package}" ;\
109197
rm -f $(1) || true ;\
110-
GOBIN=$(LOCALBIN) go install $${package} ;\
198+
GOBIN=$(LOCALBIN) GOTOOLCHAIN=$(GO_VERSION) go install $${package} ;\
111199
mv $(1) $(1)-$(3) ;\
112200
} ;\
113201
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.Version,
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.Version,
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)