func (s *S) TestCreateDeployment(c *C) { app := s.createTestApp(c, &ct.App{Name: "create-deployment"}) release := s.createTestRelease(c, &ct.Release{ Processes: map[string]ct.ProcessType{"web": {}}, }) c.Assert(s.c.PutFormation(&ct.Formation{ AppID: app.ID, ReleaseID: release.ID, Processes: map[string]int{"web": 1}, }), IsNil) defer s.c.DeleteFormation(app.ID, release.ID) // deploying an initial release should no-op d, err := s.c.CreateDeployment(app.ID, release.ID) c.Assert(err, IsNil) c.Assert(d.FinishedAt, NotNil) // but the app release should now be set gotRelease, err := s.c.GetAppRelease(app.ID) c.Assert(release.ID, Equals, gotRelease.ID) newRelease := s.createTestRelease(c, &ct.Release{}) d, err = s.c.CreateDeployment(app.ID, newRelease.ID) c.Assert(err, IsNil) c.Assert(d.ID, Not(Equals), "") c.Assert(d.AppID, Equals, app.ID) c.Assert(d.NewReleaseID, Equals, newRelease.ID) c.Assert(d.OldReleaseID, Equals, release.ID) c.Assert(d.DeployTimeout, Equals, app.DeployTimeout) // quickly recreating a deployment should error _, err = s.c.CreateDeployment(app.ID, newRelease.ID) c.Assert(hh.IsValidationError(err), Equals, true) c.Assert(err.(hh.JSONError).Message, Equals, "Cannot create deploy, there is already one in progress for this app.") }
func (s *S) TestCreateFormation(c *C) { for i, useName := range []bool{false, true} { release := s.createTestRelease(c, &ct.Release{ Processes: map[string]ct.ProcessType{"web": {}}, }) app := s.createTestApp(c, &ct.App{Name: fmt.Sprintf("create-formation-%d", i)}) // First create a formation with an invalid process type. Will fail. in := &ct.Formation{ReleaseID: release.ID, AppID: app.ID, Processes: map[string]int{"foo": 1}} if useName { in.AppID = app.Name } err := s.c.PutFormation(in) c.Assert(hh.IsValidationError(err), Equals, true) // Now edit the formation to have valid process types. Should succeed. in.Processes = map[string]int{"web": 1} out := s.createTestFormation(c, in) defer s.deleteTestFormation(out) c.Assert(out.AppID, Equals, app.ID) c.Assert(out.ReleaseID, Equals, release.ID) c.Assert(out.Processes["web"], Equals, 1) var appID string if useName { appID = app.Name } else { appID = app.ID } gotFormation, err := s.c.GetFormation(appID, release.ID) c.Assert(err, IsNil) c.Assert(gotFormation, DeepEquals, out) expanded, err := s.c.GetExpandedFormation(appID, release.ID) c.Assert(err, IsNil) c.Assert(expanded.App.ID, Equals, app.ID) c.Assert(expanded.Release.ID, Equals, release.ID) c.Assert(expanded.Artifacts, HasLen, len(release.ArtifactIDs)) for i, id := range release.ArtifactIDs { c.Assert(expanded.Artifacts[i].ID, Equals, id) } c.Assert(expanded.Processes, DeepEquals, out.Processes) _, err = s.c.GetFormation(appID, release.ID+"fail") c.Assert(err, Equals, controller.ErrNotFound) } }
func (c *context) watchHost(h *cluster.Host, ready chan struct{}) { if !c.hosts.Add(h.ID()) { if ready != nil { ready <- struct{}{} } return } defer c.hosts.Remove(h.ID()) g := grohl.NewContext(grohl.Data{"fn": "watchHost", "host.id": h.ID()}) c.hosts.Set(h.ID(), h) g.Log(grohl.Data{"at": "start"}) ch := make(chan *host.Event) h.StreamEvents("all", ch) if ready != nil { ready <- struct{}{} } // Call PutJob in a goroutine so we don't block receiving job events whilst potentially // making multiple requests to the controller (e.g. if the controller is down). // // Use a channel (rather than spawning a goroutine per event) so that events are delivered in order. jobs := make(chan *ct.Job, 10) go func() { for job := range jobs { putJobAttempts.Run(func() error { if err := c.PutJob(job); err != nil { g.Log(grohl.Data{"at": "put_job_error", "job.id": job.ID, "state": job.State, "err": err}) // ignore validation / not found errors if httphelper.IsValidationError(err) || err == controller.ErrNotFound { return nil } return err } g.Log(grohl.Data{"at": "put_job", "job.id": job.ID, "state": job.State}) return nil }) } }() for event := range ch { meta := event.Job.Job.Metadata appID := meta["flynn-controller.app"] releaseID := meta["flynn-controller.release"] jobType := meta["flynn-controller.type"] if appID == "" || releaseID == "" { continue } job := &ct.Job{ ID: event.JobID, AppID: appID, ReleaseID: releaseID, Type: jobType, State: jobState(event), Meta: jobMetaFromMetadata(meta), } g.Log(grohl.Data{"at": "event", "job.id": event.JobID, "event": event.Event}) jobs <- job // get a read lock on the mutex to ensure we are not currently // syncing with the cluster c.mtx.RLock() j := c.jobs.Get(h.ID(), event.JobID) c.mtx.RUnlock() if j == nil { continue } j.startedAt = event.Job.StartedAt if event.Event != "error" && event.Event != "stop" { continue } g.Log(grohl.Data{"at": "remove", "job.id": event.JobID, "event": event.Event}) c.jobs.Remove(h.ID(), event.JobID) go func(event *host.Event) { c.mtx.RLock() j.Formation.RestartJob(jobType, h.ID(), event.JobID) c.mtx.RUnlock() }(event) } // TODO: check error/reconnect }
func (s *HTTPSuite) TestManualLeaderElection(c *C) { // service with auto election should not support manual leader err := s.client.Service("a").SetLeader("123") c.Assert(hh.IsValidationError(err), Equals, true) // create service with manual config err = s.client.AddService("b", &discoverd.ServiceConfig{LeaderType: discoverd.LeaderTypeManual}) c.Assert(err, IsNil) // register instance inst1 := fakeInstance() hb1, err := s.client.RegisterInstance("b", inst1) c.Assert(err, IsNil) defer hb1.Close() // no leader event when starting watch events := make(chan *discoverd.Event, 2) s.state.Subscribe("b", true, discoverd.EventKindUp|discoverd.EventKindDown|discoverd.EventKindLeader|discoverd.EventKindCurrent, events) assertEvent(c, events, "b", discoverd.EventKindUp, inst1) assertEvent(c, events, "b", discoverd.EventKindCurrent, nil) // get leader should 404 srv := s.client.Service("b") _, err = srv.Leader() c.Assert(discoverd.IsNotFound(err), Equals, true) // set leader err = srv.SetLeader(inst1.ID) c.Assert(err, IsNil) leader, err := srv.Leader() c.Assert(err, IsNil) assertInstanceEqual(c, leader, inst1) assertEvent(c, events, "b", discoverd.EventKindLeader, inst1) // add another instance and set to leader inst2 := fakeInstance() hb2, err := s.client.RegisterInstance("b", inst2) c.Assert(err, IsNil) defer hb2.Close() assertEvent(c, events, "b", discoverd.EventKindUp, inst2) err = srv.SetLeader(inst2.ID) c.Assert(err, IsNil) assertEvent(c, events, "b", discoverd.EventKindLeader, inst2) leader, err = srv.Leader() c.Assert(err, IsNil) assertInstanceEqual(c, leader, inst2) // remove leader instance hb2.Close() assertEvent(c, events, "b", discoverd.EventKindDown, inst2) // get leader should 404, no event _, err = srv.Leader() c.Assert(discoverd.IsNotFound(err), Equals, true) assertNoEvent(c, events) // set leader to instance 1 err = srv.SetLeader(inst1.ID) c.Assert(err, IsNil) assertEvent(c, events, "b", discoverd.EventKindLeader, inst1) leader, err = srv.Leader() c.Assert(err, IsNil) assertInstanceEqual(c, leader, inst1) }