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  }