github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap-failure/cmd_snapd_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 main_test 21 22 import ( 23 "encoding/json" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "time" 28 29 . "gopkg.in/check.v1" 30 31 failure "github.com/snapcore/snapd/cmd/snap-failure" 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/osutil" 34 "github.com/snapcore/snapd/snap" 35 "github.com/snapcore/snapd/testutil" 36 ) 37 38 func (r *failureSuite) TestRun(c *C) { 39 origArgs := os.Args 40 defer func() { os.Args = origArgs }() 41 os.Args = []string{"snap-failure", "snapd"} 42 err := failure.Run() 43 c.Check(err, IsNil) 44 c.Check(r.Stderr(), HasLen, 0) 45 } 46 47 func writeSeqFile(c *C, name string, current snap.Revision, seq []*snap.SideInfo) { 48 seqPath := filepath.Join(dirs.SnapSeqDir, name+".json") 49 50 err := os.MkdirAll(dirs.SnapSeqDir, 0755) 51 c.Assert(err, IsNil) 52 53 b, err := json.Marshal(&struct { 54 Sequence []*snap.SideInfo `json:"sequence"` 55 Current string `json:"current"` 56 }{ 57 Sequence: seq, 58 Current: current.String(), 59 }) 60 c.Assert(err, IsNil) 61 62 err = ioutil.WriteFile(seqPath, b, 0644) 63 c.Assert(err, IsNil) 64 } 65 66 func (r *failureSuite) TestCallPrevSnapdFromSnap(c *C) { 67 origArgs := os.Args 68 defer func() { os.Args = origArgs }() 69 70 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 71 {Revision: snap.R(99)}, 72 {Revision: snap.R(100)}, 73 {Revision: snap.R(123)}, 74 }) 75 76 // mock snapd command from 'previous' revision 77 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), 78 `test "$SNAPD_REVERT_TO_REV" = "100"`) 79 defer snapdCmd.Restore() 80 81 systemctlCmd := testutil.MockCommand(c, "systemctl", "") 82 defer systemctlCmd.Restore() 83 84 os.Args = []string{"snap-failure", "snapd"} 85 err := failure.Run() 86 c.Check(err, IsNil) 87 c.Check(r.Stderr(), HasLen, 0) 88 89 c.Check(snapdCmd.Calls(), DeepEquals, [][]string{ 90 {"snapd"}, 91 }) 92 c.Check(systemctlCmd.Calls(), DeepEquals, [][]string{ 93 {"systemctl", "stop", "snapd.socket"}, 94 {"systemctl", "is-failed", "snapd.socket", "snapd.service"}, 95 {"systemctl", "reset-failed", "snapd.socket", "snapd.service"}, 96 {"systemctl", "restart", "snapd.socket"}, 97 }) 98 } 99 100 func (r *failureSuite) TestCallPrevSnapdFromSnapRestartSnapdFallback(c *C) { 101 defer failure.MockWaitTimes(1*time.Millisecond, 1*time.Millisecond)() 102 103 origArgs := os.Args 104 defer func() { os.Args = origArgs }() 105 106 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 107 {Revision: snap.R(99)}, 108 {Revision: snap.R(100)}, 109 {Revision: snap.R(123)}, 110 }) 111 112 // mock snapd command from 'previous' revision 113 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), 114 `test "$SNAPD_REVERT_TO_REV" = "100"`) 115 defer snapdCmd.Restore() 116 117 systemctlCmd := testutil.MockCommand(c, "systemctl", ` 118 if [ "$1" = restart ] && [ "$2" == snapd.socket ] ; then 119 exit 1 120 fi 121 `) 122 defer systemctlCmd.Restore() 123 124 os.Args = []string{"snap-failure", "snapd"} 125 err := failure.Run() 126 c.Check(err, IsNil) 127 c.Check(r.Stderr(), HasLen, 0) 128 129 c.Check(snapdCmd.Calls(), DeepEquals, [][]string{ 130 {"snapd"}, 131 }) 132 c.Check(systemctlCmd.Calls(), DeepEquals, [][]string{ 133 {"systemctl", "stop", "snapd.socket"}, 134 {"systemctl", "is-failed", "snapd.socket", "snapd.service"}, 135 {"systemctl", "reset-failed", "snapd.socket", "snapd.service"}, 136 {"systemctl", "restart", "snapd.socket"}, 137 {"systemctl", "restart", "snapd.service"}, 138 }) 139 } 140 141 func (r *failureSuite) TestCallPrevSnapdFromSnapBackToFullyActive(c *C) { 142 defer failure.MockWaitTimes(1*time.Millisecond, 0)() 143 144 origArgs := os.Args 145 defer func() { os.Args = origArgs }() 146 147 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 148 {Revision: snap.R(99)}, 149 {Revision: snap.R(100)}, 150 {Revision: snap.R(123)}, 151 }) 152 153 // mock snapd command from 'previous' revision 154 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), 155 `test "$SNAPD_REVERT_TO_REV" = "100"`) 156 defer snapdCmd.Restore() 157 158 systemctlCmd := testutil.MockCommand(c, "systemctl", ` 159 if [ "$1" = is-failed ] ; then 160 exit 1 161 fi 162 `) 163 defer systemctlCmd.Restore() 164 165 // mock the sockets re-appearing 166 err := os.MkdirAll(filepath.Dir(dirs.SnapdSocket), 0755) 167 c.Assert(err, IsNil) 168 err = ioutil.WriteFile(dirs.SnapdSocket, nil, 0755) 169 c.Assert(err, IsNil) 170 err = ioutil.WriteFile(dirs.SnapSocket, nil, 0755) 171 c.Assert(err, IsNil) 172 173 os.Args = []string{"snap-failure", "snapd"} 174 err = failure.Run() 175 c.Check(err, IsNil) 176 c.Check(r.Stderr(), HasLen, 0) 177 178 c.Check(snapdCmd.Calls(), DeepEquals, [][]string{ 179 {"snapd"}, 180 }) 181 c.Check(systemctlCmd.Calls(), DeepEquals, [][]string{ 182 {"systemctl", "stop", "snapd.socket"}, 183 {"systemctl", "is-failed", "snapd.socket", "snapd.service"}, 184 {"systemctl", "is-active", "snapd.socket", "snapd.service"}, 185 }) 186 } 187 188 func (r *failureSuite) TestCallPrevSnapdFromSnapBackActiveNoSockets(c *C) { 189 defer failure.MockWaitTimes(1*time.Millisecond, 0)() 190 191 origArgs := os.Args 192 defer func() { os.Args = origArgs }() 193 194 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 195 {Revision: snap.R(99)}, 196 {Revision: snap.R(100)}, 197 {Revision: snap.R(123)}, 198 }) 199 200 // mock snapd command from 'previous' revision 201 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), 202 `test "$SNAPD_REVERT_TO_REV" = "100"`) 203 defer snapdCmd.Restore() 204 205 systemctlCmd := testutil.MockCommand(c, "systemctl", ` 206 if [ "$1" = is-failed ] ; then 207 exit 1 208 fi 209 `) 210 defer systemctlCmd.Restore() 211 212 // no sockets 213 214 os.Args = []string{"snap-failure", "snapd"} 215 err := failure.Run() 216 c.Check(err, IsNil) 217 c.Check(r.Stderr(), HasLen, 0) 218 219 c.Check(snapdCmd.Calls(), DeepEquals, [][]string{ 220 {"snapd"}, 221 }) 222 c.Check(systemctlCmd.Calls(), DeepEquals, [][]string{ 223 {"systemctl", "stop", "snapd.socket"}, 224 {"systemctl", "is-failed", "snapd.socket", "snapd.service"}, 225 {"systemctl", "is-active", "snapd.socket", "snapd.service"}, 226 {"systemctl", "is-active", "snapd.socket", "snapd.service"}, 227 {"systemctl", "is-active", "snapd.socket", "snapd.service"}, 228 {"systemctl", "is-active", "snapd.socket", "snapd.service"}, 229 {"systemctl", "is-active", "snapd.socket", "snapd.service"}, 230 {"systemctl", "reset-failed", "snapd.socket", "snapd.service"}, 231 {"systemctl", "restart", "snapd.socket"}, 232 }) 233 } 234 235 func (r *failureSuite) TestCallPrevSnapdFromCore(c *C) { 236 origArgs := os.Args 237 defer func() { os.Args = origArgs }() 238 239 // only one entry in sequence 240 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 241 {Revision: snap.R(123)}, 242 }) 243 244 // mock snapd in the core snap 245 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "core", "current", "/usr/lib/snapd/snapd"), 246 `test "$SNAPD_REVERT_TO_REV" = "0"`) 247 defer snapdCmd.Restore() 248 249 systemctlCmd := testutil.MockCommand(c, "systemctl", "") 250 defer systemctlCmd.Restore() 251 252 os.Args = []string{"snap-failure", "snapd"} 253 err := failure.Run() 254 c.Check(err, IsNil) 255 c.Check(r.Stderr(), HasLen, 0) 256 257 c.Check(snapdCmd.Calls(), DeepEquals, [][]string{ 258 {"snapd"}, 259 }) 260 c.Check(systemctlCmd.Calls(), DeepEquals, [][]string{ 261 {"systemctl", "stop", "snapd.socket"}, 262 {"systemctl", "is-failed", "snapd.socket", "snapd.service"}, 263 {"systemctl", "reset-failed", "snapd.socket", "snapd.service"}, 264 {"systemctl", "restart", "snapd.socket"}, 265 }) 266 } 267 268 func (r *failureSuite) TestCallPrevSnapdFail(c *C) { 269 origArgs := os.Args 270 defer func() { os.Args = origArgs }() 271 272 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 273 {Revision: snap.R(100)}, 274 {Revision: snap.R(123)}, 275 }) 276 277 // mock snapd in the core snap 278 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), 279 `exit 2`) 280 defer snapdCmd.Restore() 281 282 systemctlCmd := testutil.MockCommand(c, "systemctl", "") 283 defer systemctlCmd.Restore() 284 285 os.Args = []string{"snap-failure", "snapd"} 286 err := failure.Run() 287 c.Check(err, ErrorMatches, "snapd failed: exit status 2") 288 c.Check(r.Stderr(), HasLen, 0) 289 290 c.Check(snapdCmd.Calls(), DeepEquals, [][]string{ 291 {"snapd"}, 292 }) 293 c.Check(systemctlCmd.Calls(), DeepEquals, [][]string{ 294 {"systemctl", "stop", "snapd.socket"}, 295 }) 296 } 297 298 func (r *failureSuite) TestGarbageSeq(c *C) { 299 origArgs := os.Args 300 defer func() { os.Args = origArgs }() 301 302 seqPath := filepath.Join(dirs.SnapSeqDir, "snapd.json") 303 err := os.MkdirAll(dirs.SnapSeqDir, 0755) 304 c.Assert(err, IsNil) 305 306 err = ioutil.WriteFile(seqPath, []byte("this is garbage"), 0644) 307 c.Assert(err, IsNil) 308 309 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), 310 `exit 99`) 311 defer snapdCmd.Restore() 312 313 systemctlCmd := testutil.MockCommand(c, "systemctl", "exit 98") 314 defer systemctlCmd.Restore() 315 316 os.Args = []string{"snap-failure", "snapd"} 317 err = failure.Run() 318 c.Check(err, ErrorMatches, `cannot parse "snapd.json" sequence file: invalid .*`) 319 c.Check(r.Stderr(), HasLen, 0) 320 321 c.Check(snapdCmd.Calls(), HasLen, 0) 322 c.Check(systemctlCmd.Calls(), HasLen, 0) 323 } 324 325 func (r *failureSuite) TestBadSeq(c *C) { 326 origArgs := os.Args 327 defer func() { os.Args = origArgs }() 328 329 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 330 {Revision: snap.R(100)}, 331 // current not in sequence 332 }) 333 334 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), "") 335 defer snapdCmd.Restore() 336 systemctlCmd := testutil.MockCommand(c, "systemctl", "") 337 defer systemctlCmd.Restore() 338 339 os.Args = []string{"snap-failure", "snapd"} 340 err := failure.Run() 341 c.Check(err, ErrorMatches, "internal error: current 123 not found in sequence: .*Revision:100.*") 342 c.Check(r.Stderr(), HasLen, 0) 343 344 c.Check(snapdCmd.Calls(), HasLen, 0) 345 c.Check(systemctlCmd.Calls(), HasLen, 0) 346 } 347 348 func (r *failureSuite) TestSnapdOutputPassthrough(c *C) { 349 origArgs := os.Args 350 defer func() { os.Args = origArgs }() 351 352 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 353 {Revision: snap.R(100)}, 354 {Revision: snap.R(123)}, 355 }) 356 357 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), ` 358 echo 'stderr: hello from snapd' >&2 359 echo 'stdout: hello from snapd' 360 exit 123 361 `) 362 defer snapdCmd.Restore() 363 systemctlCmd := testutil.MockCommand(c, "systemctl", "") 364 defer systemctlCmd.Restore() 365 366 os.Args = []string{"snap-failure", "snapd"} 367 err := failure.Run() 368 c.Check(err, ErrorMatches, "snapd failed: exit status 123") 369 c.Check(r.Stderr(), Equals, "stderr: hello from snapd\n") 370 c.Check(r.Stdout(), Equals, "stdout: hello from snapd\n") 371 372 c.Check(snapdCmd.Calls(), HasLen, 1) 373 c.Check(systemctlCmd.Calls(), DeepEquals, [][]string{ 374 {"systemctl", "stop", "snapd.socket"}, 375 }) 376 } 377 378 func (r *failureSuite) TestStickySnapdSocket(c *C) { 379 origArgs := os.Args 380 defer func() { os.Args = origArgs }() 381 382 writeSeqFile(c, "snapd", snap.R(123), []*snap.SideInfo{ 383 {Revision: snap.R(100)}, 384 {Revision: snap.R(123)}, 385 }) 386 387 err := os.MkdirAll(filepath.Dir(dirs.SnapdSocket), 0755) 388 c.Assert(err, IsNil) 389 err = ioutil.WriteFile(dirs.SnapdSocket, []byte{}, 0755) 390 c.Assert(err, IsNil) 391 392 // mock snapd in the core snap 393 snapdCmd := testutil.MockCommand(c, filepath.Join(dirs.SnapMountDir, "snapd", "100", "/usr/lib/snapd/snapd"), 394 `test "$SNAPD_REVERT_TO_REV" = "100"`) 395 defer snapdCmd.Restore() 396 397 systemctlCmd := testutil.MockCommand(c, "systemctl", "") 398 defer systemctlCmd.Restore() 399 400 os.Args = []string{"snap-failure", "snapd"} 401 err = failure.Run() 402 c.Check(err, IsNil) 403 c.Check(r.Stderr(), HasLen, 0) 404 405 c.Check(snapdCmd.Calls(), DeepEquals, [][]string{ 406 {"snapd"}, 407 }) 408 c.Check(systemctlCmd.Calls(), DeepEquals, [][]string{ 409 {"systemctl", "stop", "snapd.socket"}, 410 {"systemctl", "is-failed", "snapd.socket", "snapd.service"}, 411 {"systemctl", "reset-failed", "snapd.socket", "snapd.service"}, 412 {"systemctl", "restart", "snapd.socket"}, 413 }) 414 415 // make sure the socket file was deleted 416 c.Assert(osutil.FileExists(dirs.SnapdSocket), Equals, false) 417 }