blob: 58a98224100740d80ffe6177be1d447b86eee7b6 [file] [log] [blame]
package client
// This file attaches metadata to the context of RPC calls.
import (
"context"
log "github.com/golang/glog"
"github.com/golang/protobuf/proto"
"github.com/pborman/uuid"
"google.golang.org/grpc/metadata"
repb "github.com/bazelbuild/remote-apis/build/bazel/remote/execution/v2"
)
const (
// The headers key of our RequestMetadata.
remoteHeadersKey = "build.bazel.remote.execution.v2.requestmetadata-bin"
)
// LogContextInfof(ctx, x, ...) is equivalent to log.V(x).Infof(...) except it
// also logs context metadata, if available.
func LogContextInfof(ctx context.Context, v log.Level, format string, args ...interface{}) {
if log.V(v) {
_, actionID, _, _ := GetContextMetadata(ctx)
if actionID != "" {
format = "%s: " + format
args = append([]interface{}{actionID}, args...)
}
log.V(v).Infof(format, args...)
}
}
// GetContextMetadata parses the metadata from the given context, if it exists.
// If metadata does not exist, empty values are returned.
func GetContextMetadata(ctx context.Context) (toolName, actionID, invocationID string, err error) {
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
return "", "", "", nil
}
vs := md.Get(remoteHeadersKey)
if len(vs) == 0 {
return "", "", "", nil
}
buf := []byte(vs[0])
meta := &repb.RequestMetadata{}
if err := proto.Unmarshal(buf, meta); err != nil {
return "", "", "", err
}
return meta.ToolDetails.GetToolName(), meta.ActionId, meta.ToolInvocationId, nil
}
// ContextWithMetadata attaches metadata to the passed-in context, returning a new
// context. This function should be called in every test method after a context is created. It uses
// the already created context to generate a new one containing the metadata header.
func ContextWithMetadata(ctx context.Context, toolName, actionID, invocationID string) (context.Context, error) {
if actionID == "" {
actionID = uuid.New()
log.V(2).Infof("Generated action_id %s for %s", actionID, toolName)
}
if invocationID == "" {
invocationID = uuid.New()
log.V(2).Infof("Generated invocation_id %s for %s %s", invocationID, toolName, actionID)
}
meta := &repb.RequestMetadata{
ActionId: actionID,
ToolInvocationId: invocationID,
ToolDetails: &repb.ToolDetails{ToolName: toolName},
}
// Marshal the proto to a binary buffer
buf, err := proto.Marshal(meta)
if err != nil {
return nil, err
}
// metadata package converts the binary buffer to a base64 string, so no need to encode before
// sending.
mdPair := metadata.Pairs(remoteHeadersKey, string(buf))
return metadata.NewOutgoingContext(ctx, mdPair), nil
}