@@ -7,6 +7,8 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
@@ -19,6 +21,13 @@ const grpcMetaKeyCollection = "x-gf-batch-collection"
|
||||
const grpcMetaKeyRebuildCollection = "x-gf-batch-rebuild-collection"
|
||||
const grpcMetaKeySkipValidation = "x-gf-batch-skip-validation"
|
||||
|
||||
// Logged in trace.
|
||||
var metadataKeys = []string{
|
||||
grpcMetaKeyCollection,
|
||||
grpcMetaKeyRebuildCollection,
|
||||
grpcMetaKeySkipValidation,
|
||||
}
|
||||
|
||||
func grpcMetaValueIsTrue(vals []string) bool {
|
||||
return len(vals) == 1 && vals[0] == "true"
|
||||
}
|
||||
@@ -100,9 +109,17 @@ func NewBulkSettings(md metadata.MD) (BulkSettings, error) {
|
||||
// All requests must be to the same NAMESPACE/GROUP/RESOURCE
|
||||
func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) error {
|
||||
ctx := stream.Context()
|
||||
ctx, span := s.tracer.Start(ctx, "resource.server.BulkProcess")
|
||||
defer span.End()
|
||||
|
||||
sendAndClose := func(rsp *resourcepb.BulkResponse) error {
|
||||
span.AddEvent("sendAndClose", trace.WithAttributes(attribute.String("msg", rsp.String())))
|
||||
return stream.SendAndClose(rsp)
|
||||
}
|
||||
|
||||
user, ok := authlib.AuthInfoFrom(ctx)
|
||||
if !ok || user == nil {
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
return sendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "no user found in context",
|
||||
Code: http.StatusUnauthorized,
|
||||
@@ -112,20 +129,30 @@ func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) erro
|
||||
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
if !ok {
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
return sendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "unable to read metadata gRPC request",
|
||||
Code: http.StatusPreconditionFailed,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Add relevant metadata into span.
|
||||
for _, k := range metadataKeys {
|
||||
meta := md.Get(k)
|
||||
if len(meta) > 0 {
|
||||
span.SetAttributes(attribute.StringSlice(k, meta))
|
||||
}
|
||||
}
|
||||
|
||||
runner := &batchRunner{
|
||||
checker: make(map[string]authlib.ItemChecker), // Can create
|
||||
stream: stream,
|
||||
span: span,
|
||||
}
|
||||
settings, err := NewBulkSettings(md)
|
||||
if err != nil {
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
return sendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "error reading settings",
|
||||
Reason: err.Error(),
|
||||
@@ -135,7 +162,7 @@ func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) erro
|
||||
}
|
||||
|
||||
if len(settings.Collection) < 1 {
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
return sendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "Missing target collection(s) in request header",
|
||||
Code: http.StatusBadRequest,
|
||||
@@ -153,7 +180,7 @@ func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) erro
|
||||
Verb: utils.VerbDeleteCollection,
|
||||
})
|
||||
if err != nil || !rsp.Allowed {
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
return sendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: fmt.Sprintf("Requester must be able to: %s", utils.VerbDeleteCollection),
|
||||
Code: http.StatusForbidden,
|
||||
@@ -169,7 +196,7 @@ func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) erro
|
||||
Verb: utils.VerbCreate,
|
||||
})
|
||||
if err != nil {
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
return sendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "Unable to check `create` permission",
|
||||
Code: http.StatusForbidden,
|
||||
@@ -178,7 +205,7 @@ func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) erro
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
return sendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "Bulk currently only supports RebuildCollection",
|
||||
Code: http.StatusBadRequest,
|
||||
@@ -188,7 +215,7 @@ func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) erro
|
||||
|
||||
backend, ok := s.backend.(BulkProcessingBackend)
|
||||
if !ok {
|
||||
return stream.SendAndClose(&resourcepb.BulkResponse{
|
||||
return sendAndClose(&resourcepb.BulkResponse{
|
||||
Error: &resourcepb.ErrorResult{
|
||||
Message: "The server backend does not support batch processing",
|
||||
Code: http.StatusNotImplemented,
|
||||
@@ -228,7 +255,7 @@ func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) erro
|
||||
}
|
||||
}
|
||||
}
|
||||
return stream.SendAndClose(rsp)
|
||||
return sendAndClose(rsp)
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -241,6 +268,7 @@ type batchRunner struct {
|
||||
request *resourcepb.BulkRequest
|
||||
err error
|
||||
checker map[string]authlib.ItemChecker
|
||||
span trace.Span
|
||||
}
|
||||
|
||||
// Next implements BulkRequestIterator.
|
||||
@@ -259,6 +287,7 @@ func (b *batchRunner) Next() bool {
|
||||
|
||||
if b.err != nil {
|
||||
b.rollback = true
|
||||
b.span.AddEvent("next", trace.WithAttributes(attribute.String("error", b.err.Error())))
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -273,6 +302,16 @@ func (b *batchRunner) Next() bool {
|
||||
b.err = fmt.Errorf("not allowed to create resource")
|
||||
b.rollback = true
|
||||
}
|
||||
|
||||
// Mention resource in the span.
|
||||
attrs := []attribute.KeyValue{
|
||||
attribute.String("key", k),
|
||||
}
|
||||
if b.err != nil {
|
||||
attrs = append(attrs, attribute.String("error", b.err.Error()))
|
||||
}
|
||||
|
||||
b.span.AddEvent("next", trace.WithAttributes(attrs...))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user