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.
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()
}
}
}
Looks good. Some minor comments and suggestions below.
https:/ /codereview. appspot. com/13635044/ diff/14001/ cmd/juju/ bootstrap. go bootstrap. go (right):
File cmd/juju/
https:/ /codereview. appspot. com/13635044/ diff/14001/ cmd/juju/ bootstrap. go#newcode76 bootstrap. go:76: environ = &bootstrapStora geEnviron{ environ,
cmd/juju/
bootstrapStorage}
nice
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ bootstrap/ bootstrap. go bootstrap/ bootstrap. go (right):
File environs/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ bootstrap/ bootstrap. go#newcode24 bootstrap/ bootstrap. go:24: // BootstrapStorager is an interface interface. go
environs/
that returns a environs.Storage that may
I think I'd define this inside environs/
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 filestorage/ filestorage. go (right):
File environs/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ filestorage/ filestorage. go#newcode45 filestorage/ filestorage. go:45: func NewFileStorage(path string)
environs/
(environs.Storage, error) {
Doc comment please.
Also, wouldn't it make more sense to define NewFileStorageR eader 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 NewReader would be more idiomatic names than NewFileStorage and filestorage. NewFileStorageR eader.
filestorage.
filestorage.
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ agent.go manual/ agent.go (right):
File environs/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ agent.go# newcode41 manual/ agent.go: 41: machineenv map[string]string machineEnv/
environs/
s/machineenv/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ bootstrap. go manual/ bootstrap. go (right):
File environs/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ bootstrap. go#newcode42 manual/ bootstrap. go:42: var errHostEmpty = errors.New("Host
environs/
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 manual/ bootstrap. go:43: var errEnvironNil = errors.New("Environ
environs/
argument is nil")
ditto
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ bootstrap. go#newcode51 manual/ bootstrap. go:51: // host will manually bootstrapped.
environs/
s/will/will be/ ?
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ bootstrap. go#newcode65 manual/ bootstrap. go:65: err = fmt.Errorf("error checking if
environs/
provisioned: %v", err)
"failed to check provisioned status: %v" ?
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ bootstrap. go#newcode78 manual/ bootstrap. go:78: defer func() {
environs/
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 manual/ bootstrap. go:87: err = fmt.Errorf("error detecting
environs/
hardware characteristics: %v", err)
s/err = /return /
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ bootstrap. go#newcode123 manual/ bootstrap. go:123: machineenv := map[string]string{ machineEnv/
environs/
s/machineenv/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ detection. go manual/ detection. go (right):
File environs/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ detection. go#newcode22 manual/ detection. go:22: const checkProvisione dScript = "ls
environs/
/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 manual/ detection. go:28: cmd := exec.Command("ssh", sshHost,
environs/
"bash")
or
cmd := exec.Command("ssh", sshHost, "bash", "-c", dScript)
checkProvisione
?
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ detection. go#newcode29 manual/ detection. go:29: cmd.Stdin = String( checkProvisione dScript)
environs/
bytes.NewBuffer
strings.NewReader would seem more appropriate if you continue to use
Stdin.
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ detection. go#newcode30 manual/ detection. go:30: out, err := cmd.CombinedOut put()
environs/
I think you want Output not CombinedOutput here.
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ detection. go#newcode125 manual/ detection. go:125: lsb_release -cs
environs/
I wonder if this should include a "set -e"
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ provisioner_ test.go manual/ provisioner_ test.go (right):
File environs/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ provisioner_ test.go# newcode58 manual/ provisioner_ test.go: 58: for i, errorCode := range
environs/
[]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 manual/ sshresponse_ test.go (right):
File environs/
https:/ /codereview. appspot. com/13635044/ diff/14001/ environs/ manual/ sshresponse_ test.go# newcode41 manual/ sshresponse_ test.go: 41: func sshresponse(c *gc.C, input,
environs/
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 manual/ sshresponse_ test.go: 70: func (r sshresponder) respond(c
environs/
*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 manual/ sshresponse_ test.go: 87: f := sshresponse(c, input,
environs/
output, rc)
I think things can be simpler here. How about something like:
var restores := []func() nAgent { tExitCode) ript, detectionoutput, 0) ionedScript, "", 0)
restores[ i]()
add := func(input, output string, rc int) {
restore := sshresponse(c, input, output, rc)
restores = append(restores, restore)
}
if !r.skipProvisio
add("", "", r.provisionAgen
}
add(detectionSc
add(checkProvis
return func() {
for i := len(restores)-1; i >= 0; i-- {
}
}
alternatively, you could use jc.Restorer throughout, and
add a method to it:
// Add returns a Restorer that restores first f1
f1.Restore( )
f.Restore( )
// and then f. It is valid to call this on a nil
// Restorer.
func (f Restorer) Add(f1 Restorer) Restorer {
return func() {
if f != nil {
}
}
}
then you could do:
var restore jc.Restorer Add(sshresponse (c, input, output, rc)) nAgent { tExitCode) ript, detectionoutput, 0) ionedScript, "", 0)
add := func(input, output string, rc int) {
restore = restore.
}
if !r.skipProvisio
add("", "", r.provisionAgen
}
add(detectionSc
add(checkProvis
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/