github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/configstate/configcore/corecfg_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2021 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 configcore_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"path/filepath"
    26  	"reflect"
    27  	"testing"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/osutil"
    33  	"github.com/snapcore/snapd/overlord/configstate/config"
    34  	"github.com/snapcore/snapd/overlord/configstate/configcore"
    35  	"github.com/snapcore/snapd/overlord/state"
    36  	"github.com/snapcore/snapd/snap"
    37  	"github.com/snapcore/snapd/systemd"
    38  	"github.com/snapcore/snapd/testutil"
    39  )
    40  
    41  func Test(t *testing.T) { TestingT(t) }
    42  
    43  type mockConf struct {
    44  	state   *state.State
    45  	conf    map[string]interface{}
    46  	changes map[string]interface{}
    47  	err     error
    48  }
    49  
    50  func (cfg *mockConf) Get(snapName, key string, result interface{}) error {
    51  	if snapName != "core" {
    52  		return fmt.Errorf("mockConf only knows about core")
    53  	}
    54  
    55  	var value interface{}
    56  	value = cfg.changes[key]
    57  	if value == nil {
    58  		value = cfg.conf[key]
    59  	}
    60  	if value != nil {
    61  		v1 := reflect.ValueOf(result)
    62  		v2 := reflect.Indirect(v1)
    63  		v2.Set(reflect.ValueOf(value))
    64  	}
    65  	return cfg.err
    66  }
    67  
    68  func (cfg *mockConf) GetMaybe(snapName, key string, result interface{}) error {
    69  	err := cfg.Get(snapName, key, result)
    70  	if err != nil && !config.IsNoOption(err) {
    71  		return err
    72  	}
    73  	return nil
    74  }
    75  
    76  func (cfg *mockConf) GetPristine(snapName, key string, result interface{}) error {
    77  	if snapName != "core" {
    78  		return fmt.Errorf("mockConf only knows about core")
    79  	}
    80  
    81  	var value interface{}
    82  	value = cfg.conf[key]
    83  	if value != nil {
    84  		v1 := reflect.ValueOf(result)
    85  		v2 := reflect.Indirect(v1)
    86  		v2.Set(reflect.ValueOf(value))
    87  	}
    88  	return cfg.err
    89  }
    90  
    91  func (cfg *mockConf) GetPristineMaybe(snapName, key string, result interface{}) error {
    92  	err := cfg.GetPristine(snapName, key, result)
    93  	if err != nil && !config.IsNoOption(err) {
    94  		return err
    95  	}
    96  	return nil
    97  }
    98  
    99  func (cfg *mockConf) Set(snapName, key string, v interface{}) error {
   100  	if snapName != "core" {
   101  		return fmt.Errorf("mockConf only knows about core")
   102  	}
   103  	if cfg.conf == nil {
   104  		cfg.conf = make(map[string]interface{})
   105  	}
   106  	cfg.conf[key] = v
   107  	return nil
   108  }
   109  
   110  func (cfg *mockConf) Changes() []string {
   111  	out := make([]string, 0, len(cfg.changes))
   112  	for k := range cfg.changes {
   113  		out = append(out, "core."+k)
   114  	}
   115  	return out
   116  }
   117  
   118  func (cfg *mockConf) State() *state.State {
   119  	return cfg.state
   120  }
   121  
   122  // configcoreSuite is the base for all the configcore tests
   123  type configcoreSuite struct {
   124  	testutil.BaseTest
   125  
   126  	state *state.State
   127  
   128  	systemctlOutput   func(args ...string) []byte
   129  	systemctlArgs     [][]string
   130  	systemdSysctlArgs [][]string
   131  }
   132  
   133  var _ = Suite(&configcoreSuite{})
   134  
   135  func (s *configcoreSuite) SetUpTest(c *C) {
   136  	s.BaseTest.SetUpTest(c)
   137  
   138  	dirs.SetRootDir(c.MkDir())
   139  	s.AddCleanup(func() { dirs.SetRootDir("") })
   140  
   141  	s.systemctlOutput = func(args ...string) []byte {
   142  		return []byte("ActiveState=inactive")
   143  	}
   144  
   145  	s.AddCleanup(systemd.MockSystemctl(func(args ...string) ([]byte, error) {
   146  		s.systemctlArgs = append(s.systemctlArgs, args[:])
   147  		return s.systemctlOutput(args...), nil
   148  	}))
   149  	s.systemctlArgs = nil
   150  	s.AddCleanup(systemd.MockSystemdSysctl(func(args ...string) error {
   151  		s.systemdSysctlArgs = append(s.systemdSysctlArgs, args[:])
   152  		return nil
   153  	}))
   154  	s.systemdSysctlArgs = nil
   155  
   156  	s.state = state.New(nil)
   157  
   158  	restore := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
   159  	s.AddCleanup(restore)
   160  
   161  	// mock an empty cmdline since we check the cmdline to check whether we are
   162  	// in install mode or uc20 run mode, etc. and we don't want to use the
   163  	// host's proc/cmdline
   164  	mockCmdline := filepath.Join(dirs.GlobalRootDir, "cmdline")
   165  	err := ioutil.WriteFile(mockCmdline, nil, 0644)
   166  	c.Assert(err, IsNil)
   167  	restore = osutil.MockProcCmdline(mockCmdline)
   168  	s.AddCleanup(restore)
   169  }
   170  
   171  // runCfgSuite tests configcore.Run
   172  type runCfgSuite struct {
   173  	configcoreSuite
   174  }
   175  
   176  var _ = Suite(&runCfgSuite{})
   177  
   178  func (r *runCfgSuite) TestConfigureUnknownOption(c *C) {
   179  	conf := &mockConf{
   180  		state: r.state,
   181  		changes: map[string]interface{}{
   182  			"unknown.option": "1",
   183  		},
   184  	}
   185  
   186  	err := configcore.Run(coreDev, conf)
   187  	c.Check(err, ErrorMatches, `cannot set "core.unknown.option": unsupported system option`)
   188  }
   189  
   190  type mockDev struct {
   191  	mode    string
   192  	classic bool
   193  	kernel  string
   194  	uc20    bool
   195  }
   196  
   197  func (d mockDev) RunMode() bool    { return d.mode == "" || d.mode == "run" }
   198  func (d mockDev) Classic() bool    { return d.classic }
   199  func (d mockDev) HasModeenv() bool { return d.uc20 }
   200  func (d mockDev) Kernel() string {
   201  	if d.Classic() {
   202  		return ""
   203  	}
   204  	if d.kernel == "" {
   205  		return "pc-kernel"
   206  	}
   207  	return d.kernel
   208  }
   209  
   210  var (
   211  	coreDev    = mockDev{classic: false}
   212  	classicDev = mockDev{classic: true}
   213  )
   214  
   215  // applyCfgSuite tests configcore.Apply()
   216  type applyCfgSuite struct {
   217  	tmpDir string
   218  }
   219  
   220  var _ = Suite(&applyCfgSuite{})
   221  
   222  func (s *applyCfgSuite) SetUpTest(c *C) {
   223  	s.tmpDir = c.MkDir()
   224  	dirs.SetRootDir(s.tmpDir)
   225  }
   226  
   227  func (s *applyCfgSuite) TearDownTest(c *C) {
   228  	dirs.SetRootDir("")
   229  }
   230  
   231  func (s *applyCfgSuite) TestEmptyRootDir(c *C) {
   232  	err := configcore.FilesystemOnlyApply(coreDev, "", nil)
   233  	c.Check(err, ErrorMatches, `internal error: root directory for configcore.FilesystemOnlyApply\(\) not set`)
   234  }
   235  
   236  func (s *applyCfgSuite) TestSmoke(c *C) {
   237  	c.Assert(configcore.FilesystemOnlyApply(coreDev, s.tmpDir, map[string]interface{}{}), IsNil)
   238  }
   239  
   240  func (s *applyCfgSuite) TestPlainCoreConfigGetErrorIfNotCore(c *C) {
   241  	conf := configcore.PlainCoreConfig(map[string]interface{}{})
   242  	var val interface{}
   243  	c.Assert(conf.Get("some-snap", "a", &val), ErrorMatches, `internal error: expected core snap in Get\(\), "some-snap" was requested`)
   244  }
   245  
   246  func (s *applyCfgSuite) TestPlainCoreConfigGet(c *C) {
   247  	conf := configcore.PlainCoreConfig(map[string]interface{}{"foo": "bar"})
   248  	var val interface{}
   249  	c.Assert(conf.Get("core", "a", &val), DeepEquals, &config.NoOptionError{SnapName: "core", Key: "a"})
   250  	c.Assert(conf.Get("core", "foo", &val), IsNil)
   251  	c.Check(val, DeepEquals, "bar")
   252  }
   253  
   254  func (s *applyCfgSuite) TestPlainCoreConfigGetMaybe(c *C) {
   255  	conf := configcore.PlainCoreConfig(map[string]interface{}{"foo": "bar"})
   256  	var val interface{}
   257  	c.Assert(conf.GetMaybe("core", "a", &val), IsNil)
   258  	c.Assert(val, IsNil)
   259  	c.Assert(conf.Get("core", "foo", &val), IsNil)
   260  	c.Check(val, DeepEquals, "bar")
   261  }
   262  
   263  func (s *applyCfgSuite) TestNilHandlePanics(c *C) {
   264  	c.Assert(func() { configcore.AddFSOnlyHandler(nil, nil, nil) },
   265  		Panics, "cannot have nil handle with fsOnlyHandler")
   266  
   267  	c.Assert(func() { configcore.AddWithStateHandler(nil, nil, nil) },
   268  		Panics, "cannot have nil handle with addWithStateHandler if validatedOnlyStateConfig flag is not set")
   269  }