interfaces/udev: add udev support code #636

Merged
merged 9 commits into from Mar 14, 2016
View
@@ -0,0 +1,41 @@
+// -*- 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 udev
+
+import (
+ "fmt"
+ "os/exec"
+)
+
+// ReloadRules runs two commands that reload udev rule database.
+//
+// The commands are: udevadm control --reload-rules
+// udevadm trigger
+func ReloadRules() error {
+ output, err := exec.Command("udevadm", "control", "--reload-rules").CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("cannot reload udev rules: %s\nudev output:\n%s", err, string(output))
+ }
+ output, err = exec.Command("udevadm", "trigger").CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("cannot run udev triggers: %s\nudev output:\n%s", err, string(output))
+ }
+ return nil
+}
@@ -0,0 +1,85 @@
+// -*- 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 udev_test
+
+import (
+ "testing"
+
+ . "gopkg.in/check.v1"
+
+ "github.com/ubuntu-core/snappy/interfaces/udev"
+ "github.com/ubuntu-core/snappy/testutil"
+)
+
+func Test(t *testing.T) {
+ TestingT(t)
+}
+
+type uDevSuite struct{}
+
+var _ = Suite(&uDevSuite{})
+
+// Tests for ReloadRules()
+
+func (s *uDevSuite) TestReloadUDevRulesRunsUDevAdm(c *C) {
+ cmd := testutil.MockCommand(c, "udevadm", "")
+ defer cmd.Restore()
+ err := udev.ReloadRules()
+ c.Assert(err, IsNil)
+ c.Assert(cmd.Calls(), DeepEquals, []string{
+ "control --reload-rules",
+ "trigger",
+ })
+}
+
+func (s *uDevSuite) TestReloadUDevRulesReportsErrorsFromReloadRules(c *C) {
+ cmd := testutil.MockCommand(c, "udevadm", `
+if [ "$1" = "control" ]; then
+ echo "failure 1"
+ exit 1
+fi
+ `)
+ defer cmd.Restore()
+ err := udev.ReloadRules()
+ c.Assert(err.Error(), Equals, ""+
+ "cannot reload udev rules: exit status 1\n"+
+ "udev output:\n"+
+ "failure 1\n")
+ c.Assert(cmd.Calls(), DeepEquals, []string{"control --reload-rules"})
+}
+
+func (s *uDevSuite) TestReloadUDevRulesReportsErrorsFromTrigger(c *C) {
+ cmd := testutil.MockCommand(c, "udevadm", `
+if [ "$1" = "trigger" ]; then
+ echo "failure 2"
+ exit 2
+fi
+ `)
+ defer cmd.Restore()
+ err := udev.ReloadRules()
+ c.Assert(err.Error(), Equals, ""+
+ "cannot run udev triggers: exit status 2\n"+
+ "udev output:\n"+
+ "failure 2\n")
+ c.Assert(cmd.Calls(), DeepEquals, []string{
+ "control --reload-rules",
+ "trigger",
+ })
+}
View
@@ -20,7 +20,6 @@
package testutil
import (
- "bytes"
"fmt"
"io/ioutil"
"os"
@@ -32,72 +31,30 @@ import (
// MockCmd allows mocking commands for testing.
type MockCmd struct {
- binDir string
- exeFile string
- logFile string
- countFile string
+ binDir string
+ exeFile string
+ logFile string
}
// MockCommand adds a mocked command to PATH.
//
-// The command logs all invocations to a dedicated log file and exits with the
-// specified code.
-func MockCommand(c *check.C, basename string, status int) *MockCmd {
+// The command logs all invocations to a dedicated log file. If script is
+// non-empty then it is used as is and the caller is responsible for how the
+// script behaves (exit code and any extra behavior). If script is empty then
+// the command exits successfully without any other side-effect.
+func MockCommand(c *check.C, basename, script string) *MockCmd {
binDir := c.MkDir()
exeFile := path.Join(binDir, basename)
logFile := path.Join(binDir, basename+".log")
- countFile := path.Join(binDir, basename+".cnt")
err := ioutil.WriteFile(exeFile, []byte(fmt.Sprintf(""+
"#!/bin/sh\n"+
"echo \"$@\" >> %q\n"+
- "exit %d\n", logFile, status)), 0700)
+ "%s\n", logFile, script)), 0700)
if err != nil {
panic(err)
}
os.Setenv("PATH", binDir+":"+os.Getenv("PATH"))
- return &MockCmd{binDir: binDir, exeFile: exeFile, logFile: logFile, countFile: countFile}
-}
-
-// SetDynamicBehavior sets dynamic behavior, depending on the number of times
-// the command is invoked.
-//
-// The first argument defines the number of dynamic cases. The second argument
-// is a function that is called exactly n times (from 0 to n - 1). Each time
-// the function returns a message that is printed to standard output and the
-// exit code to return in that case.
-//
-// If the command is called more than the expected number of times a diagnostic
-// message is printed to stderr and the command fails with code 1.
-func (cmd *MockCmd) SetDynamicBehavior(n int, fn func(int) (string, int)) {
- buf := bytes.NewBuffer(nil)
- buf.WriteString(fmt.Sprintf(
- `#!/bin/sh
-echo "$@" >> %q
-case $(cat %q) in
-`, cmd.logFile, cmd.countFile))
- for i := 0; i < n; i++ {
- msg, status := fn(i)
- buf.WriteString(fmt.Sprintf(
- ` %d)
- echo %d > %q
- echo %q
- exit %d
- ;;
-`, i, i+1, cmd.countFile, msg, status))
- }
- buf.WriteString(
- `esac
-echo "reached beyond dynamic behavior range!" >&2
-exit 1
-`)
- err := ioutil.WriteFile(cmd.exeFile, buf.Bytes(), 0700)
- if err != nil {
- panic(err)
- }
- err = ioutil.WriteFile(cmd.countFile, []byte("0"), 0600)
- if err != nil {
- panic(err)
- }
+ return &MockCmd{binDir: binDir, exeFile: exeFile, logFile: logFile}
}
// Restore removes the mocked command from PATH