func (l *labelWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { md, _ := metadata.FromContext(ctx) labels := make(map[string]string) for k, v := range md { if strings.HasPrefix(k, LabelPrefix) { key := strings.TrimPrefix(k, LabelPrefix) key = strings.ToLower(key) labels[key] = strings.ToLower(v) } } filter := func(services []*registry.Service) []*registry.Service { var filtered []*registry.Service SERVICE: for _, service := range services { // check labels for the service for lk, lv := range labels { // if we match then append the service and return if v := service.Metadata[lk]; v == lv { filtered = append(filtered, service) continue SERVICE } } var nodes []*registry.Node NODE: for _, node := range service.Nodes { // check labels against the node for lk, lv := range labels { // if it matches then append node if v := node.Metadata[lk]; v == lv { nodes = append(nodes, node) continue NODE } } } if len(nodes) > 0 { service.Nodes = nodes filtered = append(filtered, service) } } return filtered } callOptions := append(opts, client.WithSelectOption( selector.Filter(filter), )) return l.Client.Call(ctx, req, rsp, callOptions...) }
// getSegment creates a new segment based on whether we're part of an existing flow func getSegment(name string, ctx context.Context) *awsxray.Segment { md, _ := metadata.FromContext(ctx) parentId := getParentId(md) traceId := getTraceId(md) // try get existing segment for parent Id if s, ok := awsxray.FromContext(ctx); ok { // only set existing segment as parent if its not a subsegment itself if len(parentId) == 0 && len(s.Type) == 0 { parentId = s.Id } if len(traceId) == 0 { traceId = s.TraceId } } // create segment s := &awsxray.Segment{ Id: fmt.Sprintf("%x", getRandom(8)), Name: name, TraceId: traceId, StartTime: float64(time.Now().Truncate(time.Millisecond).UnixNano()) / 1e9, } // we have a parent so subsegment if len(parentId) > 0 { s.ParentId = parentId s.Type = "subsegment" } return s }
// Nearby returns all hotels within a given distance. func (s *Geo) Nearby(ctx context.Context, req *geo.Request, rsp *geo.Result) error { md, _ := metadata.FromContext(ctx) traceID := md["traceID"] if tr, ok := trace.FromContext(ctx); ok { tr.LazyPrintf("traceID %s", traceID) } // create center point for query center := &geoindex.GeoPoint{ Pid: "", Plat: float64(req.Lat), Plon: float64(req.Lon), } // find points around center point points := s.index.KNearest(center, maxSearchResults, geoindex.Km(maxSearchRadius), func(p geoindex.Point) bool { return true }) for _, p := range points { rsp.HotelIds = append(rsp.HotelIds, p.Id()) } return nil }
func (g *grpcClient) Publish(ctx context.Context, p client.Publication, opts ...client.PublishOption) error { md, ok := metadata.FromContext(ctx) if !ok { md = make(map[string]string) } md["Content-Type"] = p.ContentType() cf, err := g.newCodec(p.ContentType()) if err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } b := &buffer{bytes.NewBuffer(nil)} if err := cf(b).Write(&codec.Message{Type: codec.Publication}, p.Message()); err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } g.once.Do(func() { g.opts.Broker.Connect() }) return g.opts.Broker.Publish(p.Topic(), &broker.Message{ Header: md, Body: b.Bytes(), }) }
func TestRequestToContext(t *testing.T) { testData := []struct { request *http.Request expect metadata.Metadata }{ { &http.Request{ Header: http.Header{ "foo1": []string{"bar"}, "foo2": []string{"bar", "baz"}, }, }, metadata.Metadata{ "foo1": "bar", "foo2": "bar,baz", }, }, } for _, d := range testData { ctx := RequestToContext(d.request) md, ok := metadata.FromContext(ctx) if !ok { t.Fatalf("Expected metadata for request %+v", d.request) } for k, v := range d.expect { if val := md[k]; val != v { t.Fatal("Expected %s for key %s for expected md %+v, got md %+v", v, k, d.expect, md) } } } }
func (p *platform) Introspect(ctx context.Context) (*Token, error) { t, ok := p.FromContext(ctx) if !ok { md, kk := metadata.FromContext(ctx) if !kk { return nil, ErrInvalidToken } t, ok = p.FromHeader(md) if !ok { return nil, ErrInvalidToken } } rsp, err := p.c.Introspect(context.TODO(), &oauth2.IntrospectRequest{ AccessToken: t.AccessToken, }) if err != nil { return nil, err } // if its not active just err? if !rsp.Active { return nil, ErrInvalidToken } return &Token{ AccessToken: rsp.Token.AccessToken, RefreshToken: rsp.Token.RefreshToken, TokenType: rsp.Token.TokenType, ExpiresAt: time.Unix(rsp.Token.ExpiresAt, 0), Scopes: rsp.Token.Scopes, Metadata: rsp.Token.Metadata, }, nil }
func handlerWrapper(fn server.HandlerFunc, t Trace, s *registry.Service) server.HandlerFunc { return func(ctx context.Context, req server.Request, rsp interface{}) error { // embed trace instance newCtx := NewContext(ctx, t) var span *Span var err error // Expectation is that we're the initiator of tracing // So get trace info from metadata md, ok := metadata.FromContext(ctx) if !ok { // this is a new span span = t.NewSpan(nil) span.Debug = true } else { // can we gt the span from the header? span, ok = t.FromHeader(md) if !ok { // no, ok create one span = t.NewSpan(nil) } span.Timestamp = time.Time{} span.Debug = true } // mark client request span.Annotations = append(span.Annotations, &Annotation{ Timestamp: time.Now(), Type: AnnServerRequest, Service: s, }) // embed the span in the context newCtx = t.NewContext(newCtx, span) // defer the completion of the span defer func() { var debug map[string]string if err != nil { debug = map[string]string{"error": err.Error()} } // mark server response span.Annotations = append(span.Annotations, &Annotation{ Timestamp: time.Now(), Type: AnnServerResponse, Service: s, Debug: debug, }) // flush the span to the collector on return t.Collect(span) }() err = fn(newCtx, req, rsp) return err } }
func (r *rpcClient) stream(ctx context.Context, address string, req Request, opts CallOptions) (Streamer, error) { msg := &transport.Message{ Header: make(map[string]string), } md, ok := metadata.FromContext(ctx) if ok { for k, v := range md { msg.Header[k] = v } } // set timeout in nanoseconds msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) // set the content type for the request msg.Header["Content-Type"] = req.ContentType() cf, err := r.newCodec(req.ContentType()) if err != nil { return nil, errors.InternalServerError("go.micro.client", err.Error()) } c, err := r.opts.Transport.Dial(address, transport.WithStream(), transport.WithTimeout(opts.DialTimeout)) if err != nil { return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) } stream := &rpcStream{ context: ctx, request: req, closed: make(chan bool), codec: newRpcPlusCodec(msg, c, cf), } ch := make(chan error, 1) go func() { ch <- stream.Send(req.Request()) }() var grr error select { case err := <-ch: grr = err case <-ctx.Done(): grr = errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) } if grr != nil { stream.Close() return nil, grr } return stream, nil }
// GetProfiles returns hotel profiles for requested IDs func (s *Profile) GetProfiles(ctx context.Context, req *profile.Request, rsp *profile.Result) error { md, _ := metadata.FromContext(ctx) traceID := md["traceID"] if tr, ok := trace.FromContext(ctx); ok { tr.LazyPrintf("traceID %s", traceID) } for _, i := range req.HotelIds { rsp.Hotels = append(rsp.Hotels, s.hotels[i]) } return nil }
func (c *clientWrapper) setHeaders(ctx context.Context) context.Context { md, ok := metadata.FromContext(ctx) if !ok { md = metadata.Metadata{} } for k, v := range c.headers { if _, ok := md[k]; !ok { md[k] = v } } return metadata.NewContext(ctx, md) }
// VerifyToken returns a customer from authentication token. func (s *Auth) VerifyToken(ctx context.Context, req *auth.Request, rsp *auth.Result) error { md, _ := metadata.FromContext(ctx) traceID := md["traceID"] if tr, ok := trace.FromContext(ctx); ok { tr.LazyPrintf("traceID %s", traceID) } customer := s.customers[req.AuthToken] if customer == nil { return errors.New("Invalid Token") } rsp.Customer = customer return nil }
func (g *grpcClient) stream(ctx context.Context, address string, req client.Request, opts client.CallOptions) (client.Streamer, error) { header := make(map[string]string) if md, ok := metadata.FromContext(ctx); ok { for k, v := range md { header[k] = v } } // set timeout in nanoseconds header["timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) // set the content type for the request header["x-content-type"] = req.ContentType() md := gmetadata.New(header) ctx = gmetadata.NewContext(ctx, md) cf, err := g.newGRPCCodec(req.ContentType()) if err != nil { return nil, errors.InternalServerError("go.micro.client", err.Error()) } // TODO: do not use insecure cc, err := grpc.Dial(address, grpc.WithCodec(cf), grpc.WithTimeout(opts.DialTimeout), grpc.WithInsecure()) if err != nil { return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) } desc := &grpc.StreamDesc{ StreamName: req.Service() + req.Method(), ClientStreams: true, ServerStreams: true, } st, err := grpc.NewClientStream(ctx, desc, cc, req.Method()) if err != nil { return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error creating stream: %v", err)) } return &grpcStream{ context: ctx, request: req, closed: make(chan bool), stream: st, conn: cc, }, nil }
func (t *TestHandler) Exec(ctx context.Context, req *TestRequest, rsp *TestResponse) error { md, ok := metadata.FromContext(ctx) if !ok { return fmt.Errorf("Expected metadata got %t", ok) } for k, v := range t.expect { if val := md[k]; val != v { return fmt.Errorf("Expected %s for key %s got %s", v, k, val) } } t.t.Logf("Received request %+v", req) t.t.Logf("Received metadata %+v", md) return nil }
func (o *otWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { md, _ := metadata.FromContext(ctx) name := fmt.Sprintf("%s.%s", req.Service(), req.Method()) var sp opentracing.Span wireContext, err := o.ot.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md)) if err != nil { sp = o.ot.StartSpan(name) } else { sp = o.ot.StartSpan(name, opentracing.ChildOf(wireContext)) } defer sp.Finish() if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.TextMapCarrier(md)); err != nil { return err } ctx = metadata.NewContext(ctx, md) return o.Client.Call(ctx, req, rsp, opts...) }
func TestWrapper(t *testing.T) { testData := []struct { existing metadata.Metadata headers metadata.Metadata overwrite bool }{ { existing: metadata.Metadata{}, headers: metadata.Metadata{ "foo": "bar", }, overwrite: true, }, { existing: metadata.Metadata{ "foo": "bar", }, headers: metadata.Metadata{ "foo": "baz", }, overwrite: false, }, } for _, d := range testData { c := &clientWrapper{ headers: d.headers, } ctx := metadata.NewContext(context.Background(), d.existing) c.setHeaders(ctx) md, _ := metadata.FromContext(ctx) for k, v := range d.headers { if d.overwrite && md[k] != v { t.Fatalf("Expected %s=%s got %s=%s", k, v, k, md[k]) } if !d.overwrite && md[k] != d.existing[k] { t.Fatalf("Expected %s=%s got %s=%s", k, d.existing[k], k, md[k]) } } } }
func (g *grpcClient) call(ctx context.Context, address string, req client.Request, rsp interface{}, opts client.CallOptions) error { header := make(map[string]string) if md, ok := metadata.FromContext(ctx); ok { for k, v := range md { header[k] = v } } // set timeout in nanoseconds header["timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) // set the content type for the request header["x-content-type"] = req.ContentType() md := gmetadata.New(header) ctx = gmetadata.NewContext(ctx, md) cf, err := g.newGRPCCodec(req.ContentType()) if err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } var grr error // TODO: do not use insecure cc, err := grpc.Dial(address, grpc.WithCodec(cf), grpc.WithTimeout(opts.DialTimeout), grpc.WithInsecure()) if err != nil { return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) } defer cc.Close() ch := make(chan error, 1) go func() { ch <- grpc.Invoke(ctx, req.Method(), req.Request(), rsp, cc) }() select { case err := <-ch: grr = err case <-ctx.Done(): grr = ctx.Err() } return grr }
// GetRates gets rates for hotels for specific date range. func (s *Rate) GetRates(ctx context.Context, req *rate.Request, rsp *rate.Result) error { md, _ := metadata.FromContext(ctx) traceID := md["traceID"] if tr, ok := trace.FromContext(ctx); ok { tr.LazyPrintf("traceID %s", traceID) } for _, hotelID := range req.HotelIds { stay := stay{ HotelID: hotelID, InDate: req.InDate, OutDate: req.OutDate, } if s.rateTable[stay] != nil { rsp.RatePlans = append(rsp.RatePlans, s.rateTable[stay]) } } return nil }
func newContext(ctx context.Context, s *awsxray.Segment) context.Context { md, _ := metadata.FromContext(ctx) // make copy to avoid races newMd := metadata.Metadata{} for k, v := range md { newMd[k] = v } // set trace id in header newMd[awsxray.TraceHeader] = awsxray.SetTraceId(newMd[awsxray.TraceHeader], s.TraceId) // set parent id in header newMd[awsxray.TraceHeader] = awsxray.SetParentId(newMd[awsxray.TraceHeader], s.ParentId) // store segment in context ctx = awsxray.NewContext(ctx, s) // store metadata in context ctx = metadata.NewContext(ctx, newMd) return ctx }
// NewHandlerWrapper accepts an opentracing Tracer and returns a Handler Wrapper func NewHandlerWrapper(ot opentracing.Tracer) server.HandlerWrapper { return func(h server.HandlerFunc) server.HandlerFunc { return func(ctx context.Context, req server.Request, rsp interface{}) error { md, _ := metadata.FromContext(ctx) name := fmt.Sprintf("%s.%s", req.Service(), req.Method()) var sp opentracing.Span wireContext, err := ot.Extract(opentracing.TextMap, opentracing.TextMapCarrier(md)) if err != nil { sp = ot.StartSpan(name) } else { sp = ot.StartSpan(name, opentracing.ChildOf(wireContext)) } defer sp.Finish() if err := sp.Tracer().Inject(sp.Context(), opentracing.TextMap, opentracing.TextMapCarrier(md)); err != nil { return err } ctx = metadata.NewContext(ctx, md) return h(ctx, req, rsp) } } }
func (c *clientWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { // retrieve token if one exists t, err := c.a.Introspect(ctx) if err != nil { // no? ok let's try make the call as ourself t, err = c.a.Token() if err != nil { return err } } // create new context with token newCtx := ContextWithToken(ctx, t) // get metadata md, ok := metadata.FromContext(newCtx) if !ok { md = metadata.Metadata{} } // set auth headers for k, v := range HeaderWithToken(map[string]string{}, t) { md[k] = v } // set metadata newCtx = metadata.NewContext(newCtx, md) // circuit break, check authorization here t, err = c.a.Authorized(newCtx, req) if err != nil { return ErrInvalidToken } // now just make a regular call down the stack err = c.Client.Call(newCtx, req, rsp, opts...) return err }
func (dc *dcWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { md, _ := metadata.FromContext(ctx) filter := func(services []*registry.Service) []*registry.Service { for _, service := range services { var nodes []*registry.Node for _, node := range service.Nodes { if node.Metadata["datacenter"] == md["datacenter"] { nodes = append(nodes, node) } } service.Nodes = nodes } return services } callOptions := append(opts, client.WithSelectOption( selector.WithFilter(filter), )) fmt.Printf("[DC Wrapper] filtering for datacenter %s\n", md["datacenter"]) return dc.Client.Call(ctx, req, rsp, callOptions...) }
func (s *Hotel) Rates(ctx context.Context, req *hotel.Request, rsp *hotel.Response) error { // tracing tr := trace.New("api.v1", "Hotel.Rates") defer tr.Finish() // context ctx = trace.NewContext(ctx, tr) md, ok := metadata.FromContext(ctx) if !ok { md = metadata.Metadata{} } // add a unique request id to context if traceID, err := uuid.NewV4(); err == nil { // make copy tmd := metadata.Metadata{} for k, v := range md { tmd[k] = v } tmd["traceID"] = traceID.String() tmd["fromName"] = "api.v1" ctx = metadata.NewContext(ctx, tmd) } // token from request headers token, err := getToken(md) if err != nil { return merr.Forbidden("api.hotel.rates", err.Error()) } // verify token w/ auth service authClient := auth.NewAuthClient("go.micro.srv.auth", s.Client) if _, err = authClient.VerifyToken(ctx, &auth.Request{AuthToken: token}); err != nil { return merr.Unauthorized("api.hotel.rates", "Unauthorized") } // checkin and checkout date query params inDate, outDate := req.InDate, req.OutDate if inDate == "" || outDate == "" { return merr.BadRequest("api.hotel.rates", "Please specify inDate/outDate params") } // finds nearby hotels // TODO(hw): use lat/lon from request params geoClient := geo.NewGeoClient("go.micro.srv.geo", s.Client) nearby, err := geoClient.Nearby(ctx, &geo.Request{ Lat: 51.502973, Lon: -0.114723, }) if err != nil { return merr.InternalServerError("api.hotel.rates", err.Error()) } // make reqeusts for profiles and rates profileCh := getHotelProfiles(s.Client, ctx, nearby.HotelIds) rateCh := getRatePlans(s.Client, ctx, nearby.HotelIds, inDate, outDate) // wait on profiles reply profileReply := <-profileCh if err := profileReply.err; err != nil { return merr.InternalServerError("api.hotel.rates", err.Error()) } // wait on rates reply rateReply := <-rateCh if err := rateReply.err; err != nil { return merr.InternalServerError("api.hotel.rates", err.Error()) } rsp.Hotels = profileReply.hotels rsp.RatePlans = rateReply.ratePlans return nil }
func (c *clientWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { var span *Span var ok, okk bool var err error md := metadata.Metadata{} // try pull span from context span, ok = c.t.FromContext(ctx) if !ok { // couldn't do that, try from the metadata // So get trace info from metadata var kk bool md, kk = metadata.FromContext(ctx) if !kk { // couldn't do that either // so this is a new span! md = metadata.Metadata{} span = c.t.NewSpan(nil) } else { // ok we got some md // can we get the span from the header? span, okk = c.t.FromHeader(md) if !okk { // no, ok create one! span = c.t.NewSpan(nil) } } } // got parent span from context or metadata if okk || ok { // setup the span with parent span = c.t.NewSpan(&Span{ // same trace id TraceId: span.TraceId, // set parent id to parent span id ParentId: span.Id, // use previous debug Debug: span.Debug, }) } // start the span span.Annotations = append(span.Annotations, &Annotation{ Timestamp: time.Now(), Type: AnnStart, Service: c.s, }) // and mark as debug? might want to do this based on a setting span.Debug = true // set uniq span name span.Name = req.Service() + "." + req.Method() // set source/dest span.Source = c.s span.Destination = ®istry.Service{Name: req.Service()} // set context key newCtx := c.t.NewContext(ctx, span) // set metadata newCtx = metadata.NewContext(newCtx, c.t.NewHeader(md, span)) // mark client request span.Annotations = append(span.Annotations, &Annotation{ Timestamp: time.Now(), Type: AnnClientRequest, Service: c.s, }) // defer the completion of the span defer func() { // mark client response span.Annotations = append(span.Annotations, &Annotation{ Timestamp: time.Now(), Type: AnnClientResponse, Service: c.s, }) // if we were the creator var debug map[string]string if err != nil { debug = map[string]string{"error": err.Error()} } // mark end of span span.Annotations = append(span.Annotations, &Annotation{ Timestamp: time.Now(), Type: AnnEnd, Service: c.s, Debug: debug, }) span.Duration = time.Now().Sub(span.Timestamp) // flush the span to the collector on return c.t.Collect(span) }() // now just make a regular call down the stack err = c.Client.Call(newCtx, req, rsp, opts...) return err }
func handlerWrapper(fn server.HandlerFunc, t Trace, s *registry.Service) server.HandlerFunc { return func(ctx context.Context, req server.Request, rsp interface{}) error { // embed trace instance newCtx := NewContext(ctx, t) var span *Span var err error // get trace info from metadata md, ok := metadata.FromContext(ctx) if !ok { // this is a new span span = t.NewSpan(nil) } else { // can we gt the span from the header? span, ok = t.FromHeader(md) if !ok { // no, ok create one span = t.NewSpan(nil) } } // mark client request span.Annotations = append(span.Annotations, &Annotation{ Timestamp: time.Now(), Type: AnnServerRequest, Service: s, }) // and mark as debug? might want to do this based on a setting span.Debug = true // set unique span name span.Name = req.Service() + "." + req.Method() // set source/dest span.Source = s span.Destination = ®istry.Service{Name: req.Service()} // embed the span in the context newCtx = t.NewContext(newCtx, span) // defer the completion of the span defer func() { var debug map[string]string if err != nil { debug = map[string]string{"error": err.Error()} } // mark server response span.Annotations = append(span.Annotations, &Annotation{ Timestamp: time.Now(), Type: AnnServerResponse, Service: s, Debug: debug, }) span.Duration = time.Now().Sub(span.Timestamp) // flush the span to the collector on return t.Collect(span) }() err = fn(newCtx, req, rsp) return err } }
func (e *Example) Call(ctx context.Context, req *example.Request, rsp *example.Response) error { md, _ := metadata.FromContext(ctx) log.Printf("Received Example.Call request with metadata: %v", md) rsp.Msg = server.DefaultOptions().Id + ": Hello " + req.Name return nil }
func (l *logWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { md, _ := metadata.FromContext(ctx) fmt.Printf("[Log Wrapper] ctx: %v service: %s method: %s\n", md, req.Service(), req.Method()) return l.Client.Call(ctx, req, rsp) }
func (r *rpcClient) call(ctx context.Context, address string, req Request, resp interface{}, opts CallOptions) error { msg := &transport.Message{ Header: make(map[string]string), } md, ok := metadata.FromContext(ctx) if ok { for k, v := range md { msg.Header[k] = v } } // set timeout in nanoseconds msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) // set the content type for the request msg.Header["Content-Type"] = req.ContentType() cf, err := r.newCodec(req.ContentType()) if err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } var grr error c, err := r.pool.getConn(address, r.opts.Transport, transport.WithTimeout(opts.DialTimeout)) if err != nil { return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) } defer func() { // defer execution of release r.pool.release(address, c, grr) }() stream := &rpcStream{ context: ctx, request: req, closed: make(chan bool), codec: newRpcPlusCodec(msg, c, cf), } defer stream.Close() ch := make(chan error, 1) go func() { defer func() { if r := recover(); r != nil { ch <- errors.InternalServerError("go.micro.client", "request error") } }() // send request if err := stream.Send(req.Request()); err != nil { ch <- err return } // recv request if err := stream.Recv(resp); err != nil { ch <- err return } // success ch <- nil }() select { case err := <-ch: grr = err return err case <-ctx.Done(): grr = ctx.Err() return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) } }