Skip to content

Commit 2dc89da

Browse files
Copilotmudler
andcommitted
fix: prevent deletion of model files shared by multiple configurations (#7317)
* Initial plan * fix: do not delete files if used by other configured models - Fixed bug in DeleteModelFromSystem where OR was used instead of AND for file suffix check - Fixed bug where model config filename comparison was incorrect - Added comprehensive Ginkgo test to verify shared model files are not deleted Co-authored-by: mudler <[email protected]> * fix: prevent deletion of model files shared by multiple configurations Co-authored-by: mudler <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: mudler <[email protected]> Signed-off-by: Ettore Di Giacinto <[email protected]>
1 parent eb0f501 commit 2dc89da

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

core/gallery/models.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,10 @@ func DeleteModelFromSystem(systemState *system.SystemState, name string) error {
381381
if strings.HasPrefix(f.Name(), "._gallery_") {
382382
continue
383383
}
384-
if !strings.HasSuffix(f.Name(), ".yaml") || !strings.HasSuffix(f.Name(), ".yml") {
384+
if !strings.HasSuffix(f.Name(), ".yaml") && !strings.HasSuffix(f.Name(), ".yml") {
385385
continue
386386
}
387-
if f.Name() == name {
387+
if f.Name() == fmt.Sprintf("%s.yaml", name) || f.Name() == fmt.Sprintf("%s.yml", name) {
388388
continue
389389
}
390390

core/gallery/models_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,5 +183,98 @@ var _ = Describe("Model test", func() {
183183
_, err = InstallModel(context.TODO(), systemState, "../../../foo", c, map[string]interface{}{}, func(string, string, string, float64) {}, true)
184184
Expect(err).To(HaveOccurred())
185185
})
186+
187+
It("does not delete shared model files when one config is deleted", func() {
188+
tempdir, err := os.MkdirTemp("", "test")
189+
Expect(err).ToNot(HaveOccurred())
190+
defer os.RemoveAll(tempdir)
191+
192+
systemState, err := system.GetSystemState(
193+
system.WithModelPath(tempdir),
194+
)
195+
Expect(err).ToNot(HaveOccurred())
196+
197+
// Create a shared model file
198+
sharedModelFile := filepath.Join(tempdir, "shared_model.bin")
199+
err = os.WriteFile(sharedModelFile, []byte("fake model content"), 0600)
200+
Expect(err).ToNot(HaveOccurred())
201+
202+
// Create first model configuration
203+
config1 := `name: model1
204+
model: shared_model.bin`
205+
err = os.WriteFile(filepath.Join(tempdir, "model1.yaml"), []byte(config1), 0600)
206+
Expect(err).ToNot(HaveOccurred())
207+
208+
// Create first model's gallery file
209+
galleryConfig1 := ModelConfig{
210+
Name: "model1",
211+
Files: []File{
212+
{Filename: "shared_model.bin"},
213+
},
214+
}
215+
galleryData1, err := yaml.Marshal(galleryConfig1)
216+
Expect(err).ToNot(HaveOccurred())
217+
err = os.WriteFile(filepath.Join(tempdir, "._gallery_model1.yaml"), galleryData1, 0600)
218+
Expect(err).ToNot(HaveOccurred())
219+
220+
// Create second model configuration sharing the same model file
221+
config2 := `name: model2
222+
model: shared_model.bin`
223+
err = os.WriteFile(filepath.Join(tempdir, "model2.yaml"), []byte(config2), 0600)
224+
Expect(err).ToNot(HaveOccurred())
225+
226+
// Create second model's gallery file
227+
galleryConfig2 := ModelConfig{
228+
Name: "model2",
229+
Files: []File{
230+
{Filename: "shared_model.bin"},
231+
},
232+
}
233+
galleryData2, err := yaml.Marshal(galleryConfig2)
234+
Expect(err).ToNot(HaveOccurred())
235+
err = os.WriteFile(filepath.Join(tempdir, "._gallery_model2.yaml"), galleryData2, 0600)
236+
Expect(err).ToNot(HaveOccurred())
237+
238+
// Verify both configurations exist
239+
_, err = os.Stat(filepath.Join(tempdir, "model1.yaml"))
240+
Expect(err).ToNot(HaveOccurred())
241+
_, err = os.Stat(filepath.Join(tempdir, "model2.yaml"))
242+
Expect(err).ToNot(HaveOccurred())
243+
244+
// Verify the shared model file exists
245+
_, err = os.Stat(sharedModelFile)
246+
Expect(err).ToNot(HaveOccurred())
247+
248+
// Delete the first model
249+
err = DeleteModelFromSystem(systemState, "model1")
250+
Expect(err).ToNot(HaveOccurred())
251+
252+
// Verify the first configuration is deleted
253+
_, err = os.Stat(filepath.Join(tempdir, "model1.yaml"))
254+
Expect(err).To(HaveOccurred())
255+
Expect(errors.Is(err, os.ErrNotExist)).To(BeTrue())
256+
257+
// Verify the shared model file still exists (not deleted because model2 still uses it)
258+
_, err = os.Stat(sharedModelFile)
259+
Expect(err).ToNot(HaveOccurred(), "shared model file should not be deleted when used by other configs")
260+
261+
// Verify the second configuration still exists
262+
_, err = os.Stat(filepath.Join(tempdir, "model2.yaml"))
263+
Expect(err).ToNot(HaveOccurred())
264+
265+
// Now delete the second model
266+
err = DeleteModelFromSystem(systemState, "model2")
267+
Expect(err).ToNot(HaveOccurred())
268+
269+
// Verify the second configuration is deleted
270+
_, err = os.Stat(filepath.Join(tempdir, "model2.yaml"))
271+
Expect(err).To(HaveOccurred())
272+
Expect(errors.Is(err, os.ErrNotExist)).To(BeTrue())
273+
274+
// Verify the shared model file is now deleted (no more references)
275+
_, err = os.Stat(sharedModelFile)
276+
Expect(err).To(HaveOccurred(), "shared model file should be deleted when no configs reference it")
277+
Expect(errors.Is(err, os.ErrNotExist)).To(BeTrue())
278+
})
186279
})
187280
})

0 commit comments

Comments
 (0)