github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/asserts/pool.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 asserts
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  
    26  	"github.com/snapcore/snapd/asserts/internal"
    27  )
    28  
    29  // A Grouping identifies opaquely a grouping of assertions.
    30  // Pool uses it to label the interesection between a set of groups.
    31  type Grouping string
    32  
    33  // A pool helps holding and tracking a set of assertions and their
    34  // prerequisites as they need to be updated or resolved.  The
    35  // assertions can be organized in groups. Failure can be tracked
    36  // isolated to groups, conversely any error related to a single group
    37  // alone will stop any work to resolve it. Independent assertions
    38  // should not be grouped. Assertions and prerequisites that are part
    39  // of more than one group are tracked properly only once.
    40  //
    41  // Typical usage involves specifying the initial assertions needing to
    42  // be resolved or updated using AddUnresolved and AddToUpdate. At this
    43  // point ToResolve can be called to get them organized in groupings
    44  // ready for fetching. Fetched assertions can then be provided with
    45  // Add or AddBatch. Because these can have prerequisites calling
    46  // ToResolve and fetching needs to be repeated until ToResolve's
    47  // result is empty.  Between any two ToResolve invocations but after
    48  // any Add or AddBatch AddUnresolved/AddToUpdate can also be used
    49  // again.
    50  //
    51  //                      V
    52  //                      |
    53  //        /-> AddUnresolved, AddToUpdate
    54  //        |             |
    55  //        |             V
    56  //        |------> ToResolve -> empty? done
    57  //        |             |
    58  //        |             V
    59  //        \ __________ Add
    60  //
    61  //
    62  // If errors prevent from fulfilling assertions from a ToResolve,
    63  // AddError and AddGroupingError can be used to report the errors so
    64  // that they can be associated with groups.
    65  //
    66  // All the resolved assertions in a Pool from groups not in error can
    67  // be committed to a destination database with CommitTo.
    68  type Pool struct {
    69  	groundDB RODatabase
    70  
    71  	numbering map[string]uint16
    72  	groupings *internal.Groupings
    73  
    74  	unresolved    map[string]*unresolvedRec
    75  	prerequisites map[string]*unresolvedRec
    76  
    77  	bs        Backstore
    78  	unchanged map[string]bool
    79  
    80  	groups map[uint16]*groupRec
    81  
    82  	curPhase poolPhase
    83  }
    84  
    85  // NewPool creates a new Pool, groundDB is used to resolve trusted and
    86  // predefined assertions and to provide the current revision for
    87  // assertions to update and their prerequisites. Up to n groups can be
    88  // used to organize the assertions.
    89  func NewPool(groundDB RODatabase, n int) *Pool {
    90  	groupings, err := internal.NewGroupings(n)
    91  	if err != nil {
    92  		panic(fmt.Sprintf("NewPool: %v", err))
    93  	}
    94  	return &Pool{
    95  		groundDB:      groundDB,
    96  		numbering:     make(map[string]uint16),
    97  		groupings:     groupings,
    98  		unresolved:    make(map[string]*unresolvedRec),
    99  		prerequisites: make(map[string]*unresolvedRec),
   100  		bs:            NewMemoryBackstore(),
   101  		unchanged:     make(map[string]bool),
   102  		groups:        make(map[uint16]*groupRec),
   103  	}
   104  }
   105  
   106  func (p *Pool) groupNum(group string) (gnum uint16, err error) {
   107  	if gnum, ok := p.numbering[group]; ok {
   108  		return gnum, nil
   109  	}
   110  	gnum = uint16(len(p.numbering))
   111  	if err = p.groupings.WithinRange(gnum); err != nil {
   112  		return 0, err
   113  	}
   114  	p.numbering[group] = gnum
   115  	return gnum, nil
   116  }
   117  
   118  func (p *Pool) ensureGroup(group string) (gnum uint16, err error) {
   119  	gnum, err = p.groupNum(group)
   120  	if err != nil {
   121  		return 0, err
   122  	}
   123  	if gRec := p.groups[gnum]; gRec == nil {
   124  		p.groups[gnum] = &groupRec{
   125  			name: group,
   126  		}
   127  	}
   128  	return gnum, nil
   129  }
   130  
   131  // Singleton returns a grouping containing only the given group.
   132  // It is useful mainly for tests and to drive Add are AddBatch when the
   133  // server is pushing assertions (instead of the usual pull scenario).
   134  func (p *Pool) Singleton(group string) (Grouping, error) {
   135  	gnum, err := p.ensureGroup(group)
   136  	if err != nil {
   137  		return Grouping(""), nil
   138  	}
   139  
   140  	var grouping internal.Grouping
   141  	p.groupings.AddTo(&grouping, gnum)
   142  	return Grouping(p.groupings.Serialize(&grouping)), nil
   143  }
   144  
   145  // An unresolvedRec tracks a single unresolved assertion until it is
   146  // resolved or there is an error doing so. The field 'grouping' will
   147  // grow to contain all the groups requiring this assertion while it
   148  // is unresolved.
   149  type unresolvedRec struct {
   150  	at       *AtRevision
   151  	grouping internal.Grouping
   152  
   153  	serializedLabel Grouping
   154  
   155  	err error
   156  }
   157  
   158  func (u *unresolvedRec) exportTo(r map[Grouping][]*AtRevision, gr *internal.Groupings) {
   159  	serLabel := Grouping(gr.Serialize(&u.grouping))
   160  	// remember serialized label
   161  	u.serializedLabel = serLabel
   162  	r[serLabel] = append(r[serLabel], u.at)
   163  }
   164  
   165  func (u *unresolvedRec) merge(at *AtRevision, gnum uint16, gr *internal.Groupings) {
   166  	gr.AddTo(&u.grouping, gnum)
   167  	// assume we want to resolve/update wrt the highest revision
   168  	if at.Revision > u.at.Revision {
   169  		u.at.Revision = at.Revision
   170  	}
   171  }
   172  
   173  // A groupRec keeps track of all the resolved assertions in a group
   174  // or whether the group should be considered in error (err != nil).
   175  type groupRec struct {
   176  	name     string
   177  	err      error
   178  	resolved []Ref
   179  }
   180  
   181  func (gRec *groupRec) hasErr() bool {
   182  	return gRec.err != nil
   183  }
   184  
   185  func (gRec *groupRec) setErr(e error) {
   186  	if gRec.err == nil {
   187  		gRec.err = e
   188  	}
   189  }
   190  
   191  func (gRec *groupRec) markResolved(ref *Ref) (marked bool) {
   192  	if gRec.hasErr() {
   193  		return false
   194  	}
   195  	gRec.resolved = append(gRec.resolved, *ref)
   196  	return true
   197  }
   198  
   199  // markResolved marks the assertion referenced by ref as resolved
   200  // in all the groups in grouping, except those already in error.
   201  func (p *Pool) markResolved(grouping *internal.Grouping, resolved *Ref) (marked bool) {
   202  	p.groupings.Iter(grouping, func(gnum uint16) error {
   203  		if p.groups[gnum].markResolved(resolved) {
   204  			marked = true
   205  		}
   206  		return nil
   207  	})
   208  	return marked
   209  }
   210  
   211  // setErr marks all the groups in grouping as in error with error err
   212  // except those already in error.
   213  func (p *Pool) setErr(grouping *internal.Grouping, err error) {
   214  	p.groupings.Iter(grouping, func(gnum uint16) error {
   215  		p.groups[gnum].setErr(err)
   216  		return nil
   217  	})
   218  }
   219  
   220  func (p *Pool) isPredefined(ref *Ref) (bool, error) {
   221  	_, err := ref.Resolve(p.groundDB.FindPredefined)
   222  	if err == nil {
   223  		return true, nil
   224  	}
   225  	if !IsNotFound(err) {
   226  		return false, err
   227  	}
   228  	return false, nil
   229  }
   230  
   231  func (p *Pool) isResolved(ref *Ref) (bool, error) {
   232  	if p.unchanged[ref.Unique()] {
   233  		return true, nil
   234  	}
   235  	_, err := p.bs.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat())
   236  	if err == nil {
   237  		return true, nil
   238  	}
   239  	if !IsNotFound(err) {
   240  		return false, err
   241  	}
   242  	return false, nil
   243  }
   244  
   245  func (p *Pool) curRevision(ref *Ref) (int, error) {
   246  	a, err := ref.Resolve(p.groundDB.Find)
   247  	if err != nil && !IsNotFound(err) {
   248  		return 0, err
   249  	}
   250  	if err == nil {
   251  		return a.Revision(), nil
   252  	}
   253  	return RevisionNotKnown, nil
   254  }
   255  
   256  type poolPhase int
   257  
   258  const (
   259  	poolPhaseAddUnresolved = iota
   260  	poolPhaseAdd
   261  )
   262  
   263  func (p *Pool) phase(ph poolPhase) error {
   264  	if ph == p.curPhase {
   265  		return nil
   266  	}
   267  	if ph == poolPhaseAdd {
   268  		return fmt.Errorf("internal error: cannot switch to Pool add phase without invoking ToResolve first")
   269  	}
   270  	// ph == poolPhaseAddUnresolved
   271  	p.unresolvedBookkeeping()
   272  	p.curPhase = poolPhaseAddUnresolved
   273  	return nil
   274  }
   275  
   276  // AddUnresolved adds the assertion referenced by unresolved
   277  // AtRevision to the Pool as unresolved and as required by the given group.
   278  // Usually unresolved.Revision will have been set to RevisionNotKnown.
   279  func (p *Pool) AddUnresolved(unresolved *AtRevision, group string) error {
   280  	if err := p.phase(poolPhaseAddUnresolved); err != nil {
   281  		return err
   282  	}
   283  	gnum, err := p.ensureGroup(group)
   284  	if err != nil {
   285  		return err
   286  	}
   287  	u := *unresolved
   288  	ok, err := p.isPredefined(&u.Ref)
   289  	if err != nil {
   290  		return err
   291  	}
   292  	if ok {
   293  		// predefined, nothing to do
   294  		return nil
   295  	}
   296  	return p.addUnresolved(&u, gnum)
   297  }
   298  
   299  func (p *Pool) addUnresolved(unresolved *AtRevision, gnum uint16) error {
   300  	ok, err := p.isResolved(&unresolved.Ref)
   301  	if err != nil {
   302  		return err
   303  	}
   304  	if ok {
   305  		// We assume that either the resolving of
   306  		// prerequisites for the already resolved assertion in
   307  		// progress has succeeded or will. If that's not the
   308  		// case we will fail at CommitTo time. We could
   309  		// instead recurse into its prerequisites again but the
   310  		// complexity isn't clearly worth it.
   311  		// See TestParallelPartialResolutionFailure
   312  		// Mark this as resolved in the group.
   313  		p.groups[gnum].markResolved(&unresolved.Ref)
   314  		return nil
   315  	}
   316  	uniq := unresolved.Ref.Unique()
   317  	var u *unresolvedRec
   318  	if u = p.unresolved[uniq]; u == nil {
   319  		u = &unresolvedRec{
   320  			at: unresolved,
   321  		}
   322  		p.unresolved[uniq] = u
   323  	}
   324  	u.merge(unresolved, gnum, p.groupings)
   325  	return nil
   326  }
   327  
   328  // ToResolve returns all the currently unresolved assertions in the
   329  // Pool, organized in opaque groupings based on which set of groups
   330  // requires each of them.
   331  // At the next ToResolve any unresolved assertion with not known
   332  // revision that was not added via Add or AddBatch will result in all
   333  // groups requiring it being in error with ErrUnresolved.
   334  // Conversely, the remaining unresolved assertions originally added
   335  // via AddToUpdate will be assumed to still be at their current
   336  // revisions.
   337  func (p *Pool) ToResolve() (map[Grouping][]*AtRevision, error) {
   338  	if p.curPhase == poolPhaseAdd {
   339  		p.unresolvedBookkeeping()
   340  	} else {
   341  		p.curPhase = poolPhaseAdd
   342  	}
   343  	r := make(map[Grouping][]*AtRevision)
   344  	for _, u := range p.unresolved {
   345  		if u.at.Revision == RevisionNotKnown {
   346  			rev, err := p.curRevision(&u.at.Ref)
   347  			if err != nil {
   348  				return nil, err
   349  			}
   350  			if rev != RevisionNotKnown {
   351  				u.at.Revision = rev
   352  			}
   353  		}
   354  		u.exportTo(r, p.groupings)
   355  	}
   356  	return r, nil
   357  }
   358  
   359  func (p *Pool) addPrerequisite(pref *Ref, g *internal.Grouping) error {
   360  	uniq := pref.Unique()
   361  	u := p.unresolved[uniq]
   362  	at := &AtRevision{
   363  		Ref:      *pref,
   364  		Revision: RevisionNotKnown,
   365  	}
   366  	if u == nil {
   367  		u = p.prerequisites[uniq]
   368  	}
   369  	if u != nil {
   370  		gr := p.groupings
   371  		gr.Iter(g, func(gnum uint16) error {
   372  			u.merge(at, gnum, gr)
   373  			return nil
   374  		})
   375  		return nil
   376  	}
   377  	ok, err := p.isPredefined(pref)
   378  	if err != nil {
   379  		return err
   380  	}
   381  	if ok {
   382  		// nothing to do
   383  		return nil
   384  	}
   385  	ok, err = p.isResolved(pref)
   386  	if err != nil {
   387  		return err
   388  	}
   389  	if ok {
   390  		// nothing to do, it is anyway implied
   391  		return nil
   392  	}
   393  	p.prerequisites[uniq] = &unresolvedRec{
   394  		at:       at,
   395  		grouping: g.Copy(),
   396  	}
   397  	return nil
   398  }
   399  
   400  func (p *Pool) add(a Assertion, g *internal.Grouping) error {
   401  	if err := p.bs.Put(a.Type(), a); err != nil {
   402  		if revErr, ok := err.(*RevisionError); ok {
   403  			if revErr.Current >= a.Revision() {
   404  				// we already got something more recent
   405  				return nil
   406  			}
   407  		}
   408  
   409  		return err
   410  	}
   411  	for _, pref := range a.Prerequisites() {
   412  		if err := p.addPrerequisite(pref, g); err != nil {
   413  			return err
   414  		}
   415  	}
   416  	keyRef := &Ref{
   417  		Type:       AccountKeyType,
   418  		PrimaryKey: []string{a.SignKeyID()},
   419  	}
   420  	if err := p.addPrerequisite(keyRef, g); err != nil {
   421  		return err
   422  	}
   423  	return nil
   424  }
   425  
   426  func (p *Pool) resolveWith(unresolved map[string]*unresolvedRec, uniq string, u *unresolvedRec, a Assertion, extrag *internal.Grouping) (ok bool, err error) {
   427  	if a.Revision() > u.at.Revision {
   428  		if extrag == nil {
   429  			extrag = &u.grouping
   430  		} else {
   431  			p.groupings.Iter(&u.grouping, func(gnum uint16) error {
   432  				p.groupings.AddTo(extrag, gnum)
   433  				return nil
   434  			})
   435  		}
   436  		ref := a.Ref()
   437  		if p.markResolved(extrag, ref) {
   438  			// remove from tracking -
   439  			// remove u from unresolved only if the assertion
   440  			// is added to the resolved backstore;
   441  			// otherwise it might resurface as unresolved;
   442  			// it will be ultimately handled in
   443  			// unresolvedBookkeeping if it stays around
   444  			delete(unresolved, uniq)
   445  			if err := p.add(a, extrag); err != nil {
   446  				p.setErr(extrag, err)
   447  				return false, err
   448  			}
   449  		}
   450  	}
   451  	return true, nil
   452  }
   453  
   454  // Add adds the given assertion associated with the given grouping to the
   455  // Pool as resolved in all the groups requiring it.
   456  // Any not already resolved prerequisites of the assertion will
   457  // be implicitly added as unresolved and required by all of those groups.
   458  // The grouping will usually have been associated with the assertion
   459  // in a ToResolve's result. Otherwise the union of all groups
   460  // requiring the assertion plus the groups in grouping will be considered.
   461  // The latter is mostly relevant in scenarios where the server is pushing
   462  // assertions.
   463  // If an error is returned it refers to an immediate or local error.
   464  // Errors related to the assertions are associated with the relevant groups
   465  // and can be retrieved with Err, in which case ok is set to false.
   466  func (p *Pool) Add(a Assertion, grouping Grouping) (ok bool, err error) {
   467  	if err := p.phase(poolPhaseAdd); err != nil {
   468  		return false, err
   469  	}
   470  
   471  	if !a.SupportedFormat() {
   472  		e := &UnsupportedFormatError{Ref: a.Ref(), Format: a.Format()}
   473  		p.AddGroupingError(e, grouping)
   474  		return false, nil
   475  	}
   476  
   477  	return p.addToGrouping(a, grouping, p.groupings.Deserialize)
   478  }
   479  
   480  func (p *Pool) addToGrouping(a Assertion, grouping Grouping, deserializeGrouping func(string) (*internal.Grouping, error)) (ok bool, err error) {
   481  	uniq := a.Ref().Unique()
   482  	var u *unresolvedRec
   483  	var extrag *internal.Grouping
   484  	var unresolved map[string]*unresolvedRec
   485  	if u = p.unresolved[uniq]; u != nil {
   486  		unresolved = p.unresolved
   487  	} else if u = p.prerequisites[uniq]; u != nil {
   488  		unresolved = p.prerequisites
   489  	} else {
   490  		ok, err := p.isPredefined(a.Ref())
   491  		if err != nil {
   492  			return false, err
   493  		}
   494  		if ok {
   495  			// nothing to do
   496  			return true, nil
   497  		}
   498  		// a is not tracked as unresolved in any way so far,
   499  		// this is an atypical scenario where something gets
   500  		// pushed but we still want to add it to the resolved
   501  		// lists of the relevant groups; in case it is
   502  		// actually already resolved most of resolveWith below will
   503  		// be a nop
   504  		u = &unresolvedRec{
   505  			at: a.At(),
   506  		}
   507  		u.at.Revision = RevisionNotKnown
   508  	}
   509  
   510  	if u.serializedLabel != grouping {
   511  		var err error
   512  		extrag, err = deserializeGrouping(string(grouping))
   513  		if err != nil {
   514  			return false, err
   515  		}
   516  	}
   517  
   518  	return p.resolveWith(unresolved, uniq, u, a, extrag)
   519  }
   520  
   521  // AddBatch adds all the assertions in the Batch to the Pool,
   522  // associated with the given grouping and as resolved in all the
   523  // groups requiring them. It is equivalent to using Add on each of them.
   524  // If an error is returned it refers to an immediate or local error.
   525  // Errors related to the assertions are associated with the relevant groups
   526  // and can be retrieved with Err, in which case ok set to false.
   527  func (p *Pool) AddBatch(b *Batch, grouping Grouping) (ok bool, err error) {
   528  	if err := p.phase(poolPhaseAdd); err != nil {
   529  		return false, err
   530  	}
   531  
   532  	// b dealt with unsupported formats already
   533  
   534  	// deserialize grouping if needed only once
   535  	var cachedGrouping *internal.Grouping
   536  	deser := func(_ string) (*internal.Grouping, error) {
   537  		if cachedGrouping != nil {
   538  			// do a copy as addToGrouping and resolveWith
   539  			// might add to their input
   540  			g := cachedGrouping.Copy()
   541  			return &g, nil
   542  		}
   543  		var err error
   544  		cachedGrouping, err = p.groupings.Deserialize(string(grouping))
   545  		return cachedGrouping, err
   546  	}
   547  
   548  	inError := false
   549  	for _, a := range b.added {
   550  		ok, err := p.addToGrouping(a, grouping, deser)
   551  		if err != nil {
   552  			return false, err
   553  		}
   554  		if !ok {
   555  			inError = true
   556  		}
   557  	}
   558  
   559  	return !inError, nil
   560  }
   561  
   562  var (
   563  	ErrUnresolved       = errors.New("unresolved assertion")
   564  	ErrUnknownPoolGroup = errors.New("unknown pool group")
   565  )
   566  
   567  // unresolvedBookkeeping processes any left over unresolved assertions
   568  // since the last ToResolve invocation and intervening calls to Add/AddBatch,
   569  //  * they were either marked as in error which will be propagated
   570  //    to all groups requiring them
   571  //  * simply unresolved, which will be propagated to groups requiring them
   572  //    as ErrUnresolved
   573  //  * unchanged (update case)
   574  // unresolvedBookkeeping will also promote any recorded prerequisites
   575  // into actively unresolved, as long as not all the groups requiring them
   576  // are in error.
   577  func (p *Pool) unresolvedBookkeeping() {
   578  	// any left over unresolved are either:
   579  	//  * in error
   580  	//  * unchanged
   581  	//  * or unresolved
   582  	for uniq, u := range p.unresolved {
   583  		e := u.err
   584  		if e == nil {
   585  			if u.at.Revision == RevisionNotKnown {
   586  				e = ErrUnresolved
   587  			} else {
   588  				// unchanged
   589  				p.unchanged[uniq] = true
   590  			}
   591  		}
   592  		if e != nil {
   593  			p.setErr(&u.grouping, e)
   594  		}
   595  		delete(p.unresolved, uniq)
   596  	}
   597  
   598  	// prerequisites will become the new unresolved but drop them
   599  	// if all their groups are in error
   600  	for uniq, prereq := range p.prerequisites {
   601  		useful := false
   602  		p.groupings.Iter(&prereq.grouping, func(gnum uint16) error {
   603  			if !p.groups[gnum].hasErr() {
   604  				useful = true
   605  			}
   606  			return nil
   607  		})
   608  		if !useful {
   609  			delete(p.prerequisites, uniq)
   610  			continue
   611  		}
   612  	}
   613  
   614  	// prerequisites become the new unresolved, the emptied
   615  	// unresolved is used for prerequisites in the next round
   616  	p.unresolved, p.prerequisites = p.prerequisites, p.unresolved
   617  }
   618  
   619  // Err returns the error for group if group is in error, nil otherwise.
   620  func (p *Pool) Err(group string) error {
   621  	gnum, err := p.groupNum(group)
   622  	if err != nil {
   623  		return err
   624  	}
   625  	gRec := p.groups[gnum]
   626  	if gRec == nil {
   627  		return ErrUnknownPoolGroup
   628  	}
   629  	return gRec.err
   630  }
   631  
   632  // Errors returns a mapping of groups in error to their errors.
   633  func (p *Pool) Errors() map[string]error {
   634  	res := make(map[string]error)
   635  	for _, gRec := range p.groups {
   636  		if err := gRec.err; err != nil {
   637  			res[gRec.name] = err
   638  		}
   639  	}
   640  	if len(res) == 0 {
   641  		return nil
   642  	}
   643  	return res
   644  }
   645  
   646  // AddError associates error e with the unresolved assertion.
   647  // The error will be propagated to all the affected groups at
   648  // the next ToResolve.
   649  func (p *Pool) AddError(e error, ref *Ref) error {
   650  	if err := p.phase(poolPhaseAdd); err != nil {
   651  		return err
   652  	}
   653  	uniq := ref.Unique()
   654  	if u := p.unresolved[uniq]; u != nil && u.err == nil {
   655  		u.err = e
   656  	}
   657  	return nil
   658  }
   659  
   660  // AddGroupingError puts all the groups of grouping in error, with error e.
   661  func (p *Pool) AddGroupingError(e error, grouping Grouping) error {
   662  	if err := p.phase(poolPhaseAdd); err != nil {
   663  		return err
   664  	}
   665  
   666  	g, err := p.groupings.Deserialize(string(grouping))
   667  	if err != nil {
   668  		return err
   669  	}
   670  
   671  	p.setErr(g, e)
   672  	return nil
   673  }
   674  
   675  // AddToUpdate adds the assertion referenced by toUpdate and all its
   676  // prerequisites to the Pool as unresolved and as required by the
   677  // given group. It is assumed that the assertion is currently in the
   678  // ground database of the Pool, otherwise this will error.
   679  // The current revisions of the assertion and its prerequisites will
   680  // be recorded and only higher revisions will then resolve them,
   681  // otherwise if ultimately unresolved they will be assumed to still be
   682  // at their current ones.
   683  func (p *Pool) AddToUpdate(toUpdate *Ref, group string) error {
   684  	if err := p.phase(poolPhaseAddUnresolved); err != nil {
   685  		return err
   686  	}
   687  	gnum, err := p.ensureGroup(group)
   688  	if err != nil {
   689  		return err
   690  	}
   691  	retrieve := func(ref *Ref) (Assertion, error) {
   692  		return ref.Resolve(p.groundDB.Find)
   693  	}
   694  	add := func(a Assertion) error {
   695  		return p.addUnresolved(a.At(), gnum)
   696  	}
   697  	f := NewFetcher(p.groundDB, retrieve, add)
   698  	if err := f.Fetch(toUpdate); err != nil {
   699  		return err
   700  	}
   701  	return nil
   702  }
   703  
   704  // CommitTo adds the assertions from groups without errors to the
   705  // given assertion database. Commit errors can be retrieved via Err
   706  // per group. An error is returned directly only if CommitTo is called
   707  // with possible pending unresolved assertions.
   708  func (p *Pool) CommitTo(db *Database) error {
   709  	if p.curPhase == poolPhaseAddUnresolved {
   710  		return fmt.Errorf("internal error: cannot commit Pool during add unresolved phase")
   711  	}
   712  	p.unresolvedBookkeeping()
   713  
   714  	retrieve := func(ref *Ref) (Assertion, error) {
   715  		a, err := p.bs.Get(ref.Type, ref.PrimaryKey, ref.Type.MaxSupportedFormat())
   716  		if IsNotFound(err) {
   717  			// fallback to pre-existing assertions
   718  			a, err = ref.Resolve(db.Find)
   719  		}
   720  		if err != nil {
   721  			return nil, resolveError("cannot resolve prerequisite assertion: %s", ref, err)
   722  		}
   723  		return a, nil
   724  	}
   725  	save := func(a Assertion) error {
   726  		err := db.Add(a)
   727  		if IsUnaccceptedUpdate(err) {
   728  			// unsupported format case is handled before.
   729  			// be idempotent, db has already the same or
   730  			// newer.
   731  			return nil
   732  		}
   733  		return err
   734  	}
   735  
   736  NextGroup:
   737  	for _, gRec := range p.groups {
   738  		if gRec.hasErr() {
   739  			// already in error, ignore
   740  			continue
   741  		}
   742  		// TODO: try to reuse fetcher
   743  		f := NewFetcher(db, retrieve, save)
   744  		for i := range gRec.resolved {
   745  			if err := f.Fetch(&gRec.resolved[i]); err != nil {
   746  				gRec.setErr(err)
   747  				continue NextGroup
   748  			}
   749  		}
   750  	}
   751  
   752  	return nil
   753  }
   754  
   755  // ClearGroups clears the pool in terms of information associated with groups
   756  // while preserving information about already resolved or unchanged assertions.
   757  // It is useful for reusing a pool once the maximum of usable groups
   758  // that was set with NewPool has been exhausted. Group errors must be
   759  // queried before calling it otherwise they are lost. It is an error
   760  // to call it when there are still pending unresolved assertions in
   761  // the pool.
   762  func (p *Pool) ClearGroups() error {
   763  	if len(p.unresolved) != 0 || len(p.prerequisites) != 0 {
   764  		return fmt.Errorf("internal error: trying to clear groups of asserts.Pool with pending unresolved or prerequisites")
   765  	}
   766  
   767  	p.numbering = make(map[string]uint16)
   768  	// use a fresh Groupings as well so that max group tracking starts
   769  	// from scratch.
   770  	// NewGroupings cannot fail on a value accepted by it previously
   771  	p.groupings, _ = internal.NewGroupings(p.groupings.N())
   772  	p.groups = make(map[uint16]*groupRec)
   773  	p.curPhase = poolPhaseAdd
   774  	return nil
   775  }