diff --git a/cmd/router/main.go b/cmd/router/main.go index 01f5e7a4604..17e3051f49c 100644 --- a/cmd/router/main.go +++ b/cmd/router/main.go @@ -31,7 +31,6 @@ import ( "github.com/kserve/kserve/pkg/constants" "github.com/pkg/errors" - "github.com/tidwall/gjson" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -368,8 +367,8 @@ func main() { WriteTimeout: time.Minute, // set the maximum duration before timing out writes of the response IdleTimeout: 3 * time.Minute, // set the maximum amount of time to wait for the next request when keep-alives are enabled } - err = server.ListenAndServe() + err = server.ListenAndServeTLS("/etc/tls/private/tls.crt", "/etc/tls/private/tls.key") if err != nil { log.Error(err, "failed to listen on 8080") os.Exit(1) diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 3b8f2369dee..3db58dafff6 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -132,14 +132,15 @@ var ( // kserve networking constants const ( - NetworkVisibility = "networking.kserve.io/visibility" - ClusterLocalVisibility = "cluster-local" - ClusterLocalDomain = "svc.cluster.local" - IsvcNameHeader = "KServe-Isvc-Name" - IsvcNamespaceHeader = "KServe-Isvc-Namespace" - ODHKserveRawAuth = "security.opendatahub.io/enable-auth" - ODHRouteEnabled = "exposed" - ServingCertSecretSuffix = "-serving-cert" + NetworkVisibility = "networking.kserve.io/visibility" + ClusterLocalVisibility = "cluster-local" + ClusterLocalDomain = "svc.cluster.local" + IsvcNameHeader = "KServe-Isvc-Name" + IsvcNamespaceHeader = "KServe-Isvc-Namespace" + ODHKserveRawAuth = "security.opendatahub.io/enable-auth" + ODHRouteEnabled = "exposed" + ServingCertSecretSuffix = "-serving-cert" + OpenshiftServingCertAnnotation = "service.beta.openshift.io/serving-cert-secret-name" ) // StorageSpec Constants @@ -518,6 +519,13 @@ const ( OpenShiftServiceCaConfigMapName = "openshift-service-ca.crt" ) +type ResourceType string + +const ( + InferenceServiceResource ResourceType = "InferenceService" + InferenceGraphResource ResourceType = "InferenceGraph" +) + // GetRawServiceLabel generate native service label func GetRawServiceLabel(service string) string { return "isvc." + service diff --git a/pkg/controller/v1alpha1/inferencegraph/raw_ig.go b/pkg/controller/v1alpha1/inferencegraph/raw_ig.go index 49321f0e536..e5ebfd81632 100644 --- a/pkg/controller/v1alpha1/inferencegraph/raw_ig.go +++ b/pkg/controller/v1alpha1/inferencegraph/raw_ig.go @@ -89,7 +89,6 @@ func createInferenceGraphPodSpec(graph *v1alpha1api.InferenceGraph, config *Rout }, } } - return podSpec } @@ -145,7 +144,7 @@ func handleInferenceGraphRawDeployment(cl client.Client, clientset kubernetes.In objectMeta, componentExtSpec := constructForRawDeployment(graph) // create the reconciler - reconciler, err := raw.NewRawKubeReconciler(cl, clientset, scheme, objectMeta, metav1.ObjectMeta{}, &componentExtSpec, desiredSvc, nil) + reconciler, err := raw.NewRawKubeReconciler(cl, clientset, scheme, constants.InferenceGraphResource, objectMeta, metav1.ObjectMeta{}, &componentExtSpec, desiredSvc, nil) if err != nil { return nil, reconciler.URL, errors.Wrapf(err, "fails to create NewRawKubeReconciler for inference graph") @@ -158,6 +157,7 @@ func handleInferenceGraphRawDeployment(cl client.Client, clientset kubernetes.In } // set Service Controller for _, svc := range reconciler.Service.ServiceList { + svc.ObjectMeta.Annotations[constants.OpenshiftServingCertAnnotation] = graph.Name + constants.ServingCertSecretSuffix if err := controllerutil.SetControllerReference(graph, svc, scheme); err != nil { return nil, reconciler.URL, errors.Wrapf(err, "fails to set service owner reference for inference graph") } diff --git a/pkg/controller/v1beta1/inferenceservice/components/explainer.go b/pkg/controller/v1beta1/inferenceservice/components/explainer.go index 8e1b7750a0d..cde182ca6aa 100644 --- a/pkg/controller/v1beta1/inferenceservice/components/explainer.go +++ b/pkg/controller/v1beta1/inferenceservice/components/explainer.go @@ -160,7 +160,7 @@ func (e *Explainer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro // Here we allow switch between knative and vanilla deployment if e.deploymentMode == constants.RawDeployment { - r, err := raw.NewRawKubeReconciler(e.client, e.clientset, e.scheme, objectMeta, metav1.ObjectMeta{}, + r, err := raw.NewRawKubeReconciler(e.client, e.clientset, e.scheme, constants.InferenceServiceResource, objectMeta, metav1.ObjectMeta{}, &isvc.Spec.Explainer.ComponentExtensionSpec, &podSpec, nil) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to create NewRawKubeReconciler for explainer") diff --git a/pkg/controller/v1beta1/inferenceservice/components/predictor.go b/pkg/controller/v1beta1/inferenceservice/components/predictor.go index 2a8737911c5..f5a0a59798c 100644 --- a/pkg/controller/v1beta1/inferenceservice/components/predictor.go +++ b/pkg/controller/v1beta1/inferenceservice/components/predictor.go @@ -365,7 +365,7 @@ func (p *Predictor) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, erro rawDeployment = true podLabelKey = constants.RawDeploymentAppLabel // This is main RawKubeReconciler to create objects (deployment, svc, scaler) - r, err := raw.NewRawKubeReconciler(p.client, p.clientset, p.scheme, objectMeta, workerObjectMeta, &isvc.Spec.Predictor.ComponentExtensionSpec, + r, err := raw.NewRawKubeReconciler(p.client, p.clientset, p.scheme, constants.InferenceServiceResource, objectMeta, workerObjectMeta, &isvc.Spec.Predictor.ComponentExtensionSpec, &podSpec, workerPodSpec) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to create NewRawKubeReconciler for predictor") diff --git a/pkg/controller/v1beta1/inferenceservice/components/transformer.go b/pkg/controller/v1beta1/inferenceservice/components/transformer.go index 288270d9cb4..6687bc477dd 100644 --- a/pkg/controller/v1beta1/inferenceservice/components/transformer.go +++ b/pkg/controller/v1beta1/inferenceservice/components/transformer.go @@ -188,7 +188,7 @@ func (p *Transformer) Reconcile(isvc *v1beta1.InferenceService) (ctrl.Result, er // Here we allow switch between knative and vanilla deployment if p.deploymentMode == constants.RawDeployment { - r, err := raw.NewRawKubeReconciler(p.client, p.clientset, p.scheme, objectMeta, metav1.ObjectMeta{}, + r, err := raw.NewRawKubeReconciler(p.client, p.clientset, p.scheme, constants.InferenceServiceResource, objectMeta, metav1.ObjectMeta{}, &isvc.Spec.Transformer.ComponentExtensionSpec, &podSpec, nil) if err != nil { return ctrl.Result{}, errors.Wrapf(err, "fails to create NewRawKubeReconciler for transformer") diff --git a/pkg/controller/v1beta1/inferenceservice/rawkube_controller_test.go b/pkg/controller/v1beta1/inferenceservice/rawkube_controller_test.go index 090989f5bb4..17a4138c06d 100644 --- a/pkg/controller/v1beta1/inferenceservice/rawkube_controller_test.go +++ b/pkg/controller/v1beta1/inferenceservice/rawkube_controller_test.go @@ -21,6 +21,7 @@ import ( "fmt" "time" + "github.com/onsi/gomega/format" apierr "k8s.io/apimachinery/pkg/api/errors" "github.com/kserve/kserve/pkg/apis/serving/v1alpha1" @@ -100,6 +101,7 @@ var _ = Describe("v1beta1 inference service controller", func() { It("Should have ingress/service/deployment/hpa created", func() { By("By creating a new InferenceService") + format.MaxLength = 1000000 // Create configmap var configMap = &v1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ @@ -229,6 +231,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/autoscalerClass": "hpa", "serving.kserve.io/metrics": "cpu", "serving.kserve.io/targetUtilizationPercentage": "75", + constants.OpenshiftServingCertAnnotation: predictorDeploymentKey.Name + constants.ServingCertSecretSuffix, }, }, Spec: v1.PodSpec{ @@ -648,6 +651,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/autoscalerClass": "hpa", "serving.kserve.io/metrics": "cpu", "serving.kserve.io/targetUtilizationPercentage": "75", + constants.OpenshiftServingCertAnnotation: "raw-foo-customized-predictor-serving-cert", }, }, Spec: v1.PodSpec{ @@ -1052,6 +1056,7 @@ var _ = Describe("v1beta1 inference service controller", func() { constants.StorageInitializerSourceUriInternalAnnotationKey: *isvc.Spec.Predictor.Model.StorageURI, "serving.kserve.io/deploymentMode": "RawDeployment", "serving.kserve.io/autoscalerClass": "external", + constants.OpenshiftServingCertAnnotation: predictorDeploymentKey.Name + constants.ServingCertSecretSuffix, }, }, Spec: v1.PodSpec{ @@ -1723,6 +1728,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/autoscalerClass": "hpa", "serving.kserve.io/metrics": "cpu", "serving.kserve.io/targetUtilizationPercentage": "75", + constants.OpenshiftServingCertAnnotation: predictorDeploymentKey.Name + constants.ServingCertSecretSuffix, }, }, Spec: v1.PodSpec{ @@ -2157,6 +2163,7 @@ var _ = Describe("v1beta1 inference service controller", func() { "serving.kserve.io/autoscalerClass": "hpa", "serving.kserve.io/metrics": "cpu", "serving.kserve.io/targetUtilizationPercentage": "75", + constants.OpenshiftServingCertAnnotation: predictorDeploymentKey.Name + constants.ServingCertSecretSuffix, }, }, Spec: v1.PodSpec{ @@ -2597,6 +2604,12 @@ var _ = Describe("v1beta1 inference service controller", func() { "--model_base_path=" + constants.DefaultModelLocalMountPath, "--rest_api_timeout_in_ms=60000", }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "proxy-tls", + MountPath: "/etc/tls/private", + }, + }, Resources: defaultResource, ReadinessProbe: &v1.Probe{ ProbeHandler: v1.ProbeHandler{ diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler.go index 7be9bf9ad6b..96177a909ad 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment/deployment_reconciler.go @@ -26,7 +26,6 @@ import ( "strings" "k8s.io/apimachinery/pkg/api/resource" - "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/client-go/kubernetes" "github.com/google/go-cmp/cmp/cmpopts" @@ -41,6 +40,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/strategicpatch" "knative.dev/pkg/kmp" kclient "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -64,11 +64,12 @@ const ( func NewDeploymentReconciler(client kclient.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, + resourceType constants.ResourceType, componentMeta metav1.ObjectMeta, workerComponentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, podSpec *corev1.PodSpec, workerPodSpec *corev1.PodSpec) (*DeploymentReconciler, error) { - deploymentList, err := createRawDeploymentODH(clientset, componentMeta, workerComponentMeta, componentExt, podSpec, workerPodSpec) + deploymentList, err := createRawDeploymentODH(clientset, resourceType, componentMeta, workerComponentMeta, componentExt, podSpec, workerPodSpec) if err != nil { return nil, err } @@ -80,21 +81,26 @@ func NewDeploymentReconciler(client kclient.Client, }, nil } -func createRawDeploymentODH(clientset kubernetes.Interface, componentMeta metav1.ObjectMeta, workerComponentMeta metav1.ObjectMeta, +func createRawDeploymentODH(clientset kubernetes.Interface, resourceType constants.ResourceType, componentMeta metav1.ObjectMeta, workerComponentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, podSpec *corev1.PodSpec, workerPodSpec *corev1.PodSpec) ([]*appsv1.Deployment, error) { deploymentList, err := createRawDeployment(componentMeta, workerComponentMeta, componentExt, podSpec, workerPodSpec) if err != nil { return nil, err } + enableAuth := false + // Deployment list is for multi-node, we only need to add oauth proxy and serving sercret certs to the head deployment + headDeployment := deploymentList[0] if val, ok := componentMeta.Annotations[constants.ODHKserveRawAuth]; ok && strings.EqualFold(val, "true") { - for _, deployment := range deploymentList { - err := addOauthContainerToDeployment(clientset, deployment, componentMeta, componentExt, podSpec) - if err != nil { - return nil, err - } + enableAuth = true + err := addOauthContainerToDeployment(clientset, headDeployment, componentMeta, componentExt, podSpec) + if err != nil { + return nil, err } } + if (resourceType == constants.InferenceServiceResource && enableAuth) || resourceType == constants.InferenceGraphResource { + mountServingSecretVolumeToDeployment(headDeployment, componentMeta, resourceType) + } return deploymentList, nil } @@ -187,6 +193,36 @@ func createRawDefaultDeployment(componentMeta metav1.ObjectMeta, return deployment, nil } +func mountServingSecretVolumeToDeployment(deployment *appsv1.Deployment, componentMeta metav1.ObjectMeta, resourceType constants.ResourceType) { + updatedPodSpec := deployment.Spec.Template.Spec.DeepCopy() + tlsSecretVolume := corev1.Volume{ + Name: tlsVolumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: componentMeta.Name + constants.ServingCertSecretSuffix, + DefaultMode: func(i int32) *int32 { return &i }(420), + }, + }, + } + + updatedPodSpec.Volumes = append(updatedPodSpec.Volumes, tlsSecretVolume) + + containerName := "kserve-container" + if resourceType == constants.InferenceGraphResource { + containerName = componentMeta.Name + } + for i, container := range updatedPodSpec.Containers { + if container.Name == containerName { + updatedPodSpec.Containers[i].VolumeMounts = append(updatedPodSpec.Containers[i].VolumeMounts, corev1.VolumeMount{ + Name: tlsVolumeName, + MountPath: "/etc/tls/private", + }) + } + } + + deployment.Spec.Template.Spec = *updatedPodSpec +} + func addOauthContainerToDeployment(clientset kubernetes.Interface, deployment *appsv1.Deployment, componentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, podSpec *corev1.PodSpec) error { var isvcname string @@ -223,16 +259,6 @@ func addOauthContainerToDeployment(clientset kubernetes.Interface, deployment *a updatedPodSpec := deployment.Spec.Template.Spec.DeepCopy() // updatedPodSpec := podSpec.DeepCopy() updatedPodSpec.Containers = append(updatedPodSpec.Containers, *oauthProxyContainer) - tlsSecretVolume := corev1.Volume{ - Name: tlsVolumeName, - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: componentMeta.Name + constants.ServingCertSecretSuffix, - DefaultMode: func(i int32) *int32 { return &i }(420), - }, - }, - } - updatedPodSpec.Volumes = append(updatedPodSpec.Volumes, tlsSecretVolume) deployment.Spec.Template.Spec = *updatedPodSpec } return nil diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/raw/raw_kube_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/raw/raw_kube_reconciler.go index 77d494cec30..4e7a45087e2 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/raw/raw_kube_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/raw/raw_kube_reconciler.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/kserve/kserve/pkg/apis/serving/v1beta1" + "github.com/kserve/kserve/pkg/constants" autoscaler "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/autoscaler" deployment "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/deployment" "github.com/kserve/kserve/pkg/controller/v1beta1/inferenceservice/reconcilers/ingress" @@ -51,6 +52,7 @@ type RawKubeReconciler struct { func NewRawKubeReconciler(client client.Client, clientset kubernetes.Interface, scheme *runtime.Scheme, + resourceType constants.ResourceType, componentMeta metav1.ObjectMeta, workerComponentMeta metav1.ObjectMeta, componentExt *v1beta1.ComponentExtensionSpec, @@ -76,7 +78,7 @@ func NewRawKubeReconciler(client client.Client, log.Error(err1, "failed to get service config") } - depl, err := deployment.NewDeploymentReconciler(client, clientset, scheme, componentMeta, workerComponentMeta, componentExt, podSpec, workerPodSpec) + depl, err := deployment.NewDeploymentReconciler(client, clientset, scheme, resourceType, componentMeta, workerComponentMeta, componentExt, podSpec, workerPodSpec) if err != nil { return nil, err } diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler.go index 3b7f836de4f..80c4cc2042b 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler.go @@ -170,11 +170,12 @@ func createDefaultSvc(componentMeta metav1.ObjectMeta, componentExt *v1beta1.Com }, } + if service.ObjectMeta.Annotations == nil { + service.ObjectMeta.Annotations = make(map[string]string) + } + service.ObjectMeta.Annotations[constants.OpenshiftServingCertAnnotation] = componentMeta.Name + constants.ServingCertSecretSuffix + if val, ok := componentMeta.Annotations[constants.ODHKserveRawAuth]; ok && strings.EqualFold(val, "true") { - if service.ObjectMeta.Annotations == nil { - service.ObjectMeta.Annotations = make(map[string]string) - } - service.ObjectMeta.Annotations["service.beta.openshift.io/serving-cert-secret-name"] = componentMeta.Name + constants.ServingCertSecretSuffix httpsPort := corev1.ServicePort{ Name: "https", Port: constants.OauthProxyPort, diff --git a/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler_test.go b/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler_test.go index 37e285c9953..bcfa0a33334 100644 --- a/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler_test.go +++ b/pkg/controller/v1beta1/inferenceservice/reconcilers/service/service_reconciler_test.go @@ -118,7 +118,8 @@ func TestCreateDefaultDeployment(t *testing.T) { constants.DeploymentMode: string(constants.RawDeployment), }, Annotations: map[string]string{ - "annotation": "annotation-value", + "annotation": "annotation-value", + constants.OpenshiftServingCertAnnotation: "default-predictor-serving-cert", }, }, Spec: corev1.ServiceSpec{ @@ -149,7 +150,8 @@ func TestCreateDefaultDeployment(t *testing.T) { constants.InferenceServiceGenerationPodLabelKey: "1", }, Annotations: map[string]string{ - "annotation": "annotation-value", + "annotation": "annotation-value", + constants.OpenshiftServingCertAnnotation: "default-predictor-serving-cert", }, }, Spec: corev1.ServiceSpec{ @@ -178,7 +180,8 @@ func TestCreateDefaultDeployment(t *testing.T) { constants.MultiNodeRoleLabelKey: constants.MultiNodeHead, }, Annotations: map[string]string{ - "annotation": "annotation-value", + "annotation": "annotation-value", + constants.OpenshiftServingCertAnnotation: "default-predictor-serving-cert", }, }, Spec: corev1.ServiceSpec{ @@ -268,9 +271,13 @@ func TestCreateServiceRawTrueAndConfigNil(t *testing.T) { } func runTestServiceCreate(serviceConfig *v1beta1.ServiceConfig, expectedClusterIP string, t *testing.T) { + // Adding the annotation here as the test it is expected that this annotation is injected automatically for all services componentMeta := metav1.ObjectMeta{ Name: "test-service", Namespace: "default", + Annotations: map[string]string{ + constants.OpenshiftServingCertAnnotation: "default-predictor-serving-cert", + }, } componentExt := &v1beta1.ComponentExtensionSpec{} podSpec := &corev1.PodSpec{}