func (*DebugHooksClientSuite) TestClientScript(c *C) { ctx := debug.NewHooksContext("foo/8") // Test the variable substitutions. result := debug.ClientScript(ctx, nil) // No variables left behind. c.Assert(result, Matches, "[^{}]*") // tmux new-session -d -s {unit_name} c.Assert(result, Matches, fmt.Sprintf("(.|\n)*tmux new-session -s %s(.|\n)*", regexp.QuoteMeta(ctx.Unit))) //) 9>{exit_flock} c.Assert(result, Matches, fmt.Sprintf("(.|\n)*\\) 9>%s(.|\n)*", regexp.QuoteMeta(ctx.ClientExitFileLock()))) //) 8>{entry_flock} c.Assert(result, Matches, fmt.Sprintf("(.|\n)*\\) 8>%s(.|\n)*", regexp.QuoteMeta(ctx.ClientFileLock()))) // nil is the same as empty slice is the same as "*". // Also, if "*" is present as well as a named hook, // it is equivalent to "*". c.Assert(debug.ClientScript(ctx, nil), Equals, debug.ClientScript(ctx, []string{})) c.Assert(debug.ClientScript(ctx, []string{"*"}), Equals, debug.ClientScript(ctx, nil)) c.Assert(debug.ClientScript(ctx, []string{"*", "something"}), Equals, debug.ClientScript(ctx, []string{"*"})) // debug.ClientScript does not validate hook names, as it doesn't have // a full state API connection to determine valid relation hooks. expected := fmt.Sprintf( `(.|\n)*echo "aG9va3M6Ci0gc29tZXRoaW5nIHNvbWV0aGluZ2Vsc2UK" | base64 -d > %s(.|\n)*`, regexp.QuoteMeta(ctx.ClientFileLock()), ) c.Assert(debug.ClientScript(ctx, []string{"something somethingelse"}), Matches, expected) }
// RunHook executes a hook in an environment which allows it to to call back // into ctx to execute jujuc tools. func (ctx *HookContext) RunHook(hookName, charmDir, toolsDir, socketPath string) error { var err error env := ctx.hookVars(charmDir, toolsDir, socketPath) debugctx := unitdebug.NewHooksContext(ctx.unit.Name()) if session, _ := debugctx.FindSession(); session != nil && session.MatchHook(hookName) { logger.Infof("executing %s via debug-hooks", hookName) err = session.RunHook(hookName, charmDir, env) } else { err = runCharmHook(hookName, charmDir, env) } write := err == nil for id, rctx := range ctx.relations { if write { if e := rctx.WriteSettings(); e != nil { e = fmt.Errorf( "could not write settings from %q to relation %d: %v", hookName, id, e, ) logger.Errorf("%v", e) if err == nil { err = e } } } rctx.ClearCache() } return err }
// TestCommonScript tests the behaviour of HooksContext. func (*DebugHooksCommonSuite) TestHooksContext(c *C) { ctx := debug.NewHooksContext("foo/8") c.Assert(ctx.Unit, Equals, "foo/8") c.Assert(ctx.FlockDir, Equals, "/tmp") ctx.FlockDir = "/var/lib/juju" c.Assert(ctx.ClientFileLock(), Equals, "/var/lib/juju/juju-unit-foo-8-debug-hooks") c.Assert(ctx.ClientExitFileLock(), Equals, "/var/lib/juju/juju-unit-foo-8-debug-hooks-exit") }
func (s *DebugHooksServerSuite) SetUpTest(c *C) { s.fakebin = c.MkDir() s.tmpdir = c.MkDir() s.setenv("PATH", s.fakebin+":"+os.Getenv("PATH")) s.setenv("TMPDIR", s.tmpdir) s.setenv("TEST_RESULT", "") for _, name := range fakecommands { err := ioutil.WriteFile(filepath.Join(s.fakebin, name), []byte(echocommand), 0777) c.Assert(err, IsNil) } s.ctx = debug.NewHooksContext("foo/8") s.ctx.FlockDir = s.tmpdir s.setenv("JUJU_UNIT_NAME", s.ctx.Unit) }
// Run ensures c.Target is a unit, and resolves its address, // and connects to it via SSH to execute the debug-hooks // script. func (c *DebugHooksCommand) Run(ctx *cmd.Context) error { conn, err := c.initConn() if err != nil { return err } defer conn.Close() unit, err := conn.State.Unit(c.Target) if err != nil { return err } err = c.validateHooks(unit) if err != nil { return err } debugctx := unitdebug.NewHooksContext(c.Target) script := base64.StdEncoding.EncodeToString([]byte(unitdebug.ClientScript(debugctx, c.hooks))) innercmd := fmt.Sprintf(`F=$(mktemp); echo %s | base64 -d > $F; . $F`, script) args := []string{"--", fmt.Sprintf("sudo /bin/bash -c '%s'", innercmd)} c.Args = args return c.SSHCommand.Run(ctx) }