github.com/dctrud/umoci@v0.4.3-0.20191016193643-05a1d37de015/oci/casext/gc.go (about)

     1  /*
     2   * umoci: Umoci Modifies Open Containers' Images
     3   * Copyright (C) 2016, 2017, 2018 SUSE LLC.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *    http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package casext
    19  
    20  import (
    21  	"github.com/apex/log"
    22  	"github.com/opencontainers/go-digest"
    23  	ispec "github.com/opencontainers/image-spec/specs-go/v1"
    24  	"github.com/pkg/errors"
    25  	"golang.org/x/net/context"
    26  )
    27  
    28  // GC will perform a mark-and-sweep garbage collection of the OCI image
    29  // referenced by the given CAS engine. The root set is taken to be the set of
    30  // references stored in the image, and all blobs not reachable by following a
    31  // descriptor path from the root set will be removed.
    32  //
    33  // GC will only call ListBlobs and ListReferences once, and assumes that there
    34  // is no change in the set of references or blobs after calling those
    35  // functions. In other words, it assumes it is the only user of the image that
    36  // is making modifications. Things will not go well if this assumption is
    37  // challenged.
    38  func (e Engine) GC(ctx context.Context) error {
    39  	// Generate the root set of descriptors.
    40  	var root []ispec.Descriptor
    41  
    42  	names, err := e.ListReferences(ctx)
    43  	if err != nil {
    44  		return errors.Wrap(err, "get roots")
    45  	}
    46  
    47  	for _, name := range names {
    48  		// TODO: This code is no longer necessary once we have index.json.
    49  		descriptorPaths, err := e.ResolveReference(ctx, name)
    50  		if err != nil {
    51  			return errors.Wrapf(err, "get root %s", name)
    52  		}
    53  		if len(descriptorPaths) == 0 {
    54  			return errors.Errorf("tag not found: %s", name)
    55  		}
    56  		if len(descriptorPaths) != 1 {
    57  			// TODO: Handle this more nicely.
    58  			return errors.Errorf("tag is ambiguous: %s", name)
    59  		}
    60  		descriptor := descriptorPaths[0].Descriptor()
    61  		log.WithFields(log.Fields{
    62  			"name":   name,
    63  			"digest": descriptor.Digest,
    64  		}).Debugf("GC: got reference")
    65  		root = append(root, descriptor)
    66  	}
    67  
    68  	// Mark from the root sets.
    69  	black := map[digest.Digest]struct{}{}
    70  	for idx, descriptor := range root {
    71  		log.WithFields(log.Fields{
    72  			"digest": descriptor.Digest,
    73  		}).Debugf("GC: marking from root")
    74  
    75  		reachables, err := e.Reachable(ctx, descriptor)
    76  		if err != nil {
    77  			return errors.Wrapf(err, "getting reachables from root %d", idx)
    78  		}
    79  		for _, reachable := range reachables {
    80  			black[reachable] = struct{}{}
    81  		}
    82  	}
    83  
    84  	// Sweep all blobs in the white set.
    85  	blobs, err := e.ListBlobs(ctx)
    86  	if err != nil {
    87  		return errors.Wrap(err, "get blob list")
    88  	}
    89  
    90  	n := 0
    91  	for _, digest := range blobs {
    92  		if _, ok := black[digest]; ok {
    93  			// Digest is in the black set.
    94  			continue
    95  		}
    96  		log.Infof("garbage collecting blob: %s", digest)
    97  
    98  		if err := e.DeleteBlob(ctx, digest); err != nil {
    99  			return errors.Wrapf(err, "remove unmarked blob %s", digest)
   100  		}
   101  		n++
   102  	}
   103  
   104  	// Finally, tell CAS to GC it.
   105  	if err := e.Clean(ctx); err != nil {
   106  		return errors.Wrapf(err, "clean engine")
   107  	}
   108  
   109  	log.Debugf("garbage collected %d blobs", n)
   110  	return nil
   111  }