mirror of
https://github.com/hanzo-ml/kserve.git
synced 2026-10-30 20:53:49 +00:00
Signed-off-by: Vedant Mahabaleshwarkar <[email protected]>
162 lines
6.7 KiB
Go
162 lines
6.7 KiB
Go
/*
|
|
Copyright 2024 The KServe Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package localmodelcache
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/kserve/kserve/pkg/utils"
|
|
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
|
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
|
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
|
|
|
"github.com/kserve/kserve/pkg/apis/serving/v1alpha1"
|
|
"github.com/kserve/kserve/pkg/apis/serving/v1alpha2"
|
|
"github.com/kserve/kserve/pkg/apis/serving/v1beta1"
|
|
"github.com/kserve/kserve/pkg/constants"
|
|
)
|
|
|
|
// logger for the validation webhook.
|
|
var localModelCacheValidatorLogger = logf.Log.WithName("localmodelcache-v1alpha1-validation-webhook")
|
|
|
|
// +kubebuilder:object:generate=false
|
|
// +k8s:openapi-gen=false
|
|
// LocalModelCacheValidator is responsible for validating the LocalModelCache resource
|
|
// when it is created, updated, or deleted.
|
|
//
|
|
// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
|
|
// as this struct is used only for temporary operations and does not need to be deeply copied.
|
|
type LocalModelCacheValidator struct {
|
|
client.Client
|
|
}
|
|
|
|
// +kubebuilder:webhook:verbs=create;update;delete,path=/validate-localmodelcaches,mutating=false,failurePolicy=fail,groups=serving.kserve.io,resources=localmodelcaches,versions=v1alpha1,name=localmodelcache.kserve-webhook-server.validator
|
|
var _ webhook.CustomValidator = &LocalModelCacheValidator{}
|
|
|
|
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
|
|
func (v *LocalModelCacheValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
|
localModelCache, err := utils.Convert[*v1alpha1.LocalModelCache](obj)
|
|
if err != nil {
|
|
localModelCacheValidatorLogger.Error(err, "Unable to convert object to LocalModelCache")
|
|
return nil, err
|
|
}
|
|
localModelCacheValidatorLogger.Info("validate create", "name", localModelCache.Name)
|
|
localModelCacheWithSameStorageURI, err := v.validateUniqueStorageURI(ctx, localModelCache)
|
|
if err != nil {
|
|
localModelCacheValidatorLogger.Error(err, "Unable to check LocalModelCache with the same storage URI")
|
|
return nil, err
|
|
}
|
|
if localModelCacheWithSameStorageURI != nil {
|
|
return admission.Warnings{}, fmt.Errorf("LocalModelCache %s has the same StorageURI %s", localModelCacheWithSameStorageURI.Name, localModelCacheWithSameStorageURI.Spec.SourceModelUri)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
|
|
func (v *LocalModelCacheValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
|
|
localModelCache, err := utils.Convert[*v1alpha1.LocalModelCache](newObj)
|
|
if err != nil {
|
|
localModelCacheValidatorLogger.Error(err, "Unable to convert object to LocalModelCache")
|
|
return nil, err
|
|
}
|
|
if localModelCache.GetDeletionTimestamp() != nil {
|
|
return nil, nil
|
|
}
|
|
localModelCacheValidatorLogger.Info("validate update", "name", localModelCache.Name)
|
|
localModelCacheWithSameStorageURI, err := v.validateUniqueStorageURI(ctx, localModelCache)
|
|
if err != nil {
|
|
localModelCacheValidatorLogger.Error(err, "Unable to check LocalModelCache with the same storage URI")
|
|
return nil, err
|
|
}
|
|
if localModelCacheWithSameStorageURI != nil {
|
|
return admission.Warnings{}, fmt.Errorf("LocalModelCache %s has the same StorageURI %s", localModelCacheWithSameStorageURI.Name, localModelCacheWithSameStorageURI.Spec.SourceModelUri)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
|
|
func (v *LocalModelCacheValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
|
localModelCache, err := utils.Convert[*v1alpha1.LocalModelCache](obj)
|
|
if err != nil {
|
|
localModelCacheValidatorLogger.Error(err, "Unable to convert object to LocalModelCache")
|
|
return nil, err
|
|
}
|
|
localModelCacheValidatorLogger.Info("validate delete", "name", localModelCache.Name)
|
|
|
|
// Check if current LocalModelCache is being used
|
|
for _, isvcMeta := range localModelCache.Status.InferenceServices {
|
|
isvc := v1beta1.InferenceService{}
|
|
if err := v.Get(ctx, client.ObjectKey(isvcMeta), &isvc); err != nil {
|
|
localModelCacheValidatorLogger.Error(err, "Error getting InferenceService", "name", isvcMeta.Name)
|
|
return nil, err
|
|
}
|
|
modelName, ok := isvc.Labels[constants.LocalModelLabel]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if modelName == localModelCache.Name {
|
|
return admission.Warnings{}, fmt.Errorf("LocalModelCache %s is being used by InferenceService %s", localModelCache.Name, isvcMeta.Name)
|
|
}
|
|
}
|
|
|
|
// Check if current LocalModelCache is being used by LLMInferenceServices
|
|
for _, llmIsvcMeta := range localModelCache.Status.LLMInferenceServices {
|
|
llmIsvc := v1alpha2.LLMInferenceService{}
|
|
if err := v.Get(ctx, client.ObjectKey(llmIsvcMeta), &llmIsvc); err != nil {
|
|
if apierrors.IsNotFound(err) {
|
|
continue
|
|
}
|
|
localModelCacheValidatorLogger.Error(err, "Error getting LLMInferenceService", "name", llmIsvcMeta.Name)
|
|
return nil, err
|
|
}
|
|
modelName, ok := llmIsvc.Labels[constants.LocalModelLabel]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if modelName == localModelCache.Name {
|
|
return admission.Warnings{}, fmt.Errorf("LocalModelCache %s is being used by LLMInferenceService %s", localModelCache.Name, llmIsvcMeta.Name)
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// Checks if there are other LocalModelCache with the same storage URI
|
|
func (v *LocalModelCacheValidator) validateUniqueStorageURI(ctx context.Context, currentLocalModelCache *v1alpha1.LocalModelCache) (*v1alpha1.LocalModelCache, error) {
|
|
// Get all LocalModelCache CR
|
|
localModelCacheList := &v1alpha1.LocalModelCacheList{}
|
|
if err := v.List(ctx, localModelCacheList); err != nil {
|
|
localModelCacheValidatorLogger.Error(err, "Unable to list LocalModelCaches")
|
|
return nil, err
|
|
}
|
|
// Check the storage URI of each LocalModelCache CR except for the current one
|
|
for _, localModelCache := range localModelCacheList.Items {
|
|
// Skip the current one
|
|
if localModelCache.Name == currentLocalModelCache.Name {
|
|
continue
|
|
}
|
|
if localModelCache.Spec.SourceModelUri == currentLocalModelCache.Spec.SourceModelUri {
|
|
return &localModelCache, nil
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|