Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
eae4c99
Add inferencepool_lifecycle test.
SinaChavoshi May 1, 2025
404f522
Resolve setup issues and enable InferencePool test
SinaChavoshi May 2, 2025
d396ecb
correct Lint error Multiplication of durations
SinaChavoshi May 5, 2025
0455c47
Fix missing containerPort, is missing
SinaChavoshi May 5, 2025
b531e66
change gateway name from "gateway-conformance-app" to "conformance-ga…
SinaChavoshi May 5, 2025
5510e69
clarify why K8s types are needed.
SinaChavoshi May 5, 2025
04c9798
Update conformance/conformance.go
SinaChavoshi May 5, 2025
9427f31
Update conformance/conformance.go
SinaChavoshi May 5, 2025
093d3ad
remove for loop when adding SupportedFeatures
SinaChavoshi May 5, 2025
e3c8381
remove exessive logging
SinaChavoshi May 5, 2025
c9a8f77
Update conformance/conformance.go
SinaChavoshi May 5, 2025
5ac19ff
move excess debug logs behind debug flag.
SinaChavoshi May 5, 2025
3b8df3b
remove CONFORMANCE.GO prefix from logs.
SinaChavoshi May 5, 2025
bde61b8
change the pull logic and use default value from GatewayMustHaveAddress
SinaChavoshi May 5, 2025
57d68fd
fix mt.Sprintf can be replaced with string concatenation
SinaChavoshi May 6, 2025
fa93b15
add a function for logDebug
SinaChavoshi May 7, 2025
06f29cc
factor out ensureGatewayAvailableAndReady
SinaChavoshi May 7, 2025
da2e9ef
removed todo comment in helper.go
SinaChavoshi May 7, 2025
5e1d8e9
remove CONFORMANCE.GO from log
SinaChavoshi May 7, 2025
d8577b0
error messages, should not be capitalized or end with punctuation
SinaChavoshi May 9, 2025
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
117 changes: 84 additions & 33 deletions conformance/conformance.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,36 @@ limitations under the License.
package conformance

