Skip to content

Commit

Permalink
cmd/juju: implement resolved
Browse files Browse the repository at this point in the history
R=john.meinel, niemeyer
CC=
https://codereview.appspot.com/6814055
  • Loading branch information
William Reade authored and fwereade committed Oct 31, 2012
2 parents 7f42dfa + 90e5700 commit e9f7146
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/juju/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func Main(args []string) {
juju.Register(&GetCommand{})
juju.Register(&RemoveRelationCommand{})
juju.Register(&RemoveUnitCommand{})
juju.Register(&ResolvedCommand{})
juju.Register(&SetCommand{})
juju.Register(&SCPCommand{})
juju.Register(&SSHCommand{})
Expand Down
1 change: 1 addition & 0 deletions cmd/juju/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ var commandNames = []string{
"get",
"remove-relation",
"remove-unit",
"resolved",
"scp",
"set",
"ssh",
Expand Down
53 changes: 53 additions & 0 deletions cmd/juju/resolved.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"fmt"
"launchpad.net/gnuflag"
"launchpad.net/juju-core/cmd"
"launchpad.net/juju-core/juju"
"launchpad.net/juju-core/state"
)

// ResolvedCommand marks a unit in an error state as ready to continue.
type ResolvedCommand struct {
EnvName string
UnitName string
Retry bool
}

func (c *ResolvedCommand) Info() *cmd.Info {
return &cmd.Info{"resolved", "<unit>", "marks unit errors resolved", ""}
}

func (c *ResolvedCommand) Init(f *gnuflag.FlagSet, args []string) error {
addEnvironFlags(&c.EnvName, f)
f.BoolVar(&c.Retry, "r", false, "re-execute failed hooks")
f.BoolVar(&c.Retry, "retry", false, "")
if err := f.Parse(true, args); err != nil {
return err
}
args = f.Args()
if len(args) > 0 {
c.UnitName = args[0]
if !state.IsUnitName(c.UnitName) {
return fmt.Errorf("invalid unit name %q", c.UnitName)
}
args = args[1:]
} else {
return fmt.Errorf("no unit specified")
}
return cmd.CheckEmpty(args)
}

func (c *ResolvedCommand) Run(_ *cmd.Context) error {
conn, err := juju.NewConnFromName(c.EnvName)
if err != nil {
return err
}
defer conn.Close()
unit, err := conn.State.Unit(c.UnitName)
if err != nil {
return err
}
return conn.Resolved(unit, c.Retry)
}
99 changes: 99 additions & 0 deletions cmd/juju/resolved_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"bytes"
. "launchpad.net/gocheck"
"launchpad.net/juju-core/cmd"
"launchpad.net/juju-core/state"
"launchpad.net/juju-core/testing"
)

type ResolvedSuite struct {
repoSuite
}

var _ = Suite(&ResolvedSuite{})

func runResolved(c *C, args []string) error {
com := &ResolvedCommand{}
if err := com.Init(newFlagSet(), args); err != nil {
return err
}
return com.Run(&cmd.Context{c.MkDir(), &bytes.Buffer{}, &bytes.Buffer{}, &bytes.Buffer{}})
}

var resolvedTests = []struct {
args []string
err string
unit string
mode state.ResolvedMode
}{
{
err: `no unit specified`,
}, {
args: []string{"jeremy-fisher"},
err: `invalid unit name "jeremy-fisher"`,
}, {
args: []string{"jeremy-fisher/99"},
err: `unit "jeremy-fisher/99" not found`,
}, {
args: []string{"dummy/0"},
err: `unit "dummy/0" is not in an error state`,
unit: "dummy/0",
mode: state.ResolvedNone,
}, {
args: []string{"dummy/1", "--retry"},
err: `unit "dummy/1" is not in an error state`,
unit: "dummy/1",
mode: state.ResolvedNone,
}, {
args: []string{"dummy/2"},
unit: "dummy/2",
mode: state.ResolvedNoHooks,
}, {
args: []string{"dummy/2", "--retry"},
err: `cannot set resolved mode for unit "dummy/2": already resolved`,
unit: "dummy/2",
mode: state.ResolvedNoHooks,
}, {
args: []string{"dummy/3", "--retry"},
unit: "dummy/3",
mode: state.ResolvedRetryHooks,
}, {
args: []string{"dummy/3"},
err: `cannot set resolved mode for unit "dummy/3": already resolved`,
unit: "dummy/3",
mode: state.ResolvedRetryHooks,
}, {
args: []string{"dummy/4", "roflcopter"},
err: `unrecognized args: \["roflcopter"\]`,
},
}

