Skip to content

Commit eda00c9

Browse files
committed
Addl changes after e2e testing
Signed-off-by: Daneyon Hansen <[email protected]>
1 parent 0458f97 commit eda00c9

File tree

10 files changed

+217
-93
lines changed

10 files changed

+217
-93
lines changed

install/helm/kgateway/templates/role.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,47 @@ rules:
126126
- get
127127
- list
128128
- watch
129+
- apiGroups:
130+
- inference.networking.x-k8s.io
131+
resources:
132+
- inferencemodels
133+
verbs:
134+
- get
135+
- list
136+
- watch
137+
- apiGroups:
138+
- inference.networking.x-k8s.io
139+
resources:
140+
- inferencepools
141+
verbs:
142+
- get
143+
- list
144+
- watch
145+
- update
146+
- apiGroups:
147+
- rbac.authorization.k8s.io
148+
# TODO [danehans]: EPP should use Role and RoleBinding resources: https://github.com/kubernetes-sigs/gateway-api-inference-extension/issues/224
149+
resources:
150+
- clusterroles
151+
- clusterrolebindings
152+
verbs:
153+
- create
154+
- delete
155+
- get
156+
- list
157+
- patch
158+
- update
159+
- watch
160+
# TODO [danehans]: Unsure why the following rules are needed: https://github.com/kubernetes-sigs/gateway-api-inference-extension/issues/224
161+
- apiGroups:
162+
- authentication.k8s.io
163+
resources:
164+
- tokenreviews
165+
verbs:
166+
- create
167+
- apiGroups:
168+
- authorization.k8s.io
169+
resources:
170+
- subjectaccessreviews
171+
verbs:
172+
- create

internal/kgateway/controller/inferencepool_controller.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
1212

1313
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/deployer"
14+
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/wellknown"
1415
)
1516

