summaryrefslogtreecommitdiff
diff options
authorMichael Vogt <mvo@ubuntu.com>2016-11-16 18:02:13 +0100
committerMichael Vogt <mvo@ubuntu.com>2016-11-16 18:18:03 +0100
commit92f99f59d3c60f2c41c2d3729f2b5f99aa0bf6bf (patch)
treefccb8fc231b19e7651ecbb3f61fe8431c62e9ddf
parentc42d61c4132145ae08ea23fc91f8926c2aece4cc (diff)
initial health checks draftfeature/simple-health-checks
-rw-r--r--overlord/snapstate/backend.go3
-rw-r--r--overlord/snapstate/backend/health.go43
-rw-r--r--overlord/snapstate/backend_test.go11
-rw-r--r--overlord/snapstate/snapmgr.go16
-rw-r--r--overlord/snapstate/snapmgr_test.go11
-rw-r--r--overlord/snapstate/snapstate.go6
-rwxr-xr-xtests/lib/snaps/test-snapd-service-v3-bad-health/bin/good3
-rwxr-xr-xtests/lib/snaps/test-snapd-service-v3-bad-health/meta/checks/static-check4
-rw-r--r--tests/lib/snaps/test-snapd-service-v3-bad-health/meta/snap.yaml7
-rw-r--r--tests/main/health-check-static/task.yaml45
10 files changed, 145 insertions, 4 deletions
diff --git a/overlord/snapstate/backend.go b/overlord/snapstate/backend.go
index c67b2e04ef..4916806d61 100644
--- a/overlord/snapstate/backend.go
+++ b/overlord/snapstate/backend.go
@@ -63,6 +63,9 @@ type managerBackend interface {
RemoveSnapCommonData(info *snap.Info) error
DiscardSnapNamespace(snapName string) error
+ // health check
+ HealthCheckStatic(name string, rev snap.Revision) error
+
// testing helpers
CurrentInfo(cur *snap.Info)
Candidate(sideInfo *snap.SideInfo)
diff --git a/overlord/snapstate/backend/health.go b/overlord/snapstate/backend/health.go
new file mode 100644
index 0000000000..490ad97449
--- /dev/null
+++ b/overlord/snapstate/backend/health.go
@@ -0,0 +1,43 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2016 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package backend
+
+import (
+ "fmt"
+ "os/exec"
+ "path/filepath"
+
+ "github.com/snapcore/snapd/osutil"
+ "github.com/snapcore/snapd/snap"
+)
+
+func (b Backend) HealthCheckStatic(snapName string, rev snap.Revision) error {
+ checker := filepath.Join(snap.MountDir(snapName, rev), "meta/checks/static-check")
+ if osutil.FileExists(checker) {
+ // FIXME: this is curently unconfined, we need
+ // `snap run --health-check=static snap-name`
+ output, err := exec.Command(checker).CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("static health check for %q failed: %s", snapName, osutil.OutputErr(output, err))
+ }
+ }
+
+ return nil
+}
diff --git a/overlord/snapstate/backend_test.go b/overlord/snapstate/backend_test.go
index 86849338ed..2b2d8223c4 100644
--- a/overlord/snapstate/backend_test.go
+++ b/overlord/snapstate/backend_test.go
@@ -418,6 +418,17 @@ func (f *fakeSnappyBackend) DiscardSnapNamespace(snapName string) error {
return nil
}
+func (f *fakeSnappyBackend) HealthCheckStatic(snapName string, rev snap.Revision) error {
+ // FIXME: update all tests
+ /*
+ f.ops = append(f.ops, fakeOp{
+ op: "health-check-static",
+ name: snapName,
+ })
+ */
+ return nil
+}
+
func (f *fakeSnappyBackend) Candidate(sideInfo *snap.SideInfo) {
var sinfo snap.SideInfo
if sideInfo != nil {
diff --git a/overlord/snapstate/snapmgr.go b/overlord/snapstate/snapmgr.go
index 434655ccf6..6efc568078 100644
--- a/overlord/snapstate/snapmgr.go
+++ b/overlord/snapstate/snapmgr.go
@@ -315,6 +315,9 @@ func Manager(s *state.State) (*SnapManager, error) {
runner.AddHandler("clear-snap", m.doClearSnapData, nil)
runner.AddHandler("discard-snap", m.doDiscardSnap, nil)
+ // health check
+ runner.AddHandler("health-check-static", m.doHealthCheckStatic, nil)
+
// test handlers
runner.AddHandler("fake-install-snap", func(t *state.Task, _ *tomb.Tomb) error {
return nil
@@ -562,6 +565,19 @@ func (m *SnapManager) doDiscardSnap(t *state.Task, _ *tomb.Tomb) error {
return nil
}
+func (m *SnapManager) doHealthCheckStatic(t *state.Task, _ *tomb.Tomb) error {
+ st := t.State()
+
+ st.Lock()
+ ss, err := TaskSnapSetup(t)
+ st.Unlock()
+ if err != nil {
+ return err
+ }
+
+ return m.backend.HealthCheckStatic(ss.Name(), ss.Revision())
+}
+
// Ensure implements StateManager.Ensure.
func (m *SnapManager) Ensure() error {
m.runner.Ensure()
diff --git a/overlord/snapstate/snapmgr_test.go b/overlord/snapstate/snapmgr_test.go
index ed514a8ab2..1470fe364f 100644
--- a/overlord/snapstate/snapmgr_test.go
+++ b/overlord/snapstate/snapmgr_test.go
@@ -163,6 +163,7 @@ func verifyInstallUpdateTasks(c *C, opts, discards int, ts *state.TaskSet, st *s
}
expected = append(expected,
"run-hook",
+ "health-check-static",
)
c.Assert(kinds, DeepEquals, expected)
@@ -214,6 +215,7 @@ func (s *snapmgrTestSuite) TestRevertTasks(c *C) {
"link-snap",
"start-snap-services",
"run-hook",
+ "health-check-static",
})
}
@@ -424,6 +426,7 @@ func (s *snapmgrTestSuite) TestRevertCreatesNoGCTasks(c *C) {
"link-snap",
"start-snap-services",
"run-hook",
+ "health-check-static",
})
}
@@ -881,9 +884,9 @@ func (s *snapmgrTestSuite) TestInstallRunThrough(c *C) {
c.Check(task.Summary(), Equals, `Download snap "some-snap" (42) from channel "some-channel"`)
// check link/start snap summary
- linkTask := ta[len(ta)-3]
+ linkTask := ta[len(ta)-4]
c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (42) available to the system`)
- startTask := ta[len(ta)-2]
+ startTask := ta[len(ta)-3]
c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (42) services`)
// verify snap-setup in the task state
@@ -3898,13 +3901,13 @@ func (s *snapmgrTestSuite) TestUpdateTasksWithOldCurrent(c *C) {
var ss snapstate.SnapSetup
tasks := ts.Tasks()
- i := len(tasks) - 6
+ i := len(tasks) - 7
c.Check(tasks[i].Kind(), Equals, "clear-snap")
err = tasks[i].Get("snap-setup", &ss)
c.Assert(err, IsNil)
c.Check(ss.Revision(), Equals, si3.Revision)
- i = len(tasks) - 4
+ i = len(tasks) - 5
c.Check(tasks[i].Kind(), Equals, "clear-snap")
err = tasks[i].Get("snap-setup", &ss)
c.Assert(err, IsNil)
diff --git a/overlord/snapstate/snapstate.go b/overlord/snapstate/snapstate.go
index 2a23f348b4..4af6ea3a1d 100644
--- a/overlord/snapstate/snapstate.go
+++ b/overlord/snapstate/snapstate.go
@@ -179,6 +179,12 @@ func doInstall(s *state.State, snapst *SnapState, ss *SnapSetup) (*state.TaskSet
configSet.WaitAll(installSet)
installSet.AddAll(configSet)
+ // FIXME: move to a separate pkg?
+ healthCheck := s.NewTask("health-check-static", fmt.Sprintf("Static health check for %q", ss.Name()))
+ healthCheck.Set("snap-setup-task", prepare.ID())
+ healthCheck.WaitAll(configSet)
+ installSet.AddAll(state.NewTaskSet(healthCheck))
+
return installSet, nil
}
diff --git a/tests/lib/snaps/test-snapd-service-v3-bad-health/bin/good b/tests/lib/snaps/test-snapd-service-v3-bad-health/bin/good
new file mode 100755
index 0000000000..d07a9f9584
--- /dev/null
+++ b/tests/lib/snaps/test-snapd-service-v3-bad-health/bin/good
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "service v1"
diff --git a/tests/lib/snaps/test-snapd-service-v3-bad-health/meta/checks/static-check b/tests/lib/snaps/test-snapd-service-v3-bad-health/meta/checks/static-check
new file mode 100755
index 0000000000..759dbfd2d3
--- /dev/null
+++ b/tests/lib/snaps/test-snapd-service-v3-bad-health/meta/checks/static-check
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo "I don't feel well"
+exit 1
diff --git a/tests/lib/snaps/test-snapd-service-v3-bad-health/meta/snap.yaml b/tests/lib/snaps/test-snapd-service-v3-bad-health/meta/snap.yaml
new file mode 100644
index 0000000000..3a93f5574d
--- /dev/null
+++ b/tests/lib/snaps/test-snapd-service-v3-bad-health/meta/snap.yaml
@@ -0,0 +1,7 @@
+name: test-snapd-service
+version: 3.0
+apps:
+ service:
+ command: bin/good
+ daemon: oneshot
+ restart-condition: never
diff --git a/tests/main/health-check-static/task.yaml b/tests/main/health-check-static/task.yaml
new file mode 100644
index 0000000000..89bdedf52f
--- /dev/null
+++ b/tests/main/health-check-static/task.yaml
@@ -0,0 +1,45 @@
+summary: Run static health check
+details: |
+ When a snap is refreshed and the health check fails, the snap in reverted
+
+environment:
+ SNAP_NAME: test-snapd-service
+ SNAP_NAME_GOOD: ${SNAP_NAME}-v1-good
+ SNAP_NAME_BAD: ${SNAP_NAME}-v3-bad-health
+ SNAP_FILE_GOOD: ${SNAP_NAME}_1.0_all.snap
+ SNAP_FILE_BAD: ${SNAP_NAME}_3.0_all.snap
+
+prepare: |
+ echo "Given a good (v1) and a bad health check (v3) snap"
+ snapbuild $TESTSLIB/snaps/$SNAP_NAME_GOOD .
+ snapbuild $TESTSLIB/snaps/$SNAP_NAME_BAD .
+
+debug: |
+ journalctl -u snap.test-snapd-service.service.service
+
+execute: |
+ wait_for_service_status() {
+ retries=0
+ while ! systemctl status snap.test-snapd-service.service.service|grep "$1"; do
+ # retry
+ retries=$((retries+1))
+ if [ $retries -gt 20 ]; then
+ echo 'expected "service v1" output did not appear in systemctl status snap.test-snapd-service.service.service'
+ exit 1
+ fi
+ sleep 1
+ done
+ }
+ echo "When we install v1"
+ snap install --dangerous ${SNAP_FILE_GOOD}
+ echo "The v1 service started correctly"
+ wait_for_service_status "Started Service for snap application test-snapd-service.service"
+
+ echo "When we refresh to v3 with a broken health check"
+ if snap install --dangerous ${SNAP_FILE_BAD}; then
+ echo "The ${SNAP_FILE_BAD} snap should not install cleanly, test broken"
+ exit 1
+ fi
+ echo "Then v3 is rolled back and v1 is started again"
+ wait_for_service_status "Started Service for snap application test-snapd-service.service"
+ wait_for_service_status "service v1"