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