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