github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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/release"
    35  	"github.com/snapcore/snapd/testutil"
    36  )
    37  
    38  type watchdogSuite struct {
    39  	configcoreSuite
    40  
    41  	mockEtcEnvironment string
    42  }
    43  
    44  var _ = Suite(&watchdogSuite{})
    45  
    46  func (s *watchdogSuite) SetUpTest(c *C) {
    47  	s.configcoreSuite.SetUpTest(c)
    48  
    49  	dirs.SetRootDir(c.MkDir())
    50  	s.mockEtcEnvironment = filepath.Join(dirs.SnapSystemdConfDir, "10-snapd-watchdog.conf")
    51  	err := os.MkdirAll(dirs.SnapSystemdConfDir, 0755)
    52  	c.Assert(err, IsNil)
    53  }
    54  
    55  func (s *watchdogSuite) TearDownTest(c *C) {
    56  	dirs.SetRootDir("/")
    57  }
    58  
    59  func (s *watchdogSuite) TestConfigureWatchdog(c *C) {
    60  	restore := release.MockOnClassic(false)
    61  	defer restore()
    62  
    63  	for option, val := range map[string]string{"runtime-timeout": "10", "shutdown-timeout": "60"} {
    64  
    65  		err := configcore.Run(&mockConf{
    66  			state: s.state,
    67  			conf: map[string]interface{}{
    68  				fmt.Sprintf("watchdog.%s", option): val + "s",
    69  			},
    70  		})
    71  		c.Assert(err, IsNil)
    72  
    73  		var systemdOption string
    74  		switch option {
    75  		case "runtime-timeout":
    76  			systemdOption = "RuntimeWatchdogSec"
    77  		case "shutdown-timeout":
    78  			systemdOption = "ShutdownWatchdogSec"
    79  		}
    80  		c.Check(s.mockEtcEnvironment, testutil.FileEquals,
    81  			fmt.Sprintf("[Manager]\n%s=%s\n", systemdOption, val))
    82  	}
    83  }
    84  
    85  func (s *watchdogSuite) TestConfigureWatchdogUnits(c *C) {
    86  	restore := release.MockOnClassic(false)
    87  	defer restore()
    88  
    89  	times := []int{56, 432}
    90  	type timeUnit struct {
    91  		unit  string
    92  		toSec int
    93  	}
    94  
    95  	for _, tunit := range []timeUnit{{"s", 1}, {"m", 60}, {"h", 3600}} {
    96  		err := configcore.Run(&mockConf{
    97  			state: s.state,
    98  			conf: map[string]interface{}{
    99  				"watchdog.runtime-timeout":  fmt.Sprintf("%d", times[0]) + tunit.unit,
   100  				"watchdog.shutdown-timeout": fmt.Sprintf("%d", times[1]) + tunit.unit,
   101  			},
   102  		})
   103  		c.Assert(err, IsNil)
   104  		c.Check(s.mockEtcEnvironment, testutil.FileEquals, "[Manager]\n"+
   105  			fmt.Sprintf("RuntimeWatchdogSec=%d\n", times[0]*tunit.toSec)+
   106  			fmt.Sprintf("ShutdownWatchdogSec=%d\n", times[1]*tunit.toSec))
   107  	}
   108  }
   109  
   110  func (s *watchdogSuite) TestConfigureWatchdogAll(c *C) {
   111  	restore := release.MockOnClassic(false)
   112  	defer restore()
   113  
   114  	times := []int{10, 100}
   115  	err := configcore.Run(&mockConf{
   116  		state: s.state,
   117  		conf: map[string]interface{}{
   118  			"watchdog.runtime-timeout":  fmt.Sprintf("%ds", times[0]),
   119  			"watchdog.shutdown-timeout": fmt.Sprintf("%ds", times[1]),
   120  		},
   121  	})
   122  	c.Assert(err, IsNil)
   123  	c.Check(s.mockEtcEnvironment, testutil.FileEquals, "[Manager]\n"+
   124  		fmt.Sprintf("RuntimeWatchdogSec=%d\n", times[0])+
   125  		fmt.Sprintf("ShutdownWatchdogSec=%d\n", times[1]))
   126  }
   127  
   128  func (s *watchdogSuite) TestConfigureWatchdogBadFormat(c *C) {
   129  	restore := release.MockOnClassic(false)
   130  	defer restore()
   131  
   132  	type badValErr struct {
   133  		val string
   134  		err string
   135  	}
   136  	for _, badVal := range []badValErr{{"BAD", ".*invalid duration.*"},
   137  		{"-5s", ".*negative duration.*"},
   138  		{"34k", ".*unknown unit.*"}} {
   139  		err := configcore.Run(&mockConf{
   140  			state: s.state,
   141  			conf: map[string]interface{}{
   142  				"watchdog.runtime-timeout": badVal.val,
   143  			},
   144  		})
   145  		c.Assert(err, ErrorMatches, badVal.err)
   146  	}
   147  }
   148  
   149  func (s *watchdogSuite) TestConfigureWatchdogNoFileUpdate(c *C) {
   150  	restore := release.MockOnClassic(false)
   151  	defer restore()
   152  
   153  	times := []int{10, 100}
   154  	content := "[Manager]\n" +
   155  		fmt.Sprintf("RuntimeWatchdogSec=%d\n", times[0]) +
   156  		fmt.Sprintf("ShutdownWatchdogSec=%d\n", times[1])
   157  	err := ioutil.WriteFile(s.mockEtcEnvironment, []byte(content), 0644)
   158  	c.Assert(err, IsNil)
   159  
   160  	info, err := os.Stat(s.mockEtcEnvironment)
   161  	c.Assert(err, IsNil)
   162  
   163  	fileModTime := info.ModTime()
   164  
   165  	// To make sure the times will defer if the file is newly written
   166  	time.Sleep(100 * time.Millisecond)
   167  
   168  	err = configcore.Run(&mockConf{
   169  		state: s.state,
   170  		conf: map[string]interface{}{
   171  			"watchdog.runtime-timeout":  fmt.Sprintf("%ds", times[0]),
   172  			"watchdog.shutdown-timeout": fmt.Sprintf("%ds", times[1]),
   173  		},
   174  	})
   175  	c.Assert(err, IsNil)
   176  	c.Check(s.mockEtcEnvironment, testutil.FileEquals, content)
   177  
   178  	info, err = os.Stat(s.mockEtcEnvironment)
   179  	c.Assert(err, IsNil)
   180  	c.Assert(info.ModTime(), Equals, fileModTime)
   181  }
   182  
   183  func (s *watchdogSuite) TestConfigureWatchdogRemovesIfEmpty(c *C) {
   184  	restore := release.MockOnClassic(false)
   185  	defer restore()
   186  
   187  	// add canary to ensure we don't touch other files
   188  	canary := filepath.Join(dirs.SnapSystemdConfDir, "05-canary.conf")
   189  	err := ioutil.WriteFile(canary, nil, 0644)
   190  	c.Assert(err, IsNil)
   191  
   192  	content := `[Manager]
   193  RuntimeWatchdogSec=10
   194  ShutdownWatchdogSec=20
   195  `
   196  	err = ioutil.WriteFile(s.mockEtcEnvironment, []byte(content), 0644)
   197  	c.Assert(err, IsNil)
   198  
   199  	err = configcore.Run(&mockConf{
   200  		state: s.state,
   201  		conf: map[string]interface{}{
   202  			"watchdog.runtime-timeout":  0,
   203  			"watchdog.shutdown-timeout": 0,
   204  		},
   205  	})
   206  	c.Assert(err, IsNil)
   207  
   208  	// ensure the file got deleted
   209  	c.Check(osutil.FileExists(s.mockEtcEnvironment), Equals, false)
   210  	// but the canary is still here
   211  	c.Check(osutil.FileExists(canary), Equals, true)
   212  }