github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/snap/cmd_model_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 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 main_test 21 22 import ( 23 "fmt" 24 "net/http" 25 26 "gopkg.in/check.v1" 27 28 snap "github.com/snapcore/snapd/cmd/snap" 29 ) 30 31 const happyModelAssertionResponse = `type: model 32 authority-id: mememe 33 series: 16 34 brand-id: mememe 35 model: test-model 36 architecture: amd64 37 base: core18 38 gadget: pc=18 39 kernel: pc-kernel=18 40 required-snaps: 41 - core 42 - hello-world 43 timestamp: 2017-07-27T00:00:00.0Z 44 sign-key-sha3-384: 8B3Wmemeu3H6i4dEV4Q85Q4gIUCHIBCNMHq49e085QeLGHi7v27l3Cqmemer4__t 45 46 AcLBcwQAAQoAHRYhBMbX+t6MbKGH5C3nnLZW7+q0g6ELBQJdTdwTAAoJELZW7+q0g6ELEvgQAI3j 47 jXTqR6kKOqvw94pArwdMDUaZ++tebASAZgso8ejrW2DQGWSc0Q7SQICIR8bvHxqS1GtupQswOzwS 48 U8hjDTv7WEchH1jylyTj/1W1GernmitTKycecRlEkSOE+EpuqBFgTtj6PdA1Fj3CiCRi1rLMhgF2 49 luCOitBLaP+E8P3fuATsLqqDLYzt1VY4Y14MU75hMn+CxAQdnOZTI+NzGMasPsldmOYCPNaN/b3N 50 6/fDLU47RtNlMJ3K0Tz8kj0bqRbegKlD0RdNbAgo9iZwNmrr5E9WCu9f/0rUor/NIxO77H2ExIll 51 zhmsZ7E6qlxvAgBmzKgAXrn68gGrBkIb0eXKiCaKy/i2ApvjVZ9HkOzA6Ldd+SwNJv/iA8rdiMsq 52 p2BfKV5f3ju5b6+WktHxAakJ8iqQmj9Yh7piHjsOAUf1PEJd2s2nqQ+pEEn1F0B23gVCY/Fa9YRQ 53 iKtWVeL3rBw4dSAaK9rpTMqlNcr+yrdXfTK5YzkCC6RU4yzc5MW0hKeseeSiEDSaRYxvftjFfVNa 54 ZaVXKg8Lu+cHtCJDeYXEkPIDQzXswdBO1M8Mb9D0mYxQwHxwvsWv1DByB+Otq08EYgPh4kyHo7ag 55 85yK2e/NQ/fxSwQJMhBF74jM1z9arq6RMiE/KOleFAOraKn2hcROKnEeinABW+sOn6vNuMVv 56 ` 57 58 const happyModelWithDisplayNameAssertionResponse = `type: model 59 authority-id: mememe 60 series: 16 61 brand-id: mememe 62 model: test-model 63 architecture: amd64 64 display-name: Model Name 65 base: core18 66 gadget: pc=18 67 kernel: pc-kernel=18 68 required-snaps: 69 - core 70 - hello-world 71 timestamp: 2017-07-27T00:00:00.0Z 72 sign-key-sha3-384: 8B3Wmemeu3H6i4dEV4Q85Q4gIUCHIBCNMHq49e085QeLGHi7v27l3Cqmemer4__t 73 74 AcLBcwQAAQoAHRYhBMbX+t6MbKGH5C3nnLZW7+q0g6ELBQJdTdwTAAoJELZW7+q0g6ELEvgQAI3j 75 jXTqR6kKOqvw94pArwdMDUaZ++tebASAZgso8ejrW2DQGWSc0Q7SQICIR8bvHxqS1GtupQswOzwS 76 U8hjDTv7WEchH1jylyTj/1W1GernmitTKycecRlEkSOE+EpuqBFgTtj6PdA1Fj3CiCRi1rLMhgF2 77 luCOitBLaP+E8P3fuATsLqqDLYzt1VY4Y14MU75hMn+CxAQdnOZTI+NzGMasPsldmOYCPNaN/b3N 78 6/fDLU47RtNlMJ3K0Tz8kj0bqRbegKlD0RdNbAgo9iZwNmrr5E9WCu9f/0rUor/NIxO77H2ExIll 79 zhmsZ7E6qlxvAgBmzKgAXrn68gGrBkIb0eXKiCaKy/i2ApvjVZ9HkOzA6Ldd+SwNJv/iA8rdiMsq 80 p2BfKV5f3ju5b6+WktHxAakJ8iqQmj9Yh7piHjsOAUf1PEJd2s2nqQ+pEEn1F0B23gVCY/Fa9YRQ 81 iKtWVeL3rBw4dSAaK9rpTMqlNcr+yrdXfTK5YzkCC6RU4yzc5MW0hKeseeSiEDSaRYxvftjFfVNa 82 ZaVXKg8Lu+cHtCJDeYXEkPIDQzXswdBO1M8Mb9D0mYxQwHxwvsWv1DByB+Otq08EYgPh4kyHo7ag 83 85yK2e/NQ/fxSwQJMhBF74jM1z9arq6RMiE/KOleFAOraKn2hcROKnEeinABW+sOn6vNuMVv 84 ` 85 86 const happyAccountAssertionResponse = `type: account 87 authority-id: canonical 88 account-id: mememe 89 display-name: MeMeMe 90 timestamp: 2016-04-01T00:00:00.0Z 91 username: meuser 92 validation: certified 93 sign-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk 94 95 AcLDXAQAAQoABgUCV7UYzwAKCRDUpVvql9g3IK7uH/4udqNOurx5WYVknzXdwekp0ovHCQJ0iBPw 96 TSFxEVr9faZSzb7eqJ1WicHsShf97PYS3ClRYAiluFsjRA8Y03kkSVJHjC+sIwGFubsnkmgflt6D 97 WEmYIl0UBmeaEDS8uY4Xvp9NsLTzNEj2kvzy/52gKaTc1ZSl5RDL9ppMav+0V9iBYpiDPBWH2rJ+ 98 aDSD8Rkyygm0UscfAKyDKH4lrvZ0WkYyi1YVNPrjQ/AtBySh6Q4iJ3LifzKa9woIyAuJET/4/FPY 99 oirqHAfuvNod36yNQIyNqEc20AvTvZNH0PSsg4rq3DLjIPzv5KbJO9lhsasNJK1OdL6x8Yqrdsbk 100 ldZp4qkzfjV7VOMQKaadfcZPRaVVeJWOBnBiaukzkhoNlQi1sdCdkBB/AJHZF8QXw6c7vPDcfnCV 101 1lW7ddQ2p8IsJbT6LzpJu3GW/P4xhNgCjtCJ1AJm9a9RqLwQYgdLZwwDa9iCRtqTbRXBlfy3apps 102 1VjbQ3h5iCd0hNfwDBnGVm1rhLKHCD1DUdNE43oN2ZlE7XGyh0HFV6vKlpqoW3eoXCIxWu+HBY96 103 +LSl/jQgCkb0nxYyzEYK4Reb31D0mYw1Nji5W+MIF5E09+DYZoOT0UvR05YMwMEOeSdI/hLWg/5P 104 k+GDK+/KopMmpd4D1+jjtF7ZvqDpmAV98jJGB2F88RyVb4gcjmFFyTi4Kv6vzz/oLpbm0qrizC0W 105 HLGDN/ymGA5sHzEgEx7U540vz/q9VX60FKqL2YZr/DcyY9GKX5kCG4sNqIIHbcJneZ4frM99oVDu 106 7Jv+DIx/Di6D1ULXol2XjxbbJLKHFtHksR97ceaFvcZwTogC61IYUBJCvvMoqdXAWMhEXCr0QfQ5 107 Xbi31XW2d4/lF/zWlAkRnGTzufIXFni7+nEuOK0SQEzO3/WaRedK1SGOOtTDjB8/3OJeW96AUYK5 108 oTIynkYkEyHWMNCXALg+WQW6L4/YO7aUjZ97zOWIugd7Xy63aT3r/EHafqaY2nacOhLfkeKZ830b 109 o/ezjoZQAxbh6ce7JnXRgE9ELxjdAhBTpGjmmmN2sYrJ7zP9bOgly0BnEPXGSQfFA+NNNw1FADx1 110 MUY8q9DBjmVtgqY+1KGTV5X8KvQCBMODZIf/XJPHdCRAHxMd8COypcwgL2vDIIXpOFbi1J/B0GF+ 111 eklxk9wzBA8AecBMCwCzIRHDNpD1oa2we38bVFrOug6e/VId1k1jYFJjiLyLCDmV8IMYwEllHSXp 112 LQAdm3xZ7t4WnxYC8YSCk9mXf3CZg59SpmnV5Q5Z6A5Pl7Nc3sj7hcsMBZEsOMPzNC9dPsBnZvjs 113 WpPUffJzEdhHBFhvYMuD4Vqj6ejUv9l3oTrjQWVC` 114 115 // note: this serial assertion was generated by adding print statements to the 116 // test in api_model_test.go that generate a fake serial assertion 117 const happySerialAssertionResponse = `type: serial 118 authority-id: my-brand 119 brand-id: my-brand 120 model: my-old-model 121 serial: serialserial 122 device-key: 123 AcZrBFaFwYABAvCgEOrrLA6FKcreHxCcOoTgBUZ+IRG7Nb8tzmEAklaQPGpv7skapUjwD1luE2go 124 mTcoTssVHrfLpBoSDV1aBs44rg3NK40ZKPJP7d2zkds1GxUo1Ea5vfet3SJ4h3aRABEBAAE= 125 device-key-sha3-384: iqLo9doLzK8De9925UrdUyuvPbBad72OTWVE9YJXqd6nz9dKvwJ_lHP5bVxrl3VO 126 timestamp: 2019-08-26T16:34:21-05:00 127 sign-key-sha3-384: anCEGC2NYq7DzDEi6y7OafQCVeVLS90XlLt9PNjrRl9sim5rmRHDDNFNO7ODcWQW 128 129 AcJwBAABCgAGBQJdZFBdAADCLALwR6Sy24wm9PffwbvUhOEXneyY3BnxKC0+NgdHu1gU8go9vEP1 130 i+Flh5uoS70+MBIO+nmF8T+9JWIx2QWFDDxvcuFosnIhvUajCEQohauys5FMz/H/WvB0vrbTBpvK 131 eg== 132 ` 133 134 const noModelAssertionYetResponse = ` 135 { 136 "type": "error", 137 "status-code": 404, 138 "status": "Not Found", 139 "result": { 140 "message": "no model assertion yet", 141 "kind": "assertion-not-found", 142 "value": "model" 143 } 144 }` 145 146 const noSerialAssertionYetResponse = ` 147 { 148 "type": "error", 149 "status-code": 404, 150 "status": "Not Found", 151 "result": { 152 "message": "no serial assertion yet", 153 "kind": "assertion-not-found", 154 "value": "serial" 155 } 156 }` 157 158 // helper for constructing different types of responses to the client 159 type checkResponder func(c *check.C, w http.ResponseWriter, r *http.Request) 160 161 func simpleHappyResponder(body string) checkResponder { 162 return func(c *check.C, w http.ResponseWriter, r *http.Request) { 163 c.Check(r.Method, check.Equals, "GET") 164 c.Check(r.URL.RawQuery, check.Equals, "") 165 fmt.Fprintln(w, body) 166 } 167 } 168 169 func simpleUnhappyResponder(errBody string) checkResponder { 170 return func(c *check.C, w http.ResponseWriter, r *http.Request) { 171 c.Check(r.Method, check.Equals, "GET") 172 c.Check(r.URL.RawQuery, check.Equals, "") 173 w.Header().Set("Content-Type", "application/json") 174 w.WriteHeader(404) 175 fmt.Fprintln(w, errBody) 176 } 177 } 178 179 func simpleAssertionAccountResponder(body string) checkResponder { 180 return func(c *check.C, w http.ResponseWriter, r *http.Request) { 181 c.Check(r.Method, check.Equals, "GET") 182 w.Header().Set("X-Ubuntu-Assertions-Count", "1") 183 fmt.Fprintln(w, body) 184 } 185 } 186 187 func makeHappyTestServerHandler(c *check.C, modelResp, serialResp, accountResp checkResponder) func(w http.ResponseWriter, r *http.Request) { 188 var nModelSerial, nModel, nKnown int 189 return func(w http.ResponseWriter, r *http.Request) { 190 switch r.URL.Path { 191 case "/v2/model": 192 switch nModel { 193 case 0: 194 modelResp(c, w, r) 195 default: 196 c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModel+1) 197 } 198 nModel++ 199 case "/v2/model/serial": 200 switch nModelSerial { 201 case 0: 202 serialResp(c, w, r) 203 default: 204 c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModelSerial+1) 205 } 206 nModelSerial++ 207 case "/v2/assertions/account": 208 switch nKnown { 209 case 0: 210 accountResp(c, w, r) 211 default: 212 c.Fatalf("expected to get 1 request for /v2/model, now on %d", nKnown+1) 213 } 214 nKnown++ 215 default: 216 c.Fatalf("unexpected request to %s", r.URL.Path) 217 } 218 } 219 } 220 221 func (s *SnapSuite) TestNoModelYet(c *check.C) { 222 s.RedirectClientToTestServer( 223 makeHappyTestServerHandler( 224 c, 225 simpleUnhappyResponder(noModelAssertionYetResponse), 226 simpleUnhappyResponder(noSerialAssertionYetResponse), 227 simpleAssertionAccountResponder(happyAccountAssertionResponse), 228 )) 229 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"model"}) 230 c.Assert(err, check.ErrorMatches, `device not ready yet \(no assertions found\)`) 231 } 232 233 func (s *SnapSuite) TestNoSerialYet(c *check.C) { 234 s.RedirectClientToTestServer( 235 makeHappyTestServerHandler( 236 c, 237 simpleHappyResponder(happyModelAssertionResponse), 238 simpleUnhappyResponder(noSerialAssertionYetResponse), 239 simpleAssertionAccountResponder(happyAccountAssertionResponse), 240 )) 241 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial"}) 242 c.Assert(err, check.ErrorMatches, `device not registered yet \(no serial assertion found\)`) 243 c.Check(s.Stderr(), check.Equals, "") 244 c.Check(s.Stdout(), check.Equals, ` 245 brand-id: mememe 246 model: test-model 247 `[1:]) 248 } 249 250 func (s *SnapSuite) TestModel(c *check.C) { 251 252 for _, tt := range []struct { 253 comment string 254 modelF checkResponder 255 serialF checkResponder 256 outText string 257 }{ 258 { 259 comment: "normal serial and model asserts", 260 modelF: simpleHappyResponder(happyModelAssertionResponse), 261 serialF: simpleHappyResponder(happySerialAssertionResponse), 262 outText: ` 263 brand MeMeMe (meuser*) 264 model test-model 265 serial serialserial 266 `[1:], 267 }, 268 { 269 comment: "model assert has display-name", 270 modelF: simpleHappyResponder(happyModelWithDisplayNameAssertionResponse), 271 serialF: simpleHappyResponder(happySerialAssertionResponse), 272 outText: ` 273 brand MeMeMe (meuser*) 274 model Model Name (test-model) 275 serial serialserial 276 `[1:], 277 }, 278 { 279 comment: "missing serial assert", 280 modelF: simpleHappyResponder(happyModelAssertionResponse), 281 serialF: simpleUnhappyResponder(noSerialAssertionYetResponse), 282 outText: ` 283 brand MeMeMe (meuser*) 284 model test-model 285 serial - (device not registered yet) 286 `[1:], 287 }, 288 } { 289 s.RedirectClientToTestServer( 290 makeHappyTestServerHandler( 291 c, 292 tt.modelF, 293 tt.serialF, 294 simpleAssertionAccountResponder(happyAccountAssertionResponse), 295 )) 296 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model"}) 297 c.Assert(err, check.IsNil) 298 c.Assert(rest, check.DeepEquals, []string{}) 299 c.Check(s.Stdout(), check.Equals, tt.outText, check.Commentf("\n%s\n", tt.outText)) 300 c.Check(s.Stderr(), check.Equals, "") 301 s.ResetStdStreams() 302 } 303 } 304 305 func (s *SnapSuite) TestModelVerbose(c *check.C) { 306 s.RedirectClientToTestServer( 307 makeHappyTestServerHandler( 308 c, 309 simpleHappyResponder(happyModelAssertionResponse), 310 simpleHappyResponder(happySerialAssertionResponse), 311 simpleAssertionAccountResponder(happyAccountAssertionResponse), 312 )) 313 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"}) 314 c.Assert(err, check.IsNil) 315 c.Assert(rest, check.DeepEquals, []string{}) 316 c.Check(s.Stdout(), check.Equals, ` 317 brand-id: mememe 318 model: test-model 319 serial: serialserial 320 architecture: amd64 321 base: core18 322 gadget: pc=18 323 kernel: pc-kernel=18 324 timestamp: 2017-07-27T00:00:00Z 325 required-snaps: 326 - core 327 - hello-world 328 `[1:]) 329 c.Check(s.Stderr(), check.Equals, "") 330 } 331 332 func (s *SnapSuite) TestModelVerboseNoSerialYet(c *check.C) { 333 s.RedirectClientToTestServer( 334 makeHappyTestServerHandler( 335 c, 336 simpleHappyResponder(happyModelAssertionResponse), 337 simpleUnhappyResponder(noSerialAssertionYetResponse), 338 simpleAssertionAccountResponder(happyAccountAssertionResponse), 339 )) 340 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"}) 341 c.Assert(err, check.IsNil) 342 c.Assert(rest, check.DeepEquals, []string{}) 343 c.Check(s.Stdout(), check.Equals, ` 344 brand-id: mememe 345 model: test-model 346 serial: -- (device not registered yet) 347 architecture: amd64 348 base: core18 349 gadget: pc=18 350 kernel: pc-kernel=18 351 timestamp: 2017-07-27T00:00:00Z 352 required-snaps: 353 - core 354 - hello-world 355 `[1:]) 356 c.Check(s.Stderr(), check.Equals, "") 357 } 358 359 func (s *SnapSuite) TestModelAssertion(c *check.C) { 360 s.RedirectClientToTestServer( 361 makeHappyTestServerHandler( 362 c, 363 simpleHappyResponder(happyModelAssertionResponse), 364 simpleHappyResponder(happySerialAssertionResponse), 365 simpleAssertionAccountResponder(happyAccountAssertionResponse), 366 )) 367 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--assertion"}) 368 c.Assert(err, check.IsNil) 369 c.Assert(rest, check.DeepEquals, []string{}) 370 c.Check(s.Stdout(), check.Equals, happyModelAssertionResponse) 371 c.Check(s.Stderr(), check.Equals, "") 372 } 373 374 func (s *SnapSuite) TestModelAssertionVerbose(c *check.C) { 375 // check that no calls to the server happen 376 s.RedirectClientToTestServer( 377 func(w http.ResponseWriter, r *http.Request) { 378 c.Fatalf("unexpected request to %s", r.URL.Path) 379 }, 380 ) 381 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--assertion", "--verbose"}) 382 c.Assert(err, check.ErrorMatches, "cannot use --verbose with --assertion") 383 c.Check(s.Stdout(), check.Equals, "") 384 c.Check(s.Stderr(), check.Equals, "") 385 } 386 387 func (s *SnapSuite) TestSerial(c *check.C) { 388 s.RedirectClientToTestServer( 389 makeHappyTestServerHandler( 390 c, 391 simpleHappyResponder(happyModelAssertionResponse), 392 simpleHappyResponder(happySerialAssertionResponse), 393 simpleAssertionAccountResponder(happyAccountAssertionResponse), 394 )) 395 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial"}) 396 c.Assert(err, check.IsNil) 397 c.Assert(rest, check.DeepEquals, []string{}) 398 c.Check(s.Stdout(), check.Equals, ` 399 brand-id: my-brand 400 model: my-old-model 401 serial: serialserial 402 `[1:]) 403 c.Check(s.Stderr(), check.Equals, "") 404 } 405 406 func (s *SnapSuite) TestSerialVerbose(c *check.C) { 407 s.RedirectClientToTestServer( 408 makeHappyTestServerHandler( 409 c, 410 simpleHappyResponder(happyModelAssertionResponse), 411 simpleHappyResponder(happySerialAssertionResponse), 412 simpleAssertionAccountResponder(happyAccountAssertionResponse), 413 )) 414 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--verbose", "--abs-time"}) 415 c.Assert(err, check.IsNil) 416 c.Assert(rest, check.DeepEquals, []string{}) 417 c.Check(s.Stdout(), check.Equals, ` 418 brand-id: my-brand 419 model: my-old-model 420 serial: serialserial 421 timestamp: 2019-08-26T16:34:21-05:00 422 device-key-sha3-384: | 423 iqLo9doLzK8De9925UrdUyuvPbBad72OTWVE9YJXqd6nz9dKvwJ_lHP5bVxrl3VO 424 device-key: | 425 AcZrBFaFwYABAvCgEOrrLA6FKcreHxCcOoTgBUZ+IRG7Nb8tzmEAklaQPGpv7skapUjwD1luE2g 426 omTcoTssVHrfLpBoSDV1aBs44rg3NK40ZKPJP7d2zkds1GxUo1Ea5vfet3SJ4h3aRABEBAAE= 427 `[1:]) 428 c.Check(s.Stderr(), check.Equals, "") 429 } 430 431 func (s *SnapSuite) TestSerialAssertion(c *check.C) { 432 s.RedirectClientToTestServer( 433 makeHappyTestServerHandler( 434 c, 435 simpleHappyResponder(happyModelAssertionResponse), 436 simpleHappyResponder(happySerialAssertionResponse), 437 simpleAssertionAccountResponder(happyAccountAssertionResponse), 438 )) 439 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--assertion"}) 440 c.Assert(err, check.IsNil) 441 c.Assert(rest, check.DeepEquals, []string{}) 442 c.Check(s.Stdout(), check.Equals, happySerialAssertionResponse) 443 c.Check(s.Stderr(), check.Equals, "") 444 } 445 446 func (s *SnapSuite) TestSerialAssertionSerialAssertionMissing(c *check.C) { 447 s.RedirectClientToTestServer( 448 makeHappyTestServerHandler( 449 c, 450 simpleHappyResponder(happyModelAssertionResponse), 451 simpleUnhappyResponder(noSerialAssertionYetResponse), 452 simpleAssertionAccountResponder(happyAccountAssertionResponse), 453 )) 454 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--assertion"}) 455 c.Assert(err, check.ErrorMatches, `device not ready yet \(no assertions found\)`) 456 c.Assert(s.Stdout(), check.Equals, "") 457 c.Assert(s.Stderr(), check.Equals, "") 458 }