github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapstate/devicectx.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018-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 snapstate 21 22 import ( 23 "github.com/snapcore/snapd/asserts" 24 "github.com/snapcore/snapd/overlord/state" 25 ) 26 27 // A DeviceContext provides for operating as a given device and with 28 // its brand store either for normal operation or over a remodeling. 29 type DeviceContext interface { 30 // Model returns the governing device model assertion for the context. 31 Model() *asserts.Model 32 // Store returns the store service to use under this context or nil if the snapstate store is appropriate. 33 Store() StoreService 34 35 // ForRemodeling returns whether this context is for use over a remodeling. 36 ForRemodeling() bool 37 } 38 39 // Hook setup by devicestate to pick a device context from state, 40 // optional task or an optionally pre-provided one. It's expected to 41 // return ErrNoState if a model assertion is not yet known. 42 var ( 43 DeviceCtx func(st *state.State, task *state.Task, providedDeviceCtx DeviceContext) (DeviceContext, error) 44 ) 45 46 // Hook setup by devicestate to know whether a remodeling is in progress. 47 var ( 48 Remodeling func(st *state.State) bool 49 ) 50 51 // ModelFromTask returns a model assertion through the device context for the task. 52 func ModelFromTask(task *state.Task) (*asserts.Model, error) { 53 deviceCtx, err := DeviceCtx(task.State(), task, nil) 54 if err != nil { 55 return nil, err 56 } 57 return deviceCtx.Model(), nil 58 } 59 60 // DevicePastSeeding returns a device context if a model assertion is 61 // available and the device is seeded, at that point the device store 62 // is known and seeding done. Otherwise it returns a 63 // ChangeConflictError about being too early unless a pre-provided 64 // DeviceContext is passed in. It will again return a conflict error 65 // during remodeling unless the providedDeviceCtx is for it. 66 func DevicePastSeeding(st *state.State, providedDeviceCtx DeviceContext) (DeviceContext, error) { 67 var seeded bool 68 err := st.Get("seeded", &seeded) 69 if err != nil && err != state.ErrNoState { 70 return nil, err 71 } 72 if Remodeling(st) { 73 // a remodeling is in progress and this is not called 74 // as part of it. The 2nd check should not be needed 75 // in practice. 76 if providedDeviceCtx == nil || !providedDeviceCtx.ForRemodeling() { 77 return nil, &ChangeConflictError{ 78 Message: "remodeling in progress, no other " + 79 "changes allowed until this is done", 80 ChangeKind: "remodel", 81 } 82 } 83 } 84 devCtx, err := DeviceCtx(st, nil, providedDeviceCtx) 85 if err != nil && err != state.ErrNoState { 86 return nil, err 87 } 88 // when seeded devCtx should not be nil except in the rare 89 // case of upgrades from a snapd before the introduction of 90 // the fallback generic/generic-classic model 91 if !seeded || devCtx == nil { 92 return nil, &ChangeConflictError{ 93 Message: "too early for operation, device not yet" + 94 " seeded or device model not acknowledged", 95 ChangeKind: "seed", 96 } 97 } 98 99 return devCtx, nil 100 } 101 102 // DeviceCtxFromState returns a device context if a model assertion is 103 // available. Otherwise it returns a ChangeConflictError about being 104 // too early unless an pre-provided DeviceContext is passed in. 105 func DeviceCtxFromState(st *state.State, providedDeviceCtx DeviceContext) (DeviceContext, error) { 106 deviceCtx, err := DeviceCtx(st, nil, providedDeviceCtx) 107 if err != nil { 108 if err == state.ErrNoState { 109 return nil, &ChangeConflictError{ 110 Message: "too early for operation, device model not yet acknowledged", 111 ChangeKind: "seed", 112 } 113 } 114 return nil, err 115 } 116 return deviceCtx, nil 117 }