gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/cmdstate/cmdstate_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 cmdstate_test 21 22 import ( 23 "path/filepath" 24 "strings" 25 "testing" 26 "time" 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" 33 "github.com/snapcore/snapd/overlord/cmdstate" 34 "github.com/snapcore/snapd/overlord/state" 35 ) 36 37 // hook up gocheck to testing 38 func TestCommand(t *testing.T) { check.TestingT(t) } 39 40 type cmdSuite struct { 41 rootdir string 42 state *state.State 43 se *overlord.StateEngine 44 manager overlord.StateManager 45 restore func() 46 } 47 48 var _ = check.Suite(&cmdSuite{}) 49 50 type statr interface { 51 Status() state.Status 52 } 53 54 func (s *cmdSuite) waitfor(thing statr) { 55 s.state.Unlock() 56 for i := 0; i < 5; i++ { 57 s.se.Ensure() 58 s.se.Wait() 59 s.state.Lock() 60 if thing.Status().Ready() { 61 return 62 } 63 s.state.Unlock() 64 } 65 s.state.Lock() 66 } 67 68 func (s *cmdSuite) SetUpTest(c *check.C) { 69 d := c.MkDir() 70 dirs.SetRootDir(d) 71 s.rootdir = d 72 s.state = state.New(nil) 73 s.se = overlord.NewStateEngine(s.state) 74 runner := state.NewTaskRunner(s.state) 75 s.manager = cmdstate.Manager(s.state, runner) 76 s.se.AddManager(s.manager) 77 s.se.AddManager(runner) 78 c.Assert(s.se.StartUp(), check.IsNil) 79 s.restore = cmdstate.MockDefaultExecTimeout(time.Second / 10) 80 } 81 82 func (s *cmdSuite) TearDownTest(c *check.C) { 83 s.restore() 84 } 85 86 func (s *cmdSuite) TestExecTask(c *check.C) { 87 s.state.Lock() 88 defer s.state.Unlock() 89 argvIn := []string{"/bin/echo", "hello"} 90 tasks := cmdstate.ExecWithTimeout(s.state, "this is the summary", argvIn, time.Second/10).Tasks() 91 c.Assert(tasks, check.HasLen, 1) 92 task := tasks[0] 93 c.Check(task.Kind(), check.Equals, "exec-command") 94 95 var argvOut []string 96 c.Check(task.Get("argv", &argvOut), check.IsNil) 97 c.Check(argvOut, check.DeepEquals, argvIn) 98 } 99 100 func (s *cmdSuite) TestExecHappy(c *check.C) { 101 s.state.Lock() 102 defer s.state.Unlock() 103 104 fn := filepath.Join(s.rootdir, "flag") 105 ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"touch", fn}, time.Second/10) 106 chg := s.state.NewChange("do-the-thing", "Doing the thing") 107 chg.AddAll(ts) 108 109 s.waitfor(chg) 110 111 c.Check(osutil.FileExists(fn), check.Equals, true) 112 c.Check(chg.Status(), check.Equals, state.DoneStatus) 113 } 114 115 func (s *cmdSuite) TestExecIgnore(c *check.C) { 116 s.state.Lock() 117 defer s.state.Unlock() 118 119 fn := filepath.Join(s.rootdir, "flag") 120 ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"touch", fn}, time.Second/10) 121 c.Assert(ts.Tasks(), check.HasLen, 1) 122 ignore := true 123 ts.Tasks()[0].Set("ignore", ignore) 124 125 chg := s.state.NewChange("do-the-thing", "Doing the thing") 126 chg.AddAll(ts) 127 128 s.waitfor(chg) 129 130 // file not created 131 c.Check(osutil.FileExists(fn), check.Equals, false) 132 c.Check(chg.Status(), check.Equals, state.DoneStatus) 133 134 c.Check(strings.Join(ts.Tasks()[0].Log(), ""), check.Matches, `.*task ignored`) 135 } 136 137 func (s *cmdSuite) TestExecSad(c *check.C) { 138 s.state.Lock() 139 defer s.state.Unlock() 140 141 ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sh", "-c", "echo hello; false"}, time.Second/10) 142 chg := s.state.NewChange("do-the-thing", "Doing the thing") 143 chg.AddAll(ts) 144 145 s.waitfor(chg) 146 147 c.Check(chg.Status(), check.Equals, state.ErrorStatus) 148 } 149 150 func (s *cmdSuite) TestExecAbort(c *check.C) { 151 s.state.Lock() 152 defer s.state.Unlock() 153 154 ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sleep", "1h"}, time.Second/10) 155 chg := s.state.NewChange("do-the-thing", "Doing the thing") 156 chg.AddAll(ts) 157 158 s.state.Unlock() 159 s.se.Ensure() 160 s.state.Lock() 161 162 c.Assert(chg.Status(), check.Equals, state.DoingStatus) 163 164 chg.Abort() 165 166 s.waitfor(chg) 167 168 c.Check(chg.Status(), check.Equals, state.ErrorStatus) 169 c.Check(strings.Join(chg.Tasks()[0].Log(), "\n"), check.Matches, `(?s).*ERROR aborted`) 170 } 171 172 func (s *cmdSuite) TestExecStop(c *check.C) { 173 s.state.Lock() 174 defer s.state.Unlock() 175 176 ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sleep", "1h"}, time.Second/10) 177 chg := s.state.NewChange("do-the-thing", "Doing the thing") 178 chg.AddAll(ts) 179 180 c.Assert(chg.Status(), check.Equals, state.DoStatus) 181 182 s.state.Unlock() 183 s.se.Stop() 184 s.state.Lock() 185 186 c.Check(chg.Status(), check.Equals, state.DoStatus) 187 chg.Abort() 188 } 189 190 func (s *cmdSuite) TestExecTimesOut(c *check.C) { 191 s.state.Lock() 192 defer s.state.Unlock() 193 194 ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sleep", "1m"}, time.Second/10) 195 chg := s.state.NewChange("do-the-thing", "Doing the thing") 196 chg.AddAll(ts) 197 198 s.waitfor(chg) 199 200 c.Check(chg.Status(), check.Equals, state.ErrorStatus) 201 c.Check(strings.Join(chg.Tasks()[0].Log(), "\n"), check.Matches, `(?s).*ERROR exceeded maximum runtime.*`) 202 } 203 204 func (s *cmdSuite) TestExecTimeoutMissing(c *check.C) { 205 s.state.Lock() 206 defer s.state.Unlock() 207 208 restore := cmdstate.MockDefaultExecTimeout(1 * time.Second) 209 defer restore() 210 211 ts := cmdstate.ExecWithTimeout(s.state, "Doing the thing", []string{"sleep", "0.3"}, time.Second/10) 212 c.Assert(len(ts.Tasks()), check.Equals, 1) 213 t := ts.Tasks()[0] 214 // no timeout means the default timeout will be used 215 t.Clear("timeout") 216 chg := s.state.NewChange("do-the-thing", "Doing the thing") 217 chg.AddAll(ts) 218 219 s.waitfor(chg) 220 221 // slept for 222 c.Check(chg.Status(), check.Equals, state.DoneStatus) 223 }