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