Skip to content

Commit c5067bd

Browse files
Merge pull request #1 from VedantMahabaleshwarkar/rhodsmain
Merge from upstream 0.11.0-alpha
2 parents 0bad407 + c99f9f4 commit c5067bd

File tree

3 files changed

+245
-4
lines changed

3 files changed

+245
-4
lines changed

model-serving-puller/puller/puller.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
_ "github.com/kserve/modelmesh-runtime-adapter/pullman/storageproviders/azure"
3131
_ "github.com/kserve/modelmesh-runtime-adapter/pullman/storageproviders/gcs"
3232
_ "github.com/kserve/modelmesh-runtime-adapter/pullman/storageproviders/http"
33+
_ "github.com/kserve/modelmesh-runtime-adapter/pullman/storageproviders/pvc"
3334
_ "github.com/kserve/modelmesh-runtime-adapter/pullman/storageproviders/s3"
3435
)
3536

@@ -183,10 +184,13 @@ func (s *Puller) ProcessLoadModelRequest(ctx context.Context, req *mmesh.LoadMod
183184
}
184185

185186
// update model path to an absolute path in the local filesystem
186-
modelFullPath, joinErr := util.SecureJoin(modelDir, modelPathFilename)
187-
if joinErr != nil {
188-
return nil, fmt.Errorf("Error joining paths '%s' and '%s': %w", modelDir, modelPathFilename, joinErr)
189-
}
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
193+
190194
req.ModelPath = modelFullPath
191195

