Code review comment for lp:~axwalk/juju-core/manual-bootstrap

Revision history for this message
Roger Peppe (rogpeppe) wrote :

Looks good. Some minor comments and suggestions below.

https://codereview.appspot.com/13635044/diff/14001/cmd/juju/bootstrap.go
File cmd/juju/bootstrap.go (right):

https://codereview.appspot.com/13635044/diff/14001/cmd/juju/bootstrap.go#newcode76
cmd/juju/bootstrap.go:76: environ = &bootstrapStorageEnviron{environ,
bootstrapStorage}
nice

https://codereview.appspot.com/13635044/diff/14001/environs/bootstrap/bootstrap.go
File environs/bootstrap/bootstrap.go (right):

https://codereview.appspot.com/13635044/diff/14001/environs/bootstrap/bootstrap.go#newcode24
environs/bootstrap/bootstrap.go:24: // BootstrapStorager is an interface
that returns a environs.Storage that may
I think I'd define this inside environs/interface.go

This package doesn't define or use it, and that file
is a useful reference for people implementing providers.

https://codereview.appspot.com/13635044/diff/14001/environs/filestorage/filestorage.go
File environs/filestorage/filestorage.go (right):

https://codereview.appspot.com/13635044/diff/14001/environs/filestorage/filestorage.go#newcode45
environs/filestorage/filestorage.go:45: func NewFileStorage(path string)
(environs.Storage, error) {
Doc comment please.

Also, wouldn't it make more sense to define NewFileStorageReader in
terms of NewFileStorage? That way you wouldn't need the dynamic type
conversion, as Storage can be assigned to StorageReader.

For future consideration, I think filestorage.New and
filestorage.NewReader would be more idiomatic names than
filestorage.NewFileStorage and filestorage.NewFileStorageReader.

https://codereview.appspot.com/13635044/diff/14001/environs/manual/agent.go
File environs/manual/agent.go (right):

https://codereview.appspot.com/13635044/diff/14001/environs/manual/agent.go#newcode41
environs/manual/agent.go:41: machineenv map[string]string
s/machineenv/machineEnv/

https://codereview.appspot.com/13635044/diff/14001/environs/manual/bootstrap.go
File environs/manual/bootstrap.go (right):

https://codereview.appspot.com/13635044/diff/14001/environs/manual/bootstrap.go#newcode42
environs/manual/bootstrap.go:42: var errHostEmpty = errors.New("Host
argument is empty")
Errors don't generally start with a capital letter.

Also, if it's only used once, I wouldn't bother defining a variable for
it.

https://codereview.appspot.com/13635044/diff/14001/environs/manual/bootstrap.go#newcode43
environs/manual/bootstrap.go:43: var errEnvironNil = errors.New("Environ
argument is nil")
ditto

https://codereview.appspot.com/13635044/diff/14001/environs/manual/bootstrap.go#newcode51
environs/manual/bootstrap.go:51: // host will manually bootstrapped.
s/will/will be/ ?

https://codereview.appspot.com/13635044/diff/14001/environs/manual/bootstrap.go#newcode65
environs/manual/bootstrap.go:65: err = fmt.Errorf("error checking if
provisioned: %v", err)
"failed to check provisioned status: %v" ?

https://codereview.appspot.com/13635044/diff/14001/environs/manual/bootstrap.go#newcode78
environs/manual/bootstrap.go:78: defer func() {
Why not defer this after the call to SaveState and thereby lose the need
for the savedState variable?

https://codereview.appspot.com/13635044/diff/14001/environs/manual/bootstrap.go#newcode87
environs/manual/bootstrap.go:87: err = fmt.Errorf("error detecting
hardware characteristics: %v", err)
s/err = /return /

https://codereview.appspot.com/13635044/diff/14001/environs/manual/bootstrap.go#newcode123
environs/manual/bootstrap.go:123: machineenv := map[string]string{
s/machineenv/machineEnv/

https://codereview.appspot.com/13635044/diff/14001/environs/manual/detection.go
File environs/manual/detection.go (right):

https://codereview.appspot.com/13635044/diff/14001/environs/manual/detection.go#newcode22
environs/manual/detection.go:22: const checkProvisionedScript = "ls
/etc/init/ | grep juju.*\\.conf || exit 0"
How about a more direct test?

"ls /etc/init/juju*.conf"

which will succeed if provisioned and fail otherwise.

https://codereview.appspot.com/13635044/diff/14001/environs/manual/detection.go#newcode28
environs/manual/detection.go:28: cmd := exec.Command("ssh", sshHost,
"bash")
or

cmd := exec.Command("ssh", sshHost, "bash", "-c",
checkProvisionedScript)

?

https://codereview.appspot.com/13635044/diff/14001/environs/manual/detection.go#newcode29
environs/manual/detection.go:29: cmd.Stdin =
bytes.NewBufferString(checkProvisionedScript)
strings.NewReader would seem more appropriate if you continue to use
Stdin.

https://codereview.appspot.com/13635044/diff/14001/environs/manual/detection.go#newcode30
environs/manual/detection.go:30: out, err := cmd.CombinedOutput()
I think you want Output not CombinedOutput here.

https://codereview.appspot.com/13635044/diff/14001/environs/manual/detection.go#newcode125
environs/manual/detection.go:125: lsb_release -cs
I wonder if this should include a "set -e"

https://codereview.appspot.com/13635044/diff/14001/environs/manual/provisioner_test.go
File environs/manual/provisioner_test.go (right):

https://codereview.appspot.com/13635044/diff/14001/environs/manual/provisioner_test.go#newcode58
environs/manual/provisioner_test.go:58: for i, errorCode := range
[]int{255, 0} {
Please add a log statement here, so that if the test fails, we
know which iteration we were on.

Something like this is conventional:

c.Logf("test %d: code %d", i, errorCode)

https://codereview.appspot.com/13635044/diff/14001/environs/manual/sshresponse_test.go
File environs/manual/sshresponse_test.go (right):

https://codereview.appspot.com/13635044/diff/14001/environs/manual/sshresponse_test.go#newcode41
environs/manual/sshresponse_test.go:41: func sshresponse(c *gc.C, input,
output string, rc int) func() {
I found these names quite confusing; the respond method doesn't
respond to anything...

How about something like "installFakeSSH" instead instead of
sshresponse?

Then the type could be fakeSSH, and the respond method could be named
SetUp or similar.

https://codereview.appspot.com/13635044/diff/14001/environs/manual/sshresponse_test.go#newcode70
environs/manual/sshresponse_test.go:70: func (r sshresponder) respond(c
*gc.C) func() {
I'd like to see a doc comment on this please.

https://codereview.appspot.com/13635044/diff/14001/environs/manual/sshresponse_test.go#newcode87
environs/manual/sshresponse_test.go:87: f := sshresponse(c, input,
output, rc)
I think things can be simpler here. How about something like:

var restores := []func()
add := func(input, output string, rc int) {
     restore := sshresponse(c, input, output, rc)
     restores = append(restores, restore)
}
if !r.skipProvisionAgent {
     add("", "", r.provisionAgentExitCode)
}
add(detectionScript, detectionoutput, 0)
add(checkProvisionedScript, "", 0)
return func() {
     for i := len(restores)-1; i >= 0; i-- {
         restores[i]()
     }
}

alternatively, you could use jc.Restorer throughout, and
add a method to it:

// Add returns a Restorer that restores first f1
// and then f. It is valid to call this on a nil
// Restorer.
func (f Restorer) Add(f1 Restorer) Restorer {
      return func() {
          f1.Restore()
          if f != nil {
              f.Restore()
          }
      }
}

then you could do:

var restore jc.Restorer
add := func(input, output string, rc int) {
     restore = restore.Add(sshresponse(c, input, output, rc))
}
if !r.skipProvisionAgent {
     add("", "", r.provisionAgentExitCode)
}
add(detectionScript, detectionoutput, 0)
add(checkProvisionedScript, "", 0)
return restore

On balance I prefer the latter, because the defer
calls can be:

defer sshresponder{...}.respond().Restore()

which is more obvious to my eyes (I had missed the extra set of () in
the call).

https://codereview.appspot.com/13635044/

« Back to merge proposal