1829fb61bd
Currently if the config field is missing in the manifest file (or corrupted), Ollama will crash when it tries to read it. This can happen at startup or when pulling new models. This data is mostly just used for showing model information so we can be tolerant of it not being present - it is not required to run the models. Besides avoiding crashing, this also gives us the ability to restructure the config in the future by pulling it into the main manifest file.
126 lines
2.2 KiB
Go
126 lines
2.2 KiB
Go
package server
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
type Layer struct {
|
|
MediaType string `json:"mediaType"`
|
|
Digest string `json:"digest"`
|
|
Size int64 `json:"size"`
|
|
From string `json:"from,omitempty"`
|
|
status string
|
|
}
|
|
|
|
func NewLayer(r io.Reader, mediatype string) (*Layer, error) {
|
|
blobs, err := GetBlobsPath("")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
temp, err := os.CreateTemp(blobs, "sha256-")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer temp.Close()
|
|
defer os.Remove(temp.Name())
|
|
|
|
sha256sum := sha256.New()
|
|
n, err := io.Copy(io.MultiWriter(temp, sha256sum), r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := temp.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
digest := fmt.Sprintf("sha256:%x", sha256sum.Sum(nil))
|
|
blob, err := GetBlobsPath(digest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
status := "using existing layer"
|
|
if _, err := os.Stat(blob); err != nil {
|
|
status = "creating new layer"
|
|
if err := os.Rename(temp.Name(), blob); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &Layer{
|
|
MediaType: mediatype,
|
|
Digest: digest,
|
|
Size: n,
|
|
status: fmt.Sprintf("%s %s", status, digest),
|
|
}, nil
|
|
}
|
|
|
|
func NewLayerFromLayer(digest, mediatype, from string) (*Layer, error) {
|
|
if digest == "" {
|
|
return nil, errors.New("creating new layer from layer with empty digest")
|
|
}
|
|
|
|
blob, err := GetBlobsPath(digest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fi, err := os.Stat(blob)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &Layer{
|
|
MediaType: mediatype,
|
|
Digest: digest,
|
|
Size: fi.Size(),
|
|
From: from,
|
|
status: fmt.Sprintf("using existing layer %s", digest),
|
|
}, nil
|
|
}
|
|
|
|
func (l *Layer) Open() (io.ReadSeekCloser, error) {
|
|
if l.Digest == "" {
|
|
return nil, errors.New("opening layer with empty digest")
|
|
}
|
|
|
|
blob, err := GetBlobsPath(l.Digest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return os.Open(blob)
|
|
}
|
|
|
|
func (l *Layer) Remove() error {
|
|
if l.Digest == "" {
|
|
return nil
|
|
}
|
|
|
|
ms, err := Manifests()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, m := range ms {
|
|
for _, layer := range append(m.Layers, &m.Config) {
|
|
if layer.Digest == l.Digest {
|
|
// something is using this layer
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
blob, err := GetBlobsPath(l.Digest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.Remove(blob)
|
|
}
|