1617
type inferencePoolReconciler struct {
@@ -29,11 +30,22 @@ func (r *inferencePoolReconciler) Reconcile(ctx context.Context, req ctrl.Reques
2930
}
3031

3132
if pool.GetDeletionTimestamp() != nil {
32-
// no need to do anything as we have owner refs, so children will be deleted
33-
log.Info("inferencepool deleted, no need for reconciling")
33+
// Remove the cluster-scoped resources and finalizer.
34+
if err := r.deployer.CleanupClusterScopedResources(ctx, pool); err != nil {
35+
return ctrl.Result{}, err
36+
}
37+
pool.Finalizers = removeString(pool.Finalizers, wellknown.InferencePoolFinalizer)
38+
if err := r.cli.Update(ctx, pool); err != nil {
39+
return ctrl.Result{}, err
40+
}
3441
return ctrl.Result{}, nil
3542
}
3643

44+
// Ensure the finalizer is present for the InferencePool.
45+
if err := r.deployer.EnsureFinalizer(ctx, pool); err != nil {
46+
return ctrl.Result{}, err
47+
}
48+
3749
// Use the registered index to list HTTPRoutes that reference this pool.
3850
var routeList gwv1.HTTPRouteList
3951
if err := r.cli.List(ctx, &routeList,
@@ -58,7 +70,7 @@ func (r *inferencePoolReconciler) Reconcile(ctx context.Context, req ctrl.Reques
5870
// TODO [danehans]: Manage inferencepool status conditions.
5971

6072
// Deploy the endpoint picker resources.
61-
log.Info("Deploying endpoint picker from InferencePool", "name", pool.Name, "namespace", pool.Namespace)
73+
log.Info("Deploying endpoint picker for InferencePool", "name", pool.Name, "namespace", pool.Namespace)
6274
err = r.deployer.DeployObjs(ctx, objs)
6375
if err != nil {
6476
return ctrl.Result{}, err
@@ -68,3 +80,14 @@ func (r *inferencePoolReconciler) Reconcile(ctx context.Context, req ctrl.Reques
6880

6981
return ctrl.Result{}, nil
7082
}
83+
84+
// removeString is a helper function to remove a string from a slice.
85+
func removeString(slice []string, s string) []string {
86+
var result []string
87+
for _, item := range slice {
88+
if item != s {
89+
result = append(result, item)
90+
}
91+
}
92+
return result
93+
}

internal/kgateway/controller/start.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import (
3434
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/utils/krtutil"
3535
"github.com/kgateway-dev/kgateway/v2/internal/kgateway/wellknown"
3636
"github.com/kgateway-dev/kgateway/v2/pkg/client/clientset/versioned"
37-
glooschemes "github.com/kgateway-dev/kgateway/v2/pkg/schemes"
37+
kgtwschemes "github.com/kgateway-dev/kgateway/v2/pkg/schemes"
3838
"github.com/kgateway-dev/kgateway/v2/pkg/utils/kubeutils"
3939
"github.com/kgateway-dev/kgateway/v2/pkg/utils/namespaces"
4040
)
@@ -102,7 +102,7 @@ func NewControllerBuilder(ctx context.Context, cfg StartConfig) (*ControllerBuil
102102
scheme := DefaultScheme()
103103

104104
// Extend the scheme if the TCPRoute CRD exists.
105-
if err := glooschemes.AddGatewayV1A2Scheme(cfg.RestConfig, scheme); err != nil {
105+
if err := kgtwschemes.AddGatewayV1A2Scheme(cfg.RestConfig, scheme); err != nil {
106106
return nil, err
107107
}
108108

@@ -146,11 +146,17 @@ func NewControllerBuilder(ctx context.Context, cfg StartConfig) (*ControllerBuil
146146
)
147147

148148
// Extend the scheme and add the EPP plugin if the InferencePool CRD exists.
149-
exists, err := glooschemes.AddInferExtV1A1Scheme(cfg.RestConfig, scheme)
149+
exists, err := kgtwschemes.AddInferExtV1A1Scheme(cfg.RestConfig, scheme)
150+
setupLog.Info("checking inference extension CRDs exist", "result", exists)
151+
150152
switch {
151153
case err != nil:
152154
return nil, err
153155
case exists:
156+
setupLog.Info("adding inference extension endpoint picker plugin")
157+
if cfg.ExtraPlugins == nil {
158+
cfg.ExtraPlugins = []extensionsplug.Plugin{}
159+
}
154160
cfg.ExtraPlugins = append(cfg.ExtraPlugins, endpointpicker.NewPlugin(ctx, commoncol))
155161
}
156162

internal/kgateway/deployer/deployer.go

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@ import (
88
"io"
99
"io/fs"
1010
"path/filepath"
11+
"slices"
1112

1213
"github.com/rotisserie/eris"
13-
"golang.org/x/exp/slices"
1414
"helm.sh/helm/v3/pkg/action"
1515
"helm.sh/helm/v3/pkg/chart"
1616
"helm.sh/helm/v3/pkg/chart/loader"
1717
"helm.sh/helm/v3/pkg/storage"
1818
"helm.sh/helm/v3/pkg/storage/driver"
1919
corev1 "k8s.io/api/core/v1"
20+
rbacv1 "k8s.io/api/rbac/v1"
2021
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2223
"k8s.io/apimachinery/pkg/runtime"
@@ -516,19 +517,26 @@ func (d *Deployer) GetEndpointPickerObjs(pool *infextv1a1.InferencePool) ([]clie
516517
return nil, fmt.Errorf("failed to render inference extension objects: %w", err)
517518
}
518519

519-
// Ensure that each rendered object has its namespace set.
520+
// Ensure that each namespaced rendered object has its namespace and ownerRef set.
520521
for _, obj := range objs {
521-
if obj.GetNamespace() == "" {
522-
obj.SetNamespace(pool.Namespace)
522+
gvk := obj.GetObjectKind().GroupVersionKind()
523+
if IsNamespaced(gvk) {
524+
if obj.GetNamespace() == "" {
525+
obj.SetNamespace(pool.Namespace)
526+
}
527+
obj.SetOwnerReferences([]metav1.OwnerReference{{
528+
APIVersion: pool.APIVersion,
529+
Kind: pool.Kind,
530+
Name: pool.Name,
531+
UID: pool.UID,
532+
Controller: ptr.To(true),
533+
}})
534+
} else {
535+
// TODO [danehans]: Not sure why a ns must be set for cluster-scoped objects:
536+
// failed to apply object rbac.authorization.k8s.io/v1, Kind=ClusterRoleBinding
537+
// vllm-llama2-7b-pool-endpoint-picker: Namespace parameter required.
538+
obj.SetNamespace("")
523539
}
524-
// Set owner references so that these objects are tied to the InferencePool.
525-
obj.SetOwnerReferences([]metav1.OwnerReference{{
526-
APIVersion: pool.APIVersion,
527-
Kind: pool.Kind,
528-
Name: pool.Name,
529-
UID: pool.UID,
530-
Controller: ptr.To(true),
531-
}})
532540
}
533541

534542
return objs, nil
@@ -545,6 +553,47 @@ func (d *Deployer) DeployObjs(ctx context.Context, objs []client.Object) error {
545553
return nil
546554
}
547555

556+
// EnsureFinalizer adds the InferencePool finalizer to the given pool if it’s not already present.
557+
func (d *Deployer) EnsureFinalizer(ctx context.Context, pool *infextv1a1.InferencePool) error {
558+
if slices.Contains(pool.Finalizers, wellknown.InferencePoolFinalizer) {
559+
return nil
560+
}
561+
pool.Finalizers = append(pool.Finalizers, wellknown.InferencePoolFinalizer)
562+
return d.cli.Update(ctx, pool)
563+
}
564+
565+
// CleanupClusterScopedResources deletes the ClusterRole and ClusterRoleBinding for the given pool.
566+
func (d *Deployer) CleanupClusterScopedResources(ctx context.Context, pool *infextv1a1.InferencePool) error {
567+
// The same release name as in the Helm template.
568+
releaseName := fmt.Sprintf("%s-endpoint-picker", pool.Name)
569+
570+
// Delete the ClusterRole.
571+
var cr rbacv1.ClusterRole
572+
if err := d.cli.Get(ctx, client.ObjectKey{Name: releaseName}, &cr); err == nil {
573+
if err := d.cli.Delete(ctx, &cr); err != nil {
574+
return fmt.Errorf("failed to delete ClusterRole %s: %w", releaseName, err)
575+
}
576+
}
577+
578+
// Delete the ClusterRoleBinding.
579+
var crb rbacv1.ClusterRoleBinding
580+
if err := d.cli.Get(ctx, client.ObjectKey{Name: releaseName}, &crb); err == nil {
581+
if err := d.cli.Delete(ctx, &crb); err != nil {
582+
return fmt.Errorf("failed to delete ClusterRoleBinding %s: %w", releaseName, err)
583+
}
584+
}
585+
586+
return nil
587+
}
588+
589+
// IsNamespaced returns true if the resource is namespaced.
590+
func IsNamespaced(gvk schema.GroupVersionKind) bool {
591+
if gvk == wellknown.ClusterRoleGVK || gvk == wellknown.ClusterRoleBindingGVK {
592+
return false
593+
}
594+
return true
595+
}
596+
548597
func loadFs(filesystem fs.FS) (*chart.Chart, error) {
549598
var bufferedFiles []*loader.BufferedFile
550599
entries, err := fs.ReadDir(filesystem, ".")

internal/kgateway/deployer/deployer_test.go

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,6 +1466,13 @@ var _ = Describe("Deployer", func() {
14661466
})
14671467
Expect(err).NotTo(HaveOccurred())
14681468

1469+
// Simulate reconciliation so that the pool gets its finalizer added.
1470+
err = d.EnsureFinalizer(context.Background(), pool)
1471+
Expect(err).NotTo(HaveOccurred())
1472+
1473+
// Check that the pool itself has the finalizer set.
1474+
Expect(pool.GetFinalizers()).To(ContainElement(wellknown.InferencePoolFinalizer))
1475+
14691476
// Get the endpoint picker objects for the InferencePool.
14701477
objs, err := d.GetEndpointPickerObjs(pool)
14711478
Expect(err).NotTo(HaveOccurred())
@@ -1474,48 +1481,51 @@ var _ = Describe("Deployer", func() {
14741481

14751482
// Find the child objects.
14761483
var sa *corev1.ServiceAccount
1477-
var role *rbacv1.Role
1478-
var rb *rbacv1.RoleBinding
1484+
var clusterRole *rbacv1.ClusterRole
1485+
var crb *rbacv1.ClusterRoleBinding
14791486
var dep *appsv1.Deployment
14801487
var svc *corev1.Service
14811488
for _, obj := range objs {
14821489
switch t := obj.(type) {
14831490
case *corev1.ServiceAccount:
14841491
sa = t
1485-
case *rbacv1.Role:
1486-
role = t
1487-
case *rbacv1.RoleBinding:
1488-
rb = t
1492+
case *rbacv1.ClusterRole:
1493+
clusterRole = t
1494+
case *rbacv1.ClusterRoleBinding:
1495+
crb = t
14891496
case *appsv1.Deployment:
14901497
dep = t
14911498
case *corev1.Service:
14921499
svc = t
14931500
}
14941501
}
14951502
Expect(sa).NotTo(BeNil(), "expected a ServiceAccount to be rendered")
1496-
Expect(role).NotTo(BeNil(), "expected a Role to be rendered")
1497-
Expect(rb).NotTo(BeNil(), "expected a RoleBinding to be rendered")
1503+
Expect(clusterRole).NotTo(BeNil(), "expected a Role to be rendered")
1504+
Expect(crb).NotTo(BeNil(), "expected a RoleBinding to be rendered")
14981505
Expect(dep).NotTo(BeNil(), "expected a Deployment to be rendered")
14991506
Expect(svc).NotTo(BeNil(), "expected a Service to be rendered")
15001507

15011508
// Check that owner references are set on all rendered objects to the InferencePool.
15021509
for _, obj := range objs {
1503-
ownerRefs := obj.GetOwnerReferences()
1504-
Expect(ownerRefs).To(HaveLen(1))
1505-
ref := ownerRefs[0]
1506-
Expect(ref.Name).To(Equal(pool.Name))
1507-
Expect(ref.UID).To(Equal(pool.UID))
1508-
Expect(ref.Kind).To(Equal(pool.Kind))
1509-
Expect(ref.APIVersion).To(Equal(pool.APIVersion))
1510-
Expect(*ref.Controller).To(BeTrue())
1510+
gvk := obj.GetObjectKind().GroupVersionKind()
1511+
if deployer.IsNamespaced(gvk) {
1512+
ownerRefs := obj.GetOwnerReferences()
1513+
Expect(ownerRefs).To(HaveLen(1))
1514+
ref := ownerRefs[0]
1515+
Expect(ref.Name).To(Equal(pool.Name))
1516+
Expect(ref.UID).To(Equal(pool.UID))
1517+
Expect(ref.Kind).To(Equal(pool.Kind))
1518+
Expect(ref.APIVersion).To(Equal(pool.APIVersion))
1519+
Expect(*ref.Controller).To(BeTrue())
1520+
}
15111521
}
15121522

15131523
// Validate that the rendered Deployment and Service have the expected names.
15141524
// (The template hardcodes the names to "inference-gateway-ext-proc".)
15151525
expectedName := fmt.Sprintf("%s-endpoint-picker", pool.Name)
15161526
Expect(sa.Name).To(Equal(expectedName))
1517-
Expect(role.Name).To(Equal(expectedName))
1518-
Expect(rb.Name).To(Equal(expectedName))
1527+
Expect(clusterRole.Name).To(Equal(expectedName))
1528+
Expect(crb.Name).To(Equal(expectedName))
15191529
Expect(dep.Name).To(Equal(expectedName))
15201530
Expect(svc.Name).To(Equal(expectedName))
15211531

0 commit comments

Comments
 (0)