feat:multi cluster application

This commit is contained in:
pengcong06
2020-05-25 22:38:03 +08:00
parent d4b7d88b4b
commit 59839439d5
73 changed files with 9838 additions and 6278 deletions

View File

@@ -27,11 +27,12 @@ go_library(
deps = [
"//internal:go_default_library",
"//utilities:go_default_library",
"@com_github_golang_protobuf//descriptor:go_default_library_gen",
"@com_github_golang_protobuf//jsonpb:go_default_library_gen",
"@com_github_golang_protobuf//proto:go_default_library",
"@com_github_golang_protobuf//protoc-gen-go/generator:go_default_library_gen",
"@go_googleapis//google/api:httpbody_go_proto",
"@io_bazel_rules_go//proto/wkt:any_go_proto",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
"@io_bazel_rules_go//proto/wkt:duration_go_proto",
"@io_bazel_rules_go//proto/wkt:field_mask_go_proto",
"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",
@@ -48,6 +49,7 @@ go_test(
size = "small",
srcs = [
"context_test.go",
"convert_test.go",
"errors_test.go",
"fieldmask_test.go",
"handler_test.go",

View File

@@ -57,13 +57,39 @@ except that the forwarded destination is not another HTTP service but rather
a gRPC service.
*/
func AnnotateContext(ctx context.Context, mux *ServeMux, req *http.Request) (context.Context, error) {
ctx, md, err := annotateContext(ctx, mux, req)
if err != nil {
return nil, err
}
if md == nil {
return ctx, nil
}
return metadata.NewOutgoingContext(ctx, md), nil
}
// AnnotateIncomingContext adds context information such as metadata from the request.
// Attach metadata as incoming context.
func AnnotateIncomingContext(ctx context.Context, mux *ServeMux, req *http.Request) (context.Context, error) {
ctx, md, err := annotateContext(ctx, mux, req)
if err != nil {
return nil, err
}
if md == nil {
return ctx, nil
}
return metadata.NewIncomingContext(ctx, md), nil
}
func annotateContext(ctx context.Context, mux *ServeMux, req *http.Request) (context.Context, metadata.MD, error) {
var pairs []string
timeout := DefaultContextTimeout
if tm := req.Header.Get(metadataGrpcTimeout); tm != "" {
var err error
timeout, err = timeoutDecode(tm)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid grpc-timeout: %s", tm)
return nil, nil, status.Errorf(codes.InvalidArgument, "invalid grpc-timeout: %s", tm)
}
}
@@ -80,7 +106,7 @@ func AnnotateContext(ctx context.Context, mux *ServeMux, req *http.Request) (con
if strings.HasSuffix(key, metadataHeaderBinarySuffix) {
b, err := decodeBinHeader(val)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "invalid binary header %s: %s", key, err)
return nil, nil, status.Errorf(codes.InvalidArgument, "invalid binary header %s: %s", key, err)
}
val = string(b)
@@ -111,13 +137,13 @@ func AnnotateContext(ctx context.Context, mux *ServeMux, req *http.Request) (con
ctx, _ = context.WithTimeout(ctx, timeout)
}
if len(pairs) == 0 {
return ctx, nil
return ctx, nil, nil
}
md := metadata.Pairs(pairs...)
for _, mda := range mux.metadataAnnotators {
md = metadata.Join(md, mda(ctx, req))
}
return metadata.NewOutgoingContext(ctx, md), nil
return ctx, md, nil
}
// ServerMetadata consists of metadata sent from gRPC server.

View File

@@ -206,16 +206,22 @@ func BytesSlice(val, sep string) ([][]byte, error) {
// Timestamp converts the given RFC3339 formatted string into a timestamp.Timestamp.
func Timestamp(val string) (*timestamp.Timestamp, error) {
var r *timestamp.Timestamp
err := jsonpb.UnmarshalString(val, r)
return r, err
var r timestamp.Timestamp
err := jsonpb.UnmarshalString(val, &r)
if err != nil {
return nil, err
}
return &r, nil
}
// Duration converts the given string into a timestamp.Duration.
func Duration(val string) (*duration.Duration, error) {
var r *duration.Duration
err := jsonpb.UnmarshalString(val, r)
return r, err
var r duration.Duration
err := jsonpb.UnmarshalString(val, &r)
if err != nil {
return nil, err
}
return &r, nil
}
// Enum converts the given string into an int32 that should be type casted into the

View File

@@ -66,12 +66,12 @@ var (
)
type errorBody struct {
Error string `protobuf:"bytes,1,name=error" json:"error"`
Error string `protobuf:"bytes,100,name=error" json:"error"`
// This is to make the error more compatible with users that expect errors to be Status objects:
// https://github.com/grpc/grpc/blob/master/src/proto/grpc/status/status.proto
// It should be the exact same message as the Error field.
Message string `protobuf:"bytes,1,name=message" json:"message"`
Code int32 `protobuf:"varint,2,name=code" json:"code"`
Code int32 `protobuf:"varint,1,name=code" json:"code"`
Message string `protobuf:"bytes,2,name=message" json:"message"`
Details []*any.Any `protobuf:"bytes,3,rep,name=details" json:"details,omitempty"`
}

View File

@@ -5,12 +5,37 @@ import (
"io"
"strings"
"github.com/golang/protobuf/protoc-gen-go/generator"
descriptor2 "github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/protoc-gen-go/descriptor"
"google.golang.org/genproto/protobuf/field_mask"
)
func translateName(name string, md *descriptor.DescriptorProto) (string, *descriptor.DescriptorProto) {
// TODO - should really gate this with a test that the marshaller has used json names
if md != nil {
for _, f := range md.Field {
if f.JsonName != nil && f.Name != nil && *f.JsonName == name {
var subType *descriptor.DescriptorProto
// If the field has a TypeName then we retrieve the nested type for translating the embedded message names.
if f.TypeName != nil {
typeSplit := strings.Split(*f.TypeName, ".")
typeName := typeSplit[len(typeSplit)-1]
for _, t := range md.NestedType {
if typeName == *t.Name {
subType = t
}
}
}
return *f.Name, subType
}
}
}
return name, nil
}
// FieldMaskFromRequestBody creates a FieldMask printing all complete paths from the JSON body.
func FieldMaskFromRequestBody(r io.Reader) (*field_mask.FieldMask, error) {
func FieldMaskFromRequestBody(r io.Reader, md *descriptor.DescriptorProto) (*field_mask.FieldMask, error) {
fm := &field_mask.FieldMask{}
var root interface{}
if err := json.NewDecoder(r).Decode(&root); err != nil {
@@ -20,7 +45,7 @@ func FieldMaskFromRequestBody(r io.Reader) (*field_mask.FieldMask, error) {
return nil, err
}
queue := []fieldMaskPathItem{{node: root}}
queue := []fieldMaskPathItem{{node: root, md: md}}
for len(queue) > 0 {
// dequeue an item
item := queue[0]
@@ -29,7 +54,11 @@ func FieldMaskFromRequestBody(r io.Reader) (*field_mask.FieldMask, error) {
if m, ok := item.node.(map[string]interface{}); ok {
// if the item is an object, then enqueue all of its children
for k, v := range m {
queue = append(queue, fieldMaskPathItem{path: append(item.path, generator.CamelCase(k)), node: v})
protoName, subMd := translateName(k, item.md)
if subMsg, ok := v.(descriptor2.Message); ok {
_, subMd = descriptor2.ForMessage(subMsg)
}
queue = append(queue, fieldMaskPathItem{path: append(item.path, protoName), node: v, md: subMd})
}
} else if len(item.path) > 0 {
// otherwise, it's a leaf node so print its path
@@ -47,24 +76,7 @@ type fieldMaskPathItem struct {
// a generic decoded json object the current item to inspect for further path extraction
node interface{}
}
// CamelCaseFieldMask updates the given FieldMask by converting all of its paths to CamelCase, using the same heuristic
// that's used for naming protobuf fields in Go.
func CamelCaseFieldMask(mask *field_mask.FieldMask) {
if mask == nil || mask.Paths == nil {
return
}
var newPaths []string
for _, path := range mask.Paths {
lowerCasedParts := strings.Split(path, ".")
var camelCasedParts []string
for _, part := range lowerCasedParts {
camelCasedParts = append(camelCasedParts, generator.CamelCase(part))
}
newPaths = append(newPaths, strings.Join(camelCasedParts, "."))
}
mask.Paths = newPaths
// descriptor for parent message
md *descriptor.DescriptorProto
}

View File

@@ -15,15 +15,13 @@ import (
"google.golang.org/grpc/grpclog"
)
var valuesKeyRegexp = regexp.MustCompile("^(.*)\\[(.*)\\]$")
// PopulateQueryParameters populates "values" into "msg".
// A value is ignored if its key starts with one of the elements in "filter".
func PopulateQueryParameters(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
for key, values := range values {
re, err := regexp.Compile("^(.*)\\[(.*)\\]$")
if err != nil {
return err
}
match := re.FindStringSubmatch(key)
match := valuesKeyRegexp.FindStringSubmatch(key)
if len(match) == 3 {
key = match[1]
values = append([]string{match[2]}, values...)