github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/juju/commands/publish_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package commands 5 6 import ( 7 "fmt" 8 "os" 9 10 "github.com/juju/cmd" 11 gitjujutesting "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils" 14 "github.com/juju/utils/bzr" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/charmrepo.v2-unstable" 17 18 "github.com/juju/juju/cmd/modelcmd" 19 "github.com/juju/juju/testing" 20 ) 21 22 // Sadly, this is a very slow test suite, heavily dominated by calls to bzr. 23 24 type PublishSuite struct { 25 testing.FakeJujuXDGDataHomeSuite 26 gitjujutesting.HTTPSuite 27 28 dir string 29 oldBaseURL string 30 branch *bzr.Branch 31 } 32 33 var _ = gc.Suite(&PublishSuite{}) 34 35 func touch(c *gc.C, filename string) { 36 f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY, 0644) 37 c.Assert(err, jc.ErrorIsNil) 38 f.Close() 39 } 40 41 func addMeta(c *gc.C, branch *bzr.Branch, meta string) { 42 if meta == "" { 43 meta = "name: wordpress\nsummary: Some summary\ndescription: Some description.\n" 44 } 45 f, err := os.Create(branch.Join("metadata.yaml")) 46 c.Assert(err, jc.ErrorIsNil) 47 _, err = f.Write([]byte(meta)) 48 f.Close() 49 c.Assert(err, jc.ErrorIsNil) 50 err = branch.Add("metadata.yaml") 51 c.Assert(err, jc.ErrorIsNil) 52 err = branch.Commit("Added metadata.yaml.") 53 c.Assert(err, jc.ErrorIsNil) 54 } 55 56 func (s *PublishSuite) runPublish(c *gc.C, args ...string) (*cmd.Context, error) { 57 return testing.RunCommandInDir(c, newPublishCommand(), args, s.dir) 58 } 59 60 const pollDelay = testing.ShortWait 61 62 func (s *PublishSuite) SetUpSuite(c *gc.C) { 63 s.FakeJujuXDGDataHomeSuite.SetUpSuite(c) 64 s.HTTPSuite.SetUpSuite(c) 65 66 s.oldBaseURL = charmrepo.LegacyStore.BaseURL 67 charmrepo.LegacyStore.BaseURL = s.URL("") 68 } 69 70 func (s *PublishSuite) TearDownSuite(c *gc.C) { 71 s.FakeJujuXDGDataHomeSuite.TearDownSuite(c) 72 s.HTTPSuite.TearDownSuite(c) 73 74 charmrepo.LegacyStore.BaseURL = s.oldBaseURL 75 } 76 77 func (s *PublishSuite) SetUpTest(c *gc.C) { 78 s.FakeJujuXDGDataHomeSuite.SetUpTest(c) 79 s.HTTPSuite.SetUpTest(c) 80 s.PatchEnvironment("BZR_HOME", utils.Home()) 81 s.FakeJujuXDGDataHomeSuite.Home.AddFiles(c, gitjujutesting.TestFile{ 82 Name: bzrHomeFile, 83 Data: "[DEFAULT]\nemail = Test <testing@testing.invalid>\n", 84 }) 85 86 s.dir = c.MkDir() 87 s.branch = bzr.New(s.dir) 88 err := s.branch.Init() 89 c.Assert(err, jc.ErrorIsNil) 90 } 91 92 func (s *PublishSuite) TearDownTest(c *gc.C) { 93 s.HTTPSuite.TearDownTest(c) 94 s.FakeJujuXDGDataHomeSuite.TearDownTest(c) 95 } 96 97 func (s *PublishSuite) TestNoBranch(c *gc.C) { 98 dir := c.MkDir() 99 _, err := testing.RunCommandInDir(c, newPublishCommand(), []string{"cs:precise/wordpress"}, dir) 100 // We need to do this here because \U is outputed on windows 101 // and it's an invalid regex escape sequence 102 c.Assert(err.Error(), gc.Equals, fmt.Sprintf("not a charm branch: %s", dir)) 103 } 104 105 func (s *PublishSuite) TestEmpty(c *gc.C) { 106 _, err := s.runPublish(c, "cs:precise/wordpress") 107 c.Assert(err, gc.ErrorMatches, `cannot obtain local digest: branch has no content`) 108 } 109 110 func (s *PublishSuite) TestFrom(c *gc.C) { 111 _, err := testing.RunCommandInDir(c, newPublishCommand(), []string{"--from", s.dir, "cs:precise/wordpress"}, c.MkDir()) 112 c.Assert(err, gc.ErrorMatches, `cannot obtain local digest: branch has no content`) 113 } 114 115 func (s *PublishSuite) TestNotClean(c *gc.C) { 116 touch(c, s.branch.Join("file")) 117 _, err := s.runPublish(c, "cs:precise/wordpress") 118 c.Assert(err, gc.ErrorMatches, `branch is not clean \(bzr status\)`) 119 } 120 121 func (s *PublishSuite) TestNoPushLocation(c *gc.C) { 122 addMeta(c, s.branch, "") 123 _, err := s.runPublish(c) 124 c.Assert(err, gc.ErrorMatches, `no charm URL provided and cannot infer from current directory \(no push location\)`) 125 } 126 127 func (s *PublishSuite) TestUnknownPushLocation(c *gc.C) { 128 addMeta(c, s.branch, "") 129 err := s.branch.Push(&bzr.PushAttr{Location: c.MkDir() + "/foo", Remember: true}) 130 c.Assert(err, jc.ErrorIsNil) 131 _, err = s.runPublish(c) 132 c.Assert(err, gc.ErrorMatches, `cannot infer charm URL from branch location: ".*/foo"`) 133 } 134 135 func (s *PublishSuite) TestWrongRepository(c *gc.C) { 136 addMeta(c, s.branch, "") 137 _, err := s.runPublish(c, "local:precise/wordpress") 138 c.Assert(err, gc.ErrorMatches, "charm URL must reference the juju charm store") 139 } 140 141 func (s *PublishSuite) TestParseReference(c *gc.C) { 142 addMeta(c, s.branch, "") 143 144 cmd := &publishCommand{} 145 cmd.ChangePushLocation(func(location string) string { 146 c.Assert(location, gc.Equals, "lp:charms/precise/wordpress") 147 c.SucceedNow() 148 panic("unreachable") 149 }) 150 151 _, err := testing.RunCommandInDir(c, modelcmd.Wrap(cmd), []string{"precise/wordpress"}, s.dir) 152 c.Assert(err, jc.ErrorIsNil) 153 c.Fatal("shouldn't get here; location closure didn't run?") 154 } 155 156 func (s *PublishSuite) TestBrokenCharm(c *gc.C) { 157 addMeta(c, s.branch, "name: wordpress\nsummary: Some summary\n") 158 _, err := s.runPublish(c, "cs:precise/wordpress") 159 c.Assert(err, gc.ErrorMatches, "metadata: description: expected string, got nothing") 160 } 161 162 func (s *PublishSuite) TestWrongName(c *gc.C) { 163 addMeta(c, s.branch, "") 164 _, err := s.runPublish(c, "cs:precise/mysql") 165 c.Assert(err, gc.ErrorMatches, `charm name in metadata must match name in URL: "wordpress" != "mysql"`) 166 } 167 168 func (s *PublishSuite) TestPreExistingPublished(c *gc.C) { 169 addMeta(c, s.branch, "") 170 171 // Pretend the store has seen the digest before, and it has succeeded. 172 digest, err := s.branch.RevisionId() 173 c.Assert(err, jc.ErrorIsNil) 174 body := `{"cs:precise/wordpress": {"kind": "published", "digest": %q, "revision": 42}}` 175 gitjujutesting.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) 176 177 ctx, err := s.runPublish(c, "cs:precise/wordpress") 178 c.Assert(err, jc.ErrorIsNil) 179 c.Assert(testing.Stdout(ctx), gc.Equals, "cs:precise/wordpress-42\n") 180 181 req := gitjujutesting.Server.WaitRequest() 182 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 183 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:precise/wordpress@"+digest) 184 } 185 186 func (s *PublishSuite) TestPreExistingPublishedEdge(c *gc.C) { 187 addMeta(c, s.branch, "") 188 189 // If it doesn't find the right digest on the first try, it asks again for 190 // any digest at all to keep the tip in mind. There's a small chance that 191 // on the second request the tip has changed and matches the digest we're 192 // looking for, in which case we have the answer already. 193 digest, err := s.branch.RevisionId() 194 c.Assert(err, jc.ErrorIsNil) 195 var body string 196 body = `{"cs:precise/wordpress": {"errors": ["entry not found"]}}` 197 gitjujutesting.Server.Response(200, nil, []byte(body)) 198 body = `{"cs:precise/wordpress": {"kind": "published", "digest": %q, "revision": 42}}` 199 gitjujutesting.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) 200 201 ctx, err := s.runPublish(c, "cs:precise/wordpress") 202 c.Assert(err, jc.ErrorIsNil) 203 c.Assert(testing.Stdout(ctx), gc.Equals, "cs:precise/wordpress-42\n") 204 205 req := gitjujutesting.Server.WaitRequest() 206 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 207 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:precise/wordpress@"+digest) 208 209 req = gitjujutesting.Server.WaitRequest() 210 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 211 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:precise/wordpress") 212 } 213 214 func (s *PublishSuite) TestPreExistingPublishError(c *gc.C) { 215 addMeta(c, s.branch, "") 216 217 // Pretend the store has seen the digest before, and it has failed. 218 digest, err := s.branch.RevisionId() 219 c.Assert(err, jc.ErrorIsNil) 220 body := `{"cs:precise/wordpress": {"kind": "publish-error", "digest": %q, "errors": ["an error"]}}` 221 gitjujutesting.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) 222 223 _, err = s.runPublish(c, "cs:precise/wordpress") 224 c.Assert(err, gc.ErrorMatches, "charm could not be published: an error") 225 226 req := gitjujutesting.Server.WaitRequest() 227 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 228 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:precise/wordpress@"+digest) 229 } 230 231 func (s *PublishSuite) TestFullPublish(c *gc.C) { 232 addMeta(c, s.branch, "") 233 234 digest, err := s.branch.RevisionId() 235 c.Assert(err, jc.ErrorIsNil) 236 237 pushBranch := bzr.New(c.MkDir()) 238 err = pushBranch.Init() 239 c.Assert(err, jc.ErrorIsNil) 240 241 cmd := &publishCommand{} 242 cmd.ChangePushLocation(func(location string) string { 243 c.Assert(location, gc.Equals, "lp:~user/charms/precise/wordpress/trunk") 244 return pushBranch.Location() 245 }) 246 cmd.SetPollDelay(testing.ShortWait) 247 248 var body string 249 250 // The local digest isn't found. 251 body = `{"cs:~user/precise/wordpress": {"kind": "", "errors": ["entry not found"]}}` 252 gitjujutesting.Server.Response(200, nil, []byte(body)) 253 254 // But the charm exists with an arbitrary non-matching digest. 255 body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": "other-digest"}}` 256 gitjujutesting.Server.Response(200, nil, []byte(body)) 257 258 // After the branch is pushed we fake the publishing delay. 259 body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": "other-digest"}}` 260 gitjujutesting.Server.Response(200, nil, []byte(body)) 261 262 // And finally report success. 263 body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": %q, "revision": 42}}` 264 gitjujutesting.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) 265 266 ctx, err := testing.RunCommandInDir(c, modelcmd.Wrap(cmd), []string{"cs:~user/precise/wordpress"}, s.dir) 267 c.Assert(err, jc.ErrorIsNil) 268 c.Assert(testing.Stdout(ctx), gc.Equals, "cs:~user/precise/wordpress-42\n") 269 270 // Ensure the branch was actually pushed. 271 pushDigest, err := pushBranch.RevisionId() 272 c.Assert(err, jc.ErrorIsNil) 273 c.Assert(pushDigest, gc.Equals, digest) 274 275 // And that all the requests were sent with the proper data. 276 req := gitjujutesting.Server.WaitRequest() 277 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 278 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress@"+digest) 279 280 for i := 0; i < 3; i++ { 281 // The second request grabs tip to see the current state, and the 282 // following requests are done after pushing to see when it changes. 283 req = gitjujutesting.Server.WaitRequest() 284 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 285 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress") 286 } 287 } 288 289 func (s *PublishSuite) TestFullPublishError(c *gc.C) { 290 addMeta(c, s.branch, "") 291 292 digest, err := s.branch.RevisionId() 293 c.Assert(err, jc.ErrorIsNil) 294 295 pushBranch := bzr.New(c.MkDir()) 296 err = pushBranch.Init() 297 c.Assert(err, jc.ErrorIsNil) 298 299 cmd := &publishCommand{} 300 cmd.ChangePushLocation(func(location string) string { 301 c.Assert(location, gc.Equals, "lp:~user/charms/precise/wordpress/trunk") 302 return pushBranch.Location() 303 }) 304 cmd.SetPollDelay(pollDelay) 305 306 var body string 307 308 // The local digest isn't found. 309 body = `{"cs:~user/precise/wordpress": {"kind": "", "errors": ["entry not found"]}}` 310 gitjujutesting.Server.Response(200, nil, []byte(body)) 311 312 // And tip isn't found either, meaning the charm was never published. 313 gitjujutesting.Server.Response(200, nil, []byte(body)) 314 315 // After the branch is pushed we fake the publishing delay. 316 gitjujutesting.Server.Response(200, nil, []byte(body)) 317 318 // And finally report success. 319 body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": %q, "revision": 42}}` 320 gitjujutesting.Server.Response(200, nil, []byte(fmt.Sprintf(body, digest))) 321 322 ctx, err := testing.RunCommandInDir(c, modelcmd.Wrap(cmd), []string{"cs:~user/precise/wordpress"}, s.dir) 323 c.Assert(err, jc.ErrorIsNil) 324 c.Assert(testing.Stdout(ctx), gc.Equals, "cs:~user/precise/wordpress-42\n") 325 326 // Ensure the branch was actually pushed. 327 pushDigest, err := pushBranch.RevisionId() 328 c.Assert(err, jc.ErrorIsNil) 329 c.Assert(pushDigest, gc.Equals, digest) 330 331 // And that all the requests were sent with the proper data. 332 req := gitjujutesting.Server.WaitRequest() 333 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 334 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress@"+digest) 335 336 for i := 0; i < 3; i++ { 337 // The second request grabs tip to see the current state, and the 338 // following requests are done after pushing to see when it changes. 339 req = gitjujutesting.Server.WaitRequest() 340 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 341 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress") 342 } 343 } 344 345 func (s *PublishSuite) TestFullPublishRace(c *gc.C) { 346 addMeta(c, s.branch, "") 347 348 digest, err := s.branch.RevisionId() 349 c.Assert(err, jc.ErrorIsNil) 350 351 pushBranch := bzr.New(c.MkDir()) 352 err = pushBranch.Init() 353 c.Assert(err, jc.ErrorIsNil) 354 355 cmd := &publishCommand{} 356 cmd.ChangePushLocation(func(location string) string { 357 c.Assert(location, gc.Equals, "lp:~user/charms/precise/wordpress/trunk") 358 return pushBranch.Location() 359 }) 360 cmd.SetPollDelay(pollDelay) 361 362 var body string 363 364 // The local digest isn't found. 365 body = `{"cs:~user/precise/wordpress": {"kind": "", "errors": ["entry not found"]}}` 366 gitjujutesting.Server.Response(200, nil, []byte(body)) 367 368 // And tip isn't found either, meaning the charm was never published. 369 gitjujutesting.Server.Response(200, nil, []byte(body)) 370 371 // After the branch is pushed we fake the publishing delay. 372 gitjujutesting.Server.Response(200, nil, []byte(body)) 373 374 // But, surprisingly, the digest changed to something else entirely. 375 body = `{"cs:~user/precise/wordpress": {"kind": "published", "digest": "surprising-digest", "revision": 42}}` 376 gitjujutesting.Server.Response(200, nil, []byte(body)) 377 378 _, err = testing.RunCommandInDir(c, modelcmd.Wrap(cmd), []string{"cs:~user/precise/wordpress"}, s.dir) 379 c.Assert(err, gc.ErrorMatches, `charm changed but not to local charm digest; publishing race\?`) 380 381 // Ensure the branch was actually pushed. 382 pushDigest, err := pushBranch.RevisionId() 383 c.Assert(err, jc.ErrorIsNil) 384 c.Assert(pushDigest, gc.Equals, digest) 385 386 // And that all the requests were sent with the proper data. 387 req := gitjujutesting.Server.WaitRequest() 388 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 389 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress@"+digest) 390 391 for i := 0; i < 3; i++ { 392 // The second request grabs tip to see the current state, and the 393 // following requests are done after pushing to see when it changes. 394 req = gitjujutesting.Server.WaitRequest() 395 c.Assert(req.URL.Path, gc.Equals, "/charm-event") 396 c.Assert(req.Form.Get("charms"), gc.Equals, "cs:~user/precise/wordpress") 397 } 398 }