gitee.com/mysnapcore/mysnapd@v0.1.0/daemon/api_snap_conf_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2020 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 daemon_test 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "io/ioutil" 26 "net/http" 27 "net/http/httptest" 28 "os" 29 "path/filepath" 30 "strings" 31 32 "gopkg.in/check.v1" 33 34 "gitee.com/mysnapcore/mysnapd/dirs" 35 "gitee.com/mysnapcore/mysnapd/overlord/configstate/config" 36 "gitee.com/mysnapcore/mysnapd/testutil" 37 ) 38 39 var _ = check.Suite(&snapConfSuite{}) 40 41 type snapConfSuite struct { 42 apiBaseSuite 43 } 44 45 func (s *snapConfSuite) SetUpTest(c *check.C) { 46 s.apiBaseSuite.SetUpTest(c) 47 48 s.expectAuthenticatedAccess() 49 } 50 51 func (s *snapConfSuite) runGetConf(c *check.C, snapName string, keys []string, statusCode int) map[string]interface{} { 52 req, err := http.NewRequest("GET", "/v2/snaps/"+snapName+"/conf?keys="+strings.Join(keys, ","), nil) 53 c.Check(err, check.IsNil) 54 rec := httptest.NewRecorder() 55 s.req(c, req, nil).ServeHTTP(rec, req) 56 c.Check(rec.Code, check.Equals, statusCode) 57 58 var body map[string]interface{} 59 err = json.Unmarshal(rec.Body.Bytes(), &body) 60 c.Check(err, check.IsNil) 61 return body["result"].(map[string]interface{}) 62 } 63 64 func (s *snapConfSuite) TestGetConfSingleKey(c *check.C) { 65 d := s.daemon(c) 66 67 // Set a config that we'll get in a moment 68 d.Overlord().State().Lock() 69 tr := config.NewTransaction(d.Overlord().State()) 70 tr.Set("test-snap", "test-key1", "test-value1") 71 tr.Set("test-snap", "test-key2", "test-value2") 72 tr.Commit() 73 d.Overlord().State().Unlock() 74 75 result := s.runGetConf(c, "test-snap", []string{"test-key1"}, 200) 76 c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1"}) 77 78 result = s.runGetConf(c, "test-snap", []string{"test-key1", "test-key2"}, 200) 79 c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1", "test-key2": "test-value2"}) 80 } 81 82 func (s *snapConfSuite) TestGetConfCoreSystemAlias(c *check.C) { 83 d := s.daemon(c) 84 85 // Set a config that we'll get in a moment 86 d.Overlord().State().Lock() 87 tr := config.NewTransaction(d.Overlord().State()) 88 tr.Set("core", "test-key1", "test-value1") 89 tr.Commit() 90 d.Overlord().State().Unlock() 91 92 result := s.runGetConf(c, "core", []string{"test-key1"}, 200) 93 c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1"}) 94 95 result = s.runGetConf(c, "system", []string{"test-key1"}, 200) 96 c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1"}) 97 } 98 99 func (s *snapConfSuite) TestGetConfMissingKey(c *check.C) { 100 s.daemon(c) 101 result := s.runGetConf(c, "test-snap", []string{"test-key2"}, 400) 102 c.Check(result, check.DeepEquals, map[string]interface{}{ 103 "value": map[string]interface{}{ 104 "SnapName": "test-snap", 105 "Key": "test-key2", 106 }, 107 "message": `snap "test-snap" has no "test-key2" configuration option`, 108 "kind": "option-not-found", 109 }) 110 } 111 112 func (s *snapConfSuite) TestGetRootDocument(c *check.C) { 113 d := s.daemon(c) 114 d.Overlord().State().Lock() 115 tr := config.NewTransaction(d.Overlord().State()) 116 tr.Set("test-snap", "test-key1", "test-value1") 117 tr.Set("test-snap", "test-key2", "test-value2") 118 tr.Commit() 119 d.Overlord().State().Unlock() 120 121 result := s.runGetConf(c, "test-snap", nil, 200) 122 c.Check(result, check.DeepEquals, map[string]interface{}{"test-key1": "test-value1", "test-key2": "test-value2"}) 123 } 124 125 func (s *snapConfSuite) TestGetConfBadKey(c *check.C) { 126 s.daemon(c) 127 // TODO: this one in particular should really be a 400 also 128 result := s.runGetConf(c, "test-snap", []string{"."}, 500) 129 c.Check(result, check.DeepEquals, map[string]interface{}{"message": `invalid option name: ""`}) 130 } 131 132 const configYaml = ` 133 name: config-snap 134 version: 1 135 hooks: 136 configure: 137 ` 138 139 func (s *snapConfSuite) TestSetConf(c *check.C) { 140 d := s.daemon(c) 141 s.mockSnap(c, configYaml) 142 143 // Mock the hook runner 144 hookRunner := testutil.MockCommand(c, "snap", "") 145 defer hookRunner.Restore() 146 147 d.Overlord().Loop() 148 defer d.Overlord().Stop() 149 150 text, err := json.Marshal(map[string]interface{}{"key": "value"}) 151 c.Assert(err, check.IsNil) 152 153 buffer := bytes.NewBuffer(text) 154 req, err := http.NewRequest("PUT", "/v2/snaps/config-snap/conf", buffer) 155 c.Assert(err, check.IsNil) 156 157 rec := httptest.NewRecorder() 158 s.req(c, req, nil).ServeHTTP(rec, req) 159 c.Check(rec.Code, check.Equals, 202) 160 161 var body map[string]interface{} 162 err = json.Unmarshal(rec.Body.Bytes(), &body) 163 c.Assert(err, check.IsNil) 164 id := body["change"].(string) 165 166 st := d.Overlord().State() 167 st.Lock() 168 chg := st.Change(id) 169 st.Unlock() 170 c.Assert(chg, check.NotNil) 171 172 <-chg.Ready() 173 174 st.Lock() 175 err = chg.Err() 176 st.Unlock() 177 c.Assert(err, check.IsNil) 178 179 // Check that the configure hook was run correctly 180 c.Check(hookRunner.Calls(), check.DeepEquals, [][]string{{ 181 "snap", "run", "--hook", "configure", "-r", "unset", "config-snap", 182 }}) 183 } 184 185 func (s *snapConfSuite) TestSetConfCoreSystemAlias(c *check.C) { 186 d := s.daemon(c) 187 s.mockSnap(c, ` 188 name: core 189 version: 1 190 `) 191 192 err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "/etc/"), 0755) 193 c.Assert(err, check.IsNil) 194 err = ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "/etc/environment"), nil, 0644) 195 c.Assert(err, check.IsNil) 196 197 d.Overlord().Loop() 198 defer d.Overlord().Stop() 199 200 text, err := json.Marshal(map[string]interface{}{"proxy.ftp": "value"}) 201 c.Assert(err, check.IsNil) 202 203 buffer := bytes.NewBuffer(text) 204 req, err := http.NewRequest("PUT", "/v2/snaps/system/conf", buffer) 205 c.Assert(err, check.IsNil) 206 207 rec := httptest.NewRecorder() 208 s.req(c, req, nil).ServeHTTP(rec, req) 209 c.Check(rec.Code, check.Equals, 202) 210 211 var body map[string]interface{} 212 err = json.Unmarshal(rec.Body.Bytes(), &body) 213 c.Assert(err, check.IsNil) 214 id := body["change"].(string) 215 216 st := d.Overlord().State() 217 st.Lock() 218 chg := st.Change(id) 219 st.Unlock() 220 c.Assert(chg, check.NotNil) 221 222 <-chg.Ready() 223 224 st.Lock() 225 err = chg.Err() 226 tr := config.NewTransaction(st) 227 st.Unlock() 228 c.Assert(err, check.IsNil) 229 230 var value string 231 tr.Get("core", "proxy.ftp", &value) 232 c.Assert(value, check.Equals, "value") 233 } 234 235 func (s *snapConfSuite) TestSetConfNumber(c *check.C) { 236 d := s.daemon(c) 237 s.mockSnap(c, configYaml) 238 239 // Mock the hook runner 240 hookRunner := testutil.MockCommand(c, "snap", "") 241 defer hookRunner.Restore() 242 243 d.Overlord().Loop() 244 defer d.Overlord().Stop() 245 246 text, err := json.Marshal(map[string]interface{}{"key": 1234567890}) 247 c.Assert(err, check.IsNil) 248 249 buffer := bytes.NewBuffer(text) 250 req, err := http.NewRequest("PUT", "/v2/snaps/config-snap/conf", buffer) 251 c.Assert(err, check.IsNil) 252 253 rec := httptest.NewRecorder() 254 s.req(c, req, nil).ServeHTTP(rec, req) 255 c.Check(rec.Code, check.Equals, 202) 256 257 var body map[string]interface{} 258 err = json.Unmarshal(rec.Body.Bytes(), &body) 259 c.Assert(err, check.IsNil) 260 id := body["change"].(string) 261 262 st := d.Overlord().State() 263 st.Lock() 264 chg := st.Change(id) 265 st.Unlock() 266 c.Assert(chg, check.NotNil) 267 268 <-chg.Ready() 269 270 st.Lock() 271 defer st.Unlock() 272 tr := config.NewTransaction(d.Overlord().State()) 273 var result interface{} 274 c.Assert(tr.Get("config-snap", "key", &result), check.IsNil) 275 c.Assert(result, check.DeepEquals, json.Number("1234567890")) 276 } 277 278 func (s *snapConfSuite) TestSetConfBadSnap(c *check.C) { 279 s.daemonWithOverlordMockAndStore() 280 281 text, err := json.Marshal(map[string]interface{}{"key": "value"}) 282 c.Assert(err, check.IsNil) 283 284 buffer := bytes.NewBuffer(text) 285 req, err := http.NewRequest("PUT", "/v2/snaps/config-snap/conf", buffer) 286 c.Assert(err, check.IsNil) 287 288 rec := httptest.NewRecorder() 289 s.req(c, req, nil).ServeHTTP(rec, req) 290 c.Check(rec.Code, check.Equals, 404) 291 292 var body map[string]interface{} 293 err = json.Unmarshal(rec.Body.Bytes(), &body) 294 c.Assert(err, check.IsNil) 295 c.Check(body, check.DeepEquals, map[string]interface{}{ 296 "status-code": 404., 297 "status": "Not Found", 298 "result": map[string]interface{}{ 299 "message": `snap "config-snap" is not installed`, 300 "kind": "snap-not-found", 301 "value": "config-snap", 302 }, 303 "type": "error"}) 304 } 305 306 func (s *snapConfSuite) TestSetConfChangeConflict(c *check.C) { 307 s.daemon(c) 308 s.mockSnap(c, configYaml) 309 310 s.simulateConflict("config-snap") 311 312 text, err := json.Marshal(map[string]interface{}{"key": "value"}) 313 c.Assert(err, check.IsNil) 314 315 buffer := bytes.NewBuffer(text) 316 req, err := http.NewRequest("PUT", "/v2/snaps/config-snap/conf", buffer) 317 c.Assert(err, check.IsNil) 318 319 rec := httptest.NewRecorder() 320 s.req(c, req, nil).ServeHTTP(rec, req) 321 c.Check(rec.Code, check.Equals, 409) 322 323 var body map[string]interface{} 324 err = json.Unmarshal(rec.Body.Bytes(), &body) 325 c.Assert(err, check.IsNil) 326 c.Check(body, check.DeepEquals, map[string]interface{}{ 327 "status-code": 409., 328 "status": "Conflict", 329 "result": map[string]interface{}{ 330 "message": `snap "config-snap" has "manip" change in progress`, 331 "kind": "snap-change-conflict", 332 "value": map[string]interface{}{ 333 "change-kind": "manip", 334 "snap-name": "config-snap", 335 }, 336 }, 337 "type": "error"}) 338 }