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