github.com/blixtra/rkt@v0.8.1-0.20160204105720-ab0d1add1a43/rkt/image_gc.go (about)

     1  // Copyright 2015 The rkt Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package main
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"time"
    21  
    22  	"github.com/coreos/rkt/common"
    23  	"github.com/coreos/rkt/pkg/lock"
    24  	"github.com/coreos/rkt/store"
    25  	"github.com/hashicorp/errwrap"
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  const (
    30  	defaultImageGracePeriod = 24 * time.Hour
    31  )
    32  
    33  var (
    34  	cmdImageGC = &cobra.Command{
    35  		Use:   "gc",
    36  		Short: "Garbage collect local store",
    37  		Long: `This is intended to be run periodically from a timer or cron job.
    38  
    39  The default grace period is 24h. Use --grace-period=0s to effectively disable
    40  the grace-period.`,
    41  		Run: runWrapper(runGCImage),
    42  	}
    43  	flagImageGracePeriod time.Duration
    44  )
    45  
    46  func init() {
    47  	cmdImage.AddCommand(cmdImageGC)
    48  	cmdImageGC.Flags().DurationVar(&flagImageGracePeriod, "grace-period", defaultImageGracePeriod, "duration to wait since an image was last used before removing it")
    49  }
    50  
    51  func runGCImage(cmd *cobra.Command, args []string) (exit int) {
    52  	s, err := store.NewStore(getDataDir())
    53  	if err != nil {
    54  		stderr.PrintE("cannot open store", err)
    55  		return 1
    56  	}
    57  
    58  	if err := gcTreeStore(s); err != nil {
    59  		stderr.PrintE("failed to remove unreferenced treestores", err)
    60  		return 1
    61  	}
    62  
    63  	if err := gcStore(s, flagImageGracePeriod); err != nil {
    64  		stderr.Error(err)
    65  		return 1
    66  	}
    67  
    68  	return 0
    69  }
    70  
    71  // gcTreeStore removes all treeStoreIDs not referenced by any non garbage
    72  // collected pod from the store.
    73  func gcTreeStore(s *store.Store) error {
    74  	// Take an exclusive lock to block other pods being created.
    75  	// This is needed to avoid races between the below steps (getting the
    76  	// list of referenced treeStoreIDs, getting the list of treeStoreIDs
    77  	// from the store, removal of unreferenced treeStoreIDs) and new
    78  	// pods/treeStores being created/referenced
    79  	keyLock, err := lock.ExclusiveKeyLock(lockDir(), common.PrepareLock)
    80  	if err != nil {
    81  		return errwrap.Wrap(errors.New("cannot get exclusive prepare lock"), err)
    82  	}
    83  	defer keyLock.Close()
    84  	referencedTreeStoreIDs, err := getReferencedTreeStoreIDs()
    85  	if err != nil {
    86  		return errwrap.Wrap(errors.New("cannot get referenced treestoreIDs"), err)
    87  	}
    88  	treeStoreIDs, err := s.GetTreeStoreIDs()
    89  	if err != nil {
    90  		return errwrap.Wrap(errors.New("cannot get treestoreIDs from the store"), err)
    91  	}
    92  	for _, treeStoreID := range treeStoreIDs {
    93  		if _, ok := referencedTreeStoreIDs[treeStoreID]; !ok {
    94  			if err := s.RemoveTreeStore(treeStoreID); err != nil {
    95  				stderr.PrintE(fmt.Sprintf("error removing treestore %q", treeStoreID), err)
    96  			} else {
    97  				stderr.Printf("removed treestore %q", treeStoreID)
    98  			}
    99  		}
   100  	}
   101  	return nil
   102  }
   103  
   104  func getReferencedTreeStoreIDs() (map[string]struct{}, error) {
   105  	treeStoreIDs := map[string]struct{}{}
   106  	var walkErr error
   107  	// Consider pods in preparing, prepared, run, exitedgarbage state
   108  	if err := walkPods(includeMostDirs, func(p *pod) {
   109  		stage1TreeStoreID, err := p.getStage1TreeStoreID()
   110  		if err != nil {
   111  			walkErr = errwrap.Wrap(fmt.Errorf("cannot get stage1 treestoreID for pod %s", p.uuid), err)
   112  			return
   113  		}
   114  		appsTreeStoreIDs, err := p.getAppsTreeStoreIDs()
   115  		if err != nil {
   116  			walkErr = errwrap.Wrap(fmt.Errorf("cannot get apps treestoreIDs for pod %s", p.uuid), err)
   117  			return
   118  		}
   119  		allTreeStoreIDs := append(appsTreeStoreIDs, stage1TreeStoreID)
   120  
   121  		for _, treeStoreID := range allTreeStoreIDs {
   122  			treeStoreIDs[treeStoreID] = struct{}{}
   123  		}
   124  	}); err != nil {
   125  		return nil, errwrap.Wrap(errors.New("failed to get pod handles"), err)
   126  	}
   127  	if walkErr != nil {
   128  		return nil, walkErr
   129  	}
   130  	return treeStoreIDs, nil
   131  }
   132  
   133  func gcStore(s *store.Store, gracePeriod time.Duration) error {
   134  	var imagesToRemove []string
   135  	aciinfos, err := s.GetAllACIInfos([]string{"lastused"}, true)
   136  	if err != nil {
   137  		return errwrap.Wrap(errors.New("failed to get aciinfos"), err)
   138  	}
   139  	for _, ai := range aciinfos {
   140  		if time.Now().Sub(ai.LastUsed) <= gracePeriod {
   141  			break
   142  		}
   143  		imagesToRemove = append(imagesToRemove, ai.BlobKey)
   144  	}
   145  
   146  	if err := rmImages(s, imagesToRemove); err != nil {
   147  		return err
   148  	}
   149  
   150  	return nil
   151  }