gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/configstate/hooks.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 configstate 21 22 import ( 23 "fmt" 24 25 "github.com/snapcore/snapd/overlord/configstate/config" 26 "github.com/snapcore/snapd/overlord/hookstate" 27 "github.com/snapcore/snapd/overlord/snapstate" 28 "github.com/snapcore/snapd/overlord/state" 29 ) 30 31 // configureHandler is the handler for the configure hook. 32 type configureHandler struct { 33 context *hookstate.Context 34 } 35 36 // cachedTransaction is the index into the context cache where the initialized 37 // transaction is stored. 38 type cachedTransaction struct{} 39 40 // ContextTransaction retrieves the transaction cached within the context (and 41 // creates one if it hasn't already been cached). 42 func ContextTransaction(context *hookstate.Context) *config.Transaction { 43 // Check for one already cached 44 tr, ok := context.Cached(cachedTransaction{}).(*config.Transaction) 45 if ok { 46 return tr 47 } 48 49 // It wasn't already cached, so create and cache a new one 50 tr = config.NewTransaction(context.State()) 51 52 context.OnDone(func() error { 53 tr.Commit() 54 if context.InstanceName() == "core" { 55 // make sure the Ensure logic can process 56 // system configuration changes as soon as possible 57 context.State().EnsureBefore(0) 58 } 59 return nil 60 }) 61 62 context.Cache(cachedTransaction{}, tr) 63 return tr 64 } 65 66 func newConfigureHandler(context *hookstate.Context) hookstate.Handler { 67 return &configureHandler{context: context} 68 } 69 70 // Before is called by the HookManager before the configure hook is run. 71 func (h *configureHandler) Before() error { 72 h.context.Lock() 73 defer h.context.Unlock() 74 75 tr := ContextTransaction(h.context) 76 77 // Initialize the transaction if there's a patch provided in the 78 // context or useDefaults is set in which case gadget defaults are used. 79 80 var patch map[string]interface{} 81 var useDefaults bool 82 if err := h.context.Get("use-defaults", &useDefaults); err != nil && err != state.ErrNoState { 83 return err 84 } 85 86 instanceName := h.context.InstanceName() 87 st := h.context.State() 88 if useDefaults { 89 task, _ := h.context.Task() 90 deviceCtx, err := snapstate.DeviceCtx(st, task, nil) 91 if err != nil { 92 return err 93 } 94 95 patch, err = snapstate.ConfigDefaults(st, deviceCtx, instanceName) 96 if err != nil && err != state.ErrNoState { 97 return err 98 } 99 // core is handled internally and does not need a configure 100 // hook, for other snaps double check that the hook is present 101 if len(patch) != 0 && instanceName != "core" { 102 // TODO: helper on context? 103 info, err := snapstate.CurrentInfo(st, instanceName) 104 if err != nil { 105 return err 106 } 107 if info.Hooks["configure"] == nil { 108 return fmt.Errorf("cannot apply gadget config defaults for snap %q, no configure hook", instanceName) 109 } 110 } 111 } else { 112 if err := h.context.Get("patch", &patch); err != nil && err != state.ErrNoState { 113 return err 114 } 115 } 116 117 if err := config.Patch(tr, instanceName, patch); err != nil { 118 return err 119 } 120 121 return nil 122 } 123 124 // Done is called by the HookManager after the configure hook has exited 125 // successfully. 126 func (h *configureHandler) Done() error { 127 return nil 128 } 129 130 // Error is called by the HookManager after the configure hook has exited 131 // non-zero, and includes the error. 132 func (h *configureHandler) Error(err error) (bool, error) { 133 return false, nil 134 }