github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/configstate/configcore/services_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 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  	"os"
    26  	"path/filepath"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/dirs"
    31  	"github.com/snapcore/snapd/overlord/configstate/configcore"
    32  	"github.com/snapcore/snapd/release"
    33  	"github.com/snapcore/snapd/snap"
    34  	"github.com/snapcore/snapd/testutil"
    35  )
    36  
    37  type servicesSuite struct {
    38  	configcoreSuite
    39  }
    40  
    41  var _ = Suite(&servicesSuite{})
    42  
    43  func (s *servicesSuite) SetUpTest(c *C) {
    44  	s.configcoreSuite.SetUpTest(c)
    45  	c.Assert(os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "etc"), 0755), IsNil)
    46  	s.systemctlArgs = nil
    47  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    48  }
    49  
    50  func (s *servicesSuite) TestConfigureServiceInvalidValue(c *C) {
    51  	restore := release.MockOnClassic(false)
    52  	defer restore()
    53  
    54  	err := configcore.Run(&mockConf{
    55  		state: s.state,
    56  		changes: map[string]interface{}{
    57  			"service.ssh.disable": "xxx",
    58  		},
    59  	})
    60  	c.Check(err, ErrorMatches, `option "service.ssh.disable" has invalid value "xxx"`)
    61  }
    62  
    63  func (s *servicesSuite) TestConfigureServiceNotDisabled(c *C) {
    64  	err := configcore.SwitchDisableService("sshd.service", false, nil)
    65  	c.Assert(err, IsNil)
    66  	c.Check(s.systemctlArgs, DeepEquals, [][]string{
    67  		{"--root", dirs.GlobalRootDir, "unmask", "sshd.service"},
    68  		{"--root", dirs.GlobalRootDir, "enable", "sshd.service"},
    69  		{"start", "sshd.service"},
    70  	})
    71  }
    72  
    73  func (s *servicesSuite) TestConfigureServiceDisabled(c *C) {
    74  	err := configcore.SwitchDisableService("sshd.service", true, nil)
    75  	c.Assert(err, IsNil)
    76  	c.Check(s.systemctlArgs, DeepEquals, [][]string{
    77  		{"--root", dirs.GlobalRootDir, "disable", "sshd.service"},
    78  		{"--root", dirs.GlobalRootDir, "mask", "sshd.service"},
    79  		{"stop", "sshd.service"},
    80  		{"show", "--property=ActiveState", "sshd.service"},
    81  	})
    82  }
    83  
    84  func (s *servicesSuite) TestConfigureServiceDisabledIntegration(c *C) {
    85  	restore := release.MockOnClassic(false)
    86  	defer restore()
    87  
    88  	err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "/etc/ssh"), 0755)
    89  	c.Assert(err, IsNil)
    90  
    91  	for _, service := range []struct {
    92  		cfgName     string
    93  		systemdName string
    94  	}{
    95  		{"ssh", "ssh.service"},
    96  		{"rsyslog", "rsyslog.service"},
    97  	} {
    98  		s.systemctlArgs = nil
    99  
   100  		err := configcore.Run(&mockConf{
   101  			state: s.state,
   102  			conf: map[string]interface{}{
   103  				fmt.Sprintf("service.%s.disable", service.cfgName): true,
   104  			},
   105  		})
   106  		c.Assert(err, IsNil)
   107  		srv := service.systemdName
   108  		switch service.cfgName {
   109  		case "ssh":
   110  			sshCanary := filepath.Join(dirs.GlobalRootDir, "/etc/ssh/sshd_not_to_be_run")
   111  			_, err := os.Stat(sshCanary)
   112  			c.Assert(err, IsNil)
   113  			c.Check(s.systemctlArgs, DeepEquals, [][]string{
   114  				{"stop", srv},
   115  				{"show", "--property=ActiveState", srv},
   116  			})
   117  		default:
   118  			c.Check(s.systemctlArgs, DeepEquals, [][]string{
   119  				{"--root", dirs.GlobalRootDir, "disable", srv},
   120  				{"--root", dirs.GlobalRootDir, "mask", srv},
   121  				{"stop", srv},
   122  				{"show", "--property=ActiveState", srv},
   123  			})
   124  		}
   125  	}
   126  }
   127  
   128  func (s *servicesSuite) TestConfigureConsoleConfDisableFSOnly(c *C) {
   129  	restore := release.MockOnClassic(false)
   130  	defer restore()
   131  
   132  	conf := configcore.PlainCoreConfig(map[string]interface{}{
   133  		"service.console-conf.disable": true,
   134  	})
   135  
   136  	tmpDir := c.MkDir()
   137  	c.Assert(configcore.FilesystemOnlyApply(tmpDir, conf, nil), IsNil)
   138  
   139  	consoleConfDisabled := filepath.Join(tmpDir, "/var/lib/console-conf/complete")
   140  	c.Check(consoleConfDisabled, testutil.FileEquals, "console-conf has been disabled by the snapd system configuration\n")
   141  }
   142  
   143  func (s *servicesSuite) TestConfigureConsoleConfEnabledFSOnly(c *C) {
   144  	restore := release.MockOnClassic(false)
   145  	defer restore()
   146  
   147  	conf := configcore.PlainCoreConfig(map[string]interface{}{
   148  		"service.console-conf.disable": false,
   149  	})
   150  
   151  	tmpDir := c.MkDir()
   152  	c.Assert(configcore.FilesystemOnlyApply(tmpDir, conf, nil), IsNil)
   153  
   154  	consoleConfDisabled := filepath.Join(tmpDir, "/var/lib/console-conf/complete")
   155  	c.Check(consoleConfDisabled, testutil.FileAbsent)
   156  }
   157  
   158  func (s *servicesSuite) TestConfigureConsoleConfEnableNotAtRuntime(c *C) {
   159  	restore := release.MockOnClassic(false)
   160  	defer restore()
   161  
   162  	// pretend that console-conf is disabled
   163  	canary := filepath.Join(dirs.GlobalRootDir, "/var/lib/console-conf/complete")
   164  	err := os.MkdirAll(filepath.Dir(canary), 0755)
   165  	c.Assert(err, IsNil)
   166  	err = ioutil.WriteFile(canary, nil, 0644)
   167  	c.Assert(err, IsNil)
   168  
   169  	// now enable it
   170  	err = configcore.Run(&mockConf{
   171  		state: s.state,
   172  		conf: map[string]interface{}{
   173  			"service.console-conf.disable": false,
   174  		},
   175  	})
   176  	c.Assert(err, ErrorMatches, "cannot toggle console-conf at runtime, but only initially via gadget defaults")
   177  }
   178  
   179  func (s *servicesSuite) TestConfigureConsoleConfDisableNotAtRuntime(c *C) {
   180  	restore := release.MockOnClassic(false)
   181  	defer restore()
   182  
   183  	// console-conf is not disabled, i.e. there is no
   184  	// "/var/lib/console-conf/complete" file
   185  
   186  	// now try to enable it
   187  	err := configcore.Run(&mockConf{
   188  		state: s.state,
   189  		conf: map[string]interface{}{
   190  			"service.console-conf.disable": true,
   191  		},
   192  	})
   193  	c.Assert(err, ErrorMatches, "cannot toggle console-conf at runtime, but only initially via gadget defaults")
   194  }
   195  
   196  func (s *servicesSuite) TestConfigureConsoleConfEnableAlreadyEnabledIsFine(c *C) {
   197  	restore := release.MockOnClassic(false)
   198  	defer restore()
   199  
   200  	// Note that we have no
   201  	//        /var/lib/console-conf/complete
   202  	// file. So console-conf is already enabled
   203  	err := configcore.Run(&mockConf{
   204  		state: s.state,
   205  		conf: map[string]interface{}{
   206  			"service.console-conf.disable": false,
   207  		},
   208  	})
   209  	c.Assert(err, IsNil)
   210  }
   211  
   212  func (s *servicesSuite) TestConfigureConsoleConfDisableAlreadyDisabledIsFine(c *C) {
   213  	restore := release.MockOnClassic(false)
   214  	defer restore()
   215  
   216  	// pretend that console-conf is disabled
   217  	canary := filepath.Join(dirs.GlobalRootDir, "/var/lib/console-conf/complete")
   218  	err := os.MkdirAll(filepath.Dir(canary), 0755)
   219  	c.Assert(err, IsNil)
   220  	err = ioutil.WriteFile(canary, nil, 0644)
   221  	c.Assert(err, IsNil)
   222  
   223  	err = configcore.Run(&mockConf{
   224  		state: s.state,
   225  		conf: map[string]interface{}{
   226  			"service.console-conf.disable": true,
   227  		},
   228  	})
   229  	c.Assert(err, IsNil)
   230  }
   231  
   232  func (s *servicesSuite) TestConfigureServiceEnableIntegration(c *C) {
   233  	restore := release.MockOnClassic(false)
   234  	defer restore()
   235  
   236  	err := os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "/etc/ssh"), 0755)
   237  	c.Assert(err, IsNil)
   238  
   239  	for _, service := range []struct {
   240  		cfgName     string
   241  		systemdName string
   242  	}{
   243  		{"ssh", "ssh.service"},
   244  		{"rsyslog", "rsyslog.service"},
   245  	} {
   246  		s.systemctlArgs = nil
   247  		err := configcore.Run(&mockConf{
   248  			state: s.state,
   249  			conf: map[string]interface{}{
   250  				fmt.Sprintf("service.%s.disable", service.cfgName): false,
   251  			},
   252  		})
   253  
   254  		c.Assert(err, IsNil)
   255  		srv := service.systemdName
   256  		switch service.cfgName {
   257  		case "ssh":
   258  			c.Check(s.systemctlArgs, DeepEquals, [][]string{
   259  				{"--root", dirs.GlobalRootDir, "unmask", "sshd.service"},
   260  				{"--root", dirs.GlobalRootDir, "unmask", "ssh.service"},
   261  				{"start", srv},
   262  			})
   263  			sshCanary := filepath.Join(dirs.GlobalRootDir, "/etc/ssh/sshd_not_to_be_run")
   264  			_, err := os.Stat(sshCanary)
   265  			c.Assert(err, ErrorMatches, ".* no such file or directory")
   266  		default:
   267  			c.Check(s.systemctlArgs, DeepEquals, [][]string{
   268  				{"--root", dirs.GlobalRootDir, "unmask", srv},
   269  				{"--root", dirs.GlobalRootDir, "enable", srv},
   270  				{"start", srv},
   271  			})
   272  		}
   273  	}
   274  }
   275  
   276  func (s *servicesSuite) TestConfigureServiceUnsupportedService(c *C) {
   277  	restore := release.MockOnClassic(false)
   278  	defer restore()
   279  
   280  	err := configcore.Run(&mockConf{
   281  		state: s.state,
   282  		conf: map[string]interface{}{
   283  			"service.snapd.disable": true,
   284  		},
   285  	})
   286  	c.Assert(err, IsNil)
   287  
   288  	// ensure nothing gets enabled/disabled when an unsupported
   289  	// service is set for disable
   290  	c.Check(s.systemctlArgs, IsNil)
   291  }
   292  
   293  func (s *servicesSuite) TestFilesystemOnlyApply(c *C) {
   294  	tmpDir := c.MkDir()
   295  	c.Assert(os.MkdirAll(filepath.Join(tmpDir, "etc", "ssh"), 0755), IsNil)
   296  
   297  	conf := configcore.PlainCoreConfig(map[string]interface{}{
   298  		"service.ssh.disable":     "true",
   299  		"service.rsyslog.disable": "true",
   300  	})
   301  	c.Assert(configcore.FilesystemOnlyApply(tmpDir, conf, nil), IsNil)
   302  	c.Check(s.systemctlArgs, DeepEquals, [][]string{
   303  		{"--root", tmpDir, "mask", "rsyslog.service"},
   304  	})
   305  }