Skip to content

Commit d0fb235

Browse files
committed
Revert "fix(gallery): correctly show status for downloading OCI images"
This reverts commit 780d034.
1 parent 780d034 commit d0fb235

File tree

7 files changed

+38
-213
lines changed

7 files changed

+38
-213
lines changed

core/gallery/backends.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func InstallBackend(basePath string, config *GalleryBackend, downloadStatus func
162162
return fmt.Errorf("failed to create backend path %q: %v", backendPath, err)
163163
}
164164

165-
if err := oci.ExtractOCIImage(img, config.URI, backendPath, downloadStatus); err != nil {
165+
if err := oci.ExtractOCIImage(img, backendPath, downloadStatus); err != nil {
166166
return fmt.Errorf("failed to extract image %q: %v", config.URI, err)
167167
}
168168

@@ -246,15 +246,6 @@ func ListSystemBackends(basePath string) (map[string]string, error) {
246246
for _, backend := range backends {
247247
if backend.IsDir() {
248248
runFile := filepath.Join(basePath, backend.Name(), runFile)
249-
// Skip if runfile and metadata file don't exist
250-
if _, err := os.Stat(runFile); os.IsNotExist(err) {
251-
continue
252-
}
253-
metadataFilePath := filepath.Join(basePath, backend.Name(), metadataFile)
254-
if _, err := os.Stat(metadataFilePath); os.IsNotExist(err) {
255-
continue
256-
}
257-
258249
backendsNames[backend.Name()] = runFile
259250

260251
// Check for alias in metadata

core/gallery/gallery.go

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,7 @@ func AvailableGalleryModels(galleries []config.Gallery, basePath string) (Galler
121121

122122
// Get models from galleries
123123
for _, gallery := range galleries {
124-
galleryModels, err := getGalleryElements[*GalleryModel](gallery, basePath, func(model *GalleryModel) bool {
125-
if _, err := os.Stat(filepath.Join(basePath, fmt.Sprintf("%s.yaml", model.GetName()))); err == nil {
126-
return true
127-
}
128-
return false
129-
})
124+
galleryModels, err := getGalleryElements[*GalleryModel](gallery, basePath)
130125
if err != nil {
131126
return nil, err
132127
}
@@ -142,14 +137,7 @@ func AvailableBackends(galleries []config.Gallery, basePath string) (GalleryElem
142137

143138
// Get models from galleries
144139
for _, gallery := range galleries {
145-
galleryModels, err := getGalleryElements[*GalleryBackend](gallery, basePath, func(backend *GalleryBackend) bool {
146-
backends, err := ListSystemBackends(basePath)
147-
if err != nil {
148-
return false
149-
}
150-
_, exists := backends[backend.GetName()]
151-
return exists
152-
})
140+
galleryModels, err := getGalleryElements[*GalleryBackend](gallery, basePath)
153141
if err != nil {
154142
return nil, err
155143
}
@@ -174,7 +162,7 @@ func findGalleryURLFromReferenceURL(url string, basePath string) (string, error)
174162
return refFile, err
175163
}
176164

177-
func getGalleryElements[T GalleryElement](gallery config.Gallery, basePath string, isInstalledCallback func(T) bool) ([]T, error) {
165+
func getGalleryElements[T GalleryElement](gallery config.Gallery, basePath string) ([]T, error) {
178166
var models []T = []T{}
179167

180168
if strings.HasSuffix(gallery.URL, ".ref") {
@@ -199,7 +187,15 @@ func getGalleryElements[T GalleryElement](gallery config.Gallery, basePath strin
199187
// Add gallery to models
200188
for _, model := range models {
201189
model.SetGallery(gallery)
202-
model.SetInstalled(isInstalledCallback(model))
190+
// we check if the model was already installed by checking if the config file exists
191+
// TODO: (what to do if the model doesn't install a config file?)
192+
// TODO: This is sub-optimal now that the gallery handles both backends and models - we need to abstract this away
193+
if _, err := os.Stat(filepath.Join(basePath, fmt.Sprintf("%s.yaml", model.GetName()))); err == nil {
194+
model.SetInstalled(true)
195+
}
196+
if _, err := os.Stat(filepath.Join(basePath, model.GetName())); err == nil {
197+
model.SetInstalled(true)
198+
}
203199
}
204200
return models, nil
205201
}

core/http/routes/ui_backend_gallery.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func registerBackendGalleryRoutes(app *fiber.App, appConfig *config.ApplicationC
223223
return c.SendString(elements.ProgressBar("0"))
224224
}
225225

226-
if status.Progress == 100 && status.Processed && status.Message == "completed" {
226+
if status.Progress == 100 {
227227
c.Set("HX-Trigger", "done") // this triggers /browse/backend/job/:uid
228228
return c.SendString(elements.ProgressBar("100"))
229229
}

core/http/routes/ui_gallery.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ func registerGalleryRoutes(app *fiber.App, cl *config.BackendConfigLoader, appCo
243243
return c.SendString(elements.ProgressBar("0"))
244244
}
245245

246-
if status.Progress == 100 && status.Processed && status.Message == "completed" {
246+
if status.Progress == 100 {
247247
c.Set("HX-Trigger", "done") // this triggers /browse/job/:uid (which is when the job is done)
248248
return c.SendString(elements.ProgressBar("100"))
249249
}

pkg/downloader/uri.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ func (uri URI) DownloadFile(filePath, sha string, fileN, total int, downloadStat
256256
return fmt.Errorf("failed to get image %q: %v", url, err)
257257
}
258258

259-
return oci.ExtractOCIImage(img, url, filepath.Dir(filePath), downloadStatus)
259+
return oci.ExtractOCIImage(img, filepath.Dir(filePath), downloadStatus)
260260
}
261261

262262
// Check if the file already exists

pkg/oci/image.go

Lines changed: 21 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"io"
88
"net/http"
9-
"os"
109
"runtime"
1110
"strconv"
1211
"strings"
@@ -22,7 +21,6 @@ import (
2221
"github.com/google/go-containerregistry/pkg/v1/mutate"
2322
"github.com/google/go-containerregistry/pkg/v1/remote"
2423
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
25-
"github.com/google/go-containerregistry/pkg/v1/tarball"
2624
)
2725

2826
// ref: https://github.com/mudler/luet/blob/master/pkg/helpers/docker/docker.go#L117
@@ -97,30 +95,31 @@ func (pw *progressWriter) Write(p []byte) (int, error) {
9795
}
9896

9997
// ExtractOCIImage will extract a given targetImage into a given targetDestination
100-
func ExtractOCIImage(img v1.Image, imageRef string, targetDestination string, downloadStatus func(string, string, string, float64)) error {
101-
// Create a temporary tar file
102-
tmpTarFile, err := os.CreateTemp("", "localai-oci-*.tar")
103-
if err != nil {
104-
return fmt.Errorf("failed to create temporary tar file: %v", err)
105-
}
106-
defer os.Remove(tmpTarFile.Name())
107-
defer tmpTarFile.Close()
98+
func ExtractOCIImage(img v1.Image, targetDestination string, downloadStatus func(string, string, string, float64)) error {
99+
var reader io.Reader
100+
reader = mutate.Extract(img)
108101

109-
// Download the image as tar with progress tracking
110-
err = DownloadOCIImageTar(img, imageRef, tmpTarFile.Name(), downloadStatus)
111-
if err != nil {
112-
return fmt.Errorf("failed to download image tar: %v", err)
102+
if downloadStatus != nil {
103+
var totalSize int64
104+
layers, err := img.Layers()
105+
if err != nil {
106+
return err
107+
}
108+
for _, layer := range layers {
109+
size, err := layer.Size()
110+
if err != nil {
111+
return err
112+
}
113+
totalSize += size
114+
}
115+
reader = io.TeeReader(reader, &progressWriter{total: totalSize, downloadStatus: downloadStatus})
113116
}
114117

115-
downloadStatus("Extracting", "", "", 0)
116-
117-
// Extract the tar file to the target destination
118-
err = ExtractOCIImageFromTar(tmpTarFile.Name(), imageRef, targetDestination, downloadStatus)
119-
if err != nil {
120-
return fmt.Errorf("failed to extract image tar: %v", err)
121-
}
118+
_, err := archive.Apply(context.Background(),
119+
targetDestination, reader,
120+
archive.WithNoSameOwner())
122121

123-
return nil
122+
return err
124123
}
125124

126125
func ParseImageParts(image string) (tag, repository, dstimage string) {
@@ -206,164 +205,3 @@ func GetOCIImageSize(targetImage, targetPlatform string, auth *registrytypes.Aut
206205

207206
return size, nil
208207
}
209-
210-
// DownloadOCIImageTar downloads the compressed layers of an image and then creates an uncompressed tar
211-
// This provides accurate size estimation and allows for later extraction
212-
func DownloadOCIImageTar(img v1.Image, imageRef string, tarFilePath string, downloadStatus func(string, string, string, float64)) error {
213-
// Get layers to calculate total compressed size for estimation
214-
layers, err := img.Layers()
215-
if err != nil {
216-
return fmt.Errorf("failed to get layers: %v", err)
217-
}
218-
219-
// Calculate total compressed size for progress tracking
220-
var totalCompressedSize int64
221-
for _, layer := range layers {
222-
size, err := layer.Size()
223-
if err != nil {
224-
return fmt.Errorf("failed to get layer size: %v", err)
225-
}
226-
totalCompressedSize += size
227-
}
228-
229-
// Create a temporary directory to store the compressed layers
230-
tmpDir, err := os.MkdirTemp("", "localai-oci-layers-*")
231-
if err != nil {
232-
return fmt.Errorf("failed to create temporary directory: %v", err)
233-
}
234-
defer os.RemoveAll(tmpDir)
235-
236-
// Download all compressed layers with progress tracking
237-
var downloadedLayers []v1.Layer
238-
var downloadedSize int64
239-
240-
// Extract image name from the reference for display
241-
imageName := imageRef
242-
for i, layer := range layers {
243-
layerSize, err := layer.Size()
244-
if err != nil {
245-
return fmt.Errorf("failed to get layer size: %v", err)
246-
}
247-
248-
// Create a temporary file for this layer
249-
layerFile := fmt.Sprintf("%s/layer-%d.tar.gz", tmpDir, i)
250-
file, err := os.Create(layerFile)
251-
if err != nil {
252-
return fmt.Errorf("failed to create layer file: %v", err)
253-
}
254-
255-
// Create progress writer for this layer
256-
var writer io.Writer = file
257-
if downloadStatus != nil {
258-
writer = io.MultiWriter(file, &progressWriter{
259-
total: totalCompressedSize,
260-
fileName: fmt.Sprintf("Downloading %d/%d %s", i+1, len(layers), imageName),
261-
downloadStatus: downloadStatus,
262-
})
263-
}
264-
265-
// Download the compressed layer
266-
layerReader, err := layer.Compressed()
267-
if err != nil {
268-
file.Close()
269-
return fmt.Errorf("failed to get compressed layer: %v", err)
270-
}
271-
272-
_, err = io.Copy(writer, layerReader)
273-
file.Close()
274-
if err != nil {
275-
return fmt.Errorf("failed to download layer %d: %v", i, err)
276-
}
277-
278-
// Load the downloaded layer
279-
downloadedLayer, err := tarball.LayerFromFile(layerFile)
280-
if err != nil {
281-
return fmt.Errorf("failed to load downloaded layer: %v", err)
282-
}
283-
284-
downloadedLayers = append(downloadedLayers, downloadedLayer)
285-
downloadedSize += layerSize
286-
}
287-
288-
// Create a local image from the downloaded layers
289-
localImg, err := mutate.AppendLayers(img, downloadedLayers...)
290-
if err != nil {
291-
return fmt.Errorf("failed to create local image: %v", err)
292-
}
293-
294-
// Now extract the uncompressed tar from the local image
295-
tarFile, err := os.Create(tarFilePath)
296-
if err != nil {
297-
return fmt.Errorf("failed to create tar file: %v", err)
298-
}
299-
defer tarFile.Close()
300-
301-
// Extract uncompressed tar from local image
302-
extractReader := mutate.Extract(localImg)
303-
_, err = io.Copy(tarFile, extractReader)
304-
if err != nil {
305-
return fmt.Errorf("failed to extract uncompressed tar: %v", err)
306-
}
307-
308-
return nil
309-
}
310-
311-
// ExtractOCIImageFromTar extracts an image from a previously downloaded tar file
312-
func ExtractOCIImageFromTar(tarFilePath, imageRef, targetDestination string, downloadStatus func(string, string, string, float64)) error {
313-
// Open the tar file
314-
tarFile, err := os.Open(tarFilePath)
315-
if err != nil {
316-
return fmt.Errorf("failed to open tar file: %v", err)
317-
}
318-
defer tarFile.Close()
319-
320-
// Get file size for progress tracking
321-
fileInfo, err := tarFile.Stat()
322-
if err != nil {
323-
return fmt.Errorf("failed to get file info: %v", err)
324-
}
325-
326-
var reader io.Reader = tarFile
327-
if downloadStatus != nil {
328-
reader = io.TeeReader(tarFile, &progressWriter{
329-
total: fileInfo.Size(),
330-
fileName: fmt.Sprintf("Extracting %s", imageRef),
331-
downloadStatus: downloadStatus,
332-
})
333-
}
334-
335-
// Extract the tar file
336-
_, err = archive.Apply(context.Background(),
337-
targetDestination, reader,
338-
archive.WithNoSameOwner())
339-
340-
return err
341-
}
342-
343-
// GetOCIImageUncompressedSize returns the total uncompressed size of an image
344-
func GetOCIImageUncompressedSize(targetImage, targetPlatform string, auth *registrytypes.AuthConfig, t http.RoundTripper) (int64, error) {
345-
var totalSize int64
346-
var img v1.Image
347-
var err error
348-
349-
img, err = GetImage(targetImage, targetPlatform, auth, t)
350-
if err != nil {
351-
return totalSize, err
352-
}
353-
354-
layers, err := img.Layers()
355-
if err != nil {
356-
return totalSize, err
357-
}
358-
359-
for _, layer := range layers {
360-
// Use compressed size as an approximation since uncompressed size is not directly available
361-
size, err := layer.Size()
362-
if err != nil {
363-
return totalSize, err
364-
}
365-
totalSize += size
366-
}
367-
368-
return totalSize, nil
369-
}

pkg/oci/image_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ var _ = Describe("OCI", func() {
3030
Expect(err).NotTo(HaveOccurred())
3131
defer os.RemoveAll(dir)
3232

33-
err = ExtractOCIImage(img, imageName, dir, nil)
33+
err = ExtractOCIImage(img, dir, nil)
3434
Expect(err).NotTo(HaveOccurred())
3535
})
3636
})

0 commit comments

Comments
 (0)