Skip to content

Commit 7b8a63d

Browse files
committed
[RHOAIENG-38499] - OCI model storage is not working with multi-node feature
chore: Fix the scenarion when using OCI model cache with multi-node feature where the issue happens: ` denied the request: no container found with name kserve-container` Signed-off-by: Spolti <[email protected]>
1 parent f0eaa31 commit 7b8a63d

File tree

2 files changed

+182
-2
lines changed

2 files changed

+182
-2
lines changed

pkg/webhook/admission/pod/storage_initializer_injector.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,26 @@ func (mi *StorageInitializerInjector) InjectModelcar(pod *corev1.Pod) error {
9292
return nil
9393
}
9494

95-
if err := utils.ConfigureModelcarToContainer(srcURI, &pod.Spec, constants.InferenceServiceContainerName, mi.config); err != nil {
96-
return err
95+
// Find the kserve-container (this is the model inference server) and worker-container
96+
userContainer := utils.GetContainerWithName(&pod.Spec, constants.InferenceServiceContainerName)
97+
workerContainer := utils.GetContainerWithName(&pod.Spec, constants.WorkerContainerName)
98+
99+
if userContainer == nil {
100+
if workerContainer == nil {
101+
return fmt.Errorf("Invalid configuration: cannot find container: %s", constants.InferenceServiceContainerName)
102+
} else {
103+
// Use worker container for multi-node scenarios
104+
if err := utils.ConfigureModelcarToContainer(srcURI, &pod.Spec, constants.WorkerContainerName, mi.config); err != nil {
105+
return err
106+
}
107+
}
108+
} else {
109+
if err := utils.ConfigureModelcarToContainer(srcURI, &pod.Spec, constants.InferenceServiceContainerName, mi.config); err != nil {
110+
return err
111+
}
97112
}
98113

114+
// Configure modelcar for transformer container if it exists
99115
if utils.GetContainerWithName(&pod.Spec, constants.TransformerContainerName) != nil {
100116
return utils.ConfigureModelcarToContainer(srcURI, &pod.Spec, constants.TransformerContainerName, mi.config)
101117
}

pkg/webhook/admission/pod/storage_initializer_injector_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4453,3 +4453,167 @@ func TestOVMSAutoVersioningIdempotency(t *testing.T) {
44534453
t.Errorf("Expected exactly 1 OVMS versioning container, got %d", ovmsContainerCount)
44544454
}
44554455
}
4456+
4457+
// TestInjectModelcarMultiNode tests the multinode scenario fixes for OCI model storage
4458+
func TestInjectModelcarMultiNode(t *testing.T) {
4459+
t.Run("Test InjectModelcar with worker-container only (multi-node scenario)", func(t *testing.T) {
4460+
pod := createTestPodForModelcarWithWorkerContainer()
4461+
injector := &StorageInitializerInjector{config: &kserveTypes.StorageInitializerConfig{}}
4462+
4463+
err := injector.InjectModelcar(pod)
4464+
require.NoError(t, err)
4465+
4466+
// Verify that modelcar was injected for worker container
4467+
modelcarContainer := utils.GetContainerWithName(&pod.Spec, constants.ModelcarContainerName)
4468+
assert.NotNil(t, modelcarContainer, "Modelcar container should be created")
4469+
4470+
workerContainer := utils.GetContainerWithName(&pod.Spec, constants.WorkerContainerName)
4471+
assert.NotNil(t, workerContainer, "Worker container should exist")
4472+
4473+
// Verify that worker container has the correct volume mounts
4474+
found := false
4475+
for _, mount := range workerContainer.VolumeMounts {
4476+
if mount.Name == constants.StorageInitializerVolumeName {
4477+
found = true
4478+
break
4479+
}
4480+
}
4481+
assert.True(t, found, "Worker container should have storage initializer volume mount")
4482+
4483+
// Verify that the volume was created
4484+
assert.Len(t, pod.Spec.Volumes, 1, "Should have exactly one volume")
4485+
assert.Equal(t, constants.StorageInitializerVolumeName, pod.Spec.Volumes[0].Name)
4486+
})
4487+
4488+
t.Run("Test InjectModelcar error when no valid container found", func(t *testing.T) {
4489+
pod := createTestPodForModelcarNoValidContainer()
4490+
injector := &StorageInitializerInjector{config: &kserveTypes.StorageInitializerConfig{}}
4491+
4492+
err := injector.InjectModelcar(pod)
4493+
require.Error(t, err)
4494+
assert.Contains(t, err.Error(), "Invalid configuration: cannot find container: kserve-container")
4495+
})
4496+
4497+
t.Run("Test InjectModelcar prioritizes kserve-container over worker-container", func(t *testing.T) {
4498+
pod := createTestPodForModelcarWithBothContainers()
4499+
injector := &StorageInitializerInjector{config: &kserveTypes.StorageInitializerConfig{}}
4500+
4501+
err := injector.InjectModelcar(pod)
4502+
require.NoError(t, err)
4503+
4504+
// Both containers should have volume mounts but kserve-container should be prioritized
4505+
kserveContainer := utils.GetContainerWithName(&pod.Spec, constants.InferenceServiceContainerName)
4506+
workerContainer := utils.GetContainerWithName(&pod.Spec, constants.WorkerContainerName)
4507+
4508+
assert.NotNil(t, kserveContainer)
4509+
assert.NotNil(t, workerContainer)
4510+
4511+
// Check that kserve-container got the volume mount
4512+
kserveHasMount := false
4513+
for _, mount := range kserveContainer.VolumeMounts {
4514+
if mount.Name == constants.StorageInitializerVolumeName {
4515+
kserveHasMount = true
4516+
break
4517+
}
4518+
}
4519+
assert.True(t, kserveHasMount, "kserve-container should have storage initializer volume mount")
4520+
})
4521+
4522+
t.Run("Test InjectModelcar with transformer and worker containers", func(t *testing.T) {
4523+
pod := createTestPodForModelcarWithWorkerAndTransformer()
4524+
injector := &StorageInitializerInjector{config: &kserveTypes.StorageInitializerConfig{}}
4525+
4526+
err := injector.InjectModelcar(pod)
4527+
require.NoError(t, err)
4528+
4529+
// Check both worker and transformer containers got volume mounts
4530+
workerContainer := utils.GetContainerWithName(&pod.Spec, constants.WorkerContainerName)
4531+
transformerContainer := utils.GetContainerWithName(&pod.Spec, constants.TransformerContainerName)
4532+
4533+
assert.NotNil(t, workerContainer)
4534+
assert.NotNil(t, transformerContainer)
4535+
4536+
// Both should have volume mounts
4537+
workerHasMount := false
4538+
for _, mount := range workerContainer.VolumeMounts {
4539+
if mount.Name == constants.StorageInitializerVolumeName {
4540+
workerHasMount = true
4541+
break
4542+
}
4543+
}
4544+
assert.True(t, workerHasMount, "Worker container should have storage initializer volume mount")
4545+
4546+
transformerHasMount := false
4547+
for _, mount := range transformerContainer.VolumeMounts {
4548+
if mount.Name == constants.StorageInitializerVolumeName {
4549+
transformerHasMount = true
4550+
break
4551+
}
4552+
}
4553+
assert.True(t, transformerHasMount, "Transformer container should have storage initializer volume mount")
4554+
})
4555+
}
4556+
4557+
// Helper functions for multi-node testing
4558+
4559+
func createTestPodForModelcarWithWorkerContainer() *corev1.Pod {
4560+
return &corev1.Pod{
4561+
ObjectMeta: metav1.ObjectMeta{
4562+
Annotations: map[string]string{
4563+
constants.StorageInitializerSourceUriInternalAnnotationKey: constants.OciURIPrefix + "myrepo/mymodelimage",
4564+
},
4565+
},
4566+
Spec: corev1.PodSpec{
4567+
Containers: []corev1.Container{
4568+
{Name: constants.WorkerContainerName},
4569+
},
4570+
},
4571+
}
4572+
}
4573+
4574+
func createTestPodForModelcarNoValidContainer() *corev1.Pod {
4575+
return &corev1.Pod{
4576+
ObjectMeta: metav1.ObjectMeta{
4577+
Annotations: map[string]string{
4578+
constants.StorageInitializerSourceUriInternalAnnotationKey: constants.OciURIPrefix + "myrepo/mymodelimage",
4579+
},
4580+
},
4581+
Spec: corev1.PodSpec{
4582+
Containers: []corev1.Container{
4583+
{Name: "some-other-container"},
4584+
},
4585+
},
4586+
}
4587+
}
4588+
4589+
func createTestPodForModelcarWithBothContainers() *corev1.Pod {
4590+
return &corev1.Pod{
4591+
ObjectMeta: metav1.ObjectMeta{
4592+
Annotations: map[string]string{
4593+
constants.StorageInitializerSourceUriInternalAnnotationKey: constants.OciURIPrefix + "myrepo/mymodelimage",
4594+
},
4595+
},
4596+
Spec: corev1.PodSpec{
4597+
Containers: []corev1.Container{
4598+
{Name: constants.InferenceServiceContainerName},
4599+
{Name: constants.WorkerContainerName},
4600+
},
4601+
},
4602+
}
4603+
}
4604+
4605+
func createTestPodForModelcarWithWorkerAndTransformer() *corev1.Pod {
4606+
return &corev1.Pod{
4607+
ObjectMeta: metav1.ObjectMeta{
4608+
Annotations: map[string]string{
4609+
constants.StorageInitializerSourceUriInternalAnnotationKey: constants.OciURIPrefix + "myrepo/mymodelimage",
4610+
},
4611+
},
4612+
Spec: corev1.PodSpec{
4613+
Containers: []corev1.Container{
4614+
{Name: constants.WorkerContainerName},
4615+
{Name: constants.TransformerContainerName},
4616+
},
4617+
},
4618+
}
4619+
}

0 commit comments

Comments
 (0)