func (s *ResolvedSuite) TestResolved(c *C) {
testing.Charms.BundlePath(s.seriesPath, "series", "dummy")
err := runDeploy(c, "-n", "5", "local:dummy", "dummy")
c.Assert(err, IsNil)

for _, name := range []string{"dummy/2", "dummy/3", "dummy/4"} {
u, err := s.State.Unit(name)
c.Assert(err, IsNil)
err = u.SetStatus(state.UnitError, "lol borken")
c.Assert(err, IsNil)
}

for i, t := range resolvedTests {
c.Logf("test %d: %v", i, t.args)
err := runResolved(c, t.args)
if t.err != "" {
c.Assert(err, ErrorMatches, t.err)
} else {
c.Assert(err, IsNil)
}
if t.unit != "" {
unit, err := s.State.Unit(t.unit)
c.Assert(err, IsNil)
c.Assert(unit.Resolved(), Equals, t.mode)
}
}
}
20 changes: 20 additions & 0 deletions juju/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,23 @@ func (conn *Conn) DestroyUnits(units ...*state.Unit) error {
}
return nil
}

// Resolved marks the unit as having had any previous state transition
// problems resolved, and informs the unit that it may attempt to
// reestablish normal workflow. The retryHooks parameter informs
// whether to attempt to reexecute previous failed hooks or to continue
// as if they had succeeded before.
func (conn *Conn) Resolved(unit *state.Unit, retryHooks bool) error {
status, _, err := unit.Status()
if err != nil {
return err
}
if status != state.UnitError {
return fmt.Errorf("unit %q is not in an error state", unit)
}
mode := state.ResolvedNoHooks
if retryHooks {
mode = state.ResolvedRetryHooks
}
return unit.SetResolved(mode)
}
32 changes: 32 additions & 0 deletions juju/conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,3 +435,35 @@ func (s *ConnSuite) TestDestroyUnits(c *C) {
c.Assert(u.Life(), Equals, state.Dying)
}
}

func (s *ConnSuite) TestResolved(c *C) {
curl := coretesting.Charms.ClonedURL(s.repo.Path, "series", "riak")
sch, err := s.conn.PutCharm(curl, s.repo, false)
c.Assert(err, IsNil)
svc, err := s.conn.AddService("testriak", sch)
c.Assert(err, IsNil)
us, err := s.conn.AddUnits(svc, 1)
c.Assert(err, IsNil)
u := us[0]

err = s.conn.Resolved(u, false)
c.Assert(err, ErrorMatches, `unit "testriak/0" is not in an error state`)
err = s.conn.Resolved(u, true)
c.Assert(err, ErrorMatches, `unit "testriak/0" is not in an error state`)

err = u.SetStatus(state.UnitError, "gaaah")
c.Assert(err, IsNil)
err = s.conn.Resolved(u, false)
c.Assert(err, IsNil)
err = s.conn.Resolved(u, true)
c.Assert(err, ErrorMatches, `cannot set resolved mode for unit "testriak/0": already resolved`)
c.Assert(u.Resolved(), Equals, state.ResolvedNoHooks)

err = u.ClearResolved()
c.Assert(err, IsNil)
err = s.conn.Resolved(u, true)
c.Assert(err, IsNil)
err = s.conn.Resolved(u, false)
c.Assert(err, ErrorMatches, `cannot set resolved mode for unit "testriak/0": already resolved`)
c.Assert(u.Resolved(), Equals, state.ResolvedRetryHooks)
}

0 comments on commit e9f7146

Please sign in to comment.