manifest.go 3.08 KB
Newer Older
1
2
3
4
package server

import (
	"crypto/sha256"
Michael Yang's avatar
lint  
Michael Yang committed
5
	"encoding/hex"
6
	"encoding/json"
7
	"errors"
8
	"io"
9
	"log/slog"
10
11
12
13
14
15
16
	"os"
	"path/filepath"

	"github.com/ollama/ollama/types/model"
)

type Manifest struct {
Michael Yang's avatar
Michael Yang committed
17
18
	SchemaVersion int      `json:"schemaVersion"`
	MediaType     string   `json:"mediaType"`
19
	Config        Layer    `json:"config"`
Michael Yang's avatar
Michael Yang committed
20
	Layers        []*Layer `json:"layers"`
21
22

	filepath string
23
	fi       os.FileInfo
24
	digest   string
25
26
27
}

func (m *Manifest) Size() (size int64) {
28
	for _, layer := range append(m.Layers, &m.Config) {
29
30
31
32
33
34
		size += layer.Size
	}

	return
}

35
36
37
38
39
40
41
42
43
44
45
46
47
func (m *Manifest) Remove() error {
	if err := os.Remove(m.filepath); err != nil {
		return err
	}

	manifests, err := GetManifestPath()
	if err != nil {
		return err
	}

	return PruneDirectory(manifests)
}

48
func (m *Manifest) RemoveLayers() error {
49
50
51
52
53
54
55
	for _, layer := range append(m.Layers, &m.Config) {
		if layer.Digest != "" {
			if err := layer.Remove(); errors.Is(err, os.ErrNotExist) {
				slog.Debug("layer does not exist", "digest", layer.Digest)
			} else if err != nil {
				return err
			}
56
57
58
59
60
61
		}
	}

	return nil
}

62
63
64
func ParseNamedManifest(n model.Name) (*Manifest, error) {
	if !n.IsFullyQualified() {
		return nil, model.Unqualified(n)
65
66
67
68
69
70
71
	}

	manifests, err := GetManifestPath()
	if err != nil {
		return nil, err
	}

72
73
	p := filepath.Join(manifests, n.Filepath())

Michael Yang's avatar
Michael Yang committed
74
	var m Manifest
75
	f, err := os.Open(p)
76
77
78
	if err != nil {
		return nil, err
	}
79
	defer f.Close()
80

81
82
83
84
85
	fi, err := f.Stat()
	if err != nil {
		return nil, err
	}

86
	sha256sum := sha256.New()
87
	if err := json.NewDecoder(io.TeeReader(f, sha256sum)).Decode(&m); err != nil {
88
89
90
		return nil, err
	}

Michael Yang's avatar
Michael Yang committed
91
92
	m.filepath = p
	m.fi = fi
Michael Yang's avatar
lint  
Michael Yang committed
93
	m.digest = hex.EncodeToString(sha256sum.Sum(nil))
Michael Yang's avatar
Michael Yang committed
94
95

	return &m, nil
96
97
}

98
99
100
101
func WriteManifest(name model.Name, config *Layer, layers []*Layer) error {
	manifests, err := GetManifestPath()
	if err != nil {
		return err
102
103
	}

104
105
	p := filepath.Join(manifests, name.Filepath())
	if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil {
106
107
108
		return err
	}

109
	f, err := os.Create(p)
110
111
112
	if err != nil {
		return err
	}
113
	defer f.Close()
114

Michael Yang's avatar
Michael Yang committed
115
	m := Manifest{
116
117
		SchemaVersion: 2,
		MediaType:     "application/vnd.docker.distribution.manifest.v2+json",
118
		Config:        *config,
119
		Layers:        layers,
120
121
	}

122
	return json.NewEncoder(f).Encode(m)
123
}
124
125
126
127
128
129
130
131

func Manifests() (map[model.Name]*Manifest, error) {
	manifests, err := GetManifestPath()
	if err != nil {
		return nil, err
	}

	// TODO(mxyng): use something less brittle
Michael Yang's avatar
Michael Yang committed
132
	matches, err := filepath.Glob(filepath.Join(manifests, "*", "*", "*", "*"))
133
134
135
136
137
138
	if err != nil {
		return nil, err
	}

	ms := make(map[model.Name]*Manifest)
	for _, match := range matches {
Michael Yang's avatar
Michael Yang committed
139
		fi, err := os.Stat(match)
140
141
142
143
		if err != nil {
			return nil, err
		}

Michael Yang's avatar
Michael Yang committed
144
145
146
147
148
149
150
151
152
153
154
155
156
		if !fi.IsDir() {
			rel, err := filepath.Rel(manifests, match)
			if err != nil {
				slog.Warn("bad filepath", "path", match, "error", err)
				continue
			}

			n := model.ParseNameFromFilepath(rel)
			if !n.IsValid() {
				slog.Warn("bad manifest name", "path", rel, "error", err)
				continue
			}

157
158
			m, err := ParseNamedManifest(n)
			if err != nil {
159
160
				slog.Warn("bad manifest", "name", n, "error", err)
				continue
161
162
163
164
165
166
167
168
			}

			ms[n] = m
		}
	}

	return ms, nil
}