diff --git a/chat/README.md b/chat/README.md new file mode 100644 index 0000000..7410db8 --- /dev/null +++ b/chat/README.md @@ -0,0 +1,40 @@ +# Chat Service + +The chat service is an example Micro service which leverages bidirectional streaming, the store and events to build a chat backend. There is both a server and client which can be run together to demonstrate the application (see client/main.go for more instructions on running the service). + +The service is documented inline and is designed to act as a reference for the events package. + +### Calling the service + +You can call the service via the CLI: + +Create a chat: +```bash +> micro chat new --user_ids=JohnBarry +{ + "chat_id": "3c9ea66c-d516-45d4-abe8-082089e18b27" +} +``` + +Send a message to the chat: +```bash +> micro chat send --chat_id=bed4f0f0-da12-46d2-90d2-17ae1714a214 --user_id=John --subject=Hello --text='Hey Barry' +{} +``` + +View the chat history +```bash +> micro chat history --chat_id=bed4f0f0-da12-46d2-90d2-17ae1714a214 +{ + "messages": [ + { + "id": "a61284a8-f471-4734-9192-640d89762e98", + "client_id": "6ba0d2a6-96fa-47d8-8f6f-7f75b4cc8b3e", + "chat_id": "bed4f0f0-da12-46d2-90d2-17ae1714a214", + "user_id": "John", + "subject": "Hello", + "text": "Hey Barry" + } + ] +} +``` \ No newline at end of file diff --git a/chat/client/main.go b/chat/client/main.go new file mode 100644 index 0000000..9091b34 --- /dev/null +++ b/chat/client/main.go @@ -0,0 +1,121 @@ +// Package main is a client for the chat service to demonstrate how it would work for a client. To +// run the client, first launch the chat service by running `micro run ./chat` from the top level of +// this repo. Then run `micro run ./chat/client` and `micro logs -f client` to follow the logs of +// the client. +package main + +import ( + "context" + "fmt" + "time" + + "github.com/google/uuid" + "github.com/micro/micro/v3/service" + "github.com/micro/micro/v3/service/context/metadata" + "github.com/micro/micro/v3/service/logger" + chat "github.com/micro/services/chat/proto" +) + +var ( + userOneID = "user-one-" + uuid.New().String() + userTwoID = "user-two-" + uuid.New().String() +) + +func main() { + // create a chat service client + srv := service.New() + cli := chat.NewChatService("chat", srv.Client()) + + // create a chat for our users + userIDs := []string{userOneID, userTwoID} + nRsp, err := cli.New(context.TODO(), &chat.NewRequest{UserIds: userIDs}) + if err != nil { + logger.Fatalf("Error creating the chat: %v", err) + } + chatID := nRsp.GetChatId() + logger.Infof("Chat Created. ID: %v", chatID) + + // list the number messages in the chat history + hRsp, err := cli.History(context.TODO(), &chat.HistoryRequest{ChatId: chatID}) + if err != nil { + logger.Fatalf("Error getting the chat history: %v", err) + } + logger.Infof("Chat has %v message(s)", len(hRsp.Messages)) + + // create a channel to handle errors + errChan := make(chan error) + + // run user one + go func() { + ctx := metadata.NewContext(context.TODO(), metadata.Metadata{ + "user-id": userOneID, "chat-id": chatID, + }) + stream, err := cli.Connect(ctx) + if err != nil { + errChan <- err + return + } + + for i := 1; true; i++ { + // send a message to the chat + err = stream.Send(&chat.Message{ + ClientId: uuid.New().String(), + SentAt: time.Now().Unix(), + Subject: "Message from user one", + Text: fmt.Sprintf("Message #%v", i), + }) + if err != nil { + errChan <- err + return + } + logger.Infof("User one sent message") + + // wait for user two to respond + msg, err := stream.Recv() + if err != nil { + errChan <- err + return + } + logger.Infof("User one recieved message %v from %v", msg.Text, msg.UserId) + time.Sleep(time.Second) + } + }() + + // run user two + go func() { + ctx := metadata.NewContext(context.TODO(), metadata.Metadata{ + "user-id": userTwoID, "chat-id": chatID, + }) + stream, err := cli.Connect(ctx) + if err != nil { + errChan <- err + return + } + + for i := 1; true; i++ { + // send a response to the chat + err = stream.Send(&chat.Message{ + ClientId: uuid.New().String(), + SentAt: time.Now().Unix(), + Subject: "Response from user two", + Text: fmt.Sprintf("Response #%v", i), + }) + if err != nil { + errChan <- err + return + } + logger.Infof("User two sent message") + + // wait for a message from user one + msg, err := stream.Recv() + if err != nil { + errChan <- err + return + } + logger.Infof("User two recieved message %v from %v", msg.Text, msg.UserId) + time.Sleep(time.Second) + } + }() + + logger.Fatal(<-errChan) +} diff --git a/chat/handler/handler.go b/chat/handler/handler.go new file mode 100644 index 0000000..5d46153 --- /dev/null +++ b/chat/handler/handler.go @@ -0,0 +1,305 @@ +package handler + +import ( + "context" + "sort" + "strings" + + "github.com/google/uuid" + "github.com/micro/micro/v3/service/context/metadata" + "github.com/micro/micro/v3/service/errors" + "github.com/micro/micro/v3/service/events" + "github.com/micro/micro/v3/service/logger" + "github.com/micro/micro/v3/service/store" + + // it's standard to import the services own proto under the alias pb + pb "github.com/micro/services/chat/proto" +) + +// New returns an initialized chat handler +func New() pb.ChatHandler { + return new(handler) +} + +const ( + chatStoreKeyPrefix = "chats/" + chatEventKeyPrefix = "chats/" + messageStoreKeyPrefix = "messages/" +) + +// handler satisfies the ChatHandler interface. You can see this inteface defined in chat.pb.micro.go +type handler struct{} + +// New creates a chat for a group of users. The RPC is idempotent so if it's called multiple times +// for the same users, the same response will be returned. It's good practice to design APIs as +// idempotent since this enables safe retries. +func (h *handler) New(ctx context.Context, req *pb.NewRequest, rsp *pb.NewResponse) error { + // in a real world application we would authorize the request to ensure the authenticated user + // is part of the chat they're attempting to create. We could do this by getting the user id from + // auth.AccountFromContext(ctx) and then validating the presence of their id in req.UserIds. If + // the user is not part of the request then we'd return a Forbidden error, which the micro api + // would transform to a 403 status code. + + // validate the request + if len(req.UserIds) == 0 { + // Return a bad request error to the client, the first argument is a unique id which the client + // can check for. The second argument is a human readable description. Returning the correct type + // of error is important as it's used by the network to know if a request should be retried. Only + // 500 (InternalServerError) and 408 (Timeout) errors are retried. + return errors.BadRequest("chat.New.MissingUserIDs", "One or more user IDs are required") + } + + // construct a key to identify the chat, we'll do this by sorting the user ids alphabetically and + // then joining them. When a service calls the store, the data returned will be automatically scoped + // to the service however it's still advised to use a prefix when writing data since this allows + // other types of keys to be written in the future. We'll make a copy of the req.UserIds object as + // it's a good practice to not mutate the request object. + sortedIDs := make([]string, len(req.UserIds)) + copy(sortedIDs, req.UserIds) + sort.Strings(sortedIDs) + + // key to lookup the chat in the store using, e.g. "chat/usera-userb-userc" + key := chatStoreKeyPrefix + strings.Join(sortedIDs, "-") + + // read from the store to check if a chat with these users already exists + recs, err := store.Read(key) + if err == nil { + // if an error wasn't returned, at least one record was found. The value returned by the store + // is the bytes representation of the chat id. We'll convert this back into a string and return + // it to the client. + rsp.ChatId = string(recs[0].Value) + return nil + } else if err != store.ErrNotFound { + // if no records were found then we'd expect to get a store.ErrNotFound error returned. If this + // wasn't the case, the service could've experienced an issue connecting to the store so we should + // log the error and return an InternalServerError to the client, indicating the request should + // be retried + logger.Errorf("Error reading from the store. Key: %v. Error: %v", key, err) + return errors.InternalServerError("chat.New.Unknown", "Error reading from the store") + } + + // no chat id was returned so we'll generate one, write it to the store and then return it to the + // client + chatID := uuid.New().String() + record := store.Record{Key: chatStoreKeyPrefix + chatID, Value: []byte(chatID)} + if err := store.Write(&record); err != nil { + logger.Errorf("Error writing to the store. Key: %v. Error: %v", record.Key, err) + return errors.InternalServerError("chat.New.Unknown", "Error writing to the store") + } + + // The chat was successfully created so we'll log the event and then return the id to the client. + // Note that we'll use logger.Infof here vs the Errorf above. + logger.Infof("New chat created with ID %v", chatID) + rsp.ChatId = chatID + return nil +} + +// History returns the historical messages in a chat +func (h *handler) History(ctx context.Context, req *pb.HistoryRequest, rsp *pb.HistoryResponse) error { + // as per the New function, in a real world application we would authorize the request to ensure + // the authenticated user is part of the chat they're attempting to read the history of + + // validate the request + if len(req.ChatId) == 0 { + return errors.BadRequest("chat.History.MissingChatID", "ChatID is missing") + } + + // lookup the chat from the store to ensure it's valid + if _, err := store.Read(chatStoreKeyPrefix + req.ChatId); err == store.ErrNotFound { + return errors.BadRequest("chat.History.InvalidChatID", "Chat not found with this ID") + } else if err != nil { + logger.Errorf("Error reading from the store. Chat ID: %v. Error: %v", req.ChatId, err) + return errors.InternalServerError("chat.History.Unknown", "Error reading from the store") + } + + // lookup the historical messages for the chat using the event store. lots of packages in micro + // support options, in this case we'll pass the ReadLimit option to restrict the number of messages + // we'll load from the events store. + messages, err := events.Read(chatEventKeyPrefix+req.ChatId, events.ReadLimit(50)) + if err != nil { + logger.Errorf("Error reading from the event store. Chat ID: %v. Error: %v", req.ChatId, err) + return errors.InternalServerError("chat.History.Unknown", "Error reading from the event store") + } + + // we've loaded the messages from the event store. next we need to serialize them and return them + // to the client. The message is stored in the event payload, to retrieve it we need to unmarshal + // the event into a message struct. + rsp.Messages = make([]*pb.Message, len(messages)) + for i, ev := range messages { + var msg pb.Message + if err := ev.Unmarshal(&msg); err != nil { + logger.Errorf("Error unmarshaling event: %v", err) + return errors.InternalServerError("chat.History.Unknown", "Error unmarshaling event") + } + rsp.Messages[i] = &msg + } + + return nil +} + +// Send a single message to the chat, designed for ease of use via the API / CLI +func (h *handler) Send(ctx context.Context, req *pb.SendRequest, rsp *pb.SendResponse) error { + // validate the request + if len(req.ChatId) == 0 { + return errors.BadRequest("chat.Send.MissingChatID", "ChatID is missing") + } + if len(req.UserId) == 0 { + return errors.BadRequest("chat.Send.MissingUserID", "UserID is missing") + } + if len(req.Text) == 0 { + return errors.BadRequest("chat.Send.MissingText", "Text is missing") + } + + // construct the message + msg := &pb.Message{ + Id: uuid.New().String(), + ClientId: req.ClientId, + ChatId: req.ChatId, + UserId: req.UserId, + Subject: req.Subject, + Text: req.Text, + } + + // default the client id if not provided + if len(msg.ClientId) == 0 { + msg.ClientId = uuid.New().String() + } + + // create the message + return h.createMessage(msg) +} + +// Connect to a chat using a bidirectional stream enabling the client to send and recieve messages +// over a single RPC. When a message is sent on the stream, it will be added to the chat history +// and sent to the other connected users. When opening the connection, the client should provide +// the chat_id and user_id in the context so the server knows which messages to stream. +func (h *handler) Connect(ctx context.Context, stream pb.Chat_ConnectStream) error { + // the client passed the chat id and user id in the request context. we'll load that information + // now and validate it. If any information is missing we'll return a BadRequest error to the client + userID, ok := metadata.Get(ctx, "user-id") + if !ok { + return errors.BadRequest("chat.Connect.MissingUserID", "UserID missing in context") + } + chatID, ok := metadata.Get(ctx, "chat-id") + if !ok { + return errors.BadRequest("chat.Connect.MissingChatID", "ChatId missing in context") + } + + // lookup the chat from the store to ensure it's valid + if _, err := store.Read(chatStoreKeyPrefix + chatID); err == store.ErrNotFound { + return errors.BadRequest("chat.Connect.InvalidChatID", "Chat not found with this ID") + } else if err != nil { + logger.Errorf("Error reading from the store. Chat ID: %v. Error: %v", chatID, err) + return errors.InternalServerError("chat.Connect.Unknown", "Error reading from the store") + } + + // as per the New and Connect functions, at this point in a real world application we would + // authorize the request to ensure the authenticated user is part of the chat they're attempting + // to read the history of + + // create a new context which can be cancelled, in the case either the consumer of publisher errors + // we don't want one to keep running in the background + cancelCtx, cancel := context.WithCancel(ctx) + defer cancel() + + // create a channel to send errors on, because the subscriber / publisher will run in seperate go- + // routines, they need a way of returning errors to the client + errChan := make(chan error) + + // create an event stream to consume messages posted by other users into the chat. we'll use the + // user id as a queue to ensure each user recieves the message + evStream, err := events.Subscribe(chatEventKeyPrefix+chatID, events.WithQueue(userID)) + if err != nil { + logger.Errorf("Error streaming events. Chat ID: %v. Error: %v", chatID, err) + return errors.InternalServerError("chat.Connect.Unknown", "Error connecting to the event stream") + } + go func() { + for { + select { + case <-cancelCtx.Done(): + // the context has been cancelled or timed out, stop subscribing to new messages + return + case ev := <-evStream: + // recieved a message, unmarshal it into a message struct. if an error occurs log it and + // cancel the context + var msg pb.Message + if err := ev.Unmarshal(&msg); err != nil { + logger.Errorf("Error unmarshaling message. ChatID: %v. Error: %v", chatID, err) + errChan <- err + return + } + + // ignore any messages published by the current user + if msg.UserId == userID { + continue + } + + // publish the message to the stream + if err := stream.Send(&msg); err != nil { + logger.Errorf("Error sending message to stream. ChatID: %v. Message ID: %v. Error: %v", chatID, msg.Id, err) + errChan <- err + return + } + } + } + }() + + // transform the stream.Recv into a channel which can be used in the select statement below + msgChan := make(chan *pb.Message) + go func() { + for { + msg, err := stream.Recv() + if err != nil { + errChan <- err + close(msgChan) + return + } + msgChan <- msg + } + }() + + for { + select { + case <-cancelCtx.Done(): + // the context has been cancelled or timed out, stop subscribing to new messages + return nil + case err := <-errChan: + // an error occured in another goroutine, terminate the stream + return err + case msg := <-msgChan: + // set the defaults + msg.UserId = userID + msg.ChatId = chatID + + // create the message + if err := h.createMessage(msg); err != nil { + return err + } + } + } +} + +// createMessage is a helper function which creates a message in the event stream. It handles the +// logic for ensuring client id is unique. +func (h *handler) createMessage(msg *pb.Message) error { + // a message was recieved from the client. validate it hasn't been recieved before + if _, err := store.Read(messageStoreKeyPrefix + msg.ClientId); err == nil { + // the message has already been processed + return nil + } else if err != store.ErrNotFound { + // an unexpected error occured + return err + } + + // send the message to the event stream + if err := events.Publish(chatEventKeyPrefix+msg.ChatId, msg); err != nil { + return err + } + + // record the messages client id + if err := store.Write(&store.Record{Key: messageStoreKeyPrefix + msg.ClientId}); err != nil { + return err + } + + return nil +} diff --git a/chat/main.go b/chat/main.go new file mode 100644 index 0000000..46d608b --- /dev/null +++ b/chat/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/micro/micro/v3/service" + "github.com/micro/micro/v3/service/logger" + + "github.com/micro/services/chat/handler" + pb "github.com/micro/services/chat/proto" +) + +func main() { + // Create the service + srv := service.New( + service.Name("chat"), + service.Version("latest"), + ) + + // Register the handler against the server + pb.RegisterChatHandler(srv.Server(), handler.New()) + + // Run the service + if err := srv.Run(); err != nil { + logger.Fatal(err) + } +} diff --git a/chat/proto/chat.pb.go b/chat/proto/chat.pb.go new file mode 100644 index 0000000..cca8e16 --- /dev/null +++ b/chat/proto/chat.pb.go @@ -0,0 +1,427 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: chat/proto/chat.proto + +package chat + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// 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.ProtoPackageIsVersion3 // please upgrade the proto package + +// NewRequest contains the infromation needed to create a new chat +type NewRequest struct { + UserIds []string `protobuf:"bytes,1,rep,name=user_ids,json=userIds,proto3" json:"user_ids,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NewRequest) Reset() { *m = NewRequest{} } +func (m *NewRequest) String() string { return proto.CompactTextString(m) } +func (*NewRequest) ProtoMessage() {} +func (*NewRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_825b1469f80f958d, []int{0} +} + +func (m *NewRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NewRequest.Unmarshal(m, b) +} +func (m *NewRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NewRequest.Marshal(b, m, deterministic) +} +func (m *NewRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_NewRequest.Merge(m, src) +} +func (m *NewRequest) XXX_Size() int { + return xxx_messageInfo_NewRequest.Size(m) +} +func (m *NewRequest) XXX_DiscardUnknown() { + xxx_messageInfo_NewRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_NewRequest proto.InternalMessageInfo + +func (m *NewRequest) GetUserIds() []string { + if m != nil { + return m.UserIds + } + return nil +} + +// NewResponse contains the chat id for the users +type NewResponse struct { + ChatId string `protobuf:"bytes,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NewResponse) Reset() { *m = NewResponse{} } +func (m *NewResponse) String() string { return proto.CompactTextString(m) } +func (*NewResponse) ProtoMessage() {} +func (*NewResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_825b1469f80f958d, []int{1} +} + +func (m *NewResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NewResponse.Unmarshal(m, b) +} +func (m *NewResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NewResponse.Marshal(b, m, deterministic) +} +func (m *NewResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_NewResponse.Merge(m, src) +} +func (m *NewResponse) XXX_Size() int { + return xxx_messageInfo_NewResponse.Size(m) +} +func (m *NewResponse) XXX_DiscardUnknown() { + xxx_messageInfo_NewResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_NewResponse proto.InternalMessageInfo + +func (m *NewResponse) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +// HistoryRequest contains the id of the chat we want the history for. This RPC will return all +// historical messages, however in a real life application we'd introduce some form of pagination +// here, only loading the older messages when required. +type HistoryRequest struct { + ChatId string `protobuf:"bytes,1,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HistoryRequest) Reset() { *m = HistoryRequest{} } +func (m *HistoryRequest) String() string { return proto.CompactTextString(m) } +func (*HistoryRequest) ProtoMessage() {} +func (*HistoryRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_825b1469f80f958d, []int{2} +} + +func (m *HistoryRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HistoryRequest.Unmarshal(m, b) +} +func (m *HistoryRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HistoryRequest.Marshal(b, m, deterministic) +} +func (m *HistoryRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_HistoryRequest.Merge(m, src) +} +func (m *HistoryRequest) XXX_Size() int { + return xxx_messageInfo_HistoryRequest.Size(m) +} +func (m *HistoryRequest) XXX_DiscardUnknown() { + xxx_messageInfo_HistoryRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_HistoryRequest proto.InternalMessageInfo + +func (m *HistoryRequest) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +// HistoryResponse contains the historical messages in a chat +type HistoryResponse struct { + Messages []*Message `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HistoryResponse) Reset() { *m = HistoryResponse{} } +func (m *HistoryResponse) String() string { return proto.CompactTextString(m) } +func (*HistoryResponse) ProtoMessage() {} +func (*HistoryResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_825b1469f80f958d, []int{3} +} + +func (m *HistoryResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HistoryResponse.Unmarshal(m, b) +} +func (m *HistoryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HistoryResponse.Marshal(b, m, deterministic) +} +func (m *HistoryResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_HistoryResponse.Merge(m, src) +} +func (m *HistoryResponse) XXX_Size() int { + return xxx_messageInfo_HistoryResponse.Size(m) +} +func (m *HistoryResponse) XXX_DiscardUnknown() { + xxx_messageInfo_HistoryResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_HistoryResponse proto.InternalMessageInfo + +func (m *HistoryResponse) GetMessages() []*Message { + if m != nil { + return m.Messages + } + return nil +} + +// SendRequest contains a single message to send to a chat +type SendRequest struct { + // a client side id, should be validated by the server to make the request retry safe + ClientId string `protobuf:"bytes,1,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + // id of the chat the message is being sent to / from + ChatId string `protobuf:"bytes,2,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + // id of the user who sent the message + UserId string `protobuf:"bytes,3,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + // subject of the message + Subject string `protobuf:"bytes,4,opt,name=subject,proto3" json:"subject,omitempty"` + // text of the message + Text string `protobuf:"bytes,5,opt,name=text,proto3" json:"text,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SendRequest) Reset() { *m = SendRequest{} } +func (m *SendRequest) String() string { return proto.CompactTextString(m) } +func (*SendRequest) ProtoMessage() {} +func (*SendRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_825b1469f80f958d, []int{4} +} + +func (m *SendRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SendRequest.Unmarshal(m, b) +} +func (m *SendRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SendRequest.Marshal(b, m, deterministic) +} +func (m *SendRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_SendRequest.Merge(m, src) +} +func (m *SendRequest) XXX_Size() int { + return xxx_messageInfo_SendRequest.Size(m) +} +func (m *SendRequest) XXX_DiscardUnknown() { + xxx_messageInfo_SendRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_SendRequest proto.InternalMessageInfo + +func (m *SendRequest) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *SendRequest) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +func (m *SendRequest) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *SendRequest) GetSubject() string { + if m != nil { + return m.Subject + } + return "" +} + +func (m *SendRequest) GetText() string { + if m != nil { + return m.Text + } + return "" +} + +// SendResponse is a blank message returned when a message is successfully created +type SendResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SendResponse) Reset() { *m = SendResponse{} } +func (m *SendResponse) String() string { return proto.CompactTextString(m) } +func (*SendResponse) ProtoMessage() {} +func (*SendResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_825b1469f80f958d, []int{5} +} + +func (m *SendResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_SendResponse.Unmarshal(m, b) +} +func (m *SendResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_SendResponse.Marshal(b, m, deterministic) +} +func (m *SendResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_SendResponse.Merge(m, src) +} +func (m *SendResponse) XXX_Size() int { + return xxx_messageInfo_SendResponse.Size(m) +} +func (m *SendResponse) XXX_DiscardUnknown() { + xxx_messageInfo_SendResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_SendResponse proto.InternalMessageInfo + +// Message sent to a chat +type Message struct { + // id of the message, allocated by the server + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // a client side id, should be validated by the server to make the request retry safe + ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"` + // id of the chat the message is being sent to / from + ChatId string `protobuf:"bytes,3,opt,name=chat_id,json=chatId,proto3" json:"chat_id,omitempty"` + // id of the user who sent the message + UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + // time time the message was sent in unix format + SentAt int64 `protobuf:"varint,5,opt,name=sent_at,json=sentAt,proto3" json:"sent_at,omitempty"` + // subject of the message + Subject string `protobuf:"bytes,6,opt,name=subject,proto3" json:"subject,omitempty"` + // text of the message + Text string `protobuf:"bytes,7,opt,name=text,proto3" json:"text,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} +func (*Message) Descriptor() ([]byte, []int) { + return fileDescriptor_825b1469f80f958d, []int{6} +} + +func (m *Message) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Message.Unmarshal(m, b) +} +func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Message.Marshal(b, m, deterministic) +} +func (m *Message) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message.Merge(m, src) +} +func (m *Message) XXX_Size() int { + return xxx_messageInfo_Message.Size(m) +} +func (m *Message) XXX_DiscardUnknown() { + xxx_messageInfo_Message.DiscardUnknown(m) +} + +var xxx_messageInfo_Message proto.InternalMessageInfo + +func (m *Message) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Message) GetClientId() string { + if m != nil { + return m.ClientId + } + return "" +} + +func (m *Message) GetChatId() string { + if m != nil { + return m.ChatId + } + return "" +} + +func (m *Message) GetUserId() string { + if m != nil { + return m.UserId + } + return "" +} + +func (m *Message) GetSentAt() int64 { + if m != nil { + return m.SentAt + } + return 0 +} + +func (m *Message) GetSubject() string { + if m != nil { + return m.Subject + } + return "" +} + +func (m *Message) GetText() string { + if m != nil { + return m.Text + } + return "" +} + +func init() { + proto.RegisterType((*NewRequest)(nil), "chat.NewRequest") + proto.RegisterType((*NewResponse)(nil), "chat.NewResponse") + proto.RegisterType((*HistoryRequest)(nil), "chat.HistoryRequest") + proto.RegisterType((*HistoryResponse)(nil), "chat.HistoryResponse") + proto.RegisterType((*SendRequest)(nil), "chat.SendRequest") + proto.RegisterType((*SendResponse)(nil), "chat.SendResponse") + proto.RegisterType((*Message)(nil), "chat.Message") +} + +func init() { proto.RegisterFile("chat/proto/chat.proto", fileDescriptor_825b1469f80f958d) } + +var fileDescriptor_825b1469f80f958d = []byte{ + // 370 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xcf, 0x4e, 0x3a, 0x31, + 0x10, 0xc7, 0xd3, 0xdd, 0xfd, 0x6d, 0x61, 0xf8, 0x89, 0xda, 0x48, 0x58, 0xd7, 0x0b, 0xe9, 0x41, + 0x41, 0x23, 0x18, 0x4c, 0x3c, 0x79, 0x31, 0x5c, 0xe4, 0x20, 0x87, 0xf5, 0x01, 0xc8, 0x42, 0x1b, + 0x59, 0xa3, 0xbb, 0x48, 0x4b, 0xd0, 0x57, 0xf0, 0x65, 0x7c, 0x0b, 0x9f, 0xcb, 0xf4, 0x0f, 0xd0, + 0x35, 0x72, 0x9b, 0xf9, 0xce, 0x74, 0xfa, 0xf9, 0x76, 0x0a, 0x8d, 0xe9, 0x2c, 0x95, 0xbd, 0xf9, + 0xa2, 0x90, 0x45, 0x4f, 0x85, 0x5d, 0x1d, 0x92, 0x40, 0xc5, 0xf4, 0x0c, 0x60, 0xc4, 0x57, 0x09, + 0x7f, 0x5b, 0x72, 0x21, 0xc9, 0x31, 0x54, 0x96, 0x82, 0x2f, 0xc6, 0x19, 0x13, 0x11, 0x6a, 0xf9, + 0xed, 0x6a, 0x82, 0x55, 0x3e, 0x64, 0x82, 0x9e, 0x42, 0x4d, 0x37, 0x8a, 0x79, 0x91, 0x0b, 0x4e, + 0x9a, 0x80, 0xd5, 0xf9, 0x71, 0xc6, 0x22, 0xd4, 0x42, 0xed, 0x6a, 0x12, 0xaa, 0x74, 0xc8, 0x68, + 0x07, 0xea, 0xf7, 0x99, 0x90, 0xc5, 0xe2, 0x63, 0x3d, 0x74, 0x67, 0xeb, 0x2d, 0xec, 0x6f, 0x5a, + 0xed, 0xd8, 0x0e, 0x54, 0x5e, 0xb9, 0x10, 0xe9, 0x13, 0x37, 0x00, 0xb5, 0xfe, 0x5e, 0x57, 0x33, + 0x3f, 0x18, 0x35, 0xd9, 0x94, 0xe9, 0x27, 0x82, 0xda, 0x23, 0xcf, 0xd9, 0xfa, 0x9a, 0x13, 0xa8, + 0x4e, 0x5f, 0x32, 0x9e, 0x3b, 0x17, 0x55, 0x8c, 0x30, 0x64, 0x2e, 0x83, 0xe7, 0x32, 0xa8, 0x82, + 0x75, 0x1c, 0xf9, 0xa6, 0x60, 0x0c, 0x93, 0x08, 0xb0, 0x58, 0x4e, 0x9e, 0xf9, 0x54, 0x46, 0x81, + 0x2e, 0xac, 0x53, 0x42, 0x20, 0x90, 0xfc, 0x5d, 0x46, 0xff, 0xb4, 0xac, 0x63, 0x5a, 0x87, 0xff, + 0x86, 0xc5, 0xf8, 0xa0, 0x5f, 0x08, 0xb0, 0x45, 0x26, 0x75, 0xf0, 0x36, 0x44, 0x5e, 0xc6, 0xca, + 0xa0, 0xde, 0x6e, 0x50, 0x7f, 0x17, 0x68, 0x50, 0x02, 0x6d, 0x02, 0x16, 0x6a, 0x58, 0x6a, 0x88, + 0xfc, 0x24, 0x54, 0xe9, 0x9d, 0x74, 0x1d, 0x84, 0x7f, 0x3b, 0xc0, 0x5b, 0x07, 0xfd, 0x6f, 0x04, + 0xc1, 0x60, 0x96, 0x4a, 0x72, 0x0e, 0xfe, 0x88, 0xaf, 0xc8, 0x81, 0x79, 0xf7, 0xed, 0xe7, 0x88, + 0x0f, 0x1d, 0xc5, 0xae, 0xeb, 0x06, 0xb0, 0xdd, 0x20, 0x39, 0x32, 0xd5, 0xf2, 0xee, 0xe3, 0xc6, + 0x2f, 0xd5, 0x9e, 0xbb, 0x84, 0x40, 0x3d, 0x17, 0xb1, 0x23, 0x9d, 0x35, 0xc6, 0xc4, 0x95, 0x6c, + 0xfb, 0x05, 0xe0, 0x41, 0x91, 0xe7, 0x0a, 0xbd, 0xfc, 0x1d, 0xe2, 0x72, 0xda, 0x46, 0x57, 0x68, + 0x12, 0xea, 0xef, 0x7d, 0xfd, 0x13, 0x00, 0x00, 0xff, 0xff, 0x81, 0x53, 0x8a, 0xc9, 0xf7, 0x02, + 0x00, 0x00, +} diff --git a/chat/proto/chat.pb.micro.go b/chat/proto/chat.pb.micro.go new file mode 100644 index 0000000..5e6ec91 --- /dev/null +++ b/chat/proto/chat.pb.micro.go @@ -0,0 +1,244 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: chat/proto/chat.proto + +package chat + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +import ( + context "context" + api "github.com/micro/micro/v3/service/api" + client "github.com/micro/micro/v3/service/client" + server "github.com/micro/micro/v3/service/server" +) + +// 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.ProtoPackageIsVersion3 // please upgrade the proto package + +// Reference imports to suppress errors if they are not otherwise used. +var _ api.Endpoint +var _ context.Context +var _ client.Option +var _ server.Option + +// Api Endpoints for Chat service + +func NewChatEndpoints() []*api.Endpoint { + return []*api.Endpoint{} +} + +// Client API for Chat service + +type ChatService interface { + // New creates a chat for a group of users. The RPC is idempotent so if it's called multiple times + // for the same users, the same response will be returned. It's good practice to design APIs as + // idempotent since this enables safe retries. + New(ctx context.Context, in *NewRequest, opts ...client.CallOption) (*NewResponse, error) + // History returns the historical messages in a chat + History(ctx context.Context, in *HistoryRequest, opts ...client.CallOption) (*HistoryResponse, error) + // Send a single message to the chat + Send(ctx context.Context, in *SendRequest, opts ...client.CallOption) (*SendResponse, error) + // Connect to a chat using a bidirectional stream enabling the client to send and recieve messages + // over a single RPC. When a message is sent on the stream, it will be added to the chat history + // and sent to the other connected users. When opening the connection, the client should provide + // the chat_id and user_id in the context so the server knows which messages to stream. + Connect(ctx context.Context, opts ...client.CallOption) (Chat_ConnectService, error) +} + +type chatService struct { + c client.Client + name string +} + +func NewChatService(name string, c client.Client) ChatService { + return &chatService{ + c: c, + name: name, + } +} + +func (c *chatService) New(ctx context.Context, in *NewRequest, opts ...client.CallOption) (*NewResponse, error) { + req := c.c.NewRequest(c.name, "Chat.New", in) + out := new(NewResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *chatService) History(ctx context.Context, in *HistoryRequest, opts ...client.CallOption) (*HistoryResponse, error) { + req := c.c.NewRequest(c.name, "Chat.History", in) + out := new(HistoryResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *chatService) Send(ctx context.Context, in *SendRequest, opts ...client.CallOption) (*SendResponse, error) { + req := c.c.NewRequest(c.name, "Chat.Send", in) + out := new(SendResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *chatService) Connect(ctx context.Context, opts ...client.CallOption) (Chat_ConnectService, error) { + req := c.c.NewRequest(c.name, "Chat.Connect", &Message{}) + stream, err := c.c.Stream(ctx, req, opts...) + if err != nil { + return nil, err + } + return &chatServiceConnect{stream}, nil +} + +type Chat_ConnectService interface { + Context() context.Context + SendMsg(interface{}) error + RecvMsg(interface{}) error + Close() error + Send(*Message) error + Recv() (*Message, error) +} + +type chatServiceConnect struct { + stream client.Stream +} + +func (x *chatServiceConnect) Close() error { + return x.stream.Close() +} + +func (x *chatServiceConnect) Context() context.Context { + return x.stream.Context() +} + +func (x *chatServiceConnect) SendMsg(m interface{}) error { + return x.stream.Send(m) +} + +func (x *chatServiceConnect) RecvMsg(m interface{}) error { + return x.stream.Recv(m) +} + +func (x *chatServiceConnect) Send(m *Message) error { + return x.stream.Send(m) +} + +func (x *chatServiceConnect) Recv() (*Message, error) { + m := new(Message) + err := x.stream.Recv(m) + if err != nil { + return nil, err + } + return m, nil +} + +// Server API for Chat service + +type ChatHandler interface { + // New creates a chat for a group of users. The RPC is idempotent so if it's called multiple times + // for the same users, the same response will be returned. It's good practice to design APIs as + // idempotent since this enables safe retries. + New(context.Context, *NewRequest, *NewResponse) error + // History returns the historical messages in a chat + History(context.Context, *HistoryRequest, *HistoryResponse) error + // Send a single message to the chat + Send(context.Context, *SendRequest, *SendResponse) error + // Connect to a chat using a bidirectional stream enabling the client to send and recieve messages + // over a single RPC. When a message is sent on the stream, it will be added to the chat history + // and sent to the other connected users. When opening the connection, the client should provide + // the chat_id and user_id in the context so the server knows which messages to stream. + Connect(context.Context, Chat_ConnectStream) error +} + +func RegisterChatHandler(s server.Server, hdlr ChatHandler, opts ...server.HandlerOption) error { + type chat interface { + New(ctx context.Context, in *NewRequest, out *NewResponse) error + History(ctx context.Context, in *HistoryRequest, out *HistoryResponse) error + Send(ctx context.Context, in *SendRequest, out *SendResponse) error + Connect(ctx context.Context, stream server.Stream) error + } + type Chat struct { + chat + } + h := &chatHandler{hdlr} + return s.Handle(s.NewHandler(&Chat{h}, opts...)) +} + +type chatHandler struct { + ChatHandler +} + +func (h *chatHandler) New(ctx context.Context, in *NewRequest, out *NewResponse) error { + return h.ChatHandler.New(ctx, in, out) +} + +func (h *chatHandler) History(ctx context.Context, in *HistoryRequest, out *HistoryResponse) error { + return h.ChatHandler.History(ctx, in, out) +} + +func (h *chatHandler) Send(ctx context.Context, in *SendRequest, out *SendResponse) error { + return h.ChatHandler.Send(ctx, in, out) +} + +func (h *chatHandler) Connect(ctx context.Context, stream server.Stream) error { + return h.ChatHandler.Connect(ctx, &chatConnectStream{stream}) +} + +type Chat_ConnectStream interface { + Context() context.Context + SendMsg(interface{}) error + RecvMsg(interface{}) error + Close() error + Send(*Message) error + Recv() (*Message, error) +} + +type chatConnectStream struct { + stream server.Stream +} + +func (x *chatConnectStream) Close() error { + return x.stream.Close() +} + +func (x *chatConnectStream) Context() context.Context { + return x.stream.Context() +} + +func (x *chatConnectStream) SendMsg(m interface{}) error { + return x.stream.Send(m) +} + +func (x *chatConnectStream) RecvMsg(m interface{}) error { + return x.stream.Recv(m) +} + +func (x *chatConnectStream) Send(m *Message) error { + return x.stream.Send(m) +} + +func (x *chatConnectStream) Recv() (*Message, error) { + m := new(Message) + if err := x.stream.Recv(m); err != nil { + return nil, err + } + return m, nil +} diff --git a/chat/proto/chat.proto b/chat/proto/chat.proto new file mode 100644 index 0000000..991608d --- /dev/null +++ b/chat/proto/chat.proto @@ -0,0 +1,77 @@ +syntax = "proto3"; + +package chat; +option go_package = "github.com/micro/services/chat/proto;chat"; + +service Chat { + // New creates a chat for a group of users. The RPC is idempotent so if it's called multiple times + // for the same users, the same response will be returned. It's good practice to design APIs as + // idempotent since this enables safe retries. + rpc New(NewRequest) returns (NewResponse); + // History returns the historical messages in a chat + rpc History(HistoryRequest) returns (HistoryResponse); + // Send a single message to the chat + rpc Send(SendRequest) returns (SendResponse); + // Connect to a chat using a bidirectional stream enabling the client to send and recieve messages + // over a single RPC. When a message is sent on the stream, it will be added to the chat history + // and sent to the other connected users. When opening the connection, the client should provide + // the chat_id and user_id in the context so the server knows which messages to stream. + rpc Connect(stream Message) returns (stream Message); +} + +// NewRequest contains the infromation needed to create a new chat +message NewRequest { + repeated string user_ids = 1; +} + +// NewResponse contains the chat id for the users +message NewResponse { + string chat_id = 1; +} + +// HistoryRequest contains the id of the chat we want the history for. This RPC will return all +// historical messages, however in a real life application we'd introduce some form of pagination +// here, only loading the older messages when required. +message HistoryRequest { + string chat_id = 1; +} + +// HistoryResponse contains the historical messages in a chat +message HistoryResponse { + repeated Message messages = 1; +} + +// SendRequest contains a single message to send to a chat +message SendRequest { + // a client side id, should be validated by the server to make the request retry safe + string client_id = 1; + // id of the chat the message is being sent to / from + string chat_id = 2; + // id of the user who sent the message + string user_id = 3; + // subject of the message + string subject = 4; + // text of the message + string text = 5; +} + +// SendResponse is a blank message returned when a message is successfully created +message SendResponse {} + +// Message sent to a chat +message Message { + // id of the message, allocated by the server + string id = 1; + // a client side id, should be validated by the server to make the request retry safe + string client_id = 2; + // id of the chat the message is being sent to / from + string chat_id = 3; + // id of the user who sent the message + string user_id = 4; + // time time the message was sent in unix format + int64 sent_at = 5; + // subject of the message + string subject = 6; + // text of the message + string text = 7; +} \ No newline at end of file diff --git a/go.sum b/go.sum index 630fa5f..dcf025d 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,7 @@ github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvd github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= @@ -55,10 +56,13 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -72,16 +76,20 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/caddyserver/certmagic v0.10.6 h1:sCya6FmfaN74oZE46kqfaFOVoROD/mF36rTQfjN7TZc= github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ= github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU= github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= github.com/cloudflare/cloudflare-go v0.10.9 h1:d8KOgLpYiC+Xq3T4tuO+/goM+RZvuO+T4pojuv8giL8= github.com/cloudflare/cloudflare-go v0.10.9/go.mod h1:5TrsWH+3f4NV6WjtS5QFp+DifH81rph40gU374Sh0dQ= @@ -94,7 +102,6 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= @@ -138,8 +145,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -224,29 +233,43 @@ github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gosimple/slug v1.9.0 h1:r5vDcYrFz9BmfIAMC829un9hq7hKM4cHUrsv36LbEqs= github.com/gosimple/slug v1.9.0/go.mod h1:AMZ+sOVe65uByN3kgEyf9WEBKBCSS+dJjMX9x4vDJbg= +github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY= github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8= github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -277,6 +300,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= +github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= @@ -311,6 +335,18 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= +github.com/nats-io/jwt v0.3.2 h1:+RB5hMpXUUA2dfxuhBTEkMOrYmM+gKIZYS1KjSostMI= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.7/go.mod h1:rbRrRE/Iv93O/rUvZ9dh4NfT0Cm9HWjW/BqOWLGgYiE= +github.com/nats-io/nats-streaming-server v0.18.0/go.mod h1:Y9Aiif2oANuoKazQrs4wXtF3jqt6p97ODQg68lR5TnY= +github.com/nats-io/nats.go v1.10.0 h1:L8qnKaofSfNFbXg0C5F71LdjPRnmQwSsA4ukmkt1TvY= +github.com/nats-io/nats.go v1.10.0/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.4 h1:aEsHIssIk6ETN5m2/MD8Y4B2X7FfXrBAUdkyRvbVYzA= +github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nats-io/stan.go v0.7.0/go.mod h1:Ci6mUIpGQTjl++MqK2XzkWI/0vF+Bl72uScx7ejSYmU= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= @@ -337,11 +373,13 @@ github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5X github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -352,21 +390,29 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ= github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q= @@ -390,6 +436,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -403,7 +450,9 @@ github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4U github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf h1:Z2X3Os7oRzpdJ75iPqWZc0HeJWFYNCvKsfpQwFpRNTA= github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf/go.mod h1:M8agBzgqHIhgj7wEn9/0hJUZcrvt9VY+Ln+S1I5Mha0= github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= @@ -422,6 +471,7 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2 github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -445,9 +495,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -488,6 +540,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -556,6 +609,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -567,6 +621,7 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -579,6 +634,7 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -620,6 +676,7 @@ golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -718,6 +775,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -739,6 +797,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -758,3 +817,4 @@ k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=