gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/asserts/snapasserts/validation_sets_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 snapasserts_test 21 22 import ( 23 "fmt" 24 "sort" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/asserts" 29 "github.com/snapcore/snapd/asserts/assertstest" 30 "github.com/snapcore/snapd/asserts/snapasserts" 31 "github.com/snapcore/snapd/snap" 32 "github.com/snapcore/snapd/snap/naming" 33 ) 34 35 type validationSetsSuite struct{} 36 37 var _ = Suite(&validationSetsSuite{}) 38 39 func (s *validationSetsSuite) TestAddFromSameSequence(c *C) { 40 mySnapAt7Valset := assertstest.FakeAssertion(map[string]interface{}{ 41 "type": "validation-set", 42 "authority-id": "account-id", 43 "series": "16", 44 "account-id": "account-id", 45 "name": "my-snap-ctl", 46 "sequence": "1", 47 "snaps": []interface{}{ 48 map[string]interface{}{ 49 "name": "my-snap", 50 "id": "mysnapididididididididididididid", 51 "presence": "required", 52 "revision": "7", 53 }, 54 }, 55 }).(*asserts.ValidationSet) 56 57 mySnapAt8Valset := assertstest.FakeAssertion(map[string]interface{}{ 58 "type": "validation-set", 59 "authority-id": "account-id", 60 "series": "16", 61 "account-id": "account-id", 62 "name": "my-snap-ctl", 63 "sequence": "2", 64 "snaps": []interface{}{ 65 map[string]interface{}{ 66 "name": "my-snap", 67 "id": "mysnapididididididididididididid", 68 "presence": "required", 69 "revision": "8", 70 }, 71 }, 72 }).(*asserts.ValidationSet) 73 74 valsets := snapasserts.NewValidationSets() 75 err := valsets.Add(mySnapAt7Valset) 76 c.Assert(err, IsNil) 77 err = valsets.Add(mySnapAt8Valset) 78 c.Check(err, ErrorMatches, `cannot add a second validation-set under "account-id/my-snap-ctl"`) 79 } 80 81 func (s *validationSetsSuite) TestIntersections(c *C) { 82 mySnapAt7Valset := assertstest.FakeAssertion(map[string]interface{}{ 83 "type": "validation-set", 84 "authority-id": "account-id", 85 "series": "16", 86 "account-id": "account-id", 87 "name": "my-snap-ctl", 88 "sequence": "1", 89 "snaps": []interface{}{ 90 map[string]interface{}{ 91 "name": "my-snap", 92 "id": "mysnapididididididididididididid", 93 "presence": "required", 94 "revision": "7", 95 }, 96 }, 97 }).(*asserts.ValidationSet) 98 99 mySnapAt7Valset2 := assertstest.FakeAssertion(map[string]interface{}{ 100 "type": "validation-set", 101 "authority-id": "account-id", 102 "series": "16", 103 "account-id": "account-id", 104 "name": "my-snap-ctl2", 105 "sequence": "2", 106 "snaps": []interface{}{ 107 map[string]interface{}{ 108 "name": "my-snap", 109 "id": "mysnapididididididididididididid", 110 "presence": "required", 111 "revision": "7", 112 }, 113 }, 114 }).(*asserts.ValidationSet) 115 116 mySnapAt8Valset := assertstest.FakeAssertion(map[string]interface{}{ 117 "type": "validation-set", 118 "authority-id": "account-id", 119 "series": "16", 120 "account-id": "account-id", 121 "name": "my-snap-ctl-other", 122 "sequence": "1", 123 "snaps": []interface{}{ 124 map[string]interface{}{ 125 "name": "my-snap", 126 "id": "mysnapididididididididididididid", 127 "presence": "required", 128 "revision": "8", 129 }, 130 }, 131 }).(*asserts.ValidationSet) 132 133 mySnapAt8OptValset := assertstest.FakeAssertion(map[string]interface{}{ 134 "type": "validation-set", 135 "authority-id": "account-id", 136 "series": "16", 137 "account-id": "account-id", 138 "name": "my-snap-ctl-opt", 139 "sequence": "1", 140 "snaps": []interface{}{ 141 map[string]interface{}{ 142 "name": "my-snap", 143 "id": "mysnapididididididididididididid", 144 "presence": "optional", 145 "revision": "8", 146 }, 147 }, 148 }).(*asserts.ValidationSet) 149 150 mySnapInvalidValset := assertstest.FakeAssertion(map[string]interface{}{ 151 "type": "validation-set", 152 "authority-id": "account-id", 153 "series": "16", 154 "account-id": "account-id", 155 "name": "my-snap-ctl-inv", 156 "sequence": "1", 157 "snaps": []interface{}{ 158 map[string]interface{}{ 159 "name": "my-snap", 160 "id": "mysnapididididididididididididid", 161 "presence": "invalid", 162 }, 163 }, 164 }).(*asserts.ValidationSet) 165 166 mySnapAt7OptValset := assertstest.FakeAssertion(map[string]interface{}{ 167 "type": "validation-set", 168 "authority-id": "account-id", 169 "series": "16", 170 "account-id": "account-id", 171 "name": "my-snap-ctl-opt2", 172 "sequence": "1", 173 "snaps": []interface{}{ 174 map[string]interface{}{ 175 "name": "my-snap", 176 "id": "mysnapididididididididididididid", 177 "presence": "optional", 178 "revision": "7", 179 }, 180 }, 181 }).(*asserts.ValidationSet) 182 183 mySnapReqValset := assertstest.FakeAssertion(map[string]interface{}{ 184 "type": "validation-set", 185 "authority-id": "account-id", 186 "series": "16", 187 "account-id": "account-id", 188 "name": "my-snap-ctl-req-only", 189 "sequence": "1", 190 "snaps": []interface{}{ 191 map[string]interface{}{ 192 "name": "my-snap", 193 "id": "mysnapididididididididididididid", 194 "presence": "required", 195 }, 196 }, 197 }).(*asserts.ValidationSet) 198 199 mySnapOptValset := assertstest.FakeAssertion(map[string]interface{}{ 200 "type": "validation-set", 201 "authority-id": "account-id", 202 "series": "16", 203 "account-id": "account-id", 204 "name": "my-snap-ctl-opt-only", 205 "sequence": "1", 206 "snaps": []interface{}{ 207 map[string]interface{}{ 208 "name": "my-snap", 209 "id": "mysnapididididididididididididid", 210 "presence": "optional", 211 }, 212 }, 213 }).(*asserts.ValidationSet) 214 215 tests := []struct { 216 sets []*asserts.ValidationSet 217 conflictErr string 218 }{ 219 {[]*asserts.ValidationSet{mySnapAt7Valset}, ""}, 220 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt7Valset2}, ""}, 221 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\)`}, 222 {[]*asserts.ValidationSet{mySnapAt8Valset, mySnapAt8OptValset}, ""}, 223 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8OptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-opt\)`}, 224 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at revision 7 \(account-id/my-snap-ctl\)`}, 225 {[]*asserts.ValidationSet{mySnapInvalidValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at revision 7 \(account-id/my-snap-ctl\)`}, 226 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapInvalidValset}, ""}, 227 {[]*asserts.ValidationSet{mySnapInvalidValset, mySnapAt8OptValset}, ""}, 228 {[]*asserts.ValidationSet{mySnapAt7OptValset, mySnapAt8OptValset}, ""}, // no conflict but interpreted as invalid 229 {[]*asserts.ValidationSet{mySnapAt7OptValset, mySnapAt8OptValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl,account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\)`}, 230 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapInvalidValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-opt\)`}, 231 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapReqValset}, ""}, 232 {[]*asserts.ValidationSet{mySnapReqValset, mySnapAt7Valset}, ""}, 233 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapReqValset}, ""}, 234 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapReqValset, mySnapAt7OptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\) or required at any revision \(account-id/my-snap-ctl-req-only\)`}, 235 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapAt7OptValset, mySnapReqValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\) or required at any revision \(account-id/my-snap-ctl-req-only\)`}, 236 {[]*asserts.ValidationSet{mySnapReqValset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at any revision \(account-id/my-snap-ctl-req-only\)`}, 237 {[]*asserts.ValidationSet{mySnapInvalidValset, mySnapReqValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at any revision \(account-id/my-snap-ctl-req-only\)`}, 238 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset, mySnapOptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\)`}, 239 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapOptValset}, ""}, 240 {[]*asserts.ValidationSet{mySnapOptValset, mySnapAt7Valset}, ""}, 241 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapOptValset}, ""}, 242 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapOptValset, mySnapAt7OptValset}, ""}, // no conflict but interpreted as invalid 243 {[]*asserts.ValidationSet{mySnapInvalidValset, mySnapOptValset}, ""}, 244 {[]*asserts.ValidationSet{mySnapOptValset, mySnapInvalidValset}, ""}, 245 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset, mySnapReqValset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\) or at any revision \(account-id/my-snap-ctl-req-only\)`}, 246 } 247 248 for _, t := range tests { 249 valsets := snapasserts.NewValidationSets() 250 cSets := make(map[string]*asserts.ValidationSet) 251 for _, valset := range t.sets { 252 err := valsets.Add(valset) 253 c.Assert(err, IsNil) 254 // mySnapOptValset never influcens an outcome 255 if valset != mySnapOptValset { 256 cSets[fmt.Sprintf("%s/%s", valset.AccountID(), valset.Name())] = valset 257 } 258 } 259 err := valsets.Conflict() 260 if t.conflictErr == "" { 261 c.Check(err, IsNil) 262 } else { 263 c.Check(err, ErrorMatches, t.conflictErr) 264 ce := err.(*snapasserts.ValidationSetsConflictError) 265 c.Check(ce.Sets, DeepEquals, cSets) 266 } 267 } 268 } 269 270 func (s *validationSetsSuite) TestCheckInstalledSnapsNoValidationSets(c *C) { 271 valsets := snapasserts.NewValidationSets() 272 snaps := []*snapasserts.InstalledSnap{ 273 snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1)), 274 } 275 err := valsets.CheckInstalledSnaps(snaps) 276 c.Assert(err, IsNil) 277 } 278 279 func (s *validationSetsSuite) TestCheckInstalledSnaps(c *C) { 280 // require: snapB rev 3, snapC rev 2. 281 // invalid: snapA 282 vs1 := assertstest.FakeAssertion(map[string]interface{}{ 283 "type": "validation-set", 284 "authority-id": "acme", 285 "series": "16", 286 "account-id": "acme", 287 "name": "fooname", 288 "sequence": "1", 289 "snaps": []interface{}{ 290 map[string]interface{}{ 291 "name": "snap-a", 292 "id": "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", 293 "presence": "invalid", 294 }, 295 map[string]interface{}{ 296 "name": "snap-b", 297 "id": "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", 298 "revision": "3", 299 "presence": "required", 300 }, 301 map[string]interface{}{ 302 "name": "snap-c", 303 "id": "mysnapcccccccccccccccccccccccccc", 304 "revision": "2", 305 "presence": "optional", 306 }, 307 }, 308 }).(*asserts.ValidationSet) 309 310 // require: snapD any rev 311 // optional: snapE any rev 312 vs2 := assertstest.FakeAssertion(map[string]interface{}{ 313 "type": "validation-set", 314 "authority-id": "acme", 315 "series": "16", 316 "account-id": "acme", 317 "name": "barname", 318 "sequence": "3", 319 "snaps": []interface{}{ 320 map[string]interface{}{ 321 "name": "snap-d", 322 "id": "mysnapdddddddddddddddddddddddddd", 323 "presence": "required", 324 }, 325 map[string]interface{}{ 326 "name": "snap-e", 327 "id": "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee", 328 "presence": "optional", 329 }, 330 }, 331 }).(*asserts.ValidationSet) 332 333 // optional: snapE any rev 334 // note: since it only has an optional snap, acme/bazname is not expected 335 // not be invalid by any of the checks. 336 vs3 := assertstest.FakeAssertion(map[string]interface{}{ 337 "type": "validation-set", 338 "authority-id": "acme", 339 "series": "16", 340 "account-id": "acme", 341 "name": "bazname", 342 "sequence": "2", 343 "snaps": []interface{}{ 344 map[string]interface{}{ 345 "name": "snap-e", 346 "id": "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee", 347 "presence": "optional", 348 }, 349 }, 350 }).(*asserts.ValidationSet) 351 352 // invalid: snapA 353 vs4 := assertstest.FakeAssertion(map[string]interface{}{ 354 "type": "validation-set", 355 "authority-id": "acme", 356 "series": "16", 357 "account-id": "acme", 358 "name": "booname", 359 "sequence": "1", 360 "snaps": []interface{}{ 361 map[string]interface{}{ 362 "name": "snap-a", 363 "id": "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", 364 "presence": "invalid", 365 }, 366 }, 367 }).(*asserts.ValidationSet) 368 369 valsets := snapasserts.NewValidationSets() 370 c.Assert(valsets.Add(vs1), IsNil) 371 c.Assert(valsets.Add(vs2), IsNil) 372 c.Assert(valsets.Add(vs3), IsNil) 373 c.Assert(valsets.Add(vs4), IsNil) 374 375 snapA := snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1)) 376 snapAlocal := snapasserts.NewInstalledSnap("snap-a", "", snap.R("x2")) 377 snapB := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(3)) 378 snapBinvRev := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(8)) 379 snapBlocal := snapasserts.NewInstalledSnap("snap-b", "", snap.R("x3")) 380 snapC := snapasserts.NewInstalledSnap("snap-c", "mysnapcccccccccccccccccccccccccc", snap.R(2)) 381 snapCinvRev := snapasserts.NewInstalledSnap("snap-c", "mysnapcccccccccccccccccccccccccc", snap.R(99)) 382 snapD := snapasserts.NewInstalledSnap("snap-d", "mysnapdddddddddddddddddddddddddd", snap.R(2)) 383 snapDrev99 := snapasserts.NewInstalledSnap("snap-d", "mysnapdddddddddddddddddddddddddd", snap.R(99)) 384 snapDlocal := snapasserts.NewInstalledSnap("snap-d", "", snap.R("x3")) 385 snapE := snapasserts.NewInstalledSnap("snap-e", "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee", snap.R(2)) 386 // extra snap, not referenced by any validation set 387 snapZ := snapasserts.NewInstalledSnap("snap-z", "mysnapzzzzzzzzzzzzzzzzzzzzzzzzzz", snap.R(1)) 388 389 tests := []struct { 390 snaps []*snapasserts.InstalledSnap 391 expectedInvalid map[string][]string 392 expectedMissing map[string][]string 393 expectedWrongRev map[string]map[snap.Revision][]string 394 }{ 395 { 396 // required snaps not installed 397 snaps: nil, 398 expectedMissing: map[string][]string{ 399 "snap-b": {"acme/fooname"}, 400 "snap-d": {"acme/barname"}, 401 }, 402 }, 403 { 404 // required snaps not installed 405 snaps: []*snapasserts.InstalledSnap{ 406 snapZ, 407 }, 408 expectedMissing: map[string][]string{ 409 "snap-b": {"acme/fooname"}, 410 "snap-d": {"acme/barname"}, 411 }, 412 }, 413 { 414 snaps: []*snapasserts.InstalledSnap{ 415 // covered by acme/fooname validation-set 416 snapB, 417 // covered by acme/barname validation-set. snap-e not installed but optional 418 snapDrev99}, 419 // ale fine 420 }, 421 { 422 snaps: []*snapasserts.InstalledSnap{ 423 // covered by acme/fooname validation-set and acme/booname, snap-a presence is invalid 424 snapA, 425 snapB, 426 // covered by acme/barname validation-set. snap-e not installed but optional 427 snapDrev99}, 428 expectedInvalid: map[string][]string{ 429 "snap-a": {"acme/booname", "acme/fooname"}, 430 }, 431 }, 432 { 433 snaps: []*snapasserts.InstalledSnap{ 434 // covered by acme/fooname and acme/booname validation-sets, snapB missing, snap-a presence is invalid 435 snapA, 436 // covered by acme/barname validation-set. snap-e not installed but optional 437 snapDrev99}, 438 expectedInvalid: map[string][]string{ 439 "snap-a": {"acme/booname", "acme/fooname"}, 440 }, 441 expectedMissing: map[string][]string{ 442 "snap-b": {"acme/fooname"}, 443 }, 444 }, 445 { 446 snaps: []*snapasserts.InstalledSnap{ 447 // covered by acme/fooname validation-set 448 snapB, 449 snapC, 450 // covered by acme/barname validation-set. snap-e not installed but optional 451 snapD}, 452 // ale fine 453 }, 454 { 455 snaps: []*snapasserts.InstalledSnap{ 456 // covered by acme/fooname validation-set, snap-c optional but wrong revision 457 snapB, 458 snapCinvRev, 459 // covered by acme/barname validation-set. snap-e not installed but optional 460 snapD}, 461 expectedWrongRev: map[string]map[snap.Revision][]string{ 462 "snap-c": { 463 snap.R(2): {"acme/fooname"}, 464 }, 465 }, 466 }, 467 { 468 snaps: []*snapasserts.InstalledSnap{ 469 // covered by acme/fooname validation-set but wrong revision 470 snapBinvRev, 471 // covered by acme/barname validation-set. 472 snapD}, 473 expectedWrongRev: map[string]map[snap.Revision][]string{ 474 "snap-b": { 475 snap.R(3): {"acme/fooname"}, 476 }, 477 }, 478 }, 479 { 480 snaps: []*snapasserts.InstalledSnap{ 481 // covered by acme/fooname validation-set 482 snapB, 483 // covered by acme/barname validation-set. snap-d not installed. 484 snapE}, 485 expectedMissing: map[string][]string{ 486 "snap-d": {"acme/barname"}, 487 }, 488 }, 489 { 490 snaps: []*snapasserts.InstalledSnap{ 491 // required snaps from acme/fooname are not installed. 492 // covered by acme/barname validation-set 493 snapDrev99, 494 snapE}, 495 expectedMissing: map[string][]string{ 496 "snap-b": {"acme/fooname"}, 497 }, 498 }, 499 { 500 snaps: []*snapasserts.InstalledSnap{ 501 // covered by acme/fooname validation-set, required missing. 502 snapC, 503 // covered by acme/barname validation-set, required missing. 504 snapE}, 505 expectedMissing: map[string][]string{ 506 "snap-b": {"acme/fooname"}, 507 "snap-d": {"acme/barname"}, 508 }, 509 }, 510 // local snaps 511 { 512 snaps: []*snapasserts.InstalledSnap{ 513 // covered by acme/fooname validation-set. 514 snapB, 515 // covered by acme/barname validation-set, local snap-d. 516 snapDlocal}, 517 // all fine 518 }, 519 { 520 snaps: []*snapasserts.InstalledSnap{ 521 // covered by acme/fooname validation-set, snap-a is invalid. 522 snapAlocal, 523 snapB, 524 // covered by acme/barname validation-set. 525 snapD}, 526 expectedInvalid: map[string][]string{ 527 "snap-a": {"acme/booname", "acme/fooname"}, 528 }, 529 }, 530 { 531 snaps: []*snapasserts.InstalledSnap{ 532 // covered by acme/fooname validation-set, snap-b is wrong rev (local). 533 snapBlocal, 534 // covered by acme/barname validation-set. 535 snapD}, 536 expectedWrongRev: map[string]map[snap.Revision][]string{ 537 "snap-b": { 538 snap.R(3): {"acme/fooname"}, 539 }, 540 }, 541 }, 542 } 543 544 checkSets := func(snapsToValidationSets map[string][]string, vs map[string]*asserts.ValidationSet) { 545 for _, vsetKeys := range snapsToValidationSets { 546 for _, key := range vsetKeys { 547 vset, ok := vs[key] 548 c.Assert(ok, Equals, true) 549 c.Assert(vset.AccountID()+"/"+vset.Name(), Equals, key) 550 } 551 } 552 } 553 554 for i, tc := range tests { 555 err := valsets.CheckInstalledSnaps(tc.snaps) 556 if err == nil { 557 c.Assert(tc.expectedInvalid, IsNil) 558 c.Assert(tc.expectedMissing, IsNil) 559 c.Assert(tc.expectedWrongRev, IsNil) 560 continue 561 } 562 verr, ok := err.(*snapasserts.ValidationSetsValidationError) 563 c.Assert(ok, Equals, true, Commentf("#%d", i)) 564 c.Assert(tc.expectedInvalid, DeepEquals, verr.InvalidSnaps, Commentf("#%d", i)) 565 c.Assert(tc.expectedMissing, DeepEquals, verr.MissingSnaps, Commentf("#%d", i)) 566 c.Assert(tc.expectedWrongRev, DeepEquals, verr.WrongRevisionSnaps, Commentf("#%d", i)) 567 checkSets(verr.InvalidSnaps, verr.Sets) 568 } 569 } 570 571 func (s *validationSetsSuite) TestCheckInstalledSnapsErrorFormat(c *C) { 572 vs1 := assertstest.FakeAssertion(map[string]interface{}{ 573 "type": "validation-set", 574 "authority-id": "acme", 575 "series": "16", 576 "account-id": "acme", 577 "name": "fooname", 578 "sequence": "1", 579 "snaps": []interface{}{ 580 map[string]interface{}{ 581 "name": "snap-a", 582 "id": "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", 583 "presence": "invalid", 584 }, 585 map[string]interface{}{ 586 "name": "snap-b", 587 "id": "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", 588 "revision": "3", 589 "presence": "required", 590 }, 591 }, 592 }).(*asserts.ValidationSet) 593 vs2 := assertstest.FakeAssertion(map[string]interface{}{ 594 "type": "validation-set", 595 "authority-id": "acme", 596 "series": "16", 597 "account-id": "acme", 598 "name": "barname", 599 "sequence": "2", 600 "snaps": []interface{}{ 601 map[string]interface{}{ 602 "name": "snap-b", 603 "id": "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", 604 "revision": "5", 605 "presence": "required", 606 }, 607 }, 608 }).(*asserts.ValidationSet) 609 610 valsets := snapasserts.NewValidationSets() 611 c.Assert(valsets.Add(vs1), IsNil) 612 c.Assert(valsets.Add(vs2), IsNil) 613 614 snapA := snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1)) 615 snapBlocal := snapasserts.NewInstalledSnap("snap-b", "", snap.R("x3")) 616 617 tests := []struct { 618 snaps []*snapasserts.InstalledSnap 619 errorMsg string 620 }{ 621 { 622 nil, 623 "validation sets assertions are not met:\n" + 624 "- missing required snaps:\n" + 625 " - snap-b \\(required by sets acme/barname,acme/fooname\\)", 626 }, 627 { 628 []*snapasserts.InstalledSnap{snapA}, 629 "validation sets assertions are not met:\n" + 630 "- missing required snaps:\n" + 631 " - snap-b \\(required by sets acme/barname,acme/fooname\\)\n" + 632 "- invalid snaps:\n" + 633 " - snap-a \\(invalid for sets acme/fooname\\)", 634 }, 635 { 636 []*snapasserts.InstalledSnap{snapBlocal}, 637 "validation sets assertions are not met:\n" + 638 "- snaps at wrong revisions:\n" + 639 " - snap-b \\(required at revision 3 by sets acme/fooname, at revision 5 by sets acme/barname\\)", 640 }, 641 } 642 643 for i, tc := range tests { 644 err := valsets.CheckInstalledSnaps(tc.snaps) 645 c.Assert(err, NotNil, Commentf("#%d", i)) 646 c.Assert(err, ErrorMatches, tc.errorMsg, Commentf("#%d: ", i)) 647 } 648 } 649 650 func (s *validationSetsSuite) TestSortByRevision(c *C) { 651 revs := []snap.Revision{snap.R(10), snap.R(4), snap.R(5), snap.R(-1)} 652 653 sort.Sort(snapasserts.ByRevision(revs)) 654 c.Assert(revs, DeepEquals, []snap.Revision{snap.R(-1), snap.R(4), snap.R(5), snap.R(10)}) 655 } 656 657 func (s *validationSetsSuite) TestCheckPresenceRequired(c *C) { 658 valset1 := assertstest.FakeAssertion(map[string]interface{}{ 659 "type": "validation-set", 660 "authority-id": "account-id", 661 "series": "16", 662 "account-id": "account-id", 663 "name": "my-snap-ctl", 664 "sequence": "1", 665 "snaps": []interface{}{ 666 map[string]interface{}{ 667 "name": "my-snap", 668 "id": "mysnapididididididididididididid", 669 "presence": "required", 670 "revision": "7", 671 }, 672 map[string]interface{}{ 673 "name": "other-snap", 674 "id": "123456ididididididididididididid", 675 "presence": "optional", 676 }, 677 }, 678 }).(*asserts.ValidationSet) 679 680 valset2 := assertstest.FakeAssertion(map[string]interface{}{ 681 "type": "validation-set", 682 "authority-id": "account-id", 683 "series": "16", 684 "account-id": "account-id", 685 "name": "my-snap-ctl2", 686 "sequence": "2", 687 "snaps": []interface{}{ 688 map[string]interface{}{ 689 "name": "my-snap", 690 "id": "mysnapididididididididididididid", 691 "presence": "required", 692 "revision": "7", 693 }, 694 map[string]interface{}{ 695 "name": "other-snap", 696 "id": "123456ididididididididididididid", 697 "presence": "invalid", 698 }, 699 }, 700 }).(*asserts.ValidationSet) 701 702 // my-snap required but no specific revision set. 703 valset3 := assertstest.FakeAssertion(map[string]interface{}{ 704 "type": "validation-set", 705 "authority-id": "account-id", 706 "series": "16", 707 "account-id": "account-id", 708 "name": "my-snap-ctl3", 709 "sequence": "1", 710 "snaps": []interface{}{ 711 map[string]interface{}{ 712 "name": "my-snap", 713 "id": "mysnapididididididididididididid", 714 "presence": "required", 715 }, 716 }, 717 }).(*asserts.ValidationSet) 718 719 valsets := snapasserts.NewValidationSets() 720 721 // no validation sets 722 vsKeys, _, err := valsets.CheckPresenceRequired(naming.Snap("my-snap")) 723 c.Assert(err, IsNil) 724 c.Check(vsKeys, HasLen, 0) 725 726 c.Assert(valsets.Add(valset1), IsNil) 727 c.Assert(valsets.Add(valset2), IsNil) 728 c.Assert(valsets.Add(valset3), IsNil) 729 730 // sanity 731 c.Assert(valsets.Conflict(), IsNil) 732 733 vsKeys, rev, err := valsets.CheckPresenceRequired(naming.Snap("my-snap")) 734 c.Assert(err, IsNil) 735 c.Check(rev, DeepEquals, snap.Revision{N: 7}) 736 c.Check(vsKeys, DeepEquals, []string{"account-id/my-snap-ctl", "account-id/my-snap-ctl2", "account-id/my-snap-ctl3"}) 737 738 vsKeys, rev, err = valsets.CheckPresenceRequired(naming.NewSnapRef("my-snap", "mysnapididididididididididididid")) 739 c.Assert(err, IsNil) 740 c.Check(rev, DeepEquals, snap.Revision{N: 7}) 741 c.Check(vsKeys, DeepEquals, []string{"account-id/my-snap-ctl", "account-id/my-snap-ctl2", "account-id/my-snap-ctl3"}) 742 743 // other-snap is not required 744 vsKeys, rev, err = valsets.CheckPresenceRequired(naming.Snap("other-snap")) 745 c.Assert(err, ErrorMatches, `unexpected presence "invalid" for snap "other-snap"`) 746 pr, ok := err.(*snapasserts.PresenceConstraintError) 747 c.Assert(ok, Equals, true) 748 c.Check(pr.SnapName, Equals, "other-snap") 749 c.Check(pr.Presence, Equals, asserts.PresenceInvalid) 750 c.Check(rev, DeepEquals, snap.Revision{N: 0}) 751 c.Check(vsKeys, HasLen, 0) 752 753 // unknown snap is not required 754 vsKeys, rev, err = valsets.CheckPresenceRequired(naming.NewSnapRef("unknown-snap", "00000000idididididididididididid")) 755 c.Assert(err, IsNil) 756 c.Check(rev, DeepEquals, snap.Revision{N: 0}) 757 c.Check(vsKeys, HasLen, 0) 758 759 // just one set, required but no revision specified 760 valsets = snapasserts.NewValidationSets() 761 c.Assert(valsets.Add(valset3), IsNil) 762 vsKeys, rev, err = valsets.CheckPresenceRequired(naming.Snap("my-snap")) 763 c.Assert(err, IsNil) 764 c.Check(rev, DeepEquals, snap.Revision{N: 0}) 765 c.Check(vsKeys, DeepEquals, []string{"account-id/my-snap-ctl3"}) 766 } 767 768 func (s *validationSetsSuite) TestIsPresenceInvalid(c *C) { 769 valset1 := assertstest.FakeAssertion(map[string]interface{}{ 770 "type": "validation-set", 771 "authority-id": "account-id", 772 "series": "16", 773 "account-id": "account-id", 774 "name": "my-snap-ctl", 775 "sequence": "1", 776 "snaps": []interface{}{ 777 map[string]interface{}{ 778 "name": "my-snap", 779 "id": "mysnapididididididididididididid", 780 "presence": "invalid", 781 }, 782 map[string]interface{}{ 783 "name": "other-snap", 784 "id": "123456ididididididididididididid", 785 "presence": "optional", 786 }, 787 }, 788 }).(*asserts.ValidationSet) 789 790 valset2 := assertstest.FakeAssertion(map[string]interface{}{ 791 "type": "validation-set", 792 "authority-id": "account-id", 793 "series": "16", 794 "account-id": "account-id", 795 "name": "my-snap-ctl2", 796 "sequence": "2", 797 "snaps": []interface{}{ 798 map[string]interface{}{ 799 "name": "my-snap", 800 "id": "mysnapididididididididididididid", 801 "presence": "invalid", 802 }, 803 }, 804 }).(*asserts.ValidationSet) 805 806 valsets := snapasserts.NewValidationSets() 807 808 // no validation sets 809 vsKeys, err := valsets.CheckPresenceInvalid(naming.Snap("my-snap")) 810 c.Assert(err, IsNil) 811 c.Check(vsKeys, HasLen, 0) 812 813 c.Assert(valsets.Add(valset1), IsNil) 814 c.Assert(valsets.Add(valset2), IsNil) 815 816 // sanity 817 c.Assert(valsets.Conflict(), IsNil) 818 819 // invalid in two sets 820 vsKeys, err = valsets.CheckPresenceInvalid(naming.Snap("my-snap")) 821 c.Assert(err, IsNil) 822 c.Check(vsKeys, DeepEquals, []string{"account-id/my-snap-ctl", "account-id/my-snap-ctl2"}) 823 824 vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("my-snap", "mysnapididididididididididididid")) 825 c.Assert(err, IsNil) 826 c.Check(vsKeys, DeepEquals, []string{"account-id/my-snap-ctl", "account-id/my-snap-ctl2"}) 827 828 // other-snap isn't invalid 829 vsKeys, err = valsets.CheckPresenceInvalid(naming.Snap("other-snap")) 830 c.Assert(err, ErrorMatches, `unexpected presence "optional" for snap "other-snap"`) 831 pr, ok := err.(*snapasserts.PresenceConstraintError) 832 c.Assert(ok, Equals, true) 833 c.Check(pr.SnapName, Equals, "other-snap") 834 c.Check(pr.Presence, Equals, asserts.PresenceOptional) 835 c.Check(vsKeys, HasLen, 0) 836 837 vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("other-snap", "123456ididididididididididididid")) 838 c.Assert(err, ErrorMatches, `unexpected presence "optional" for snap "other-snap"`) 839 c.Check(vsKeys, HasLen, 0) 840 841 // unknown snap isn't invalid 842 vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("unknown-snap", "00000000idididididididididididid")) 843 c.Assert(err, IsNil) 844 c.Check(vsKeys, HasLen, 0) 845 }