manifest.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package server
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "log/slog"
  8. "os"
  9. "path/filepath"
  10. "github.com/minio/sha256-simd"
  11. "github.com/ollama/ollama/types/model"
  12. )
  13. type Manifest struct {
  14. ManifestV2
  15. filepath string
  16. fi os.FileInfo
  17. digest string
  18. }
  19. func (m *Manifest) Size() (size int64) {
  20. for _, layer := range append(m.Layers, m.Config) {
  21. size += layer.Size
  22. }
  23. return
  24. }
  25. func (m *Manifest) Remove() error {
  26. if err := os.Remove(m.filepath); err != nil {
  27. return err
  28. }
  29. for _, layer := range append(m.Layers, m.Config) {
  30. if err := layer.Remove(); err != nil {
  31. return err
  32. }
  33. }
  34. manifests, err := GetManifestPath()
  35. if err != nil {
  36. return err
  37. }
  38. return PruneDirectory(manifests)
  39. }
  40. func ParseNamedManifest(n model.Name) (*Manifest, error) {
  41. if !n.IsFullyQualified() {
  42. return nil, model.Unqualified(n)
  43. }
  44. manifests, err := GetManifestPath()
  45. if err != nil {
  46. return nil, err
  47. }
  48. p := filepath.Join(manifests, n.Filepath())
  49. var m ManifestV2
  50. f, err := os.Open(p)
  51. if err != nil {
  52. return nil, err
  53. }
  54. defer f.Close()
  55. fi, err := f.Stat()
  56. if err != nil {
  57. return nil, err
  58. }
  59. sha256sum := sha256.New()
  60. if err := json.NewDecoder(io.TeeReader(f, sha256sum)).Decode(&m); err != nil {
  61. return nil, err
  62. }
  63. return &Manifest{
  64. ManifestV2: m,
  65. filepath: p,
  66. fi: fi,
  67. digest: fmt.Sprintf("%x", sha256sum.Sum(nil)),
  68. }, nil
  69. }
  70. func WriteManifest(name string, config *Layer, layers []*Layer) error {
  71. manifest := ManifestV2{
  72. SchemaVersion: 2,
  73. MediaType: "application/vnd.docker.distribution.manifest.v2+json",
  74. Config: config,
  75. Layers: layers,
  76. }
  77. var b bytes.Buffer
  78. if err := json.NewEncoder(&b).Encode(manifest); err != nil {
  79. return err
  80. }
  81. modelpath := ParseModelPath(name)
  82. manifestPath, err := modelpath.GetManifestPath()
  83. if err != nil {
  84. return err
  85. }
  86. if err := os.MkdirAll(filepath.Dir(manifestPath), 0o755); err != nil {
  87. return err
  88. }
  89. return os.WriteFile(manifestPath, b.Bytes(), 0o644)
  90. }
  91. func Manifests() (map[model.Name]*Manifest, error) {
  92. manifests, err := GetManifestPath()
  93. if err != nil {
  94. return nil, err
  95. }
  96. // TODO(mxyng): use something less brittle
  97. matches, err := filepath.Glob(filepath.Join(manifests, "*", "*", "*", "*"))
  98. if err != nil {
  99. return nil, err
  100. }
  101. ms := make(map[model.Name]*Manifest)
  102. for _, match := range matches {
  103. fi, err := os.Stat(match)
  104. if err != nil {
  105. return nil, err
  106. }
  107. if !fi.IsDir() {
  108. rel, err := filepath.Rel(manifests, match)
  109. if err != nil {
  110. slog.Warn("bad filepath", "path", match, "error", err)
  111. continue
  112. }
  113. n := model.ParseNameFromFilepath(rel)
  114. if !n.IsValid() {
  115. slog.Warn("bad manifest name", "path", rel, "error", err)
  116. continue
  117. }
  118. m, err := ParseNamedManifest(n)
  119. if err != nil {
  120. slog.Warn("bad manifest", "name", n, "error", err)
  121. continue
  122. }
  123. ms[n] = m
  124. }
  125. }
  126. return ms, nil
  127. }