github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/application/upgradecharm_resources_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package application_test 5 6 import ( 7 "bytes" 8 "io/ioutil" 9 "net/http/httptest" 10 "path" 11 "sort" 12 "strings" 13 "time" 14 15 "github.com/juju/cmd/cmdtesting" 16 gitjujutesting "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 gc "gopkg.in/check.v1" 19 "gopkg.in/juju/charm.v6" 20 charmresource "gopkg.in/juju/charm.v6/resource" 21 "gopkg.in/juju/charmrepo.v3" 22 "gopkg.in/juju/charmrepo.v3/csclient" 23 "gopkg.in/juju/charmstore.v5" 24 "gopkg.in/juju/names.v2" 25 "gopkg.in/mgo.v2" 26 27 "github.com/juju/juju/cmd/juju/application" 28 "github.com/juju/juju/component/all" 29 "github.com/juju/juju/controller" 30 "github.com/juju/juju/core/constraints" 31 jujutesting "github.com/juju/juju/juju/testing" 32 "github.com/juju/juju/resource" 33 "github.com/juju/juju/state" 34 "github.com/juju/juju/testcharms" 35 ) 36 37 type UpgradeCharmResourceSuite struct { 38 jujutesting.RepoSuite 39 } 40 41 var _ = gc.Suite(&UpgradeCharmResourceSuite{}) 42 43 func (s *UpgradeCharmResourceSuite) SetUpSuite(c *gc.C) { 44 s.RepoSuite.SetUpSuite(c) 45 all.RegisterForServer() 46 } 47 48 func (s *UpgradeCharmResourceSuite) SetUpTest(c *gc.C) { 49 s.RepoSuite.SetUpTest(c) 50 chPath := testcharms.Repo.ClonedDirPath(s.CharmsPath, "riak") 51 _, err := runDeploy(c, chPath, "riak", "--series", "quantal", "--force") 52 c.Assert(err, jc.ErrorIsNil) 53 curl := charm.MustParseURL("local:quantal/riak-7") 54 riak, _ := s.RepoSuite.AssertApplication(c, "riak", curl, 1, 1) 55 c.Assert(err, jc.ErrorIsNil) 56 _, forced, err := riak.Charm() 57 c.Assert(err, jc.ErrorIsNil) 58 c.Assert(forced, jc.IsFalse) 59 } 60 61 func (s *UpgradeCharmResourceSuite) TestUpgradeWithResources(c *gc.C) { 62 const riakResourceMeta = ` 63 name: riak 64 summary: "K/V storage engine" 65 description: "Scalable K/V Store in Erlang with Clocks :-)" 66 provides: 67 endpoint: 68 interface: http 69 admin: 70 interface: http 71 peers: 72 ring: 73 interface: riak 74 resources: 75 data: 76 type: file 77 filename: foo.lib 78 description: some comment 79 ` 80 81 myriakPath := testcharms.Repo.ClonedDir(c.MkDir(), "riak") 82 err := ioutil.WriteFile(path.Join(myriakPath.Path, "metadata.yaml"), []byte(riakResourceMeta), 0644) 83 c.Assert(err, jc.ErrorIsNil) 84 85 data := []byte("some-data") 86 fp, err := charmresource.GenerateFingerprint(bytes.NewReader(data)) 87 c.Assert(err, jc.ErrorIsNil) 88 89 resourceFile := path.Join(c.MkDir(), "data.lib") 90 err = ioutil.WriteFile(resourceFile, data, 0644) 91 c.Assert(err, jc.ErrorIsNil) 92 93 _, err = cmdtesting.RunCommand(c, application.NewUpgradeCharmCommand(), 94 "riak", "--path="+myriakPath.Path, "--resource", "data="+resourceFile) 95 c.Assert(err, jc.ErrorIsNil) 96 97 resources, err := s.State.Resources() 98 c.Assert(err, jc.ErrorIsNil) 99 100 sr, err := resources.ListResources("riak") 101 c.Assert(err, jc.ErrorIsNil) 102 103 c.Check(sr.Resources, gc.HasLen, 1) 104 105 c.Check(sr.Resources[0].ApplicationID, gc.Equals, "riak") 106 107 // Most of this is just a sanity check... this is all tested elsewhere. 108 c.Check(sr.Resources[0].PendingID, gc.Equals, "") 109 c.Check(sr.Resources[0].Username, gc.Not(gc.Equals), "") 110 c.Check(sr.Resources[0].ID, gc.Not(gc.Equals), "") 111 c.Check(sr.Resources[0].Timestamp.IsZero(), jc.IsFalse) 112 113 // Ensure we get the data we passed in from the metadata.yaml. 114 c.Check(sr.Resources[0].Resource, gc.DeepEquals, charmresource.Resource{ 115 Meta: charmresource.Meta{ 116 Name: "data", 117 Type: charmresource.TypeFile, 118 Path: "foo.lib", 119 Description: "some comment", 120 }, 121 Origin: charmresource.OriginUpload, 122 Fingerprint: fp, 123 Size: int64(len(data)), 124 }) 125 } 126 127 // charmStoreSuite is a suite fixture that puts the machinery in 128 // place to allow testing code that calls addCharmViaAPI. 129 type charmStoreSuite struct { 130 jujutesting.JujuConnSuite 131 handler charmstore.HTTPCloseHandler 132 srv *httptest.Server 133 srvSession *mgo.Session 134 client *csclient.Client 135 } 136 137 func (s *charmStoreSuite) SetUpTest(c *gc.C) { 138 srvSession, err := gitjujutesting.MgoServer.Dial() 139 c.Assert(err, gc.IsNil) 140 s.srvSession = srvSession 141 142 // Set up the charm store testing server. 143 db := s.srvSession.DB("juju-testing") 144 params := charmstore.ServerParams{ 145 AuthUsername: "test-user", 146 AuthPassword: "test-password", 147 } 148 handler, err := charmstore.NewServer(db, nil, "", params, charmstore.V5) 149 c.Assert(err, jc.ErrorIsNil) 150 s.handler = handler 151 s.srv = httptest.NewServer(handler) 152 s.client = csclient.New(csclient.Params{ 153 URL: s.srv.URL, 154 User: params.AuthUsername, 155 Password: params.AuthPassword, 156 }) 157 158 // Set charmstore URL config so the config is set during bootstrap 159 if s.ControllerConfigAttrs == nil { 160 s.ControllerConfigAttrs = make(map[string]interface{}) 161 } 162 s.JujuConnSuite.ControllerConfigAttrs[controller.CharmStoreURL] = s.srv.URL 163 164 s.JujuConnSuite.SetUpTest(c) 165 166 // Initialize the charm cache dir. 167 s.PatchValue(&charmrepo.CacheDir, c.MkDir()) 168 } 169 170 func (s *charmStoreSuite) TearDownTest(c *gc.C) { 171 s.handler.Close() 172 s.srv.Close() 173 s.srvSession.Close() 174 s.JujuConnSuite.TearDownTest(c) 175 } 176 177 type UpgradeCharmStoreResourceSuite struct { 178 charmStoreSuite 179 } 180 181 var _ = gc.Suite(&UpgradeCharmStoreResourceSuite{}) 182 183 func (s *UpgradeCharmStoreResourceSuite) SetUpSuite(c *gc.C) { 184 s.charmStoreSuite.SetUpSuite(c) 185 err := all.RegisterForServer() 186 c.Assert(err, jc.ErrorIsNil) 187 err = all.RegisterForClient() 188 c.Assert(err, jc.ErrorIsNil) 189 } 190 191 // TODO(ericsnow) Adapt this test to check passing revisions once the 192 // charmstore endpoints are implemented. 193 194 func (s *UpgradeCharmStoreResourceSuite) TestDeployStarsaySuccess(c *gc.C) { 195 testcharms.UploadCharm(c, s.client, "trusty/starsay-1", "starsay") 196 197 // let's make a fake resource file to upload 198 resourceContent := "some-data" 199 200 resourceFile := path.Join(c.MkDir(), "data.xml") 201 err := ioutil.WriteFile(resourceFile, []byte(resourceContent), 0644) 202 c.Assert(err, jc.ErrorIsNil) 203 204 output, err := runDeploy(c, "trusty/starsay", "--resource", "upload-resource="+resourceFile) 205 c.Assert(err, jc.ErrorIsNil) 206 207 expectedOutput := `Located charm "cs:trusty/starsay-1". 208 Deploying charm "cs:trusty/starsay-1".` 209 c.Assert(output, gc.Equals, expectedOutput) 210 s.assertCharmsUploaded(c, "cs:trusty/starsay-1") 211 s.assertApplicationsDeployed(c, map[string]applicationInfo{ 212 "starsay": {charm: "cs:trusty/starsay-1"}, 213 }) 214 215 unit, err := s.State.Unit("starsay/0") 216 c.Assert(err, jc.ErrorIsNil) 217 tags := []names.UnitTag{unit.UnitTag()} 218 errs, err := s.APIState.UnitAssigner().AssignUnits(tags) 219 c.Assert(err, jc.ErrorIsNil) 220 c.Assert(errs, gc.DeepEquals, []error{nil}) 221 222 res, err := s.State.Resources() 223 c.Assert(err, jc.ErrorIsNil) 224 svcres, err := res.ListResources("starsay") 225 c.Assert(err, jc.ErrorIsNil) 226 227 sort.Sort(byname(svcres.Resources)) 228 229 c.Assert(svcres.Resources, gc.HasLen, 3) 230 c.Check(svcres.Resources[2].Timestamp, gc.Not(gc.Equals), time.Time{}) 231 svcres.Resources[2].Timestamp = time.Time{} 232 233 // Note that all charm resources were uploaded by testcharms.UploadCharm 234 // so that the charm could be published. 235 expectedResources := []resource.Resource{{ 236 Resource: charmresource.Resource{ 237 Meta: charmresource.Meta{ 238 Name: "install-resource", 239 Type: charmresource.TypeFile, 240 Path: "gotta-have-it.txt", 241 Description: "get things started", 242 }, 243 Origin: charmresource.OriginStore, 244 Revision: 0, 245 Fingerprint: resourceHash("install-resource content"), 246 Size: int64(len("install-resource content")), 247 }, 248 ID: "starsay/install-resource", 249 ApplicationID: "starsay", 250 }, { 251 Resource: charmresource.Resource{ 252 Meta: charmresource.Meta{ 253 Name: "store-resource", 254 Type: charmresource.TypeFile, 255 Path: "filename.tgz", 256 Description: "One line that is useful when operators need to push it.", 257 }, 258 Origin: charmresource.OriginStore, 259 Revision: 0, 260 Fingerprint: resourceHash("store-resource content"), 261 Size: int64(len("store-resource content")), 262 }, 263 ID: "starsay/store-resource", 264 ApplicationID: "starsay", 265 }, { 266 Resource: charmresource.Resource{ 267 Meta: charmresource.Meta{ 268 Name: "upload-resource", 269 Type: charmresource.TypeFile, 270 Path: "somename.xml", 271 Description: "Who uses xml anymore?", 272 }, 273 Origin: charmresource.OriginUpload, 274 Revision: 0, 275 Fingerprint: resourceHash(resourceContent), 276 Size: int64(len(resourceContent)), 277 }, 278 ID: "starsay/upload-resource", 279 ApplicationID: "starsay", 280 Username: "admin", 281 // Timestamp is checked above 282 }} 283 284 c.Check(svcres.Resources, jc.DeepEquals, expectedResources) 285 286 oldCharmStoreResources := make([]charmresource.Resource, len(svcres.CharmStoreResources)) 287 copy(oldCharmStoreResources, svcres.CharmStoreResources) 288 289 sort.Sort(csbyname(oldCharmStoreResources)) 290 291 testcharms.UploadCharm(c, s.client, "trusty/starsay-2", "starsay") 292 293 _, err = cmdtesting.RunCommand(c, application.NewUpgradeCharmCommand(), "starsay") 294 c.Assert(err, jc.ErrorIsNil) 295 296 s.assertApplicationsDeployed(c, map[string]applicationInfo{ 297 "starsay": {charm: "cs:trusty/starsay-2"}, 298 }) 299 300 res, err = s.State.Resources() 301 c.Assert(err, jc.ErrorIsNil) 302 svcres, err = res.ListResources("starsay") 303 c.Assert(err, jc.ErrorIsNil) 304 305 sort.Sort(byname(svcres.Resources)) 306 307 c.Assert(svcres.Resources, gc.HasLen, 3) 308 c.Check(svcres.Resources[2].Timestamp, gc.Not(gc.Equals), time.Time{}) 309 svcres.Resources[2].Timestamp = time.Time{} 310 311 // ensure that we haven't overridden the previously uploaded resource. 312 c.Check(svcres.Resources, jc.DeepEquals, expectedResources) 313 314 sort.Sort(csbyname(svcres.CharmStoreResources)) 315 c.Check(oldCharmStoreResources, gc.DeepEquals, svcres.CharmStoreResources) 316 } 317 318 func resourceHash(content string) charmresource.Fingerprint { 319 fp, err := charmresource.GenerateFingerprint(strings.NewReader(content)) 320 if err != nil { 321 panic(err) 322 } 323 return fp 324 } 325 326 type byname []resource.Resource 327 328 func (b byname) Len() int { return len(b) } 329 func (b byname) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 330 func (b byname) Less(i, j int) bool { return b[i].Name < b[j].Name } 331 332 type csbyname []charmresource.Resource 333 334 func (b csbyname) Len() int { return len(b) } 335 func (b csbyname) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 336 func (b csbyname) Less(i, j int) bool { return b[i].Name < b[j].Name } 337 338 // assertCharmsUploaded checks that the given charm ids have been uploaded. 339 func (s *charmStoreSuite) assertCharmsUploaded(c *gc.C, ids ...string) { 340 charms, err := s.State.AllCharms() 341 c.Assert(err, jc.ErrorIsNil) 342 uploaded := make([]string, len(charms)) 343 for i, charm := range charms { 344 uploaded[i] = charm.URL().String() 345 } 346 c.Assert(uploaded, jc.SameContents, ids) 347 } 348 349 // assertApplicationsDeployed checks that the given applications have been deployed. 350 func (s *charmStoreSuite) assertApplicationsDeployed(c *gc.C, info map[string]applicationInfo) { 351 applications, err := s.State.AllApplications() 352 c.Assert(err, jc.ErrorIsNil) 353 deployed := make(map[string]applicationInfo, len(applications)) 354 for _, application := range applications { 355 charm, _ := application.CharmURL() 356 config, err := application.CharmConfig() 357 c.Assert(err, jc.ErrorIsNil) 358 if len(config) == 0 { 359 config = nil 360 } 361 constraints, err := application.Constraints() 362 c.Assert(err, jc.ErrorIsNil) 363 storage, err := application.StorageConstraints() 364 c.Assert(err, jc.ErrorIsNil) 365 if len(storage) == 0 { 366 storage = nil 367 } 368 deployed[application.Name()] = applicationInfo{ 369 charm: charm.String(), 370 config: config, 371 constraints: constraints, 372 exposed: application.IsExposed(), 373 storage: storage, 374 } 375 } 376 c.Assert(deployed, jc.DeepEquals, info) 377 } 378 379 // applicationInfo holds information about a deployed application. 380 type applicationInfo struct { 381 charm string 382 config charm.Settings 383 constraints constraints.Value 384 exposed bool 385 storage map[string]state.StorageConstraints 386 endpointBindings map[string]string 387 } 388 389 // runDeploy executes the deploy command in order to deploy the given 390 // charm or bundle. The deployment stderr output and error are returned. 391 // TODO(rog) delete this when tests are universally internal or external. 392 func runDeploy(c *gc.C, args ...string) (string, error) { 393 ctx, err := cmdtesting.RunCommand(c, application.NewDeployCommand(), args...) 394 return strings.Trim(cmdtesting.Stderr(ctx), "\n"), err 395 }