-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathrepository.go
141 lines (120 loc) · 3.47 KB
/
repository.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package overmount
import (
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"sync"
"github.com/pkg/errors"
)
const lockFile = "repository.lock"
// NewRepository constructs a *Repository and creates the dir in which the
// repository lives. A repository is used to hold images and mounts.
func NewRepository(baseDir string, virtual bool) (*Repository, error) {
return &Repository{
baseDir: baseDir,
layers: map[string]*Layer{},
mounts: []*Mount{},
editMutex: new(sync.Mutex),
virtual: virtual,
}, os.MkdirAll(baseDir, 0700)
}
// IsVirtual reports if the repository is virtual. Virtual repositories hold
// tars and cannot accept mounts.
func (r *Repository) IsVirtual() bool {
return r.virtual
}
// TempDir returns a temporary path within the repository
func (r *Repository) TempDir() (string, error) {
basePath := filepath.Join(r.baseDir, tmpdirBase)
if err := os.MkdirAll(basePath, 0700); err != nil {
return "", err
}
return ioutil.TempDir(basePath, "")
}
// TempFile returns a temporary file within the repository
func (r *Repository) TempFile() (*os.File, error) {
basePath := filepath.Join(r.baseDir, tmpdirBase)
if err := os.MkdirAll(basePath, 0700); err != nil {
return nil, err
}
return ioutil.TempFile(basePath, "")
}
// NewMount creates a new mount for use. Target, lower, and upper correspond to
// specific fields in the mount stanza; read
// https://www.kernel.org/doc/Documentation/filesystems/overlayfs.txt for more
// information.
func (r *Repository) NewMount(target, lower, upper string) (*Mount, error) {
workDir, err := r.TempDir()
if err != nil {
return nil, errors.Wrap(ErrMountCannotProceed, err.Error())
}
mount := &Mount{
target: target,
upper: upper,
lower: lower,
work: workDir,
}
if err := r.AddMount(mount); err != nil {
return nil, err
}
return mount, nil
}
func (r *Repository) mkdirCheckRel(path string) error {
rel, err := filepath.Rel(r.baseDir, path)
if err != nil {
return err
}
if strings.HasPrefix(rel, "../") {
return errors.Wrap(ErrMountCannotProceed, "relative path falls below basedir root")
}
return os.MkdirAll(path, 0700)
}
func (r *Repository) edit(editFunc func() error) error {
return edit(path.Join(r.baseDir, lockFile), r.editMutex, editFunc)
}
// AddLayer adds a layer to the repository.
func (r *Repository) AddLayer(layer *Layer, overwrite bool) error {
return r.edit(func() error {
if _, ok := r.layers[layer.id]; ok && !overwrite {
return ErrLayerExists
}
r.layers[layer.id] = layer
return nil
})
}
// RemoveLayer removes a layer from the repository
func (r *Repository) RemoveLayer(layer *Layer) {
r.edit(func() error {
delete(r.layers, layer.id)
return nil
})
}
// AddMount adds a layer to the repository.
func (r *Repository) AddMount(mount *Mount) error {
return r.edit(func() error {
r.mounts = append(r.mounts, mount)
return nil
})
}
// RemoveMount removes a layer from the repository
func (r *Repository) RemoveMount(mount *Mount) {
r.edit(func() error {
for i, x := range r.mounts {
if mount.Equals(x) {
r.mounts = append(r.mounts[:i], r.mounts[i+1:]...)
}
}
return nil
})
}
// Import an image (provided over reader) to the repository.
func (r *Repository) Import(i Importer, reader io.ReadCloser) ([]*Layer, error) {
return i.Import(r, reader)
}
// Export an image (provided via writer) from the repository.
func (r *Repository) Export(e Exporter, layer *Layer, tags []string) (io.ReadCloser, error) {
return e.Export(r, layer, tags)
}