Skip to content

Commit 3fd2814

Browse files
fix(puller): fixes related to an empty model path (#52)
#### Motivation Removes some buggy behavior related to the handling of an empty `modelPath` being passed into the puller #### Modifications - use `filepath.Join()` instead of manual string concatenation with the file separator to handle an empty `modelPathFilename` - in the http storageProvider, remove duplication of the `localPath` into the rendered path - just to not it: I think this bug has not been caught because using the HTTP provider with a `storageURI` means that the local path is empty - this will need to be fixed before kserve/modelmesh-serving#382 can be merged - add check to ensure that the local path for files downloaded by the puller is always a path within the generated model dir (prevents the generated dir name from being the model file as was seen in kserve#41 (comment)) - if no path is extracted from the request `ModelPath`, use `_model` by default #### Result Resolves: kserve#41 Signed-off-by: Travis Johnson <[email protected]>
1 parent f9dc1dc commit 3fd2814

File tree

3 files changed

+45
-10
lines changed

3 files changed

+45
-10
lines changed

model-serving-puller/puller/puller.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,16 @@ func (s *Puller) ProcessLoadModelRequest(ctx context.Context, req *mmesh.LoadMod
142142
// build and execute the pull command
143143

144144
// name the local files based on the last element of the paths
145-
// TODO: should have some sanitization for filenames
146-
modelPathFilename := filepath.Base(req.ModelPath)
145+
// or set a default if the ModelPath is empty or just /'s
146+
var modelPathFilename string
147+
switch basePath := filepath.Base(req.ModelPath); basePath {
148+
case ".", string(filepath.Separator):
149+
modelPathFilename = "_model"
150+
default:
151+
// TODO: should have some sanitization for filenames
152+
modelPathFilename = basePath
153+
}
154+
147155
targets := []pullman.Target{
148156
{
149157
RemotePath: req.ModelPath,
@@ -184,12 +192,10 @@ func (s *Puller) ProcessLoadModelRequest(ctx context.Context, req *mmesh.LoadMod
184192
}
185193

186194
// update model path to an absolute path in the local filesystem
187-
// commment out SecureJoin since it doesn't handle symlinks well
188-
// modelFullPath, joinErr := util.SecureJoin(modelDir, modelPathFilename)
189-
// if joinErr != nil {
190-
// return nil, fmt.Errorf("Error joining paths '%s' and '%s': %w", modelDir, modelPathFilename, joinErr)
191-
// }
192-
modelFullPath := modelDir + string(filepath.Separator) + modelPathFilename
195+
196+
// SecureJoin doesn't allow symlinks pointing outside the scope of the first element, which breaks PVC support since
197+
// pullman will create a symlink to the mounted PVC in /pvc_mounts
198+
modelFullPath := filepath.Join(modelDir, modelPathFilename)
193199

194200
req.ModelPath = modelFullPath
195201

pullman/storageproviders/http/provider.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,9 @@ func (r *httpRepository) Pull(ctx context.Context, pc pullman.PullCommand) error
160160
localPath = path.Base(pt.RemotePath)
161161
}
162162

163-
filePath, joinErr := util.SecureJoin(destDir, pt.LocalPath, localPath)
163+
filePath, joinErr := util.SecureJoin(destDir, localPath)
164164
if joinErr != nil {
165-
return fmt.Errorf("error joining filepaths '%s' and '%s': %w", pt.LocalPath, localPath, joinErr)
165+
return fmt.Errorf("error joining filepaths '%s' and '%s': %w", destDir, localPath, joinErr)
166166
}
167167
r.log.V(1).Info("constructed local path to download file", "local", filePath, "remote", pt.RemotePath)
168168

pullman/storageproviders/http/provider_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,35 @@ func Test_Download_SimpleFile(t *testing.T) {
116116
assert.NoError(t, err)
117117
}
118118

119+
func Test_Download_RenameFile(t *testing.T) {
120+
_, _, testRepo, mockClient, _ := newTestMocks(t)
121+
122+
testURL := "http://someurl:8080"
123+
c := pullman.NewRepositoryConfig("http", nil)
124+
c.Set("url", testURL)
125+
126+
downloadDir := filepath.Join("test", "output")
127+
inputPullCommand := pullman.PullCommand{
128+
RepositoryConfig: c,
129+
Directory: downloadDir,
130+
Targets: []pullman.Target{
131+
{
132+
RemotePath: "models/some_model_file",
133+
LocalPath: "local/path/my-model",
134+
},
135+
},
136+
}
137+
138+
expectedURL := testURL + "/models/some_model_file"
139+
expectedFile := filepath.Join(downloadDir, "local", "path", "my-model")
140+
mockClient.EXPECT().download(gomock.Any(), newHttpRequestMatcher("GET", expectedURL), gomock.Eq(expectedFile)).
141+
Return(nil).
142+
Times(1)
143+
144+
err := testRepo.Pull(context.Background(), inputPullCommand)
145+
assert.NoError(t, err)
146+
}
147+
119148
func Test_GetKey(t *testing.T) {
120149
provider := httpProvider{}
121150

0 commit comments

Comments
 (0)