import (
"context"
"fmt"
"io/fs"
"os"
"testing"
"time"

"github.com/stretchr/testify/require"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
clientset "k8s.io/client-go/kubernetes"
clientsetscheme "k8s.io/client-go/kubernetes/scheme"

// Import runtime package for scheme creation
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/config"
k8sconfig "sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/yaml"

// Import necessary types and utilities from the core Gateway API conformance suite.
// Assumes sigs.k8s.io/gateway-api is a dependency in the go.mod.
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" // Import core Gateway API types
confapis "sigs.k8s.io/gateway-api/conformance/apis/v1" // Report struct definition
confconfig "sigs.k8s.io/gateway-api/conformance/utils/config"
confflags "sigs.k8s.io/gateway-api/conformance/utils/flags"
apikubernetes "sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
confsuite "sigs.k8s.io/gateway-api/conformance/utils/suite"
"sigs.k8s.io/gateway-api/pkg/features" // Using core features definitions if applicable
"sigs.k8s.io/gateway-api/pkg/features"

// Import the test definitions package to access the ConformanceTests slice
"sigs.k8s.io/gateway-api-inference-extension/conformance/tests"
Expand All @@ -58,18 +64,23 @@ import (
inferencev1alpha2 "sigs.k8s.io/gateway-api-inference-extension/api/v1alpha2"
)

// Constants for the shared Gateway
const (
SharedGatewayName = "gateway-conformance-app" // Name of the Gateway in manifests.yaml
SharedGatewayNamespace = "gateway-conformance-infra" // Namespace of the Gateway
)

// GatewayLayerProfileName defines the name for the conformance profile that tests
// the Gateway API layer aspects of the Inference Extension (e.g., InferencePool, InferenceModel CRDs).
// Future profiles will cover EPP and ModelServer layers.
const GatewayLayerProfileName confsuite.ConformanceProfileName = "Gateway"

var InferenceCoreFeatures = sets.New[features.FeatureName]() // Placeholder - Populate with actual features specific to this profile or manage features per profile
// InferenceCoreFeatures defines the core features that implementations
// of the "Gateway" profile for the Inference Extension MUST support.
var InferenceCoreFeatures = sets.New(
features.SupportGateway, // This is needed to ensure manifest gets applied during setup.
)

// GatewayLayerProfile defines the conformance profile for the Gateway API layer
// of the Inference Extension.
// In future iterations, we will add constants and ConformanceProfile structs for
// EPPProfileName ("EPP") and ModelServerProfileName ("ModelServer")
// to cover their respective conformance layers.
var GatewayLayerProfile = confsuite.ConformanceProfile{
Name: GatewayLayerProfileName,
CoreFeatures: InferenceCoreFeatures,
Expand All @@ -80,26 +91,24 @@ var GatewayLayerProfile = confsuite.ConformanceProfile{
func DefaultOptions(t *testing.T) confsuite.ConformanceOptions {
t.Helper()

cfg, err := config.GetConfig()
cfg, err := k8sconfig.GetConfig()
require.NoError(t, err, "error loading Kubernetes config")

// Initialize client options. The scheme must include Gateway API types
// and the Inference Extension types.
clientOptions := client.Options{}
scheme := clientOptions.Scheme
if scheme == nil {
// If default options don't provide a scheme, create one using runtime.NewScheme().
scheme = runtime.NewScheme()
clientOptions.Scheme = scheme
}
scheme := runtime.NewScheme()

t.Log("Registering API types with scheme...")
// Add core Kubernetes types (like Secret, Service, etc.) to the scheme
require.NoError(t, clientsetscheme.AddToScheme(scheme), "Failed to add core Kubernetes types to scheme") // CORE FIX
// Add Gateway API types
require.NoError(t, gatewayv1.Install(scheme), "Failed to install gatewayv1 types into scheme")
// Add APIExtensions types (for CRDs)
require.NoError(t, apiextensionsv1.AddToScheme(scheme), "Failed to add apiextensionsv1 types to scheme")

// Register necessary API Types
require.NoError(t, gatewayv1.Install(scheme)) // Add core Gateway API types
// Add the Inference Extension API types to the scheme using the correct import alias
require.NoError(t, inferencev1alpha2.Install(scheme))
require.NoError(t, apiextensionsv1.AddToScheme(scheme)) // Needed for CRD checks
// Register Inference Extension API types
t.Logf("Attempting to install inferencev1alpha2 types (like InferencePool) into scheme from package: %s", inferencev1alpha2.GroupName)
require.NoError(t, inferencev1alpha2.Install(scheme), "Failed to install inferencev1alpha2 types into scheme.")

// Create the Kubernetes clients
clientOptions := client.Options{Scheme: scheme}
c, err := client.New(cfg, clientOptions)
require.NoError(t, err, "error initializing Kubernetes client")
cs, err := clientset.NewForConfig(cfg)
Expand All @@ -124,23 +133,27 @@ func DefaultOptions(t *testing.T) confsuite.ConformanceOptions {
inferenceExtensionVersion := "v0.3.0"
_ = inferenceExtensionVersion // Avoid unused variable error until implemented

// Create ConformanceOptions
baseManifestsValue := "resources/manifests/manifests.yaml"
t.Logf("Explicitly setting BaseManifests path to: %q", baseManifestsValue)

opts := confsuite.ConformanceOptions{
Client: c,
ClientOptions: clientOptions,
Clientset: cs,
RestConfig: cfg,
GatewayClassName: *confflags.GatewayClassName,
BaseManifests: baseManifestsValue,
Debug: *confflags.ShowDebug,
CleanupBaseResources: *confflags.CleanupBaseResources,
SupportedFeatures: sets.New[features.FeatureName](), // Initialize empty, will be populated below
SupportedFeatures: sets.New[features.FeatureName](),
TimeoutConfig: confconfig.DefaultTimeoutConfig(),
SkipTests: skipTests,
ExemptFeatures: exemptFeatures,
RunTest: *confflags.RunTest,
Mode: *confflags.Mode,
Implementation: implementation,
ConformanceProfiles: conformanceProfiles,
ManifestFS: []fs.FS{&Manifests}, // Assumes embed.go defines `Manifests`
ManifestFS: []fs.FS{&Manifests},
ReportOutputPath: *confflags.ReportOutput,
SkipProvisionalTests: *confflags.SkipProvisionalTests,
// TODO: Add the inference extension specific fields to ConformanceOptions struct if needed,
Expand All @@ -152,6 +165,7 @@ func DefaultOptions(t *testing.T) confsuite.ConformanceOptions {
// Populate SupportedFeatures based on the GatewayLayerProfile.
// Since all features are mandatory for this profile, add all defined core features.
if opts.ConformanceProfiles.Has(GatewayLayerProfileName) {
t.Logf("CONFORMANCE.GO: Populating SupportedFeatures from GatewayLayerProfile.CoreFeatures (%v)", GatewayLayerProfile.CoreFeatures.UnsortedList())
for feature := range GatewayLayerProfile.CoreFeatures {
opts.SupportedFeatures.Insert(feature)
}
Expand All @@ -161,6 +175,7 @@ func DefaultOptions(t *testing.T) confsuite.ConformanceOptions {
for feature := range opts.ExemptFeatures {
opts.SupportedFeatures.Delete(feature)
}
t.Logf("CONFORMANCE.GO: Final opts.SupportedFeatures: %v", opts.SupportedFeatures.UnsortedList())

return opts
}
Expand All @@ -173,6 +188,7 @@ func RunConformance(t *testing.T) {
// RunConformanceWithOptions runs the Inference Extension conformance tests with specific options.
func RunConformanceWithOptions(t *testing.T, opts confsuite.ConformanceOptions) {
t.Logf("Running Inference Extension conformance tests with GatewayClass %s", opts.GatewayClassName)
t.Logf("CONFORMANCE.GO RunConformanceWithOptions: BaseManifests path being used by opts: %q", opts.BaseManifests)

// Register the GatewayLayerProfile with the suite runner.
// In the future, other profiles (EPP, ModelServer) will also be registered here,
Expand All @@ -183,13 +199,48 @@ func RunConformanceWithOptions(t *testing.T, opts confsuite.ConformanceOptions)
cSuite, err := confsuite.NewConformanceTestSuite(opts)
require.NoError(t, err, "error initializing conformance suite")

t.Log("Setting up Inference Extension conformance tests")
// Setup requires the list of tests, which is populated by the init() functions
// triggered by the blank imports at the top of this file.
t.Log("Setting up Inference Extension conformance tests (applying base manifests via cSuite.Setup)")
cSuite.Setup(t, tests.ConformanceTests)

t.Log("Running Inference Extension conformance tests")
// Run the tests.
// TODO: Move gateway setup validation to a helper method.
t.Logf("CONFORMANCE.GO: Attempting to fetch Gateway %s/%s after cSuite.Setup().", SharedGatewayNamespace, SharedGatewayName)
sharedGwNN := types.NamespacedName{Name: SharedGatewayName, Namespace: SharedGatewayNamespace}
t.Logf("Attempting to directly fetch Gateway %s/%s after cSuite.Setup()", sharedGwNN.Namespace, sharedGwNN.Name)
gw := &gatewayv1.Gateway{}
var getErr error
// TODO: Confirm what is a reasonable wait time here.
maxRetries := 10
for i := 0; i < maxRetries; i++ {
getErr = cSuite.Client.Get(context.TODO(), sharedGwNN, gw)
if getErr == nil {
t.Logf("CONFORMANCE.GO: Successfully fetched Gateway %s/%s. Spec.GatewayClassName: %s",
sharedGwNN.Namespace, sharedGwNN.Name, gw.Spec.GatewayClassName)
break
}
if apierrors.IsNotFound(getErr) {
t.Logf("CONFORMANCE.GO: Gateway %s/%s not found (attempt %d/%d). Retrying in 1s...", sharedGwNN.Namespace, sharedGwNN.Name, i+1, maxRetries)
time.Sleep(1 * time.Second)
} else {
t.Logf("CONFORMANCE.GO: Error fetching Gateway %s/%s (attempt %d/%d): %v.", sharedGwNN.Namespace, sharedGwNN.Name, i+1, maxRetries, getErr)
break
}
}
require.NoErrorf(t, getErr, "Failed to fetch Gateway %s/%s after cSuite.Setup(), even after retries. It should have been created if the Applier worked with the correct manifest.", sharedGwNN.Namespace, sharedGwNN.Name)

t.Logf("CONFORMANCE.GO: Waiting for shared Gateway %s/%s to be ready", SharedGatewayNamespace, SharedGatewayName)
apikubernetes.GatewayMustHaveCondition(t, cSuite.Client, cSuite.TimeoutConfig, sharedGwNN, metav1.Condition{
Type: string(gatewayv1.GatewayConditionAccepted),
Status: metav1.ConditionTrue,
})
apikubernetes.GatewayMustHaveCondition(t, cSuite.Client, cSuite.TimeoutConfig, sharedGwNN, metav1.Condition{
Type: string(gatewayv1.GatewayConditionProgrammed),
Status: metav1.ConditionTrue,
})
_, err = apikubernetes.WaitForGatewayAddress(t, cSuite.Client, cSuite.TimeoutConfig, apikubernetes.NewGatewayRef(sharedGwNN))
require.NoErrorf(t, err, "shared gateway %s/%s did not get an address", SharedGatewayNamespace, SharedGatewayName)
t.Logf("CONFORMANCE.GO: Shared Gateway %s/%s is ready.", SharedGatewayNamespace, SharedGatewayName)

t.Log("Running Inference Extension conformance tests against all registered tests")
err = cSuite.Run(t, tests.ConformanceTests)
require.NoError(t, err, "error running conformance tests")

Expand Down
2 changes: 1 addition & 1 deletion conformance/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ import "embed"
// Manifests embeds the contents of the conformance/resources directory making
// the YAML files within them available to the test suite at runtime.
//
//go:embed resources/* tests/*
//go:embed resources tests/*
var Manifests embed.FS
18 changes: 13 additions & 5 deletions conformance/resources/manifests/manifests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,23 @@ metadata:
labels:
gateway-conformance: backend

---
# Namespace for simple web server backends. This is expected by
# the upstream conformance suite's Setup method.
apiVersion: v1
kind: Namespace
metadata:
name: gateway-conformance-web-backend
labels:
gateway-conformance: web-backend

---
# A basic Gateway resource that allows HTTPRoutes from the same namespace.
# Tests can use this as a parent reference for routes that target InferencePools.
# Using a simple echo server instead of an actual model server to simplify the test
# execution, this design may need to be revised based on the test case needs.
apiVersion: gateway.networking.k8s.io/v1 # Using v1 as per latest Gateway API standard
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: same-namespace
name: gateway-conformance-app
namespace: gateway-conformance-infra
spec:
# The conformance suite runner will replace this placeholder
Expand All @@ -42,7 +50,7 @@ spec:
protocol: HTTP
allowedRoutes:
namespaces:
from: Same # Restrict to same namespace initially for simplicity
from: All
kinds:
# Allows HTTPRoutes to attach, which can then reference InferencePools.
- group: gateway.networking.k8s.io
Expand Down
76 changes: 71 additions & 5 deletions conformance/tests/basic/inferencepool_accepted.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,46 @@
# Basic InferencePool for acceptance testing.
# This manifest defines the minimal required fields to create a valid
# InferencePool resource, which the InferencePoolAccepted test will use
# to verify that the controller recognizes and accepts the resource.
# This manifest defines the minimal required fields to create valid
# InferencePool and HTTPRoute resources, which the InferencePoolAccepted
# test will use to verify that the controller recognizes and accepts the resource.

# --- Minimal Backend Deployment (using agnhost echo server) ---
# This Deployment provides Pods for the InferencePool to select.
apiVersion: apps/v1
kind: Deployment
metadata:
name: infra-backend-v1-deployment
namespace: gateway-conformance-app-backend
labels:
app: infra-backend-v1
spec:
replicas: 1
selector:
matchLabels:
app: infra-backend-v1
template:
metadata:
labels:
app: infra-backend-v1
spec:
containers:
- name: agnhost-echo
image: k8s.gcr.io/e2e-test-images/agnhost:2.39
args:
- serve-hostname
- --http-port=8080
ports:
- name: http
containerPort:
readinessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
failureThreshold: 2

---
# --- InferencePool Definition ---
apiVersion: inference.networking.x-k8s.io/v1alpha2
kind: InferencePool
metadata:
Expand All @@ -18,10 +56,38 @@ spec:
app: "infra-backend-v1"

# --- Target Port (Required) ---
# The port the model server container listens on.
targetPortNumber: 3000
# The port the model server container (agnhost in this case) listens on.
targetPortNumber: 8080 # Matches agnhost's http-port

# --- Extension Reference ---
# GKE-specific configuration reference.
extensionRef:
# group: "" # Optional
# kind: Service # Optional
name: infra-backend-v1-epp

---
# --- HTTPRoute Definition ---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httproute-for-inferencepool-accepted
namespace: gateway-conformance-app-backend
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: gateway-conformance-app # Name of the shared Gateway from maniffests.yaml
namespace: gateway-conformance-infra # Namespace of the shared Gateway
sectionName: http
rules:
- backendRefs:
- group: inference.networking.x-k8s.io # InferencePool API group
kind: InferencePool
name: inferencepool-basic-accepted # Name of the InferencePool this route points to
# namespace: gateway-conformance-app-backend - is omitted since it is in the same namespace as HTTPRoute
port: 8080 # Matching the InferencePool's targetPortNumber
matches:
- path:
type: PathPrefix
value: /accepted-pool-test
Loading