Skip to content

Commit

Permalink
Automatically inject expected ODH annotations to InferenceGraph and I…
Browse files Browse the repository at this point in the history
…nferenceServices (#339)

* Implementation of ODH defaulters for InferenceGraph and InferenceService

On creation of InferenceGraph or InferenceService resources, the following default annotations will be added:
* `serving.knative.openshift.io/enablePassthrough: true`
* `sidecar.istio.io/inject: true`
* `sidecar.istio.io/rewriteAppHTTPProbers: true`

The annotations are added only for Serverless mode, and only if they are missing.

Signed-off-by: Edgar Hernández <[email protected]>

* Feedback: Filippe

Extract "ENABLE_WEBHOOKS" string to constant

Signed-off-by: Edgar Hernández <[email protected]>

---------

Signed-off-by: Edgar Hernández <[email protected]>
  • Loading branch information
israel-hdez authored Jan 10, 2025
1 parent cfe7f97 commit 2a69143
Show file tree
Hide file tree
Showing 18 changed files with 650 additions and 41 deletions.
1 change: 1 addition & 0 deletions Dockerfile
10 changes: 10 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ resources:
version: v1beta1
webhooks:
validation: true
defaulting: true
webhookVersion: v1
- controller: true
core: true
Expand Down Expand Up @@ -59,4 +60,13 @@ resources:
webhooks:
validation: true
webhookVersion: v1
- domain: kserve.io
external: true
group: serving
kind: InferenceGraph
path: github.com/kserve/kserve/pkg/apis/serving/v1alpha1
version: v1alpha1
webhooks:
defaulting: true
webhookVersion: v1
version: "3"
18 changes: 15 additions & 3 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@ import (
"github.com/opendatahub-io/odh-model-controller/internal/controller/utils"
webhooknimv1 "github.com/opendatahub-io/odh-model-controller/internal/webhook/nim/v1"
webhookservingv1 "github.com/opendatahub-io/odh-model-controller/internal/webhook/serving/v1"
webhookservingv1alpha1 "github.com/opendatahub-io/odh-model-controller/internal/webhook/serving/v1alpha1"
webhookservingv1beta1 "github.com/opendatahub-io/odh-model-controller/internal/webhook/serving/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// +kubebuilder:scaffold:imports
)

const (
enableWebhooksEnv = "ENABLE_WEBHOOKS"
)

var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
Expand Down Expand Up @@ -289,14 +294,14 @@ func main() {
}

// nolint:goconst
if os.Getenv("ENABLE_WEBHOOKS") != "false" {
if os.Getenv(enableWebhooksEnv) != "false" {
if err = webhooknimv1.SetupAccountWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "NIMAccount")
os.Exit(1)
}
}
// nolint:goconst
if os.Getenv("ENABLE_WEBHOOKS") != "false" {
if os.Getenv(enableWebhooksEnv) != "false" {
if kserveWithMeshEnabled {
if err = webhookservingv1.SetupServiceWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "Knative Service")
Expand All @@ -308,12 +313,19 @@ func main() {
}
}
// nolint:goconst
if os.Getenv("ENABLE_WEBHOOKS") != "false" {
if os.Getenv(enableWebhooksEnv) != "false" {
if err = webhookservingv1beta1.SetupInferenceServiceWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "InferenceService")
os.Exit(1)
}
}
// nolint:goconst
if os.Getenv(enableWebhooksEnv) != "false" {
if err = webhookservingv1alpha1.SetupInferenceGraphWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "InferenceGraph")
os.Exit(1)
}
}
// +kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand Down
58 changes: 42 additions & 16 deletions config/webhook/kustomization.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,44 @@ patches:
annotations:
service.beta.openshift.io/inject-cabundle: true
webhooks:
- name: validating.ksvc.odh-model-controller.opendatahub.io
clientConfig:
service:
name: odh-model-controller-webhook-service
objectSelector:
matchExpressions:
- key: serving.kserve.io/inferenceservice
operator: Exists
- name: validating.nim.account.odh-model-controller.opendatahub.io
clientConfig:
service:
name: odh-model-controller-webhook-service
- name: validating.isvc.odh-model-controller.opendatahub.io
clientConfig:
service:
name: odh-model-controller-webhook-service
- name: validating.ksvc.odh-model-controller.opendatahub.io
clientConfig:
service:
name: odh-model-controller-webhook-service
objectSelector:
matchExpressions:
- key: serving.kserve.io/inferenceservice
operator: Exists
- name: validating.nim.account.odh-model-controller.opendatahub.io
clientConfig:
service:
name: odh-model-controller-webhook-service
- name: validating.isvc.odh-model-controller.opendatahub.io
clientConfig:
service:
name: odh-model-controller-webhook-service
target:
group: admissionregistration.k8s.io
kind: ValidatingWebhookConfiguration
name: validating-webhook-configuration
version: v1
- patch: |-
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: mutating-webhook-configuration
annotations:
service.beta.openshift.io/inject-cabundle: true
webhooks:
- name: minferencegraph-v1alpha1.odh-model-controller.opendatahub.io
clientConfig:
service:
name: odh-model-controller-webhook-service
- name: minferenceservice-v1beta1.odh-model-controller.opendatahub.io
clientConfig:
service:
name: odh-model-controller-webhook-service
- patch: |-
- op: replace
path: /metadata/name
Expand All @@ -41,6 +58,15 @@ patches:
kind: ValidatingWebhookConfiguration
name: validating-webhook-configuration
version: v1
- patch: |-
- op: replace
path: /metadata/name
value: mutating.odh-model-controller.opendatahub.io
target:
group: admissionregistration.k8s.io
kind: MutatingWebhookConfiguration
name: mutating-webhook-configuration
version: v1

