github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/refreshhints.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package snapstate 21 22 import ( 23 "time" 24 25 "github.com/snapcore/snapd/overlord/auth" 26 "github.com/snapcore/snapd/overlord/state" 27 "github.com/snapcore/snapd/release" 28 "github.com/snapcore/snapd/store" 29 "github.com/snapcore/snapd/timings" 30 ) 31 32 var refreshHintsDelay = time.Duration(24 * time.Hour) 33 34 // refreshHints will ensure that we get regular data about refreshes 35 // so that we can potentially warn the user about important missing 36 // refreshes. 37 type refreshHints struct { 38 state *state.State 39 } 40 41 func newRefreshHints(st *state.State) *refreshHints { 42 return &refreshHints{state: st} 43 } 44 45 func (r *refreshHints) lastRefresh(timestampKey string) (time.Time, error) { 46 return getTime(r.state, timestampKey) 47 } 48 49 func (r *refreshHints) needsUpdate() (bool, error) { 50 tFull, err := r.lastRefresh("last-refresh") 51 if err != nil { 52 return false, err 53 } 54 tHints, err := r.lastRefresh("last-refresh-hints") 55 if err != nil { 56 return false, err 57 } 58 59 recentEnough := time.Now().Add(-refreshHintsDelay) 60 if tFull.After(recentEnough) || tFull.Equal(recentEnough) { 61 return false, nil 62 } 63 return tHints.Before(recentEnough), nil 64 } 65 66 func (r *refreshHints) refresh() error { 67 var refreshManaged bool 68 refreshManaged, _, _ = refreshScheduleManaged(r.state) 69 70 var err error 71 perfTimings := timings.New(map[string]string{"ensure": "refresh-hints"}) 72 defer perfTimings.Save(r.state) 73 74 timings.Run(perfTimings, "refresh-candidates", "query store for refresh candidates", func(tm timings.Measurer) { 75 _, _, _, err = refreshCandidates(auth.EnsureContextTODO(), r.state, nil, nil, &store.RefreshOptions{RefreshManaged: refreshManaged}) 76 }) 77 // TODO: we currently set last-refresh-hints even when there was an 78 // error. In the future we may retry with a backoff. 79 r.state.Set("last-refresh-hints", time.Now()) 80 return err 81 } 82 83 // AtSeed configures hints refresh policies at end of seeding. 84 func (r *refreshHints) AtSeed() error { 85 // on classic hold hints refreshes for a full 24h 86 if release.OnClassic { 87 var t1 time.Time 88 err := r.state.Get("last-refresh-hints", &t1) 89 if err != state.ErrNoState { 90 // already set or other error 91 return err 92 } 93 r.state.Set("last-refresh-hints", time.Now()) 94 } 95 return nil 96 } 97 98 // Ensure will ensure that refresh hints are available on a regular 99 // interval. 100 func (r *refreshHints) Ensure() error { 101 r.state.Lock() 102 defer r.state.Unlock() 103 104 // CanAutoRefresh is a hook that is set by the devicestate 105 // code to ensure that we only AutoRefersh if the device has 106 // bootstraped itself enough. This is only nil when snapstate 107 // is used in isolation (like in tests). 108 if CanAutoRefresh == nil { 109 return nil 110 } 111 if ok, err := CanAutoRefresh(r.state); err != nil || !ok { 112 return err 113 } 114 115 needsUpdate, err := r.needsUpdate() 116 if err != nil { 117 return err 118 } 119 if !needsUpdate { 120 return nil 121 } 122 return r.refresh() 123 }