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 }