github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/overlord/configstate/handler_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 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 configstate_test
    21  
    22  import (
    23  	"io/ioutil"
    24  	"path/filepath"
    25  	"testing"
    26  	"time"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/asserts"
    31  	"github.com/snapcore/snapd/asserts/assertstest"
    32  	"github.com/snapcore/snapd/dirs"
    33  	"github.com/snapcore/snapd/overlord"
    34  	"github.com/snapcore/snapd/overlord/configstate"
    35  	"github.com/snapcore/snapd/overlord/configstate/config"
    36  	"github.com/snapcore/snapd/overlord/hookstate"
    37  	"github.com/snapcore/snapd/overlord/hookstate/hooktest"
    38  	"github.com/snapcore/snapd/overlord/snapstate"
    39  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    40  	"github.com/snapcore/snapd/overlord/state"
    41  	"github.com/snapcore/snapd/release"
    42  	"github.com/snapcore/snapd/snap"
    43  	"github.com/snapcore/snapd/snap/snaptest"
    44  	"github.com/snapcore/snapd/sysconfig"
    45  	"github.com/snapcore/snapd/testutil"
    46  )
    47  
    48  func TestConfigState(t *testing.T) { TestingT(t) }
    49  
    50  type configureHandlerSuite struct {
    51  	testutil.BaseTest
    52  
    53  	state   *state.State
    54  	context *hookstate.Context
    55  	handler hookstate.Handler
    56  }
    57  
    58  var _ = Suite(&configureHandlerSuite{})
    59  
    60  func (s *configureHandlerSuite) SetUpTest(c *C) {
    61  	s.BaseTest.SetUpTest(c)
    62  	dirs.SetRootDir(c.MkDir())
    63  	s.AddCleanup(func() { dirs.SetRootDir("/") })
    64  
    65  	s.state = state.New(nil)
    66  	s.state.Lock()
    67  	defer s.state.Unlock()
    68  
    69  	coreSnapYaml := `name: core
    70  version: 1.0
    71  type: os
    72  `
    73  	snaptest.MockSnap(c, coreSnapYaml, &snap.SideInfo{
    74  		RealName: "core",
    75  		Revision: snap.R(1),
    76  	})
    77  	snapstate.Set(s.state, "core", &snapstate.SnapState{
    78  		Active: true,
    79  		Sequence: []*snap.SideInfo{
    80  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
    81  		},
    82  		Current:  snap.R(1),
    83  		SnapType: "os",
    84  	})
    85  
    86  	s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    87  
    88  	task := s.state.NewTask("test-task", "my test task")
    89  	setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"}
    90  
    91  	var err error
    92  	s.context, err = hookstate.NewContext(task, task.State(), setup, hooktest.NewMockHandler(), "")
    93  	c.Assert(err, IsNil)
    94  
    95  	s.handler = configstate.NewConfigureHandler(s.context)
    96  }
    97  
    98  func (s *configureHandlerSuite) TestBeforeInitializesTransaction(c *C) {
    99  	// Initialize context
   100  	s.context.Lock()
   101  	s.context.Set("patch", map[string]interface{}{
   102  		"foo": "bar",
   103  	})
   104  	s.context.Unlock()
   105  
   106  	c.Check(s.handler.Before(), IsNil)
   107  
   108  	s.context.Lock()
   109  	tr := configstate.ContextTransaction(s.context)
   110  	s.context.Unlock()
   111  
   112  	var value string
   113  	c.Check(tr.Get("test-snap", "foo", &value), IsNil)
   114  	c.Check(value, Equals, "bar")
   115  }
   116  
   117  func makeModel(override map[string]interface{}) *asserts.Model {
   118  	model := map[string]interface{}{
   119  		"type":         "model",
   120  		"authority-id": "brand",
   121  		"series":       "16",
   122  		"brand-id":     "brand",
   123  		"model":        "baz-3000",
   124  		"architecture": "armhf",
   125  		"gadget":       "brand-gadget",
   126  		"kernel":       "kernel",
   127  		"timestamp":    "2018-01-01T08:00:00+00:00",
   128  	}
   129  	return assertstest.FakeAssertion(model, override).(*asserts.Model)
   130  }
   131  
   132  func (s *configureHandlerSuite) TestBeforeInitializesTransactionUseDefaults(c *C) {
   133  	r := release.MockOnClassic(false)
   134  	defer r()
   135  
   136  	const mockGadgetSnapYaml = `
   137  name: canonical-pc
   138  type: gadget
   139  `
   140  	var mockGadgetYaml = []byte(`
   141  defaults:
   142    testsnapidididididididididididid:
   143        bar: baz
   144        num: 1.305
   145  
   146  volumes:
   147      volume-id:
   148          bootloader: grub
   149  `)
   150  
   151  	info := snaptest.MockSnap(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(1)})
   152  	err := ioutil.WriteFile(filepath.Join(info.MountDir(), "meta", "gadget.yaml"), mockGadgetYaml, 0644)
   153  	c.Assert(err, IsNil)
   154  
   155  	s.state.Lock()
   156  	snapstate.Set(s.state, "canonical-pc", &snapstate.SnapState{
   157  		Active: true,
   158  		Sequence: []*snap.SideInfo{
   159  			{RealName: "canonical-pc", Revision: snap.R(1)},
   160  		},
   161  		Current:  snap.R(1),
   162  		SnapType: "gadget",
   163  	})
   164  
   165  	r = snapstatetest.MockDeviceModel(makeModel(map[string]interface{}{
   166  		"gadget": "canonical-pc",
   167  	}))
   168  	defer r()
   169  
   170  	const mockTestSnapYaml = `
   171  name: test-snap
   172  hooks:
   173      configure:
   174  `
   175  
   176  	snaptest.MockSnap(c, mockTestSnapYaml, &snap.SideInfo{Revision: snap.R(11)})
   177  	snapstate.Set(s.state, "test-snap", &snapstate.SnapState{
   178  		Active: true,
   179  		Sequence: []*snap.SideInfo{
   180  			{RealName: "test-snap", Revision: snap.R(11), SnapID: "testsnapidididididididididididid"},
   181  		},
   182  		Current:  snap.R(11),
   183  		SnapType: "app",
   184  	})
   185  	s.state.Unlock()
   186  
   187  	// Initialize context
   188  	s.context.Lock()
   189  	s.context.Set("use-defaults", true)
   190  	s.context.Unlock()
   191  
   192  	c.Assert(s.handler.Before(), IsNil)
   193  
   194  	s.context.Lock()
   195  	tr := configstate.ContextTransaction(s.context)
   196  	s.context.Unlock()
   197  
   198  	var value string
   199  	c.Check(tr.Get("test-snap", "bar", &value), IsNil)
   200  	c.Check(value, Equals, "baz")
   201  	var fl float64
   202  	c.Check(tr.Get("test-snap", "num", &fl), IsNil)
   203  	c.Check(fl, Equals, 1.305)
   204  }
   205  
   206  func (s *configureHandlerSuite) TestBeforeUseDefaultsMissingHook(c *C) {
   207  	r := release.MockOnClassic(false)
   208  	defer r()
   209  
   210  	const mockGadgetSnapYaml = `
   211  name: canonical-pc
   212  type: gadget
   213  `
   214  	var mockGadgetYaml = []byte(`
   215  defaults:
   216    testsnapidididididididididididid:
   217        bar: baz
   218        num: 1.305
   219  
   220  volumes:
   221      volume-id:
   222          bootloader: grub
   223  `)
   224  
   225  	info := snaptest.MockSnap(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(1)})
   226  	err := ioutil.WriteFile(filepath.Join(info.MountDir(), "meta", "gadget.yaml"), mockGadgetYaml, 0644)
   227  	c.Assert(err, IsNil)
   228  
   229  	s.state.Lock()
   230  	snapstate.Set(s.state, "canonical-pc", &snapstate.SnapState{
   231  		Active: true,
   232  		Sequence: []*snap.SideInfo{
   233  			{RealName: "canonical-pc", Revision: snap.R(1)},
   234  		},
   235  		Current:  snap.R(1),
   236  		SnapType: "gadget",
   237  	})
   238  
   239  	r = snapstatetest.MockDeviceModel(makeModel(map[string]interface{}{
   240  		"gadget": "canonical-pc",
   241  	}))
   242  	defer r()
   243  
   244  	snapstate.Set(s.state, "test-snap", &snapstate.SnapState{
   245  		Active: true,
   246  		Sequence: []*snap.SideInfo{
   247  			{RealName: "test-snap", Revision: snap.R(11), SnapID: "testsnapidididididididididididid"},
   248  		},
   249  		Current:  snap.R(11),
   250  		SnapType: "app",
   251  	})
   252  	s.state.Unlock()
   253  
   254  	// Initialize context
   255  	s.context.Lock()
   256  	s.context.Set("use-defaults", true)
   257  	s.context.Unlock()
   258  
   259  	err = s.handler.Before()
   260  	c.Check(err, ErrorMatches, `cannot apply gadget config defaults for snap "test-snap", no configure hook`)
   261  }
   262  
   263  type configcoreHandlerSuite struct {
   264  	testutil.BaseTest
   265  
   266  	o     *overlord.Overlord
   267  	state *state.State
   268  }
   269  
   270  var _ = Suite(&configcoreHandlerSuite{})
   271  
   272  func (s *configcoreHandlerSuite) SetUpTest(c *C) {
   273  	s.BaseTest.SetUpTest(c)
   274  
   275  	dirs.SetRootDir(c.MkDir())
   276  	s.AddCleanup(func() { dirs.SetRootDir("/") })
   277  
   278  	s.o = overlord.Mock()
   279  	s.state = s.o.State()
   280  
   281  	restore := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
   282  	s.AddCleanup(restore)
   283  
   284  	hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner())
   285  	c.Assert(err, IsNil)
   286  	s.o.AddManager(hookMgr)
   287  	r := configstate.MockConfigcoreExportExperimentalFlags(func(_ config.ConfGetter) error {
   288  		return nil
   289  	})
   290  	s.AddCleanup(r)
   291  
   292  	err = configstate.Init(s.state, hookMgr)
   293  
   294  	c.Assert(err, IsNil)
   295  	s.o.AddManager(s.o.TaskRunner())
   296  
   297  	r = snapstatetest.MockDeviceModel(makeModel(map[string]interface{}{
   298  		"gadget": "canonical-pc",
   299  	}))
   300  	s.AddCleanup(r)
   301  
   302  	s.state.Lock()
   303  	defer s.state.Unlock()
   304  
   305  	s.state.Set("seeded", true)
   306  	snapstate.Set(s.state, "canonical-pc", &snapstate.SnapState{
   307  		Active: true,
   308  		Sequence: []*snap.SideInfo{
   309  			{RealName: "canonical-pc", Revision: snap.R(1)},
   310  		},
   311  		Current:  snap.R(1),
   312  		SnapType: "gadget",
   313  	})
   314  }
   315  
   316  const mockGadgetSnapYaml = `
   317  name: canonical-pc
   318  type: gadget
   319  `
   320  
   321  func (s *configcoreHandlerSuite) TestRunsWhenSnapdOnly(c *C) {
   322  	r := release.MockOnClassic(false)
   323  	defer r()
   324  
   325  	var mockGadgetYaml = `
   326  defaults:
   327    system:
   328        foo: bar
   329  
   330  volumes:
   331      volume-id:
   332          bootloader: grub
   333  `
   334  	s.state.Lock()
   335  	defer s.state.Unlock()
   336  
   337  	ts := configstate.Configure(s.state, "core", nil, snapstate.UseConfigDefaults)
   338  	chg := s.state.NewChange("configure-core", "configure core")
   339  	chg.AddAll(ts)
   340  
   341  	snaptest.MockSnapWithFiles(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(1)}, [][]string{
   342  		{"meta/gadget.yaml", mockGadgetYaml},
   343  	})
   344  
   345  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
   346  		Active: true,
   347  		Sequence: []*snap.SideInfo{
   348  			{RealName: "snapd", Revision: snap.R(1)},
   349  		},
   350  		Current:  snap.R(1),
   351  		SnapType: "snapd",
   352  	})
   353  
   354  	witnessConfigcoreRun := func(dev sysconfig.Device, conf config.Conf) error {
   355  		c.Check(dev.Kernel(), Equals, "kernel")
   356  		// called with no state lock!
   357  		conf.State().Lock()
   358  		defer conf.State().Unlock()
   359  		var val string
   360  		err := conf.Get("core", "foo", &val)
   361  		c.Assert(err, IsNil)
   362  		c.Check(val, Equals, "bar")
   363  		return nil
   364  	}
   365  	r = configstate.MockConfigcoreRun(witnessConfigcoreRun)
   366  	defer r()
   367  
   368  	s.state.Unlock()
   369  	err := s.o.Settle(5 * time.Second)
   370  	s.state.Lock()
   371  	// Initialize context
   372  	c.Assert(err, IsNil)
   373  
   374  	tr := config.NewTransaction(s.state)
   375  	var foo string
   376  	err = tr.Get("core", "foo", &foo)
   377  	c.Assert(err, IsNil)
   378  	c.Check(foo, Equals, "bar")
   379  }
   380  
   381  func (s *configcoreHandlerSuite) TestRunsWhenCoreOnly(c *C) {
   382  	r := release.MockOnClassic(false)
   383  	defer r()
   384  
   385  	var mockGadgetYaml = `
   386  defaults:
   387    system:
   388        foo: bar
   389  
   390  volumes:
   391      volume-id:
   392          bootloader: grub
   393  `
   394  	s.state.Lock()
   395  	defer s.state.Unlock()
   396  
   397  	ts := configstate.Configure(s.state, "core", nil, snapstate.UseConfigDefaults)
   398  	chg := s.state.NewChange("configure-core", "configure core")
   399  	chg.AddAll(ts)
   400  
   401  	snaptest.MockSnapWithFiles(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(1)}, [][]string{
   402  		{"meta/gadget.yaml", mockGadgetYaml},
   403  	})
   404  
   405  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   406  		Active: true,
   407  		Sequence: []*snap.SideInfo{
   408  			{RealName: "core", Revision: snap.R(1)},
   409  		},
   410  		Current:  snap.R(1),
   411  		SnapType: "os",
   412  	})
   413  
   414  	witnessConfigcoreRun := func(dev sysconfig.Device, conf config.Conf) error {
   415  		c.Check(dev.Kernel(), Equals, "kernel")
   416  		// called with no state lock!
   417  		conf.State().Lock()
   418  		defer conf.State().Unlock()
   419  		var val string
   420  		err := conf.Get("core", "foo", &val)
   421  		c.Assert(err, IsNil)
   422  		c.Check(val, Equals, "bar")
   423  		return nil
   424  	}
   425  	r = configstate.MockConfigcoreRun(witnessConfigcoreRun)
   426  	defer r()
   427  
   428  	s.state.Unlock()
   429  	err := s.o.Settle(5 * time.Second)
   430  	s.state.Lock()
   431  	// Initialize context
   432  	c.Assert(err, IsNil)
   433  
   434  	tr := config.NewTransaction(s.state)
   435  	var foo string
   436  	err = tr.Get("core", "foo", &foo)
   437  	c.Assert(err, IsNil)
   438  	c.Check(foo, Equals, "bar")
   439  }