github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/configstate/config/helpers_test.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 config_test 21 22 import ( 23 "bytes" 24 "encoding/json" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/jsonutil" 29 "github.com/snapcore/snapd/overlord/configstate/config" 30 "github.com/snapcore/snapd/overlord/state" 31 "github.com/snapcore/snapd/snap" 32 ) 33 34 type configHelpersSuite struct { 35 state *state.State 36 } 37 38 var _ = Suite(&configHelpersSuite{}) 39 40 func (s *configHelpersSuite) SetUpTest(c *C) { 41 s.state = state.New(nil) 42 } 43 44 func (s *configHelpersSuite) TestConfigSnapshot(c *C) { 45 s.state.Lock() 46 defer s.state.Unlock() 47 48 tr := config.NewTransaction(s.state) 49 c.Assert(tr.Set("snap1", "foo", "a"), IsNil) 50 c.Assert(tr.Set("snap2", "bar", "q"), IsNil) 51 tr.Commit() 52 53 // store current config 54 c.Assert(config.SaveRevisionConfig(s.state, "snap1", snap.R(1)), IsNil) 55 c.Assert(config.SaveRevisionConfig(s.state, "snap2", snap.R(7)), IsNil) 56 57 var cfgsnapshot map[string]map[string]map[string]interface{} 58 c.Assert(s.state.Get("revision-config", &cfgsnapshot), IsNil) 59 c.Assert(cfgsnapshot, DeepEquals, map[string]map[string]map[string]interface{}{ 60 "snap1": {"1": {"foo": "a"}}, 61 "snap2": {"7": {"bar": "q"}}, 62 }) 63 64 c.Assert(cfgsnapshot["snap1"], NotNil) 65 66 // modify 'foo' config key 67 tr = config.NewTransaction(s.state) 68 c.Assert(tr.Set("snap1", "foo", "b"), IsNil) 69 tr.Commit() 70 71 // store current config 72 c.Assert(config.SaveRevisionConfig(s.state, "snap1", snap.R(2)), IsNil) 73 74 c.Assert(s.state.Get("revision-config", &cfgsnapshot), IsNil) 75 c.Assert(cfgsnapshot, DeepEquals, map[string]map[string]map[string]interface{}{ 76 "snap1": {"1": {"foo": "a"}, "2": {"foo": "b"}}, 77 "snap2": {"7": {"bar": "q"}}, 78 }) 79 80 var value string 81 82 // Restore first revision 83 c.Assert(config.RestoreRevisionConfig(s.state, "snap1", snap.R(1)), IsNil) 84 tr = config.NewTransaction(s.state) 85 c.Assert(tr.Get("snap1", "foo", &value), IsNil) 86 c.Check(value, Equals, "a") 87 88 // Restore second revision 89 c.Assert(config.RestoreRevisionConfig(s.state, "snap1", snap.R(2)), IsNil) 90 tr = config.NewTransaction(s.state) 91 c.Assert(tr.Get("snap1", "foo", &value), IsNil) 92 c.Check(value, Equals, "b") 93 } 94 95 func (s *configHelpersSuite) TestDiscardRevisionConfig(c *C) { 96 s.state.Lock() 97 defer s.state.Unlock() 98 99 tr := config.NewTransaction(s.state) 100 c.Assert(tr.Set("snap3", "foo", "a"), IsNil) 101 tr.Commit() 102 103 for i := 1; i <= 3; i++ { 104 c.Assert(config.SaveRevisionConfig(s.state, "snap3", snap.R(i)), IsNil) 105 } 106 107 var cfgsnapshot map[string]map[string]interface{} 108 c.Assert(s.state.Get("revision-config", &cfgsnapshot), IsNil) 109 c.Assert(cfgsnapshot["snap3"], NotNil) 110 c.Assert(cfgsnapshot["snap3"], HasLen, 3) 111 112 for i := 1; i <= 2; i++ { 113 c.Assert(config.DiscardRevisionConfig(s.state, "snap3", snap.R(i)), IsNil) 114 } 115 cfgsnapshot = nil 116 c.Assert(s.state.Get("revision-config", &cfgsnapshot), IsNil) 117 c.Assert(cfgsnapshot["snap3"], NotNil) 118 c.Assert(cfgsnapshot["snap3"], HasLen, 1) 119 120 // removing the last revision removes snap completely from the config map 121 cfgsnapshot = nil 122 c.Assert(config.DiscardRevisionConfig(s.state, "snap3", snap.R(3)), IsNil) 123 c.Assert(s.state.Get("revision-config", &cfgsnapshot), IsNil) 124 c.Assert(cfgsnapshot["snap3"], IsNil) 125 } 126 127 func (s *configHelpersSuite) TestConfigSnapshotNoConfigs(c *C) { 128 s.state.Lock() 129 defer s.state.Unlock() 130 131 // snap has no config in global state 132 c.Assert(config.SaveRevisionConfig(s.state, "snap1", snap.R(1)), IsNil) 133 134 // snap has no config in global state, but config is not nil 135 tr := config.NewTransaction(s.state) 136 c.Assert(tr.Set("snap2", "bar", "q"), IsNil) 137 tr.Commit() 138 c.Assert(config.SaveRevisionConfig(s.state, "snap1", snap.R(1)), IsNil) 139 140 // no configuration to restore in revision-config 141 c.Assert(config.RestoreRevisionConfig(s.state, "snap1", snap.R(1)), IsNil) 142 } 143 144 func (s *configHelpersSuite) TestSnapConfig(c *C) { 145 s.state.Lock() 146 defer s.state.Unlock() 147 148 empty1 := json.RawMessage(nil) 149 150 for _, emptyCfg := range []*json.RawMessage{nil, &empty1, {}} { 151 rawCfg, err := config.GetSnapConfig(s.state, "snap1") 152 c.Assert(err, IsNil) 153 c.Check(rawCfg, IsNil) 154 155 // can set to empty when empty and it's fine 156 c.Assert(config.SetSnapConfig(s.state, "snap1", emptyCfg), IsNil) 157 rawCfg, err = config.GetSnapConfig(s.state, "snap1") 158 c.Assert(err, IsNil) 159 c.Check(rawCfg, IsNil) 160 161 cfg := json.RawMessage(`{"foo":"bar"}`) 162 c.Assert(config.SetSnapConfig(s.state, "snap1", &cfg), IsNil) 163 164 // the set sets it 165 rawCfg, err = config.GetSnapConfig(s.state, "snap1") 166 c.Assert(err, IsNil) 167 c.Assert(rawCfg, NotNil) 168 c.Check(*rawCfg, DeepEquals, json.RawMessage(`{"foo":"bar"}`)) 169 170 // empty or nil clears it 171 c.Assert(config.SetSnapConfig(s.state, "snap1", emptyCfg), IsNil) 172 rawCfg, err = config.GetSnapConfig(s.state, "snap1") 173 c.Assert(err, IsNil) 174 c.Check(rawCfg, IsNil) 175 } 176 } 177 178 func (s *configHelpersSuite) TestPatchInvalidConfig(c *C) { 179 s.state.Lock() 180 defer s.state.Unlock() 181 182 invalid := []string{} 183 value := json.RawMessage([]byte("[]")) 184 _, err := config.PatchConfig("snap1", []string{"foo"}, 0, invalid, &value) 185 c.Assert(err, ErrorMatches, `internal error: unexpected configuration type \[\]string`) 186 } 187 188 func (s *configHelpersSuite) TestPurgeNulls(c *C) { 189 cfg1 := map[string]interface{}{ 190 "foo": nil, 191 "bar": map[string]interface{}{ 192 "one": 1, 193 "two": nil, 194 }, 195 "baz": map[string]interface{}{ 196 "three": nil, 197 }, 198 } 199 config.PurgeNulls(cfg1) 200 c.Check(cfg1, DeepEquals, map[string]interface{}{ 201 "bar": map[string]interface{}{ 202 "one": 1, 203 }, 204 "baz": map[string]interface{}{}, 205 }) 206 207 cfg2 := map[string]interface{}{"foo": nil} 208 c.Check(config.PurgeNulls(cfg2), DeepEquals, map[string]interface{}{}) 209 c.Check(cfg2, DeepEquals, map[string]interface{}{}) 210 211 jsonData, err := json.Marshal(map[string]interface{}{ 212 "foo": nil, 213 "bar": map[string]interface{}{ 214 "one": 2, 215 "two": nil, 216 }, 217 "baz": map[string]interface{}{ 218 "three": nil, 219 }, 220 }) 221 c.Assert(err, IsNil) 222 raw := json.RawMessage(jsonData) 223 cfg4 := map[string]*json.RawMessage{ 224 "root": &raw, 225 } 226 config.PurgeNulls(cfg4) 227 228 val, ok := cfg4["root"] 229 c.Assert(ok, Equals, true) 230 231 var out interface{} 232 jsonutil.DecodeWithNumber(bytes.NewReader(*val), &out) 233 c.Check(out, DeepEquals, map[string]interface{}{ 234 "bar": map[string]interface{}{ 235 "one": json.Number("2"), 236 }, 237 "baz": map[string]interface{}{}, 238 }) 239 240 sub := json.RawMessage(`{"foo":"bar"}`) 241 cfg5 := map[string]interface{}{ 242 "core": map[string]*json.RawMessage{ 243 "proxy": nil, 244 "sub": &sub, 245 }, 246 } 247 config.PurgeNulls(cfg5) 248 c.Check(cfg5, DeepEquals, map[string]interface{}{ 249 "core": map[string]*json.RawMessage{ 250 "sub": &sub, 251 }, 252 }) 253 } 254 255 func (s *configHelpersSuite) TestPurgeNullsTopLevelNull(c *C) { 256 cfgJSON := `{ 257 "experimental": { 258 "parallel-instances": true, 259 "snapd-snap": true 260 }, 261 "proxy": null, 262 "seed": { 263 "loaded": true 264 } 265 }` 266 var cfg map[string]*json.RawMessage 267 err := jsonutil.DecodeWithNumber(bytes.NewReader([]byte(cfgJSON)), &cfg) 268 c.Assert(err, IsNil) 269 270 config.PurgeNulls(cfg) 271 272 cfgJSON2, err := json.Marshal(cfg) 273 c.Assert(err, IsNil) 274 275 var out interface{} 276 jsonutil.DecodeWithNumber(bytes.NewReader(cfgJSON2), &out) 277 c.Check(out, DeepEquals, map[string]interface{}{ 278 "experimental": map[string]interface{}{ 279 "parallel-instances": true, 280 "snapd-snap": true, 281 }, 282 "seed": map[string]interface{}{ 283 "loaded": true, 284 }, 285 }) 286 }