Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions pkg/webhook/admission/pod/storage_initializer_injector.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,26 @@ func (mi *StorageInitializerInjector) InjectModelcar(pod *corev1.Pod) error {
return nil
}

if err := utils.ConfigureModelcarToContainer(srcURI, &pod.Spec, constants.InferenceServiceContainerName, mi.config); err != nil {
return err
// Find the kserve-container (this is the model inference server) and worker-container
userContainer := utils.GetContainerWithName(&pod.Spec, constants.InferenceServiceContainerName)
workerContainer := utils.GetContainerWithName(&pod.Spec, constants.WorkerContainerName)

if userContainer == nil {
if workerContainer == nil {
return fmt.Errorf("Invalid configuration: cannot find container: %s", constants.InferenceServiceContainerName)
} else {
// Use worker container for multi-node scenarios
if err := utils.ConfigureModelcarToContainer(srcURI, &pod.Spec, constants.WorkerContainerName, mi.config); err != nil {
return err
}
}
} else {
if err := utils.ConfigureModelcarToContainer(srcURI, &pod.Spec, constants.InferenceServiceContainerName, mi.config); err != nil {
return err
}
}

// Configure modelcar for transformer container if it exists
if utils.GetContainerWithName(&pod.Spec, constants.TransformerContainerName) != nil {
return utils.ConfigureModelcarToContainer(srcURI, &pod.Spec, constants.TransformerContainerName, mi.config)
}
Expand Down
164 changes: 164 additions & 0 deletions pkg/webhook/admission/pod/storage_initializer_injector_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4453,3 +4453,167 @@ func TestOVMSAutoVersioningIdempotency(t *testing.T) {
t.Errorf("Expected exactly 1 OVMS versioning container, got %d", ovmsContainerCount)
}
}

// TestInjectModelcarMultiNode tests the multinode scenario fixes for OCI model storage
func TestInjectModelcarMultiNode(t *testing.T) {
t.Run("Test InjectModelcar with worker-container only (multi-node scenario)", func(t *testing.T) {
pod := createTestPodForModelcarWithWorkerContainer()
injector := &StorageInitializerInjector{config: &kserveTypes.StorageInitializerConfig{}}

err := injector.InjectModelcar(pod)
require.NoError(t, err)

// Verify that modelcar was injected for worker container
modelcarContainer := utils.GetContainerWithName(&pod.Spec, constants.ModelcarContainerName)
assert.NotNil(t, modelcarContainer, "Modelcar container should be created")

workerContainer := utils.GetContainerWithName(&pod.Spec, constants.WorkerContainerName)
assert.NotNil(t, workerContainer, "Worker container should exist")

// Verify that worker container has the correct volume mounts
found := false
for _, mount := range workerContainer.VolumeMounts {
if mount.Name == constants.StorageInitializerVolumeName {
found = true
break
}
}
assert.True(t, found, "Worker container should have storage initializer volume mount")

// Verify that the volume was created
assert.Len(t, pod.Spec.Volumes, 1, "Should have exactly one volume")
assert.Equal(t, constants.StorageInitializerVolumeName, pod.Spec.Volumes[0].Name)
})

t.Run("Test InjectModelcar error when no valid container found", func(t *testing.T) {
pod := createTestPodForModelcarNoValidContainer()
injector := &StorageInitializerInjector{config: &kserveTypes.StorageInitializerConfig{}}

err := injector.InjectModelcar(pod)
require.Error(t, err)
assert.Contains(t, err.Error(), "Invalid configuration: cannot find container: kserve-container")
})

t.Run("Test InjectModelcar prioritizes kserve-container over worker-container", func(t *testing.T) {
pod := createTestPodForModelcarWithBothContainers()
injector := &StorageInitializerInjector{config: &kserveTypes.StorageInitializerConfig{}}

err := injector.InjectModelcar(pod)
require.NoError(t, err)

// Both containers should have volume mounts but kserve-container should be prioritized
kserveContainer := utils.GetContainerWithName(&pod.Spec, constants.InferenceServiceContainerName)
workerContainer := utils.GetContainerWithName(&pod.Spec, constants.WorkerContainerName)

assert.NotNil(t, kserveContainer)
assert.NotNil(t, workerContainer)

// Check that kserve-container got the volume mount
kserveHasMount := false
for _, mount := range kserveContainer.VolumeMounts {
if mount.Name == constants.StorageInitializerVolumeName {
kserveHasMount = true
break
}
}
assert.True(t, kserveHasMount, "kserve-container should have storage initializer volume mount")
})

t.Run("Test InjectModelcar with transformer and worker containers", func(t *testing.T) {
pod := createTestPodForModelcarWithWorkerAndTransformer()
injector := &StorageInitializerInjector{config: &kserveTypes.StorageInitializerConfig{}}

err := injector.InjectModelcar(pod)
require.NoError(t, err)

// Check both worker and transformer containers got volume mounts
workerContainer := utils.GetContainerWithName(&pod.Spec, constants.WorkerContainerName)
transformerContainer := utils.GetContainerWithName(&pod.Spec, constants.TransformerContainerName)

assert.NotNil(t, workerContainer)
assert.NotNil(t, transformerContainer)

// Both should have volume mounts
workerHasMount := false
for _, mount := range workerContainer.VolumeMounts {
if mount.Name == constants.StorageInitializerVolumeName {
workerHasMount = true
break
}
}
assert.True(t, workerHasMount, "Worker container should have storage initializer volume mount")

transformerHasMount := false
for _, mount := range transformerContainer.VolumeMounts {
if mount.Name == constants.StorageInitializerVolumeName {
transformerHasMount = true
break
}
}
assert.True(t, transformerHasMount, "Transformer container should have storage initializer volume mount")
})
}

// Helper functions for multi-node testing

func createTestPodForModelcarWithWorkerContainer() *corev1.Pod {
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
constants.StorageInitializerSourceUriInternalAnnotationKey: constants.OciURIPrefix + "myrepo/mymodelimage",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{Name: constants.WorkerContainerName},
},
},
}
}

func createTestPodForModelcarNoValidContainer() *corev1.Pod {
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
constants.StorageInitializerSourceUriInternalAnnotationKey: constants.OciURIPrefix + "myrepo/mymodelimage",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{Name: "some-other-container"},
},
},
}
}

func createTestPodForModelcarWithBothContainers() *corev1.Pod {
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
constants.StorageInitializerSourceUriInternalAnnotationKey: constants.OciURIPrefix + "myrepo/mymodelimage",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{Name: constants.InferenceServiceContainerName},
{Name: constants.WorkerContainerName},
},
},
}
}

func createTestPodForModelcarWithWorkerAndTransformer() *corev1.Pod {
return &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
constants.StorageInitializerSourceUriInternalAnnotationKey: constants.OciURIPrefix + "myrepo/mymodelimage",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{Name: constants.WorkerContainerName},
{Name: constants.TransformerContainerName},
},
},
}
}
Loading