func SetService(service *core.Service) error { // in case of failure oldServices, err := database.GetServices() if err != nil { return err } // apply to balancer err = balance.SetService(service) if err != nil { return err } if !database.CentralStore { // save to backend err = database.SetService(service) if err != nil { // undo balancer action if uerr := balance.SetServices(oldServices); uerr != nil { err = fmt.Errorf("%v - %v", err.Error(), uerr.Error()) } return err } } return nil }
func TestGetServices(t *testing.T) { services, err := database.GetServices() if err != nil { t.Errorf("Failed to GET services - %v", err) } if services[0].Id != testService2.Id { t.Errorf("Read service differs from written service") } }
func TestNoneGetServices(t *testing.T) { // don't use cluster.GetServices() services, err := database.GetServices() if err != nil { t.Errorf("Failed to GET services - %v", err) t.FailNow() } if services[0].Id != testService2.Id { t.Errorf("Read service differs from written service") } }
// GetServices gets a list of services from the database, or another cluster member. func (r *Redis) GetServices() ([]core.Service, error) { if database.CentralStore { return database.GetServices() } conn := pool.Get() defer conn.Close() // get known members(other than me) to 'poll' for services members, _ := redis.Strings(conn.Do("SMEMBERS", "members")) if len(members) == 0 { // should only happen on new cluster // assume i'm ok to be master so don't reset imported services config.Log.Trace("[cluster] - Assuming OK to be master, using services from my database...") return common.GetServices() } for i := range members { if members[i] == self { // if i'm in the list of members, new requests should have failed while `waitForMembers`ing config.Log.Trace("[cluster] - Assuming I was in sync, using services from my database...") return common.GetServices() } } c, err := redis.DialURL(config.ClusterConnection, redis.DialConnectTimeout(15*time.Second), redis.DialPassword(config.ClusterToken)) if err != nil { return nil, fmt.Errorf("Failed to reach redis for services subscriber - %v", err) } defer c.Close() message := make(chan interface{}) subconn := redis.PubSubConn{c} // subscribe to channel that services will be published on if err := subconn.Subscribe("services"); err != nil { return nil, fmt.Errorf("Failed to reach redis for services subscriber - %v", err) } defer subconn.Close() // listen always go func() { for { message <- subconn.Receive() } }() // todo: maybe use ttl? // timeout is how long to wait for the listed members to come back online timeout := time.After(time.Duration(20) * time.Second) // loop attempts for timeout, allows last dead members to start back up for { select { case <-timeout: return nil, fmt.Errorf("Timed out waiting for services from %v", strings.Join(members, ", ")) default: // request services from each member until successful for _, member := range members { // memberTimeout is how long to wait for a member to respond with list of services memberTimeout := time.After(3 * time.Second) // ask a member for its services config.Log.Trace("[cluster] - Attempting to request services from %v...", member) _, err := conn.Do("PUBLISH", "portal", fmt.Sprintf("get-services %s", member)) if err != nil { return nil, err } // wait for member to respond for { select { case <-memberTimeout: config.Log.Debug("[cluster] - Timed out waiting for services from %v", member) goto nextMember case msg := <-message: switch v := msg.(type) { case redis.Message: config.Log.Trace("[cluster] - Received message on 'services' channel") services, err := marshalSvcs(v.Data) if err != nil { return nil, fmt.Errorf("Failed to marshal services - %v", err.Error()) } config.Log.Trace("[cluster] - Services from cluster: %#v\n", *services) return *services, nil case error: return nil, fmt.Errorf("Subscriber failed to receive services - %v", v.Error()) } } } nextMember: } } } }
func GetServices() ([]core.Service, error) { return database.GetServices() }