gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/assertstate/validation_set_tracking_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 assertstate_test
    21  
    22  import (
    23  	. "gopkg.in/check.v1"
    24  
    25  	"github.com/snapcore/snapd/asserts"
    26  	"github.com/snapcore/snapd/asserts/assertstest"
    27  	"github.com/snapcore/snapd/overlord/assertstate"
    28  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    29  	"github.com/snapcore/snapd/overlord/state"
    30  )
    31  
    32  type validationSetTrackingSuite struct {
    33  	st *state.State
    34  	//storeSigning *assertstest.StoreStack
    35  	dev1Signing *assertstest.SigningDB
    36  	dev1acct    *asserts.Account
    37  }
    38  
    39  var _ = Suite(&validationSetTrackingSuite{})
    40  
    41  func (s *validationSetTrackingSuite) SetUpTest(c *C) {
    42  	s.st = state.New(nil)
    43  
    44  	s.st.Lock()
    45  	defer s.st.Unlock()
    46  	storeSigning := assertstest.NewStoreStack("can0nical", nil)
    47  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
    48  		Backstore: asserts.NewMemoryBackstore(),
    49  		Trusted:   storeSigning.Trusted,
    50  	})
    51  	c.Assert(err, IsNil)
    52  	c.Assert(db.Add(storeSigning.StoreAccountKey("")), IsNil)
    53  	assertstate.ReplaceDB(s.st, db)
    54  
    55  	s.dev1acct = assertstest.NewAccount(storeSigning, "developer1", nil, "")
    56  	c.Assert(storeSigning.Add(s.dev1acct), IsNil)
    57  
    58  	dev1PrivKey, _ = assertstest.GenerateKey(752)
    59  	acct1Key := assertstest.NewAccountKey(storeSigning, s.dev1acct, nil, dev1PrivKey.PublicKey(), "")
    60  
    61  	assertstatetest.AddMany(s.st, storeSigning.StoreAccountKey(""), s.dev1acct, acct1Key)
    62  
    63  	s.dev1Signing = assertstest.NewSigningDB(s.dev1acct.AccountID(), dev1PrivKey)
    64  	c.Check(s.dev1Signing, NotNil)
    65  	c.Assert(storeSigning.Add(acct1Key), IsNil)
    66  }
    67  
    68  func (s *validationSetTrackingSuite) TestUpdate(c *C) {
    69  	s.st.Lock()
    70  	defer s.st.Unlock()
    71  
    72  	all, err := assertstate.ValidationSets(s.st)
    73  	c.Assert(err, IsNil)
    74  	c.Assert(all, HasLen, 0)
    75  
    76  	tr := assertstate.ValidationSetTracking{
    77  		AccountID: "foo",
    78  		Name:      "bar",
    79  		Mode:      assertstate.Enforce,
    80  		PinnedAt:  1,
    81  		Current:   2,
    82  	}
    83  	assertstate.UpdateValidationSet(s.st, &tr)
    84  
    85  	all, err = assertstate.ValidationSets(s.st)
    86  	c.Assert(err, IsNil)
    87  	c.Assert(all, HasLen, 1)
    88  	for k, v := range all {
    89  		c.Check(k, Equals, "foo/bar")
    90  		c.Check(v, DeepEquals, &assertstate.ValidationSetTracking{AccountID: "foo", Name: "bar", Mode: assertstate.Enforce, PinnedAt: 1, Current: 2})
    91  	}
    92  
    93  	tr = assertstate.ValidationSetTracking{
    94  		AccountID: "foo",
    95  		Name:      "bar",
    96  		Mode:      assertstate.Monitor,
    97  		PinnedAt:  2,
    98  		Current:   3,
    99  	}
   100  	assertstate.UpdateValidationSet(s.st, &tr)
   101  
   102  	all, err = assertstate.ValidationSets(s.st)
   103  	c.Assert(err, IsNil)
   104  	c.Assert(all, HasLen, 1)
   105  	for k, v := range all {
   106  		c.Check(k, Equals, "foo/bar")
   107  		c.Check(v, DeepEquals, &assertstate.ValidationSetTracking{AccountID: "foo", Name: "bar", Mode: assertstate.Monitor, PinnedAt: 2, Current: 3})
   108  	}
   109  
   110  	tr = assertstate.ValidationSetTracking{
   111  		AccountID: "foo",
   112  		Name:      "baz",
   113  		Mode:      assertstate.Enforce,
   114  		Current:   3,
   115  	}
   116  	assertstate.UpdateValidationSet(s.st, &tr)
   117  
   118  	all, err = assertstate.ValidationSets(s.st)
   119  	c.Assert(err, IsNil)
   120  	c.Assert(all, HasLen, 2)
   121  
   122  	var gotFirst, gotSecond bool
   123  	for k, v := range all {
   124  		if k == "foo/bar" {
   125  			gotFirst = true
   126  			c.Check(v, DeepEquals, &assertstate.ValidationSetTracking{AccountID: "foo", Name: "bar", Mode: assertstate.Monitor, PinnedAt: 2, Current: 3})
   127  		} else {
   128  			gotSecond = true
   129  			c.Check(k, Equals, "foo/baz")
   130  			c.Check(v, DeepEquals, &assertstate.ValidationSetTracking{AccountID: "foo", Name: "baz", Mode: assertstate.Enforce, PinnedAt: 0, Current: 3})
   131  		}
   132  	}
   133  	c.Check(gotFirst, Equals, true)
   134  	c.Check(gotSecond, Equals, true)
   135  }
   136  
   137  func (s *validationSetTrackingSuite) TestDelete(c *C) {
   138  	s.st.Lock()
   139  	defer s.st.Unlock()
   140  
   141  	// delete non-existing one is fine
   142  	assertstate.DeleteValidationSet(s.st, "foo", "bar")
   143  	all, err := assertstate.ValidationSets(s.st)
   144  	c.Assert(err, IsNil)
   145  	c.Assert(all, HasLen, 0)
   146  
   147  	tr := assertstate.ValidationSetTracking{
   148  		AccountID: "foo",
   149  		Name:      "bar",
   150  		Mode:      assertstate.Monitor,
   151  	}
   152  	assertstate.UpdateValidationSet(s.st, &tr)
   153  
   154  	all, err = assertstate.ValidationSets(s.st)
   155  	c.Assert(err, IsNil)
   156  	c.Assert(all, HasLen, 1)
   157  
   158  	// deletes existing one
   159  	assertstate.DeleteValidationSet(s.st, "foo", "bar")
   160  	all, err = assertstate.ValidationSets(s.st)
   161  	c.Assert(err, IsNil)
   162  	c.Assert(all, HasLen, 0)
   163  }
   164  
   165  func (s *validationSetTrackingSuite) TestGet(c *C) {
   166  	s.st.Lock()
   167  	defer s.st.Unlock()
   168  
   169  	err := assertstate.GetValidationSet(s.st, "foo", "bar", nil)
   170  	c.Assert(err, ErrorMatches, `internal error: tr is nil`)
   171  
   172  	tr := assertstate.ValidationSetTracking{
   173  		AccountID: "foo",
   174  		Name:      "bar",
   175  		Mode:      assertstate.Enforce,
   176  		Current:   3,
   177  	}
   178  	assertstate.UpdateValidationSet(s.st, &tr)
   179  
   180  	var res assertstate.ValidationSetTracking
   181  	err = assertstate.GetValidationSet(s.st, "foo", "bar", &res)
   182  	c.Assert(err, IsNil)
   183  	c.Check(res, DeepEquals, tr)
   184  
   185  	// non-existing
   186  	err = assertstate.GetValidationSet(s.st, "foo", "baz", &res)
   187  	c.Assert(err, Equals, state.ErrNoState)
   188  }
   189  
   190  func (s *validationSetTrackingSuite) mockAssert(c *C, name, sequence, presence string) asserts.Assertion {
   191  	snaps := []interface{}{map[string]interface{}{
   192  		"id":       "yOqKhntON3vR7kwEbVPsILm7bUViPDzz",
   193  		"name":     "snap-b",
   194  		"presence": presence,
   195  	}}
   196  	headers := map[string]interface{}{
   197  		"authority-id": s.dev1acct.AccountID(),
   198  		"account-id":   s.dev1acct.AccountID(),
   199  		"name":         name,
   200  		"series":       "16",
   201  		"sequence":     sequence,
   202  		"revision":     "5",
   203  		"timestamp":    "2030-11-06T09:16:26Z",
   204  		"snaps":        snaps,
   205  	}
   206  	as, err := s.dev1Signing.Sign(asserts.ValidationSetType, headers, nil, "")
   207  	c.Assert(err, IsNil)
   208  	return as
   209  }
   210  
   211  func (s *validationSetTrackingSuite) TestEnforcedValidationSets(c *C) {
   212  	s.st.Lock()
   213  	defer s.st.Unlock()
   214  
   215  	tr := assertstate.ValidationSetTracking{
   216  		AccountID: s.dev1acct.AccountID(),
   217  		Name:      "foo",
   218  		Mode:      assertstate.Enforce,
   219  		Current:   2,
   220  	}
   221  	assertstate.UpdateValidationSet(s.st, &tr)
   222  
   223  	tr = assertstate.ValidationSetTracking{
   224  		AccountID: s.dev1acct.AccountID(),
   225  		Name:      "bar",
   226  		Mode:      assertstate.Enforce,
   227  		PinnedAt:  1,
   228  		Current:   3,
   229  	}
   230  	assertstate.UpdateValidationSet(s.st, &tr)
   231  
   232  	tr = assertstate.ValidationSetTracking{
   233  		AccountID: s.dev1acct.AccountID(),
   234  		Name:      "baz",
   235  		Mode:      assertstate.Monitor,
   236  		Current:   5,
   237  	}
   238  	assertstate.UpdateValidationSet(s.st, &tr)
   239  
   240  	vs1 := s.mockAssert(c, "foo", "2", "required")
   241  	c.Assert(assertstate.Add(s.st, vs1), IsNil)
   242  
   243  	vs2 := s.mockAssert(c, "bar", "1", "invalid")
   244  	c.Assert(assertstate.Add(s.st, vs2), IsNil)
   245  
   246  	vs3 := s.mockAssert(c, "baz", "5", "invalid")
   247  	c.Assert(assertstate.Add(s.st, vs3), IsNil)
   248  
   249  	valsets, err := assertstate.EnforcedValidationSets(s.st)
   250  	c.Assert(err, IsNil)
   251  
   252  	// foo and bar are in conflict, use this as an indirect way of checking
   253  	// that both were added to valsets.
   254  	// XXX: switch to CheckPresenceInvalid / CheckPresenceRequired once available.
   255  	err = valsets.Conflict()
   256  	c.Check(err, ErrorMatches, `validation sets are in conflict:\n- cannot constrain snap "snap-b" as both invalid \(.*/bar\) and required at any revision \(.*/foo\)`)
   257  }