192196
// if it was included, update schema path to an absolute path in the local filesystem
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// Copyright 2022 IBM Corporation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package pvcprovider
15+
16+
import (
17+
"context"
18+
"fmt"
19+
"os"
20+
"path/filepath"
21+
22+
"github.com/go-logr/logr"
23+
24+
"github.com/kserve/modelmesh-runtime-adapter/internal/util"
25+
"github.com/kserve/modelmesh-runtime-adapter/pullman"
26+
)
27+
28+
const (
29+
// config fields
30+
configPVCName = "name"
31+
32+
// defaults
33+
defaultPVCMountBase = "/pvc_mounts"
34+
)
35+
36+
type pvcProvider struct {
37+
pvcMountBase string
38+
}
39+
40+
// pvcProvider implements StorageProvider
41+
var _ pullman.StorageProvider = (*pvcProvider)(nil)
42+
43+
func (p pvcProvider) GetKey(config pullman.Config) string {
44+
// Since the same instance of the repository can be used for all PVCs, there is no need to distinguish them here.
45+
return ""
46+
}
47+
48+
func (p pvcProvider) NewRepository(config pullman.Config, log logr.Logger) (pullman.RepositoryClient, error) {
49+
if p.pvcMountBase == "" {
50+
p.pvcMountBase = defaultPVCMountBase
51+
}
52+
53+
fileInfo, err := os.Stat(p.pvcMountBase)
54+
if err != nil {
55+
if os.IsNotExist(err) {
56+
return nil, fmt.Errorf("the PVC mount base '%s' doesn't exist: %w", p.pvcMountBase, err)
57+
}
58+
return nil, fmt.Errorf("failed to access the PVC mount base '%s': %w", p.pvcMountBase, err)
59+
}
60+
61+
if !fileInfo.IsDir() {
62+
return nil, fmt.Errorf("the PVC mount base '%s' is not a directory", p.pvcMountBase)
63+
}
64+
65+
return &pvcRepositoryClient{
66+
pvcProvider: p,
67+
log: log,
68+
}, nil
69+
}
70+
71+
type pvcRepositoryClient struct {
72+
pvcProvider pvcProvider
73+
log logr.Logger
74+
}
75+
76+
// pvcRepositoryClient implements RepositoryClient
77+
var _ pullman.RepositoryClient = (*pvcRepositoryClient)(nil)
78+
79+
func (r *pvcRepositoryClient) Pull(ctx context.Context, pc pullman.PullCommand) error {
80+
targets := pc.Targets
81+
destDir := pc.Directory
82+
83+
// Process per-command configuration
84+
pvcName, ok := pullman.GetString(pc.RepositoryConfig, configPVCName)
85+
if !ok {
86+
return fmt.Errorf("required configuration '%s' missing from command", configPVCName)
87+
}
88+
89+
// create destination directories
90+
if mkdirErr := os.MkdirAll(destDir, 0755); mkdirErr != nil {
91+
return fmt.Errorf("unable to create directories '%s': %w", destDir, mkdirErr)
92+
}
93+
94+
pvcDir, joinErr := util.SecureJoin(r.pvcProvider.pvcMountBase, pvcName)
95+
if joinErr != nil {
96+
return fmt.Errorf("unable to join paths '%s' and '%s': %v", r.pvcProvider.pvcMountBase, pvcName, joinErr)
97+
}
98+
r.log.V(1).Info("The PVC directory is set", "pvcDir", pvcDir)
99+
100+
for _, pt := range targets {
101+
fullModelPath, joinErr := util.SecureJoin(pvcDir, pt.RemotePath)
102+
if joinErr != nil {
103+
return fmt.Errorf("unable to join paths '%s' and '%s': %v", pvcDir, pt.RemotePath, joinErr)
104+
}
105+
r.log.V(1).Info("The model path is set", "fullModelPath", fullModelPath)
106+
107+
// check the local model path /pvcMountBase/pvcName/modelPath exists
108+
if _, err := os.Stat(fullModelPath); err != nil {
109+
return fmt.Errorf("unable to access model local path '%s': %w", fullModelPath, err)
110+
}
111+
112+
// create symlink
113+
linkPath, err := util.SecureJoin(destDir, filepath.Base(fullModelPath))
114+
if err != nil {
115+
return fmt.Errorf("unable to join paths '%s' and '%s': %v", destDir, filepath.Base(fullModelPath), err)
116+
}
117+
if err := os.Symlink(fullModelPath, linkPath); err != nil {
118+
return fmt.Errorf("unable to create symlink '%s': %w", linkPath, err)
119+
}
120+
}
121+
122+
return nil
123+
}
124+
125+
func init() {
126+
p := pvcProvider{}
127+
pullman.RegisterProvider("pvc", p)
128+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright 2022 IBM Corporation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package pvcprovider
15+
16+
import (
17+
"context"
18+
"os"
19+
"testing"
20+
21+
"github.com/go-logr/logr"
22+
"github.com/kserve/modelmesh-runtime-adapter/pullman"
23+
"github.com/stretchr/testify/assert"
24+
"sigs.k8s.io/controller-runtime/pkg/log/zap"
25+
)
26+
27+
func newPVCProvider(t *testing.T) (pvcProvider, logr.Logger) {
28+
p := pvcProvider{}
29+
log := zap.New()
30+
return p, log
31+
}
32+
33+
func newPVCRepositoryClient(t *testing.T) *pvcRepositoryClient {
34+
pvcMountBase, _ := os.Getwd()
35+
p, log := newPVCProvider(t)
36+
p.pvcMountBase = pvcMountBase
37+
pvcrc := pvcRepositoryClient{
38+
pvcProvider: p,
39+
log: log,
40+
}
41+
return &pvcrc
42+
}
43+
44+
func Test_NewRepository(t *testing.T) {
45+
pvcMountBase, _ := os.Getwd()
46+
p, log := newPVCProvider(t)
47+
p.pvcMountBase = pvcMountBase
48+
c := pullman.NewRepositoryConfig("pvc", nil)
49+
c.Set("name", "pvcName")
50+
_, err := p.NewRepository(c, log)
51+
assert.NoError(t, err)
52+
}
53+
54+
func Test_Verify_Directory(t *testing.T) {
55+
pvcName := "pvcName"
56+
modelDir := "modelDir"
57+
modelID := "testId"
58+
pvcRc := newPVCRepositoryClient(t)
59+
c := pullman.NewRepositoryConfig("pvc", nil)
60+
c.Set("name", pvcName)
61+
62+
pvcNameDir := pvcRc.pvcProvider.pvcMountBase + "/" + pvcName
63+
pvcModelDir := pvcNameDir + "/" + modelDir
64+
serveModelDir := pvcRc.pvcProvider.pvcMountBase + "/" + modelID
65+
66+
inputPullCommand := pullman.PullCommand{
67+
RepositoryConfig: c,
68+
Directory: serveModelDir,
69+
Targets: []pullman.Target{
70+
{
71+
RemotePath: modelDir,
72+
},
73+
},
74+
}
75+
76+
// should return error because the directory doesn't exist
77+
err := pvcRc.Pull(context.Background(), inputPullCommand)
78+
assert.Error(t, err)
79+
80+
err = os.MkdirAll(pvcModelDir, os.ModePerm)
81+
assert.NoError(t, err)
82+
83+
// should not return error because the directory exists
84+
err = pvcRc.Pull(context.Background(), inputPullCommand)
85+
assert.NoError(t, err)
86+
87+
err = os.RemoveAll(pvcNameDir)
88+
assert.NoError(t, err)
89+
90+
err = os.RemoveAll(serveModelDir)
91+
assert.NoError(t, err)
92+
}
93+
94+
func Test_GetKey(t *testing.T) {
95+
provider := pvcProvider{}
96+
97+
createTestConfig := func(pvcName string) *pullman.RepositoryConfig {
98+
config := pullman.NewRepositoryConfig("pvc", nil)
99+
config.Set(configPVCName, pvcName)
100+
return config
101+
}
102+
103+
// different pvc names should have the same key
104+
t.Run("shouldChangeForTokenUri", func(t *testing.T) {
105+
config1 := createTestConfig("pvcName1")
106+
config2 := createTestConfig("pvcName2")
107+
assert.Equal(t, provider.GetKey(config1), provider.GetKey(config2))
108+
})
109+
}

0 commit comments

Comments
 (0)