Merge lp:~thumper/juju-core/api-juju-run into lp:~go-bot/juju-core/trunk

Proposed by Tim Penhey
Status: Merged
Approved by: Tim Penhey
Approved revision: no longer in the source branch.
Merged at revision: 2192
Proposed branch: lp:~thumper/juju-core/api-juju-run
Merge into: lp:~go-bot/juju-core/trunk
Prerequisite: lp:~thumper/juju-core/ssh-run-on-remote
Diff against target: 706 lines (+545/-12)
12 files modified
cmd/jujud/machine.go (+2/-1)
provider/dummy/environs.go (+1/-1)
state/api/client.go (+18/-0)
state/api/params/internal.go (+29/-0)
state/apiserver/apiserver.go (+9/-7)
state/apiserver/client/client.go (+3/-1)
state/apiserver/client/export_test.go (+2/-0)
state/apiserver/client/run.go (+179/-0)
state/apiserver/client/run_test.go (+299/-0)
state/apiserver/login_test.go (+1/-0)
state/apiserver/root.go (+1/-1)
state/apiserver/server_test.go (+1/-1)
To merge this branch: bzr merge lp:~thumper/juju-core/api-juju-run
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+201103@code.launchpad.net

Commit message

Provide api end points for juju-run

Adds two methods the the Client api end-point. One to run
commands against all machines, and another to run against
a specified collection of machines.

In order to have the api server able to ssh to the other
machines, it needed to know the location of the system
identity file, which is stored in the datadir, which means
that the agent config is needed by the api server. The
dummy provider is updated to pass a fake(ish) agent config
through to the apiserver.

https://codereview.appspot.com/49960045/

Description of the change

Provide api end points for juju-run

Adds two methods the the Client api end-point. One to run
commands against all machines, and another to run against
a specified collection of machines.

In order to have the api server able to ssh to the other
machines, it needed to know the location of the system
identity file, which is stored in the datadir, which means
that the agent config is needed by the api server. The
dummy provider is updated to pass a fake(ish) agent config
through to the apiserver.

https://codereview.appspot.com/49960045/

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Reviewers: mp+201103_code.launchpad.net,

Message:
Please take a look.

Description:
Provide api end points for juju-run

Adds two methods the the Client api end-point. One to run
commands against all machines, and another to run against
a specified collection of machines.

In order to have the api server able to ssh to the other
machines, it needed to know the location of the system
identity file, which is stored in the datadir, which means
that the agent config is needed by the api server. The
dummy provider is updated to pass a fake(ish) agent config
through to the apiserver.

https://code.launchpad.net/~thumper/juju-core/api-juju-run/+merge/201103

Requires:
https://code.launchpad.net/~thumper/juju-core/ssh-run-on-remote/+merge/201095

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/49960045/

Affected files (+565, -19 lines):
   A [revision details]
   M cmd/jujud/machine.go
   M provider/dummy/environs.go
   M state/api/client.go
   M state/apiserver/apiserver.go
   M state/apiserver/client/client.go
   M state/apiserver/client/export_test.go
   A state/apiserver/client/run.go
   A state/apiserver/client/run_test.go
   M state/apiserver/login_test.go
   M state/apiserver/root.go
   M state/apiserver/server_test.go

Revision history for this message
Ian Booth (wallyworld) wrote :

I have an initial concern about the need for apiConfig - see comments in
code.

https://codereview.appspot.com/49960045/diff/1/cmd/jujud/machine.go
File cmd/jujud/machine.go (right):

https://codereview.appspot.com/49960045/diff/1/cmd/jujud/machine.go#newcode310
cmd/jujud/machine.go:310: })
This is clumsy - port, cert, key all are derived from agentConfig, aka
a.Conf.config

So we should include dataDir with the result of APIServerDetails and
pass that in

https://codereview.appspot.com/49960045/diff/1/provider/dummy/environs.go
File provider/dummy/environs.go (right):

https://codereview.appspot.com/49960045/diff/1/provider/dummy/environs.go#newcode601
provider/dummy/environs.go:601: estate.apiServer, err =
apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert),
[]byte(testing.ServerKey), config)
As per my other comment, just pass in dataDir

https://codereview.appspot.com/49960045/diff/1/state/api/client.go
File state/api/client.go (right):

https://codereview.appspot.com/49960045/diff/1/state/api/client.go#newcode417
state/api/client.go:417:
The above structs should be in params/internal.go right?

https://codereview.appspot.com/49960045/diff/1/state/api/client.go#newcode437
state/api/client.go:437:
Ditto

https://codereview.appspot.com/49960045/diff/1/state/apiserver/apiserver.go
File state/apiserver/apiserver.go (right):

https://codereview.appspot.com/49960045/diff/1/state/apiserver/apiserver.go#newcode33
state/apiserver/apiserver.go:33: agentConfig agent.Config
Just dataDir would be better I think

https://codereview.appspot.com/49960045/diff/1/state/apiserver/client/run.go
File state/apiserver/client/run.go (right):

https://codereview.appspot.com/49960045/diff/1/state/apiserver/client/run.go#newcode168
state/apiserver/client/run.go:168: outstanding.Wait()
Should there be some sort of timeout? Will this hang if an individual
command hangs?

https://codereview.appspot.com/49960045/

Revision history for this message
Tim Penhey (thumper) wrote :
Revision history for this message
Tim Penhey (thumper) wrote :

https://codereview.appspot.com/49960045/diff/1/cmd/jujud/machine.go
File cmd/jujud/machine.go (right):

https://codereview.appspot.com/49960045/diff/1/cmd/jujud/machine.go#newcode310
cmd/jujud/machine.go:310: })
On 2014/01/10 03:19:03, wallyworld wrote:
> This is clumsy - port, cert, key all are derived from agentConfig, aka
> a.Conf.config

> So we should include dataDir with the result of APIServerDetails and
pass that
> in

Done.

https://codereview.appspot.com/49960045/diff/1/provider/dummy/environs.go
File provider/dummy/environs.go (right):

https://codereview.appspot.com/49960045/diff/1/provider/dummy/environs.go#newcode601
provider/dummy/environs.go:601: estate.apiServer, err =
apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert),
[]byte(testing.ServerKey), config)
On 2014/01/10 03:19:03, wallyworld wrote:
> As per my other comment, just pass in dataDir

