13
14
"github.com/juju/errors"
15
"github.com/juju/testing"
14
16
jc "github.com/juju/testing/checkers"
15
17
"github.com/juju/utils"
18
"github.com/juju/version"
16
19
gc "gopkg.in/check.v1"
17
20
"gopkg.in/juju/charm.v6-unstable"
21
charmresource "gopkg.in/juju/charm.v6-unstable/resource"
18
22
"gopkg.in/juju/charmrepo.v2-unstable"
19
23
"gopkg.in/juju/charmrepo.v2-unstable/csclient"
20
24
csclientparams "gopkg.in/juju/charmrepo.v2-unstable/csclient/params"
21
"gopkg.in/juju/charmstore.v5-unstable"
25
charmstore "gopkg.in/juju/charmstore.v5-unstable"
22
26
"gopkg.in/macaroon-bakery.v1/httpbakery"
27
macaroon "gopkg.in/macaroon.v1"
29
"github.com/juju/juju/api"
30
"github.com/juju/juju/api/application"
31
"github.com/juju/juju/api/base"
32
"github.com/juju/juju/api/charms"
33
jujucharmstore "github.com/juju/juju/charmstore"
24
34
"github.com/juju/juju/cmd/modelcmd"
35
"github.com/juju/juju/environs/config"
25
36
jujutesting "github.com/juju/juju/juju/testing"
37
"github.com/juju/juju/jujuclient"
38
"github.com/juju/juju/jujuclient/jujuclienttesting"
39
"github.com/juju/juju/resource/resourceadapters"
26
40
"github.com/juju/juju/rpc"
27
41
"github.com/juju/juju/state"
42
"github.com/juju/juju/storage"
28
43
"github.com/juju/juju/testcharms"
29
"github.com/juju/juju/testing"
44
coretesting "github.com/juju/juju/testing"
32
type UpgradeCharmErrorsSuite struct {
47
type UpgradeCharmSuite struct {
48
testing.IsolationSuite
51
deployResources resourceadapters.DeployResourcesFunc
52
resolveCharm ResolveCharmFunc
53
resolvedCharmURL *charm.URL
54
apiConnection mockAPIConnection
55
charmAdder mockCharmAdder
56
charmClient mockCharmClient
57
charmUpgradeClient mockCharmUpgradeClient
58
modelConfigGetter mockModelConfigGetter
59
resourceLister mockResourceLister
63
var _ = gc.Suite(&UpgradeCharmSuite{})
65
func (s *UpgradeCharmSuite) SetUpTest(c *gc.C) {
66
s.IsolationSuite.SetUpTest(c)
69
// Create persistent cookies in a temporary location.
70
cookieFile := filepath.Join(c.MkDir(), "cookies")
71
s.PatchEnvironment("JUJU_COOKIEFILE", cookieFile)
73
s.deployResources = func(
75
chID jujucharmstore.CharmID,
76
csMac *macaroon.Macaroon,
77
filesAndRevisions map[string]string,
78
resources map[string]charmresource.Meta,
79
conn base.APICallCloser,
80
) (ids map[string]string, err error) {
81
s.AddCall("DeployResources", applicationID, chID, csMac, filesAndRevisions, resources, conn)
82
return nil, s.NextErr()
85
s.resolveCharm = func(
86
resolveWithChannel func(*charm.URL) (*charm.URL, csclientparams.Channel, []string, error),
89
) (*charm.URL, csclientparams.Channel, []string, error) {
90
s.AddCall("ResolveCharm", resolveWithChannel, conf, url)
91
if err := s.NextErr(); err != nil {
92
return nil, csclientparams.NoChannel, nil, err
94
return s.resolvedCharmURL, csclientparams.StableChannel, []string{"quantal"}, nil
97
currentCharmURL := charm.MustParseURL("cs:quantal/foo-1")
98
latestCharmURL := charm.MustParseURL("cs:quantal/foo-2")
99
s.resolvedCharmURL = latestCharmURL
101
s.apiConnection = mockAPIConnection{
102
bestFacadeVersion: 2,
103
serverVersion: &version.Number{
109
s.charmAdder = mockCharmAdder{}
110
s.charmClient = mockCharmClient{
111
charmInfo: &charms.CharmInfo{
115
s.charmUpgradeClient = mockCharmUpgradeClient{charmURL: currentCharmURL}
116
s.modelConfigGetter = mockModelConfigGetter{}
117
s.resourceLister = mockResourceLister{}
119
store := jujuclienttesting.NewMemStore()
120
store.CurrentControllerName = "foo"
121
store.Controllers["foo"] = jujuclient.ControllerDetails{}
122
store.Models["foo"] = &jujuclient.ControllerModels{
123
CurrentModel: "admin@local/bar",
124
Models: map[string]jujuclient.ModelDetails{"admin@local/bar": {}},
126
apiOpener := modelcmd.OpenFunc(func(store jujuclient.ClientStore, controller, model string) (api.Connection, error) {
127
s.AddCall("OpenAPI", store, controller, model)
128
return &s.apiConnection, nil
131
s.cmd = NewUpgradeCharmCommandForTest(
136
func(conn api.Connection, bakeryClient *httpbakery.Client, channel csclientparams.Channel) CharmAdder {
137
s.AddCall("NewCharmAdder", conn, bakeryClient, channel)
141
func(conn api.Connection) CharmClient {
142
s.AddCall("NewCharmClient", conn)
144
return &s.charmClient
146
func(conn api.Connection) CharmUpgradeClient {
147
s.AddCall("NewCharmUpgradeClient", conn)
149
return &s.charmUpgradeClient
151
func(conn api.Connection) ModelConfigGetter {
152
s.AddCall("NewModelConfigGetter", conn)
153
return &s.modelConfigGetter
155
func(conn api.Connection) (ResourceLister, error) {
156
s.AddCall("NewResourceLister", conn)
157
return &s.resourceLister, s.NextErr()
162
func (s *UpgradeCharmSuite) runUpgradeCharm(c *gc.C, args ...string) (*cmd.Context, error) {
163
return coretesting.RunCommand(c, s.cmd, args...)
166
func (s *UpgradeCharmSuite) TestStorageConstraints(c *gc.C) {
167
_, err := s.runUpgradeCharm(c, "foo", "--storage", "bar=baz")
168
c.Assert(err, jc.ErrorIsNil)
169
s.charmUpgradeClient.CheckCallNames(c, "GetCharmURL", "SetCharm")
170
s.charmUpgradeClient.CheckCall(c, 1, "SetCharm", application.SetCharmConfig{
171
ApplicationName: "foo",
172
CharmID: jujucharmstore.CharmID{
173
URL: s.resolvedCharmURL,
174
Channel: csclientparams.StableChannel,
176
StorageConstraints: map[string]storage.Constraints{
177
"bar": {Pool: "baz", Count: 1},
182
func (s *UpgradeCharmSuite) TestStorageConstraintsMinFacadeVersion(c *gc.C) {
183
s.apiConnection.bestFacadeVersion = 1
184
_, err := s.runUpgradeCharm(c, "foo", "--storage", "bar=baz")
185
c.Assert(err, gc.ErrorMatches,
186
"updating storage constraints at upgrade-charm time is not supported by server version 1.2.3")
189
func (s *UpgradeCharmSuite) TestStorageConstraintsMinFacadeVersionNoServerVersion(c *gc.C) {
190
s.apiConnection.bestFacadeVersion = 1
191
s.apiConnection.serverVersion = nil
192
_, err := s.runUpgradeCharm(c, "foo", "--storage", "bar=baz")
193
c.Assert(err, gc.ErrorMatches,
194
"updating storage constraints at upgrade-charm time is not supported by this server")
197
func (s *UpgradeCharmSuite) TestConfigSettings(c *gc.C) {
199
configFile := filepath.Join(tempdir, "config.yaml")
200
err := ioutil.WriteFile(configFile, []byte("foo:{}"), 0644)
201
c.Assert(err, jc.ErrorIsNil)
203
_, err = s.runUpgradeCharm(c, "foo", "--config", configFile)
204
c.Assert(err, jc.ErrorIsNil)
205
s.charmUpgradeClient.CheckCallNames(c, "GetCharmURL", "SetCharm")
206
s.charmUpgradeClient.CheckCall(c, 1, "SetCharm", application.SetCharmConfig{
207
ApplicationName: "foo",
208
CharmID: jujucharmstore.CharmID{
209
URL: s.resolvedCharmURL,
210
Channel: csclientparams.StableChannel,
212
ConfigSettingsYAML: "foo:{}",
216
func (s *UpgradeCharmSuite) TestConfigSettingsMinFacadeVersion(c *gc.C) {
218
configFile := filepath.Join(tempdir, "config.yaml")
219
err := ioutil.WriteFile(configFile, []byte("foo:{}"), 0644)
220
c.Assert(err, jc.ErrorIsNil)
222
s.apiConnection.bestFacadeVersion = 1
223
_, err = s.runUpgradeCharm(c, "foo", "--config", configFile)
224
c.Assert(err, gc.ErrorMatches,
225
"updating config at upgrade-charm time is not supported by server version 1.2.3")
228
type UpgradeCharmErrorsStateSuite struct {
33
229
jujutesting.RepoSuite
34
230
handler charmstore.HTTPCloseHandler
35
231
srv *httptest.Server
38
func (s *UpgradeCharmErrorsSuite) SetUpTest(c *gc.C) {
234
func (s *UpgradeCharmErrorsStateSuite) SetUpTest(c *gc.C) {
39
235
s.RepoSuite.SetUpTest(c)
40
236
// Set up the charm store testing server.
41
237
handler, err := charmstore.NewServer(s.Session.DB("juju-testing"), nil, "", charmstore.ServerParams{
98
294
// TODO(dimitern): add tests with incompatible charms
101
func (s *UpgradeCharmErrorsSuite) TestNoPathFails(c *gc.C) {
297
func (s *UpgradeCharmErrorsStateSuite) TestNoPathFails(c *gc.C) {
102
298
s.deployService(c)
103
299
err := runUpgradeCharm(c, "riak")
104
300
c.Assert(err, gc.ErrorMatches, "upgrading a local charm requires either --path or --switch")
107
func (s *UpgradeCharmErrorsSuite) TestSwitchAndRevisionFails(c *gc.C) {
303
func (s *UpgradeCharmErrorsStateSuite) TestSwitchAndRevisionFails(c *gc.C) {
108
304
s.deployService(c)
109
305
err := runUpgradeCharm(c, "riak", "--switch=riak", "--revision=2")
110
306
c.Assert(err, gc.ErrorMatches, "--switch and --revision are mutually exclusive")
113
func (s *UpgradeCharmErrorsSuite) TestPathAndRevisionFails(c *gc.C) {
309
func (s *UpgradeCharmErrorsStateSuite) TestPathAndRevisionFails(c *gc.C) {
114
310
s.deployService(c)
115
311
err := runUpgradeCharm(c, "riak", "--path=foo", "--revision=2")
116
312
c.Assert(err, gc.ErrorMatches, "--path and --revision are mutually exclusive")
119
func (s *UpgradeCharmErrorsSuite) TestSwitchAndPathFails(c *gc.C) {
315
func (s *UpgradeCharmErrorsStateSuite) TestSwitchAndPathFails(c *gc.C) {
120
316
s.deployService(c)
121
317
err := runUpgradeCharm(c, "riak", "--switch=riak", "--path=foo")
122
318
c.Assert(err, gc.ErrorMatches, "--switch and --path are mutually exclusive")
125
func (s *UpgradeCharmErrorsSuite) TestInvalidRevision(c *gc.C) {
321
func (s *UpgradeCharmErrorsStateSuite) TestInvalidRevision(c *gc.C) {
126
322
s.deployService(c)
127
323
err := runUpgradeCharm(c, "riak", "--revision=blah")
128
c.Assert(err, gc.ErrorMatches, `invalid value "blah" for flag --revision: strconv.ParseInt: parsing "blah": invalid syntax`)
324
c.Assert(err, gc.ErrorMatches, `invalid value "blah" for flag --revision: strconv.(ParseInt|Atoi): parsing "blah": invalid syntax`)
131
type BaseUpgradeCharmSuite struct{}
327
type BaseUpgradeCharmStateSuite struct{}
133
type UpgradeCharmSuccessSuite struct {
134
BaseUpgradeCharmSuite
329
type UpgradeCharmSuccessStateSuite struct {
330
BaseUpgradeCharmStateSuite
135
331
jujutesting.RepoSuite
136
testing.CmdBlockHelper
332
coretesting.CmdBlockHelper
138
334
riak *state.Application
141
func (s *BaseUpgradeCharmSuite) assertUpgraded(c *gc.C, riak *state.Application, revision int, forced bool) *charm.URL {
337
func (s *BaseUpgradeCharmStateSuite) assertUpgraded(c *gc.C, riak *state.Application, revision int, forced bool) *charm.URL {
142
338
err := riak.Refresh()
143
339
c.Assert(err, jc.ErrorIsNil)
144
340
ch, force, err := riak.Charm()
443
639
err = runUpgradeCharm(c, "terms1")
444
640
c.Assert(err, gc.ErrorMatches, expectedError)
643
type mockAPIConnection struct {
645
bestFacadeVersion int
646
serverVersion *version.Number
649
func (m *mockAPIConnection) BestFacadeVersion(name string) int {
650
return m.bestFacadeVersion
653
func (m *mockAPIConnection) ServerVersion() (version.Number, bool) {
654
if m.serverVersion != nil {
655
return *m.serverVersion, true
657
return version.Number{}, false
660
func (*mockAPIConnection) Close() error {
664
type mockCharmAdder struct {
669
func (m *mockCharmAdder) AddCharm(curl *charm.URL, channel csclientparams.Channel) error {
670
m.MethodCall(m, "AddCharm", curl, channel)
674
type mockCharmClient struct {
677
charmInfo *charms.CharmInfo
680
func (m *mockCharmClient) CharmInfo(curl string) (*charms.CharmInfo, error) {
681
m.MethodCall(m, "CharmInfo", curl)
682
if err := m.NextErr(); err != nil {
685
return m.charmInfo, nil
688
type mockCharmUpgradeClient struct {
694
func (m *mockCharmUpgradeClient) GetCharmURL(applicationName string) (*charm.URL, error) {
695
m.MethodCall(m, "GetCharmURL", applicationName)
696
return m.charmURL, m.NextErr()
699
func (m *mockCharmUpgradeClient) SetCharm(cfg application.SetCharmConfig) error {
700
m.MethodCall(m, "SetCharm", cfg)
704
type mockModelConfigGetter struct {
709
func (m *mockModelConfigGetter) ModelGet() (map[string]interface{}, error) {
710
m.MethodCall(m, "ModelGet")
711
return coretesting.FakeConfig(), m.NextErr()
714
type mockResourceLister struct {