manifest.go 3.0 KB

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