Done.

https://codereview.appspot.com/49960045/diff/1/state/api/client.go
File state/api/client.go (right):

https://codereview.appspot.com/49960045/diff/1/state/api/client.go#newcode417
state/api/client.go:417:
On 2014/01/10 03:19:03, wallyworld wrote:
> The above structs should be in params/internal.go right?

Is there a standard? This needs to be accessed by both the api caller
and the apiserver. If internal is the place to put them, I'll move it,
but it wasn't clear when writing that they should go there.

https://codereview.appspot.com/49960045/diff/1/state/apiserver/apiserver.go
File state/apiserver/apiserver.go (right):

https://codereview.appspot.com/49960045/diff/1/state/apiserver/apiserver.go#newcode33
state/apiserver/apiserver.go:33: agentConfig agent.Config
On 2014/01/10 03:19:03, wallyworld wrote:
> Just dataDir would be better I think

Done.

https://codereview.appspot.com/49960045/diff/1/state/apiserver/client/run.go
File state/apiserver/client/run.go (right):

https://codereview.appspot.com/49960045/diff/1/state/apiserver/client/run.go#newcode168
state/apiserver/client/run.go:168: outstanding.Wait()
On 2014/01/10 03:19:03, wallyworld wrote:
> Should there be some sort of timeout? Will this hang if an individual
command
> hangs?

It will wait based on the timeout passed through to
ExecuteCommandOnMachine.

https://codereview.appspot.com/49960045/

Revision history for this message
Ian Booth (wallyworld) wrote :

LGTM with a couple of tweaks

https://codereview.appspot.com/49960045/diff/1/state/api/client.go
File state/api/client.go (right):

https://codereview.appspot.com/49960045/diff/1/state/api/client.go#newcode417
state/api/client.go:417:
On 2014/01/10 04:24:39, thumper wrote:
> On 2014/01/10 03:19:03, wallyworld wrote:
> > The above structs should be in params/internal.go right?

> Is there a standard? This needs to be accessed by both the api caller
and the
> apiserver. If internal is the place to put them, I'll move it, but it
wasn't
> clear when writing that they should go there.

I'm not sure TBH. The main point of the comment was that we seem to put
params structs for most everything else in either params.go or
internal.go. Perhaps check with Dimiter or Roger before landing on the
best place to put them.

https://codereview.appspot.com/49960045/diff/20001/state/apiserver/client/run_test.go
File state/apiserver/client/run_test.go (right):

