github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/snapshots/snapshotter.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package snapshots 18 19 import ( 20 "context" 21 "encoding/json" 22 "strings" 23 "time" 24 25 "github.com/containerd/containerd/mount" 26 ) 27 28 const ( 29 inheritedLabelsPrefix = "containerd.io/snapshot/" 30 labelSnapshotRef = "containerd.io/snapshot.ref" 31 ) 32 33 // Kind identifies the kind of snapshot. 34 type Kind uint8 35 36 // definitions of snapshot kinds 37 const ( 38 KindUnknown Kind = iota 39 KindView 40 KindActive 41 KindCommitted 42 ) 43 44 // ParseKind parses the provided string into a Kind 45 // 46 // If the string cannot be parsed KindUnknown is returned 47 func ParseKind(s string) Kind { 48 s = strings.ToLower(s) 49 switch s { 50 case "view": 51 return KindView 52 case "active": 53 return KindActive 54 case "committed": 55 return KindCommitted 56 } 57 58 return KindUnknown 59 } 60 61 // String returns the string representation of the Kind 62 func (k Kind) String() string { 63 switch k { 64 case KindView: 65 return "View" 66 case KindActive: 67 return "Active" 68 case KindCommitted: 69 return "Committed" 70 } 71 72 return "Unknown" 73 } 74 75 // MarshalJSON the Kind to JSON 76 func (k Kind) MarshalJSON() ([]byte, error) { 77 return json.Marshal(k.String()) 78 } 79 80 // UnmarshalJSON the Kind from JSON 81 func (k *Kind) UnmarshalJSON(b []byte) error { 82 var s string 83 if err := json.Unmarshal(b, &s); err != nil { 84 return err 85 } 86 87 *k = ParseKind(s) 88 return nil 89 } 90 91 // Info provides information about a particular snapshot. 92 // JSON marshallability is supported for interactive with tools like ctr, 93 type Info struct { 94 Kind Kind // active or committed snapshot 95 Name string // name or key of snapshot 96 Parent string `json:",omitempty"` // name of parent snapshot 97 98 // Labels for a snapshot. 99 // 100 // Note: only labels prefixed with `containerd.io/snapshot/` will be inherited by the 101 // snapshotter's `Prepare`, `View`, or `Commit` calls. 102 Labels map[string]string `json:",omitempty"` 103 Created time.Time `json:",omitempty"` // Created time 104 Updated time.Time `json:",omitempty"` // Last update time 105 } 106 107 // Usage defines statistics for disk resources consumed by the snapshot. 108 // 109 // These resources only include the resources consumed by the snapshot itself 110 // and does not include resources usage by the parent. 111 type Usage struct { 112 Inodes int64 // number of inodes in use. 113 Size int64 // provides usage, in bytes, of snapshot 114 } 115 116 // Add the provided usage to the current usage 117 func (u *Usage) Add(other Usage) { 118 u.Size += other.Size 119 120 // TODO(stevvooe): assumes independent inodes, but provides and upper 121 // bound. This should be pretty close, assuming the inodes for a 122 // snapshot are roughly unique to it. Don't trust this assumption. 123 u.Inodes += other.Inodes 124 } 125 126 // WalkFunc defines the callback for a snapshot walk. 127 type WalkFunc func(context.Context, Info) error 128 129 // Snapshotter defines the methods required to implement a snapshot snapshotter for 130 // allocating, snapshotting and mounting filesystem changesets. The model works 131 // by building up sets of changes with parent-child relationships. 132 // 133 // A snapshot represents a filesystem state. Every snapshot has a parent, where 134 // the empty parent is represented by the empty string. A diff can be taken 135 // between a parent and its snapshot to generate a classic layer. 136 // 137 // An active snapshot is created by calling `Prepare`. After mounting, changes 138 // can be made to the snapshot. The act of committing creates a committed 139 // snapshot. The committed snapshot will get the parent of active snapshot. The 140 // committed snapshot can then be used as a parent. Active snapshots can never 141 // act as a parent. 142 // 143 // Snapshots are best understood by their lifecycle. Active snapshots are 144 // always created with Prepare or View. Committed snapshots are always created 145 // with Commit. Active snapshots never become committed snapshots and vice 146 // versa. All snapshots may be removed. 147 // 148 // For consistency, we define the following terms to be used throughout this 149 // interface for snapshotter implementations: 150 // 151 // `ctx` - refers to a context.Context 152 // `key` - refers to an active snapshot 153 // `name` - refers to a committed snapshot 154 // `parent` - refers to the parent in relation 155 // 156 // Most methods take various combinations of these identifiers. Typically, 157 // `name` and `parent` will be used in cases where a method *only* takes 158 // committed snapshots. `key` will be used to refer to active snapshots in most 159 // cases, except where noted. All variables used to access snapshots use the 160 // same key space. For example, an active snapshot may not share the same key 161 // with a committed snapshot. 162 // 163 // We cover several examples below to demonstrate the utility of a snapshot 164 // snapshotter. 165 // 166 // Importing a Layer 167 // 168 // To import a layer, we simply have the Snapshotter provide a list of 169 // mounts to be applied such that our dst will capture a changeset. We start 170 // out by getting a path to the layer tar file and creating a temp location to 171 // unpack it to: 172 // 173 // layerPath, tmpDir := getLayerPath(), mkTmpDir() // just a path to layer tar file. 174 // 175 // We start by using a Snapshotter to Prepare a new snapshot transaction, using a 176 // key and descending from the empty parent "". To prevent our layer from being 177 // garbage collected during unpacking, we add the `containerd.io/gc.root` label: 178 // 179 // noGcOpt := snapshots.WithLabels(map[string]string{ 180 // "containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339), 181 // }) 182 // mounts, err := snapshotter.Prepare(ctx, key, "", noGcOpt) 183 // if err != nil { ... } 184 // 185 // We get back a list of mounts from Snapshotter.Prepare, with the key identifying 186 // the active snapshot. Mount this to the temporary location with the 187 // following: 188 // 189 // if err := mount.All(mounts, tmpDir); err != nil { ... } 190 // 191 // Once the mounts are performed, our temporary location is ready to capture 192 // a diff. In practice, this works similar to a filesystem transaction. The 193 // next step is to unpack the layer. We have a special function unpackLayer 194 // that applies the contents of the layer to target location and calculates the 195 // DiffID of the unpacked layer (this is a requirement for docker 196 // implementation): 197 // 198 // layer, err := os.Open(layerPath) 199 // if err != nil { ... } 200 // digest, err := unpackLayer(tmpLocation, layer) // unpack into layer location 201 // if err != nil { ... } 202 // 203 // When the above completes, we should have a filesystem the represents the 204 // contents of the layer. Careful implementations should verify that digest 205 // matches the expected DiffID. When completed, we unmount the mounts: 206 // 207 // unmount(mounts) // optional, for now 208 // 209 // Now that we've verified and unpacked our layer, we commit the active 210 // snapshot to a name. For this example, we are just going to use the layer 211 // digest, but in practice, this will probably be the ChainID. This also removes 212 // the active snapshot: 213 // 214 // if err := snapshotter.Commit(ctx, digest.String(), key, noGcOpt); err != nil { ... } 215 // 216 // Now, we have a layer in the Snapshotter that can be accessed with the digest 217 // provided during commit. 218 // 219 // Importing the Next Layer 220 // 221 // Making a layer depend on the above is identical to the process described 222 // above except that the parent is provided as parent when calling 223 // Manager.Prepare, assuming a clean, unique key identifier: 224 // 225 // mounts, err := snapshotter.Prepare(ctx, key, parentDigest, noGcOpt) 226 // 227 // We then mount, apply and commit, as we did above. The new snapshot will be 228 // based on the content of the previous one. 229 // 230 // Running a Container 231 // 232 // To run a container, we simply provide Snapshotter.Prepare the committed image 233 // snapshot as the parent. After mounting, the prepared path can 234 // be used directly as the container's filesystem: 235 // 236 // mounts, err := snapshotter.Prepare(ctx, containerKey, imageRootFSChainID) 237 // 238 // The returned mounts can then be passed directly to the container runtime. If 239 // one would like to create a new image from the filesystem, Manager.Commit is 240 // called: 241 // 242 // if err := snapshotter.Commit(ctx, newImageSnapshot, containerKey); err != nil { ... } 243 // 244 // Alternatively, for most container runs, Snapshotter.Remove will be called to 245 // signal the Snapshotter to abandon the changes. 246 type Snapshotter interface { 247 // Stat returns the info for an active or committed snapshot by name or 248 // key. 249 // 250 // Should be used for parent resolution, existence checks and to discern 251 // the kind of snapshot. 252 Stat(ctx context.Context, key string) (Info, error) 253 254 // Update updates the info for a snapshot. 255 // 256 // Only mutable properties of a snapshot may be updated. 257 Update(ctx context.Context, info Info, fieldpaths ...string) (Info, error) 258 259 // Usage returns the resource usage of an active or committed snapshot 260 // excluding the usage of parent snapshots. 261 // 262 // The running time of this call for active snapshots is dependent on 263 // implementation, but may be proportional to the size of the resource. 264 // Callers should take this into consideration. Implementations should 265 // attempt to honer context cancellation and avoid taking locks when making 266 // the calculation. 267 Usage(ctx context.Context, key string) (Usage, error) 268 269 // Mounts returns the mounts for the active snapshot transaction identified 270 // by key. Can be called on an read-write or readonly transaction. This is 271 // available only for active snapshots. 272 // 273 // This can be used to recover mounts after calling View or Prepare. 274 Mounts(ctx context.Context, key string) ([]mount.Mount, error) 275 276 // Prepare creates an active snapshot identified by key descending from the 277 // provided parent. The returned mounts can be used to mount the snapshot 278 // to capture changes. 279 // 280 // If a parent is provided, after performing the mounts, the destination 281 // will start with the content of the parent. The parent must be a 282 // committed snapshot. Changes to the mounted destination will be captured 283 // in relation to the parent. The default parent, "", is an empty 284 // directory. 285 // 286 // The changes may be saved to a committed snapshot by calling Commit. When 287 // one is done with the transaction, Remove should be called on the key. 288 // 289 // Multiple calls to Prepare or View with the same key should fail. 290 Prepare(ctx context.Context, key, parent string, opts ...Opt) ([]mount.Mount, error) 291 292 // View behaves identically to Prepare except the result may not be 293 // committed back to the snapshot snapshotter. View returns a readonly view on 294 // the parent, with the active snapshot being tracked by the given key. 295 // 296 // This method operates identically to Prepare, except that Mounts returned 297 // may have the readonly flag set. Any modifications to the underlying 298 // filesystem will be ignored. Implementations may perform this in a more 299 // efficient manner that differs from what would be attempted with 300 // `Prepare`. 301 // 302 // Commit may not be called on the provided key and will return an error. 303 // To collect the resources associated with key, Remove must be called with 304 // key as the argument. 305 View(ctx context.Context, key, parent string, opts ...Opt) ([]mount.Mount, error) 306 307 // Commit captures the changes between key and its parent into a snapshot 308 // identified by name. The name can then be used with the snapshotter's other 309 // methods to create subsequent snapshots. 310 // 311 // A committed snapshot will be created under name with the parent of the 312 // active snapshot. 313 // 314 // After commit, the snapshot identified by key is removed. 315 Commit(ctx context.Context, name, key string, opts ...Opt) error 316 317 // Remove the committed or active snapshot by the provided key. 318 // 319 // All resources associated with the key will be removed. 320 // 321 // If the snapshot is a parent of another snapshot, its children must be 322 // removed before proceeding. 323 Remove(ctx context.Context, key string) error 324 325 // Walk will call the provided function for each snapshot in the 326 // snapshotter which match the provided filters. If no filters are 327 // given all items will be walked. 328 // Filters: 329 // name 330 // parent 331 // kind (active,view,committed) 332 // labels.(label) 333 Walk(ctx context.Context, fn WalkFunc, filters ...string) error 334 335 // Close releases the internal resources. 336 // 337 // Close is expected to be called on the end of the lifecycle of the snapshotter, 338 // but not mandatory. 339 // 340 // Close returns nil when it is already closed. 341 Close() error 342 } 343 344 // Cleaner defines a type capable of performing asynchronous resource cleanup. 345 // The Cleaner interface should be used by snapshotters which implement fast 346 // removal and deferred resource cleanup. This prevents snapshots from needing 347 // to perform lengthy resource cleanup before acknowledging a snapshot key 348 // has been removed and available for re-use. This is also useful when 349 // performing multi-key removal with the intent of cleaning up all the 350 // resources after each snapshot key has been removed. 351 type Cleaner interface { 352 Cleanup(ctx context.Context) error 353 } 354 355 // Opt allows setting mutable snapshot properties on creation 356 type Opt func(info *Info) error 357 358 // WithLabels appends labels to a created snapshot 359 func WithLabels(labels map[string]string) Opt { 360 return func(info *Info) error { 361 if info.Labels == nil { 362 info.Labels = make(map[string]string) 363 } 364 365 for k, v := range labels { 366 info.Labels[k] = v 367 } 368 369 return nil 370 } 371 } 372 373 // FilterInheritedLabels filters the provided labels by removing any key which 374 // isn't a snapshot label. Snapshot labels have a prefix of "containerd.io/snapshot/" 375 // or are the "containerd.io/snapshot.ref" label. 376 func FilterInheritedLabels(labels map[string]string) map[string]string { 377 if labels == nil { 378 return nil 379 } 380 381 filtered := make(map[string]string) 382 for k, v := range labels { 383 if k == labelSnapshotRef || strings.HasPrefix(k, inheritedLabelsPrefix) { 384 filtered[k] = v 385 } 386 } 387 return filtered 388 }