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