configurations:
- kustomizeconfig.yaml
44 changes: 44 additions & 0 deletions config/webhook/manifests.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-serving-kserve-io-v1alpha1-inferencegraph
failurePolicy: Fail
name: minferencegraph-v1alpha1.odh-model-controller.opendatahub.io
rules:
- apiGroups:
- serving.kserve.io
apiVersions:
- v1alpha1
operations:
- CREATE
resources:
- inferencegraphs
sideEffects: None
- admissionReviewVersions:
- v1
clientConfig:
service:
name: webhook-service
namespace: system
path: /mutate-serving-kserve-io-v1beta1-inferenceservice
failurePolicy: Fail
name: minferenceservice-v1beta1.odh-model-controller.opendatahub.io
rules:
- apiGroups:
- serving.kserve.io
apiVersions:
- v1beta1
operations:
- CREATE
resources:
- inferenceservices
sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: validating-webhook-configuration
Expand Down
8 changes: 4 additions & 4 deletions internal/controller/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ limitations under the License.

package constants

type IsvcDeploymentMode string
type KServeDeploymentMode string

const (
InferenceServiceKind = "InferenceService"
Expand Down Expand Up @@ -43,9 +43,9 @@ const (

// isvc modes
var (
Serverless IsvcDeploymentMode = "Serverless"
RawDeployment IsvcDeploymentMode = "RawDeployment"
ModelMesh IsvcDeploymentMode = "ModelMesh"
Serverless KServeDeploymentMode = "Serverless"
RawDeployment KServeDeploymentMode = "RawDeployment"
ModelMesh KServeDeploymentMode = "ModelMesh"
)

// model registry
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/serving/inferenceservice_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func (r *InferenceServiceReconciler) ReconcileServing(ctx context.Context, req c
}

// Check what deployment mode is used by the InferenceService. We have differing reconciliation logic for Kserve and ModelMesh
IsvcDeploymentMode, err := utils.GetDeploymentModeForIsvc(ctx, r.Client, isvc)
IsvcDeploymentMode, err := utils.GetDeploymentModeForKServeResource(ctx, r.Client, isvc.GetAnnotations())
if err != nil {
return ctrl.Result{}, err
}
Expand Down Expand Up @@ -259,7 +259,7 @@ func (r *InferenceServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {
func (r *InferenceServiceReconciler) onDeletion(ctx context.Context, log logr.Logger, inferenceService *kservev1beta1.InferenceService) error {
log.V(1).Info("Running cleanup logic")

IsvcDeploymentMode, err := utils.GetDeploymentModeForIsvc(ctx, r.Client, inferenceService)
IsvcDeploymentMode, err := utils.GetDeploymentModeForKServeResource(ctx, r.Client, inferenceService.GetAnnotations())
if err != nil {
log.V(1).Error(err, "Could not determine deployment mode for ISVC. Some resources related to the inferenceservice might not be deleted.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (r *KserveRawInferenceServiceReconciler) CleanupNamespaceIfNoKserveIsvcExis

for i := len(inferenceServiceList.Items) - 1; i >= 0; i-- {
inferenceService := inferenceServiceList.Items[i]
isvcDeploymentMode, err := utils.GetDeploymentModeForIsvc(ctx, r.client, &inferenceService)
isvcDeploymentMode, err := utils.GetDeploymentModeForKServeResource(ctx, r.client, inferenceService.GetAnnotations())
if err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (r *KserveServerlessInferenceServiceReconciler) CleanupNamespaceIfNoKserveI

for i := len(inferenceServiceList.Items) - 1; i >= 0; i-- {
inferenceService := inferenceServiceList.Items[i]
isvcDeploymentMode, err := utils.GetDeploymentModeForIsvc(ctx, r.client, &inferenceService)
isvcDeploymentMode, err := utils.GetDeploymentModeForKServeResource(ctx, r.client, inferenceService.GetAnnotations())
if err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (r *ModelMeshInferenceServiceReconciler) DeleteModelMeshResourcesIfNoMMIsvc

for i := len(inferenceServiceList.Items) - 1; i >= 0; i-- {
inferenceService := inferenceServiceList.Items[i]
isvcDeploymentMode, err := utils.GetDeploymentModeForIsvc(ctx, r.client, &inferenceService)
isvcDeploymentMode, err := utils.GetDeploymentModeForKServeResource(ctx, r.client, inferenceService.GetAnnotations())
if err != nil {
return err
}
Expand Down
16 changes: 8 additions & 8 deletions internal/controller/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,15 @@ var (
)

const (
inferenceServiceDeploymentModeAnnotation = "serving.kserve.io/deploymentMode"
KserveConfigMapName = "inferenceservice-config"
KServeWithServiceMeshComponent = "kserve-service-mesh"
KServeDeploymentModeAnnotation = "serving.kserve.io/deploymentMode"
KserveConfigMapName = "inferenceservice-config"
KServeWithServiceMeshComponent = "kserve-service-mesh"
)

func GetDeploymentModeForIsvc(ctx context.Context, cli client.Client, isvc *kservev1beta1.InferenceService) (constants.IsvcDeploymentMode, error) {
func GetDeploymentModeForKServeResource(ctx context.Context, cli client.Client, annotations map[string]string) (constants.KServeDeploymentMode, error) {

// If ISVC specifically sets deployment mode using an annotation, return bool depending on value
value, exists := isvc.Annotations[inferenceServiceDeploymentModeAnnotation]
// If explicitly sets deployment mode using an annotation, return bool depending on value
value, exists := annotations[KServeDeploymentModeAnnotation]
if exists {
switch value {
case string(constants.ModelMesh):
Expand All @@ -56,10 +56,10 @@ func GetDeploymentModeForIsvc(ctx context.Context, cli client.Client, isvc *kser
case string(constants.RawDeployment):
return constants.RawDeployment, nil
default:
return "", fmt.Errorf("the deployment mode '%s' of the Inference Service is invalid", value)
return "", fmt.Errorf("the deployment mode '%s' of the KServe resource is invalid", value)
}
} else {
// ISVC does not specifically set deployment mode using an annotation, determine the default from configmap
// There is no explicit deployment mode using an annotation, determine the default from configmap
controllerNs := os.Getenv("POD_NAMESPACE")
inferenceServiceConfigMap := &corev1.ConfigMap{}
err := cli.Get(ctx, client.ObjectKey{
Expand Down
51 changes: 51 additions & 0 deletions internal/webhook/serving/defaulters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package serving

import (
"context"
"fmt"
"strings"

"github.com/go-logr/logr"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/opendatahub-io/odh-model-controller/internal/controller/constants"
"github.com/opendatahub-io/odh-model-controller/internal/controller/utils"
)

func ApplyDefaultServerlessAnnotations(ctx context.Context, client client.Client, resourceName string, resourceMetadata *v1.ObjectMeta, logger logr.Logger) error {
deploymentMode, err := utils.GetDeploymentModeForKServeResource(ctx, client, resourceMetadata.GetAnnotations())
if err != nil {
return fmt.Errorf("error resolving deployment mode for resource %s: %w", resourceName, err)
}

if deploymentMode == constants.Serverless {
logAnnotationsAdded := make([]string, 0, 3)
resourceAnnotations := resourceMetadata.GetAnnotations()
if resourceAnnotations == nil {
resourceAnnotations = make(map[string]string)
}

if _, exists := resourceAnnotations["serving.knative.openshift.io/enablePassthrough"]; !exists {
resourceAnnotations["serving.knative.openshift.io/enablePassthrough"] = "true"
logAnnotationsAdded = append(logAnnotationsAdded, "serving.knative.openshift.io/enablePassthrough")
}

if _, exists := resourceAnnotations["sidecar.istio.io/inject"]; !exists {
resourceAnnotations["sidecar.istio.io/inject"] = "true"
logAnnotationsAdded = append(logAnnotationsAdded, "sidecar.istio.io/inject")
}

if _, exists := resourceAnnotations["sidecar.istio.io/rewriteAppHTTPProbers"]; !exists {
resourceAnnotations["sidecar.istio.io/rewriteAppHTTPProbers"] = "true"
logAnnotationsAdded = append(logAnnotationsAdded, "sidecar.istio.io/rewriteAppHTTPProbers")
}

if len(logAnnotationsAdded) > 0 {
logger.V(1).Info("Annotations added", "annotations", strings.Join(logAnnotationsAdded, ","))
}

resourceMetadata.SetAnnotations(resourceAnnotations)
}
return nil
}
Loading

0 comments on commit 2a69143

Please sign in to comment.