api: add experimental gRPC

This commit is contained in:
Fabian Reinartz 2017-04-06 13:48:10 +02:00
parent a1c2c525eb
commit 63113a7030
11 changed files with 933 additions and 62 deletions

View File

@ -0,0 +1,124 @@
{
"swagger": "2.0",
"info": {
"title": "api/grpcapi/apipb/api.proto",
"version": "version not set"
},
"schemes": [
"http",
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/alerts": {
"get": {
"operationId": "Get",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/apipbAlertsGetResponse"
}
}
},
"tags": [
"Alerts"
]
},
"post": {
"operationId": "Add",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/apipbAlertsAddResponse"
}
}
},
"tags": [
"Alerts"
]
}
}
},
"definitions": {
"apipbAlert": {
"type": "object",
"properties": {
"labels": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"annotations": {
"type": "object",
"additionalProperties": {
"type": "string"
}
},
"starts_at": {
"type": "string",
"format": "date-time"
},
"ends_at": {
"type": "string",
"format": "date-time"
},
"generator_url": {
"type": "string"
}
}
},
"apipbAlertsAddRequest": {
"type": "object",
"properties": {
"alerts": {
"type": "array",
"items": {
"$ref": "#/definitions/apipbAlert"
}
}
}
},
"apipbAlertsAddResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/apipbResponseHeader"
}
}
},
"apipbAlertsGetRequest": {
"type": "object"
},
"apipbAlertsGetResponse": {
"type": "object",
"properties": {
"header": {
"$ref": "#/definitions/apipbResponseHeader"
},
"alerts": {
"type": "array",
"items": {
"$ref": "#/definitions/apipbAlert"
}
}
}
},
"apipbResponseHeader": {
"type": "object",
"properties": {
"peer_name": {
"type": "string"
}
},
"description": "ResponseHeader is part of every API response."
}
}
}

View File

@ -24,7 +24,6 @@ ifdef DEBUG
bindata_flags = -debug
endif
all: format build test
test:
@ -66,5 +65,4 @@ promu:
GOARCH=$(subst x86_64,amd64,$(patsubst i%86,386,$(shell uname -m))) \
$(GO) get -u github.com/prometheus/promu
.PHONY: all style format build test vet assets tarball docker promu

318
api/grpcapi/apipb/api.pb.go Normal file
View File

