layer.go 2.35 KB
Newer Older
Michael Yang's avatar
Michael Yang committed
1
2
3
4
package server

import (
	"crypto/sha256"
5
	"errors"
Michael Yang's avatar
Michael Yang committed
6
7
8
9
10
11
12
13
14
15
	"fmt"
	"io"
	"os"
)

type Layer struct {
	MediaType string `json:"mediaType"`
	Digest    string `json:"digest"`
	Size      int64  `json:"size"`
	From      string `json:"from,omitempty"`
Michael Yang's avatar
Michael Yang committed
16
	status    string
Michael Yang's avatar
Michael Yang committed
17
18
}

19
func NewLayer(r io.Reader, mediatype string) (Layer, error) {
Michael Yang's avatar
Michael Yang committed
20
21
	blobs, err := GetBlobsPath("")
	if err != nil {
22
		return Layer{}, err
Michael Yang's avatar
Michael Yang committed
23
24
	}

Michael Yang's avatar
Michael Yang committed
25
	temp, err := os.CreateTemp(blobs, "sha256-")
Michael Yang's avatar
Michael Yang committed
26
	if err != nil {
27
		return Layer{}, err
Michael Yang's avatar
Michael Yang committed
28
29
	}
	defer temp.Close()
Michael Yang's avatar
Michael Yang committed
30
	defer os.Remove(temp.Name())
Michael Yang's avatar
Michael Yang committed
31
32
33
34

	sha256sum := sha256.New()
	n, err := io.Copy(io.MultiWriter(temp, sha256sum), r)
	if err != nil {
35
		return Layer{}, err
Michael Yang's avatar
Michael Yang committed
36
37
	}

Michael Yang's avatar
Michael Yang committed
38
	if err := temp.Close(); err != nil {
39
		return Layer{}, err
Michael Yang's avatar
Michael Yang committed
40
41
42
43
44
	}

	digest := fmt.Sprintf("sha256:%x", sha256sum.Sum(nil))
	blob, err := GetBlobsPath(digest)
	if err != nil {
45
		return Layer{}, err
Michael Yang's avatar
Michael Yang committed
46
47
48
49
50
51
	}

	status := "using existing layer"
	if _, err := os.Stat(blob); err != nil {
		status = "creating new layer"
		if err := os.Rename(temp.Name(), blob); err != nil {
52
			return Layer{}, err
Michael Yang's avatar
Michael Yang committed
53
		}
54
55
56
		if err := os.Chmod(blob, 0o644); err != nil {
			return Layer{}, err
		}
Michael Yang's avatar
Michael Yang committed
57
58
	}

59
	return Layer{
Michael Yang's avatar
Michael Yang committed
60
61
62
63
		MediaType: mediatype,
		Digest:    digest,
		Size:      n,
		status:    fmt.Sprintf("%s %s", status, digest),
Michael Yang's avatar
Michael Yang committed
64
65
66
	}, nil
}

67
func NewLayerFromLayer(digest, mediatype, from string) (Layer, error) {
68
	if digest == "" {
69
		return Layer{}, errors.New("creating new layer from layer with empty digest")
70
71
	}

Michael Yang's avatar
Michael Yang committed
72
73
	blob, err := GetBlobsPath(digest)
	if err != nil {
74
		return Layer{}, err
Michael Yang's avatar
Michael Yang committed
75
76
77
78
	}

	fi, err := os.Stat(blob)
	if err != nil {
79
		return Layer{}, err
Michael Yang's avatar
Michael Yang committed
80
81
	}

82
	return Layer{
Michael Yang's avatar
Michael Yang committed
83
84
85
86
		MediaType: mediatype,
		Digest:    digest,
		Size:      fi.Size(),
		From:      from,
Michael Yang's avatar
Michael Yang committed
87
		status:    fmt.Sprintf("using existing layer %s", digest),
Michael Yang's avatar
Michael Yang committed
88
89
90
	}, nil
}

91
func (l *Layer) Open() (io.ReadSeekCloser, error) {
92
93
94
95
	if l.Digest == "" {
		return nil, errors.New("opening layer with empty digest")
	}

Michael Yang's avatar
Michael Yang committed
96
97
	blob, err := GetBlobsPath(l.Digest)
	if err != nil {
Michael Yang's avatar
Michael Yang committed
98
		return nil, err
Michael Yang's avatar
Michael Yang committed
99
100
	}

Michael Yang's avatar
Michael Yang committed
101
	return os.Open(blob)
Michael Yang's avatar
Michael Yang committed
102
}
103
104

func (l *Layer) Remove() error {
105
106
107
108
	if l.Digest == "" {
		return nil
	}

109
110
111
112
113
114
	ms, err := Manifests()
	if err != nil {
		return err
	}

	for _, m := range ms {
115
		for _, layer := range append(m.Layers, m.Config) {
116
117
118
119
120
121
122
123
124
125
126
127
128
129
			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)
}