github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/configstate/configcore/corecfg_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2021 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 configcore_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "path/filepath" 26 "reflect" 27 "testing" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/dirs" 32 "github.com/snapcore/snapd/osutil" 33 "github.com/snapcore/snapd/overlord/configstate/config" 34 "github.com/snapcore/snapd/overlord/configstate/configcore" 35 "github.com/snapcore/snapd/overlord/state" 36 "github.com/snapcore/snapd/snap" 37 "github.com/snapcore/snapd/systemd" 38 "github.com/snapcore/snapd/testutil" 39 ) 40 41 func Test(t *testing.T) { TestingT(t) } 42 43 type mockConf struct { 44 state *state.State 45 conf map[string]interface{} 46 changes map[string]interface{} 47 err error 48 } 49 50 func (cfg *mockConf) Get(snapName, key string, result interface{}) error { 51 if snapName != "core" { 52 return fmt.Errorf("mockConf only knows about core") 53 } 54 55 var value interface{} 56 value = cfg.changes[key] 57 if value == nil { 58 value = cfg.conf[key] 59 } 60 if value != nil { 61 v1 := reflect.ValueOf(result) 62 v2 := reflect.Indirect(v1) 63 v2.Set(reflect.ValueOf(value)) 64 } 65 return cfg.err 66 } 67 68 func (cfg *mockConf) GetMaybe(snapName, key string, result interface{}) error { 69 err := cfg.Get(snapName, key, result) 70 if err != nil && !config.IsNoOption(err) { 71 return err 72 } 73 return nil 74 } 75 76 func (cfg *mockConf) GetPristine(snapName, key string, result interface{}) error { 77 if snapName != "core" { 78 return fmt.Errorf("mockConf only knows about core") 79 } 80 81 var value interface{} 82 value = cfg.conf[key] 83 if value != nil { 84 v1 := reflect.ValueOf(result) 85 v2 := reflect.Indirect(v1) 86 v2.Set(reflect.ValueOf(value)) 87 } 88 return cfg.err 89 } 90 91 func (cfg *mockConf) GetPristineMaybe(snapName, key string, result interface{}) error { 92 err := cfg.GetPristine(snapName, key, result) 93 if err != nil && !config.IsNoOption(err) { 94 return err 95 } 96 return nil 97 } 98 99 func (cfg *mockConf) Set(snapName, key string, v interface{}) error { 100 if snapName != "core" { 101 return fmt.Errorf("mockConf only knows about core") 102 } 103 if cfg.conf == nil { 104 cfg.conf = make(map[string]interface{}) 105 } 106 cfg.conf[key] = v 107 return nil 108 } 109 110 func (cfg *mockConf) Changes() []string { 111 out := make([]string, 0, len(cfg.changes)) 112 for k := range cfg.changes { 113 out = append(out, "core."+k) 114 } 115 return out 116 } 117 118 func (cfg *mockConf) State() *state.State { 119 return cfg.state 120 } 121 122 // configcoreSuite is the base for all the configcore tests 123 type configcoreSuite struct { 124 testutil.BaseTest 125 126 state *state.State 127 128 systemctlOutput func(args ...string) []byte 129 systemctlArgs [][]string 130 systemdSysctlArgs [][]string 131 } 132 133 var _ = Suite(&configcoreSuite{}) 134 135 func (s *configcoreSuite) SetUpTest(c *C) { 136 s.BaseTest.SetUpTest(c) 137 138 dirs.SetRootDir(c.MkDir()) 139 s.AddCleanup(func() { dirs.SetRootDir("") }) 140 141 s.systemctlOutput = func(args ...string) []byte { 142 return []byte("ActiveState=inactive") 143 } 144 145 s.AddCleanup(systemd.MockSystemctl(func(args ...string) ([]byte, error) { 146 s.systemctlArgs = append(s.systemctlArgs, args[:]) 147 return s.systemctlOutput(args...), nil 148 })) 149 s.systemctlArgs = nil 150 s.AddCleanup(systemd.MockSystemdSysctl(func(args ...string) error { 151 s.systemdSysctlArgs = append(s.systemdSysctlArgs, args[:]) 152 return nil 153 })) 154 s.systemdSysctlArgs = nil 155 156 s.state = state.New(nil) 157 158 restore := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 159 s.AddCleanup(restore) 160 161 // mock an empty cmdline since we check the cmdline to check whether we are 162 // in install mode or uc20 run mode, etc. and we don't want to use the 163 // host's proc/cmdline 164 mockCmdline := filepath.Join(dirs.GlobalRootDir, "cmdline") 165 err := ioutil.WriteFile(mockCmdline, nil, 0644) 166 c.Assert(err, IsNil) 167 restore = osutil.MockProcCmdline(mockCmdline) 168 s.AddCleanup(restore) 169 } 170 171 // runCfgSuite tests configcore.Run 172 type runCfgSuite struct { 173 configcoreSuite 174 } 175 176 var _ = Suite(&runCfgSuite{}) 177 178 func (r *runCfgSuite) TestConfigureUnknownOption(c *C) { 179 conf := &mockConf{ 180 state: r.state, 181 changes: map[string]interface{}{ 182 "unknown.option": "1", 183 }, 184 } 185 186 err := configcore.Run(coreDev, conf) 187 c.Check(err, ErrorMatches, `cannot set "core.unknown.option": unsupported system option`) 188 } 189 190 type mockDev struct { 191 mode string 192 classic bool 193 kernel string 194 uc20 bool 195 } 196 197 func (d mockDev) RunMode() bool { return d.mode == "" || d.mode == "run" } 198 func (d mockDev) Classic() bool { return d.classic } 199 func (d mockDev) HasModeenv() bool { return d.uc20 } 200 func (d mockDev) Kernel() string { 201 if d.Classic() { 202 return "" 203 } 204 if d.kernel == "" { 205 return "pc-kernel" 206 } 207 return d.kernel 208 } 209 210 var ( 211 coreDev = mockDev{classic: false} 212 classicDev = mockDev{classic: true} 213 ) 214 215 // applyCfgSuite tests configcore.Apply() 216 type applyCfgSuite struct { 217 tmpDir string 218 } 219 220 var _ = Suite(&applyCfgSuite{}) 221 222 func (s *applyCfgSuite) SetUpTest(c *C) { 223 s.tmpDir = c.MkDir() 224 dirs.SetRootDir(s.tmpDir) 225 } 226 227 func (s *applyCfgSuite) TearDownTest(c *C) { 228 dirs.SetRootDir("") 229 } 230 231 func (s *applyCfgSuite) TestEmptyRootDir(c *C) { 232 err := configcore.FilesystemOnlyApply(coreDev, "", nil) 233 c.Check(err, ErrorMatches, `internal error: root directory for configcore.FilesystemOnlyApply\(\) not set`) 234 } 235 236 func (s *applyCfgSuite) TestSmoke(c *C) { 237 c.Assert(configcore.FilesystemOnlyApply(coreDev, s.tmpDir, map[string]interface{}{}), IsNil) 238 } 239 240 func (s *applyCfgSuite) TestPlainCoreConfigGetErrorIfNotCore(c *C) { 241 conf := configcore.PlainCoreConfig(map[string]interface{}{}) 242 var val interface{} 243 c.Assert(conf.Get("some-snap", "a", &val), ErrorMatches, `internal error: expected core snap in Get\(\), "some-snap" was requested`) 244 } 245 246 func (s *applyCfgSuite) TestPlainCoreConfigGet(c *C) { 247 conf := configcore.PlainCoreConfig(map[string]interface{}{"foo": "bar"}) 248 var val interface{} 249 c.Assert(conf.Get("core", "a", &val), DeepEquals, &config.NoOptionError{SnapName: "core", Key: "a"}) 250 c.Assert(conf.Get("core", "foo", &val), IsNil) 251 c.Check(val, DeepEquals, "bar") 252 } 253 254 func (s *applyCfgSuite) TestPlainCoreConfigGetMaybe(c *C) { 255 conf := configcore.PlainCoreConfig(map[string]interface{}{"foo": "bar"}) 256 var val interface{} 257 c.Assert(conf.GetMaybe("core", "a", &val), IsNil) 258 c.Assert(val, IsNil) 259 c.Assert(conf.Get("core", "foo", &val), IsNil) 260 c.Check(val, DeepEquals, "bar") 261 } 262 263 func (s *applyCfgSuite) TestNilHandlePanics(c *C) { 264 c.Assert(func() { configcore.AddFSOnlyHandler(nil, nil, nil) }, 265 Panics, "cannot have nil handle with fsOnlyHandler") 266 267 c.Assert(func() { configcore.AddWithStateHandler(nil, nil, nil) }, 268 Panics, "cannot have nil handle with addWithStateHandler if validatedOnlyStateConfig flag is not set") 269 }