Skip to content

Commit 6632a70

Browse files
Copilotmudler
andauthored
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]>
1 parent 49fc20f commit 6632a70

File tree

3 files changed

+96
-3
lines changed

3 files changed

+96
-3
lines changed

core/gallery/models.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,10 @@ func DeleteModelFromSystem(systemState *system.SystemState, name string) error {
373373
if strings.HasPrefix(f.Name(), "._gallery_") {
374374
continue
375375
}
376-
if !strings.HasSuffix(f.Name(), ".yaml") || !strings.HasSuffix(f.Name(), ".yml") {
376+
if !strings.HasSuffix(f.Name(), ".yaml") && !strings.HasSuffix(f.Name(), ".yml") {
377377
continue
378378
}
379-
if f.Name() == name {
379+
if f.Name() == fmt.Sprintf("%s.yaml", name) || f.Name() == fmt.Sprintf("%s.yml", name) {
380380
continue
381381
}
382382

core/gallery/models_test.go

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

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ require (
5959
go.opentelemetry.io/otel/metric v1.38.0
6060
go.opentelemetry.io/otel/sdk/metric v1.38.0
6161
google.golang.org/grpc v1.76.0
62+
google.golang.org/protobuf v1.36.8
6263
gopkg.in/yaml.v2 v2.4.0
6364
gopkg.in/yaml.v3 v3.0.1
6465
oras.land/oras-go/v2 v2.6.0
@@ -148,7 +149,6 @@ require (
148149
golang.org/x/oauth2 v0.30.0 // indirect
149150
golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053 // indirect
150151
golang.org/x/time v0.12.0 // indirect
151-
google.golang.org/protobuf v1.36.8 // indirect
152152
)
153153

154154
require (

0 commit comments

Comments
 (0)