Skip to content

Commit c381190

Browse files
committed
Add unit tests for request body
1 parent 855436e commit c381190

39 files changed

+1266
-213
lines changed

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ COPY cmd ./cmd
1919
COPY pkg ./pkg
2020
COPY internal ./internal
2121
COPY api ./api
22+
COPY .git ./.git
2223
WORKDIR /src/cmd/epp
23-
RUN go build -o /epp
24+
RUN go build -buildvcs=true -o /epp
2425

2526
## Multistage deploy
2627
FROM ${BASE_IMAGE}

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ vet: ## Run go vet against code.
121121

122122
.PHONY: test
123123
test: manifests generate fmt vet envtest image-build ## Run tests.
124-
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -race -coverprofile cover.out
124+
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e | grep -v /conformance) -race -coverprofile cover.out
125125

126126
.PHONY: test-unit
127127
test-unit: ## Run unit tests.

cmd/epp/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
backendmetrics "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/backend/metrics"
4242
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/datastore"
4343
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/metrics"
44+
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/scheduling"
4445
runserver "sigs.k8s.io/gateway-api-inference-extension/pkg/epp/server"
4546
"sigs.k8s.io/gateway-api-inference-extension/pkg/epp/util/logging"
4647
)
@@ -169,6 +170,7 @@ func run() error {
169170

170171
datastore := datastore.NewDatastore(ctx, pmf)
171172

173+
scheduler := scheduling.NewScheduler(datastore)
172174
serverRunner := &runserver.ExtProcServerRunner{
173175
GrpcPort: *grpcPort,
174176
DestinationEndpointHintMetadataNamespace: *destinationEndpointHintMetadataNamespace,
@@ -178,6 +180,7 @@ func run() error {
178180
SecureServing: *secureServing,
179181
CertPath: *certPath,
180182
RefreshPrometheusMetricsInterval: *refreshPrometheusMetricsInterval,
183+
Scheduler: scheduler,
181184
}
182185
if err := serverRunner.SetupWithManager(ctx, mgr); err != nil {
183186
setupLog.Error(err, "Failed to setup ext-proc controllers")
@@ -247,6 +250,8 @@ func registerHealthServer(mgr manager.Manager, logger logr.Logger, ds datastore.
247250
func registerMetricsHandler(mgr manager.Manager, port int, cfg *rest.Config) error {
248251
metrics.Register()
249252

253+
metrics.RecordInferenceExtensionInfo()
254+
250255
// Init HTTP server.
251256
h, err := metricsHandlerWithAuthenticationAndAuthorization(cfg)
252257
if err != nil {

conformance/conformance.go

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package conformance contains the core setup and execution logic
18+
// for the Gateway API Inference Extension conformance test suite.
19+
package conformance
20+
21+
import (
22+
"fmt"
23+
"io/fs"
24+
"os"
25+
"testing"
26+
27+
"github.com/stretchr/testify/require"
28+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
29+
clientset "k8s.io/client-go/kubernetes"
30+
31+
// Import runtime package for scheme creation
32+
"k8s.io/apimachinery/pkg/runtime"
33+
"k8s.io/apimachinery/pkg/util/sets"
34+
"sigs.k8s.io/controller-runtime/pkg/client"
35+
"sigs.k8s.io/controller-runtime/pkg/client/config"
36+
"sigs.k8s.io/yaml"
37+
38+
// Import necessary types and utilities from the core Gateway API conformance suite.
39+
// Assumes sigs.k8s.io/gateway-api is a dependency in the go.mod.
40+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" // Import core Gateway API types
41+
confapis "sigs.k8s.io/gateway-api/conformance/apis/v1" // Report struct definition
42+
confconfig "sigs.k8s.io/gateway-api/conformance/utils/config"
43+
confflags "sigs.k8s.io/gateway-api/conformance/utils/flags"
44+
confsuite "sigs.k8s.io/gateway-api/conformance/utils/suite"
45+
"sigs.k8s.io/gateway-api/pkg/features" // Using core features definitions if applicable
46+
47+
// Import the test definitions package to access the ConformanceTests slice
48+
"sigs.k8s.io/gateway-api-inference-extension/conformance/tests"
49+
50+
// Import test packages using blank identifier
51+
// This triggers the init() functions in these packages, which register the tests
52+
// by appending them to the tests.ConformanceTests slice.
53+
_ "sigs.k8s.io/gateway-api-inference-extension/conformance/tests/basic"
54+
// TODO: Add blank imports for other test categories as they are created.
55+
// _ "sigs.k8s.io/gateway-api-inference-extension/conformance/tests/model_routing"
56+
57+
// Import the Inference Extension API types
58+
inferencev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/api/v1alpha2"
59+
)
60+
61+
// GatewayLayerProfileName defines the name for the conformance profile that tests
62+
// the Gateway API layer aspects of the Inference Extension (e.g., InferencePool, InferenceModel CRDs).
63+
// Future profiles will cover EPP and ModelServer layers.
64+
const GatewayLayerProfileName confsuite.ConformanceProfileName = "Gateway"
65+
66+
var InferenceCoreFeatures = sets.New[features.FeatureName]() // Placeholder - Populate with actual features specific to this profile or manage features per profile
67+
68+
// GatewayLayerProfile defines the conformance profile for the Gateway API layer
69+
// of the Inference Extension.
70+
// In future iterations, we will add constants and ConformanceProfile structs for
71+
// EPPProfileName ("EPP") and ModelServerProfileName ("ModelServer")
72+
// to cover their respective conformance layers.
73+
var GatewayLayerProfile = confsuite.ConformanceProfile{
74+
Name: GatewayLayerProfileName,
75+
CoreFeatures: InferenceCoreFeatures,
76+
}
77+
78+
// DefaultOptions parses command line flags and sets up the suite options.
79+
// Adapted from the core Gateway API conformance suite.
80+
func DefaultOptions(t *testing.T) confsuite.ConformanceOptions {
81+
t.Helper()
82+
83+
cfg, err := config.GetConfig()
84+
require.NoError(t, err, "error loading Kubernetes config")
85+
86+
// Initialize client options. The scheme must include Gateway API types
87+
// and the Inference Extension types.
88+
clientOptions := client.Options{}
89+
scheme := clientOptions.Scheme
90+
if scheme == nil {
91+
// If default options don't provide a scheme, create one using runtime.NewScheme().
92+
scheme = runtime.NewScheme()
93+
clientOptions.Scheme = scheme
94+
}
95+
96+
// Register necessary API Types
97+
require.NoError(t, gatewayv1.Install(scheme)) // Add core Gateway API types
98+
// Add the Inference Extension API types to the scheme using the correct import alias
99+
require.NoError(t, inferencev1alpha2.Install(scheme))
100+
require.NoError(t, apiextensionsv1.AddToScheme(scheme)) // Needed for CRD checks
101+
102+
// Create the Kubernetes clients
103+
c, err := client.New(cfg, clientOptions)
104+
require.NoError(t, err, "error initializing Kubernetes client")
105+
cs, err := clientset.NewForConfig(cfg)
106+
require.NoError(t, err, "error initializing Kubernetes clientset")
107+
108+
exemptFeatures := confsuite.ParseSupportedFeatures(*confflags.ExemptFeatures)
109+
skipTests := confsuite.ParseSkipTests(*confflags.SkipTests)
110+
// Initially, run the GatewayLayerProfile. This will expand as other profiles
111+
// (EPP, ModelServer) are added and can be selected via flags in future iterations.
112+
conformanceProfiles := sets.New(GatewayLayerProfileName)
113+
114+
// Implementation details from flags
115+
implementation := confsuite.ParseImplementation(
116+
*confflags.ImplementationOrganization,
117+
*confflags.ImplementationProject,
118+
*confflags.ImplementationURL,
119+
*confflags.ImplementationVersion,
120+
*confflags.ImplementationContact,
121+
)
122+
123+
// Inference Extension Specific Report Fields
124+
inferenceExtensionVersion := "v0.3.0"
125+
_ = inferenceExtensionVersion // Avoid unused variable error until implemented
126+
127+
// Create ConformanceOptions
128+
opts := confsuite.ConformanceOptions{
129+
Client: c,
130+
Clientset: cs,
131+
RestConfig: cfg,
132+
GatewayClassName: *confflags.GatewayClassName,
133+
Debug: *confflags.ShowDebug,
134+
CleanupBaseResources: *confflags.CleanupBaseResources,
135+
SupportedFeatures: sets.New[features.FeatureName](), // Initialize empty, will be populated below
136+
TimeoutConfig: confconfig.DefaultTimeoutConfig(),
137+
SkipTests: skipTests,
138+
ExemptFeatures: exemptFeatures,
139+
RunTest: *confflags.RunTest,
140+
Mode: *confflags.Mode,
141+
Implementation: implementation,
142+
ConformanceProfiles: conformanceProfiles,
143+
ManifestFS: []fs.FS{&Manifests}, // Assumes embed.go defines `Manifests`
144+
ReportOutputPath: *confflags.ReportOutput,
145+
SkipProvisionalTests: *confflags.SkipProvisionalTests,
146+
// TODO: Add the inference extension specific fields to ConformanceOptions struct if needed,
147+
// or handle them during report generation.
148+
// GatewayAPIInferenceExtensionChannel: inferenceExtensionChannel,
149+
// GatewayAPIInferenceExtensionVersion: inferenceExtensionVersion,
150+
}
151+
152+
// Populate SupportedFeatures based on the GatewayLayerProfile.
153+
// Since all features are mandatory for this profile, add all defined core features.
154+
if opts.ConformanceProfiles.Has(GatewayLayerProfileName) {
155+
for feature := range GatewayLayerProfile.CoreFeatures {
156+
opts.SupportedFeatures.Insert(feature)
157+
}
158+
}
159+
160+
// Remove any features explicitly exempted via flags.
161+
for feature := range opts.ExemptFeatures {
162+
opts.SupportedFeatures.Delete(feature)
163+
}
164+
165+
return opts
166+
}
167+
168+
// RunConformance runs the Inference Extension conformance tests using default options.
169+
func RunConformance(t *testing.T) {
170+
RunConformanceWithOptions(t, DefaultOptions(t))
171+
}
172+
173+
// RunConformanceWithOptions runs the Inference Extension conformance tests with specific options.
174+
func RunConformanceWithOptions(t *testing.T, opts confsuite.ConformanceOptions) {
175+
t.Logf("Running Inference Extension conformance tests with GatewayClass %s", opts.GatewayClassName)
176+
177+
// Register the GatewayLayerProfile with the suite runner.
178+
// In the future, other profiles (EPP, ModelServer) will also be registered here,
179+
// and the suite runner will execute tests based on the selected profiles.
180+
confsuite.RegisterConformanceProfile(GatewayLayerProfile)
181+
182+
// Initialize the test suite.
183+
cSuite, err := confsuite.NewConformanceTestSuite(opts)
184+
require.NoError(t, err, "error initializing conformance suite")
185+
186+
t.Log("Setting up Inference Extension conformance tests")
187+
// Setup requires the list of tests, which is populated by the init() functions
188+
// triggered by the blank imports at the top of this file.
189+
cSuite.Setup(t, tests.ConformanceTests)
190+
191+
t.Log("Running Inference Extension conformance tests")
192+
// Run the tests.
193+
err = cSuite.Run(t, tests.ConformanceTests)
194+
require.NoError(t, err, "error running conformance tests")
195+
196+
// Generate and write the report if requested.
197+
if opts.ReportOutputPath != "" {
198+
t.Log("Generating Inference Extension conformance report")
199+
report, err := cSuite.Report() // Use the existing report generation logic.
200+
require.NoError(t, err, "error generating conformance report")
201+
202+
// TODO: Modify the report struct here if channel, version need to be modified.
203+
// Example (requires adding fields to confapis.ConformanceReport):
204+
// report.GatewayAPIInferenceExtensionChannel = opts.GatewayAPIInferenceExtensionChannel
205+
// report.GatewayAPIInferenceExtensionVersion = opts.GatewayAPIInferenceExtensionVersion
206+
207+
err = writeReport(t.Logf, *report, opts.ReportOutputPath)
208+
require.NoError(t, err, "error writing conformance report")
209+
}
210+
}
211+
212+
// writeReport writes the generated conformance report to the specified output file or logs it.
213+
// Adapted from the core Gateway API suite.
214+
func writeReport(logf func(string, ...any), report confapis.ConformanceReport, output string) error {
215+
rawReport, err := yaml.Marshal(report)
216+
if err != nil {
217+
return fmt.Errorf("error marshaling report: %w", err)
218+
}
219+
220+
if output != "" {
221+
if err = os.WriteFile(output, rawReport, 0o600); err != nil {
222+
return fmt.Errorf("error writing report file %s: %w", output, err)
223+
}
224+
logf("Conformance report written to %s", output)
225+
} else {
226+
// Log the report YAML to stdout if no output file is specified.
227+
logf("Conformance report:\n%s", string(rawReport))
228+
}
229+
return nil
230+
}

conformance/conformance_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package conformance
18+
19+
import (
20+
"testing"
21+
)
22+
23+
// TestConformance is the top-level function that runs the conformance tests.
24+
// It calls the RunConformance function which sets up the suite and executes
25+
// the registered tests.
26+
func TestConformance(t *testing.T) {
27+
// RunConformance is defined in conformance.go
28+
RunConformance(t)
29+
}

conformance/embed.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package conformance
18+
19+
import "embed"
20+
21+
// Manifests embeds the contents of the conformance/resources directory making
22+
// the YAML files within them available to the test suite at runtime.
23+
//
24+
//go:embed resources/* tests/*
25+
var Manifests embed.FS

0 commit comments

Comments
 (0)