// bindUnit handles the bind-service message, binding a unit to all service // instances bound to the app. func bindUnit(msg *queue.Message) error { a := App{Name: msg.Args[0]} err := a.Get() if err != nil { msg.Delete() return fmt.Errorf("Error handling %q: app %q does not exist.", msg.Action, a.Name) } conn, err := db.Conn() if err != nil { return fmt.Errorf("Error handling %q: %s", msg.Action, err) } defer conn.Close() units := getUnits(&a, msg.Args[1:]) if len(units) == 0 { msg.Delete() return errors.New("Unknown unit in the message.") } unit := units[0] var instances []service.ServiceInstance q := bson.M{"apps": bson.M{"$in": []string{msg.Args[0]}}} err = conn.ServiceInstances().Find(q).All(&instances) if err != nil { return err } for _, instance := range instances { _, err = instance.BindUnit(&a, &unit) if err != nil { log.Printf("Error binding the unit %s with the service instance %s.", unit.Name, instance.Name) } } return nil }
func handle(msg *queue.Message) { if msg.Action == addUnitToLoadBalancer { if len(msg.Args) < 1 { log.Printf("Failed to handle %q: it requires at least one argument.", msg.Action) msg.Delete() return } a := qApp{name: msg.Args[0]} unitNames := msg.Args[1:] sort.Strings(unitNames) status, err := (&JujuProvisioner{}).collectStatus() if err != nil { log.Printf("Failed to handle %q: juju status failed.\n%s.", msg.Action, err) return } var units []provision.Unit for _, u := range status { if u.AppName != a.name { continue } n := sort.SearchStrings(unitNames, u.Name) if len(unitNames) == 0 || n < len(unitNames) && unitNames[n] == u.Name { units = append(units, u) } } if len(units) == 0 { log.Printf("Failed to handle %q: units not found.", msg.Action) msg.Delete() return } var noId []string var ok []provision.Unit for _, u := range units { if u.InstanceId == "pending" || u.InstanceId == "" { noId = append(noId, u.Name) } else { ok = append(ok, u) } } if len(noId) == len(units) { getQueue(queueName).Release(msg, 0) } else { manager := ELBManager{} manager.Register(&a, ok...) msg.Delete() if len(noId) > 0 { args := []string{a.name} args = append(args, noId...) msg := queue.Message{ Action: msg.Action, Args: args, } getQueue(queueName).Put(&msg, 1e9) } } } else { msg.Delete() } }
// handle is the function called by the queue handler on each message. func handle(msg *queue.Message) { switch msg.Action { case RegenerateApprcAndStart: fallthrough case regenerateApprc: if len(msg.Args) < 1 { log.Printf("Error handling %q: this action requires at least 1 argument.", msg.Action) msg.Delete() return } app, err := ensureAppIsStarted(msg) if err != nil { log.Print(err) return } msg.Delete() app.SerializeEnvVars() fallthrough case startApp: if msg.Action == regenerateApprc { break } if len(msg.Args) < 1 { log.Printf("Error handling %q: this action requires at least 1 argument.", msg.Action) } app, err := ensureAppIsStarted(msg) if err != nil { log.Print(err) return } err = app.Restart(ioutil.Discard) if err != nil { log.Printf("Error handling %q. App failed to start:\n%s.", msg.Action, err) return } msg.Delete() case BindService: err := bindUnit(msg) if err != nil { log.Print(err) return } msg.Delete() default: log.Printf("Error handling %q: invalid action.", msg.Action) msg.Delete() } }
// ensureAppIsStarted make sure that the app and all units present in the given // message are started. func ensureAppIsStarted(msg *queue.Message) (App, error) { a := App{Name: msg.Args[0]} err := a.Get() if err != nil { msg.Delete() return a, fmt.Errorf("Error handling %q: app %q does not exist.", msg.Action, a.Name) } units := getUnits(&a, msg.Args[1:]) if len(msg.Args) > 1 && len(units) == 0 { msg.Delete() format := "Error handling %q for the app %q: unknown units in the message. Deleting it..." return a, fmt.Errorf(format, msg.Action, a.Name) } if !a.Available() || !units.Started() { format := "Error handling %q for the app %q:" uState := units.State() if uState == "error" || uState == "down" { format += fmt.Sprintf(" units are in %q state.", uState) msg.Delete() } else { format += " all units must be started." } return a, fmt.Errorf(format, msg.Action, a.Name) } return a, nil }
func (s *S) TestHandleMessageErrors(c *gocheck.C) { var data = []struct { action string args []string unitName string expectedLog string }{ { action: "unknown-action", args: []string{"does not matter"}, expectedLog: `Error handling "unknown-action": invalid action.`, }, { action: startApp, args: []string{"nemesis"}, expectedLog: `Error handling "start-app" for the app "nemesis":` + ` all units must be started.`, }, { action: startApp, args: []string{"totem", "totem/0", "totem/1"}, expectedLog: `Error handling "start-app" for the app "totem":` + ` all units must be started.`, }, { action: regenerateApprc, args: []string{"nemesis"}, expectedLog: `Error handling "regenerate-apprc" for the app "nemesis":` + ` all units must be started.`, }, { action: regenerateApprc, args: []string{"totem", "totem/0", "totem/1"}, expectedLog: `Error handling "regenerate-apprc" for the app "totem":` + ` all units must be started.`, }, { action: regenerateApprc, args: []string{"unknown-app"}, expectedLog: `Error handling "regenerate-apprc": app "unknown-app" does not exist.`, }, { action: regenerateApprc, expectedLog: `Error handling "regenerate-apprc": this action requires at least 1 argument.`, }, { action: regenerateApprc, args: []string{"marathon", "marathon/0"}, expectedLog: `Error handling "regenerate-apprc" for the app "marathon":` + ` units are in "error" state.`, }, { action: regenerateApprc, args: []string{"territories", "territories/0"}, expectedLog: `Error handling "regenerate-apprc" for the app "territories":` + ` units are in "down" state.`, }, } var buf bytes.Buffer a := App{Name: "nemesis"} err := s.conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer s.conn.Apps().Remove(bson.M{"name": a.Name}) a = App{ Name: "totem", Units: []Unit{ {Name: "totem/0", State: "pending"}, {Name: "totem/1", State: "started"}, }, } err = s.conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer s.conn.Apps().Remove(bson.M{"name": a.Name}) a = App{Name: "marathon", Units: []Unit{{Name: "marathon/0", State: "error"}}} err = s.conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer s.conn.Apps().Remove(bson.M{"name": a.Name}) a = App{Name: "territories", Units: []Unit{{Name: "territories/0", State: "down"}}} err = s.conn.Apps().Insert(a) c.Assert(err, gocheck.IsNil) defer s.conn.Apps().Remove(bson.M{"name": a.Name}) log.SetLogger(stdlog.New(&buf, "", 0)) for _, d := range data { message := queue.Message{Action: d.action} if len(d.args) > 0 { message.Args = d.args } handle(&message) defer message.Delete() // Sanity } content := buf.String() lines := strings.Split(content, "\n") for i, d := range data { var found bool for j := i; j < len(lines); j++ { if lines[j] == d.expectedLog { found = true break } } if !found { c.Errorf("\nWant: %q.\nGot:\n%s", d.expectedLog, content) } } }