github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/configstate/configcore/watchdog_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 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 "time" 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/configcore" 34 "github.com/snapcore/snapd/testutil" 35 ) 36 37 type watchdogSuite struct { 38 configcoreSuite 39 40 mockEtcEnvironment string 41 } 42 43 var _ = Suite(&watchdogSuite{}) 44 45 func (s *watchdogSuite) SetUpTest(c *C) { 46 s.configcoreSuite.SetUpTest(c) 47 48 s.mockEtcEnvironment = filepath.Join(dirs.SnapSystemdConfDir, "10-snapd-watchdog.conf") 49 } 50 51 func (s *watchdogSuite) TestConfigureWatchdog(c *C) { 52 for option, val := range map[string]string{"runtime-timeout": "10", "shutdown-timeout": "60"} { 53 54 err := configcore.Run(coreDev, &mockConf{ 55 state: s.state, 56 conf: map[string]interface{}{ 57 fmt.Sprintf("watchdog.%s", option): val + "s", 58 }, 59 }) 60 c.Assert(err, IsNil) 61 62 var systemdOption string 63 switch option { 64 case "runtime-timeout": 65 systemdOption = "RuntimeWatchdogSec" 66 case "shutdown-timeout": 67 systemdOption = "ShutdownWatchdogSec" 68 } 69 c.Check(s.mockEtcEnvironment, testutil.FileEquals, 70 fmt.Sprintf("[Manager]\n%s=%s\n", systemdOption, val)) 71 } 72 73 c.Check(s.systemctlArgs, DeepEquals, [][]string{ 74 {"daemon-reexec"}, 75 {"daemon-reexec"}, 76 }) 77 } 78 79 func (s *watchdogSuite) TestConfigureWatchdogUnits(c *C) { 80 times := []int{56, 432} 81 type timeUnit struct { 82 unit string 83 toSec int 84 } 85 86 for _, tunit := range []timeUnit{{"s", 1}, {"m", 60}, {"h", 3600}} { 87 err := configcore.Run(coreDev, &mockConf{ 88 state: s.state, 89 conf: map[string]interface{}{ 90 "watchdog.runtime-timeout": fmt.Sprintf("%d", times[0]) + tunit.unit, 91 "watchdog.shutdown-timeout": fmt.Sprintf("%d", times[1]) + tunit.unit, 92 }, 93 }) 94 c.Assert(err, IsNil) 95 c.Check(s.mockEtcEnvironment, testutil.FileEquals, "[Manager]\n"+ 96 fmt.Sprintf("RuntimeWatchdogSec=%d\n", times[0]*tunit.toSec)+ 97 fmt.Sprintf("ShutdownWatchdogSec=%d\n", times[1]*tunit.toSec)) 98 } 99 } 100 101 func (s *watchdogSuite) TestConfigureWatchdogAll(c *C) { 102 times := []int{10, 100} 103 err := configcore.Run(coreDev, &mockConf{ 104 state: s.state, 105 conf: map[string]interface{}{ 106 "watchdog.runtime-timeout": fmt.Sprintf("%ds", times[0]), 107 "watchdog.shutdown-timeout": fmt.Sprintf("%ds", times[1]), 108 }, 109 }) 110 c.Assert(err, IsNil) 111 c.Check(s.mockEtcEnvironment, testutil.FileEquals, "[Manager]\n"+ 112 fmt.Sprintf("RuntimeWatchdogSec=%d\n", times[0])+ 113 fmt.Sprintf("ShutdownWatchdogSec=%d\n", times[1])) 114 115 c.Check(s.systemctlArgs, DeepEquals, [][]string{ 116 {"daemon-reexec"}, 117 }) 118 } 119 120 func (s *watchdogSuite) TestConfigureWatchdogAllConfDirExistsAlready(c *C) { 121 // make .conf.d directory already 122 err := os.MkdirAll(dirs.SnapSystemdConfDir, 0755) 123 c.Assert(err, IsNil) 124 125 times := []int{10, 100} 126 err = configcore.Run(coreDev, &mockConf{ 127 state: s.state, 128 conf: map[string]interface{}{ 129 "watchdog.runtime-timeout": fmt.Sprintf("%ds", times[0]), 130 "watchdog.shutdown-timeout": fmt.Sprintf("%ds", times[1]), 131 }, 132 }) 133 c.Assert(err, IsNil) 134 c.Check(s.mockEtcEnvironment, testutil.FileEquals, "[Manager]\n"+ 135 fmt.Sprintf("RuntimeWatchdogSec=%d\n", times[0])+ 136 fmt.Sprintf("ShutdownWatchdogSec=%d\n", times[1])) 137 138 c.Check(s.systemctlArgs, DeepEquals, [][]string{ 139 {"daemon-reexec"}, 140 }) 141 } 142 143 func (s *watchdogSuite) TestConfigureWatchdogBadFormat(c *C) { 144 type badValErr struct { 145 val string 146 err string 147 } 148 for _, badVal := range []badValErr{{"BAD", ".*invalid duration.*"}, 149 {"-5s", ".*negative duration.*"}, 150 {"34k", ".*unknown unit.*"}} { 151 err := configcore.Run(coreDev, &mockConf{ 152 state: s.state, 153 conf: map[string]interface{}{ 154 "watchdog.runtime-timeout": badVal.val, 155 }, 156 }) 157 c.Assert(err, ErrorMatches, badVal.err) 158 } 159 160 c.Check(s.systemctlArgs, HasLen, 0) 161 } 162 163 func (s *watchdogSuite) TestConfigureWatchdogNoFileUpdate(c *C) { 164 err := os.MkdirAll(dirs.SnapSystemdConfDir, 0755) 165 c.Assert(err, IsNil) 166 times := []int{10, 100} 167 content := "[Manager]\n" + 168 fmt.Sprintf("RuntimeWatchdogSec=%d\n", times[0]) + 169 fmt.Sprintf("ShutdownWatchdogSec=%d\n", times[1]) 170 err = ioutil.WriteFile(s.mockEtcEnvironment, []byte(content), 0644) 171 c.Assert(err, IsNil) 172 173 info, err := os.Stat(s.mockEtcEnvironment) 174 c.Assert(err, IsNil) 175 176 fileModTime := info.ModTime() 177 178 // To make sure the times will defer if the file is newly written 179 time.Sleep(100 * time.Millisecond) 180 181 err = configcore.Run(coreDev, &mockConf{ 182 state: s.state, 183 conf: map[string]interface{}{ 184 "watchdog.runtime-timeout": fmt.Sprintf("%ds", times[0]), 185 "watchdog.shutdown-timeout": fmt.Sprintf("%ds", times[1]), 186 }, 187 }) 188 c.Assert(err, IsNil) 189 c.Check(s.mockEtcEnvironment, testutil.FileEquals, content) 190 191 info, err = os.Stat(s.mockEtcEnvironment) 192 c.Assert(err, IsNil) 193 c.Assert(info.ModTime(), Equals, fileModTime) 194 195 c.Check(s.systemctlArgs, HasLen, 0) 196 } 197 198 func (s *watchdogSuite) TestConfigureWatchdogRemovesIfEmpty(c *C) { 199 err := os.MkdirAll(dirs.SnapSystemdConfDir, 0755) 200 c.Assert(err, IsNil) 201 // add canary to ensure we don't touch other files 202 canary := filepath.Join(dirs.SnapSystemdConfDir, "05-canary.conf") 203 err = ioutil.WriteFile(canary, nil, 0644) 204 c.Assert(err, IsNil) 205 206 content := `[Manager] 207 RuntimeWatchdogSec=10 208 ShutdownWatchdogSec=20 209 ` 210 err = ioutil.WriteFile(s.mockEtcEnvironment, []byte(content), 0644) 211 c.Assert(err, IsNil) 212 213 err = configcore.Run(coreDev, &mockConf{ 214 state: s.state, 215 conf: map[string]interface{}{ 216 "watchdog.runtime-timeout": 0, 217 "watchdog.shutdown-timeout": 0, 218 }, 219 }) 220 c.Assert(err, IsNil) 221 222 // ensure the file got deleted 223 c.Check(osutil.FileExists(s.mockEtcEnvironment), Equals, false) 224 // but the canary is still here 225 c.Check(osutil.FileExists(canary), Equals, true) 226 227 // apply defaults 228 c.Check(s.systemctlArgs, DeepEquals, [][]string{ 229 {"daemon-reexec"}, 230 }) 231 } 232 233 func (s *watchdogSuite) TestFilesystemOnlyApply(c *C) { 234 conf := configcore.PlainCoreConfig(map[string]interface{}{ 235 "watchdog.runtime-timeout": "4s", 236 }) 237 238 tmpDir := c.MkDir() 239 c.Assert(configcore.FilesystemOnlyApply(coreDev, tmpDir, conf), IsNil) 240 241 watchdogCfg := filepath.Join(tmpDir, "/etc/systemd/system.conf.d/10-snapd-watchdog.conf") 242 c.Check(watchdogCfg, testutil.FileEquals, "[Manager]\nRuntimeWatchdogSec=4\n") 243 } 244 245 func (s *watchdogSuite) TestFilesystemOnlyApplyValidationFails(c *C) { 246 conf := configcore.PlainCoreConfig(map[string]interface{}{ 247 "watchdog.runtime-timeout": "foo", 248 }) 249 250 tmpDir := c.MkDir() 251 c.Assert(configcore.FilesystemOnlyApply(coreDev, tmpDir, conf), ErrorMatches, `cannot parse "foo": time: invalid duration \"?foo\"?`) 252 }