gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/assertstate/validation_set_tracking.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 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 assertstate 21 22 import ( 23 "encoding/json" 24 "fmt" 25 26 "github.com/snapcore/snapd/asserts" 27 "github.com/snapcore/snapd/asserts/snapasserts" 28 "github.com/snapcore/snapd/overlord/state" 29 "github.com/snapcore/snapd/release" 30 ) 31 32 // ValidationSetMode reflects the mode of respective validation set, which is 33 // either monitoring or enforcing. 34 type ValidationSetMode int 35 36 const ( 37 Monitor ValidationSetMode = iota 38 Enforce 39 ) 40 41 // ValidationSetTracking holds tracking parameters for associated validation set. 42 type ValidationSetTracking struct { 43 AccountID string `json:"account-id"` 44 Name string `json:"name"` 45 Mode ValidationSetMode `json:"mode"` 46 47 // PinnedAt is an optional pinned sequence point, or 0 if not pinned. 48 PinnedAt int `json:"pinned-at,omitempty"` 49 50 // Current is the current sequence point. 51 Current int `json:"current,omitempty"` 52 53 // LocalOnly indicates that the assertion was only available locally at the 54 // time it was applied for monitor mode. This tells bulk refresh logic not 55 // to error out on such assertion if it's not in the store. 56 // This flag makes sense only in monitor mode and if pinned. 57 LocalOnly bool `json:"local-only,omitempty"` 58 } 59 60 // ValidationSetKey formats the given account id and name into a validation set key. 61 func ValidationSetKey(accountID, name string) string { 62 return fmt.Sprintf("%s/%s", accountID, name) 63 } 64 65 // UpdateValidationSet updates ValidationSetTracking. 66 // The method assumes valid tr fields. 67 func UpdateValidationSet(st *state.State, tr *ValidationSetTracking) { 68 var vsmap map[string]*json.RawMessage 69 err := st.Get("validation-sets", &vsmap) 70 if err != nil && err != state.ErrNoState { 71 panic("internal error: cannot unmarshal validation set tracking state: " + err.Error()) 72 } 73 if vsmap == nil { 74 vsmap = make(map[string]*json.RawMessage) 75 } 76 data, err := json.Marshal(tr) 77 if err != nil { 78 panic("internal error: cannot marshal validation set tracking state: " + err.Error()) 79 } 80 raw := json.RawMessage(data) 81 key := ValidationSetKey(tr.AccountID, tr.Name) 82 vsmap[key] = &raw 83 st.Set("validation-sets", vsmap) 84 } 85 86 // DeleteValidationSet deletes a validation set for the given accoundID and name. 87 // It is not an error to delete a non-existing one. 88 func DeleteValidationSet(st *state.State, accountID, name string) { 89 var vsmap map[string]*json.RawMessage 90 err := st.Get("validation-sets", &vsmap) 91 if err != nil && err != state.ErrNoState { 92 panic("internal error: cannot unmarshal validation set tracking state: " + err.Error()) 93 } 94 if len(vsmap) == 0 { 95 return 96 } 97 delete(vsmap, ValidationSetKey(accountID, name)) 98 st.Set("validation-sets", vsmap) 99 } 100 101 // GetValidationSet retrieves the ValidationSetTracking for the given account and name. 102 func GetValidationSet(st *state.State, accountID, name string, tr *ValidationSetTracking) error { 103 if tr == nil { 104 return fmt.Errorf("internal error: tr is nil") 105 } 106 107 *tr = ValidationSetTracking{} 108 109 var vset map[string]*json.RawMessage 110 err := st.Get("validation-sets", &vset) 111 if err != nil { 112 return err 113 } 114 key := ValidationSetKey(accountID, name) 115 raw, ok := vset[key] 116 if !ok { 117 return state.ErrNoState 118 } 119 // XXX: &tr pointer isn't needed here but it is likely historical (a bug in 120 // old JSON marshaling probably) and carried over from snapstate.Get. 121 err = json.Unmarshal([]byte(*raw), &tr) 122 if err != nil { 123 return fmt.Errorf("cannot unmarshal validation set tracking state: %v", err) 124 } 125 return nil 126 } 127 128 // ValidationSets retrieves all ValidationSetTracking data. 129 func ValidationSets(st *state.State) (map[string]*ValidationSetTracking, error) { 130 var vsmap map[string]*ValidationSetTracking 131 if err := st.Get("validation-sets", &vsmap); err != nil && err != state.ErrNoState { 132 return nil, err 133 } 134 return vsmap, nil 135 } 136 137 // EnforcedValidationSets returns ValidationSets object with all currently tracked 138 // validation sets that are in enforcing mode. 139 func EnforcedValidationSets(st *state.State) (*snapasserts.ValidationSets, error) { 140 valsets, err := ValidationSets(st) 141 if err != nil { 142 return nil, err 143 } 144 145 db := DB(st) 146 sets := snapasserts.NewValidationSets() 147 148 for _, vs := range valsets { 149 if vs.Mode != Enforce { 150 continue 151 } 152 153 sequence := vs.Current 154 if vs.PinnedAt > 0 { 155 sequence = vs.PinnedAt 156 } 157 headers := map[string]string{ 158 "series": release.Series, 159 "account-id": vs.AccountID, 160 "name": vs.Name, 161 "sequence": fmt.Sprintf("%d", sequence), 162 } 163 164 as, err := db.Find(asserts.ValidationSetType, headers) 165 if err != nil { 166 return nil, err 167 } 168 169 vsetAssert := as.(*asserts.ValidationSet) 170 sets.Add(vsetAssert) 171 } 172 173 return sets, err 174 }