Beispiel #1
0
// NameWatch watches for specific member names to join. It can be used
// to discover when a known group of members is present.
//
//     w := condition.NewNameWatch(client, "path", "to", "watch")
//     defer w.Stop()
//
// The path must be to an etcd directory, otherwise the watch will not
// function.
//
//     for {
//         select {
//         case <- w.WatchUntil("member1", "member2"):
//             ... 2 partners are present ...
//         case err := <- w.WatchError():
//             ... error code ...
//         }
//     }
//
// Or
//     for {
//         select {
//         case names := <- w.WatchNames():
//             ... names of partners currently present ...
//         case err := <- w.WatchError():
//             ... error code ...
//         }
//     }
//
// It is not correct to select from both WatchUntil and WatchCount at
// the same time.
func NewNameWatch(e *etcd.Client, path ...string) NameWatch {
	key := strings.Join(path, "/")
	stopc := make(chan bool)
	namesc := make(chan []string, 1)
	errorc := make(chan error, 1)
	go func() {
		defer e.Close()

		var res *etcd.Response
		var err error
		var exists bool
		for !exists {
			res, err = e.Get(key, false, false)
			if err != nil {
				switch err := err.(type) {
				case *etcd.EtcdError:
					if err.ErrorCode == 100 {
						ticker := time.NewTicker(5 * time.Second)
						select {
						case <-stopc:
							ticker.Stop()
							return
						case <-ticker.C:
							ticker.Stop()
						}
					}
				default:
					errorc <- err
					return
				}
			} else {
				exists = true
			}
		}

		index := uint64(0)
		if res != nil && res.Node != nil && res.Node.Dir {
			names := make([]string, 0, len(res.Node.Nodes))
			for _, n := range res.Node.Nodes {
				names = append(names, n.Key)
			}
			select {
			case namesc <- names:
			default:
			}
			index = res.EtcdIndex
		} else {
			select {
			case namesc <- nil:
			default:
			}
		}

		watch := make(chan *etcd.Response)
		go e.Watch(key, index, true, watch, stopc)

		for {
			select {
			case <-stopc:
				return
			case res, open := <-watch:
				if !open {
					select {
					case errorc <- fmt.Errorf("name watch closed unexpectedly for: %v", key):
					default:
					}
					return
				}
				res, err := e.Get(key, false, false)
				if err != nil {
					switch err := err.(type) {
					case *etcd.EtcdError:
						if err.ErrorCode == 100 {
							// Do nothing.
						}
					default:
						errorc <- err
						return
					}
				}
				if res != nil && res.Node != nil && res.Node.Dir {
					names := make([]string, 0, len(res.Node.Nodes))
					for _, n := range res.Node.Nodes {
						names = append(names, n.Key)
					}
					select {
					case namesc <- names:
					default:
					}
				} else {
					select {
					case namesc <- nil:
					default:
					}
				}
			}
		}
	}()
	return &namewatch{
		stopc:  stopc,
		namesc: namesc,
		errorc: errorc,
	}
}
Beispiel #2
0
// CountWatch watches for some number of joins. It can be used to discover
// when a known number of group memebers are present.
//
//     w := condition.NewCountWatch(client, "path", "to", "watch")
//     defer w.Stop()
//
// The path must be to an etcd directory, otherwise the watch will not
// function.
//
//     for {
//         select {
//         case <- w.WatchUntil(10):
//             ... 10 partners are present ...
//         case err := <- w.WatchError():
//             ... error code ...
//         }
//     }
//
// Or
//     for {
//         select {
//         case n := <- w.WatchCount():
//             ... n partners are present ...
//         case err := <- w.WatchError():
//             ... error code ...
//         }
//     }
//
// It is not correct to select from both WatchUntil and WatchCount at
// the same time.
func NewCountWatch(e *etcd.Client, path ...string) CountWatch {
	key := strings.Join(path, "/")
	stopc := make(chan bool)
	countc := make(chan int, 1)
	errorc := make(chan error, 1)
	go func() {
		defer e.Close()

		var res *etcd.Response
		var err error
		var exists bool
		for !exists {
			res, err = e.Get(key, false, false)
			if err != nil {
				switch err := err.(type) {
				case *etcd.EtcdError:
					if err.ErrorCode == 100 {
						ticker := time.NewTicker(5 * time.Second)
						select {
						case <-stopc:
							ticker.Stop()
							return
						case <-ticker.C:
							ticker.Stop()
						}
					}
				default:
					errorc <- err
					return
				}
			} else {
				exists = true
			}
		}

		index := uint64(0)
		if res != nil && res.Node != nil && res.Node.Dir {
			select {
			case countc <- len(res.Node.Nodes):
			default:
			}
			index = res.EtcdIndex
		} else {
			select {
			case countc <- 0:
			default:
			}
		}

		watch := make(chan *etcd.Response)
		go e.Watch(key, index, true, watch, stopc)

		for {
			select {
			case <-stopc:
				return
			case res, open := <-watch:
				if !open {
					select {
					case errorc <- fmt.Errorf("count watch closed unexpectedly: %v", key):
					default:
					}
					return
				}
				res, err := e.Get(key, false, false)
				if err != nil {
					switch err := err.(type) {
					case *etcd.EtcdError:
						if err.ErrorCode == 100 {
							// Do nothing.
						}
					default:
						errorc <- err
						return
					}
				}
				if res != nil && res.Node != nil && res.Node.Dir {
					select {
					case countc <- len(res.Node.Nodes):
					default:
					}
				} else {
					select {
					case countc <- 0:
					default:
					}
				}
			}
		}
	}()
	return &countwatch{
		stopc:  stopc,
		countc: countc,
		errorc: errorc,
	}
}