func watchStatus(client client.Client, logger logging.Logger) { key, err := types.ToPodUniqueKey(*podUniqueKey) if err != nil { logger.Fatalf("Could not parse passed pod unique key %q as uuid: %s", *podUniqueKey, err) } ctx, cancelFunc := context.WithCancel(context.Background()) defer cancelFunc() outCh, err := client.WatchStatus(ctx, key, 1) // 1 so we wait for the key to exist if err != nil { logger.Fatal(err) } for i := 0; i < *numIterations; i++ { val, ok := <-outCh if !ok { logger.Fatal("Channel closed unexpectedly") } if val.Error != nil { logger.Fatal(val.Error) } bytes, err := json.Marshal(val) if err != nil { logger.Fatal(err) } fmt.Println(string(bytes)) } }
// Deduces a PodUniqueKey from a consul path. This is useful as pod keys are transitioned // from using node name and pod ID to using UUIDs. // Input is expected to have 3 '/' separated sections, e.g. 'intent/<node>/<pod_id>' or // 'intent/<node>/<pod_uuid>' if the prefix is "intent" or "reality" // // /hooks is also a valid pod prefix and the key under it will not be a uuid. func PodUniqueKeyFromConsulPath(consulPath string) (types.PodUniqueKey, error) { keyParts := strings.Split(consulPath, "/") if len(keyParts) == 0 { return "", util.Errorf("Malformed key '%s'", consulPath) } if keyParts[0] == "hooks" { return "", nil } if len(keyParts) != 3 { return "", util.Errorf("Malformed key '%s'", consulPath) } // Unforunately we can't use kp.INTENT_TREE and kp.REALITY_TREE here because of an import cycle if keyParts[0] != "intent" && keyParts[0] != "reality" { return "", util.Errorf("Unrecognized key tree '%s' (must be intent or reality)", keyParts[0]) } // Parse() returns nil if the input string does not match the uuid spec podUniqueKey, err := types.ToPodUniqueKey(keyParts[2]) switch { case err == types.InvalidUUID: // this is okay, it's just a legacy pod podUniqueKey = "" case err != nil: return "", util.Errorf("Could not test whether %s is a valid pod unique key: %s", keyParts[2], err) } return podUniqueKey, nil }
func (s store) WatchPodStatus(req *podstore_protos.WatchPodStatusRequest, stream podstore_protos.P2PodStore_WatchPodStatusServer) error { if req.StatusNamespace != kp.PreparerPodStatusNamespace.String() { // Today this is the only namespace so we just make sure it doesn't diverge from expected return grpc.Errorf(codes.InvalidArgument, "%q is not an understood namespace, must be %q", req.StatusNamespace, kp.PreparerPodStatusNamespace) } podUniqueKey, err := types.ToPodUniqueKey(req.PodUniqueKey) if err == types.InvalidUUID { return grpc.Errorf(codes.InvalidArgument, "%q does not parse as pod unique key (uuid)", req.PodUniqueKey) } else if err != nil { return grpc.Errorf(codes.Unavailable, err.Error()) } clientCancel := stream.Context().Done() podStatusResultCh := make(chan podStatusResult) innerQuit := make(chan struct{}) defer close(podStatusResultCh) waitIndex := req.WaitIndex go func() { for { status, queryMeta, err := s.podStatusStore.WaitForStatus(podUniqueKey, waitIndex) select { case podStatusResultCh <- podStatusResult{ status: status, queryMeta: queryMeta, err: err, }: if err != nil { return } if queryMeta != nil { waitIndex = queryMeta.LastIndex } case <-innerQuit: // Client canceled return } } }() for { select { case <-clientCancel: close(innerQuit) return nil case result := <-podStatusResultCh: resp, err := podStatusResultToResp(result) if err != nil { return err } err = stream.Send(resp) if err != nil { return err } } } }