/
snapshot.go
169 lines (138 loc) · 5.18 KB
/
snapshot.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package ctrd
import (
"context"
"fmt"
"github.com/alibaba/pouch/pkg/log"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/snapshots"
"github.com/opencontainers/image-spec/identity"
)
const (
defaultSnapshotterName = "overlayfs"
)
var (
currentSnapshotterName = defaultSnapshotterName
)
// SetSnapshotterName sets current snapshotter driver, it should be called only when daemon starts
func SetSnapshotterName(name string) {
currentSnapshotterName = name
}
// CurrentSnapshotterName returns current snapshotter driver
func CurrentSnapshotterName(ctx context.Context) string {
if v := GetSnapshotter(ctx); v != "" {
return v
}
return currentSnapshotterName
}
// CreateSnapshot creates a active snapshot with image's name and id.
func (c *Client) CreateSnapshot(ctx context.Context, id, ref string) error {
wrapperCli, err := c.Get(ctx)
if err != nil {
return fmt.Errorf("failed to get a containerd grpc client: %v", err)
}
originalCtx := ctx
ctx = leases.WithLease(ctx, wrapperCli.lease.ID)
var (
snName = CurrentSnapshotterName(ctx)
snSrv = wrapperCli.client.SnapshotService(snName)
)
image, err := wrapperCli.client.GetImage(ctx, ref)
if err != nil {
return err
}
diffIDs, err := image.RootFS(ctx)
if err != nil {
return err
}
parent := identity.ChainID(diffIDs).String()
// NOTE: PouchContainer always unpacks image during pulling. But there
// maybe crash or terminated by some reason. The image have been stored
// in containerd without unpacking. And the following creating container
// request will fail on preparing snapshot because there is no such
// parent snapshotter. Based on this case, we should skip the not
// found error and try to unpack it again.
_, err = snSrv.Prepare(ctx, id, parent)
if err == nil || !errdefs.IsNotFound(err) {
return err
}
log.With(ctx).Warnf("checking unpack status for image %s on %s snapshotter...", image.Name(), snName)
// check unpacked status
unpacked, werr := image.IsUnpacked(ctx, snName)
if werr != nil {
log.With(ctx).Warnf("failed to check unpack status for image %s on %s snapshotter: %v", image.Name(), snName, werr)
return err
}
// if it is not unpacked, try to unpack it.
if !unpacked {
log.With(ctx).Warnf("the image %s doesn't unpack for %s snapshotter, try to unpack it...", image.Name(), snName)
// NOTE: don't use pouchd lease id here because pouchd lease id
// will hold the snapshotter forever, which means that the
// snapshotter will not removed if we remove image.
if werr = image.Unpack(originalCtx, snName); werr != nil {
log.With(ctx).Warnf("failed to unpack for image %s on %s snapshotter: %v", image.Name(), snName, werr)
return err
}
// do it again.
_, err = snSrv.Prepare(ctx, id, parent)
}
return err
}
// GetSnapshot returns the snapshot's info by id.
func (c *Client) GetSnapshot(ctx context.Context, id string) (snapshots.Info, error) {
wrapperCli, err := c.Get(ctx)
if err != nil {
return snapshots.Info{}, fmt.Errorf("failed to get a containerd grpc client: %v", err)
}
service := wrapperCli.client.SnapshotService(CurrentSnapshotterName(ctx))
defer service.Close()
return service.Stat(ctx, id)
}
// RemoveSnapshot removes the snapshot by id.
func (c *Client) RemoveSnapshot(ctx context.Context, id string) error {
wrapperCli, err := c.Get(ctx)
if err != nil {
return fmt.Errorf("failed to get a containerd grpc client: %v", err)
}
service := wrapperCli.client.SnapshotService(CurrentSnapshotterName(ctx))
defer service.Close()
return service.Remove(ctx, id)
}
// GetMounts returns the mounts for the active snapshot transaction identified
// by key.
func (c *Client) GetMounts(ctx context.Context, id string) ([]mount.Mount, error) {
wrapperCli, err := c.Get(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get a containerd grpc client: %v", err)
}
service := wrapperCli.client.SnapshotService(CurrentSnapshotterName(ctx))
defer service.Close()
return service.Mounts(ctx, id)
}
// GetSnapshotUsage returns the resource usage of an active or committed snapshot
// excluding the usage of parent snapshots.
func (c *Client) GetSnapshotUsage(ctx context.Context, id string) (snapshots.Usage, error) {
wrapperCli, err := c.Get(ctx)
if err != nil {
return snapshots.Usage{}, fmt.Errorf("failed to get a containerd grpc client: %v", err)
}
service := wrapperCli.client.SnapshotService(CurrentSnapshotterName(ctx))
defer service.Close()
return service.Usage(ctx, id)
}
// WalkSnapshot walk all snapshots in specific snapshotter. If not set specific snapshotter,
// it will be set to current snapshotter. For each snapshot, the function will be called.
func (c *Client) WalkSnapshot(ctx context.Context, snapshotter string, fn func(context.Context, snapshots.Info) error) error {
wrapperCli, err := c.Get(ctx)
if err != nil {
return fmt.Errorf("failed to get a containerd grpc client: %v", err)
}
// if not set specific snapshotter, set snapshotter to current snaphotter
if snapshotter == "" {
snapshotter = CurrentSnapshotterName(ctx)
}
service := wrapperCli.client.SnapshotService(snapshotter)
defer service.Close()
return service.Walk(ctx, fn)
}