@ -0,0 +1,318 @@
// Code generated by protoc-gen-go.
// source: api.proto
// DO NOT EDIT!
/*
Package apipb is a generated protocol buffer package.
It is generated from these files:
api.proto
It has these top-level messages:
ResponseHeader
AlertsGetRequest
AlertsAddRequest
AlertsGetResponse
AlertsAddResponse
Alert
*/
package apipb
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import google_protobuf "github.com/golang/protobuf/ptypes/timestamp"
import _ "google.golang.org/genproto/googleapis/api/annotations"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
// ResponseHeader is part of every API response.
type ResponseHeader struct {
PeerName string `protobuf:"bytes,1,opt,name=peer_name,json=peerName" json:"peer_name,omitempty"`
}
func (m *ResponseHeader) Reset() { *m = ResponseHeader{} }
func (m *ResponseHeader) String() string { return proto.CompactTextString(m) }
func (*ResponseHeader) ProtoMessage() {}
func (*ResponseHeader) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *ResponseHeader) GetPeerName() string {
if m != nil {
return m.PeerName
}
return ""
}
type AlertsGetRequest struct {
}
func (m *AlertsGetRequest) Reset() { *m = AlertsGetRequest{} }
func (m *AlertsGetRequest) String() string { return proto.CompactTextString(m) }
func (*AlertsGetRequest) ProtoMessage() {}
func (*AlertsGetRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
type AlertsAddRequest struct {
Alerts []*Alert `protobuf:"bytes,1,rep,name=alerts" json:"alerts,omitempty"`
}
func (m *AlertsAddRequest) Reset() { *m = AlertsAddRequest{} }
func (m *AlertsAddRequest) String() string { return proto.CompactTextString(m) }
func (*AlertsAddRequest) ProtoMessage() {}
func (*AlertsAddRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *AlertsAddRequest) GetAlerts() []*Alert {
if m != nil {
return m.Alerts
}
return nil
}
type AlertsGetResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
Alerts []*Alert `protobuf:"bytes,2,rep,name=alerts" json:"alerts,omitempty"`
}
func (m *AlertsGetResponse) Reset() { *m = AlertsGetResponse{} }
func (m *AlertsGetResponse) String() string { return proto.CompactTextString(m) }
func (*AlertsGetResponse) ProtoMessage() {}
func (*AlertsGetResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *AlertsGetResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
func (m *AlertsGetResponse) GetAlerts() []*Alert {
if m != nil {
return m.Alerts
}
return nil
}
type AlertsAddResponse struct {
Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
}
func (m *AlertsAddResponse) Reset() { *m = AlertsAddResponse{} }
func (m *AlertsAddResponse) String() string { return proto.CompactTextString(m) }
func (*AlertsAddResponse) ProtoMessage() {}
func (*AlertsAddResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *AlertsAddResponse) GetHeader() *ResponseHeader {
if m != nil {
return m.Header
}
return nil
}
type Alert struct {
Labels map[string]string `protobuf:"bytes,1,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
Annotations map[string]string `protobuf:"bytes,2,rep,name=annotations" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
StartsAt *google_protobuf.Timestamp `protobuf:"bytes,3,opt,name=starts_at,json=startsAt" json:"starts_at,omitempty"`
EndsAt *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=ends_at,json=endsAt" json:"ends_at,omitempty"`
GeneratorUrl string `protobuf:"bytes,5,opt,name=generator_url,json=generatorUrl" json:"generator_url,omitempty"`
}
func (m *Alert) Reset() { *m = Alert{} }
func (m *Alert) String() string { return proto.CompactTextString(m) }
func (*Alert) ProtoMessage() {}
func (*Alert) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
func (m *Alert) GetLabels() map[string]string {
if m != nil {
return m.Labels
}
return nil
}
func (m *Alert) GetAnnotations() map[string]string {
if m != nil {
return m.Annotations
}
return nil
}
func (m *Alert) GetStartsAt() *google_protobuf.Timestamp {
if m != nil {
return m.StartsAt
}
return nil
}
func (m *Alert) GetEndsAt() *google_protobuf.Timestamp {
if m != nil {
return m.EndsAt
}
return nil
}
func (m *Alert) GetGeneratorUrl() string {
if m != nil {
return m.GeneratorUrl
}
return ""
}
func init() {
proto.RegisterType((*ResponseHeader)(nil), "apipb.ResponseHeader")
proto.RegisterType((*AlertsGetRequest)(nil), "apipb.AlertsGetRequest")
proto.RegisterType((*AlertsAddRequest)(nil), "apipb.AlertsAddRequest")
proto.RegisterType((*AlertsGetResponse)(nil), "apipb.AlertsGetResponse")
proto.RegisterType((*AlertsAddResponse)(nil), "apipb.AlertsAddResponse")
proto.RegisterType((*Alert)(nil), "apipb.Alert")
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// Client API for Alerts service
type AlertsClient interface {
Get(ctx context.Context, in *AlertsGetRequest, opts ...grpc.CallOption) (*AlertsGetResponse, error)
Add(ctx context.Context, in *AlertsAddRequest, opts ...grpc.CallOption) (*AlertsAddResponse, error)
}
type alertsClient struct {
cc *grpc.ClientConn
}
func NewAlertsClient(cc *grpc.ClientConn) AlertsClient {
return &alertsClient{cc}
}
func (c *alertsClient) Get(ctx context.Context, in *AlertsGetRequest, opts ...grpc.CallOption) (*AlertsGetResponse, error) {
out := new(AlertsGetResponse)
err := grpc.Invoke(ctx, "/apipb.Alerts/Get", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *alertsClient) Add(ctx context.Context, in *AlertsAddRequest, opts ...grpc.CallOption) (*AlertsAddResponse, error) {
out := new(AlertsAddResponse)
err := grpc.Invoke(ctx, "/apipb.Alerts/Add", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Alerts service
type AlertsServer interface {
Get(context.Context, *AlertsGetRequest) (*AlertsGetResponse, error)
Add(context.Context, *AlertsAddRequest) (*AlertsAddResponse, error)
}
func RegisterAlertsServer(s *grpc.Server, srv AlertsServer) {
s.RegisterService(&_Alerts_serviceDesc, srv)
}
func _Alerts_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AlertsGetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AlertsServer).Get(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/apipb.Alerts/Get",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AlertsServer).Get(ctx, req.(*AlertsGetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Alerts_Add_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AlertsAddRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(AlertsServer).Add(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/apipb.Alerts/Add",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(AlertsServer).Add(ctx, req.(*AlertsAddRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Alerts_serviceDesc = grpc.ServiceDesc{
ServiceName: "apipb.Alerts",
HandlerType: (*AlertsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Get",
Handler: _Alerts_Get_Handler,
},
{
MethodName: "Add",
Handler: _Alerts_Add_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api.proto",
}
func init() { proto.RegisterFile("api.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 445 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x52, 0xc1, 0x6e, 0xd3, 0x40,
0x10, 0x95, 0x63, 0xe2, 0xd6, 0x93, 0x02, 0x61, 0x05, 0xc2, 0x32, 0x20, 0x2a, 0xc3, 0xa1, 0x97,
0x3a, 0x28, 0x3d, 0x50, 0x38, 0x80, 0x8c, 0x84, 0x0a, 0x12, 0xe2, 0x60, 0xc1, 0x39, 0x9a, 0xc8,
0x43, 0x6a, 0xe1, 0xd8, 0xcb, 0xee, 0x18, 0xa9, 0x57, 0x4e, 0xdc, 0xb9, 0xf3, 0x53, 0xfc, 0x02,
0x1f, 0x82, 0xb2, 0xbb, 0x6e, 0xb7, 0x41, 0x02, 0xc1, 0xcd, 0x7e, 0xf3, 0xde, 0x9b, 0xb7, 0x33,
0x03, 0x31, 0xca, 0x3a, 0x97, 0xaa, 0xe3, 0x4e, 0x8c, 0x51, 0xd6, 0x72, 0x99, 0xde, 0x5f, 0x75,
0xdd, 0xaa, 0xa1, 0x99, 0x01, 0x97, 0xfd, 0x87, 0x19, 0xd7, 0x6b, 0xd2, 0x8c, 0x6b, 0x69, 0x79,
0xe9, 0x5d, 0x47, 0x40, 0x59, 0xcf, 0xb0, 0x6d, 0x3b, 0x46, 0xae, 0xbb, 0x56, 0xdb, 0x6a, 0x76,
0x08, 0xd7, 0x4a, 0xd2, 0xb2, 0x6b, 0x35, 0xbd, 0x22, 0xac, 0x48, 0x89, 0x3b, 0x10, 0x4b, 0x22,
0xb5, 0x68, 0x71, 0x4d, 0x49, 0xb0, 0x1f, 0x1c, 0xc4, 0xe5, 0xee, 0x06, 0x78, 0x8b, 0x6b, 0xca,
0x04, 0x4c, 0x8b, 0x86, 0x14, 0xeb, 0x13, 0xe2, 0x92, 0x3e, 0xf5, 0xa4, 0x39, 0x3b, 0x1e, 0xb0,
0xa2, 0xaa, 0x1c, 0x26, 0x1e, 0x42, 0x84, 0x06, 0x4b, 0x82, 0xfd, 0xf0, 0x60, 0x32, 0xdf, 0xcb,
0x4d, 0xda, 0xdc, 0x10, 0x4b, 0x57, 0xcb, 0x4e, 0xe1, 0x86, 0xe7, 0x66, 0x53, 0x88, 0x43, 0x88,
0x4e, 0x4d, 0x12, 0xd3, 0x7c, 0x32, 0xbf, 0xe5, 0xa4, 0x97, 0x63, 0x96, 0x8e, 0xe4, 0x75, 0x1a,
0xfd, 0xa1, 0xd3, 0x8b, 0xa1, 0x93, 0xc9, 0xf8, 0x5f, 0x9d, 0xb2, 0xaf, 0x21, 0x8c, 0x8d, 0x89,
0x78, 0x04, 0x51, 0x83, 0x4b, 0x6a, 0x86, 0xd7, 0x25, 0x7e, 0xcf, 0xfc, 0x8d, 0x29, 0xbd, 0x6c,
0x59, 0x9d, 0x95, 0x8e, 0x27, 0x9e, 0xc3, 0xc4, 0x9b, 0xbd, 0x8b, 0x7a, 0xef, 0x92, 0xac, 0xb8,
0xa8, 0x5b, 0xad, 0xaf, 0x10, 0x8f, 0x21, 0xd6, 0x8c, 0x8a, 0xf5, 0x02, 0x39, 0x09, 0x4d, 0xdc,
0x34, 0xb7, 0x9b, 0xcd, 0x87, 0xd5, 0xe7, 0xef, 0x86, 0xd5, 0x97, 0xbb, 0x96, 0x5c, 0xb0, 0x38,
0x82, 0x1d, 0x6a, 0x2b, 0x23, 0xbb, 0xf2, 0x57, 0x59, 0xb4, 0xa1, 0x16, 0x2c, 0x1e, 0xc0, 0xd5,
0x15, 0xb5, 0xa4, 0x90, 0x3b, 0xb5, 0xe8, 0x55, 0x93, 0x8c, 0xcd, 0x1d, 0xec, 0x9d, 0x83, 0xef,
0x55, 0x93, 0x3e, 0x81, 0x89, 0xf7, 0x54, 0x31, 0x85, 0xf0, 0x23, 0x9d, 0xb9, 0x8b, 0xd9, 0x7c,
0x8a, 0x9b, 0x30, 0xfe, 0x8c, 0x4d, 0x4f, 0xc9, 0xc8, 0x60, 0xf6, 0xe7, 0xe9, 0xe8, 0x38, 0x48,
0x9f, 0xc1, 0x74, 0xfb, 0xb9, 0xff, 0xa2, 0x9f, 0x7f, 0x0f, 0x20, 0xb2, 0xfb, 0x14, 0xaf, 0x21,
0x3c, 0x21, 0x16, 0xb7, 0xfd, 0x59, 0x7a, 0xd7, 0x99, 0x26, 0xbf, 0x17, 0xec, 0x76, 0xb3, 0xeb,
0x5f, 0x7e, 0xfc, 0xfc, 0x36, 0x8a, 0xc5, 0xce, 0x0c, 0xcf, 0xad, 0x8a, 0xaa, 0xda, 0xb2, 0xba,
0x38, 0xea, 0x2d, 0x2b, 0xef, 0x92, 0x06, 0xab, 0x6c, 0xb0, 0x5a, 0x46, 0x66, 0xb8, 0x47, 0xbf,
0x02, 0x00, 0x00, 0xff, 0xff, 0xea, 0x50, 0x36, 0x31, 0xb0, 0x03, 0x00, 0x00,
}

View File

@ -0,0 +1,155 @@
// Code generated by protoc-gen-grpc-gateway
// source: api/grpcapi/apipb/api.proto
// DO NOT EDIT!
/*
Package apipb is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package apipb
import (
"io"
"net/http"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
)
var _ codes.Code
var _ io.Reader
var _ = runtime.String
var _ = utilities.NewDoubleArray
func request_Alerts_Get_0(ctx context.Context, marshaler runtime.Marshaler, client AlertsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AlertsGetRequest
var metadata runtime.ServerMetadata
msg, err := client.Get(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
var (
filter_Alerts_Add_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_Alerts_Add_0(ctx context.Context, marshaler runtime.Marshaler, client AlertsClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq AlertsAddRequest
var metadata runtime.ServerMetadata
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_Alerts_Add_0); err != nil {
return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.Add(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterAlertsHandlerFromEndpoint is same as RegisterAlertsHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterAlertsHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Printf("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterAlertsHandler(ctx, mux, conn)
}
// RegisterAlertsHandler registers the http handlers for service Alerts to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterAlertsHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
client := NewAlertsClient(conn)
mux.Handle("GET", pattern_Alerts_Get_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_Alerts_Get_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_Alerts_Get_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Alerts_Add_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, req)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
}
resp, md, err := request_Alerts_Add_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, outboundMarshaler, w, req, err)
return
}
forward_Alerts_Add_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Alerts_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"alerts"}, ""))
pattern_Alerts_Add_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0}, []string{"alerts"}, ""))
)
var (
forward_Alerts_Get_0 = runtime.ForwardResponseMessage
forward_Alerts_Add_0 = runtime.ForwardResponseMessage
)

View File

@ -0,0 +1,54 @@
syntax = "proto3";
package apipb;
import "google/protobuf/timestamp.proto";
import "google/api/annotations.proto";
service Alerts {
rpc Get(AlertsGetRequest) returns (AlertsGetResponse) {
option (google.api.http) = {
get: "/alerts"
};
}
rpc Add(AlertsAddRequest) returns (AlertsAddResponse) {
option (google.api.http) = {
post: "/alerts"
};
}
}
// ResponseHeader is part of every API response. It allows to identify
// the responding Alertmanager instance, which can be useful to debug temporary
// inconsistencies if the Alertmanager setup is accesssed via a reverse proxy.
message ResponseHeader {
string peer_name = 1;
}
message AlertsGetRequest {
// repeated string filter = 1;
// string group_by = 2;
}
message AlertsAddRequest {
repeated Alert alerts = 1;
}
message AlertsGetResponse {
ResponseHeader header = 1;
repeated Alert alerts = 2;
}
message AlertsAddResponse {
ResponseHeader header = 1;
}
message Alert {
map<string, string> labels = 1;
map<string, string> annotations = 2;
google.protobuf.Timestamp starts_at = 3;
google.protobuf.Timestamp ends_at = 4;
string generator_url = 5;
}

85
api/grpcapi/grpc.go Normal file
View File

@ -0,0 +1,85 @@
package grpcapi
import (
"net"
"net/http"
"unsafe"
"golang.org/x/net/context"
"github.com/golang/protobuf/ptypes"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/prometheus/alertmanager/api/grpcapi/apipb"
"github.com/prometheus/alertmanager/provider"
"github.com/weaveworks/mesh"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
func Handle(grpcl net.Listener, alerts provider.Alerts, mrouter *mesh.Router) (*grpc.Server, http.Handler) {
grpcServer := grpc.NewServer()
asvc := &alertsServer{
alerts: alerts,
mrouter: mrouter,
}
apipb.RegisterAlertsServer(grpcServer, asvc)
ctx := context.Background()
gwmux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithInsecure()}
err := apipb.RegisterAlertsHandlerFromEndpoint(ctx, gwmux, grpcl.Addr().String(), opts)
if err != nil {
panic(err)
}
return grpcServer, gwmux
}
type alertsServer struct {
alerts provider.Alerts
mrouter *mesh.Router
}
func (s *alertsServer) Get(ctx context.Context, req *apipb.AlertsGetRequest) (*apipb.AlertsGetResponse, error) {
alerts := s.alerts.GetPending()
defer alerts.Close()
var (
err error
res []*apipb.Alert
)
for a := range alerts.Next() {
if err = alerts.Err(); err != nil {
return nil, err
}
start, _ := ptypes.TimestampProto(a.StartsAt)
end, _ := ptypes.TimestampProto(a.EndsAt)
res = append(res, &apipb.Alert{
Labels: *(*map[string]string)(unsafe.Pointer(&a.Labels)),
Annotations: *(*map[string]string)(unsafe.Pointer(&a.Annotations)),
StartsAt: start,
EndsAt: end,
})
}
resp := &apipb.AlertsGetResponse{
Header: s.header(),
Alerts: res,
}
return resp, nil
}
func (s *alertsServer) Add(ctx context.Context, req *apipb.AlertsAddRequest) (*apipb.AlertsAddResponse, error) {
resp := &apipb.AlertsAddResponse{
Header: s.header(),
}
return resp, grpc.Errorf(codes.Unimplemented, "not implemented")
}
func (s *alertsServer) header() *apipb.ResponseHeader {
status := mesh.NewStatus(s.mrouter)
return &apipb.ResponseHeader{PeerName: status.Name}
}

View File

@ -33,6 +33,7 @@ import (
"time"
"github.com/prometheus/alertmanager/api"
"github.com/prometheus/alertmanager/api/grpcapi"
"github.com/prometheus/alertmanager/config"
"github.com/prometheus/alertmanager/dispatch"
"github.com/prometheus/alertmanager/inhibit"
@ -81,6 +82,7 @@ func main() {
externalURL = flag.String("web.external-url", "", "The URL under which Alertmanager is externally reachable (for example, if Alertmanager is served via a reverse proxy). Used for generating relative and absolute links back to Alertmanager itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Alertmanager. If omitted, relevant URL components will be derived automatically.")
listenAddress = flag.String("web.listen-address", ":9093", "Address to listen on for the web interface and API.")
grpcAddress = flag.String("web.grpc-address", ":9094", "Address to listen on for GRPC API communication.")
meshListen = flag.String("mesh.listen-address", net.JoinHostPort("0.0.0.0", strconv.Itoa(mesh.Port)), "mesh listen address")
hwaddr = flag.String("mesh.peer-id", "", "mesh peer ID (default: MAC address)")
@ -249,14 +251,30 @@ func main() {
os.Exit(1)
}
grpcl, err := net.Listen("tcp", *grpcAddress)
if err != nil {
log.Fatal(err)
}
grpcs, h := grpcapi.Handle(grpcl, alerts, mrouter)
go grpcs.Serve(grpcl)
router := route.New(nil)
webReload := make(chan struct{})
ui.Register(router.WithPrefix(amURL.Path), webReload)
apiv.Register(router.WithPrefix(path.Join(amURL.Path, "/api")))
mux := http.NewServeMux()
// Register old API and UI
mux.Handle("/", router)
mux.Handle("/api/v2/", http.StripPrefix("/api/v2", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
fmt.Println("gotreq", req)
h.ServeHTTP(w, req)
})))
log.Infoln("Listening on", *listenAddress)
go listen(*listenAddress, router)
go http.ListenAndServe(*listenAddress, mux)
var (
hup = make(chan os.Signal)
@ -376,12 +394,6 @@ func extURL(listen, external string) (*url.URL, error) {
return u, nil
}
func listen(listen string, router *route.Router) {
if err := http.ListenAndServe(listen, router); err != nil {
log.Fatal(err)
}
}
type stringset map[string]struct{}
func (ss stringset) Set(value string) error {

View File

@ -1,12 +1,12 @@
// Code generated by protoc-gen-go.
// source: nflog/nflogpb/nflog.proto
// source: nflog.proto
// DO NOT EDIT!
/*
Package nflogpb is a generated protocol buffer package.
It is generated from these files:
nflog/nflogpb/nflog.proto
nflog.proto
It has these top-level messages:
Receiver
@ -46,6 +46,27 @@ func (m *Receiver) String() string { return proto.CompactTextString(m
func (*Receiver) ProtoMessage() {}
func (*Receiver) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *Receiver) GetGroupName() string {
if m != nil {
return m.GroupName
}
return ""
}
func (m *Receiver) GetIntegration() string {
if m != nil {
return m.Integration
}
return ""
}
func (m *Receiver) GetIdx() uint32 {
if m != nil {
return m.Idx
}
return 0
}
// Entry holds information about a successful notification
// sent to a receiver.
type Entry struct {
@ -66,6 +87,13 @@ func (m *Entry) String() string { return proto.CompactTextString(m) }
func (*Entry) ProtoMessage() {}
func (*Entry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Entry) GetGroupKey() []byte {
if m != nil {
return m.GroupKey
}
return nil
}
func (m *Entry) GetReceiver() *Receiver {
if m != nil {
return m.Receiver
@ -73,6 +101,20 @@ func (m *Entry) GetReceiver() *Receiver {
return nil
}
func (m *Entry) GetGroupHash() []byte {
if m != nil {
return m.GroupHash
}
return nil
}
func (m *Entry) GetResolved() bool {
if m != nil {
return m.Resolved
}
return false
}
func (m *Entry) GetTimestamp() *google_protobuf.Timestamp {
if m != nil {
return m.Timestamp
@ -115,27 +157,27 @@ func init() {
proto.RegisterType((*MeshEntry)(nil), "nflogpb.MeshEntry")
}
func init() { proto.RegisterFile("nflog/nflogpb/nflog.proto", fileDescriptor0) }
func init() { proto.RegisterFile("nflog.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 299 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x7c, 0x50, 0xc1, 0x4e, 0x83, 0x40,
0x10, 0x4d, 0xad, 0x55, 0x98, 0x56, 0xa3, 0x7b, 0xc2, 0x1a, 0x63, 0xd3, 0x78, 0xf0, 0xe2, 0x36,
0xa9, 0x17, 0x3d, 0x7a, 0x30, 0x31, 0x31, 0x7a, 0xd8, 0x78, 0x35, 0x04, 0xec, 0x14, 0x36, 0x02,
0x4b, 0x96, 0x6d, 0x53, 0xfe, 0xd0, 0xcf, 0x12, 0x66, 0x17, 0xea, 0xc9, 0x0b, 0xcc, 0xbe, 0xf7,
0x32, 0xef, 0xbd, 0x81, 0x8b, 0x62, 0x9d, 0xa9, 0x64, 0x41, 0xdf, 0x32, 0xb6, 0x7f, 0x5e, 0x6a,
0x65, 0x14, 0x3b, 0x76, 0xe0, 0xf4, 0x3a, 0x51, 0x2a, 0xc9, 0x70, 0x41, 0x70, 0xbc, 0x59, 0x2f,
0x8c, 0xcc, 0xb1, 0x32, 0x51, 0x5e, 0x5a, 0xe5, 0xfc, 0x13, 0x3c, 0x81, 0x5f, 0x28, 0xb7, 0xa8,
0xd9, 0x15, 0x40, 0xa2, 0xd5, 0xa6, 0x0c, 0x8b, 0x28, 0xc7, 0x60, 0x30, 0x1b, 0xdc, 0xfa, 0xc2,
0x27, 0xe4, 0xbd, 0x01, 0xd8, 0x0c, 0xc6, 0xb2, 0x30, 0x98, 0xe8, 0xc8, 0x48, 0x55, 0x04, 0x07,
0xc4, 0xff, 0x85, 0xd8, 0x19, 0x0c, 0xe5, 0x6a, 0x17, 0x0c, 0x1b, 0xe6, 0x44, 0xb4, 0xe3, 0xfc,
0x67, 0x00, 0xa3, 0xe7, 0xc2, 0xe8, 0x9a, 0x5d, 0x82, 0x5d, 0x15, 0x7e, 0x63, 0x4d, 0xbb, 0x27,
0xc2, 0x23, 0xe0, 0x15, 0x6b, 0x76, 0x07, 0x9e, 0x76, 0x29, 0x68, 0xef, 0x78, 0x79, 0xce, 0x5d,
0x05, 0xde, 0xc5, 0x13, 0xbd, 0x64, 0x1f, 0x34, 0x8d, 0xaa, 0x94, 0xec, 0x26, 0x2e, 0xe8, 0x4b,
0x03, 0xb0, 0x69, 0xbb, 0xad, 0x52, 0xd9, 0x16, 0x57, 0xc1, 0x61, 0x43, 0x7a, 0xa2, 0x7f, 0xb3,
0x07, 0xf0, 0xfb, 0x13, 0x04, 0x23, 0xb2, 0x9a, 0x72, 0x7b, 0x24, 0xde, 0x1d, 0x89, 0x7f, 0x74,
0x0a, 0xb1, 0x17, 0xcf, 0x33, 0xf0, 0xdf, 0xb0, 0x4a, 0x6d, 0x9b, 0x1b, 0x18, 0x61, 0x3b, 0x50,
0x93, 0xf1, 0xf2, 0xb4, 0x4f, 0x4b, 0xb4, 0xb0, 0x24, 0x7b, 0x04, 0xc0, 0x5d, 0x29, 0x1b, 0xf3,
0x30, 0x32, 0xae, 0xd8, 0xbf, 0x6e, 0x4e, 0xfd, 0x64, 0xe2, 0x23, 0xa2, 0xef, 0x7f, 0x03, 0x00,
0x00, 0xff, 0xff, 0xd7, 0x8e, 0x51, 0xb4, 0xe5, 0x01, 0x00, 0x00,
// 296 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x50, 0x4d, 0x4b, 0xc3, 0x40,
0x10, 0x25, 0xd6, 0x6a, 0x32, 0xa9, 0xa2, 0x7b, 0x0a, 0x15, 0x31, 0x14, 0x0f, 0xbd, 0xb8, 0x85,
0x7a, 0xd1, 0xa3, 0x07, 0x41, 0x10, 0x3d, 0x2c, 0x5e, 0xa5, 0x6c, 0xec, 0x34, 0x59, 0x4c, 0xb2,
0x61, 0x77, 0x5b, 0x9a, 0x7f, 0xe8, 0xcf, 0x92, 0x4c, 0x3e, 0xea, 0xc9, 0xdb, 0xcc, 0x7b, 0x8f,
0x37, 0xef, 0x0d, 0x84, 0xe5, 0x26, 0xd7, 0x29, 0xaf, 0x8c, 0x76, 0x9a, 0x9d, 0xd2, 0x52, 0x25,
0xd3, 0x9b, 0x54, 0xeb, 0x34, 0xc7, 0x05, 0xc1, 0xc9, 0x76, 0xb3, 0x70, 0xaa, 0x40, 0xeb, 0x64,
0x51, 0xb5, 0xca, 0xd9, 0x27, 0xf8, 0x02, 0xbf, 0x50, 0xed, 0xd0, 0xb0, 0x6b, 0x80, 0xd4, 0xe8,
0x6d, 0xb5, 0x2a, 0x65, 0x81, 0x91, 0x17, 0x7b, 0xf3, 0x40, 0x04, 0x84, 0xbc, 0xcb, 0x02, 0x59,
0x0c, 0xa1, 0x2a, 0x1d, 0xa6, 0x46, 0x3a, 0xa5, 0xcb, 0xe8, 0x88, 0xf8, 0xbf, 0x10, 0xbb, 0x80,
0x91, 0x5a, 0xef, 0xa3, 0x51, 0xec, 0xcd, 0xcf, 0x44, 0x33, 0xce, 0x7e, 0x3c, 0x18, 0x3f, 0x97,
0xce, 0xd4, 0xec, 0x0a, 0x5a, 0xab, 0xd5, 0x37, 0xd6, 0xe4, 0x3d, 0x11, 0x3e, 0x01, 0xaf, 0x58,
0xb3, 0x3b, 0xf0, 0x4d, 0x97, 0x82, 0x7c, 0xc3, 0xe5, 0x25, 0xef, 0x2a, 0xf0, 0x3e, 0x9e, 0x18,
0x24, 0x87, 0xa0, 0x99, 0xb4, 0x19, 0x9d, 0x9b, 0x74, 0x41, 0x5f, 0xa4, 0xcd, 0xd8, 0xb4, 0x71,
0xb3, 0x3a, 0xdf, 0xe1, 0x3a, 0x3a, 0x8e, 0xbd, 0xb9, 0x2f, 0x86, 0x9d, 0x3d, 0x40, 0x30, 0xbc,
0x20, 0x1a, 0xd3, 0xa9, 0x29, 0x6f, 0x9f, 0xc4, 0xfb, 0x27, 0xf1, 0x8f, 0x5e, 0x21, 0x0e, 0xe2,
0x59, 0x0e, 0xc1, 0x1b, 0xda, 0xac, 0x6d, 0x73, 0x0b, 0x63, 0x6c, 0x06, 0x6a, 0x12, 0x2e, 0xcf,
0x87, 0xb4, 0x44, 0x8b, 0x96, 0x64, 0x8f, 0x00, 0xb8, 0xaf, 0x94, 0x41, 0xbb, 0x92, 0xae, 0x2b,
0xf6, 0xef, 0xb5, 0x4e, 0xfd, 0xe4, 0x92, 0x13, 0xa2, 0xef, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff,
0xf3, 0x47, 0x57, 0xc3, 0xd7, 0x01, 0x00, 0x00,
}

View File

@ -17,7 +17,7 @@ message Receiver {
// Entry holds information about a successful notification
// sent to a receiver.
message Entry {
// The key identifying the dispatching group.
// The key identifying the dispatching group.
bytes group_key = 1;
// The receiver that was notified.
Receiver receiver = 2;

View File

@ -1,2 +1,43 @@
protoc --go_out=. nflog/nflogpb/nflog.proto
protoc --go_out=. silence/silencepb/silence.proto
#!/usr/bin/env bash
#
# Generate all etcd protobuf bindings.
# Run from repository root.
set -e
set -u
if ! [[ "$0" =~ "scripts/genproto.sh" ]]; then
echo "must be run from repository root"
exit 255
fi
if ! [[ $(protoc --version) =~ "3.2.0" ]]; then
echo "could not find protoc 3.2.0, is it installed + in PATH?"
exit 255
fi
GOGOPROTO_ROOT="${GOPATH}/src/github.com/gogo/protobuf"
GOGOPROTO_PATH="${GOGOPROTO_ROOT}:${GOGOPROTO_ROOT}/protobuf"
GRPC_GATEWAY_ROOT="${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway"
PROMETHEUS_ROOT="${GOPATH}/src/github.com/prometheus"
DIRS="nflog/nflogpb silence/silencepb/ api/grpcapi/apipb/"
for dir in ${DIRS}; do
pushd ${dir}
protoc --go_out=plugins=grpc:. -I=. \
-I="${PROMETHEUS_ROOT}" \
-I="${GRPC_GATEWAY_ROOT}/third_party/googleapis" \
*.proto
popd
done
protoc -I. \
-I="${GOGOPROTO_PATH}" \
-I="${PROMETHEUS_ROOT}" \
-I="${GRPC_GATEWAY_ROOT}/third_party/googleapis" \
--grpc-gateway_out=logtostderr=true:. \
--swagger_out=logtostderr=true:./Documentation/dev/apispec/swagger/. \
api/grpcapi/apipb/api.proto
mv Documentation/dev/apispec/swagger/api/grpcapi/apipb/api.swagger.json Documentation/dev/apispec/swagger
rm -rf Documentation/dev/apispec/swagger/api

View File

@ -1,12 +1,12 @@
// Code generated by protoc-gen-go.
// source: silence/silencepb/silence.proto
// source: silence.proto
// DO NOT EDIT!
/*
Package silencepb is a generated protocol buffer package.
It is generated from these files:
silence/silencepb/silence.proto
silence.proto
It has these top-level messages:
Matcher
@ -70,6 +70,27 @@ func (m *Matcher) String() string { return proto.CompactTextString(m)
func (*Matcher) ProtoMessage() {}
func (*Matcher) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *Matcher) GetType() Matcher_Type {
if m != nil {
return m.Type
}
return Matcher_EQUAL
}
func (m *Matcher) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Matcher) GetPattern() string {
if m != nil {
return m.Pattern
}
return ""
}
// A comment can be attached to a silence.
type Comment struct {
Author string `protobuf:"bytes,1,opt,name=author" json:"author,omitempty"`
@ -82,6 +103,20 @@ func (m *Comment) String() string { return proto.CompactTextString(m)
func (*Comment) ProtoMessage() {}
func (*Comment) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *Comment) GetAuthor() string {
if m != nil {
return m.Author
}
return ""
}
func (m *Comment) GetComment() string {
if m != nil {
return m.Comment
}
return ""
}
func (m *Comment) GetTimestamp() *google_protobuf.Timestamp {
if m != nil {
return m.Timestamp
@ -111,6 +146,13 @@ func (m *Silence) String() string { return proto.CompactTextString(m)
func (*Silence) ProtoMessage() {}
func (*Silence) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
func (m *Silence) GetId() string {
if m != nil {
return m.Id
}
return ""
}
func (m *Silence) GetMatchers() []*Matcher {
if m != nil {
return m.Matchers
@ -180,32 +222,32 @@ func init() {
proto.RegisterEnum("silencepb.Matcher_Type", Matcher_Type_name, Matcher_Type_value)
}
func init() { proto.RegisterFile("silence/silencepb/silence.proto", fileDescriptor0) }
func init() { proto.RegisterFile("silence.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 372 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x91, 0xcf, 0x4b, 0xeb, 0x40,
0x10, 0xc7, 0x5f, 0xd2, 0x34, 0x69, 0xa6, 0x50, 0xca, 0x1e, 0xde, 0x0b, 0x85, 0x47, 0x25, 0x27,
0x41, 0xd9, 0x42, 0x7b, 0x50, 0x8f, 0x45, 0x8a, 0x17, 0x0b, 0xba, 0x56, 0xf0, 0x26, 0x69, 0x33,
0xb6, 0x81, 0xe6, 0x07, 0xc9, 0x44, 0xf4, 0xec, 0x7f, 0xe2, 0x5f, 0xea, 0x66, 0xb3, 0x89, 0x48,
0x0f, 0xf5, 0x94, 0x99, 0xcc, 0xe7, 0x3b, 0x33, 0xdf, 0x59, 0x18, 0x17, 0xd1, 0x1e, 0x93, 0x0d,
0x4e, 0xf4, 0x37, 0x5b, 0x37, 0x11, 0xcf, 0xf2, 0x94, 0x52, 0xe6, 0xb6, 0x85, 0xd1, 0x78, 0x9b,
0xa6, 0xdb, 0x3d, 0x4e, 0x54, 0x61, 0x5d, 0xbe, 0x4c, 0x28, 0x8a, 0xb1, 0xa0, 0x20, 0xce, 0x6a,
0xd6, 0xff, 0x30, 0xc0, 0x59, 0x06, 0xb4, 0xd9, 0x61, 0xce, 0xce, 0xc0, 0xa2, 0xf7, 0x0c, 0x3d,
0xe3, 0xc4, 0x38, 0x1d, 0x4c, 0xff, 0xf1, 0xb6, 0x0d, 0xd7, 0x04, 0x5f, 0xc9, 0xb2, 0x50, 0x10,
0x63, 0x60, 0x25, 0x41, 0x8c, 0x9e, 0x29, 0x61, 0x57, 0xa8, 0x98, 0x79, 0xe0, 0x64, 0x01, 0x11,
0xe6, 0x89, 0xd7, 0x51, 0xbf, 0x9b, 0xd4, 0xff, 0x0f, 0x56, 0xa5, 0x65, 0x2e, 0x74, 0x17, 0xf7,
0x8f, 0xf3, 0xdb, 0xe1, 0x1f, 0x06, 0x60, 0x8b, 0xc5, 0xcd, 0xe2, 0xe9, 0x6e, 0x68, 0xf8, 0x25,
0x38, 0xd7, 0x69, 0x1c, 0x63, 0x42, 0xec, 0x2f, 0xd8, 0x41, 0x49, 0xbb, 0x34, 0x57, 0x6b, 0xb8,
0x42, 0x67, 0x55, 0xef, 0x4d, 0x8d, 0xe8, 0x91, 0x4d, 0xca, 0x2e, 0xc1, 0x6d, 0x5d, 0xa9, 0xb9,
0xfd, 0xe9, 0x88, 0xd7, 0xbe, 0x79, 0xe3, 0x9b, 0xaf, 0x1a, 0x42, 0x7c, 0xc3, 0xfe, 0xa7, 0x09,
0xce, 0x43, 0x6d, 0x92, 0x0d, 0xc0, 0x8c, 0x42, 0x3d, 0x53, 0x46, 0x8c, 0x43, 0x2f, 0xae, 0x5d,
0x17, 0x72, 0x60, 0x47, 0x36, 0x65, 0x87, 0x07, 0x11, 0x2d, 0xc3, 0x2e, 0xc0, 0x95, 0x4d, 0x73,
0x2a, 0x9e, 0x03, 0xfa, 0xc5, 0x16, 0xbd, 0x1a, 0x9e, 0x13, 0x9b, 0x81, 0x83, 0x49, 0xa8, 0x64,
0xd6, 0x51, 0x99, 0x5d, 0xa1, 0x52, 0x74, 0x05, 0x50, 0x66, 0x61, 0x40, 0x18, 0x56, 0xba, 0xee,
0x71, 0xd3, 0x9a, 0x96, 0x52, 0x69, 0x4c, 0x5f, 0xae, 0xf0, 0x9c, 0x03, 0x63, 0xfa, 0x19, 0x44,
0xcb, 0xf8, 0xaf, 0xd0, 0x5f, 0x62, 0xb1, 0x6b, 0xee, 0x74, 0x0e, 0x8e, 0xa6, 0xd5, 0xb1, 0x7e,
0xaa, 0x35, 0x24, 0x1a, 0xa4, 0xda, 0x13, 0xdf, 0xb2, 0x28, 0x47, 0xe5, 0xcf, 0x3c, 0xbe, 0xa7,
0xa6, 0xe7, 0xb4, 0xb6, 0x55, 0x79, 0xf6, 0x15, 0x00, 0x00, 0xff, 0xff, 0x7d, 0xe9, 0xc3, 0x5f,
0xef, 0x02, 0x00, 0x00,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x91, 0xc1, 0x6a, 0xdb, 0x40,
0x10, 0x86, 0x2b, 0x59, 0x96, 0xac, 0x31, 0x35, 0x66, 0x0f, 0xad, 0x30, 0x94, 0x1a, 0x9d, 0x0c,
0x2d, 0x6b, 0xb0, 0x0f, 0x6d, 0x8f, 0xa2, 0x98, 0x5e, 0x6a, 0x48, 0x36, 0x0e, 0xe4, 0x16, 0xd6,
0xd6, 0xc4, 0x16, 0x58, 0xd2, 0x22, 0x8d, 0x42, 0x7c, 0xce, 0x9b, 0xe4, 0x49, 0x83, 0x56, 0x2b,
0x85, 0xe0, 0x83, 0x73, 0xd3, 0x68, 0xbf, 0x7f, 0x67, 0xbe, 0x59, 0xf8, 0x5c, 0x26, 0x47, 0xcc,
0x76, 0xc8, 0x55, 0x91, 0x53, 0xce, 0x7c, 0x53, 0xaa, 0xed, 0xe4, 0xfb, 0x3e, 0xcf, 0xf7, 0x47,
0x9c, 0xeb, 0x83, 0x6d, 0xf5, 0x30, 0xa7, 0x24, 0xc5, 0x92, 0x64, 0xaa, 0x1a, 0x36, 0x7c, 0xb6,
0xc0, 0x5b, 0x4b, 0xda, 0x1d, 0xb0, 0x60, 0x3f, 0xc0, 0xa1, 0x93, 0xc2, 0xc0, 0x9a, 0x5a, 0xb3,
0xd1, 0xe2, 0x2b, 0xef, 0xae, 0xe1, 0x86, 0xe0, 0x9b, 0x93, 0x42, 0xa1, 0x21, 0xc6, 0xc0, 0xc9,
0x64, 0x8a, 0x81, 0x3d, 0xb5, 0x66, 0xbe, 0xd0, 0xdf, 0x2c, 0x00, 0x4f, 0x49, 0x22, 0x2c, 0xb2,
0xa0, 0xa7, 0x7f, 0xb7, 0x65, 0xf8, 0x0d, 0x9c, 0x3a, 0xcb, 0x7c, 0xe8, 0xaf, 0xae, 0x6f, 0xa3,
0xff, 0xe3, 0x4f, 0x0c, 0xc0, 0x15, 0xab, 0x7f, 0xab, 0xbb, 0xab, 0xb1, 0x15, 0x56, 0xe0, 0xfd,
0xcd, 0xd3, 0x14, 0x33, 0x62, 0x5f, 0xc0, 0x95, 0x15, 0x1d, 0xf2, 0x42, 0x8f, 0xe1, 0x0b, 0x53,
0xd5, 0x77, 0xef, 0x1a, 0xc4, 0xb4, 0x6c, 0x4b, 0xf6, 0x1b, 0xfc, 0xce, 0x4a, 0xf7, 0x1d, 0x2e,
0x26, 0xbc, 0xf1, 0xe6, 0xad, 0x37, 0xdf, 0xb4, 0x84, 0x78, 0x83, 0xc3, 0x17, 0x1b, 0xbc, 0x9b,
0x46, 0x92, 0x8d, 0xc0, 0x4e, 0x62, 0xd3, 0xd3, 0x4e, 0x62, 0xc6, 0x61, 0x90, 0x36, 0xd6, 0x65,
0x60, 0x4f, 0x7b, 0xb3, 0xe1, 0x82, 0x9d, 0x2f, 0x44, 0x74, 0x0c, 0xfb, 0x05, 0x7e, 0x49, 0xb2,
0xa0, 0xf2, 0x5e, 0xd2, 0x07, 0xa6, 0x18, 0x34, 0x70, 0x44, 0x6c, 0x09, 0x1e, 0x66, 0xb1, 0x8e,
0x39, 0x17, 0x63, 0x6e, 0x8d, 0x46, 0xc4, 0xfe, 0x00, 0x54, 0x2a, 0x96, 0x84, 0x71, 0x9d, 0xeb,
0x5f, 0x96, 0x36, 0x74, 0x44, 0xb5, 0x98, 0xd9, 0x5c, 0x19, 0x78, 0x67, 0x62, 0xe6, 0x19, 0x44,
0xc7, 0x84, 0x8f, 0x30, 0x5c, 0x63, 0x79, 0x68, 0xf7, 0xf4, 0x13, 0x3c, 0x43, 0xeb, 0x65, 0xbd,
0x4f, 0x1b, 0x48, 0xb4, 0x48, 0x3d, 0x27, 0x3e, 0xa9, 0xa4, 0x40, 0xed, 0x67, 0x5f, 0x9e, 0xd3,
0xd0, 0x11, 0x6d, 0x5d, 0x7d, 0xbc, 0x7c, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xd7, 0x1a, 0x8f, 0x20,
0xdd, 0x02, 0x00, 0x00,
}