gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/assertstate/assertstate.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-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 assertstate implements the manager and state aspects responsible 21 // for the enforcement of assertions in the system and manages the system-wide 22 // assertion database. 23 package assertstate 24 25 import ( 26 "fmt" 27 "strings" 28 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/asserts/snapasserts" 31 "github.com/snapcore/snapd/httputil" 32 "github.com/snapcore/snapd/logger" 33 "github.com/snapcore/snapd/overlord/snapstate" 34 "github.com/snapcore/snapd/overlord/state" 35 "github.com/snapcore/snapd/release" 36 "github.com/snapcore/snapd/snap" 37 ) 38 39 // Add the given assertion to the system assertion database. 40 func Add(s *state.State, a asserts.Assertion) error { 41 // TODO: deal together with asserts itself with (cascading) side effects of possible assertion updates 42 return cachedDB(s).Add(a) 43 } 44 45 // AddBatch adds the given assertion batch to the system assertion database. 46 func AddBatch(s *state.State, batch *asserts.Batch, opts *asserts.CommitOptions) error { 47 return batch.CommitTo(cachedDB(s), opts) 48 } 49 50 func findError(format string, ref *asserts.Ref, err error) error { 51 if asserts.IsNotFound(err) { 52 return fmt.Errorf(format, ref) 53 } else { 54 return fmt.Errorf(format+": %v", ref, err) 55 } 56 } 57 58 type RefreshAssertionsOptions struct { 59 IsAutoRefresh bool 60 } 61 62 // RefreshSnapDeclarations refetches all the current snap declarations and their prerequisites. 63 func RefreshSnapDeclarations(s *state.State, userID int, opts *RefreshAssertionsOptions) error { 64 if opts == nil { 65 opts = &RefreshAssertionsOptions{} 66 } 67 68 deviceCtx, err := snapstate.DevicePastSeeding(s, nil) 69 if err != nil { 70 return err 71 } 72 73 snapStates, err := snapstate.All(s) 74 if err != nil { 75 return nil 76 } 77 78 err = bulkRefreshSnapDeclarations(s, snapStates, userID, deviceCtx, opts) 79 if err == nil { 80 // done 81 return nil 82 } 83 if _, ok := err.(*bulkAssertionFallbackError); !ok { 84 // not an error that indicates the server rejecting/failing 85 // the bulk request itself 86 return err 87 } 88 logger.Noticef("bulk refresh of snap-declarations failed, falling back to one-by-one assertion fetching: %v", err) 89 90 modelAs := deviceCtx.Model() 91 92 fetching := func(f asserts.Fetcher) error { 93 for instanceName, snapst := range snapStates { 94 sideInfo := snapst.CurrentSideInfo() 95 if sideInfo.SnapID == "" { 96 continue 97 } 98 if err := snapasserts.FetchSnapDeclaration(f, sideInfo.SnapID); err != nil { 99 if notRetried, ok := err.(*httputil.PersistentNetworkError); ok { 100 return notRetried 101 } 102 return fmt.Errorf("cannot refresh snap-declaration for %q: %v", instanceName, err) 103 } 104 } 105 106 // fetch store assertion if available 107 if modelAs.Store() != "" { 108 err := snapasserts.FetchStore(f, modelAs.Store()) 109 if err != nil && !asserts.IsNotFound(err) { 110 return err 111 } 112 } 113 114 return nil 115 } 116 return doFetch(s, userID, deviceCtx, fetching) 117 } 118 119 type refreshControlError struct { 120 errs []error 121 } 122 123 func (e *refreshControlError) Error() string { 124 if len(e.errs) == 1 { 125 return e.errs[0].Error() 126 } 127 l := []string{""} 128 for _, e := range e.errs { 129 l = append(l, e.Error()) 130 } 131 return fmt.Sprintf("refresh control errors:%s", strings.Join(l, "\n - ")) 132 } 133 134 // ValidateRefreshes validates the refresh candidate revisions represented by 135 // the snapInfos, looking for the needed refresh control validation assertions, 136 // it returns a validated subset in validated and a summary error if not all 137 // candidates validated. ignoreValidation is a set of snap-instance-names that 138 // should not be gated. 139 func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) (validated []*snap.Info, err error) { 140 // maps gated snap-ids to gating snap-ids 141 controlled := make(map[string][]string) 142 // maps gating snap-ids to their snap names 143 gatingNames := make(map[string]string) 144 145 db := DB(s) 146 snapStates, err := snapstate.All(s) 147 if err != nil { 148 return nil, err 149 } 150 for instanceName, snapst := range snapStates { 151 info, err := snapst.CurrentInfo() 152 if err != nil { 153 return nil, err 154 } 155 if info.SnapID == "" { 156 continue 157 } 158 gatingID := info.SnapID 159 if gatingNames[gatingID] != "" { 160 continue 161 } 162 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 163 "series": release.Series, 164 "snap-id": gatingID, 165 }) 166 if err != nil { 167 return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q: %v", instanceName, err) 168 } 169 decl := a.(*asserts.SnapDeclaration) 170 control := decl.RefreshControl() 171 if len(control) == 0 { 172 continue 173 } 174 gatingNames[gatingID] = decl.SnapName() 175 for _, gatedID := range control { 176 controlled[gatedID] = append(controlled[gatedID], gatingID) 177 } 178 } 179 180 var errs []error 181 for _, candInfo := range snapInfos { 182 if ignoreValidation[candInfo.InstanceName()] { 183 validated = append(validated, candInfo) 184 continue 185 } 186 gatedID := candInfo.SnapID 187 gating := controlled[gatedID] 188 if len(gating) == 0 { // easy case, no refresh control 189 validated = append(validated, candInfo) 190 continue 191 } 192 193 var validationRefs []*asserts.Ref 194 195 fetching := func(f asserts.Fetcher) error { 196 for _, gatingID := range gating { 197 valref := &asserts.Ref{ 198 Type: asserts.ValidationType, 199 PrimaryKey: []string{release.Series, gatingID, gatedID, candInfo.Revision.String()}, 200 } 201 err := f.Fetch(valref) 202 if notFound, ok := err.(*asserts.NotFoundError); ok && notFound.Type == asserts.ValidationType { 203 return fmt.Errorf("no validation by %q", gatingNames[gatingID]) 204 } 205 if err != nil { 206 return fmt.Errorf("cannot find validation by %q: %v", gatingNames[gatingID], err) 207 } 208 validationRefs = append(validationRefs, valref) 209 } 210 return nil 211 } 212 err := doFetch(s, userID, deviceCtx, fetching) 213 if err != nil { 214 errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: %v", candInfo.InstanceName(), candInfo.Revision, err)) 215 continue 216 } 217 218 var revoked *asserts.Validation 219 for _, valref := range validationRefs { 220 a, err := valref.Resolve(db.Find) 221 if err != nil { 222 return nil, findError("internal error: cannot find just fetched %v", valref, err) 223 } 224 if val := a.(*asserts.Validation); val.Revoked() { 225 revoked = val 226 break 227 } 228 } 229 if revoked != nil { 230 errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: validation by %q (id %q) revoked", candInfo.InstanceName(), candInfo.Revision, gatingNames[revoked.SnapID()], revoked.SnapID())) 231 continue 232 } 233 234 validated = append(validated, candInfo) 235 } 236 237 if errs != nil { 238 return validated, &refreshControlError{errs} 239 } 240 241 return validated, nil 242 } 243 244 // BaseDeclaration returns the base-declaration assertion with policies governing all snaps. 245 func BaseDeclaration(s *state.State) (*asserts.BaseDeclaration, error) { 246 // TODO: switch keeping this in the DB and have it revisioned/updated 247 // via the store 248 baseDecl := asserts.BuiltinBaseDeclaration() 249 if baseDecl == nil { 250 return nil, &asserts.NotFoundError{Type: asserts.BaseDeclarationType} 251 } 252 return baseDecl, nil 253 } 254 255 // SnapDeclaration returns the snap-declaration for the given snap-id if it is present in the system assertion database. 256 func SnapDeclaration(s *state.State, snapID string) (*asserts.SnapDeclaration, error) { 257 db := DB(s) 258 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 259 "series": release.Series, 260 "snap-id": snapID, 261 }) 262 if err != nil { 263 return nil, err 264 } 265 return a.(*asserts.SnapDeclaration), nil 266 } 267 268 // Publisher returns the account assertion for publisher of the given snap-id if it is present in the system assertion database. 269 func Publisher(s *state.State, snapID string) (*asserts.Account, error) { 270 db := DB(s) 271 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 272 "series": release.Series, 273 "snap-id": snapID, 274 }) 275 if err != nil { 276 return nil, err 277 } 278 snapDecl := a.(*asserts.SnapDeclaration) 279 a, err = db.Find(asserts.AccountType, map[string]string{ 280 "account-id": snapDecl.PublisherID(), 281 }) 282 if err != nil { 283 return nil, fmt.Errorf("internal error: cannot find account assertion for the publisher of snap %q: %v", snapDecl.SnapName(), err) 284 } 285 return a.(*asserts.Account), nil 286 } 287 288 // Store returns the store assertion with the given name/id if it is 289 // present in the system assertion database. 290 func Store(s *state.State, store string) (*asserts.Store, error) { 291 db := DB(s) 292 a, err := db.Find(asserts.StoreType, map[string]string{ 293 "store": store, 294 }) 295 if err != nil { 296 return nil, err 297 } 298 return a.(*asserts.Store), nil 299 } 300 301 // AutoAliases returns the explicit automatic aliases alias=>app mapping for the given installed snap. 302 func AutoAliases(s *state.State, info *snap.Info) (map[string]string, error) { 303 if info.SnapID == "" { 304 // without declaration 305 return nil, nil 306 } 307 decl, err := SnapDeclaration(s, info.SnapID) 308 if err != nil { 309 return nil, fmt.Errorf("internal error: cannot find snap-declaration for installed snap %q: %v", info.InstanceName(), err) 310 } 311 explicitAliases := decl.Aliases() 312 if len(explicitAliases) != 0 { 313 return explicitAliases, nil 314 } 315 // XXX: old header fallback, just to keep edge working while we fix the 316 // store, to remove before next release! 317 oldAutoAliases := decl.AutoAliases() 318 if len(oldAutoAliases) == 0 { 319 return nil, nil 320 } 321 res := make(map[string]string, len(oldAutoAliases)) 322 for _, alias := range oldAutoAliases { 323 app := info.LegacyAliases[alias] 324 if app == nil { 325 // not a known alias anymore or yet, skip 326 continue 327 328 } 329 res[alias] = app.Name 330 } 331 return res, nil 332 } 333 334 func delayedCrossMgrInit() { 335 // hook validation of refreshes into snapstate logic 336 snapstate.ValidateRefreshes = ValidateRefreshes 337 // hook auto refresh of assertions (snap declarations) into snapstate 338 snapstate.AutoRefreshAssertions = AutoRefreshAssertions 339 // hook retrieving auto-aliases into snapstate logic 340 snapstate.AutoAliases = AutoAliases 341 // hook the helper for getting enforced validation sets 342 snapstate.EnforcedValidationSets = EnforcedValidationSets 343 } 344 345 // AutoRefreshAssertions tries to refresh all assertions 346 func AutoRefreshAssertions(s *state.State, userID int) error { 347 opts := &RefreshAssertionsOptions{IsAutoRefresh: true} 348 if err := RefreshSnapDeclarations(s, userID, opts); err != nil { 349 return err 350 } 351 return RefreshValidationSetAssertions(s, userID, opts) 352 } 353 354 // RefreshSnapAssertions tries to refresh all snap-centered assertions 355 func RefreshSnapAssertions(s *state.State, userID int) error { 356 opts := &RefreshAssertionsOptions{IsAutoRefresh: false} 357 if err := RefreshSnapDeclarations(s, userID, opts); err != nil { 358 return err 359 } 360 return RefreshValidationSetAssertions(s, userID, opts) 361 } 362 363 // RefreshValidationSetAssertions tries to refresh all validation set 364 // assertions. 365 func RefreshValidationSetAssertions(s *state.State, userID int, opts *RefreshAssertionsOptions) error { 366 if opts == nil { 367 opts = &RefreshAssertionsOptions{} 368 } 369 370 deviceCtx, err := snapstate.DevicePastSeeding(s, nil) 371 if err != nil { 372 return err 373 } 374 375 vsets, err := ValidationSets(s) 376 if err != nil { 377 return err 378 } 379 if len(vsets) == 0 { 380 return nil 381 } 382 383 monitorModeSets := make(map[string]*ValidationSetTracking) 384 enforceModeSets := make(map[string]*ValidationSetTracking) 385 for vk, vset := range vsets { 386 if vset.Mode == Monitor { 387 monitorModeSets[vk] = vset 388 } else { 389 enforceModeSets[vk] = vset 390 } 391 } 392 393 updateTracking := func(sets map[string]*ValidationSetTracking) error { 394 // update validation set tracking state 395 for _, vs := range sets { 396 if vs.PinnedAt == 0 { 397 headers := map[string]string{ 398 "series": release.Series, 399 "account-id": vs.AccountID, 400 "name": vs.Name, 401 } 402 db := DB(s) 403 as, err := db.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat()) 404 if err != nil { 405 return fmt.Errorf("internal error: cannot find assertion %v when refreshing validation-set assertions", headers) 406 } 407 if vs.Current != as.Sequence() { 408 vs.Current = as.Sequence() 409 UpdateValidationSet(s, vs) 410 } 411 } 412 } 413 return nil 414 } 415 416 if err := bulkRefreshValidationSetAsserts(s, monitorModeSets, nil, userID, deviceCtx, opts); err != nil { 417 return err 418 } 419 if err := updateTracking(monitorModeSets); err != nil { 420 return err 421 } 422 423 checkForConflicts := func(db *asserts.Database, bs asserts.Backstore) error { 424 vsets := snapasserts.NewValidationSets() 425 tmpDb := db.WithStackedBackstore(bs) 426 for _, vs := range enforceModeSets { 427 headers := map[string]string{ 428 "series": release.Series, 429 "account-id": vs.AccountID, 430 "name": vs.Name, 431 } 432 var err error 433 var as asserts.Assertion 434 if vs.PinnedAt > 0 { 435 headers["sequence"] = fmt.Sprintf("%d", vs.PinnedAt) 436 as, err = tmpDb.Find(asserts.ValidationSetType, headers) 437 } else { 438 as, err = tmpDb.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat()) 439 } 440 if err != nil { 441 return fmt.Errorf("internal error: cannot find validation set assertion: %v", err) 442 } 443 444 vsass, ok := as.(*asserts.ValidationSet) 445 if !ok { 446 return fmt.Errorf("internal error: unexpected assertion type %s for %s", vsass.Type().Name, ValidationSetKey(vs.AccountID, vs.Name)) 447 } 448 if err := vsets.Add(vsass); err != nil { 449 return fmt.Errorf("internal error: cannot check validation sets conflicts: %v", err) 450 } 451 } 452 return vsets.Conflict() 453 } 454 455 if err := bulkRefreshValidationSetAsserts(s, enforceModeSets, checkForConflicts, userID, deviceCtx, opts); err != nil { 456 if _, ok := err.(*snapasserts.ValidationSetsConflictError); ok { 457 logger.Noticef("cannot refresh to conflicting validation set assertions: %v", err) 458 return nil 459 } 460 return err 461 } 462 if err := updateTracking(enforceModeSets); err != nil { 463 return err 464 } 465 466 return nil 467 } 468 469 // ResolveOptions carries extra options for ValidationSetAssertionForMonitor. 470 type ResolveOptions struct { 471 AllowLocalFallback bool 472 } 473 474 // ValidationSetAssertionForMonitor tries to fetch or refresh the validation 475 // set assertion with accountID/name/sequence (sequence is optional) using pool. 476 // If assertion cannot be fetched but exists locally and opts.AllowLocalFallback 477 // is set then the local one is returned 478 func ValidationSetAssertionForMonitor(st *state.State, accountID, name string, sequence int, pinned bool, userID int, opts *ResolveOptions) (as *asserts.ValidationSet, local bool, err error) { 479 if opts == nil { 480 opts = &ResolveOptions{} 481 } 482 deviceCtx, err := snapstate.DevicePastSeeding(st, nil) 483 if err != nil { 484 return nil, false, err 485 } 486 487 var vs asserts.Assertion 488 headers := map[string]string{ 489 "series": release.Series, 490 "account-id": accountID, 491 "name": name, 492 } 493 494 db := cachedDB(st) 495 496 // try to get existing one from db 497 if sequence > 0 { 498 headers["sequence"] = fmt.Sprintf("%d", sequence) 499 vs, err = db.Find(asserts.ValidationSetType, headers) 500 } else { 501 // find latest 502 vs, err = db.FindSequence(asserts.ValidationSetType, headers, -1, -1) 503 } 504 if err != nil && !asserts.IsNotFound(err) { 505 return nil, false, err 506 } 507 if err == nil { 508 as = vs.(*asserts.ValidationSet) 509 } 510 511 // try to resolve or update with pool 512 pool := asserts.NewPool(db, maxGroups) 513 atSeq := &asserts.AtSequence{ 514 Type: asserts.ValidationSetType, 515 SequenceKey: []string{release.Series, accountID, name}, 516 Sequence: sequence, 517 Pinned: pinned, 518 } 519 if as != nil { 520 atSeq.Revision = as.Revision() 521 } else { 522 atSeq.Revision = asserts.RevisionNotKnown 523 } 524 525 // resolve if not found locally, otherwise add for update 526 if as == nil { 527 if err := pool.AddUnresolvedSequence(atSeq, atSeq.Unique()); err != nil { 528 return nil, false, err 529 } 530 } else { 531 atSeq.Sequence = as.Sequence() 532 // found locally, try to update 533 atSeq.Revision = as.Revision() 534 if err := pool.AddSequenceToUpdate(atSeq, atSeq.Unique()); err != nil { 535 return nil, false, err 536 } 537 } 538 539 refreshOpts := &RefreshAssertionsOptions{IsAutoRefresh: false} 540 if err := resolvePoolNoFallback(st, pool, nil, userID, deviceCtx, refreshOpts); err != nil { 541 rerr, ok := err.(*resolvePoolError) 542 if ok && as != nil && opts.AllowLocalFallback { 543 if e := rerr.errors[atSeq.Unique()]; asserts.IsNotFound(e) { 544 // fallback: support the scenario of local assertion (snap ack) 545 // not available in the store. 546 return as, true, nil 547 } 548 } 549 return nil, false, err 550 } 551 552 // fetch the requested assertion again 553 if pinned { 554 vs, err = db.Find(asserts.ValidationSetType, headers) 555 } else { 556 vs, err = db.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat()) 557 } 558 if err == nil { 559 as = vs.(*asserts.ValidationSet) 560 } 561 return as, false, err 562 } 563 564 // ValidationSetAssertionForEnforce tries to fetch the validation set assertion 565 // with the given accountID/name/sequence (sequence is optional) using pool and 566 // checks if it's not in conflict with existing validation sets in enforcing mode 567 // (all currently tracked validation set assertions get refreshed), and if they 568 // are valid for installed snaps. 569 func ValidationSetAssertionForEnforce(st *state.State, accountID, name string, sequence int, userID int, snaps []*snapasserts.InstalledSnap) (vs *asserts.ValidationSet, err error) { 570 deviceCtx, err := snapstate.DevicePastSeeding(st, nil) 571 if err != nil { 572 return nil, err 573 } 574 575 opts := &RefreshAssertionsOptions{IsAutoRefresh: false} 576 577 // refresh all currently tracked validation set assertions (this may or may not 578 // include the one requested by the caller). 579 if err = RefreshValidationSetAssertions(st, userID, opts); err != nil { 580 return nil, err 581 } 582 583 valsets, err := EnforcedValidationSets(st) 584 if err != nil { 585 return nil, err 586 } 587 588 getSpecificSequenceOrLatest := func(db *asserts.Database, headers map[string]string) (vs *asserts.ValidationSet, err error) { 589 var a asserts.Assertion 590 if _, ok := headers["sequence"]; ok { 591 a, err = db.Find(asserts.ValidationSetType, headers) 592 } else { 593 a, err = db.FindSequence(asserts.ValidationSetType, headers, -1, -1) 594 } 595 if err != nil { 596 return nil, err 597 } 598 vs = a.(*asserts.ValidationSet) 599 return vs, nil 600 } 601 602 // try to get existing from the db. It will be the latest one if it was 603 // tracked already and thus refreshed via RefreshValidationSetAssertions. 604 // Otherwise, it may be a local assertion that was tracked in the past and 605 // then forgotten, in which case we need to refresh it explicitly. 606 db := cachedDB(st) 607 headers := map[string]string{ 608 "series": release.Series, 609 "account-id": accountID, 610 "name": name, 611 } 612 if sequence > 0 { 613 headers["sequence"] = fmt.Sprintf("%d", sequence) 614 } 615 616 pool := asserts.NewPool(db, maxGroups) 617 atSeq := &asserts.AtSequence{ 618 Type: asserts.ValidationSetType, 619 SequenceKey: []string{release.Series, accountID, name}, 620 Sequence: sequence, 621 Revision: asserts.RevisionNotKnown, 622 Pinned: sequence > 0, 623 } 624 625 vs, err = getSpecificSequenceOrLatest(db, headers) 626 627 checkForConflicts := func() error { 628 if err := valsets.Add(vs); err != nil { 629 return fmt.Errorf("internal error: cannot check validation sets conflicts: %v", err) 630 } 631 if err := valsets.Conflict(); err != nil { 632 return err 633 } 634 if err := valsets.CheckInstalledSnaps(snaps); err != nil { 635 return err 636 } 637 return nil 638 } 639 640 // found locally 641 if err == nil { 642 // check if we were tracking it already; if not, that 643 // means we found an old assertion (it was very likely tracked in the 644 // past) and we need to update it as it wasn't covered 645 // by RefreshValidationSetAssertions. 646 var tr ValidationSetTracking 647 trerr := GetValidationSet(st, accountID, name, &tr) 648 if trerr != nil && trerr != state.ErrNoState { 649 return nil, trerr 650 } 651 // not tracked, update the assertion 652 if trerr == state.ErrNoState { 653 // update with pool 654 atSeq.Sequence = vs.Sequence() 655 atSeq.Revision = vs.Revision() 656 if err := pool.AddSequenceToUpdate(atSeq, atSeq.Unique()); err != nil { 657 return nil, err 658 } 659 } else { 660 // was already tracked, add to validation sets and check 661 if err := checkForConflicts(); err != nil { 662 return nil, err 663 } 664 return vs, nil 665 } 666 } else { 667 if !asserts.IsNotFound(err) { 668 return nil, err 669 } 670 671 // try to resolve with pool 672 if err := pool.AddUnresolvedSequence(atSeq, atSeq.Unique()); err != nil { 673 return nil, err 674 } 675 } 676 677 checkBeforeCommit := func(db *asserts.Database, bs asserts.Backstore) error { 678 tmpDb := db.WithStackedBackstore(bs) 679 // get the resolved validation set assert, add to validation sets and check 680 vs, err = getSpecificSequenceOrLatest(tmpDb, headers) 681 if err != nil { 682 return fmt.Errorf("internal error: cannot find validation set assertion: %v", err) 683 } 684 if err := checkForConflicts(); err != nil { 685 return err 686 } 687 // all fine, will be committed (along with its prerequisites if any) on 688 // return by resolvePoolNoFallback 689 return nil 690 } 691 692 if err := resolvePoolNoFallback(st, pool, checkBeforeCommit, userID, deviceCtx, opts); err != nil { 693 return nil, err 694 } 695 696 return vs, err 697 } 698 699 // TemporaryDB returns a temporary database stacked on top of the assertions 700 // database. Writing to it will not affect the assertions database. 701 func TemporaryDB(st *state.State) *asserts.Database { 702 db := cachedDB(st) 703 return db.WithStackedBackstore(asserts.NewMemoryBackstore()) 704 }