diff --git a/api/inference/v1alpha1/backendruntime_types.go b/api/inference/v1alpha1/backendruntime_types.go index 97ea5eeb..dc313d8c 100644 --- a/api/inference/v1alpha1/backendruntime_types.go +++ b/api/inference/v1alpha1/backendruntime_types.go @@ -88,6 +88,9 @@ type BackendRuntimeSpec struct { // Envs represents the environments set to the container. // +optional Envs []corev1.EnvVar `json:"envs,omitempty"` + // Lifecycle represents hooks executed during the lifecycle of the container. + // +optional + Lifecycle *corev1.Lifecycle `json:"lifecycle,omitempty"` // Periodic probe of backend liveness. // Backend will be restarted if the probe fails. // Cannot be updated. diff --git a/api/inference/v1alpha1/zz_generated.deepcopy.go b/api/inference/v1alpha1/zz_generated.deepcopy.go index eecffe54..6735dba5 100644 --- a/api/inference/v1alpha1/zz_generated.deepcopy.go +++ b/api/inference/v1alpha1/zz_generated.deepcopy.go @@ -155,6 +155,11 @@ func (in *BackendRuntimeSpec) DeepCopyInto(out *BackendRuntimeSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Lifecycle != nil { + in, out := &in.Lifecycle, &out.Lifecycle + *out = new(v1.Lifecycle) + (*in).DeepCopyInto(*out) + } if in.LivenessProbe != nil { in, out := &in.LivenessProbe, &out.LivenessProbe *out = new(v1.Probe) diff --git a/pkg/controller/inference/playground_controller.go b/pkg/controller/inference/playground_controller.go index 140afc57..19649eb5 100644 --- a/pkg/controller/inference/playground_controller.go +++ b/pkg/controller/inference/playground_controller.go @@ -313,6 +313,9 @@ func buildTemplate(models []*coreapi.OpenModel, playground *inferenceapi.Playgro // commands commands := parser.Commands() + // lifecycle + lifecycle := parser.Lifecycle() + // probe var livenessProbe, readinessProbe, startupProbe *corev1.Probe if backendRuntime.Spec.StartupProbe != nil { @@ -337,6 +340,7 @@ func buildTemplate(models []*coreapi.OpenModel, playground *inferenceapi.Playgro Command: commands, Args: args, Env: envs, + Lifecycle: lifecycle, Ports: []corev1.ContainerPort{ { Name: "http", diff --git a/pkg/controller_helper/backendruntime/backendruntime.go b/pkg/controller_helper/backendruntime/backendruntime.go index be1e7444..9a36f6ad 100644 --- a/pkg/controller_helper/backendruntime/backendruntime.go +++ b/pkg/controller_helper/backendruntime/backendruntime.go @@ -56,6 +56,10 @@ func (p *BackendRuntimeParser) Envs() []corev1.EnvVar { return p.backendRuntime.Spec.Envs } +func (p *BackendRuntimeParser) Lifecycle() *corev1.Lifecycle { + return p.backendRuntime.Spec.Lifecycle +} + func (p *BackendRuntimeParser) Args() ([]string, error) { mainModel := p.models[0] diff --git a/test/config/backends/fake_backend.yaml b/test/config/backends/fake_backend.yaml index ba573040..e1d4657f 100644 --- a/test/config/backends/fake_backend.yaml +++ b/test/config/backends/fake_backend.yaml @@ -13,6 +13,19 @@ spec: - echo "hello" image: busybox version: latest + lifecycle: + postStart: + exec: + command: + - /bin/sh + - -c + - echo "Container started." + preStop: + exec: + command: + - /bin/sh + - -c + - echo "Container stopped." recommendedConfigs: - name: default args: diff --git a/test/integration/controller/inference/playground_test.go b/test/integration/controller/inference/playground_test.go index 4e4f3493..cced7a41 100644 --- a/test/integration/controller/inference/playground_test.go +++ b/test/integration/controller/inference/playground_test.go @@ -455,5 +455,32 @@ var _ = ginkgo.Describe("playground controller test", func() { }, }, }), + ginkgo.Entry("Playground with fake-backend", &testValidatingCase{ + makePlayground: func() *inferenceapi.Playground { + return wrapper.MakePlayground("playground", ns.Name).ModelClaim(model.Name).Label(coreapi.ModelNameLabelKey, model.Name). + BackendRuntime("fake-backend"). + Obj() + }, + updates: []*update{ + { + updateFunc: func(playground *inferenceapi.Playground) { + gomega.Expect(k8sClient.Create(ctx, playground)).To(gomega.Succeed()) + }, + checkFunc: func(ctx context.Context, k8sClient client.Client, playground *inferenceapi.Playground) { + validation.ValidatePlayground(ctx, k8sClient, playground) + validation.ValidatePlaygroundStatusEqualTo(ctx, k8sClient, playground, inferenceapi.PlaygroundProgressing, "Pending", metav1.ConditionTrue) + }, + }, + { + updateFunc: func(playground *inferenceapi.Playground) { + util.UpdateLwsToReady(ctx, k8sClient, playground.Name, playground.Namespace) + }, + checkFunc: func(ctx context.Context, k8sClient client.Client, playground *inferenceapi.Playground) { + validation.ValidatePlayground(ctx, k8sClient, playground) + validation.ValidatePlaygroundStatusEqualTo(ctx, k8sClient, playground, inferenceapi.PlaygroundAvailable, "PlaygroundReady", metav1.ConditionTrue) + }, + }, + }, + }), ) }) diff --git a/test/util/validation/validate_playground.go b/test/util/validation/validate_playground.go index 82638d60..5336c008 100644 --- a/test/util/validation/validate_playground.go +++ b/test/util/validation/validate_playground.go @@ -164,6 +164,11 @@ func ValidatePlayground(ctx context.Context, k8sClient client.Client, playground return errors.New("command not right") } + // compare lifecycle + if diff := cmp.Diff(parser.Lifecycle(), service.Spec.WorkloadTemplate.WorkerTemplate.Spec.Containers[0].Lifecycle); diff != "" { + return errors.New("lifecycle not right") + } + // compare fields only can be configured in backend. if backendRuntime.Spec.StartupProbe != nil { diff --git a/test/util/wrapper/backend.go b/test/util/wrapper/backend.go index 03db2892..57573df5 100644 --- a/test/util/wrapper/backend.go +++ b/test/util/wrapper/backend.go @@ -62,6 +62,11 @@ func (w *BackendRuntimeWrapper) Command(commands []string) *BackendRuntimeWrappe return w } +func (w *BackendRuntimeWrapper) Lifecycle(lifecycle *corev1.Lifecycle) *BackendRuntimeWrapper { + w.Spec.Lifecycle = lifecycle + return w +} + func (w *BackendRuntimeWrapper) Arg(name string, args []string) *BackendRuntimeWrapper { if w.Spec.RecommendedConfigs == nil { w.Spec.RecommendedConfigs = []inferenceapi.RecommendedConfig{