github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/configstate/configcore/runwithstate.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 // +build !nomanagers 3 4 /* 5 * Copyright (C) 2020 Canonical Ltd 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 3 as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 package configcore 22 23 import ( 24 "fmt" 25 "strings" 26 27 "github.com/snapcore/snapd/overlord/configstate/config" 28 "github.com/snapcore/snapd/sysconfig" 29 ) 30 31 func init() { 32 // Most of these handlers are no-op on classic. 33 // TODO: consider allowing some of these on classic too? 34 // consider erroring on core-only options on classic? 35 36 coreOnly := &flags{coreOnlyConfig: true} 37 38 // capture cloud information 39 addWithStateHandler(nil, setCloudInfoWhenSeeding, nil) 40 41 // proxy.{http,https,ftp} 42 addWithStateHandler(validateProxyStore, handleProxyConfiguration, coreOnly) 43 44 // resilience.vitality-hint 45 addWithStateHandler(validateVitalitySettings, handleVitalityConfiguration, nil) 46 47 // XXX: this should become a FSOnlyHandler. We need to 48 // add/implement Changes() to the ConfGetter interface 49 // store-certs.* 50 addWithStateHandler(validateCertSettings, handleCertConfiguration, nil) 51 52 // users.create.automatic 53 addWithStateHandler(validateUsersSettings, handleUserSettings, &flags{earlyConfigFilter: earlyUsersSettingsFilter}) 54 55 validateOnly := &flags{validatedOnlyStateConfig: true} 56 addWithStateHandler(validateRefreshSchedule, nil, validateOnly) 57 addWithStateHandler(validateRefreshRateLimit, nil, validateOnly) 58 addWithStateHandler(validateAutomaticSnapshotsExpiration, nil, validateOnly) 59 } 60 61 type withStateHandler struct { 62 validateFunc func(config.Conf) error 63 handleFunc func(config.Conf, *fsOnlyContext) error 64 configFlags flags 65 } 66 67 func (h *withStateHandler) validate(cfg config.ConfGetter) error { 68 conf := cfg.(config.Conf) 69 if h.validateFunc != nil { 70 return h.validateFunc(conf) 71 } 72 return nil 73 } 74 75 func (h *withStateHandler) handle(dev sysconfig.Device, cfg config.ConfGetter, opts *fsOnlyContext) error { 76 conf := cfg.(config.Conf) 77 if h.handleFunc != nil { 78 return h.handleFunc(conf, opts) 79 } 80 return nil 81 } 82 83 func (h *withStateHandler) needsState() bool { 84 return true 85 } 86 87 func (h *withStateHandler) flags() flags { 88 return h.configFlags 89 } 90 91 // addWithStateHandler registers functions to validate and handle a subset of 92 // system config options requiring to access and manipulate state. 93 func addWithStateHandler(validate func(config.Conf) error, handle func(config.Conf, *fsOnlyContext) error, flags *flags) { 94 if handle == nil && (flags == nil || !flags.validatedOnlyStateConfig) { 95 panic("cannot have nil handle with addWithStateHandler if validatedOnlyStateConfig flag is not set") 96 } 97 h := &withStateHandler{ 98 validateFunc: validate, 99 handleFunc: handle, 100 } 101 if flags != nil { 102 h.configFlags = *flags 103 } 104 handlers = append(handlers, h) 105 } 106 107 func Run(dev sysconfig.Device, cfg config.Conf) error { 108 return applyHandlers(dev, cfg, handlers) 109 } 110 111 func applyHandlers(dev sysconfig.Device, cfg config.Conf, handlers []configHandler) error { 112 // check if the changes 113 for _, k := range cfg.Changes() { 114 switch { 115 case strings.HasPrefix(k, "core.store-certs."): 116 if !validCertOption(k) { 117 return fmt.Errorf("cannot set store ssl certificate under name %q: name must only contain word characters or a dash", k) 118 } 119 case !supportedConfigurations[k]: 120 return fmt.Errorf("cannot set %q: unsupported system option", k) 121 } 122 } 123 124 for _, h := range handlers { 125 if err := h.validate(cfg); err != nil { 126 return err 127 } 128 } 129 130 for _, h := range handlers { 131 if h.flags().coreOnlyConfig && dev.Classic() { 132 continue 133 } 134 if err := h.handle(dev, cfg, nil); err != nil { 135 return err 136 } 137 } 138 return nil 139 } 140 141 func Early(dev sysconfig.Device, cfg config.Conf, values map[string]interface{}) error { 142 early, relevant := applyFilters(func(f flags) filterFunc { 143 return f.earlyConfigFilter 144 }, values) 145 146 if err := config.Patch(cfg, "core", early); err != nil { 147 return err 148 } 149 150 return applyHandlers(dev, cfg, relevant) 151 }