Merge lp:~cmars/juju-core/resolve-cs-series into lp:~go-bot/juju-core/trunk
- resolve-cs-series
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Casey Marshall |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2559 |
Proposed branch: | lp:~cmars/juju-core/resolve-cs-series |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
1614 lines (+447/-151) 40 files modified
charm/charm.go (+5/-5) charm/charm_test.go (+4/-4) charm/url.go (+17/-2) charm/url_test.go (+20/-1) cmd/juju/addmachine.go (+2/-1) cmd/juju/bootstrap_test.go (+33/-20) cmd/juju/common.go (+39/-0) cmd/juju/deploy.go (+14/-11) cmd/juju/environment_test.go (+14/-4) cmd/juju/publish.go (+1/-1) cmd/juju/upgradecharm.go (+6/-11) cmd/juju/upgradejuju_test.go (+10/-6) cmd/plugins/juju-metadata/imagemetadata.go (+2/-2) cmd/plugins/juju-metadata/imagemetadata_test.go (+4/-2) environs/bootstrap/bootstrap_test.go (+16/-9) environs/bootstrap/synctools.go (+8/-3) environs/config/config.go (+59/-8) environs/config/config_test.go (+7/-6) environs/jujutest/livetests.go (+2/-2) environs/testing/tools.go (+9/-8) juju/apiconn_test.go (+1/-1) juju/testing/conn.go (+15/-2) juju/testing/instance.go (+2/-1) provider/azure/environ_test.go (+1/-1) provider/common/bootstrap.go (+1/-1) provider/dummy/environs.go (+1/-1) provider/ec2/ec2.go (+1/-1) provider/ec2/live_test.go (+1/-1) provider/ec2/local_test.go (+1/-1) provider/joyent/environ.go (+1/-1) provider/openstack/provider.go (+1/-1) state/api/client.go (+18/-0) state/api/params/params.go (+16/-0) state/apiserver/client/client.go (+40/-13) state/apiserver/client/client_test.go (+60/-5) testing/environ.go (+3/-1) worker/provisioner/container_initialisation_test.go (+4/-4) worker/provisioner/kvm-broker_test.go (+3/-4) worker/provisioner/lxc-broker_test.go (+3/-4) worker/provisioner/provisioner_test.go (+2/-2) |
To merge this branch: | bzr merge lp:~cmars/juju-core/resolve-cs-series |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+212755@code.launchpad.net |
Commit message
Resolve series with charm store in juju client.
For the deploy and upgradecharm commands, when a series is not provided, and a
default-series is not set in the environment config, the client will resolve
the series with the charm store, through the state server. If the client is
working with a 1.16 state server, it will resolve the series directly with the
charm store.
Existing environments will have a default-series, so they should have no
change in series selection.
New environments will continue to support the default-series config setting,
but it can be omitted, and is not set by default.
Description of the change
Resolve series with charm store in juju client.
For the deploy and upgradecharm commands, when a series is not provided, and a
default-series is not set in the environment config, the client will resolve
the series with the charm store, through the state server. If the client is
working with a 1.16 state server, it will resolve the series directly with the
charm store.
Existing environments will have a default-series, so they should have no
change in series selection.
New environments will continue to support the default-series config setting,
but it can be omitted, and is not set by default.
Casey Marshall (cmars) wrote : | # |
Casey Marshall (cmars) wrote : | # |
John A Meinel (jameinel) wrote : | # |
I think we have some common code that should be factored out, and I
think we need to consider how the code will operate with older Juju
server versions, but otherwise I think this looks pretty good.
https:/
File cmd/juju/deploy.go (right):
https:/
cmd/juju/
ctx.AbsPath(
This feels like stuff that definitely needs to be done, but shouldn't be
done in "cmd/juju/
Is there a reason this can't be part of more "library" code and tested
as such?
Specifically it feels like it should be a function that takes the
command line parameter (CharmName) a client and a conf, and returns a
fully qualified charm.URL (or error).
Especially given that you essentially implemented it 2 times in this
file. (at least the code you added below this looks a lot like the code
you added above this).
https:/
File cmd/juju/
https:/
cmd/juju/
And here, though this one seems to have a new SpecializeCharmRepo step
that I didn't see before.
https:/
File cmd/juju/
https:/
cmd/juju/
And implemented again here?
https:/
File state/api/client.go (right):
https:/
state/api/
We need to also add code to handle the fallback case. What do we do if
you are using juju 1.18 to deploy against a 1.16 server that doesn't
have the ResolveCharm API.
I'm guessing the answer is to print an error of:
"You must supply a series in your charm URL for juju < 1.18".
Either that, or we see that the ResolveCharm isn't available, and just
issue a warning with something like "server version is too old to
support ResolveCharm (juju <1.18) falling back to default series of
"precise""
To be clear, that compatibility code probably shouldn't be here. But
probably could be in the common helper I was outlining earlier.
Casey Marshall (cmars) wrote : | # |
PTAL. If everything looks ok, I'd still like to run a few more tests
against the deployed charm store update tomorrow, before landing.
Thanks,
Casey
https:/
File cmd/juju/deploy.go (right):
https:/
cmd/juju/
ctx.AbsPath(
On 2014/03/26 07:11:17, jameinel wrote:
> This feels like stuff that definitely needs to be done, but shouldn't
be done in
> "cmd/juju/
> Is there a reason this can't be part of more "library" code and tested
as such?
> Specifically it feels like it should be a function that takes the
command line
> parameter (CharmName) a client and a conf, and returns a fully
qualified
> charm.URL (or error).
> Especially given that you essentially implemented it 2 times in this
file. (at
> least the code you added below this looks a lot like the code you
added above
> this).
Good call, I've refactored functions to cmd/common.go, shared among
deploy and upgradecharm.
https:/
File cmd/juju/
https:/
cmd/juju/
On 2014/03/26 07:11:17, jameinel wrote:
> And here, though this one seems to have a new SpecializeCharmRepo step
that I
> didn't see before.
Done.
https:/
File cmd/juju/
https:/
cmd/juju/
On 2014/03/26 07:11:17, jameinel wrote:
> And implemented again here?
Done.
https:/
File state/api/client.go (right):
https:/
state/api/
On 2014/03/26 07:11:17, jameinel wrote:
> We need to also add code to handle the fallback case. What do we do if
you are
> using juju 1.18 to deploy against a 1.16 server that doesn't have the
> ResolveCharm API.
> I'm guessing the answer is to print an error of:
> "You must supply a series in your charm URL for juju < 1.18".
> Either that, or we see that the ResolveCharm isn't available, and just
issue a
> warning with something like "server version is too old to support
ResolveCharm
> (juju <1.18) falling back to default series of "precise""
> To be clear, that compatibility code probably shouldn't be here. But
probably
> could be in the common helper I was outlining earlier.
I decided to warn and fall back on "precise" for <1.18 state servers.
These existing environments will almost certainly already have a
default-series set by their bootstrapping.
I had originally intended to resolve the series directly with the charm
store for that case (since 1.16 hits the charm store directly for other
things), but it seems unlikely that such an environment would benefit
from it.
Casey Marshall (cmars) wrote : | # |
Please take a look.
Casey Marshall (cmars) wrote : | # |
The necessary charm store updates to support this change have been
deployed to store.juju.
It is safe to land this branch now, waiting for your review.
William Reade (fwereade) wrote : | # |
A few issues -- don't think any of them should be too much hassle to
resolve, the DefaultSeries return value and the API bulk call thing
should both be pretty mechanical.
https:/
File cmd/juju/common.go (right):
https:/
cmd/juju/
the API server, falling back to default series "precise".`)
We should in fact be guaranteed a value given a 1.16 state server, but I
heartily endorse this behaviour all the same.
https:/
File environs/
https:/
environs/
In-band errors squick me out... now that it's possible to have no
default-series, please add a ,ok return value. And I guess return false
if it *is* set, but is set to "".
https:/
File state/api/client.go (right):
https:/
state/api/
mmmmm I don't quite like that... can we make this a separate type?
params.CharmURL now means something different (although I'm surprised, a
little... I could have sworn that charm url fields marshalled to strings
without any hassle at all [0], and so I'd expect us to be using actual
*charm.URLs, and to have to add a new type for a reference anyway.)
[0] yeah, they should do, they have MarshalJSON and UnmarshalJSON.
Wonder why wedon't make use of it...
https:/
state/api/
"ResolveCharm", args, result); err != nil {
Bulk calls please, they don't have to be exposed in api.Client but it's
reasonable to expect to be able to resolve a few urls in one go, and we
should accommodate that in the interface.
https:/
File state/apiserver
https:/
state/apiserver
charm.ParseRefe
yeah, calling it URL is jarring -- new type OK?
https:/
File state/apiserver
https:/
state/apiserver
t.defaultSeries
set this outside loop?
https:/
state/apiserver
gc.Commentf(
t.charmName)
for i, test := range tests {
c.Logf("test %d: %#v", i, test)
...is a reasonably compact way to make the t...
Casey Marshall (cmars) wrote : | # |
Please take a look.
Casey Marshall (cmars) wrote : | # |
https:/
File environs/
https:/
environs/
On 2014/03/28 10:43:11, fwereade wrote:
> In-band errors squick me out... now that it's possible to have no
> default-series, please add a ,ok return value. And I guess return
false if it
> *is* set, but is set to "".
Done.
https:/
File state/api/client.go (right):
https:/
state/api/
On 2014/03/28 10:43:11, fwereade wrote:
> mmmmm I don't quite like that... can we make this a separate type?
> params.CharmURL now means something different (although I'm surprised,
a
> little... I could have sworn that charm url fields marshalled to
strings without
> any hassle at all [0], and so I'd expect us to be using actual
*charm.URLs, and
> to have to add a new type for a reference anyway.)
> [0] yeah, they should do, they have MarshalJSON and UnmarshalJSON.
Wonder why
> wedon't make use of it...
Done.
https:/
state/api/
"ResolveCharm", args, result); err != nil {
On 2014/03/28 10:43:11, fwereade wrote:
> Bulk calls please, they don't have to be exposed in api.Client but
it's
> reasonable to expect to be able to resolve a few urls in one go, and
we should
> accommodate that in the interface.
Done.
https:/
File state/apiserver
https:/
state/apiserver
charm.ParseRefe
On 2014/03/28 10:43:11, fwereade wrote:
> yeah, calling it URL is jarring -- new type OK?
Done.
https:/
File state/apiserver
https:/
state/apiserver
t.defaultSeries
On 2014/03/28 10:43:11, fwereade wrote:
> set this outside loop?
Varying the default series in the mock charm store among the test
conditions helps ensure the value isn't being hard-coded anywhere in the
apiserver, and it's useful for simulating a failure to resolve.
https:/
state/apiserver
gc.Commentf(
t.charmName)
On 2014/03/28 10:43:11, fwereade wrote:
> for i, test := range tests {
> c.Logf("test %d: %#v", i, test)
> ...is a reasonably compact way to make the test logs somewhat useful.
> I do really like calling it "test", not "t", though :).
Done.
https...
Casey Marshall (cmars) wrote : | # |
Please take a look.
William Reade (fwereade) wrote : | # |
Nearly there -- a quibble with the location of the PreferredSeries code,
and a bit of work on the API.
https:/
File state/apiserver
https:/
state/apiserver
t.defaultSeries
On 2014/03/31 19:57:07, cmars wrote:
> On 2014/03/28 10:43:11, fwereade wrote:
> > set this outside loop?
> Varying the default series in the mock charm store among the test
conditions
> helps ensure the value isn't being hard-coded anywhere in the
apiserver, and
> it's useful for simulating a failure to resolve.
Ofc, thanks. I think my eye slipped over the `t.` -- but it's much
harder to miss the `test.`. Thanks :).
https:/
File cmd/juju/
https:/
cmd/juju/
mm, I rather liked the PreferredSeries
were using an interface with just the DefaultSeries method. This doesn't
feel fundamental to a config -- does that seem sane?
https:/
File cmd/juju/common.go (right):
https:/
cmd/juju/
the API server, falling back to default series "precise".`)
PreferredSeries should surely be guaranteed to return non-""?
https:/
File environs/
https:/
environs/
How do we determine the value of this? I'm feeling like it really ought
to actually *be* the latest LTS, rather than just some global var set by
who-knows-who.
https:/
File state/api/
https:/
state/api/
I think we need an error per-result here, don't we?
https:/
File state/apiserver
https:/
state/apiserver
params.
sorry for the hassle, but we should always return one result per
request, and that result should contain either the answer or the error.
Client is a dog's dinner in this regard, and can only gradually and
incrementally improve, but the internal APIs are generally written as I
want them, and should be used as a model. The core idea is that bulk
APIs can be used for single calls, but single APIs can't be used in
bulk; as humans we are bad at predicting the future, and I'd r...
Casey Marshall (cmars) wrote : | # |
Thanks for reviewing. Couple of questions:
https:/
File cmd/juju/
https:/
cmd/juju/
On 2014/04/01 07:10:07, fwereade wrote:
> mm, I rather liked the PreferredSeries
it were
> using an interface with just the DefaultSeries method. This doesn't
feel
> fundamental to a config -- does that seem sane?
Will do. Should this live in environs/config or a different package?
https:/
File environs/
https:/
environs/
On 2014/04/01 07:10:07, fwereade wrote:
> How do we determine the value of this? I'm feeling like it really
ought to
> actually *be* the latest LTS, rather than just some global var set by
> who-knows-who.
I can try to get this from 'distro-info --lts'. If that fails to exec,
I'll use a hard-coded fallback (named as such). What do you think?
Casey Marshall (cmars) wrote : | # |
Please take a look.
Casey Marshall (cmars) wrote : | # |
I've addressed most of the feedback, but there is a problem lurking.
When config.
due to tools availability. I was able to resolve some of them, but not
all. Could really use some advice.
To reproduce the failures, this will simulate a post-trusty release
scenario in the proposed branch:
go test -ldflags "-X
launchpad.
These are the failures:
FAIL: bootstrap_
BootstrapSuite.
FAIL: bootstrap_
BootstrapSuite.
FAIL: upgradejuju_
FAIL: imagemetadata_
ImageMetadataSu
Everything passes when the latest LTS is precise.
Please advise,
Casey
https:/
File cmd/juju/
https:/
cmd/juju/
On 2014/04/01 07:10:07, fwereade wrote:
> mm, I rather liked the PreferredSeries
it were
> using an interface with just the DefaultSeries method. This doesn't
feel
> fundamental to a config -- does that seem sane?
Done.
https:/
File cmd/juju/common.go (right):
https:/
cmd/juju/
the API server, falling back to default series "precise".`)
On 2014/04/01 07:10:07, fwereade wrote:
> PreferredSeries should surely be guaranteed to return non-""?
Done.
https:/
File environs/
https:/
environs/
On 2014/04/01 15:05:12, cmars wrote:
> On 2014/04/01 07:10:07, fwereade wrote:
> > How do we determine the value of this? I'm feeling like it really
ought to
> > actually *be* the latest LTS, rather than just some global var set
by
> > who-knows-who.
> I can try to get this from 'distro-info --lts'. If that fails to exec,
I'll use
> a hard-coded fallback (named as such). What do you think?
Done.
https:/
File state/api/
https:/
state/api/
On 2014/04/01 07:10:07, fwereade wrote:
> I think we need an error per-result here, don't we?
Done.
https:/
File state/apiserver
https:/
state/apiserver
params.
On 2014/04/01 07:10:07, fwereade wrote:
> sorry for the hassle, but we should always return ...
Casey Marshall (cmars) wrote : | # |
Please take a look.
Casey Marshall (cmars) wrote : | # |
Tests affected by the LTS change wrt uploading tools are now all passing
when the latest LTS series is "trusty". To run the tests as if trusty
were already released, run with:
go test -ldflags "-X
launchpad.
(This var is normally initialized when not set, from `distro-info
--lts`)
Added wallyworld. I could use a review from a tools distribution
perspective, as this is an area I am only superficially familiar with.
They were affected by the LTS series change so I've tried to make fixes
where test cases and setup had "precise" hardcoded, did not anticipate
having to upload tools other than a DefaultSeries, etc.
Thanks!
-Casey
Casey Marshall (cmars) wrote : | # |
Please take a look.
Ian Booth (wallyworld) wrote : | # |
I think the tools changes are ok. What I like is that when we start
running the tests on a trusty host, the test set up will, where
relevant, continue to correctly use a series matching the host of which
the tests are running, hence replicating the current test behaviour.
Having said that, I still fear our test coverage of the various
permutations in this area is not complete, but that's outside the scope
of this branch. There's still the possibility of perhaps a subtle bug
being introduced with regard to series handling, as has been seen before
when this sort of this was tweaked, but it's hard to know for sure.
William Reade (fwereade) wrote : | # |
Looks good, would appreciate at least a short chat before landing -- in
particular, I'm not quite clear on the forces that lead us to use
FakeDefaultSeries sometimes, and LatestLts at others.
https:/
File cmd/juju/
https:/
cmd/juju/
strings.
not quite sure that config is the right package for these -- but I'm not
sure I can think of a better one. Unless you want to create a tiny,
hyper-focused, series package somewhere? I'm sure it'll grow excitingly
as we deal with other OSs...
https:/
File cmd/juju/
https:/
cmd/juju/
charm.InferRepo
We still need ctx.AbsPath, I think. Would you change the relevant test
to use a path relative to the working dir so we check this properly
please?
https:/
File environs/
https:/
environs/
yeah, this feels a bit tacked-on here. Let's give it its own package if
we don't think of anywhere better.
https:/
environs/
this is guaranteed to be a string if it's got this far, right?
https:/
File provider/
https:/
provider/
EnsureBootstrap
cons.Arch)
quibble quibble, we should probably allow bootstrap on any series we can
find tools for. Out of scope today, but maybe worth a bug?
https:/
File provider/ec2/ec2.go (right):
https:/
provider/
config.
this seems a bit weird, but so did the original. let it stand.
https:/
File provider/
https:/
provider/
wondering if this should be LatestLts? I presume you have a good reason
it isn't, but it's not immediately clear ;)
https:/
File state/api/client.go (right):
https:/
state/api/
Casey Marshall (cmars) wrote : | # |
PTAL
https:/
File cmd/juju/
https:/
cmd/juju/
strings.
On 2014/04/03 13:34:15, fwereade wrote:
> not quite sure that config is the right package for these -- but I'm
not sure I
> can think of a better one. Unless you want to create a tiny,
hyper-focused,
> series package somewhere? I'm sure it'll grow excitingly as we deal
with other
> OSs...
LP: #1301999
https:/
File cmd/juju/
https:/
cmd/juju/
charm.InferRepo
On 2014/04/03 13:34:15, fwereade wrote:
> We still need ctx.AbsPath, I think. Would you change the relevant test
to use a
> path relative to the working dir so we check this properly please?
Ah, must have botched this in a prior merge. Restored it.
https:/
File environs/
https:/
environs/
On 2014/04/03 13:34:15, fwereade wrote:
> yeah, this feels a bit tacked-on here. Let's give it its own package
if we don't
> think of anywhere better.
LP: #1301999
https:/
environs/
On 2014/04/03 13:34:15, fwereade wrote:
> this is guaranteed to be a string if it's got this far, right?
Non-string values that make their way in here will be ignored with a
warning.
https:/
File provider/
https:/
provider/
EnsureBootstrap
cons.Arch)
On 2014/04/03 13:34:15, fwereade wrote:
> quibble quibble, we should probably allow bootstrap on any series we
can find
> tools for. Out of scope today, but maybe worth a bug?
LP: #1302005
https:/
File provider/
https:/
provider/
On 2014/04/03 13:34:15, fwereade wrote:
> wondering if this should be LatestLts? I presume you have a good
reason it
> isn't, but it's not immediately clear ;)
It's to separate what is used for default-series: in tests from the
preferred series selection. This allows us to test scenarios against
different configured series vs. what will be preferred.
Most of what's affected atm is the tools setup & bootstrapping for some
tests. There are still some areas ...
William Reade (fwereade) wrote : | # |
LGTM with one trivial. Thanks for this, it's a monster diff -- let's
throw it at CI and see what we've missed ;).
https:/
File charm/url_test.go (right):
https:/
charm/url_
add one for unmarshalling gibberish, just for safety's sake
Preview Diff
1 | === modified file 'charm/charm.go' |
2 | --- charm/charm.go 2014-03-17 18:46:17 +0000 |
3 | +++ charm/charm.go 2014-04-03 17:37:27 +0000 |
4 | @@ -30,10 +30,10 @@ |
5 | return ReadBundle(path) |
6 | } |
7 | |
8 | -// InferRepository returns a charm repository inferred from |
9 | -// the provided URL. Local URLs will use the provided path. |
10 | -func InferRepository(curl *URL, localRepoPath string) (repo Repository, err error) { |
11 | - switch curl.Schema { |
12 | +// InferRepository returns a charm repository inferred from the provided charm |
13 | +// reference. Local references will use the provided path. |
14 | +func InferRepository(ref Reference, localRepoPath string) (repo Repository, err error) { |
15 | + switch ref.Schema { |
16 | case "cs": |
17 | repo = Store |
18 | case "local": |
19 | @@ -42,7 +42,7 @@ |
20 | } |
21 | repo = &LocalRepository{Path: localRepoPath} |
22 | default: |
23 | - return nil, fmt.Errorf("unknown schema for charm URL %q", curl) |
24 | + return nil, fmt.Errorf("unknown schema for charm reference %q", ref) |
25 | } |
26 | return |
27 | } |
28 | |
29 | === modified file 'charm/charm_test.go' |
30 | --- charm/charm_test.go 2014-03-18 05:08:25 +0000 |
31 | +++ charm/charm_test.go 2014-04-03 17:37:27 +0000 |
32 | @@ -48,7 +48,7 @@ |
33 | c.Logf("test %d", i) |
34 | curl, err := charm.InferURL(t.url, "precise") |
35 | c.Assert(err, gc.IsNil) |
36 | - repo, err := charm.InferRepository(curl, "/some/path") |
37 | + repo, err := charm.InferRepository(curl.Reference, "/some/path") |
38 | c.Assert(err, gc.IsNil) |
39 | switch repo := repo.(type) { |
40 | case *charm.LocalRepository: |
41 | @@ -59,11 +59,11 @@ |
42 | } |
43 | curl, err := charm.InferURL("local:whatever", "precise") |
44 | c.Assert(err, gc.IsNil) |
45 | - _, err = charm.InferRepository(curl, "") |
46 | + _, err = charm.InferRepository(curl.Reference, "") |
47 | c.Assert(err, gc.ErrorMatches, "path to local repository not specified") |
48 | curl.Schema = "foo" |
49 | - _, err = charm.InferRepository(curl, "") |
50 | - c.Assert(err, gc.ErrorMatches, "unknown schema for charm URL.*") |
51 | + _, err = charm.InferRepository(curl.Reference, "") |
52 | + c.Assert(err, gc.ErrorMatches, "unknown schema for charm reference.*") |
53 | } |
54 | |
55 | func checkDummy(c *gc.C, f charm.Charm, path string) { |
56 | |
57 | === modified file 'charm/url.go' |
58 | --- charm/url.go 2014-03-24 15:35:41 +0000 |
59 | +++ charm/url.go 2014-04-03 17:37:27 +0000 |
60 | @@ -284,8 +284,6 @@ |
61 | return nil |
62 | } |
63 | |
64 | -var jsonNull = []byte("null") |
65 | - |
66 | func (u *URL) MarshalJSON() ([]byte, error) { |
67 | if u == nil { |
68 | panic("cannot marshal nil *charm.URL") |
69 | @@ -306,6 +304,23 @@ |
70 | return nil |
71 | } |
72 | |
73 | +func (r *Reference) MarshalJSON() ([]byte, error) { |
74 | + return json.Marshal(r.String()) |
75 | +} |
76 | + |
77 | +func (r *Reference) UnmarshalJSON(b []byte) error { |
78 | + var s string |
79 | + if err := json.Unmarshal(b, &s); err != nil { |
80 | + return err |
81 | + } |
82 | + ref, _, err := ParseReference(s) |
83 | + if err != nil { |
84 | + return err |
85 | + } |
86 | + *r = ref |
87 | + return nil |
88 | +} |
89 | + |
90 | // Quote translates a charm url string into one which can be safely used |
91 | // in a file path. ASCII letters, ASCII digits, dot and dash stay the |
92 | // same; other characters are translated to their hex representation |
93 | |
94 | === modified file 'charm/url_test.go' |
95 | --- charm/url_test.go 2014-03-24 15:35:41 +0000 |
96 | +++ charm/url_test.go 2014-04-03 17:37:27 +0000 |
97 | @@ -258,7 +258,7 @@ |
98 | Unmarshal: json.Unmarshal, |
99 | }} |
100 | |
101 | -func (s *URLSuite) TestCodecs(c *gc.C) { |
102 | +func (s *URLSuite) TestURLCodecs(c *gc.C) { |
103 | for i, codec := range codecs { |
104 | c.Logf("codec %d", i) |
105 | type doc struct { |
106 | @@ -279,6 +279,25 @@ |
107 | } |
108 | } |
109 | |
110 | +func (s *URLSuite) TestReferenceJSON(c *gc.C) { |
111 | + ref, _, err := charm.ParseReference("cs:series/name") |
112 | + c.Assert(err, gc.IsNil) |
113 | + data, err := json.Marshal(&ref) |
114 | + c.Assert(err, gc.IsNil) |
115 | + c.Check(string(data), gc.Equals, `"cs:name"`) |
116 | + |
117 | + var parsed charm.Reference |
118 | + err = json.Unmarshal(data, &parsed) |
119 | + c.Assert(err, gc.IsNil) |
120 | + c.Check(parsed, gc.DeepEquals, ref) |
121 | + |
122 | + // unmarshalling json gibberish and invalid charm reference strings |
123 | + for _, value := range []string{":{", `"cs:{}+<"`, `"cs:~_~/f00^^&^/baaaar$%-?"`} { |
124 | + err = json.Unmarshal([]byte(value), &parsed) |
125 | + c.Check(err, gc.NotNil) |
126 | + } |
127 | +} |
128 | + |
129 | type QuoteSuite struct{} |
130 | |
131 | var _ = gc.Suite(&QuoteSuite{}) |
132 | |
133 | === modified file 'cmd/juju/addmachine.go' |
134 | --- cmd/juju/addmachine.go 2014-04-01 04:53:43 +0000 |
135 | +++ cmd/juju/addmachine.go 2014-04-03 17:37:27 +0000 |
136 | @@ -12,6 +12,7 @@ |
137 | "launchpad.net/juju-core/cmd" |
138 | "launchpad.net/juju-core/cmd/envcmd" |
139 | "launchpad.net/juju-core/constraints" |
140 | + "launchpad.net/juju-core/environs/config" |
141 | "launchpad.net/juju-core/environs/manual" |
142 | "launchpad.net/juju-core/instance" |
143 | "launchpad.net/juju-core/juju" |
144 | @@ -131,7 +132,7 @@ |
145 | if err != nil { |
146 | return "", err |
147 | } |
148 | - series = conf.DefaultSeries() |
149 | + series = config.PreferredSeries(conf) |
150 | } |
151 | template := state.MachineTemplate{ |
152 | Series: series, |
153 | |
154 | === modified file 'cmd/juju/bootstrap_test.go' |
155 | --- cmd/juju/bootstrap_test.go 2014-04-01 03:11:16 +0000 |
156 | +++ cmd/juju/bootstrap_test.go 2014-04-03 17:37:27 +0000 |
157 | @@ -15,6 +15,7 @@ |
158 | "launchpad.net/juju-core/cmd" |
159 | "launchpad.net/juju-core/constraints" |
160 | "launchpad.net/juju-core/environs" |
161 | + "launchpad.net/juju-core/environs/config" |
162 | "launchpad.net/juju-core/environs/configstore" |
163 | "launchpad.net/juju-core/environs/filestorage" |
164 | "launchpad.net/juju-core/environs/imagemetadata" |
165 | @@ -119,7 +120,8 @@ |
166 | func (s *BootstrapSuite) runAllowRetriesTest(c *gc.C, test bootstrapRetryTest) { |
167 | toolsVersions := envtesting.VAll |
168 | if test.version != "" { |
169 | - testVersion := version.MustParseBinary(test.version) |
170 | + useVersion := strings.Replace(test.version, "%LTS%", config.LatestLtsSeries(), 1) |
171 | + testVersion := version.MustParseBinary(useVersion) |
172 | s.PatchValue(&version.Current, testVersion) |
173 | if test.addVersionToSource { |
174 | toolsVersions = append([]version.Binary{}, toolsVersions...) |
175 | @@ -201,8 +203,9 @@ |
176 | defer fake.Restore() |
177 | |
178 | if test.version != "" { |
179 | + useVersion := strings.Replace(test.version, "%LTS%", config.LatestLtsSeries(), 1) |
180 | origVersion := version.Current |
181 | - version.Current = version.MustParseBinary(test.version) |
182 | + version.Current = version.MustParseBinary(useVersion) |
183 | defer func() { version.Current = origVersion }() |
184 | } |
185 | |
186 | @@ -217,7 +220,7 @@ |
187 | uploadCount := len(test.uploads) |
188 | if uploadCount == 0 { |
189 | usefulVersion := version.Current |
190 | - usefulVersion.Series = env.Config().DefaultSeries() |
191 | + usefulVersion.Series = config.PreferredSeries(env.Config()) |
192 | envtesting.AssertUploadFakeToolsVersions(c, env.Storage(), usefulVersion) |
193 | } |
194 | |
195 | @@ -245,6 +248,7 @@ |
196 | urls := list.URLs() |
197 | c.Check(urls, gc.HasLen, len(test.uploads)) |
198 | for _, v := range test.uploads { |
199 | + v := strings.Replace(v, "%LTS%", config.LatestLtsSeries(), 1) |
200 | c.Logf("seeking: " + v) |
201 | vers := version.MustParseBinary(v) |
202 | _, found := urls[vers] |
203 | @@ -299,10 +303,9 @@ |
204 | args: []string{"--series", "fine"}, |
205 | err: `--series requires --upload-tools`, |
206 | }, { |
207 | - info: "bad environment", |
208 | - version: "1.2.3-precise-amd64", |
209 | - args: []string{"-e", "brokenenv"}, |
210 | - err: `dummy.Bootstrap is broken`, |
211 | + info: "bad environment", |
212 | + args: []string{"-e", "brokenenv"}, |
213 | + err: `dummy.Bootstrap is broken`, |
214 | }, { |
215 | info: "constraints", |
216 | args: []string{"--constraints", "mem=4G cpu-cores=4"}, |
217 | @@ -312,9 +315,9 @@ |
218 | version: "1.2.3-saucy-amd64", |
219 | args: []string{"--upload-tools"}, |
220 | uploads: []string{ |
221 | - "1.2.3.1-saucy-amd64", // from version.Current |
222 | - "1.2.3.1-raring-amd64", // from env.Config().DefaultSeries() |
223 | - "1.2.3.1-precise-amd64", // from environs/config.DefaultSeries |
224 | + "1.2.3.1-saucy-amd64", // from version.Current |
225 | + "1.2.3.1-raring-amd64", // from env.Config().DefaultSeries() |
226 | + "1.2.3.1-%LTS%-amd64", // from environs/config.DefaultSeries |
227 | }, |
228 | }, { |
229 | info: "--upload-tools uses arch from constraint if it matches current version", |
230 | @@ -322,18 +325,18 @@ |
231 | hostArch: "ppc64", |
232 | args: []string{"--upload-tools", "--constraints", "arch=ppc64"}, |
233 | uploads: []string{ |
234 | - "1.3.3.1-saucy-ppc64", // from version.Current |
235 | - "1.3.3.1-raring-ppc64", // from env.Config().DefaultSeries() |
236 | - "1.3.3.1-precise-ppc64", // from environs/config.DefaultSeries |
237 | + "1.3.3.1-saucy-ppc64", // from version.Current |
238 | + "1.3.3.1-raring-ppc64", // from env.Config().DefaultSeries() |
239 | + "1.3.3.1-%LTS%-ppc64", // from environs/config.DefaultSeries |
240 | }, |
241 | constraints: constraints.MustParse("arch=ppc64"), |
242 | }, { |
243 | info: "--upload-tools only uploads each file once", |
244 | - version: "1.2.3-precise-amd64", |
245 | + version: "1.2.3-%LTS%-amd64", |
246 | args: []string{"--upload-tools"}, |
247 | uploads: []string{ |
248 | "1.2.3.1-raring-amd64", |
249 | - "1.2.3.1-precise-amd64", |
250 | + "1.2.3.1-%LTS%-amd64", |
251 | }, |
252 | }, { |
253 | info: "--upload-tools rejects invalid series", |
254 | @@ -357,7 +360,7 @@ |
255 | args: []string{"--upload-tools"}, |
256 | uploads: []string{ |
257 | "1.2.3.5-raring-amd64", |
258 | - "1.2.3.5-precise-amd64", |
259 | + "1.2.3.5-%LTS%-amd64", |
260 | }, |
261 | }} |
262 | |
263 | @@ -365,7 +368,7 @@ |
264 | env, fake := makeEmptyFakeHome(c) |
265 | defer fake.Restore() |
266 | defaultSeriesVersion := version.Current |
267 | - defaultSeriesVersion.Series = env.Config().DefaultSeries() |
268 | + defaultSeriesVersion.Series = config.PreferredSeries(env.Config()) |
269 | |
270 | ctx := coretesting.Context(c) |
271 | code := cmd.Main(&BootstrapCommand{}, ctx, nil) |
272 | @@ -383,7 +386,7 @@ |
273 | env, fake := makeEmptyFakeHome(c) |
274 | defer fake.Restore() |
275 | defaultSeriesVersion := version.Current |
276 | - defaultSeriesVersion.Series = env.Config().DefaultSeries() |
277 | + defaultSeriesVersion.Series = config.PreferredSeries(env.Config()) |
278 | |
279 | store, err := configstore.Default() |
280 | c.Assert(err, gc.IsNil) |
281 | @@ -515,7 +518,13 @@ |
282 | c.Assert(err, gc.IsNil) |
283 | c.Logf("found: " + list.String()) |
284 | urls := list.URLs() |
285 | - c.Assert(urls, gc.HasLen, 2) |
286 | + expectedUrlCount := 2 |
287 | + |
288 | + // There will be distinct tools for each of these if they are different |
289 | + if config.LatestLtsSeries() != coretesting.FakeDefaultSeries { |
290 | + expectedUrlCount++ |
291 | + } |
292 | + c.Assert(urls, gc.HasLen, expectedUrlCount) |
293 | expectedVers := []version.Binary{ |
294 | version.MustParseBinary(fmt.Sprintf("1.7.3.1-%s-%s", otherSeries, version.Current.Arch)), |
295 | version.MustParseBinary(fmt.Sprintf("1.7.3.1-%s-%s", version.Current.Series, version.Current.Arch)), |
296 | @@ -556,7 +565,11 @@ |
297 | code := cmd.Main(&BootstrapCommand{}, context, nil) |
298 | c.Assert(code, gc.Equals, 1) |
299 | errText := context.Stderr.(*bytes.Buffer).String() |
300 | - expectedErrText := "uploading tools for series \\[precise raring\\]\n" |
301 | + expectedErrText := "uploading tools for series \\[precise " |
302 | + if config.LatestLtsSeries() != coretesting.FakeDefaultSeries { |
303 | + expectedErrText += config.LatestLtsSeries() + " " |
304 | + } |
305 | + expectedErrText += "raring\\]\n" |
306 | expectedErrText += "error: cannot upload bootstrap tools: an error\n" |
307 | c.Assert(errText, gc.Matches, expectedErrText) |
308 | } |
309 | |
310 | === modified file 'cmd/juju/common.go' |
311 | --- cmd/juju/common.go 2014-03-26 04:36:17 +0000 |
312 | +++ cmd/juju/common.go 2014-04-03 17:37:27 +0000 |
313 | @@ -4,10 +4,13 @@ |
314 | package main |
315 | |
316 | import ( |
317 | + "launchpad.net/juju-core/charm" |
318 | "launchpad.net/juju-core/cmd" |
319 | "launchpad.net/juju-core/environs" |
320 | + "launchpad.net/juju-core/environs/config" |
321 | "launchpad.net/juju-core/environs/configstore" |
322 | "launchpad.net/juju-core/errors" |
323 | + "launchpad.net/juju-core/state/api" |
324 | ) |
325 | |
326 | // destroyPreparedEnviron destroys the environment and logs an error if it fails. |
327 | @@ -45,3 +48,39 @@ |
328 | } |
329 | return environ, cleanup, nil |
330 | } |
331 | + |
332 | +// resolveCharmURL returns a resolved charm URL, given a charm location string. |
333 | +// If the series is not resolved, the environment default-series is used, or if |
334 | +// not set, the series is resolved with the state server. |
335 | +func resolveCharmURL(url string, client *api.Client, conf *config.Config) (*charm.URL, error) { |
336 | + ref, series, err := charm.ParseReference(url) |
337 | + if err != nil { |
338 | + return nil, err |
339 | + } |
340 | + // If series is not set, use configured default series |
341 | + if series == "" { |
342 | + if defaultSeries, ok := conf.DefaultSeries(); ok { |
343 | + series = defaultSeries |
344 | + } |
345 | + } |
346 | + // Otherwise, look up the best supported series for this charm |
347 | + if series == "" { |
348 | + return client.ResolveCharm(ref) |
349 | + } |
350 | + return &charm.URL{Reference: ref, Series: series}, nil |
351 | +} |
352 | + |
353 | +// resolveCharmURL1dot16 returns a resolved charm URL for older state servers |
354 | +// that do not support ResolveCharm. The default series "precise" is |
355 | +// appropriate for these environments. |
356 | +func resolveCharmURL1dot16(url string, conf *config.Config) (*charm.URL, error) { |
357 | + ref, series, err := charm.ParseReference(url) |
358 | + if err != nil { |
359 | + return nil, err |
360 | + } |
361 | + |
362 | + if series == "" { |
363 | + series = config.PreferredSeries(conf) |
364 | + } |
365 | + return &charm.URL{Reference: ref, Series: series}, err |
366 | +} |
367 | |
368 | === modified file 'cmd/juju/deploy.go' |
369 | --- cmd/juju/deploy.go 2014-04-01 14:55:36 +0000 |
370 | +++ cmd/juju/deploy.go 2014-04-03 17:37:27 +0000 |
371 | @@ -154,11 +154,13 @@ |
372 | if err != nil { |
373 | return err |
374 | } |
375 | - curl, err := charm.InferURL(c.CharmName, conf.DefaultSeries()) |
376 | + |
377 | + curl, err := resolveCharmURL(c.CharmName, client, conf) |
378 | if err != nil { |
379 | return err |
380 | } |
381 | - repo, err := charm.InferRepository(curl, ctx.AbsPath(c.RepoPath)) |
382 | + |
383 | + repo, err := charm.InferRepository(curl.Reference, ctx.AbsPath(c.RepoPath)) |
384 | if err != nil { |
385 | return err |
386 | } |
387 | @@ -260,15 +262,16 @@ |
388 | if err != nil { |
389 | return err |
390 | } |
391 | - curl, err := charm.InferURL(c.CharmName, conf.DefaultSeries()) |
392 | - if err != nil { |
393 | - return err |
394 | - } |
395 | - repo, err := charm.InferRepository(curl, ctx.AbsPath(c.RepoPath)) |
396 | - if err != nil { |
397 | - return err |
398 | - } |
399 | - |
400 | + |
401 | + curl, err := resolveCharmURL1dot16(c.CharmName, conf) |
402 | + if err != nil { |
403 | + return err |
404 | + } |
405 | + |
406 | + repo, err := charm.InferRepository(curl.Reference, c.RepoPath) |
407 | + if err != nil { |
408 | + return err |
409 | + } |
410 | repo = config.SpecializeCharmRepo(repo, conf) |
411 | |
412 | // TODO(fwereade) it's annoying to roundtrip the bytes through the client |
413 | |
414 | === modified file 'cmd/juju/environment_test.go' |
415 | --- cmd/juju/environment_test.go 2014-03-26 05:44:41 +0000 |
416 | +++ cmd/juju/environment_test.go 2014-04-03 17:37:27 +0000 |
417 | @@ -124,12 +124,22 @@ |
418 | } |
419 | |
420 | func (s *SetEnvironmentSuite) TestChangeDefaultSeries(c *gc.C) { |
421 | - _, err := testing.RunCommand(c, &SetEnvironmentCommand{}, []string{"default-series=raring"}) |
422 | - c.Assert(err, gc.IsNil) |
423 | - |
424 | + // default-series not set |
425 | stateConfig, err := s.State.EnvironConfig() |
426 | c.Assert(err, gc.IsNil) |
427 | - c.Assert(stateConfig.DefaultSeries(), gc.Equals, "raring") |
428 | + series, ok := stateConfig.DefaultSeries() |
429 | + c.Assert(ok, gc.Equals, true) |
430 | + c.Assert(series, gc.Equals, "precise") // default-series set in RepoSuite.SetUpTest |
431 | + |
432 | + _, err = testing.RunCommand(c, &SetEnvironmentCommand{}, []string{"default-series=raring"}) |
433 | + c.Assert(err, gc.IsNil) |
434 | + |
435 | + stateConfig, err = s.State.EnvironConfig() |
436 | + c.Assert(err, gc.IsNil) |
437 | + series, ok = stateConfig.DefaultSeries() |
438 | + c.Assert(ok, gc.Equals, true) |
439 | + c.Assert(series, gc.Equals, "raring") |
440 | + c.Assert(config.PreferredSeries(stateConfig), gc.Equals, "raring") |
441 | } |
442 | |
443 | func (s *SetEnvironmentSuite) TestChangeBooleanAttribute(c *gc.C) { |
444 | |
445 | === modified file 'cmd/juju/publish.go' |
446 | --- cmd/juju/publish.go 2014-04-01 04:53:43 +0000 |
447 | +++ cmd/juju/publish.go 2014-04-03 17:37:27 +0000 |
448 | @@ -115,7 +115,7 @@ |
449 | pushLocation = c.changePushLocation(pushLocation) |
450 | } |
451 | |
452 | - repo, err := charm.InferRepository(curl, "/not/important") |
453 | + repo, err := charm.InferRepository(curl.Reference, "/not/important") |
454 | if err != nil { |
455 | return err |
456 | } |
457 | |
458 | === modified file 'cmd/juju/upgradecharm.go' |
459 | --- cmd/juju/upgradecharm.go 2014-04-01 04:53:43 +0000 |
460 | +++ cmd/juju/upgradecharm.go 2014-04-03 17:37:27 +0000 |
461 | @@ -134,8 +134,7 @@ |
462 | |
463 | var newURL *charm.URL |
464 | if c.SwitchURL != "" { |
465 | - // A new charm URL was explicitly specified. |
466 | - newURL, err = charm.InferURL(c.SwitchURL, conf.DefaultSeries()) |
467 | + newURL, err = resolveCharmURL(c.SwitchURL, client, conf) |
468 | if err != nil { |
469 | return err |
470 | } |
471 | @@ -143,11 +142,11 @@ |
472 | // No new URL specified, but revision might have been. |
473 | newURL = oldURL.WithRevision(c.Revision) |
474 | } |
475 | - repo, err := charm.InferRepository(newURL, ctx.AbsPath(c.RepoPath)) |
476 | + |
477 | + repo, err := charm.InferRepository(newURL.Reference, ctx.AbsPath(c.RepoPath)) |
478 | if err != nil { |
479 | return err |
480 | } |
481 | - |
482 | repo = config.SpecializeCharmRepo(repo, conf) |
483 | |
484 | // If no explicit revision was set with either SwitchURL |
485 | @@ -203,11 +202,7 @@ |
486 | var newURL *charm.URL |
487 | if c.SwitchURL != "" { |
488 | // A new charm URL was explicitly specified. |
489 | - conf, err := conn.State.EnvironConfig() |
490 | - if err != nil { |
491 | - return err |
492 | - } |
493 | - newURL, err = charm.InferURL(c.SwitchURL, conf.DefaultSeries()) |
494 | + newURL, err = resolveCharmURL1dot16(c.SwitchURL, conf) |
495 | if err != nil { |
496 | return err |
497 | } |
498 | @@ -215,11 +210,11 @@ |
499 | // No new URL specified, but revision might have been. |
500 | newURL = oldURL.WithRevision(c.Revision) |
501 | } |
502 | - repo, err := charm.InferRepository(newURL, ctx.AbsPath(c.RepoPath)) |
503 | + |
504 | + repo, err := charm.InferRepository(newURL.Reference, ctx.AbsPath(c.RepoPath)) |
505 | if err != nil { |
506 | return err |
507 | } |
508 | - |
509 | repo = config.SpecializeCharmRepo(repo, conf) |
510 | |
511 | // If no explicit revision was set with either SwitchURL |
512 | |
513 | === modified file 'cmd/juju/upgradejuju_test.go' |
514 | --- cmd/juju/upgradejuju_test.go 2014-03-17 06:05:54 +0000 |
515 | +++ cmd/juju/upgradejuju_test.go 2014-04-03 17:37:27 +0000 |
516 | @@ -14,6 +14,7 @@ |
517 | jc "github.com/juju/testing/checkers" |
518 | gc "launchpad.net/gocheck" |
519 | |
520 | + "launchpad.net/juju-core/environs/config" |
521 | "launchpad.net/juju-core/environs/filestorage" |
522 | "launchpad.net/juju-core/environs/storage" |
523 | "launchpad.net/juju-core/environs/sync" |
524 | @@ -225,14 +226,14 @@ |
525 | agentVersion: "2.0.0", |
526 | args: []string{"--upload-tools"}, |
527 | expectVersion: "2.2.0.1", |
528 | - expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-precise-amd64", "2.2.0.1-raring-amd64"}, |
529 | + expectUploaded: []string{"2.2.0.1-quantal-amd64", "2.2.0.1-%LTS%-amd64", "2.2.0.1-raring-amd64"}, |
530 | }, { |
531 | about: "upload with explicit version", |
532 | currentVersion: "2.2.0-quantal-amd64", |
533 | agentVersion: "2.0.0", |
534 | args: []string{"--upload-tools", "--version", "2.7.3"}, |
535 | expectVersion: "2.7.3.1", |
536 | - expectUploaded: []string{"2.7.3.1-quantal-amd64", "2.7.3.1-precise-amd64", "2.7.3.1-raring-amd64"}, |
537 | + expectUploaded: []string{"2.7.3.1-quantal-amd64", "2.7.3.1-%LTS%-amd64", "2.7.3.1-raring-amd64"}, |
538 | }, { |
539 | about: "upload with explicit series", |
540 | currentVersion: "2.2.0-quantal-amd64", |
541 | @@ -246,7 +247,7 @@ |
542 | agentVersion: "2.0.0", |
543 | args: []string{"--upload-tools"}, |
544 | expectVersion: "2.1.0.1", |
545 | - expectUploaded: []string{"2.1.0.1-quantal-amd64", "2.1.0.1-precise-amd64", "2.1.0.1-raring-amd64"}, |
546 | + expectUploaded: []string{"2.1.0.1-quantal-amd64", "2.1.0.1-%LTS%-amd64", "2.1.0.1-raring-amd64"}, |
547 | }, { |
548 | about: "upload bumps version when necessary", |
549 | tools: []string{"2.4.6-quantal-amd64", "2.4.8-quantal-amd64"}, |
550 | @@ -254,7 +255,7 @@ |
551 | agentVersion: "2.4.0", |
552 | args: []string{"--upload-tools"}, |
553 | expectVersion: "2.4.6.1", |
554 | - expectUploaded: []string{"2.4.6.1-quantal-amd64", "2.4.6.1-precise-amd64", "2.4.6.1-raring-amd64"}, |
555 | + expectUploaded: []string{"2.4.6.1-quantal-amd64", "2.4.6.1-%LTS%-amd64", "2.4.6.1-raring-amd64"}, |
556 | }, { |
557 | about: "upload re-bumps version when necessary", |
558 | tools: []string{"2.4.6-quantal-amd64", "2.4.6.2-saucy-i386", "2.4.8-quantal-amd64"}, |
559 | @@ -262,7 +263,7 @@ |
560 | agentVersion: "2.4.6.2", |
561 | args: []string{"--upload-tools"}, |
562 | expectVersion: "2.4.6.3", |
563 | - expectUploaded: []string{"2.4.6.3-quantal-amd64", "2.4.6.3-precise-amd64", "2.4.6.3-raring-amd64"}, |
564 | + expectUploaded: []string{"2.4.6.3-quantal-amd64", "2.4.6.3-%LTS%-amd64", "2.4.6.3-raring-amd64"}, |
565 | }, { |
566 | about: "upload with explicit version bumps when necessary", |
567 | currentVersion: "2.2.0-quantal-amd64", |
568 | @@ -270,7 +271,7 @@ |
569 | agentVersion: "2.0.0", |
570 | args: []string{"--upload-tools", "--version", "2.7.3"}, |
571 | expectVersion: "2.7.3.2", |
572 | - expectUploaded: []string{"2.7.3.2-quantal-amd64", "2.7.3.2-precise-amd64", "2.7.3.2-raring-amd64"}, |
573 | + expectUploaded: []string{"2.7.3.2-quantal-amd64", "2.7.3.2-%LTS%-amd64", "2.7.3.2-raring-amd64"}, |
574 | }} |
575 | |
576 | // getMockBuildTools returns a sync.BuildToolsTarballFunc implementation which generates |
577 | @@ -356,6 +357,9 @@ |
578 | c.Check(agentVersion, gc.Equals, version.MustParse(test.expectVersion)) |
579 | |
580 | for _, uploaded := range test.expectUploaded { |
581 | + // Substitute latest LTS for placeholder in expected series for uploaded tools |
582 | + uploaded = strings.Replace(uploaded, "%LTS%", config.LatestLtsSeries(), 1) |
583 | + |
584 | vers := version.MustParseBinary(uploaded) |
585 | r, err := storage.Get(s.Conn.Environ.Storage(), envtools.StorageName(vers)) |
586 | if !c.Check(err, gc.IsNil) { |
587 | |
588 | === modified file 'cmd/plugins/juju-metadata/imagemetadata.go' |
589 | --- cmd/plugins/juju-metadata/imagemetadata.go 2014-03-28 12:28:30 +0000 |
590 | +++ cmd/plugins/juju-metadata/imagemetadata.go 2014-04-03 17:37:27 +0000 |
591 | @@ -97,7 +97,7 @@ |
592 | } |
593 | cfg := environ.Config() |
594 | if c.Series == "" { |
595 | - c.Series = cfg.DefaultSeries() |
596 | + c.Series = config.PreferredSeries(cfg) |
597 | } |
598 | if v, ok := cfg.AllAttrs()["control-bucket"]; ok { |
599 | c.privateStorage = v.(string) |
600 | @@ -110,7 +110,7 @@ |
601 | logger.Infof("no environment found, creating image metadata using user supplied data") |
602 | } |
603 | if c.Series == "" { |
604 | - c.Series = config.DefaultSeries |
605 | + c.Series = config.LatestLtsSeries() |
606 | } |
607 | if c.ImageId == "" { |
608 | return fmt.Errorf("image id must be specified") |
609 | |
610 | === modified file 'cmd/plugins/juju-metadata/imagemetadata_test.go' |
611 | --- cmd/plugins/juju-metadata/imagemetadata_test.go 2014-03-13 07:54:56 +0000 |
612 | +++ cmd/plugins/juju-metadata/imagemetadata_test.go 2014-04-03 17:37:27 +0000 |
613 | @@ -15,6 +15,7 @@ |
614 | gc "launchpad.net/gocheck" |
615 | |
616 | "launchpad.net/juju-core/cmd" |
617 | + "launchpad.net/juju-core/environs/config" |
618 | "launchpad.net/juju-core/testing" |
619 | "launchpad.net/juju-core/testing/testbase" |
620 | ) |
621 | @@ -61,6 +62,7 @@ |
622 | var seriesVersions map[string]string = map[string]string{ |
623 | "precise": "12.04", |
624 | "raring": "13.04", |
625 | + "trusty": "14.04", |
626 | } |
627 | |
628 | type expectedMetadata struct { |
629 | @@ -138,7 +140,7 @@ |
630 | s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) |
631 | } |
632 | |
633 | -func (s *ImageMetadataSuite) TestImageMetadataFilesDefaultSeries(c *gc.C) { |
634 | +func (s *ImageMetadataSuite) TestImageMetadataFilesLatestLts(c *gc.C) { |
635 | ctx := testing.Context(c) |
636 | code := cmd.Main( |
637 | &ImageMetadataCommand{}, ctx, []string{ |
638 | @@ -146,7 +148,7 @@ |
639 | c.Assert(code, gc.Equals, 0) |
640 | out := testing.Stdout(ctx) |
641 | expected := expectedMetadata{ |
642 | - series: "precise", |
643 | + series: config.LatestLtsSeries(), |
644 | arch: "arch", |
645 | } |
646 | s.assertCommandOutput(c, expected, out, defaultIndexFileName, defaultImageFileName) |
647 | |
648 | === modified file 'environs/bootstrap/bootstrap_test.go' |
649 | --- environs/bootstrap/bootstrap_test.go 2014-03-28 13:27:45 +0000 |
650 | +++ environs/bootstrap/bootstrap_test.go 2014-04-03 17:37:27 +0000 |
651 | @@ -88,7 +88,7 @@ |
652 | |
653 | func uploadTools(c *gc.C, env environs.Environ) { |
654 | usefulVersion := version.Current |
655 | - usefulVersion.Series = env.Config().DefaultSeries() |
656 | + usefulVersion.Series = config.PreferredSeries(env.Config()) |
657 | envtesting.AssertUploadFakeToolsVersions(c, env.Storage(), usefulVersion) |
658 | } |
659 | |
660 | @@ -217,7 +217,7 @@ |
661 | s.setDummyStorage(c, env) |
662 | envtesting.RemoveFakeTools(c, env.Storage()) |
663 | arch := "ppc64" |
664 | - _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), &arch) |
665 | + _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), &arch) |
666 | c.Assert(err, gc.NotNil) |
667 | stripped := strings.Replace(err.Error(), "\n", "", -1) |
668 | c.Assert(stripped, |
669 | @@ -233,7 +233,7 @@ |
670 | env := newEnviron("foo", useDefaultKeys, nil) |
671 | s.setDummyStorage(c, env) |
672 | envtesting.RemoveFakeTools(c, env.Storage()) |
673 | - _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), nil) |
674 | + _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil) |
675 | c.Assert(err, gc.NotNil) |
676 | stripped := strings.Replace(err.Error(), "\n", "", -1) |
677 | c.Assert(stripped, |
678 | @@ -246,7 +246,7 @@ |
679 | env := newEnviron("foo", useDefaultKeys, map[string]interface{}{"agent-version": "1.16.0"}) |
680 | s.setDummyStorage(c, env) |
681 | envtesting.RemoveFakeTools(c, env.Storage()) |
682 | - _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), nil) |
683 | + _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil) |
684 | c.Assert(err, gc.NotNil) |
685 | stripped := strings.Replace(err.Error(), "\n", "", -1) |
686 | c.Assert(stripped, |
687 | @@ -260,7 +260,7 @@ |
688 | env := newEnviron("foo", useDefaultKeys, nil) |
689 | s.setDummyStorage(c, env) |
690 | envtesting.RemoveFakeTools(c, env.Storage()) |
691 | - _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), nil) |
692 | + _, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), nil) |
693 | c.Assert(err, gc.NotNil) |
694 | stripped := strings.Replace(err.Error(), "\n", "", -1) |
695 | c.Assert(stripped, |
696 | @@ -310,12 +310,12 @@ |
697 | return "arm64" |
698 | }) |
699 | arch := "arm64" |
700 | - agentTools, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, env.Config().DefaultSeries(), &arch) |
701 | + agentTools, err := bootstrap.EnsureToolsAvailability(coretesting.Context(c), env, config.PreferredSeries(env.Config()), &arch) |
702 | c.Assert(err, gc.IsNil) |
703 | c.Assert(agentTools, gc.HasLen, 1) |
704 | expectedVers := version.Current |
705 | expectedVers.Number.Build++ |
706 | - expectedVers.Series = env.Config().DefaultSeries() |
707 | + expectedVers.Series = config.PreferredSeries(env.Config()) |
708 | c.Assert(agentTools[0].Version, gc.DeepEquals, expectedVers) |
709 | } |
710 | |
711 | @@ -325,11 +325,18 @@ |
712 | s.PatchValue(&version.Current, vers) |
713 | env := newEnviron("foo", useDefaultKeys, nil) |
714 | cfg := env.Config() |
715 | - c.Assert(bootstrap.SeriesToUpload(cfg, nil), jc.SameContents, []string{"quantal", "precise"}) |
716 | + |
717 | + prefSeries := config.PreferredSeries(cfg) |
718 | + expect := []string{"quantal", prefSeries} |
719 | + if prefSeries != config.LatestLtsSeries() { |
720 | + expect = append(expect, config.LatestLtsSeries()) |
721 | + } |
722 | + c.Assert(bootstrap.SeriesToUpload(cfg, nil), jc.SameContents, expect) |
723 | + |
724 | c.Assert(bootstrap.SeriesToUpload(cfg, []string{"quantal"}), jc.SameContents, []string{"quantal"}) |
725 | env = newEnviron("foo", useDefaultKeys, map[string]interface{}{"default-series": "lucid"}) |
726 | cfg = env.Config() |
727 | - c.Assert(bootstrap.SeriesToUpload(cfg, nil), jc.SameContents, []string{"quantal", "precise", "lucid"}) |
728 | + c.Assert(bootstrap.SeriesToUpload(cfg, nil), jc.SameContents, []string{"quantal", config.LatestLtsSeries(), "lucid"}) |
729 | } |
730 | |
731 | func (s *bootstrapSuite) assertUploadTools(c *gc.C, vers version.Binary, allowRelease bool, |
732 | |
733 | === modified file 'environs/bootstrap/synctools.go' |
734 | --- environs/bootstrap/synctools.go 2014-03-26 03:30:35 +0000 |
735 | +++ environs/bootstrap/synctools.go 2014-04-03 17:37:27 +0000 |
736 | @@ -100,8 +100,10 @@ |
737 | unique := set.NewStrings(series...) |
738 | if unique.IsEmpty() { |
739 | unique.Add(version.Current.Series) |
740 | - unique.Add(config.DefaultSeries) |
741 | - unique.Add(cfg.DefaultSeries()) |
742 | + unique.Add(config.LatestLtsSeries()) |
743 | + if series, ok := cfg.DefaultSeries(); ok { |
744 | + unique.Add(series) |
745 | + } |
746 | } |
747 | return unique.Values() |
748 | } |
749 | @@ -175,7 +177,10 @@ |
750 | // No tools available so our only hope is to build locally and upload. |
751 | logger.Warningf("no prepackaged tools available") |
752 | uploadSeries := SeriesToUpload(cfg, nil) |
753 | - if err := UploadTools(ctx, env, toolsArch, false, append(uploadSeries, series)...); err != nil { |
754 | + if series != "" { |
755 | + uploadSeries = append(uploadSeries, series) |
756 | + } |
757 | + if err := UploadTools(ctx, env, toolsArch, false, uploadSeries...); err != nil { |
758 | logger.Errorf("%s", noToolsMessage) |
759 | return nil, fmt.Errorf("cannot upload bootstrap tools: %v", err) |
760 | } |
761 | |
762 | === modified file 'environs/config/config.go' |
763 | --- environs/config/config.go 2014-04-02 06:15:57 +0000 |
764 | +++ environs/config/config.go 2014-04-03 17:37:27 +0000 |
765 | @@ -7,6 +7,7 @@ |
766 | "fmt" |
767 | "io/ioutil" |
768 | "os" |
769 | + "os/exec" |
770 | "path/filepath" |
771 | "regexp" |
772 | "strings" |
773 | @@ -34,9 +35,6 @@ |
774 | // port opened. |
775 | FwGlobal = "global" |
776 | |
777 | - // DefaultSeries returns the most recent Ubuntu LTS release name. |
778 | - DefaultSeries string = "precise" |
779 | - |
780 | // DefaultStatePort is the default port the state server is listening on. |
781 | DefaultStatePort int = 37017 |
782 | |
783 | @@ -59,8 +57,53 @@ |
784 | // refreshing the addresses, in seconds. Not too frequent, as we |
785 | // refresh addresses from the provider each time. |
786 | DefaultBootstrapSSHAddressesDelay int = 10 |
787 | + |
788 | + // fallbackLtsSeries is the latest LTS series we'll use, if we fail to |
789 | + // obtain this information from the system. |
790 | + fallbackLtsSeries string = "precise" |
791 | ) |
792 | |
793 | +var latestLtsSeries string |
794 | + |
795 | +type HasDefaultSeries interface { |
796 | + DefaultSeries() (string, bool) |
797 | +} |
798 | + |
799 | +// PreferredSeries returns the preferred series to use when a charm does not |
800 | +// explicitly specify a series. |
801 | +func PreferredSeries(cfg HasDefaultSeries) string { |
802 | + if series, ok := cfg.DefaultSeries(); ok { |
803 | + return series |
804 | + } |
805 | + return LatestLtsSeries() |
806 | +} |
807 | + |
808 | +func LatestLtsSeries() string { |
809 | + if latestLtsSeries == "" { |
810 | + series, err := distroLtsSeries() |
811 | + if err != nil { |
812 | + latestLtsSeries = fallbackLtsSeries |
813 | + } else { |
814 | + latestLtsSeries = series |
815 | + } |
816 | + } |
817 | + return latestLtsSeries |
818 | +} |
819 | + |
820 | +// distroLtsSeries returns the latest LTS series, if this information is |
821 | +// available on this system. |
822 | +func distroLtsSeries() (string, error) { |
823 | + out, err := exec.Command("distro-info", "--lts").Output() |
824 | + if err != nil { |
825 | + return "", err |
826 | + } |
827 | + series := strings.TrimSpace(string(out)) |
828 | + if !charm.IsValidSeries(series) { |
829 | + return "", fmt.Errorf("not a valid LTS series: %q", series) |
830 | + } |
831 | + return series, nil |
832 | +} |
833 | + |
834 | // Config holds an immutable environment configuration. |
835 | type Config struct { |
836 | // defined holds the attributes that are defined for Config. |
837 | @@ -162,7 +205,6 @@ |
838 | // For backward compatibility purposes, we treat as unset string |
839 | // valued attributes that are set to the empty string, and fill |
840 | // out their defaults accordingly. |
841 | - c.fillInStringDefault("default-series") |
842 | c.fillInStringDefault("firewall-mode") |
843 | |
844 | // Load authorized-keys-path into authorized-keys if necessary. |
845 | @@ -407,9 +449,17 @@ |
846 | return c.mustString("name") |
847 | } |
848 | |
849 | -// DefaultSeries returns the default Ubuntu series for the environment. |
850 | -func (c *Config) DefaultSeries() string { |
851 | - return c.mustString("default-series") |
852 | +// DefaultSeries returns the configured default Ubuntu series for the environment, |
853 | +// and whether the default series was explicitly configured on the environment. |
854 | +func (c *Config) DefaultSeries() (string, bool) { |
855 | + if s, ok := c.defined["default-series"]; ok { |
856 | + if series, ok := s.(string); ok && series != "" { |
857 | + return series, true |
858 | + } else if !ok { |
859 | + logger.Warningf("invalid default-series: %q", s) |
860 | + } |
861 | + } |
862 | + return "", false |
863 | } |
864 | |
865 | // StatePort returns the state server port for the environment. |
866 | @@ -771,6 +821,8 @@ |
867 | "image-metadata-url": "", // TODO(rog) omit |
868 | "tools-metadata-url": "", // TODO(rog) omit |
869 | |
870 | + "default-series": "", |
871 | + |
872 | // For backward compatibility only - default ports were |
873 | // not filled out in previous versions of the configuration. |
874 | "state-port": DefaultStatePort, |
875 | @@ -795,7 +847,6 @@ |
876 | // UseDefaults. |
877 | func allDefaults() schema.Defaults { |
878 | d := schema.Defaults{ |
879 | - "default-series": DefaultSeries, |
880 | "firewall-mode": FwInstance, |
881 | "development": false, |
882 | "ssl-hostname-verification": true, |
883 | |
884 | === modified file 'environs/config/config_test.go' |
885 | --- environs/config/config_test.go 2014-03-26 08:14:32 +0000 |
886 | +++ environs/config/config_test.go 2014-04-03 17:37:27 +0000 |
887 | @@ -652,7 +652,6 @@ |
888 | authTokenConfigTest("token=value, =z", false), |
889 | authTokenConfigTest("token=value =z", false), |
890 | authTokenConfigTest("\t", false), |
891 | - missingAttributeNoDefault("default-series"), |
892 | missingAttributeNoDefault("firewall-mode"), |
893 | missingAttributeNoDefault("development"), |
894 | missingAttributeNoDefault("ssl-hostname-verification"), |
895 | @@ -861,11 +860,14 @@ |
896 | testmode, _ := test.attrs["test-mode"].(bool) |
897 | c.Assert(cfg.TestMode(), gc.Equals, testmode) |
898 | |
899 | - if series, _ := test.attrs["default-series"].(string); series != "" { |
900 | - c.Assert(cfg.DefaultSeries(), gc.Equals, series) |
901 | + series, _ := test.attrs["default-series"].(string) |
902 | + if defaultSeries, ok := cfg.DefaultSeries(); ok { |
903 | + c.Assert(defaultSeries, gc.Equals, series) |
904 | } else { |
905 | - c.Assert(cfg.DefaultSeries(), gc.Equals, config.DefaultSeries) |
906 | + c.Assert(series, gc.Equals, "") |
907 | + c.Assert(defaultSeries, gc.Equals, "") |
908 | } |
909 | + |
910 | if m, _ := test.attrs["firewall-mode"].(string); m != "" { |
911 | c.Assert(cfg.FirewallMode(), gc.Equals, m) |
912 | } |
913 | @@ -1010,7 +1012,7 @@ |
914 | "bootstrap-timeout": 3600, |
915 | "bootstrap-retry-delay": 30, |
916 | "bootstrap-addresses-delay": 10, |
917 | - "default-series": "precise", |
918 | + "default-series": testing.FakeDefaultSeries, |
919 | "charm-store-auth": "token=auth", |
920 | "test-mode": false, |
921 | } |
922 | @@ -1019,7 +1021,6 @@ |
923 | |
924 | // These attributes are added if not set. |
925 | attrs["development"] = false |
926 | - attrs["default-series"] = config.DefaultSeries |
927 | attrs["logging-config"] = "<root>=WARNING;unit=DEBUG" |
928 | attrs["ca-private-key"] = "" |
929 | attrs["image-metadata-url"] = "" |
930 | |
931 | === modified file 'environs/jujutest/livetests.go' |
932 | --- environs/jujutest/livetests.go 2014-04-03 02:57:11 +0000 |
933 | +++ environs/jujutest/livetests.go 2014-04-03 17:37:27 +0000 |
934 | @@ -133,7 +133,7 @@ |
935 | // we could connect to (actual live tests, rather than local-only) |
936 | cons := constraints.MustParse("mem=2G") |
937 | if t.CanOpenState { |
938 | - _, err := sync.Upload(t.Env.Storage(), nil, config.DefaultSeries) |
939 | + _, err := sync.Upload(t.Env.Storage(), nil, coretesting.FakeDefaultSeries) |
940 | c.Assert(err, gc.IsNil) |
941 | } |
942 | envtesting.UploadFakeTools(c, t.Env.Storage()) |
943 | @@ -442,7 +442,7 @@ |
944 | |
945 | // If the series has not been specified, we expect the most recent Ubuntu LTS release to be used. |
946 | expectedVersion := version.Current |
947 | - expectedVersion.Series = config.DefaultSeries |
948 | + expectedVersion.Series = config.LatestLtsSeries() |
949 | |
950 | mtools0 := waitAgentTools(c, mw0, expectedVersion) |
951 | |
952 | |
953 | === modified file 'environs/testing/tools.go' |
954 | --- environs/testing/tools.go 2014-03-27 00:42:27 +0000 |
955 | +++ environs/testing/tools.go 2014-04-03 17:37:27 +0000 |
956 | @@ -13,7 +13,6 @@ |
957 | |
958 | agenttools "launchpad.net/juju-core/agent/tools" |
959 | "launchpad.net/juju-core/environs" |
960 | - "launchpad.net/juju-core/environs/config" |
961 | "launchpad.net/juju-core/environs/simplestreams" |
962 | "launchpad.net/juju-core/environs/storage" |
963 | envtools "launchpad.net/juju-core/environs/tools" |
964 | @@ -163,8 +162,9 @@ |
965 | func uploadFakeTools(stor storage.Storage) error { |
966 | versions := []version.Binary{version.Current} |
967 | toolsVersion := version.Current |
968 | - if toolsVersion.Series != config.DefaultSeries { |
969 | - toolsVersion.Series = config.DefaultSeries |
970 | + latestLts := coretesting.FakeDefaultSeries |
971 | + if toolsVersion.Series != latestLts { |
972 | + toolsVersion.Series = latestLts |
973 | versions = append(versions, toolsVersion) |
974 | } |
975 | if _, err := UploadFakeToolsVersions(stor, versions...); err != nil { |
976 | @@ -175,9 +175,9 @@ |
977 | |
978 | // UploadFakeTools puts fake tools into the supplied storage with a binary |
979 | // version matching version.Current; if version.Current's series is different |
980 | -// to config.DefaultSeries, matching fake tools will be uploaded for that series. |
981 | -// This is useful for tests that are kinda casual about specifying their |
982 | -// environment. |
983 | +// to coretesting.FakeDefaultSeries, matching fake tools will be uploaded for that |
984 | +// series. This is useful for tests that are kinda casual about specifying |
985 | +// their environment. |
986 | func UploadFakeTools(c *gc.C, stor storage.Storage) { |
987 | c.Assert(uploadFakeTools(stor), gc.IsNil) |
988 | } |
989 | @@ -196,8 +196,9 @@ |
990 | name := envtools.StorageName(toolsVersion) |
991 | err := stor.Remove(name) |
992 | c.Check(err, gc.IsNil) |
993 | - if version.Current.Series != config.DefaultSeries { |
994 | - toolsVersion.Series = config.DefaultSeries |
995 | + defaultSeries := coretesting.FakeDefaultSeries |
996 | + if version.Current.Series != defaultSeries { |
997 | + toolsVersion.Series = defaultSeries |
998 | name := envtools.StorageName(toolsVersion) |
999 | err := stor.Remove(name) |
1000 | c.Check(err, gc.IsNil) |
1001 | |
1002 | === modified file 'juju/apiconn_test.go' |
1003 | --- juju/apiconn_test.go 2014-04-02 00:18:34 +0000 |
1004 | +++ juju/apiconn_test.go 2014-04-03 17:37:27 +0000 |
1005 | @@ -175,7 +175,7 @@ |
1006 | "name": "myenv", |
1007 | "state-server": true, |
1008 | "authorized-keys": "i-am-a-key", |
1009 | - "default-series": config.DefaultSeries, |
1010 | + "default-series": config.LatestLtsSeries(), |
1011 | "firewall-mode": config.FwInstance, |
1012 | "development": false, |
1013 | "ssl-hostname-verification": true, |
1014 | |
1015 | === modified file 'juju/testing/conn.go' |
1016 | --- juju/testing/conn.go 2014-03-25 15:06:31 +0000 |
1017 | +++ juju/testing/conn.go 2014-04-03 17:37:27 +0000 |
1018 | @@ -17,6 +17,7 @@ |
1019 | "launchpad.net/juju-core/constraints" |
1020 | "launchpad.net/juju-core/environs" |
1021 | "launchpad.net/juju-core/environs/bootstrap" |
1022 | + "launchpad.net/juju-core/environs/config" |
1023 | "launchpad.net/juju-core/environs/configstore" |
1024 | envtesting "launchpad.net/juju-core/environs/testing" |
1025 | "launchpad.net/juju-core/juju" |
1026 | @@ -164,6 +165,14 @@ |
1027 | return s.openAPIAs(c, machine.Tag(), password, "fake_nonce"), machine |
1028 | } |
1029 | |
1030 | +func PreferredDefaultVersions(conf *config.Config, template version.Binary) []version.Binary { |
1031 | + prefVersion := template |
1032 | + prefVersion.Series = config.PreferredSeries(conf) |
1033 | + defaultVersion := template |
1034 | + defaultVersion.Series = testing.FakeDefaultSeries |
1035 | + return []version.Binary{prefVersion, defaultVersion} |
1036 | +} |
1037 | + |
1038 | func (s *JujuConnSuite) setUpConn(c *gc.C) { |
1039 | if s.RootDir != "" { |
1040 | panic("JujuConnSuite.setUpConn without teardown") |
1041 | @@ -203,7 +212,11 @@ |
1042 | c.Assert(environ.Name(), gc.Equals, "dummyenv") |
1043 | s.PatchValue(&dummy.DataDir, s.DataDir()) |
1044 | |
1045 | - envtesting.MustUploadFakeTools(environ.Storage()) |
1046 | + versions := PreferredDefaultVersions(environ.Config(), version.Current) |
1047 | + versions = append(versions, version.Current) |
1048 | + |
1049 | + // Upload tools for both preferred and fake default series |
1050 | + envtesting.MustUploadFakeToolsVersions(environ.Storage(), versions...) |
1051 | c.Assert(bootstrap.Bootstrap(ctx, environ, constraints.Value{}), gc.IsNil) |
1052 | |
1053 | s.BackingState = environ.(GetStater).GetStateInAPIServer() |
1054 | @@ -285,7 +298,7 @@ |
1055 | ch := testing.Charms.Dir(name) |
1056 | ident := fmt.Sprintf("%s-%d", ch.Meta().Name, ch.Revision()) |
1057 | curl := charm.MustParseURL("local:quantal/" + ident) |
1058 | - repo, err := charm.InferRepository(curl, testing.Charms.Path()) |
1059 | + repo, err := charm.InferRepository(curl.Reference, testing.Charms.Path()) |
1060 | c.Assert(err, gc.IsNil) |
1061 | sch, err := s.Conn.PutCharm(curl, repo, false) |
1062 | c.Assert(err, gc.IsNil) |
1063 | |
1064 | === modified file 'juju/testing/instance.go' |
1065 | --- juju/testing/instance.go 2014-04-01 16:27:22 +0000 |
1066 | +++ juju/testing/instance.go 2014-04-03 17:37:27 +0000 |
1067 | @@ -10,6 +10,7 @@ |
1068 | |
1069 | "launchpad.net/juju-core/constraints" |
1070 | "launchpad.net/juju-core/environs" |
1071 | + "launchpad.net/juju-core/environs/config" |
1072 | "launchpad.net/juju-core/environs/tools" |
1073 | "launchpad.net/juju-core/instance" |
1074 | "launchpad.net/juju-core/names" |
1075 | @@ -112,7 +113,7 @@ |
1076 | ) ( |
1077 | instance.Instance, *instance.HardwareCharacteristics, error, |
1078 | ) { |
1079 | - series := env.Config().DefaultSeries() |
1080 | + series := config.PreferredSeries(env.Config()) |
1081 | agentVersion, ok := env.Config().AgentVersion() |
1082 | if !ok { |
1083 | return nil, nil, fmt.Errorf("missing agent version in environment config") |
1084 | |
1085 | === modified file 'provider/azure/environ_test.go' |
1086 | --- provider/azure/environ_test.go 2014-04-03 09:07:57 +0000 |
1087 | +++ provider/azure/environ_test.go 2014-04-03 17:37:27 +0000 |
1088 | @@ -448,7 +448,7 @@ |
1089 | err = env.SetConfig(cfg) |
1090 | c.Assert(err, gc.IsNil) |
1091 | |
1092 | - c.Check(env.ecfg.Config.DefaultSeries(), gc.Equals, "feisty") |
1093 | + c.Check(config.PreferredSeries(env.ecfg.Config), gc.Equals, "feisty") |
1094 | } |
1095 | |
1096 | func (*environSuite) TestSetConfigLocksEnviron(c *gc.C) { |
1097 | |
1098 | === modified file 'provider/common/bootstrap.go' |
1099 | --- provider/common/bootstrap.go 2014-03-28 22:31:08 +0000 |
1100 | +++ provider/common/bootstrap.go 2014-04-03 17:37:27 +0000 |
1101 | @@ -42,7 +42,7 @@ |
1102 | defer func() { handleBootstrapError(err, ctx, inst, env) }() |
1103 | |
1104 | // First thing, ensure we have tools otherwise there's no point. |
1105 | - selectedTools, err := EnsureBootstrapTools(ctx, env, env.Config().DefaultSeries(), cons.Arch) |
1106 | + selectedTools, err := EnsureBootstrapTools(ctx, env, config.PreferredSeries(env.Config()), cons.Arch) |
1107 | if err != nil { |
1108 | return err |
1109 | } |
1110 | |
1111 | === modified file 'provider/dummy/environs.go' |
1112 | --- provider/dummy/environs.go 2014-04-03 03:37:45 +0000 |
1113 | +++ provider/dummy/environs.go 2014-04-03 17:37:27 +0000 |
1114 | @@ -545,7 +545,7 @@ |
1115 | } |
1116 | |
1117 | func (e *environ) Bootstrap(ctx environs.BootstrapContext, cons constraints.Value) error { |
1118 | - selectedTools, err := common.EnsureBootstrapTools(ctx, e, e.Config().DefaultSeries(), cons.Arch) |
1119 | + selectedTools, err := common.EnsureBootstrapTools(ctx, e, config.PreferredSeries(e.Config()), cons.Arch) |
1120 | if err != nil { |
1121 | return err |
1122 | } |
1123 | |
1124 | === modified file 'provider/ec2/ec2.go' |
1125 | --- provider/ec2/ec2.go 2014-04-03 03:37:45 +0000 |
1126 | +++ provider/ec2/ec2.go 2014-04-03 17:37:27 +0000 |
1127 | @@ -367,7 +367,7 @@ |
1128 | return nil, err |
1129 | } |
1130 | return &simplestreams.MetadataLookupParams{ |
1131 | - Series: e.ecfg().DefaultSeries(), |
1132 | + Series: config.PreferredSeries(e.ecfg()), |
1133 | Region: cloudSpec.Region, |
1134 | Endpoint: cloudSpec.Endpoint, |
1135 | Architectures: arch.AllSupportedArches, |
1136 | |
1137 | === modified file 'provider/ec2/live_test.go' |
1138 | --- provider/ec2/live_test.go 2014-03-20 03:06:57 +0000 |
1139 | +++ provider/ec2/live_test.go 2014-04-03 17:37:27 +0000 |
1140 | @@ -101,7 +101,7 @@ |
1141 | t.LiveTests.SetUpTest(c) |
1142 | t.PatchValue(&version.Current, version.Binary{ |
1143 | Number: version.Current.Number, |
1144 | - Series: config.DefaultSeries, |
1145 | + Series: coretesting.FakeDefaultSeries, |
1146 | Arch: arch.AMD64, |
1147 | }) |
1148 | |
1149 | |
1150 | === modified file 'provider/ec2/local_test.go' |
1151 | --- provider/ec2/local_test.go 2014-04-02 11:35:49 +0000 |
1152 | +++ provider/ec2/local_test.go 2014-04-03 17:37:27 +0000 |
1153 | @@ -205,7 +205,7 @@ |
1154 | t.Tests.SetUpTest(c) |
1155 | t.PatchValue(&version.Current, version.Binary{ |
1156 | Number: version.Current.Number, |
1157 | - Series: config.DefaultSeries, |
1158 | + Series: coretesting.FakeDefaultSeries, |
1159 | Arch: arch.AMD64, |
1160 | }) |
1161 | } |
1162 | |
1163 | === modified file 'provider/joyent/environ.go' |
1164 | --- provider/joyent/environ.go 2014-04-03 03:37:45 +0000 |
1165 | +++ provider/joyent/environ.go 2014-04-03 17:37:27 +0000 |
1166 | @@ -154,7 +154,7 @@ |
1167 | region = env.Ecfg().Region() |
1168 | } |
1169 | return &simplestreams.MetadataLookupParams{ |
1170 | - Series: env.Ecfg().DefaultSeries(), |
1171 | + Series: config.PreferredSeries(env.Ecfg()), |
1172 | Region: region, |
1173 | Endpoint: env.Ecfg().sdcUrl(), |
1174 | Architectures: []string{"amd64", "arm"}, |
1175 | |
1176 | === modified file 'provider/openstack/provider.go' |
1177 | --- provider/openstack/provider.go 2014-04-03 03:37:45 +0000 |
1178 | +++ provider/openstack/provider.go 2014-04-03 17:37:27 +0000 |
1179 | @@ -1236,7 +1236,7 @@ |
1180 | return nil, err |
1181 | } |
1182 | return &simplestreams.MetadataLookupParams{ |
1183 | - Series: e.ecfg().DefaultSeries(), |
1184 | + Series: config.PreferredSeries(e.ecfg()), |
1185 | Region: cloudSpec.Region, |
1186 | Endpoint: cloudSpec.Endpoint, |
1187 | Architectures: arch.AllSupportedArches, |
1188 | |
1189 | === modified file 'state/api/client.go' |
1190 | --- state/api/client.go 2014-03-28 13:31:26 +0000 |
1191 | +++ state/api/client.go 2014-04-03 17:37:27 +0000 |
1192 | @@ -608,6 +608,24 @@ |
1193 | return c.call("AddCharm", args, nil) |
1194 | } |
1195 | |
1196 | +// ResolveCharms resolves the best available charm URLs with series, for charm |
1197 | +// locations without a series specified. |
1198 | +func (c *Client) ResolveCharm(ref charm.Reference) (*charm.URL, error) { |
1199 | + args := params.ResolveCharms{References: []charm.Reference{ref}} |
1200 | + result := new(params.ResolveCharmResults) |
1201 | + if err := c.st.Call("Client", "", "ResolveCharms", args, result); err != nil { |
1202 | + return nil, err |
1203 | + } |
1204 | + if len(result.URLs) == 0 { |
1205 | + return nil, fmt.Errorf("unexpected empty response") |
1206 | + } |
1207 | + urlInfo := result.URLs[0] |
1208 | + if urlInfo.Error != "" { |
1209 | + return nil, fmt.Errorf("%v", urlInfo.Error) |
1210 | + } |
1211 | + return urlInfo.URL, nil |
1212 | +} |
1213 | + |
1214 | func (c *Client) UploadTools( |
1215 | toolsFilename string, vers version.Binary, fakeSeries ...string, |
1216 | ) ( |
1217 | |
1218 | === modified file 'state/api/params/params.go' |
1219 | --- state/api/params/params.go 2014-04-03 02:57:11 +0000 |
1220 | +++ state/api/params/params.go 2014-04-03 17:37:27 +0000 |
1221 | @@ -330,6 +330,22 @@ |
1222 | CharmURL string |
1223 | } |
1224 | |
1225 | +// ResolveCharms stores charm references for a ResolveCharms call. |
1226 | +type ResolveCharms struct { |
1227 | + References []charm.Reference |
1228 | +} |
1229 | + |
1230 | +// ResolveCharmResult holds the result of resolving a charm reference to a URL, or any error that occurred. |
1231 | +type ResolveCharmResult struct { |
1232 | + URL *charm.URL `json:",omitempty"` |
1233 | + Error string `json:",omitempty"` |
1234 | +} |
1235 | + |
1236 | +// ResolveCharmResults holds results of the ResolveCharms call. |
1237 | +type ResolveCharmResults struct { |
1238 | + URLs []ResolveCharmResult |
1239 | +} |
1240 | + |
1241 | // AllWatcherId holds the id of an AllWatcher. |
1242 | type AllWatcherId struct { |
1243 | AllWatcherId string |
1244 | |
1245 | === modified file 'state/apiserver/client/client.go' |
1246 | --- state/apiserver/client/client.go 2014-03-28 13:31:26 +0000 |
1247 | +++ state/apiserver/client/client.go 2014-04-03 17:37:27 +0000 |
1248 | @@ -567,16 +567,8 @@ |
1249 | results := params.AddMachinesResults{ |
1250 | Machines: make([]params.AddMachinesResult, len(args.MachineParams)), |
1251 | } |
1252 | - |
1253 | - var defaultSeries string |
1254 | - conf, err := c.api.state.EnvironConfig() |
1255 | - if err != nil { |
1256 | - return results, err |
1257 | - } |
1258 | - defaultSeries = conf.DefaultSeries() |
1259 | - |
1260 | for i, p := range args.MachineParams { |
1261 | - m, err := c.addOneMachine(p, defaultSeries) |
1262 | + m, err := c.addOneMachine(p) |
1263 | results.Machines[i].Error = common.ServerError(err) |
1264 | if err == nil { |
1265 | results.Machines[i].Machine = m.Id() |
1266 | @@ -590,9 +582,13 @@ |
1267 | return c.AddMachines(args) |
1268 | } |
1269 | |
1270 | -func (c *Client) addOneMachine(p params.AddMachineParams, defaultSeries string) (*state.Machine, error) { |
1271 | +func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) { |
1272 | if p.Series == "" { |
1273 | - p.Series = defaultSeries |
1274 | + conf, err := c.api.state.EnvironConfig() |
1275 | + if err != nil { |
1276 | + return nil, err |
1277 | + } |
1278 | + p.Series = config.PreferredSeries(conf) |
1279 | } |
1280 | if p.ContainerType != "" { |
1281 | // Guard against dubious client by making sure that |
1282 | @@ -709,7 +705,7 @@ |
1283 | } |
1284 | |
1285 | info := api.EnvironmentInfo{ |
1286 | - DefaultSeries: conf.DefaultSeries(), |
1287 | + DefaultSeries: config.PreferredSeries(conf), |
1288 | ProviderType: conf.Type(), |
1289 | Name: conf.Name(), |
1290 | UUID: env.UUID(), |
1291 | @@ -955,6 +951,37 @@ |
1292 | return err |
1293 | } |
1294 | |
1295 | +func (c *Client) ResolveCharms(args params.ResolveCharms) (params.ResolveCharmResults, error) { |
1296 | + var results params.ResolveCharmResults |
1297 | + |
1298 | + envConfig, err := c.api.state.EnvironConfig() |
1299 | + if err != nil { |
1300 | + return params.ResolveCharmResults{}, err |
1301 | + } |
1302 | + repo := config.SpecializeCharmRepo(CharmStore, envConfig) |
1303 | + |
1304 | + for _, ref := range args.References { |
1305 | + result := params.ResolveCharmResult{} |
1306 | + curl, err := c.resolveCharm(ref, repo) |
1307 | + if err != nil { |
1308 | + result.Error = err.Error() |
1309 | + } else { |
1310 | + result.URL = curl |
1311 | + } |
1312 | + results.URLs = append(results.URLs, result) |
1313 | + } |
1314 | + return results, nil |
1315 | +} |
1316 | + |
1317 | +func (c *Client) resolveCharm(ref charm.Reference, repo charm.Repository) (*charm.URL, error) { |
1318 | + if ref.Schema != "cs" { |
1319 | + return nil, fmt.Errorf("only charm store charm references are supported, with cs: schema") |
1320 | + } |
1321 | + |
1322 | + // Resolve the charm location with the repository. |
1323 | + return repo.Resolve(ref) |
1324 | +} |
1325 | + |
1326 | // CharmArchiveName returns a string that is suitable as a file name |
1327 | // in a storage URL. It is constructed from the charm name, revision |
1328 | // and a random UUID string. |
1329 | @@ -993,7 +1020,7 @@ |
1330 | if err != nil { |
1331 | return err |
1332 | } |
1333 | - series = cfg.DefaultSeries() |
1334 | + series = config.PreferredSeries(cfg) |
1335 | } |
1336 | return c.api.state.EnsureAvailability(args.NumStateServers, args.Constraints, series) |
1337 | } |
1338 | |
1339 | === modified file 'state/apiserver/client/client_test.go' |
1340 | --- state/apiserver/client/client_test.go 2014-04-01 03:37:13 +0000 |
1341 | +++ state/apiserver/client/client_test.go 2014-04-03 17:37:27 +0000 |
1342 | @@ -261,7 +261,7 @@ |
1343 | c.Assert(err, gc.IsNil) |
1344 | env, err := s.State.Environment() |
1345 | c.Assert(err, gc.IsNil) |
1346 | - c.Assert(info.DefaultSeries, gc.Equals, conf.DefaultSeries()) |
1347 | + c.Assert(info.DefaultSeries, gc.Equals, config.PreferredSeries(conf)) |
1348 | c.Assert(info.ProviderType, gc.Equals, conf.Type()) |
1349 | c.Assert(info.Name, gc.Equals, conf.Name()) |
1350 | c.Assert(info.UUID, gc.Equals, env.UUID()) |
1351 | @@ -1119,8 +1119,12 @@ |
1352 | } |
1353 | |
1354 | func addCharm(c *gc.C, store *coretesting.MockCharmStore, name string) (*charm.URL, charm.Charm) { |
1355 | + return addSeriesCharm(c, store, "precise", name) |
1356 | +} |
1357 | + |
1358 | +func addSeriesCharm(c *gc.C, store *coretesting.MockCharmStore, series, name string) (*charm.URL, charm.Charm) { |
1359 | bundle := coretesting.Charms.Bundle(c.MkDir(), name) |
1360 | - scurl := fmt.Sprintf("cs:precise/%s-%d", name, bundle.Revision()) |
1361 | + scurl := fmt.Sprintf("cs:%s/%s-%d", series, name, bundle.Revision()) |
1362 | curl := charm.MustParseURL(scurl) |
1363 | err := store.SetCharm(curl, bundle) |
1364 | c.Assert(err, gc.IsNil) |
1365 | @@ -1634,7 +1638,7 @@ |
1366 | c.Assert(len(machines), gc.Equals, 3) |
1367 | for i, machineResult := range machines { |
1368 | c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) |
1369 | - s.checkMachine(c, machineResult.Machine, config.DefaultSeries, apiParams[i].Constraints.String()) |
1370 | + s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String()) |
1371 | } |
1372 | } |
1373 | |
1374 | @@ -1684,7 +1688,7 @@ |
1375 | c.Assert(len(machines), gc.Equals, 3) |
1376 | for i, machineResult := range machines { |
1377 | c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) |
1378 | - s.checkMachine(c, machineResult.Machine, config.DefaultSeries, apiParams[i].Constraints.String()) |
1379 | + s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String()) |
1380 | } |
1381 | } |
1382 | |
1383 | @@ -1755,7 +1759,7 @@ |
1384 | c.Assert(machineResult.Error, gc.ErrorMatches, "cannot add a new machine: cannot add a machine with an instance id and no nonce") |
1385 | } else { |
1386 | c.Assert(machineResult.Machine, gc.DeepEquals, strconv.Itoa(i)) |
1387 | - s.checkMachine(c, machineResult.Machine, config.DefaultSeries, apiParams[i].Constraints.String()) |
1388 | + s.checkMachine(c, machineResult.Machine, coretesting.FakeDefaultSeries, apiParams[i].Constraints.String()) |
1389 | instanceId := fmt.Sprintf("1234-%d", i) |
1390 | s.checkInstance(c, machineResult.Machine, instanceId, "foo", hc, addrs) |
1391 | } |
1392 | @@ -1943,6 +1947,57 @@ |
1393 | s.assertUploaded(c, storage, sch.BundleURL(), sch.BundleSha256()) |
1394 | } |
1395 | |
1396 | +var resolveCharmCases = []struct { |
1397 | + schema, defaultSeries, charmName string |
1398 | + parseErr string |
1399 | + resolveErr string |
1400 | +}{ |
1401 | + {"cs", "precise", "wordpress", "", ""}, |
1402 | + {"cs", "trusty", "wordpress", "", ""}, |
1403 | + {"cs", "", "wordpress", "", `missing default series, cannot resolve charm url: "cs:wordpress"`}, |
1404 | + {"cs", "trusty", "", `charm URL has invalid charm name: "cs:"`, ""}, |
1405 | + {"local", "trusty", "wordpress", "", `only charm store charm references are supported, with cs: schema`}, |
1406 | + {"cs", "precise", "hl3", "", ""}, |
1407 | + {"cs", "trusty", "hl3", "", ""}, |
1408 | + {"cs", "", "hl3", "", `missing default series, cannot resolve charm url: \"cs:hl3\"`}, |
1409 | +} |
1410 | + |
1411 | +func (s *clientSuite) TestResolveCharm(c *gc.C) { |
1412 | + store, restore := makeMockCharmStore() |
1413 | + defer restore() |
1414 | + |
1415 | + for i, test := range resolveCharmCases { |
1416 | + c.Logf("test %d: %#v", i, test) |
1417 | + // Mock charm store will use this to resolve a charm reference. |
1418 | + store.DefaultSeries = test.defaultSeries |
1419 | + |
1420 | + client := s.APIState.Client() |
1421 | + ref, series, err := charm.ParseReference(fmt.Sprintf("%s:%s", test.schema, test.charmName)) |
1422 | + if test.parseErr == "" { |
1423 | + if !c.Check(err, gc.IsNil) { |
1424 | + continue |
1425 | + } |
1426 | + } else { |
1427 | + c.Assert(err, gc.NotNil) |
1428 | + c.Check(err, gc.ErrorMatches, test.parseErr) |
1429 | + continue |
1430 | + } |
1431 | + c.Check(series, gc.Equals, "") |
1432 | + c.Check(ref.String(), gc.Equals, fmt.Sprintf("%s:%s", test.schema, test.charmName)) |
1433 | + |
1434 | + curl, err := client.ResolveCharm(ref) |
1435 | + if err == nil { |
1436 | + c.Assert(curl, gc.NotNil) |
1437 | + // Only cs: schema should make it through here |
1438 | + c.Check(curl.String(), gc.Equals, fmt.Sprintf("cs:%s/%s", test.defaultSeries, test.charmName)) |
1439 | + c.Check(test.resolveErr, gc.Equals, "") |
1440 | + } else { |
1441 | + c.Check(curl, gc.IsNil) |
1442 | + c.Check(err, gc.ErrorMatches, test.resolveErr) |
1443 | + } |
1444 | + } |
1445 | +} |
1446 | + |
1447 | func (s *clientSuite) TestAddCharmConcurrently(c *gc.C) { |
1448 | store, restore := makeMockCharmStore() |
1449 | defer restore() |
1450 | |
1451 | === modified file 'testing/environ.go' |
1452 | --- testing/environ.go 2014-04-01 08:20:44 +0000 |
1453 | +++ testing/environ.go 2014-04-03 17:37:27 +0000 |
1454 | @@ -28,6 +28,8 @@ |
1455 | } |
1456 | } |
1457 | |
1458 | +const FakeDefaultSeries = "precise" |
1459 | + |
1460 | // FakeConfig() returns an environment configuration for a |
1461 | // fake provider with all required attributes set. |
1462 | func FakeConfig() Attrs { |
1463 | @@ -43,7 +45,7 @@ |
1464 | "development": false, |
1465 | "state-port": 19034, |
1466 | "api-port": 17777, |
1467 | - "default-series": config.DefaultSeries, |
1468 | + "default-series": FakeDefaultSeries, |
1469 | } |
1470 | } |
1471 | |
1472 | |
1473 | === modified file 'worker/provisioner/container_initialisation_test.go' |
1474 | --- worker/provisioner/container_initialisation_test.go 2014-03-19 03:48:12 +0000 |
1475 | +++ worker/provisioner/container_initialisation_test.go 2014-04-03 17:37:27 +0000 |
1476 | @@ -12,10 +12,10 @@ |
1477 | |
1478 | "launchpad.net/juju-core/agent" |
1479 | "launchpad.net/juju-core/environs" |
1480 | - "launchpad.net/juju-core/environs/config" |
1481 | "launchpad.net/juju-core/instance" |
1482 | "launchpad.net/juju-core/state" |
1483 | apiprovisioner "launchpad.net/juju-core/state/api/provisioner" |
1484 | + coretesting "launchpad.net/juju-core/testing" |
1485 | "launchpad.net/juju-core/utils" |
1486 | "launchpad.net/juju-core/version" |
1487 | "launchpad.net/juju-core/worker" |
1488 | @@ -88,7 +88,7 @@ |
1489 | |
1490 | // make a container on the host machine |
1491 | template := state.MachineTemplate{ |
1492 | - Series: config.DefaultSeries, |
1493 | + Series: coretesting.FakeDefaultSeries, |
1494 | Jobs: []state.MachineJob{state.JobHostUnits}, |
1495 | } |
1496 | container, err := s.State.AddMachineInsideMachine(template, host.Id(), ctype) |
1497 | @@ -131,7 +131,7 @@ |
1498 | for _, ctype := range instance.ContainerTypes { |
1499 | // create a machine to host the container. |
1500 | m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ |
1501 | - Series: config.DefaultSeries, |
1502 | + Series: coretesting.FakeDefaultSeries, |
1503 | Jobs: []state.MachineJob{state.JobHostUnits}, |
1504 | Constraints: s.defaultConstraints, |
1505 | }) |
1506 | @@ -154,7 +154,7 @@ |
1507 | |
1508 | // create a machine to host the container. |
1509 | m, err := s.BackingState.AddOneMachine(state.MachineTemplate{ |
1510 | - Series: config.DefaultSeries, |
1511 | + Series: coretesting.FakeDefaultSeries, |
1512 | Jobs: []state.MachineJob{state.JobHostUnits}, |
1513 | Constraints: s.defaultConstraints, |
1514 | }) |
1515 | |
1516 | === modified file 'worker/provisioner/kvm-broker_test.go' |
1517 | --- worker/provisioner/kvm-broker_test.go 2014-04-02 11:35:49 +0000 |
1518 | +++ worker/provisioner/kvm-broker_test.go 2014-04-03 17:37:27 +0000 |
1519 | @@ -16,7 +16,6 @@ |
1520 | "launchpad.net/juju-core/container/kvm/mock" |
1521 | kvmtesting "launchpad.net/juju-core/container/kvm/testing" |
1522 | "launchpad.net/juju-core/environs" |
1523 | - "launchpad.net/juju-core/environs/config" |
1524 | "launchpad.net/juju-core/errors" |
1525 | "launchpad.net/juju-core/instance" |
1526 | instancetest "launchpad.net/juju-core/instance/testing" |
1527 | @@ -162,7 +161,7 @@ |
1528 | |
1529 | // The kvm provisioner actually needs the machine it is being created on |
1530 | // to be in state, in order to get the watcher. |
1531 | - m, err := s.State.AddMachine(config.DefaultSeries, state.JobHostUnits, state.JobManageEnviron) |
1532 | + m, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits, state.JobManageEnviron) |
1533 | c.Assert(err, gc.IsNil) |
1534 | err = m.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) |
1535 | c.Assert(err, gc.IsNil) |
1536 | @@ -227,7 +226,7 @@ |
1537 | defer stop(c, p) |
1538 | |
1539 | // Check that an instance is not provisioned when the machine is created. |
1540 | - _, err := s.State.AddMachine(config.DefaultSeries, state.JobHostUnits) |
1541 | + _, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) |
1542 | c.Assert(err, gc.IsNil) |
1543 | |
1544 | s.expectNoEvents(c) |
1545 | @@ -244,7 +243,7 @@ |
1546 | |
1547 | func (s *kvmProvisionerSuite) addContainer(c *gc.C) *state.Machine { |
1548 | template := state.MachineTemplate{ |
1549 | - Series: config.DefaultSeries, |
1550 | + Series: coretesting.FakeDefaultSeries, |
1551 | Jobs: []state.MachineJob{state.JobHostUnits}, |
1552 | } |
1553 | container, err := s.State.AddMachineInsideMachine(template, s.machineId, instance.KVM) |
1554 | |
1555 | === modified file 'worker/provisioner/lxc-broker_test.go' |
1556 | --- worker/provisioner/lxc-broker_test.go 2014-04-02 11:35:49 +0000 |
1557 | +++ worker/provisioner/lxc-broker_test.go 2014-04-03 17:37:27 +0000 |
1558 | @@ -17,7 +17,6 @@ |
1559 | "launchpad.net/juju-core/container/lxc/mock" |
1560 | lxctesting "launchpad.net/juju-core/container/lxc/testing" |
1561 | "launchpad.net/juju-core/environs" |
1562 | - "launchpad.net/juju-core/environs/config" |
1563 | "launchpad.net/juju-core/errors" |
1564 | "launchpad.net/juju-core/instance" |
1565 | instancetest "launchpad.net/juju-core/instance/testing" |
1566 | @@ -191,7 +190,7 @@ |
1567 | |
1568 | // The lxc provisioner actually needs the machine it is being created on |
1569 | // to be in state, in order to get the watcher. |
1570 | - m, err := s.State.AddMachine(config.DefaultSeries, state.JobHostUnits, state.JobManageEnviron) |
1571 | + m, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits, state.JobManageEnviron) |
1572 | c.Assert(err, gc.IsNil) |
1573 | err = m.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown)) |
1574 | c.Assert(err, gc.IsNil) |
1575 | @@ -260,7 +259,7 @@ |
1576 | defer stop(c, p) |
1577 | |
1578 | // Check that an instance is not provisioned when the machine is created. |
1579 | - _, err := s.State.AddMachine(config.DefaultSeries, state.JobHostUnits) |
1580 | + _, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) |
1581 | c.Assert(err, gc.IsNil) |
1582 | |
1583 | s.expectNoEvents(c) |
1584 | @@ -277,7 +276,7 @@ |
1585 | |
1586 | func (s *lxcProvisionerSuite) addContainer(c *gc.C) *state.Machine { |
1587 | template := state.MachineTemplate{ |
1588 | - Series: config.DefaultSeries, |
1589 | + Series: coretesting.FakeDefaultSeries, |
1590 | Jobs: []state.MachineJob{state.JobHostUnits}, |
1591 | } |
1592 | container, err := s.State.AddMachineInsideMachine(template, s.parentMachineId, instance.LXC) |
1593 | |
1594 | === modified file 'worker/provisioner/provisioner_test.go' |
1595 | --- worker/provisioner/provisioner_test.go 2014-04-02 07:50:15 +0000 |
1596 | +++ worker/provisioner/provisioner_test.go 2014-04-03 17:37:27 +0000 |
1597 | @@ -349,7 +349,7 @@ |
1598 | |
1599 | func (s *CommonProvisionerSuite) addMachineWithNetworks(includeNetworks, excludeNetworks []string) (*state.Machine, error) { |
1600 | return s.BackingState.AddOneMachine(state.MachineTemplate{ |
1601 | - Series: config.DefaultSeries, |
1602 | + Series: coretesting.FakeDefaultSeries, |
1603 | Jobs: []state.MachineJob{state.JobHostUnits}, |
1604 | Constraints: s.defaultConstraints, |
1605 | IncludeNetworks: includeNetworks, |
1606 | @@ -442,7 +442,7 @@ |
1607 | |
1608 | // make a container on the machine we just created |
1609 | template := state.MachineTemplate{ |
1610 | - Series: config.DefaultSeries, |
1611 | + Series: coretesting.FakeDefaultSeries, |
1612 | Jobs: []state.MachineJob{state.JobHostUnits}, |
1613 | } |
1614 | container, err := s.State.AddMachineInsideMachine(template, m.Id(), instance.LXC) |
Reviewers: mp+212755_ code.launchpad. net,
Message:
Please take a look.
Description:
Resolve series with charm store in juju client.
For the deploy and upgradecharm commands, when a series is not provided,
and a
default-series is not set in the environment config, the client will
resolve
the series with the charm store, through the state server. If the
client is
working with a 1.16 state server, it will resolve the series directly
with the
charm store.
Existing environments will have a default-series, so they should have no
change in series selection.
New environments will continue to support the default-series config
setting,
but it can be omitted, and is not set by default.
https:/ /code.launchpad .net/~cmars/ juju-core/ resolve- cs-series/ +merge/ 212755
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/80280043/
Affected files (+189, -32 lines): upgradecharm. go bootstrap/ synctools. go config/ config. go conn.go /client/ client. go /client/ client_ test.go
A [revision details]
M charm/charm.go
M charm/charm_test.go
M cmd/juju/deploy.go
M cmd/juju/publish.go
M cmd/juju/
M environs/
M environs/
M juju/testing/
M state/api/client.go
M state/apiserver
M state/apiserver