https://codereview.appspot.com/49960045/diff/20001/state/apiserver/client/run_test.go#newcode144
state/apiserver/client/run_test.go:144: func (s *runSuite) mockSSH(c
*gc.C, cmd string) {
Perhaps a brief comment to explain how this method fits into the big
scheme of things - I figured it out but I needed to exercise some brain
cells.

https://codereview.appspot.com/49960045/diff/20001/state/apiserver/client/run_test.go#newcode255
state/apiserver/client/run_test.go:255: client := s.APIState.Client()
Agreed, this is unfortunate :-(

https://codereview.appspot.com/49960045/

Revision history for this message
Go Bot (go-bot) wrote :
Download full text (8.5 KiB)

The attempt to merge lp:~thumper/juju-core/api-juju-run into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core/agent 1.321s
ok launchpad.net/juju-core/agent/tools 0.253s
ok launchpad.net/juju-core/bzr 7.757s
ok launchpad.net/juju-core/cert 3.357s
ok launchpad.net/juju-core/charm 1.197s
? launchpad.net/juju-core/charm/hooks [no test files]
ok launchpad.net/juju-core/cloudinit 0.033s
ok launchpad.net/juju-core/cloudinit/sshinit 1.141s
ok launchpad.net/juju-core/cmd 0.205s
ok launchpad.net/juju-core/cmd/charm-admin 0.826s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/juju 182.286s
ok launchpad.net/juju-core/cmd/jujud 48.424s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 2.654s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/constraints 0.030s
ok launchpad.net/juju-core/container 0.035s
ok launchpad.net/juju-core/container/factory 0.068s
ok launchpad.net/juju-core/container/kvm 0.349s
ok launchpad.net/juju-core/container/kvm/mock 0.044s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 0.424s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.285s
ok launchpad.net/juju-core/environs 3.153s
ok launchpad.net/juju-core/environs/bootstrap 4.701s
ok launchpad.net/juju-core/environs/cloudinit 0.516s
ok launchpad.net/juju-core/environs/config 1.124s
ok launchpad.net/juju-core/environs/configstore 0.053s
ok launchpad.net/juju-core/environs/filestorage 0.032s
ok launchpad.net/juju-core/environs/httpstorage 1.029s
ok launchpad.net/juju-core/environs/imagemetadata 0.501s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.056s
ok launchpad.net/juju-core/environs/jujutest 0.262s
ok launchpad.net/juju-core/environs/manual 9.784s
ok launchpad.net/juju-core/environs/simplestreams 0.361s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 1.265s
ok launchpad.net/juju-core/environs/storage 1.188s
ok launchpad.net/juju-core/environs/sync 31.502s
ok launchpad.net/juju-core/environs/testing 0.230s
ok launchpad.net/juju-core/environs/tools 6.758s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.015s
ok launchpad.net/juju-core/instance 0.022s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 16.729s
ok launchpad.net/juju-core/juju/osenv 0.018s
? launchpad.net/juju-core/juju/testing [no test files]
ok launchpad.net/juju-core/log 0.016s
ok launchpad.net/juju-core/log/syslog 0.025s
ok launchpad.net/juju-core/names 0.026s
? launchpad.net/juju-core/provider [no test files]
? launchpad.net/juju-core/provider/a...

Read more...

Revision history for this message
Go Bot (go-bot) wrote :
Download full text (13.3 KiB)

The attempt to merge lp:~thumper/juju-core/api-juju-run into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core/agent 1.259s
ok launchpad.net/juju-core/agent/tools 0.363s
ok launchpad.net/juju-core/bzr 8.783s
ok launchpad.net/juju-core/cert 2.867s
ok launchpad.net/juju-core/charm 0.571s
? launchpad.net/juju-core/charm/hooks [no test files]
ok launchpad.net/juju-core/cloudinit 0.044s
ok launchpad.net/juju-core/cloudinit/sshinit 1.085s
ok launchpad.net/juju-core/cmd 0.207s
ok launchpad.net/juju-core/cmd/charm-admin 0.814s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/juju 182.388s
ok launchpad.net/juju-core/cmd/jujud 54.847s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 2.709s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/constraints 0.026s
ok launchpad.net/juju-core/container 0.052s
ok launchpad.net/juju-core/container/factory 0.059s
ok launchpad.net/juju-core/container/kvm 0.356s
ok launchpad.net/juju-core/container/kvm/mock 0.036s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 0.316s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.276s
ok launchpad.net/juju-core/environs 3.194s
ok launchpad.net/juju-core/environs/bootstrap 4.475s
ok launchpad.net/juju-core/environs/cloudinit 0.506s
ok launchpad.net/juju-core/environs/config 1.085s
ok launchpad.net/juju-core/environs/configstore 0.044s
ok launchpad.net/juju-core/environs/filestorage 0.033s
ok launchpad.net/juju-core/environs/httpstorage 1.074s
ok launchpad.net/juju-core/environs/imagemetadata 0.536s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.056s
ok launchpad.net/juju-core/environs/jujutest 0.243s
ok launchpad.net/juju-core/environs/manual 9.777s
ok launchpad.net/juju-core/environs/simplestreams 0.369s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 1.213s
ok launchpad.net/juju-core/environs/storage 1.162s
ok launchpad.net/juju-core/environs/sync 31.547s
ok launchpad.net/juju-core/environs/testing 0.209s
ok launchpad.net/juju-core/environs/tools 6.843s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.016s
ok launchpad.net/juju-core/instance 0.023s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 16.653s
ok launchpad.net/juju-core/juju/osenv 0.018s
? launchpad.net/juju-core/juju/testing [no test files]
ok launchpad.net/juju-core/log 0.016s
ok launchpad.net/juju-core/log/syslog 0.023s
ok launchpad.net/juju-core/names 0.026s
? launchpad.net/juju-core/provider [no test files]
? launchpad.net/juju-core/provider/a...

Revision history for this message
Go Bot (go-bot) wrote :
Download full text (35.6 KiB)

The attempt to merge lp:~thumper/juju-core/api-juju-run into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core/agent 1.312s
ok launchpad.net/juju-core/agent/tools 0.237s
ok launchpad.net/juju-core/bzr 7.102s
ok launchpad.net/juju-core/cert 3.459s
ok launchpad.net/juju-core/charm 0.586s
? launchpad.net/juju-core/charm/hooks [no test files]
ok launchpad.net/juju-core/cloudinit 0.032s
ok launchpad.net/juju-core/cloudinit/sshinit 1.071s
ok launchpad.net/juju-core/cmd 0.233s
ok launchpad.net/juju-core/cmd/charm-admin 0.794s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/juju 181.782s
ok launchpad.net/juju-core/cmd/jujud 48.053s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 2.767s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/constraints 0.028s
ok launchpad.net/juju-core/container 0.036s
ok launchpad.net/juju-core/container/factory 0.058s
ok launchpad.net/juju-core/container/kvm 0.315s
ok launchpad.net/juju-core/container/kvm/mock 0.043s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 0.282s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.307s
ok launchpad.net/juju-core/environs 3.171s
ok launchpad.net/juju-core/environs/bootstrap 4.483s
ok launchpad.net/juju-core/environs/cloudinit 0.503s
ok launchpad.net/juju-core/environs/config 1.076s
ok launchpad.net/juju-core/environs/configstore 0.042s
ok launchpad.net/juju-core/environs/filestorage 0.033s
ok launchpad.net/juju-core/environs/httpstorage 0.995s
ok launchpad.net/juju-core/environs/imagemetadata 0.513s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.067s
ok launchpad.net/juju-core/environs/jujutest 0.233s
ok launchpad.net/juju-core/environs/manual 10.116s
ok launchpad.net/juju-core/environs/simplestreams 0.379s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 1.136s
ok launchpad.net/juju-core/environs/storage 1.165s
ok launchpad.net/juju-core/environs/sync 31.266s
ok launchpad.net/juju-core/environs/testing 0.265s
ok launchpad.net/juju-core/environs/tools 6.800s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.016s
ok launchpad.net/juju-core/instance 0.025s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 16.194s
ok launchpad.net/juju-core/juju/osenv 0.019s
? launchpad.net/juju-core/juju/testing [no test files]
ok launchpad.net/juju-core/log 0.016s
ok launchpad.net/juju-core/log/syslog 0.025s
ok launchpad.net/juju-core/names 0.042s
? launchpad.net/juju-core/provider [no test files]
? launchpad.net/juju-core/provider/...

Revision history for this message
Go Bot (go-bot) wrote :
Download full text (195.1 KiB)

The attempt to merge lp:~thumper/juju-core/api-juju-run into lp:juju-core failed. Below is the output from the failed tests.

ok launchpad.net/juju-core/agent 0.743s
ok launchpad.net/juju-core/agent/tools 0.250s
ok launchpad.net/juju-core/bzr 7.341s
ok launchpad.net/juju-core/cert 3.651s
ok launchpad.net/juju-core/charm 0.568s
? launchpad.net/juju-core/charm/hooks [no test files]
ok launchpad.net/juju-core/cloudinit 0.032s
ok launchpad.net/juju-core/cloudinit/sshinit 1.003s
ok launchpad.net/juju-core/cmd 0.259s
ok launchpad.net/juju-core/cmd/charm-admin 0.857s
? launchpad.net/juju-core/cmd/charmd [no test files]
? launchpad.net/juju-core/cmd/charmload [no test files]
ok launchpad.net/juju-core/cmd/juju 181.648s
ok launchpad.net/juju-core/cmd/jujud 54.857s
ok launchpad.net/juju-core/cmd/plugins/juju-metadata 2.618s
? launchpad.net/juju-core/cmd/plugins/juju-restore [no test files]
ok launchpad.net/juju-core/constraints 0.026s
ok launchpad.net/juju-core/container 0.036s
ok launchpad.net/juju-core/container/factory 0.060s
ok launchpad.net/juju-core/container/kvm 0.289s
ok launchpad.net/juju-core/container/kvm/mock 0.035s
? launchpad.net/juju-core/container/kvm/testing [no test files]
ok launchpad.net/juju-core/container/lxc 0.280s
? launchpad.net/juju-core/container/lxc/mock [no test files]
? launchpad.net/juju-core/container/lxc/testing [no test files]
? launchpad.net/juju-core/container/testing [no test files]
ok launchpad.net/juju-core/downloader 5.265s
ok launchpad.net/juju-core/environs 3.189s
ok launchpad.net/juju-core/environs/bootstrap 4.550s
ok launchpad.net/juju-core/environs/cloudinit 0.505s
ok launchpad.net/juju-core/environs/config 1.095s
ok launchpad.net/juju-core/environs/configstore 0.047s
ok launchpad.net/juju-core/environs/filestorage 0.039s
ok launchpad.net/juju-core/environs/httpstorage 1.065s
ok launchpad.net/juju-core/environs/imagemetadata 0.515s
? launchpad.net/juju-core/environs/imagemetadata/testing [no test files]
ok launchpad.net/juju-core/environs/instances 0.072s
ok launchpad.net/juju-core/environs/jujutest 0.257s
ok launchpad.net/juju-core/environs/manual 10.644s
ok launchpad.net/juju-core/environs/simplestreams 0.383s
? launchpad.net/juju-core/environs/simplestreams/testing [no test files]
ok launchpad.net/juju-core/environs/sshstorage 1.330s
ok launchpad.net/juju-core/environs/storage 1.176s
ok launchpad.net/juju-core/environs/sync 31.477s
ok launchpad.net/juju-core/environs/testing 0.207s
ok launchpad.net/juju-core/environs/tools 6.855s
? launchpad.net/juju-core/environs/tools/testing [no test files]
ok launchpad.net/juju-core/errors 0.016s
ok launchpad.net/juju-core/instance 0.023s
? launchpad.net/juju-core/instance/testing [no test files]
ok launchpad.net/juju-core/juju 16.379s
ok launchpad.net/juju-core/juju/osenv 0.018s
? launchpad.net/juju-core/juju/testing [no test files]
ok launchpad.net/juju-core/log 0.015s
ok launchpad.net/juju-core/log/syslog 0.023s
ok launchpad.net/juju-core/names 0.025s
? launchpad.net/juju-core/provider [no test files]
? launchpad.net/juju-core/provider/...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'cmd/jujud/machine.go'
--- cmd/jujud/machine.go 2014-01-10 03:48:20 +0000
+++ cmd/jujud/machine.go 2014-01-12 20:08:17 +0000
@@ -310,7 +310,8 @@
310 if len(cert) == 0 || len(key) == 0 {310 if len(cert) == 0 || len(key) == 0 {
311 return nil, &fatalError{"configuration does not have state server cert/key"}311 return nil, &fatalError{"configuration does not have state server cert/key"}
312 }312 }
313 return apiserver.NewServer(st, fmt.Sprintf(":%d", port), cert, key)313 dataDir := a.Conf.config.DataDir()
314 return apiserver.NewServer(st, fmt.Sprintf(":%d", port), cert, key, dataDir)
314 })315 })
315 runner.StartWorker("cleaner", func() (worker.Worker, error) {316 runner.StartWorker("cleaner", func() (worker.Worker, error) {
316 return cleaner.NewCleaner(st), nil317 return cleaner.NewCleaner(st), nil
317318
=== modified file 'provider/dummy/environs.go'
--- provider/dummy/environs.go 2013-12-20 11:48:03 +0000
+++ provider/dummy/environs.go 2014-01-12 20:08:17 +0000
@@ -586,7 +586,7 @@
586 if err != nil {586 if err != nil {
587 panic(err)587 panic(err)
588 }588 }
589 estate.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey))589 estate.apiServer, err = apiserver.NewServer(st, "localhost:0", []byte(testing.ServerCert), []byte(testing.ServerKey), "")
590 if err != nil {590 if err != nil {
591 panic(err)591 panic(err)
592 }592 }
593593
=== modified file 'state/api/client.go'
--- state/api/client.go 2014-01-07 07:35:12 +0000
+++ state/api/client.go 2014-01-12 20:08:17 +0000
@@ -9,6 +9,7 @@
9 "io/ioutil"9 "io/ioutil"
10 "net/http"10 "net/http"
11 "os"11 "os"
12 "time"
1213
13 "launchpad.net/juju-core/charm"14 "launchpad.net/juju-core/charm"
14 "launchpad.net/juju-core/constraints"15 "launchpad.net/juju-core/constraints"
@@ -398,6 +399,23 @@
398 return c.st.Call("Client", "", "SetEnvironAgentVersion", args, nil)399 return c.st.Call("Client", "", "SetEnvironAgentVersion", args, nil)
399}400}
400401
402// RunOnAllMachines runs the command on all the machines with the specified
403// timeout.
404func (c *Client) RunOnAllMachines(commands string, timeout time.Duration) ([]params.RunResult, error) {
405 var results params.RunResults
406 args := params.RunParams{Commands: commands, Timeout: timeout}
407 err := c.st.Call("Client", "", "RunOnAllMachines", args, &results)
408 return results.Results, err
409}
410
411// Run the Commands specified on the machines identified through the ids
412// provided in the machines, services and units slices.
413func (c *Client) Run(run params.RunParams) ([]params.RunResult, error) {
414 var results params.RunResults
415 err := c.st.Call("Client", "", "Run", run, &results)
416 return results.Results, err
417}
418
401// DestroyEnvironment puts the environment into a "dying" state,419// DestroyEnvironment puts the environment into a "dying" state,
402// and removes all non-manager machine instances. DestroyEnvironment420// and removes all non-manager machine instances. DestroyEnvironment
403// will fail if there are any manually-provisioned non-manager machines421// will fail if there are any manually-provisioned non-manager machines
404422
=== modified file 'state/api/params/internal.go'
--- state/api/params/internal.go 2013-12-17 09:49:11 +0000
+++ state/api/params/internal.go 2014-01-12 20:08:17 +0000
@@ -4,6 +4,9 @@
4package params4package params
55
6import (6import (
7 "time"
8
9 "launchpad.net/juju-core/cmd"
7 "launchpad.net/juju-core/constraints"10 "launchpad.net/juju-core/constraints"
8 "launchpad.net/juju-core/instance"11 "launchpad.net/juju-core/instance"
9 "launchpad.net/juju-core/tools"12 "launchpad.net/juju-core/tools"
@@ -509,3 +512,29 @@
509 Error string `json:",omitempty"`512 Error string `json:",omitempty"`
510 CharmURL string `json:",omitempty"`513 CharmURL string `json:",omitempty"`
511}514}
515
516// RunParams is used to provide the parameters to the Run method.
517// Commands and Timeout are expected to have values, and one or more
518// values should be in the Machines, Services, or Units slices.
519type RunParams struct {
520 Commands string
521 Timeout time.Duration
522 Machines []string
523 Services []string
524 Units []string
525}
526
527// RunResult contains the result from an individual run call on a machine.
528// UnitId is populated if the command was run inside the unit context.
529type RunResult struct {
530 cmd.RemoteResponse
531 MachineId string
532 UnitId string
533 Error string
534}
535
536// RunResults is used to return the slice of results. Api server side calls
537// need to return single structure values.
538type RunResults struct {
539 Results []RunResult
540}
512541
=== modified file 'state/apiserver/apiserver.go'
--- state/apiserver/apiserver.go 2013-12-11 15:19:25 +0000
+++ state/apiserver/apiserver.go 2014-01-12 20:08:17 +0000
@@ -25,16 +25,17 @@
2525
26// Server holds the server side of the API.26// Server holds the server side of the API.
27type Server struct {27type Server struct {
28 tomb tomb.Tomb28 tomb tomb.Tomb
29 wg sync.WaitGroup29 wg sync.WaitGroup
30 state *state.State30 state *state.State
31 addr net.Addr31 addr net.Addr
32 dataDir string
32}33}
3334
34// Serve serves the given state by accepting requests on the given35// Serve serves the given state by accepting requests on the given
35// listener, using the given certificate and key (in PEM format) for36// listener, using the given certificate and key (in PEM format) for
36// authentication.37// authentication.
37func NewServer(s *state.State, addr string, cert, key []byte) (*Server, error) {38func NewServer(s *state.State, addr string, cert, key []byte, datadir string) (*Server, error) {
38 lis, err := net.Listen("tcp", addr)39 lis, err := net.Listen("tcp", addr)
39 if err != nil {40 if err != nil {
40 return nil, err41 return nil, err
@@ -45,8 +46,9 @@
45 return nil, err46 return nil, err
46 }47 }
47 srv := &Server{48 srv := &Server{
48 state: s,49 state: s,
49 addr: lis.Addr(),50 addr: lis.Addr(),
51 dataDir: datadir,
50 }52 }
51 // TODO(rog) check that *srvRoot is a valid type for using53 // TODO(rog) check that *srvRoot is a valid type for using
52 // as an RPC server.54 // as an RPC server.
5355
=== modified file 'state/apiserver/client/client.go'
--- state/apiserver/client/client.go 2014-01-07 07:35:12 +0000
+++ state/apiserver/client/client.go 2014-01-12 20:08:17 +0000
@@ -35,6 +35,7 @@
35 auth common.Authorizer35 auth common.Authorizer
36 resources *common.Resources36 resources *common.Resources
37 client *Client37 client *Client
38 dataDir string
38}39}
3940
40// Client serves client-specific API methods.41// Client serves client-specific API methods.
@@ -43,11 +44,12 @@
43}44}
4445
45// NewAPI creates a new instance of the Client API.46// NewAPI creates a new instance of the Client API.
46func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) *API {47func NewAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer, datadir string) *API {
47 r := &API{48 r := &API{
48 state: st,49 state: st,
49 auth: authorizer,50 auth: authorizer,
50 resources: resources,51 resources: resources,
52 dataDir: datadir,
51 }53 }
52 r.client = &Client{54 r.client = &Client{
53 api: r,55 api: r,
5456
=== modified file 'state/apiserver/client/export_test.go'
--- state/apiserver/client/export_test.go 2013-09-11 08:56:44 +0000
+++ state/apiserver/client/export_test.go 2014-01-12 20:08:17 +0000
@@ -4,3 +4,5 @@
4package client4package client
55
6var ParseSettingsCompatible = parseSettingsCompatible6var ParseSettingsCompatible = parseSettingsCompatible
7var RemoteParamsForMachine = remoteParamsForMachine
8var GetAllUnitNames = getAllUnitNames
79
=== added file 'state/apiserver/client/run.go'
--- state/apiserver/client/run.go 1970-01-01 00:00:00 +0000
+++ state/apiserver/client/run.go 2014-01-12 20:08:17 +0000
@@ -0,0 +1,179 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package client
5
6import (
7 "fmt"
8 "launchpad.net/juju-core/state"
9 "launchpad.net/juju-core/utils"
10 "path/filepath"
11 "sort"
12 "sync"
13 "time"
14
15 "launchpad.net/juju-core/environs/cloudinit"
16 "launchpad.net/juju-core/instance"
17 "launchpad.net/juju-core/state/api/params"
18 "launchpad.net/juju-core/utils/set"
19 "launchpad.net/juju-core/utils/ssh"
20)
21
22// remoteParamsForMachine returns a filled in RemoteExec instance
23// based on the machine, command and timeout params. If the machine
24// does not have an internal address, the Host is empty. This is caught
25// by the function that actually tries to execute the command.
26func remoteParamsForMachine(machine *state.Machine, command string, timeout time.Duration) *RemoteExec {
27 // magic boolean parameters are bad :-(
28 address := instance.SelectInternalAddress(machine.Addresses(), false)
29 execParams := &RemoteExec{
30 ExecParams: ssh.ExecParams{
31 Command: command,
32 Timeout: timeout,
33 },
34 MachineId: machine.Id(),
35 }
36 if address != "" {
37 execParams.Host = fmt.Sprintf("ubuntu@%s", address)
38 }
39 return execParams
40}
41
42// getAllUnitNames returns a sequence of valid Unit objects from state. If any
43// of the service names or unit names are not found, an error is returned.
44func getAllUnitNames(st *state.State, units, services []string) (result []*state.Unit, err error) {
45 unitsSet := set.NewStrings(units...)
46 for _, name := range services {
47 service, err := st.Service(name)
48 if err != nil {
49 return nil, err
50 }
51 units, err := service.AllUnits()
52 if err != nil {
53 return nil, err
54 }
55 for _, unit := range units {
56 unitsSet.Add(unit.Name())
57 }
58 }
59 for _, unitName := range unitsSet.Values() {
60 unit, err := st.Unit(unitName)
61 if err != nil {
62 return nil, err
63 }
64 // We only operate on principal units, and only thise that have an
65 // assigned machines.
66 if unit.IsPrincipal() {
67 if _, err := unit.AssignedMachineId(); err != nil {
68 return nil, err
69 }
70 } else {
71 return nil, fmt.Errorf("%s is not a principal unit", unit)
72 }
73 result = append(result, unit)
74 }
75 return result, nil
76}
77
78// Run the commands specified on the machines identified through the
79// list of machines, units and services.
80func (c *Client) Run(run params.RunParams) (results params.RunResults, err error) {
81 units, err := getAllUnitNames(c.api.state, run.Units, run.Services)
82 if err != nil {
83 return results, err
84 }
85 // We want to create a RemoteExec for each unit and each machine.
86 // If we have both a unit and a machine request, we run it twice,
87 // once for the unit inside the exec context using juju-run, and
88 // the other outside the context just using bash.
89 var params []*RemoteExec
90 var quotedCommands = utils.ShQuote(run.Commands)
91 for _, unit := range units {
92 // We know that the unit is both a principal unit, and that it has an
93 // assigned machine.
94 machineId, _ := unit.AssignedMachineId()
95 machine, err := c.api.state.Machine(machineId)
96 if err != nil {
97 return results, err
98 }
99 command := fmt.Sprintf("juju-run %s %s", unit.Name(), quotedCommands)
100 execParam := remoteParamsForMachine(machine, command, run.Timeout)
101 execParam.UnitId = unit.Name()
102 params = append(params, execParam)
103 }
104 for _, machineId := range run.Machines {
105 machine, err := c.api.state.Machine(machineId)
106 if err != nil {
107 return results, err
108 }
109 execParam := remoteParamsForMachine(machine, run.Commands, run.Timeout)
110 params = append(params, execParam)
111 }
112 return ParallelExecute(c.api.dataDir, params), nil
113}
114
115// RunOnAllMachines attempts to run the specified command on all the machines.
116func (c *Client) RunOnAllMachines(run params.RunParams) (params.RunResults, error) {
117 machines, err := c.api.state.AllMachines()
118 if err != nil {
119 return params.RunResults{}, err
120 }
121 var params []*RemoteExec
122 for _, machine := range machines {
123 params = append(params, remoteParamsForMachine(machine, run.Commands, run.Timeout))
124 }
125 return ParallelExecute(c.api.dataDir, params), nil
126}
127
128// RemoteExec extends the standard ssh.ExecParams by providing the machine and
129// perhaps the unit ids. These are then returned in the params.RunResult return
130// values.
131type RemoteExec struct {
132 ssh.ExecParams
133 MachineId string
134 UnitId string
135}
136
137// ParallelExecute executes all of the requests defined in the params,
138// using the system identity stored in the dataDir.
139func ParallelExecute(dataDir string, runParams []*RemoteExec) params.RunResults {
140 logger.Debugf("exec %#v", runParams)
141 var outstanding sync.WaitGroup
142 var lock sync.Mutex
143 var result []params.RunResult
144 identity := filepath.Join(dataDir, cloudinit.SystemIdentity)
145 for _, param := range runParams {
146 outstanding.Add(1)
147 logger.Debugf("exec on %s: %#v", param.MachineId, *param)
148 param.IdentityFile = identity
149 go func(param *RemoteExec) {
150 response, err := ssh.ExecuteCommandOnMachine(param.ExecParams)
151 logger.Debugf("reponse from %s: %v (err:%v)", param.MachineId, response, err)
152 execResponse := params.RunResult{
153 RemoteResponse: response,
154 MachineId: param.MachineId,
155 UnitId: param.UnitId,
156 }
157 if err != nil {
158 execResponse.Error = fmt.Sprint(err)
159 }
160
161 lock.Lock()
162 defer lock.Unlock()
163 result = append(result, execResponse)
164 outstanding.Done()
165 }(param)
166 }
167
168 outstanding.Wait()
169 sort.Sort(MachineOrder(result))
170 return params.RunResults{result}
171}
172
173// MachineOrder is used to provide the api to sort the results by the machine
174// id.
175type MachineOrder []params.RunResult
176
177func (a MachineOrder) Len() int { return len(a) }
178func (a MachineOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
179func (a MachineOrder) Less(i, j int) bool { return a[i].MachineId < a[j].MachineId }
0180
=== added file 'state/apiserver/client/run_test.go'
--- state/apiserver/client/run_test.go 1970-01-01 00:00:00 +0000
+++ state/apiserver/client/run_test.go 2014-01-12 20:08:17 +0000
@@ -0,0 +1,299 @@
1// Copyright 2013 Canonical Ltd.
2// Licensed under the AGPLv3, see LICENCE file for details.
3
4package client_test
5
6import (
7 "fmt"
8 "io/ioutil"
9 "os"
10 "path/filepath"
11 "time"
12
13 gc "launchpad.net/gocheck"
14
15 "launchpad.net/juju-core/cmd"
16 "launchpad.net/juju-core/instance"
17 "launchpad.net/juju-core/state"
18 "launchpad.net/juju-core/state/api/params"
19 "launchpad.net/juju-core/state/apiserver/client"
20 "launchpad.net/juju-core/testing"
21 jc "launchpad.net/juju-core/testing/checkers"
22 "launchpad.net/juju-core/utils/ssh"
23)
24
25type runSuite struct {
26 baseSuite
27}
28
29var _ = gc.Suite(&runSuite{})
30
31func (s *runSuite) addMachine(c *gc.C) *state.Machine {
32 machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
33 c.Assert(err, gc.IsNil)
34 return machine
35}
36
37func (s *runSuite) addMachineWithAddress(c *gc.C, address string) *state.Machine {
38 machine := s.addMachine(c)
39 machine.SetAddresses([]instance.Address{instance.NewAddress(address)})
40 return machine
41}
42
43func (s *runSuite) TestRemoteParamsForMachinePopulates(c *gc.C) {
44 machine := s.addMachine(c)
45 result := client.RemoteParamsForMachine(machine, "command", time.Minute)
46 c.Assert(result.Command, gc.Equals, "command")
47 c.Assert(result.Timeout, gc.Equals, time.Minute)
48 c.Assert(result.MachineId, gc.Equals, machine.Id())
49 // Now an empty host isn't particularly useful, but the machine doesn't
50 // have an address to use.
51 c.Assert(machine.Addresses(), gc.HasLen, 0)
52 c.Assert(result.Host, gc.Equals, "")
53}
54
55func (s *runSuite) TestRemoteParamsForMachinePopulatesWithAddress(c *gc.C) {
56 machine := s.addMachineWithAddress(c, "10.3.2.1")
57
58 result := client.RemoteParamsForMachine(machine, "command", time.Minute)
59 c.Assert(result.Command, gc.Equals, "command")
60 c.Assert(result.Timeout, gc.Equals, time.Minute)
61 c.Assert(result.MachineId, gc.Equals, machine.Id())
62 c.Assert(result.Host, gc.Equals, "ubuntu@10.3.2.1")
63}
64
65func (s *runSuite) addUnit(c *gc.C, service *state.Service) *state.Unit {
66 unit, err := service.AddUnit()
67 c.Assert(err, gc.IsNil)
68 err = unit.AssignToNewMachine()
69 c.Assert(err, gc.IsNil)
70 mId, err := unit.AssignedMachineId()
71 c.Assert(err, gc.IsNil)
72 machine, err := s.State.Machine(mId)
73 c.Assert(err, gc.IsNil)
74 machine.SetAddresses([]instance.Address{instance.NewAddress("10.3.2.1")})
75 return unit
76}
77
78func (s *runSuite) TestGetAllUnitNames(c *gc.C) {
79 charm := s.AddTestingCharm(c, "dummy")
80 magic, err := s.State.AddService("magic", "user-admin", charm)
81 s.addUnit(c, magic)
82 s.addUnit(c, magic)
83
84 notAssigned, err := s.State.AddService("not-assigned", "user-admin", charm)
85 c.Assert(err, gc.IsNil)
86 _, err = notAssigned.AddUnit()
87 c.Assert(err, gc.IsNil)
88
89 _, err = s.State.AddService("no-units", "user-admin", charm)
90 c.Assert(err, gc.IsNil)
91
92 for i, test := range []struct {
93 message string
94 expected []string
95 units []string
96 services []string
97 error string
98 }{{
99 message: "no units, expected nil slice",
100 }, {
101 message: "asking for a unit that isn't there",
102 units: []string{"foo/0"},
103 error: `unit "foo/0" not found`,
104 }, {
105 message: "asking for a service that isn't there",
106 services: []string{"foo"},
107 error: `service "foo" not found`,
108 }, {
109 message: "service with no units is not really an error",
110 services: []string{"no-units"},
111 }, {
112 message: "A service with units not assigned is an error",
113 services: []string{"not-assigned"},
114 error: `unit "not-assigned/0" is not assigned to a machine`,
115 }, {
116 message: "A service with units",
117 services: []string{"magic"},
118 expected: []string{"magic/0", "magic/1"},
119 }, {
120 message: "Asking for just a unit",
121 units: []string{"magic/0"},
122 expected: []string{"magic/0"},
123 }, {
124 message: "Asking for a unit, and the service",
125 services: []string{"magic"},
126 units: []string{"magic/0"},
127 expected: []string{"magic/0", "magic/1"},
128 }} {
129 c.Log(fmt.Sprintf("%v: %s", i, test.message))
130 result, err := client.GetAllUnitNames(s.State, test.units, test.services)
131 if test.error == "" {
132 c.Check(err, gc.IsNil)
133 var units []string
134 for _, unit := range result {
135 units = append(units, unit.Name())
136 }
137 c.Check(units, jc.SameContents, test.expected)
138 } else {
139 c.Check(err, gc.ErrorMatches, test.error)
140 }
141 }
142}
143
144func (s *runSuite) mockSSH(c *gc.C, cmd string) {
145 testbin := c.MkDir()
146 fakessh := filepath.Join(testbin, "ssh")
147 newPath := testbin + ":" + os.Getenv("PATH")
148 s.PatchEnvironment("PATH", newPath)
149 err := ioutil.WriteFile(fakessh, []byte(cmd), 0755)
150 c.Assert(err, gc.IsNil)
151}
152
153func (s *runSuite) TestParallelExecuteErrorsOnBlankHost(c *gc.C) {
154 s.mockSSH(c, echoInputShowArgs)
155
156 params := []*client.RemoteExec{
157 &client.RemoteExec{
158 ExecParams: ssh.ExecParams{
159 Command: "foo",
160 Timeout: testing.ShortWait,
161 },
162 },
163 }
164
165 runResults := client.ParallelExecute("/some/dir", params)
166 c.Assert(runResults.Results, gc.HasLen, 1)
167 result := runResults.Results[0]
168 c.Assert(result.Error, gc.Equals, "missing host address")
169}
170
171func (s *runSuite) TestParallelExecuteAddsIdentity(c *gc.C) {
172 s.mockSSH(c, echoInputShowArgs)
173
174 params := []*client.RemoteExec{
175 &client.RemoteExec{
176 ExecParams: ssh.ExecParams{
177 Host: "localhost",
178 Command: "foo",
179 Timeout: testing.ShortWait,
180 },
181 },
182 }
183
184 runResults := client.ParallelExecute("/some/dir", params)
185 c.Assert(runResults.Results, gc.HasLen, 1)
186 result := runResults.Results[0]
187 c.Assert(result.Error, gc.Equals, "")
188 c.Assert(string(result.Stderr), jc.Contains, "-i /some/dir/system-identity")
189}
190
191func (s *runSuite) TestParallelExecuteCopiesAcrossMachineAndUnit(c *gc.C) {
192 s.mockSSH(c, echoInputShowArgs)
193
194 params := []*client.RemoteExec{
195 &client.RemoteExec{
196 ExecParams: ssh.ExecParams{
197 Host: "localhost",
198 Command: "foo",
199 Timeout: testing.ShortWait,
200 },
201 MachineId: "machine-id",
202 UnitId: "unit-id",
203 },
204 }
205
206 runResults := client.ParallelExecute("/some/dir", params)
207 c.Assert(runResults.Results, gc.HasLen, 1)
208 result := runResults.Results[0]
209 c.Assert(result.Error, gc.Equals, "")
210 c.Assert(result.MachineId, gc.Equals, "machine-id")
211 c.Assert(result.UnitId, gc.Equals, "unit-id")
212}
213
214func (s *runSuite) TestRunOnAllMachines(c *gc.C) {
215 // Make three machines.
216 s.addMachineWithAddress(c, "10.3.2.1")
217 s.addMachineWithAddress(c, "10.3.2.2")
218 s.addMachineWithAddress(c, "10.3.2.3")
219
220 s.mockSSH(c, echoInput)
221
222 // hmm... this seems to be going through the api client, and from there
223 // through to the apiserver implementation. Not ideal, but it is how the
224 // other client tests are written.
225 client := s.APIState.Client()
226 results, err := client.RunOnAllMachines("hostname", testing.ShortWait)
227 c.Assert(err, gc.IsNil)
228 c.Assert(results, gc.HasLen, 3)
229 var expectedResults []params.RunResult
230 for i := 0; i < 3; i++ {
231 expectedResults = append(expectedResults,
232 params.RunResult{
233 RemoteResponse: cmd.RemoteResponse{Stdout: []byte("hostname\n")},
234 MachineId: fmt.Sprint(i),
235 })
236 }
237
238 c.Assert(results, jc.DeepEquals, expectedResults)
239}
240
241func (s *runSuite) TestRunMachineAndService(c *gc.C) {
242 // Make three machines.
243 s.addMachineWithAddress(c, "10.3.2.1")
244
245 charm := s.AddTestingCharm(c, "dummy")
246 magic, err := s.State.AddService("magic", "user-admin", charm)
247 s.addUnit(c, magic)
248 s.addUnit(c, magic)
249
250 s.mockSSH(c, echoInput)
251
252 // hmm... this seems to be going through the api client, and from there
253 // through to the apiserver implementation. Not ideal, but it is how the
254 // other client tests are written.
255 client := s.APIState.Client()
256 results, err := client.Run(
257 params.RunParams{
258 Commands: "hostname",
259 Timeout: testing.ShortWait,
260 Machines: []string{"0"},
261 Services: []string{"magic"},
262 })
263 c.Assert(err, gc.IsNil)
264 c.Assert(results, gc.HasLen, 3)
265 expectedResults := []params.RunResult{
266 params.RunResult{
267 RemoteResponse: cmd.RemoteResponse{Stdout: []byte("hostname\n")},
268 MachineId: "0",
269 },
270 params.RunResult{
271 RemoteResponse: cmd.RemoteResponse{Stdout: []byte("juju-run magic/0 'hostname'\n")},
272 MachineId: "1",
273 UnitId: "magic/0",
274 },
275 params.RunResult{
276 RemoteResponse: cmd.RemoteResponse{Stdout: []byte("juju-run magic/1 'hostname'\n")},
277 MachineId: "2",
278 UnitId: "magic/1",
279 },
280 }
281
282 c.Assert(results, jc.DeepEquals, expectedResults)
283}
284
285var echoInputShowArgs = `#!/bin/bash
286# Write the args to stderr
287echo "$*" >&2
288# And echo stdin to stdout
289while read line
290do echo $line
291done <&0
292`
293
294var echoInput = `#!/bin/bash
295# And echo stdin to stdout
296while read line
297do echo $line
298done <&0
299`
0300
=== modified file 'state/apiserver/login_test.go'
--- state/apiserver/login_test.go 2013-12-12 07:58:43 +0000
+++ state/apiserver/login_test.go 2014-01-12 20:08:17 +0000
@@ -50,6 +50,7 @@
50 "localhost:0",50 "localhost:0",
51 []byte(coretesting.ServerCert),51 []byte(coretesting.ServerCert),
52 []byte(coretesting.ServerKey),52 []byte(coretesting.ServerKey),
53 "",
53 )54 )
54 c.Assert(err, gc.IsNil)55 c.Assert(err, gc.IsNil)
55 info := &api.Info{56 info := &api.Info{
5657
=== modified file 'state/apiserver/root.go'
--- state/apiserver/root.go 2013-12-11 07:40:14 +0000
+++ state/apiserver/root.go 2014-01-12 20:08:17 +0000
@@ -66,7 +66,7 @@
66 logger.Errorf("error closing the RPC connection: %v", err)66 logger.Errorf("error closing the RPC connection: %v", err)
67 }67 }
68 }68 }
69 r.clientAPI.API = client.NewAPI(r.srv.state, r.resources, r)69 r.clientAPI.API = client.NewAPI(r.srv.state, r.resources, r, r.srv.dataDir)
70 r.pingTimeout = newPingTimeout(action, maxPingInterval)70 r.pingTimeout = newPingTimeout(action, maxPingInterval)
71 return r71 return r
72}72}
7373
=== modified file 'state/apiserver/server_test.go'
--- state/apiserver/server_test.go 2013-12-12 17:23:45 +0000
+++ state/apiserver/server_test.go 2014-01-12 20:08:17 +0000
@@ -36,7 +36,7 @@
36func (s *serverSuite) TestStop(c *gc.C) {36func (s *serverSuite) TestStop(c *gc.C) {
37 // Start our own instance of the server so we have37 // Start our own instance of the server so we have
38 // a handle on it to stop it.38 // a handle on it to stop it.
39 srv, err := apiserver.NewServer(s.State, "localhost:0", []byte(coretesting.ServerCert), []byte(coretesting.ServerKey))39 srv, err := apiserver.NewServer(s.State, "localhost:0", []byte(coretesting.ServerCert), []byte(coretesting.ServerKey), "")
40 c.Assert(err, gc.IsNil)40 c.Assert(err, gc.IsNil)
41 defer srv.Stop()41 defer srv.Stop()
4242

Subscribers

People subscribed via source and target branches

to status/vote changes: