github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/devicestate/firstboot.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2019 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 devicestate 21 22 import ( 23 "errors" 24 "fmt" 25 "sort" 26 27 "github.com/snapcore/snapd/asserts" 28 "github.com/snapcore/snapd/dirs" 29 "github.com/snapcore/snapd/i18n" 30 "github.com/snapcore/snapd/overlord/assertstate" 31 "github.com/snapcore/snapd/overlord/devicestate/internal" 32 "github.com/snapcore/snapd/overlord/snapstate" 33 "github.com/snapcore/snapd/overlord/state" 34 "github.com/snapcore/snapd/release" 35 "github.com/snapcore/snapd/seed" 36 "github.com/snapcore/snapd/snap" 37 "github.com/snapcore/snapd/timings" 38 ) 39 40 var errNothingToDo = errors.New("nothing to do") 41 42 func installSeedSnap(st *state.State, sn *seed.Snap, flags snapstate.Flags) (*state.TaskSet, *snap.Info, error) { 43 if sn.Required { 44 flags.Required = true 45 } 46 if sn.Classic { 47 flags.Classic = true 48 } 49 if sn.DevMode { 50 flags.DevMode = true 51 } 52 53 return snapstate.InstallPath(st, sn.SideInfo, sn.Path, "", sn.Channel, flags) 54 } 55 56 func trivialSeeding(st *state.State, markSeeded *state.Task) []*state.TaskSet { 57 // give the internal core config a chance to run (even if core is 58 // not used at all we put system configuration there) 59 configTs := snapstate.ConfigureSnap(st, "core", 0) 60 markSeeded.WaitAll(configTs) 61 return []*state.TaskSet{configTs, state.NewTaskSet(markSeeded)} 62 } 63 64 func populateStateFromSeedImpl(st *state.State, tm timings.Measurer) ([]*state.TaskSet, error) { 65 // check that the state is empty 66 var seeded bool 67 err := st.Get("seeded", &seeded) 68 if err != nil && err != state.ErrNoState { 69 return nil, err 70 } 71 if seeded { 72 return nil, fmt.Errorf("cannot populate state: already seeded") 73 } 74 75 markSeeded := st.NewTask("mark-seeded", i18n.G("Mark system seeded")) 76 77 deviceSeed, err := seed.Open(dirs.SnapSeedDir) 78 if err != nil { 79 return nil, err 80 } 81 82 // ack all initial assertions 83 var model *asserts.Model 84 timings.Run(tm, "import-assertions", "import assertions from seed", func(nested timings.Measurer) { 85 model, err = importAssertionsFromSeed(st, deviceSeed) 86 }) 87 if err == errNothingToDo { 88 return trivialSeeding(st, markSeeded), nil 89 } 90 if err != nil { 91 return nil, err 92 } 93 94 err = deviceSeed.LoadMeta(tm) 95 if release.OnClassic && err == seed.ErrNoMeta { 96 // on classic it is ok to not seed any snaps 97 return trivialSeeding(st, markSeeded), nil 98 } 99 if err != nil { 100 return nil, err 101 } 102 103 essentialSeedSnaps := deviceSeed.EssentialSnaps() 104 seedSnaps, err := deviceSeed.ModeSnaps("run") // XXX mode should be passed in 105 if err != nil { 106 return nil, err 107 } 108 109 // allSnapInfos are collected for cross-check validation of bases 110 allSnapInfos := make(map[string]*snap.Info, len(essentialSeedSnaps)+len(seedSnaps)) 111 112 tsAll := []*state.TaskSet{} 113 configTss := []*state.TaskSet{} 114 chainTs := func(all []*state.TaskSet, ts *state.TaskSet) []*state.TaskSet { 115 n := len(all) 116 if n != 0 { 117 ts.WaitAll(all[n-1]) 118 } 119 return append(all, ts) 120 } 121 chainSorted := func(infos []*snap.Info, infoToTs map[*snap.Info]*state.TaskSet) { 122 sort.Stable(snap.ByType(infos)) 123 for _, info := range infos { 124 ts := infoToTs[info] 125 tsAll = chainTs(tsAll, ts) 126 } 127 } 128 129 essInfoToTs := make(map[*snap.Info]*state.TaskSet, len(essentialSeedSnaps)) 130 essInfos := make([]*snap.Info, 0, len(essentialSeedSnaps)) 131 132 if len(essentialSeedSnaps) != 0 { 133 // we *always* configure "core" here even if bases are used 134 // for booting. "core" if where the system config lives. 135 configTss = chainTs(configTss, snapstate.ConfigureSnap(st, "core", snapstate.UseConfigDefaults)) 136 } 137 138 for _, seedSnap := range essentialSeedSnaps { 139 ts, info, err := installSeedSnap(st, seedSnap, snapstate.Flags{SkipConfigure: true}) 140 if err != nil { 141 return nil, err 142 } 143 if info.GetType() == snap.TypeKernel || info.GetType() == snap.TypeGadget { 144 configTs := snapstate.ConfigureSnap(st, info.SnapName(), snapstate.UseConfigDefaults) 145 // wait for the previous configTss 146 configTss = chainTs(configTss, configTs) 147 } 148 essInfos = append(essInfos, info) 149 essInfoToTs[info] = ts 150 allSnapInfos[info.SnapName()] = info 151 } 152 // now add/chain the tasksets in the right order based on essential 153 // snap types 154 chainSorted(essInfos, essInfoToTs) 155 156 // chain together configuring core, kernel, and gadget after 157 // installing them so that defaults are availabble from gadget 158 if len(configTss) > 0 { 159 configTss[0].WaitAll(tsAll[len(tsAll)-1]) 160 tsAll = append(tsAll, configTss...) 161 } 162 163 // ensure we install in the right order 164 infoToTs := make(map[*snap.Info]*state.TaskSet, len(seedSnaps)) 165 infos := make([]*snap.Info, 0, len(seedSnaps)) 166 167 for _, seedSnap := range seedSnaps { 168 var flags snapstate.Flags 169 ts, info, err := installSeedSnap(st, seedSnap, flags) 170 if err != nil { 171 return nil, err 172 } 173 infos = append(infos, info) 174 infoToTs[info] = ts 175 allSnapInfos[info.SnapName()] = info 176 } 177 178 // validate that all snaps have bases 179 errs := snap.ValidateBasesAndProviders(allSnapInfos) 180 if errs != nil { 181 // only report the first error encountered 182 return nil, errs[0] 183 } 184 185 // now add/chain the tasksets in the right order, note that we 186 // only have tasksets that we did not already seeded 187 chainSorted(infos, infoToTs) 188 189 if len(tsAll) == 0 { 190 return nil, fmt.Errorf("cannot proceed, no snaps to seed") 191 } 192 193 ts := tsAll[len(tsAll)-1] 194 endTs := state.NewTaskSet() 195 if model.Gadget() != "" { 196 // we have a gadget that could have interface 197 // connection instructions 198 gadgetConnect := st.NewTask("gadget-connect", "Connect plugs and slots as instructed by the gadget") 199 gadgetConnect.WaitAll(ts) 200 endTs.AddTask(gadgetConnect) 201 ts = endTs 202 } 203 markSeeded.WaitAll(ts) 204 endTs.AddTask(markSeeded) 205 tsAll = append(tsAll, endTs) 206 207 return tsAll, nil 208 } 209 210 func importAssertionsFromSeed(st *state.State, deviceSeed seed.Seed) (*asserts.Model, error) { 211 // TODO: use some kind of context fo Device/SetDevice? 212 device, err := internal.Device(st) 213 if err != nil { 214 return nil, err 215 } 216 217 // collect and 218 // set device,model from the model assertion 219 commitTo := func(batch *asserts.Batch) error { 220 return assertstate.AddBatch(st, batch, nil) 221 } 222 223 err = deviceSeed.LoadAssertions(assertstate.DB(st), commitTo) 224 if err == seed.ErrNoAssertions && release.OnClassic { 225 // on classic seeding is optional 226 // set the fallback model 227 err := setClassicFallbackModel(st, device) 228 if err != nil { 229 return nil, err 230 } 231 return nil, errNothingToDo 232 } 233 if err != nil { 234 return nil, err 235 } 236 237 modelAssertion, err := deviceSeed.Model() 238 if err != nil { 239 return nil, err 240 } 241 242 classicModel := modelAssertion.Classic() 243 if release.OnClassic != classicModel { 244 var msg string 245 if classicModel { 246 msg = "cannot seed an all-snaps system with a classic model" 247 } else { 248 msg = "cannot seed a classic system with an all-snaps model" 249 } 250 return nil, fmt.Errorf(msg) 251 } 252 253 // set device,model from the model assertion 254 if err := setDeviceFromModelAssertion(st, device, modelAssertion); err != nil { 255 return nil, err 256 } 257 258 return modelAssertion, nil 259 }