From 071c13be96d52bc91b1bc63f791b94f9ecbf224a Mon Sep 17 00:00:00 2001 From: fanweixiao Date: Fri, 18 Dec 2020 21:52:17 +0800 Subject: [PATCH] Streaming-Serverless in Rx way with CLI support (#70) * feat(rx): introduce RxStream [#60] * add(serverless): add serverless runtime [#58] * feat(cli): add `yomo dev` command [#61] * doc: update README file for v0.4 Co-authored-by: jjwygjj Co-authored-by: Hong Xiaojian --- .gitignore | 3 +- LICENSE | 2 +- README.md | 132 ++++----- README_CN.md | 281 +++++++----------- cmd/yomo/main.go | 9 + configs/.keep | 0 configs/echo.go | 25 -- example/app.go | 26 ++ go.mod | 10 +- go.sum | 314 +++++++++++++++----- internal/cmd/dev/dev.go | 93 ++++++ internal/dispatcher/dispatcher.go | 32 ++ internal/framework/server.go | 12 - internal/serverless/build.go | 35 +++ internal/serverless/load.go | 22 ++ internal/serverless/runtime.go | 14 + pkg/env/env.go | 42 --- pkg/plugin/plugin.go | 14 - pkg/pprof/pprof.go | 70 ----- pkg/quic/quic-go.go | 140 +++++++++ pkg/quic/server.go | 25 ++ pkg/quic/stream.go | 20 ++ pkg/rx/rxstream.go | 91 ++++++ pkg/rx/rxstream_operator.go | 466 ++++++++++++++++++++++++++++++ pkg/util/log.go | 146 ---------- pkg/util/quic.go | 345 ---------------------- pkg/yomo/run.go | 172 ----------- 27 files changed, 1377 insertions(+), 1164 deletions(-) create mode 100644 cmd/yomo/main.go delete mode 100644 configs/.keep delete mode 100644 configs/echo.go create mode 100644 example/app.go create mode 100644 internal/cmd/dev/dev.go create mode 100644 internal/dispatcher/dispatcher.go delete mode 100644 internal/framework/server.go create mode 100644 internal/serverless/build.go create mode 100644 internal/serverless/load.go create mode 100644 internal/serverless/runtime.go delete mode 100644 pkg/env/env.go delete mode 100644 pkg/plugin/plugin.go delete mode 100644 pkg/pprof/pprof.go create mode 100644 pkg/quic/quic-go.go create mode 100644 pkg/quic/server.go create mode 100644 pkg/quic/stream.go create mode 100644 pkg/rx/rxstream.go create mode 100644 pkg/rx/rxstream_operator.go delete mode 100644 pkg/util/log.go delete mode 100644 pkg/util/quic.go delete mode 100644 pkg/yomo/run.go diff --git a/.gitignore b/.gitignore index 66fd13c90..937122958 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ *.out # Dependency directories (remove the comment below to include it) -# vendor/ +vendor/ +yomo diff --git a/LICENSE b/LICENSE index 6313812c8..2dce2431f 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019~2020 北京熹乐科技有限公司 CELLA + Copyright 2019~2020 CELLA, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 8d9743ac5..44813ded7 100644 --- a/README.md +++ b/README.md @@ -1,131 +1,97 @@ +

+ +

+ # YoMo ![Go](https://github.com/yomorun/yomo/workflows/Go/badge.svg) YoMo is an open-source Streaming Serverless Framework for building Low-latency Edge Computing applications. Built atop QUIC Transport Protocol and Functional Reactive Programming interface, it makes real-time data processing reliable, secure, and easy. More info at [https://yomo.run](https://yomo.run/?utm_source=github&utm_campaign=ossc) -[中文内容在Gitee](https://gitee.com/yomorun/yomo) - -## Getting Started - -### 1. Install the current release +🇨🇳 [简体中文](https://docs.yomo.run/zh) -Create a directory named `yomotest` and `cd` into it. +## 🚀 Getting Started - mkdir yomotest - cd yomotest +### 1. Install CLI -Make the current directory the root of a module by using `go mod init`. - - go mod init yomotest - -Download and install. +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/yomorun/install/HEAD/install.sh)" +``` - go get -u github.com/yomorun/yomo +### 2. Create your serverless code -### 2. Create file `echo.go` +```bash +mkdir yomo-demo && cd $_ && touch app.go +``` -To check that YoMo is installed correctly on your device, create a file named `echo.go` and copy the following code to your file: +Write your `app.go` code: -```go  +```go package main import ( - "github.com/yomorun/yomo/pkg/yomo" -) - -func main() { - // run echo plugin and monitor port 4241; data will be sent by yomo egde - // yomo.Run(&EchoPlugin{}, "0.0.0.0:4241") - - // a method for development and testing; when connected to the Internet, it will - // automatically connect to the development server of yomo.run - // after successfully connected to the server, the plugin will receive the value - // of the key specified by the Observed() method every 2 seconds - // yomo.RunDev(&EchoPlugin{}, "localhost:4241") - yomo.RunDevWith(&EchoPlugin{}, "localhost:4241", yomo.OutputEchoData) -} - -// EchoPlugin - a yomo plugin that converts received data into strings and appends -// additional information to the strings; the modified data will flow to the next plugin -type EchoPlugin struct{} + "context" + "fmt" + "time" -// Handle - this method will be called when data flows in; the Observed() method is used -// to tell yomo which key the plugin should monitor; the parameter value is what the plugin -// needs to process -func (p *EchoPlugin) Handle(value interface{}) (interface{}, error) { - return value.(string) + "✅", nil -} + "github.com/yomorun/yomo/pkg/rx" +) -// Observed - returns a value of type string, which is the key monitored by echo plugin; -// the corresponding value will be passed into the Handle() method as an object -func (p EchoPlugin) Observed() string { - return "0x11" //name +var printer = func(_ context.Context, i interface{}) (interface{}, error) { + value := i.(float32) + fmt.Println("serverless get value:", value) + return value, nil } -// Name - sets the name of a given plugin p (mainly used for debugging) -func (p *EchoPlugin) Name() string { - return "EchoPlugin" -} +// Handler will handle data in Rx way +func Handler(rxstream rx.RxStream) rx.RxStream { + stream := rxstream. + Y3Decoder("0x10", float32(0)). + AuditTime(100 * time.Millisecond). + Map(printer). + StdOut() -// Mold describe the struct of `Observed` value -func (p EchoPlugin) Mold() interface{} { - return "" + return stream } ``` ### 3. Build and run -1. Run `go run echo.go` from the terminal. If YoMo is installed successfully, you will see the following message: +1. Run `yomo dev` from the terminal. you will see the following message: ```bash -% go run echo.go -[EchoPlugin:6031]2020/07/06 22:14:20 plugin service start... [localhost:4241] -name:yomo!✅ -name:yomo!✅ -name:yomo!✅ -name:yomo!✅ -name:yomo!✅ -^Csignal: interrupt +(20:08:50 ~/yomo/examples)──> yomo dev +2020/12/18 20:09:12 Building the Serverless Function File... +2020/12/18 20:09:14 ✅ Listening on 0.0.0.0:4242 ``` -Congratulations! You have written and tested your first YoMo app. - -Note: If you want to use a complex Mold, please refer to [yomo-echo-plugin](https://github.com/yomorun/yomo-echo-plugin). +Congratulations! You have done your first YoMo application. -## Illustration +## 🎯 Focuses on computings out of data center -![yomo-arch](https://yomo.run/yomo-arch.png) +- Latency-sensitive applications. +- Networking situation with packet loss or high latency. +- Handling continuous high frequency generated data with stream-processing. +- Building Complex systems with Streaming-Serverless architecture. -### YoMo focuses on: +## 🌟 Why YoMo -- Industrial IoT: - - On the IoT device side, real-time communication with a latency of less than 10ms is required. - - On the smart device side, AI performing with a high hash rate is required. -- YoMo consists of 2 parts: - - `yomo-edge`: deployed on company intranet; responsible for receiving device data and executing each yomo-plugin in turn according to the configuration - - `yomo-plugin`: can be deployed on public cloud, private cloud, and `yomo-edge-server` - -### Why YoMo - -- Based on QUIC (Quick UDP Internet Connection) protocol for data transmission, which uses the User Datagram Protocol (UDP) as its basis instead of the Transmission Control Protocol (TCP); significantly improves the stability and throughput of data transmission. +- Based on QUIC (Quick UDP Internet Connection) protocol for data transmission, which uses the User Datagram Protocol (UDP) as its basis instead of the Transmission Control Protocol (TCP); significantly improves the stability and throughput of data transmission. Especially for cellular networks like 5G. - A self-developed `yomo-codec` optimizes decoding performance. For more information, visit [its own repository](https://github.com/yomorun/yomo-codec) on GitHub. - Based on stream computing, which improves speed and accuracy when dealing with data handling and analysis; simplifies the complexity of stream-oriented programming. +- Secure-by-default from transport protocol. -## Contributing +## 🦸 Contributing First off, thank you for considering making contributions. It's people like you that make YoMo better. There are many ways in which you can participate in the project, for example: - File a [bug report](https://github.com/yomorun/yomo/issues/new?assignees=&labels=bug&template=bug_report.md&title=%5BBUG%5D). Be sure to include information like what version of YoMo you are using, what your operating system is, and steps to recreate the bug. - - Suggest a new feature. - - Read our [contributing guidelines](https://github.com/yomorun/yomo/blob/master/CONTRIBUTING.md) to learn about what types of contributions we are looking for. - - We have also adopted a [code of conduct](https://github.com/yomorun/yomo/blob/master/CODE_OF_CONDUCT.md) that we expect project participants to adhere to. -## Feedback +## 🤹🏻‍♀️ Feedback -Email us at [yomo@cel.la](mailto:yomo@cel.la). Any feedback would be greatly appreciated! +Any questions or good ideas, please feel free to come to our [Discussion](https://github.com/yomorun/yomo/discussions). Any feedback would be greatly appreciated! ## License diff --git a/README_CN.md b/README_CN.md index 90ec03da7..beb1662ea 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,18 +1,111 @@ +

+ +

+ # YoMo ![Go](https://github.com/yomorun/yomo/workflows/Go/badge.svg) YoMo 是为边缘计算打造的低时延流式 Serverless 开发框架,基于 [QUIC Transport](https://quicwg.org/) 协议通讯,以 [Functional Reactive Programming](https://en.wikipedia.org/wiki/Functional_reactive_programming) 为编程范式,简化构建可靠、安全的低时延计算应用的复杂度,挖掘5G潜力,释放实时计算价值。 -5G 和 AI 的发展,带来数据的爆发式增长,和对数据的实时计算需求。从 VR/AR游戏和云游戏、超清视频,到智能制造、远程医疗和自动驾驶,低时延应用正在各行各业涌现。YoMo 提供的低时延流式计算框架,致力于简化低时延应用开发成本,屏蔽底层技术细节,帮助用户简化开发过程,缩短开发周期,极大的减少了开发和维护成本。 +官网:[https://yomo.run](https://yomo.run/?utm_source=github&utm_campaign=ossc) (感谢 Vercel 支持) + +For english, check: [Github](https://github.com/yomorun/yomo) + +## 🚀 3分钟教程 + +### 1. 安装CLI + +```bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/yomorun/install/HEAD/install.sh)" +``` + +### 2. 以 Serverless 的范式编写业务代码 + +```bash +mkdir yomo-demo && cd $_ && touch app.go +``` + +`app.go` 的内容为 + +```go +package main -官网: [https://yomo.run](https://yomo.run/) +import ( + "context" + "fmt" + "time" + + "github.com/yomorun/yomo/pkg/rx" +) + +var printer = func(_ context.Context, i interface{}) (interface{}, error) { + value := i.(float32) + fmt.Println("serverless get value:", value) + return value, nil +} + +// Handler will handle data in Rx way +func Handler(rxstream rx.RxStream) rx.RxStream { + stream := rxstream. + Y3Decoder("0x10", float32(0)). + AuditTime(100 * time.Millisecond). + Map(printer). + StdOut() + + return stream +} +``` + +### 3. 开始运行 + +1. 在终端里执行 `yomo dev`,该命令将自动连接至 YoMo 的公开调试服务,服务将以`100ms`的频率持续发送`float`类型的数据,这就是`YoMo 北京Office`的噪声传感器的实时数据。 + +```bash +(20:08:50 ~/yomo/examples)──> yomo dev +2020/12/18 20:09:12 Building the Serverless Function File... +2020/12/18 20:09:14 ✅ Listening on 0.0.0.0:4242 +``` -For English:https://github.com/yomorun/yomo +恭喜!您的 Real-time stream processing application 已经全部写完! -## QUIC +## 🎯 越来越多的数据产生在数据中心之外,YoMo 关注在离数据更近的位置,提供便利的计算框架 + +- 对时延敏感的场景 +- 蜂窝网络下的会出现性能抖动,存在丢包、延时,比如LTE、5G +- 源源不断的高频数据涌向业务处理 +- 对于复杂系统,希望使用 Streaming-Serverless 架构简化 + +## 🌟 YoMo 优势: + +- 全程基于 QUIC 协议传输数据,使用UDP协议替代TCP协议后,大幅提升了传输的稳定性和高通率 +- 自研的`yomo-codec`优化了数据解码性能 +- 全程基于 Rx 实现 Stream Computing 模型,并简化面向流式编程的复杂度 +- 通讯协议级别的“本质安全” + +## 🦸 成为 YoMo 贡献者 + +首先感谢您的 contributions,是您这样的人让 YoMo 能变得越来越好!参与 YoMo 项目有很多种方式: + +- [提交bug🐛](https://github.com/yomorun/yomo/issues/new?assignees=&labels=bug&template=bug_report.md&title=%5BBUG%5D),请务必记得描述您所运行的YoMo的版本、操作系统和复现bug的步骤。 + +- 建议新的功能 + +- 在贡献代码前,请先阅读[Contributing Guidelines](https://gitee.com/yomorun/yomo/blob/master/CONTRIBUTING.md) + +- 当然我们也有 [Code of Conduct](https://gitee.com/yomorun/yomo/blob/master/CODE_OF_CONDUCT.md) + +## 🤹🏻‍♀️ 反馈和建议 + +任何时候,建议和意见都可以写在 [Discussion](https://github.com/yomorun/yomo/discussions),每一条反馈都一定会被社区感谢! + +## 开源协议 + +[Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) + +# QUIC **QUIC** 的全称是 Quick UDP Internet Connections protocol, 由 Google 设计提出,目前由 IETF 工作组推动进展。其设计的目标是替代 TCP 成为 HTTP/3 的数据传输层协议。熹乐科技在物联网(IoT)和边缘计算(Edge Computing)场景也一直在打造底层基于 QUIC 通讯协议的边缘计算微服务框架 [YoMo](https://yomo.run),长时间关注 QUIC 协议的发展,遂整理该文集并配以适当的中文翻译,方便更多关注 QUIC 协议的人学习。 -## QUIC Weekly - 每周一草 +# QUIC Weekly - 每周一草 在线社区:🍖[discord/quic](https://discord.gg/CTH3wv9) 维护者:🦖[YoMo](https://yomo.run/) @@ -62,7 +155,7 @@ For English:https://github.com/yomorun/yomo * 📢 关于多路复用技术的WG值得关注 **MASQUE Working Group** [Multiplexed Application Substrate over QUIC Encryption (masque)](https://datatracker.ietf.org/wg/masque/about/) -### QUIC Weekly - 20201104期 +## QUIC Weekly - 20201104期 * 📢 **load-balancers** [Merged了使用POSIX timestamp的PR,这才对嘛](https://github.com/quicwg/load-balancers/pull/56/files) * 📢 **load-balancers** [draft-ietf-quic-load-balancers-05出来了,相比draft-04的更新参考这里](https://www.ietf.org/rfcdiff?url1=draft-ietf-quic-load-balancers-04&url2=draft-ietf-quic-load-balancers-05) @@ -77,7 +170,7 @@ For English:https://github.com/yomorun/yomo * **开源** [lsquic 2.24.1 发布,@sumams为其增加了新功能,也包含了一些bug修复 🔧.](https://github.com/litespeedtech/lsquic) * **工具** [Wireshark 3.4.0发布,支持IETF QUIC](https://www.wireshark.org/docs/relnotes/wireshark-3.4.0.html) -### QUIC Weekly - 20201028期 +## QUIC Weekly - 20201028期 * 📢 [DNS-over-QUIC](https://tools.ietf.org/html/draft-ietf-dprive-dnsoquic-01): * 对科学那啥可是个好东西,太敏感,咱也不敢多说... @@ -92,7 +185,7 @@ For English:https://github.com/yomorun/yomo * IoT设备是应用QUIC协议的一个好场景,因为这些设备通常工作在无线(蜂窝)网络下(Cellular network),且需要快速建连、0-RTT和重传。但是,这些设备CPU能力普遍较弱。QUIC的作者其实多次提到QUIC对IoT应用场景有很大的提升,可惜的是,至今还没有一套为这个场景设计的协议栈(其实有啊:基于QUIC协议的Edge Computing框架: [🦖YoMo](https://yomo.run/)) * **Article** [未来的Internet: HTTP/3 — No More TCP, let’s QUIC fix it(谐音梗我翻不出来了...)](https://thexbhpguy.medium.com/the-new-internet-http-3-no-more-tcp-lets-quic-fix-it-6a4cbb6280c7) -### QUIC Weekly - 20201021期 +## QUIC Weekly - 20201021期 * 📢 QUIC 协议终于出现在 [IETF last call](https://mailarchive.ietf.org/arch/msg/ietf-announce/py1vC4Iuzq18Je4rwF69029oVOI/) 中。 * 📢 QUIC 草案32文件已出: @@ -104,7 +197,7 @@ For English:https://github.com/yomorun/yomo * **Adoption** 现在 Facebook 已经使用 #QUIC + #HTTP3 来处理其全球所有本机应用流量的75%以上!他们从新协议中看到了令人印象深刻的性能提升,尤其是在他们的视频流使用案例中。 [Facebook 如何将 QUIC 带给数十亿人](https://engineering.fb.com/networking-traffic/how-facebook-is-bringing-quic-to-billions/) * **Adoption** [Node.js 15首次支持 QUIC 和 HTTP/3](https://www.infoworld.com/article/3586354/nodejs-15-debuts-support-for-http3-transport.html)。 -### QUIC Weekly - 20201014期 +## QUIC Weekly - 20201014期 * **Adoption** [Chrome 正在部署 HTTP/3 和 IETF QUIC](https://blog.chromium.org/2020/10/chrome-is-deploying-http3-and-ietf-quic.html) * 当前最新的 Google QUIC 版本(Q050)与 IETF QUIC 有很多相似之处。但是到目前为止,大多数 Chrome 用户在未启用某些命令行选项的情况下没有与 IETF QUIC 服务器通信。 @@ -118,178 +211,10 @@ For English:https://github.com/yomorun/yomo * [在 Haskell 中开发 QUIC 丢失检测和拥塞控制](https://kazu-yamamoto.hatenablog.jp/entry/2020/09/15/121613)。 --- -### IETF进展 +# IETF进展 * [draft-ietf-quic-transport-32](https://datatracker.ietf.org/doc/draft-ietf-quic-transport/) QUIC: A UDP-Based Multiplexed and Secure Transport * [draft-ietf-quic-tls-32](https://datatracker.ietf.org/doc/draft-ietf-quic-tls/) Using TLS to Secure QUIC * [draft-ietf-quic-invariants-11](https://datatracker.ietf.org/doc/draft-ietf-quic-invariants/) Version-Independent Properties of QUIC * [draft-ietf-quic-recovery-32](https://datatracker.ietf.org/doc/draft-ietf-quic-recovery/) QUIC Loss Detection and Congestion Control * [draft-ietf-quic-version-negotiation-01](https://datatracker.ietf.org/doc/draft-ietf-quic-version-negotiation/) Compatible Version Negotiation for QUIC - - -## 💘 QUIC快速学习资源 Awesome QUIC - -* 不在爱了 TCP 💔: - * [为什么TCP是个烂协议](https://zhuanlan.zhihu.com/p/20144829) - * 今天 TCP 烂了怎么办?[如何看待谷歌 Google 打算用 QUIC 协议替代 TCP/UDP?](https://www.zhihu.com/question/29705994) -* 浅尝 QUIC 科普贴 🎱: - * 知乎腾讯技术官号 [科普:QUIC协议原理分析](https://zhuanlan.zhihu.com/p/32553477) - * [新一代互联网传输协议QUIC浅析](https://zhuanlan.zhihu.com/p/76202865) -* 真干实践大厂贴 🏌️‍♂️: - * 腾讯 QUIC 实践 [让互联网更快的协议,QUIC在腾讯的实践及性能优化](https://zhuanlan.zhihu.com/p/32560981) - * 阿里 QUIC 实践 - * [阿里XQUIC:标准QUIC实现自研之路](https://mp.weixin.qq.com/s/pBv_DnG05YWl4ZYRHThaTw) - * [AliQUIC:场景化高性能传输网络实践](https://developer.aliyun.com/article/643770) - * 七牛 QUIC 实践 [流畅度提高 100%!七牛云 QUIC 推流方案如何实现直播 0 卡顿](https://zhuanlan.zhihu.com/p/33698793) - * 又拍云 QUIC 实践 [QUIC协议详解之Initial包的处理](https://zhuanlan.zhihu.com/p/162914823) - * 微博 QUIC 实践 [QUIC在微博中的落地思考](https://www.infoq.cn/article/2018/03/weibo-quic) - * B站 QUIC 实践 [B站QUIC实践之路](https://mp.weixin.qq.com/s/DrGm-OkSpJbzPWbFmSBT8g) - * Facebook QUIC 实践 [Building Zero protocol for fast, secure mobile connections](https://engineering.fb.com/networking-traffic/building-zero-protocol-for-fast-secure-mobile-connections/) - * Cloudflare QUIC 实践 [The Road to QUIC](https://blog.cloudflare.com/the-road-to-quic/) - * Uber QUIC 实践 - * [Employing QUIC Protocol to Optimize Uber’s App Performance](https://eng.uber.com/employing-quic-protocol/) - * [Uber Networking: Challenges and Opportunities](https://www.slideshare.net/dhaval2025/uber-mobility-high-performance-networking) - * Fastly QUIC 实践 [Modernizing the internet with HTTP/3 and QUIC](https://www.fastly.com/blog/modernizing-the-internet-with-http3-and-quic) -* 熬夜充电技术细节贴 🦾: - * [让互联网更快的“快”---QUIC协议原理分析](https://zhuanlan.zhihu.com/p/32630510) - * [QUIC 是如何做到 0RTT 的](https://zhuanlan.zhihu.com/p/142794794) - * [快速理解为什么说UDP有时比TCP更有优势](http://www.52im.net/thread-1277-1-1.html) - * [一泡尿的时间,快速读懂QUIC协议](http://www.52im.net/thread-2816-1-1.html) -* 墙裂推荐英文贴 🍿: - * 🍿 QUIC工作组主席 [Lars Eggert博士](https://eggert.org/) 的 [QUIC: a new internet transport](https://video.fsmpi.rwth-aachen.de/17ws-quic/12107) (🎬 58:39) @2017 - * 🍿 谷歌官方 2014 年发布的视频 [QUIC: next generation multiplexed transport over UDP](https://www.youtube.com/watch?v=hQZ-0mXFmk8) (🎬 51:40) @2014 - * F5 首席架构师 Jason Rahm [What is QUIC?](https://www.youtube.com/watch?v=RIFnXaiRs_o) (🎬 08:35) @2018 - * Codevel博客文章 [https://medium.com/codavel-blog/quic-vs-tcp-tls-and-why-quic-is-not-the-next-big-thing-d4ef59143efd](https://medium.com/codavel-blog/quic-vs-tcp-tls-and-why-quic-is-not-the-next-big-thing-d4ef59143efd) -* 估计你们不会看的🧟‍♀️: - * QUIC: A UDP-Based Multiplexed and Secure Transport [draft-ietf-quic-transport-31](https://datatracker.ietf.org/doc/draft-ietf-quic-transport/) - * Using TLS to Secure QUIC [draft-ietf-quic-tls-31](https://datatracker.ietf.org/doc/draft-ietf-quic-tls/) - * Version-Independent Properties of QUIC [draft-ietf-quic-invariants-11](https://datatracker.ietf.org/doc/draft-ietf-quic-invariants/) - * QUIC Loss Detection and Congestion Control [draft-ietf-quic-recovery-31](https://datatracker.ietf.org/doc/draft-ietf-quic-recovery/) - * Compatible Version Negotiation for QUIC [draft-ietf-quic-version-negotiation-01](https://datatracker.ietf.org/doc/draft-ietf-quic-version-negotiation/) - -## 🚀 3分钟构建工业微服务 Quick Start - -### 1. 创建工程,并引入yomo - -创建一个叫`yomotest`的目录: - -```bash -mkdir yomotest -cd yomotest -``` - -初始化项目: - -``` -go mod init yomotest -``` - -引入yomo - -``` -go get -u gitee.com/yomorun/yomo -``` - -### 2. 编写业务逻辑`echo.go` - -```go -package main - -import ( - "github.com/yomorun/yomo/pkg/yomo" -) - -func main() { - //// 运行echo plugin并监控4241端口,数据将会从YoMo Edge推送过来 - // yomo.Run(&EchoPlugin{}, "0.0.0.0:4241") - - // 开发调试时运行该方法,处于联网状态时,程序会自动连接至 yomo.run 的开发服务器,连接成功后, - // 该Plugin会每2秒收到一条Observed()方法指定的Key的Value - // yomo.RunDev(&EchoPlugin{}, "localhost:4241") - yomo.RunDevWith(&EchoPlugin{}, "localhost:4241", yomo.OutputEchoData) -} - -// EchoPlugin 是一个YoMo Plugin,会将接受到的数据转换成String形式,并再结尾添加内容,修改 -// 后的数据将流向下一个Plugin -type EchoPlugin struct{} - -// Handle 方法将会在数据流入时被执行,使用Observed()方法通知YoMo该Plugin要关注的key,参数value -// 即该Plugin要处理的内容 -func (p *EchoPlugin) Handle(value interface{}) (interface{}, error) { - return value.(string) + "✅", nil -} - -// Observed 返回一个string类型的值,该值是EchoPlugin插件关注的数据流中的Key,该数据流中Key对应 -// 的Value将会以对象的形式被传递进Handle()方法中 -func (p EchoPlugin) Observed() string { - return "0x11" //name -} - -// Name 用于设置该Plugin的名称,方便Debug等操作 -func (p *EchoPlugin) Name() string { - return "EchoPlugin" -} - -// Mold 描述`Observed`的值的数据结构 -func (p EchoPlugin) Mold() interface{} { - return "" -} -``` - -### 3. 运行 - -1. 在终端里执行 `go run echo.go`,您将会看到: - -```bash -% go run a.go -[EchoPlugin:6031]2020/07/06 22:14:20 plugin service start... [localhost:4241] -name:yomo!✅ -name:yomo!✅ -name:yomo!✅ -name:yomo!✅ -name:yomo!✅ -^Csignal: interrupt -``` -恭喜!您的第一个YoMo应用已经完成! - -小提示: 如果您使用复合数据结构(Complex Mold), 请参考:[yomo-echo-plugin](https://gitee.com/yomorun/yomo-echo-plugin)。 - -## 🌟 YoMo架构和亮点 - -![yomo-arch](https://yomo.run/yomo-arch.png) - -### YoMo关注在: - -- 工业互联网领域 - - 在IoT设备接入侧,需要<10ms的低延时实时通讯 - - 在智能设备侧,需要在边缘侧进行大算力的AI执行工作 -- YoMo 包含两部分: - - `yomo-edge`: 部署在企业内网,负责接收设备数据,并按照配置,依次执行各个`yomo-plugin` - - `yomo-plugin`: 可以部署在企业私有云、公有云及 YoMo Edge Server 上 - -### YoMo的优势: - -- 全程基于 QUIC 协议传输数据,使用UDP协议替代TCP协议后,大幅提升了传输的稳定性和高通率 -- 自研的`yomo-codec`优化了数据解码性能 -- 全程基于Stream Computing模型,并简化面向Stream编程的复杂度 - -## 🦸 成为YoMo开发者 - -First off, thank you for considering making contributions. It's people like you that make YoMo better. There are many ways in which you can participate in the project, for example: -首先感谢您的contributions,是您这样的人让YoMo能变得越来越好!参与YoMo项目有很多种方式: - -- [提交bug🐛](https://github.com/yomorun/yomo/issues/new?assignees=&labels=bug&template=bug_report.md&title=%5BBUG%5D),请务必记得描述您所运行的YoMo的版本、操作系统和复现bug的步骤。 - -- 建议新的功能 - -- 在贡献代码前,请先阅读[Contributing Guidelines](https://gitee.com/yomorun/yomo/blob/master/CONTRIBUTING.md) - -- 当然我们也有 [Code of Conduct](https://gitee.com/yomorun/yomo/blob/master/CODE_OF_CONDUCT.md) - -## 🧙 联系YoMo组织 - -Email us at [yomo@cel.la](mailto:yomo@cel.la). Any feedback would be greatly appreciated! - -## 开源协议 - -[Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) diff --git a/cmd/yomo/main.go b/cmd/yomo/main.go new file mode 100644 index 000000000..48c7a1285 --- /dev/null +++ b/cmd/yomo/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/yomorun/yomo/internal/cmd/dev" +) + +func main() { + dev.Execute() +} diff --git a/configs/.keep b/configs/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/configs/echo.go b/configs/echo.go deleted file mode 100644 index 59789db57..000000000 --- a/configs/echo.go +++ /dev/null @@ -1,25 +0,0 @@ -package configs - -import "github.com/yomorun/yomo/pkg/env" - -var ( - DefaultEchoConf EchoConf -) - -func init() { - DefaultEchoConf = GetEchoConf() -} - -type EchoConf struct { - EchoServerAddr string -} - -const ( - echoServerAddr = "YOMO_ECHO_SERVER_ADDR" -) - -func GetEchoConf() EchoConf { - conf := EchoConf{} - conf.EchoServerAddr = env.GetString(echoServerAddr, "161.189.140.133:11521") - return conf -} diff --git a/example/app.go b/example/app.go new file mode 100644 index 000000000..7acbb55f9 --- /dev/null +++ b/example/app.go @@ -0,0 +1,26 @@ +package main + +import ( + "context" + "fmt" + "time" + + "github.com/yomorun/yomo/pkg/rx" +) + +var printer = func(_ context.Context, i interface{}) (interface{}, error) { + value := i.(float32) + fmt.Println("serverless get value:", value) + return value, nil +} + +// Handler will handle data in Rx way +func Handler(rxstream rx.RxStream) rx.RxStream { + stream := rxstream. + Y3Decoder("0x10", float32(0)). + AuditTime(100 * time.Millisecond). + Map(printer). + StdOut() + + return stream +} diff --git a/go.mod b/go.mod index d53b6062f..3b2c71fc0 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/yomorun/yomo go 1.14 require ( - github.com/golang/protobuf v1.4.3 // indirect - github.com/lucas-clemente/quic-go v0.19.2 + github.com/cenkalti/backoff/v4 v4.0.0 + github.com/lucas-clemente/quic-go v0.19.3 + github.com/reactivex/rxgo/v2 v2.4.0 + github.com/spf13/cobra v1.1.1 github.com/yomorun/yomo-codec-golang v1.1.0 - golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 // indirect - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect - google.golang.org/protobuf v1.25.0 // indirect + golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f // indirect ) diff --git a/go.sum b/go.sum index 13b89f27f..d924d8520 100644 --- a/go.sum +++ b/go.sum @@ -2,63 +2,86 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/10cella/yomo-json-codec v0.0.2 h1:1GZjMGJGr8V9j009sNZKwQaZlXTjiMqZCr2GMFaaR5o= -github.com/10cella/yomo-json-codec v0.0.2/go.mod h1:9rwfqlpW8iZ9Ki5/frJsO2fBSl9x24QB16jloj7YPCc= -github.com/10cella/yomo-json-codec v0.1.0 h1:EE33np1AP3y67sbgZqWH4IroPtJubWQCoGzr39TEAuM= -github.com/10cella/yomo-json-codec v0.1.0/go.mod h1:9rwfqlpW8iZ9Ki5/frJsO2fBSl9x24QB16jloj7YPCc= -github.com/10cella/yomo-json-codec v0.2.0 h1:VCFrpoVeYX6AnVQcYaIKcPbMKNERx/ZKno7Ejuiudrc= -github.com/10cella/yomo-json-codec v0.2.0/go.mod h1:oApqc2PnZohrtlVJ3T1Knou9Ap9mcCJW1i3xiRtvF7c= -github.com/10cella/yomo-json-codec v0.2.1 h1:cWh5TovVPlrOJKkjTVuCkpQ4rj1QoY2ANHCD7afB+6M= -github.com/10cella/yomo-json-codec v0.2.1/go.mod h1:oApqc2PnZohrtlVJ3T1Knou9Ap9mcCJW1i3xiRtvF7c= -github.com/10cella/yomo-json-codec v0.2.2 h1:NppW6C1hWUaB9973E6leNnsGM6E4Aj1kSaBApMRuLx0= -github.com/10cella/yomo-json-codec v0.2.2/go.mod h1:oApqc2PnZohrtlVJ3T1Knou9Ap9mcCJW1i3xiRtvF7c= -github.com/10cella/yomo-json-codec v0.2.3 h1:IMcV7lR8cknbXFnNIU4JB6menKKGny0XXcIS55ao35U= -github.com/10cella/yomo-json-codec v0.2.3/go.mod h1:oApqc2PnZohrtlVJ3T1Knou9Ap9mcCJW1i3xiRtvF7c= -github.com/10cella/yomo-json-codec v0.2.4 h1:bdOInT1MnBhXDsGBHx/fYn0CiXzKxrm5/kFjfJyYdrw= -github.com/10cella/yomo-json-codec v0.2.4/go.mod h1:j4rl6bth7Bb6F8UC0SPN2XxlGGUsHGWO8LnrH5Q8RzI= -github.com/10cella/yomo-json-codec v0.2.5 h1:XTWWFVkQENaLLBTxBdXyG61UneA8SQF/YdlnyHptV/o= -github.com/10cella/yomo-json-codec v0.2.5/go.mod h1:j4rl6bth7Bb6F8UC0SPN2XxlGGUsHGWO8LnrH5Q8RzI= -github.com/10cella/yomo-json-codec v0.2.6 h1:LjJHlYtQ9nma84/Tbo56lessv5ojHKP3nGTuj6v4Z08= -github.com/10cella/yomo-json-codec v0.2.6/go.mod h1:j4rl6bth7Bb6F8UC0SPN2XxlGGUsHGWO8LnrH5Q8RzI= -github.com/10cella/yomo-txtkv-codec v1.0.5 h1:rVdtBcsff3RG3BEq7AHq2WvNiGDF6IEKlTiplypfX+c= -github.com/10cella/yomo-txtkv-codec v1.0.5/go.mod h1:nBfcD3qVq6kkSRHOgSUxK1+gMRoXI0obLF+b1eiyCtM= github.com/10cella/yomo-y3-stress-testing v1.0.0/go.mod h1:L31BazYe9K489eRUiNjIiUTj9xIo8jPDxRzyPxikcyw= 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/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -67,80 +90,138 @@ github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +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/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lucas-clemente/quic-go v0.17.1 h1:ezsH76xpn6hKugfsXUy6voIJBFmAOwnM/Oy9F4b/n+M= -github.com/lucas-clemente/quic-go v0.17.1/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE= -github.com/lucas-clemente/quic-go v0.19.2 h1:w8BBYUx5Z+kNpeaOeQW/KzcNsKWhh4O6PeQhb0nURPg= -github.com/lucas-clemente/quic-go v0.19.2/go.mod h1:ZUygOqIoai0ASXXLJ92LTnKdbqh9MHCLTX6Nr1jUrK0= +github.com/lucas-clemente/quic-go v0.19.3 h1:eCDQqvGBB+kCTkA0XrAFtNe81FMa0/fn4QSoeAbmiF4= +github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls v0.9.1 h1:O0YKQxNVPaiFgMng0suWEOY2Sb4LT2sRn9Qimq3Z1IQ= -github.com/marten-seemann/qtls v0.9.1/go.mod h1:T1MmAdDPyISzxlK6kjRr0pcZFBVd1OZbBb/j3cvzHhk= github.com/marten-seemann/qtls v0.10.0 h1:ECsuYUKalRL240rRD4Ri33ISb7kAQ3qGDlrrl55b2pc= github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= github.com/marten-seemann/qtls-go1-15 v0.1.1 h1:LIH6K34bPVttyXnUWixk0bzH6/N07VxbSabxn5A5gZQ= github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= -github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg= -github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/reactivex/rxgo/v2 v2.4.0 h1:0GNRR1dnVjEWEXWTcQjIbCuJUi2XAn2u0qTKwSHkTZk= +github.com/reactivex/rxgo/v2 v2.4.0/go.mod h1:NtZMAof7BaWwEiC4jNj7tUF+oCYOs7FnQBnzsyWwmgw= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= @@ -162,155 +243,248 @@ github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= +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= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.2.1/go.mod h1:cSVypSfTLm2o9fKxXvQgn3rMmkPXovcWor6Qn5tbFmI= github.com/ugorji/go/codec v1.2.1/go.mod h1:s/WxCRi46t8rA+fowL40EnmD7ec0XhR7ZypxeBNdzsM= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/yomorun/yomo-codec-golang v0.1.0 h1:K0hKnpw4/SDwvsgjl17lPIdOJqsOo193XU7iIn76w+4= -github.com/yomorun/yomo-codec-golang v0.1.0/go.mod h1:r/611MRnZbd31sd9wNzoDKxYy4Tnt8WHnC4iDTAW3NY= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yomorun/yomo-codec-golang v0.2.1/go.mod h1:r/611MRnZbd31sd9wNzoDKxYy4Tnt8WHnC4iDTAW3NY= -github.com/yomorun/yomo-codec-golang v0.4.0 h1:/6jQ5rhY5B6Q8qCk2zJF31WUsBkFsLHOF5dTQZyc+ZA= -github.com/yomorun/yomo-codec-golang v0.4.0/go.mod h1:r/611MRnZbd31sd9wNzoDKxYy4Tnt8WHnC4iDTAW3NY= -github.com/yomorun/yomo-codec-golang v1.0.0 h1:kZu4hcgE08PFQO80UPBw+ygjTfovzMlwOK40anQb6TY= -github.com/yomorun/yomo-codec-golang v1.0.0/go.mod h1:jxcwjw+UU1l9W4yMgGkVyFvLyLqcjFZ88AZLBxyr5O4= github.com/yomorun/yomo-codec-golang v1.1.0 h1:ZpWBp2+JJi6LB5dctCMDJzdflgdOV+1AW8u6oiZruRg= github.com/yomorun/yomo-codec-golang v1.1.0/go.mod h1:jxcwjw+UU1l9W4yMgGkVyFvLyLqcjFZ88AZLBxyr5O4= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= -golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc= -golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f h1:kDxGY2VmgABOe55qheT/TFqUMtcTHnomIPS1iv3G4Ms= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +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= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +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= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/internal/cmd/dev/dev.go b/internal/cmd/dev/dev.go new file mode 100644 index 000000000..50b8ecd2f --- /dev/null +++ b/internal/cmd/dev/dev.go @@ -0,0 +1,93 @@ +package dev + +import ( + "fmt" + "log" + "os" + "plugin" + "strings" + + "github.com/spf13/cobra" + "github.com/yomorun/yomo/internal/dispatcher" + "github.com/yomorun/yomo/internal/serverless" + "github.com/yomorun/yomo/pkg/quic" + "github.com/yomorun/yomo/pkg/rx" +) + +// Options are the options for dev command. +type Options struct { + // Filename is the name of Serverless function file (default is app.go). + Filename string + // Port is the port number of UDP host for Serverless function (default is 4242). + Port int +} + +var opts = &Options{} + +var devCmd = &cobra.Command{ + Use: "dev", + Short: "Run a YoMo Serverless Function", + Long: "Run a YoMo Serverless Function in development mode", + Run: func(cmd *cobra.Command, args []string) { + if len(args) >= 2 && strings.HasSuffix(args[1], ".go") { + // the second arg of `yomo dev xxx.go` is a .go file + opts.Filename = args[1] + } + + // build the file first + log.Print("Building the Serverless Function File...") + soFile, err := serverless.Build(opts.Filename) + if err != nil { + log.Print("Build serverless file failure with err: ", err) + return + } + + // load handle + slHandler, err := serverless.LoadHandle(soFile) + if err != nil { + log.Print("Load handle from .so file failure with err: ", err) + return + } + + // serve the Serverless app + endpoint := fmt.Sprintf("0.0.0.0:%d", opts.Port) + quicHandler := &quicServerHandler{ + serverlessHandle: slHandler, + } + + err = serverless.Run(endpoint, quicHandler) + if err != nil { + log.Print("Run the serverless failure with err: ", err) + } + }, +} + +// Execute executes the run command. +func Execute() { + if err := devCmd.Execute(); err != nil { + log.Print(err) + os.Exit(1) + } +} + +func init() { + devCmd.Flags().StringVar(&opts.Filename, "file-name", "app.go", "Serverless function file (default is app.go)") + devCmd.Flags().IntVar(&opts.Port, "port", 4242, "Port is the port number of UDP host for Serverless function (default is 4242)") +} + +type quicServerHandler struct { + serverlessHandle plugin.Symbol +} + +func (s quicServerHandler) Read(st quic.Stream) error { + stream := dispatcher.Dispatcher(s.serverlessHandle, rx.FromReader(st)) + + go func() { + for customer := range stream.Observe() { + if customer.Error() { + fmt.Println(customer.E.Error()) + } + } + }() + return nil +} diff --git a/internal/dispatcher/dispatcher.go b/internal/dispatcher/dispatcher.go new file mode 100644 index 000000000..a1af6eb35 --- /dev/null +++ b/internal/dispatcher/dispatcher.go @@ -0,0 +1,32 @@ +package dispatcher + +import ( + "path/filepath" + "plugin" + + "github.com/yomorun/yomo/internal/serverless" + "github.com/yomorun/yomo/pkg/rx" +) + +func Dispatcher(hanlder plugin.Symbol, rxstream rx.RxStream) rx.RxStream { + return hanlder.(func(rxStream rx.RxStream) rx.RxStream)(rxstream) +} + +func AutoDispatcher(appPath string, rxstream rx.RxStream) (rx.RxStream, error) { + file := appPath + // skip building if the extension is not .go + // For example, already built as .so in the previous step. + if filepath.Ext(appPath) == ".go" { + sofile, err := serverless.Build(appPath) + if err != nil { + return nil, err + } + file = sofile + } + + handler, err := serverless.LoadHandle(file) + if err != nil { + return nil, err + } + return Dispatcher(handler, rxstream), nil +} diff --git a/internal/framework/server.go b/internal/framework/server.go deleted file mode 100644 index ab7df1d2a..000000000 --- a/internal/framework/server.go +++ /dev/null @@ -1,12 +0,0 @@ -package framework - -import ( - "github.com/yomorun/yomo-codec-golang/pkg/codes" - "github.com/yomorun/yomo/pkg/plugin" - "github.com/yomorun/yomo/pkg/util" -) - -func NewServer(endpoint string, p plugin.YomoObjectPlugin) { - codec := codes.NewCodec(p.Observed()) - util.QuicServer(endpoint, p, codec) -} diff --git a/internal/serverless/build.go b/internal/serverless/build.go new file mode 100644 index 000000000..e8b75d2e9 --- /dev/null +++ b/internal/serverless/build.go @@ -0,0 +1,35 @@ +package serverless + +import ( + "errors" + "os/exec" + "path/filepath" + "runtime" +) + +func Build(appPath string) (string, error) { + version := runtime.GOOS + dir, _ := filepath.Split(appPath) + sl := dir + "sl.so" + + if version == "linux" { + cmd := exec.Command("/bin/sh", "-c", "CGO_ENABLED=1 GOOS=linux go build -buildmode=plugin -o "+sl+" "+appPath) + err := cmd.Start() + if err != nil { + return "", err + } + err = cmd.Wait() + return sl, err + } else if version == "darwin" { + cmd := exec.Command("/bin/sh", "-c", "go build -buildmode=plugin -o "+sl+" "+appPath) + err := cmd.Start() + if err != nil { + return "", err + } + err = cmd.Wait() + return sl, err + } else { + return "", errors.New("Not Implemented") + } + +} diff --git a/internal/serverless/load.go b/internal/serverless/load.go new file mode 100644 index 000000000..f45c314de --- /dev/null +++ b/internal/serverless/load.go @@ -0,0 +1,22 @@ +package serverless + +import ( + "fmt" + "plugin" +) + +func LoadHandle(filePath string) (plugin.Symbol, error) { + plugin, err := plugin.Open(filePath) + if err != nil { + fmt.Println("open plugin error", err) + return nil, err + } + + handler, err := plugin.Lookup("Handler") + if err != nil { + fmt.Println("lookup plugin error", err) + return nil, err + } + + return handler, nil +} diff --git a/internal/serverless/runtime.go b/internal/serverless/runtime.go new file mode 100644 index 000000000..1e7bb1b00 --- /dev/null +++ b/internal/serverless/runtime.go @@ -0,0 +1,14 @@ +package serverless + +import ( + "context" + + "github.com/yomorun/yomo/pkg/quic" +) + +// Run serves the Serverless function +func Run(endpoint string, handle quic.ServerHandler) error { + server := quic.NewServer(handle) + + return server.ListenAndServe(context.Background(), endpoint) +} diff --git a/pkg/env/env.go b/pkg/env/env.go deleted file mode 100644 index 3ec279cd4..000000000 --- a/pkg/env/env.go +++ /dev/null @@ -1,42 +0,0 @@ -package env - -import ( - "os" - "strconv" -) - -// GetBool gets the bool value from env -func GetBool(key string, defaultValue bool) bool { - value := os.Getenv(key) - if len(value) != 0 { - flag, err := strconv.ParseBool(value) - if err != nil { - return defaultValue - } - return flag - } - return defaultValue -} - -// GetInt gets the int value from env -func GetInt(key string, defaultValue int) int { - value := os.Getenv(key) - if len(value) != 0 { - result, err := strconv.Atoi(value) - if err != nil { - return defaultValue - } - - return result - } - return defaultValue -} - -// GetString gets the string value from env -func GetString(key string, defaultValue string) string { - value := os.Getenv(key) - if len(value) != 0 { - return value - } - return defaultValue -} diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go deleted file mode 100644 index 62f107da2..000000000 --- a/pkg/plugin/plugin.go +++ /dev/null @@ -1,14 +0,0 @@ -package plugin - -type YomoObjectPlugin interface { - Handle(value interface{}) (interface{}, error) - Observed() string - Mold() interface{} - Name() string -} - -type YomoStreamPlugin interface { - HandleStream(buf []byte, done bool) ([]byte, error) - Observed() string - Name() string -} diff --git a/pkg/pprof/pprof.go b/pkg/pprof/pprof.go deleted file mode 100644 index 0a297c725..000000000 --- a/pkg/pprof/pprof.go +++ /dev/null @@ -1,70 +0,0 @@ -package pprof - -import ( - "net/http" - "net/http/pprof" - "strings" - - "github.com/yomorun/yomo/pkg/util" - - "github.com/yomorun/yomo/pkg/env" -) - -type pprofConf struct { - Enabled bool - PathPrefix string - Endpoint string -} - -const ( - pprofEnabled = "YOMO_PPROF_ENABLED" - pathPrefix = "YOMO_PPROF_PATH_PREFIX" - endpoint = "YOMO_PPROF_ENDPOINT" -) - -func newEdgeConf() pprofConf { - conf := pprofConf{} - conf.Enabled = env.GetBool(pprofEnabled, false) - conf.PathPrefix = env.GetString(pathPrefix, "/debug/pprof/") - conf.Endpoint = env.GetString(endpoint, "0.0.0.0:6060") - return conf -} - -var logger = util.GetLogger("yomo::pprof") - -func Run() { - conf := newEdgeConf() - if conf.Enabled == false { - return - } - - mux := http.NewServeMux() - pathPrefix := conf.PathPrefix - mux.HandleFunc(pathPrefix, - func(w http.ResponseWriter, r *http.Request) { - name := strings.TrimPrefix(r.URL.Path, pathPrefix) - if name != "" { - pprof.Handler(name).ServeHTTP(w, r) - return - } - pprof.Index(w, r) - }) - mux.HandleFunc(pathPrefix+"cmdline", pprof.Cmdline) - mux.HandleFunc(pathPrefix+"profile", pprof.Profile) - mux.HandleFunc(pathPrefix+"symbol", pprof.Symbol) - mux.HandleFunc(pathPrefix+"trace", pprof.Trace) - - server := http.Server{ - Addr: conf.Endpoint, - Handler: mux, - } - - logger.Infof("PProf server start... http://%s%s\n", conf.Endpoint, conf.PathPrefix) - if err := server.ListenAndServe(); err != nil { - if err == http.ErrServerClosed { - logger.Errorf("PProf server closed.") - } else { - logger.Errorf("PProf server error: %v", err) - } - } -} diff --git a/pkg/quic/quic-go.go b/pkg/quic/quic-go.go new file mode 100644 index 000000000..a0c6a5b88 --- /dev/null +++ b/pkg/quic/quic-go.go @@ -0,0 +1,140 @@ +package quic + +import ( + "bytes" + "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "log" + "math/big" + "net" + "time" + + quicGo "github.com/lucas-clemente/quic-go" +) + +type quicGoServer struct { + handler ServerHandler +} + +func (s *quicGoServer) SetHandler(handler ServerHandler) { + s.handler = handler +} + +func (s *quicGoServer) ListenAndServe(ctx context.Context, addr string) error { + // Lock to use QUIC draft-29 version + conf := &quicGo.Config{ + Versions: []quicGo.VersionNumber{0xff00001d}, + MaxIdleTimeout: time.Minute * 10080, + KeepAlive: true, + MaxIncomingStreams: 1000000, + MaxIncomingUniStreams: 1000000, + } + + // listen the address + listener, err := quicGo.ListenAddr(addr, generateTLSConfig(addr), conf) + if err != nil { + return err + } + + // serve + log.Print("QUIC Server listens on ", addr) + for { + session, err := listener.Accept(context.Background()) + if err != nil { + return err + } + + stream, err := session.AcceptStream(context.Background()) + if err != nil { + return err + } + + if s.handler != nil { + s.handler.Read(stream) + } else { + log.Print("handler isn't set in QUIC server") + } + } +} + +// generateTLSConfig Setup a bare-bones TLS config for the server +func generateTLSConfig(host ...string) *tls.Config { + tlsCert, _ := generateCertificate(host...) + + return &tls.Config{ + Certificates: []tls.Certificate{tlsCert}, + NextProtos: []string{"hq-29"}, + //NextProtos: []string{"http/1.1"}, + } +} + +func generateCertificate(host ...string) (tls.Certificate, error) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return tls.Certificate{}, err + } + + notBefore := time.Now() + notAfter := notBefore.Add(time.Hour * 24 * 365) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + return tls.Certificate{}, err + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"YoMo"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + for _, h := range host { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return tls.Certificate{}, err + } + + // create public key + certOut := bytes.NewBuffer(nil) + err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + if err != nil { + return tls.Certificate{}, err + } + + // create private key + keyOut := bytes.NewBuffer(nil) + b, err := x509.MarshalECPrivateKey(priv) + if err != nil { + return tls.Certificate{}, err + } + err = pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) + if err != nil { + return tls.Certificate{}, err + } + + return tls.X509KeyPair(certOut.Bytes(), keyOut.Bytes()) +} diff --git a/pkg/quic/server.go b/pkg/quic/server.go new file mode 100644 index 000000000..d3147f8bd --- /dev/null +++ b/pkg/quic/server.go @@ -0,0 +1,25 @@ +package quic + +import "context" + +// Server is the QUIC server +type Server interface { + // SetHandler sets QUIC callbacks. + SetHandler(handler ServerHandler) + + // ListenAndServe starts listening on UDP network address addr and + // serves incoming packets. + ListenAndServe(ctx context.Context, addr string) error +} + +// ServerHandler defines interface to handle the QUIC stream callbacks. +type ServerHandler interface { + Read(st Stream) error +} + +// NewServer inits the default implementation of QUIC server +func NewServer(handle ServerHandler) Server { + server := &quicGoServer{} + server.SetHandler(handle) + return server +} diff --git a/pkg/quic/stream.go b/pkg/quic/stream.go new file mode 100644 index 000000000..7a295d7fa --- /dev/null +++ b/pkg/quic/stream.go @@ -0,0 +1,20 @@ +package quic + +import "io" + +// Stream is the QUIC stream +type Stream interface { + ReceiveStream + SendStream +} + +// ReceiveStream is an unidirectional Receive Stream. +type ReceiveStream interface { + io.Reader +} + +// A SendStream is an unidirectional Send Stream. +type SendStream interface { + io.Writer + io.Closer +} diff --git a/pkg/rx/rxstream.go b/pkg/rx/rxstream.go new file mode 100644 index 000000000..55af069ab --- /dev/null +++ b/pkg/rx/rxstream.go @@ -0,0 +1,91 @@ +package rx + +import ( + "context" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/reactivex/rxgo/v2" +) + +type RxStream interface { + rxgo.Iterable + Y3Decoder(key string, mold interface{}, opts ...rxgo.Option) RxStream + StdOut(opts ...rxgo.Option) RxStream + AuditTime(timespan time.Duration, opts ...rxgo.Option) RxStream + DefaultIfEmptyWithTime(timespan time.Duration, defaultValue interface{}, opts ...rxgo.Option) RxStream + All(predicate rxgo.Predicate, opts ...rxgo.Option) RxStream + AverageFloat32(opts ...rxgo.Option) RxStream + AverageFloat64(opts ...rxgo.Option) RxStream + AverageInt(opts ...rxgo.Option) RxStream + AverageInt8(opts ...rxgo.Option) RxStream + AverageInt16(opts ...rxgo.Option) RxStream + AverageInt32(opts ...rxgo.Option) RxStream + AverageInt64(opts ...rxgo.Option) RxStream + BackOffRetry(backOffCfg backoff.BackOff, opts ...rxgo.Option) RxStream + BufferWithCount(count int, opts ...rxgo.Option) RxStream + BufferWithTime(timespan rxgo.Duration, opts ...rxgo.Option) RxStream + BufferWithTimeOrCount(timespan rxgo.Duration, count int, opts ...rxgo.Option) RxStream + Connect(ctx context.Context) (context.Context, rxgo.Disposable) + Contains(equal rxgo.Predicate, opts ...rxgo.Option) RxStream + Count(opts ...rxgo.Option) RxStream + Debounce(timespan rxgo.Duration, opts ...rxgo.Option) RxStream + DefaultIfEmpty(defaultValue interface{}, opts ...rxgo.Option) RxStream + Distinct(apply rxgo.Func, opts ...rxgo.Option) RxStream + DistinctUntilChanged(apply rxgo.Func, opts ...rxgo.Option) RxStream + DoOnCompleted(completedFunc rxgo.CompletedFunc, opts ...rxgo.Option) rxgo.Disposed + DoOnError(errFunc rxgo.ErrFunc, opts ...rxgo.Option) rxgo.Disposed + DoOnNext(nextFunc rxgo.NextFunc, opts ...rxgo.Option) rxgo.Disposed + ElementAt(index uint, opts ...rxgo.Option) RxStream + Error(opts ...rxgo.Option) error + Errors(opts ...rxgo.Option) []error + Filter(apply rxgo.Predicate, opts ...rxgo.Option) RxStream + Find(find rxgo.Predicate, opts ...rxgo.Option) RxStream + First(opts ...rxgo.Option) RxStream + FirstOrDefault(defaultValue interface{}, opts ...rxgo.Option) RxStream + FlatMap(apply rxgo.ItemToObservable, opts ...rxgo.Option) RxStream + ForEach(nextFunc rxgo.NextFunc, errFunc rxgo.ErrFunc, completedFunc rxgo.CompletedFunc, opts ...rxgo.Option) rxgo.Disposed + GroupBy(length int, distribution func(rxgo.Item) int, opts ...rxgo.Option) RxStream + GroupByDynamic(distribution func(rxgo.Item) string, opts ...rxgo.Option) RxStream + IgnoreElements(opts ...rxgo.Option) RxStream + Join(joiner rxgo.Func2, right rxgo.Observable, timeExtractor func(interface{}) time.Time, window rxgo.Duration, opts ...rxgo.Option) RxStream + Last(opts ...rxgo.Option) RxStream + LastOrDefault(defaultValue interface{}, opts ...rxgo.Option) RxStream + Map(apply rxgo.Func, opts ...rxgo.Option) RxStream + Marshal(marshaller rxgo.Marshaller, opts ...rxgo.Option) RxStream + Max(comparator rxgo.Comparator, opts ...rxgo.Option) RxStream + Min(comparator rxgo.Comparator, opts ...rxgo.Option) RxStream + OnErrorResumeNext(resumeSequence rxgo.ErrorToObservable, opts ...rxgo.Option) RxStream + OnErrorReturn(resumeFunc rxgo.ErrorFunc, opts ...rxgo.Option) RxStream + OnErrorReturnItem(resume interface{}, opts ...rxgo.Option) RxStream + Reduce(apply rxgo.Func2, opts ...rxgo.Option) RxStream + Repeat(count int64, frequency rxgo.Duration, opts ...rxgo.Option) RxStream + Retry(count int, shouldRetry func(error) bool, opts ...rxgo.Option) RxStream + Run(opts ...rxgo.Option) rxgo.Disposed + Sample(iterable rxgo.Iterable, opts ...rxgo.Option) RxStream + Scan(apply rxgo.Func2, opts ...rxgo.Option) RxStream + SequenceEqual(iterable rxgo.Iterable, opts ...rxgo.Option) RxStream + Send(output chan<- rxgo.Item, opts ...rxgo.Option) + Serialize(from int, identifier func(interface{}) int, opts ...rxgo.Option) RxStream + Skip(nth uint, opts ...rxgo.Option) RxStream + SkipLast(nth uint, opts ...rxgo.Option) RxStream + SkipWhile(apply rxgo.Predicate, opts ...rxgo.Option) RxStream + StartWith(iterable rxgo.Iterable, opts ...rxgo.Option) RxStream + SumFloat32(opts ...rxgo.Option) RxStream + SumFloat64(opts ...rxgo.Option) RxStream + SumInt64(opts ...rxgo.Option) RxStream + Take(nth uint, opts ...rxgo.Option) RxStream + TakeLast(nth uint, opts ...rxgo.Option) RxStream + TakeUntil(apply rxgo.Predicate, opts ...rxgo.Option) RxStream + TakeWhile(apply rxgo.Predicate, opts ...rxgo.Option) RxStream + TimeInterval(opts ...rxgo.Option) RxStream + Timestamp(opts ...rxgo.Option) RxStream + ToMap(keySelector rxgo.Func, opts ...rxgo.Option) RxStream + ToMapWithValueSelector(keySelector, valueSelector rxgo.Func, opts ...rxgo.Option) RxStream + ToSlice(initialCapacity int, opts ...rxgo.Option) ([]interface{}, error) + Unmarshal(unmarshaller rxgo.Unmarshaller, factory func() interface{}, opts ...rxgo.Option) RxStream + WindowWithCount(count int, opts ...rxgo.Option) RxStream + WindowWithTime(timespan rxgo.Duration, opts ...rxgo.Option) RxStream + WindowWithTimeOrCount(timespan rxgo.Duration, count int, opts ...rxgo.Option) RxStream + ZipFromIterable(iterable rxgo.Iterable, zipper rxgo.Func2, opts ...rxgo.Option) RxStream +} diff --git a/pkg/rx/rxstream_operator.go b/pkg/rx/rxstream_operator.go new file mode 100644 index 000000000..2c76ec654 --- /dev/null +++ b/pkg/rx/rxstream_operator.go @@ -0,0 +1,466 @@ +package rx + +import ( + "context" + "fmt" + "io" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/reactivex/rxgo/v2" + "github.com/yomorun/yomo-codec-golang/pkg/codes" +) + +func FromReader(reader io.Reader) RxStream { + next := make(chan rxgo.Item) + + go func() { + for { + buf := make([]byte, 3*1024) + n, err := reader.Read(buf) + + if err != nil { + return + } else { + value := buf[:n] + next <- Of(value) + } + } + }() + + return ConvertObservable(rxgo.FromChannel(next)) +} + +func Of(i interface{}) rxgo.Item { + return rxgo.Item{V: i} +} + +type RxStreamImpl struct { + observable rxgo.Observable +} + +func (s *RxStreamImpl) All(predicate rxgo.Predicate, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.All(predicate, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) AverageFloat32(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.AverageFloat32(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) AverageFloat64(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.AverageFloat64(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) AverageInt(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.AverageInt(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) AverageInt8(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.AverageInt8(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) AverageInt16(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.AverageInt16(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) AverageInt32(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.AverageInt32(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) AverageInt64(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.AverageInt64(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) BackOffRetry(backOffCfg backoff.BackOff, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.BackOffRetry(backOffCfg, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) BufferWithCount(count int, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.BufferWithCount(count, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) BufferWithTime(timespan rxgo.Duration, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.BufferWithTime(timespan, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) BufferWithTimeOrCount(timespan rxgo.Duration, count int, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.BufferWithTimeOrCount(timespan, count, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Connect(ctx context.Context) (context.Context, rxgo.Disposable) { + return s.observable.Connect(ctx) +} + +func (s *RxStreamImpl) Contains(equal rxgo.Predicate, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Contains(equal, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Count(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Count(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Debounce(timespan rxgo.Duration, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Debounce(timespan, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) DefaultIfEmpty(defaultValue interface{}, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.DefaultIfEmpty(defaultValue, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Distinct(apply rxgo.Func, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Distinct(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) DistinctUntilChanged(apply rxgo.Func, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.DistinctUntilChanged(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) DoOnCompleted(completedFunc rxgo.CompletedFunc, opts ...rxgo.Option) rxgo.Disposed { + return s.observable.DoOnCompleted(completedFunc, opts...) +} + +func (s *RxStreamImpl) DoOnError(errFunc rxgo.ErrFunc, opts ...rxgo.Option) rxgo.Disposed { + return s.observable.DoOnError(errFunc, opts...) +} + +func (s *RxStreamImpl) DoOnNext(nextFunc rxgo.NextFunc, opts ...rxgo.Option) rxgo.Disposed { + return s.observable.DoOnNext(nextFunc, opts...) +} + +func (s *RxStreamImpl) Error(opts ...rxgo.Option) error { + return s.observable.Error(opts...) +} + +func (s *RxStreamImpl) Errors(opts ...rxgo.Option) []error { + return s.observable.Errors(opts...) +} + +func (s *RxStreamImpl) Filter(apply rxgo.Predicate, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Filter(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) ElementAt(index uint, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.ElementAt(index, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Find(find rxgo.Predicate, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Find(find, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) First(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.First(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) FirstOrDefault(defaultValue interface{}, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.FirstOrDefault(defaultValue, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) FlatMap(apply rxgo.ItemToObservable, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.FlatMap(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) ForEach(nextFunc rxgo.NextFunc, errFunc rxgo.ErrFunc, completedFunc rxgo.CompletedFunc, opts ...rxgo.Option) rxgo.Disposed { + return s.observable.ForEach(nextFunc, errFunc, completedFunc, opts...) +} + +func (s *RxStreamImpl) IgnoreElements(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.IgnoreElements(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Join(joiner rxgo.Func2, right rxgo.Observable, timeExtractor func(interface{}) time.Time, window rxgo.Duration, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Join(joiner, right, timeExtractor, window, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) GroupBy(length int, distribution func(rxgo.Item) int, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.GroupBy(length, distribution, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) GroupByDynamic(distribution func(rxgo.Item) string, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.GroupByDynamic(distribution, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Last(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Last(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) LastOrDefault(defaultValue interface{}, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.LastOrDefault(defaultValue, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Map(apply rxgo.Func, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Map(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Marshal(marshaller rxgo.Marshaller, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Marshal(marshaller, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Max(comparator rxgo.Comparator, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Max(comparator, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Min(comparator rxgo.Comparator, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Min(comparator, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) OnErrorResumeNext(resumeSequence rxgo.ErrorToObservable, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.OnErrorResumeNext(resumeSequence, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) OnErrorReturn(resumeFunc rxgo.ErrorFunc, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.OnErrorReturn(resumeFunc, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) OnErrorReturnItem(resume interface{}, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.OnErrorReturnItem(resume, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Reduce(apply rxgo.Func2, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Reduce(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Repeat(count int64, frequency rxgo.Duration, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Repeat(count, frequency, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Retry(count int, shouldRetry func(error) bool, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Retry(count, shouldRetry, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Run(opts ...rxgo.Option) rxgo.Disposed { + return s.observable.Run(opts...) +} + +func (s *RxStreamImpl) Sample(iterable rxgo.Iterable, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Sample(iterable, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Scan(apply rxgo.Func2, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Scan(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Send(output chan<- rxgo.Item, opts ...rxgo.Option) { + s.observable.Send(output, opts...) +} + +func (s *RxStreamImpl) SequenceEqual(iterable rxgo.Iterable, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.SequenceEqual(iterable, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Serialize(from int, identifier func(interface{}) int, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Serialize(from, identifier, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Skip(nth uint, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Skip(nth, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) SkipLast(nth uint, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.SkipLast(nth, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) SkipWhile(apply rxgo.Predicate, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.SkipWhile(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) StartWith(iterable rxgo.Iterable, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.StartWith(iterable, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) SumFloat32(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.SumFloat32(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) SumFloat64(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.SumFloat64(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) SumInt64(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.SumInt64(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Take(nth uint, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Take(nth, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) TakeLast(nth uint, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.TakeLast(nth, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) TakeUntil(apply rxgo.Predicate, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.TakeUntil(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) TakeWhile(apply rxgo.Predicate, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.TakeWhile(apply, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) TimeInterval(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.TimeInterval(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Timestamp(opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Timestamp(opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) ToMap(keySelector rxgo.Func, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.ToMap(keySelector, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) ToMapWithValueSelector(keySelector, valueSelector rxgo.Func, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.ToMapWithValueSelector(keySelector, valueSelector, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) ToSlice(initialCapacity int, opts ...rxgo.Option) ([]interface{}, error) { + return s.observable.ToSlice(initialCapacity, opts...) +} + +func (s *RxStreamImpl) Unmarshal(unmarshaller rxgo.Unmarshaller, factory func() interface{}, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.Unmarshal(unmarshaller, factory, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) WindowWithCount(count int, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.WindowWithCount(count, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) WindowWithTime(timespan rxgo.Duration, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.WindowWithTime(timespan, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) WindowWithTimeOrCount(timespan rxgo.Duration, count int, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.WindowWithTimeOrCount(timespan, count, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) ZipFromIterable(iterable rxgo.Iterable, zipper rxgo.Func2, opts ...rxgo.Option) RxStream { + return &RxStreamImpl{observable: rxgo.FromChannel(s.observable.ZipFromIterable(iterable, zipper, opts...).Observe(), opts...)} +} + +func (s *RxStreamImpl) Observe(opts ...rxgo.Option) <-chan rxgo.Item { + return s.observable.Observe(opts...) +} + +func (s *RxStreamImpl) DefaultIfEmptyWithTime(timespan time.Duration, defaultValue interface{}, opts ...rxgo.Option) RxStream { + f := func(ctx context.Context, next chan rxgo.Item) { + defer close(next) + observe := s.Observe(opts...) + + for { + select { + case <-ctx.Done(): + return + case item, ok := <-observe: + if !ok { + return + } + + if !item.SendContext(ctx, next) { + return + } + case <-time.After(timespan): + if !rxgo.Of(defaultValue).SendContext(ctx, next) { + return + } + } + } + } + return CreateObservable(f, opts...) +} + +func (s *RxStreamImpl) StdOut(opts ...rxgo.Option) RxStream { + f := func(ctx context.Context, next chan rxgo.Item) { + defer close(next) + observe := s.Observe(opts...) + + for { + select { + case <-ctx.Done(): + return + case item, ok := <-observe: + if !ok { + return + } + + if !item.Error() { + fmt.Println("[StdOut]: ", item.V) + } + + if !item.SendContext(ctx, next) { + return + } + } + } + } + return CreateObservable(f, opts...) +} + +func (s *RxStreamImpl) AuditTime(timespan time.Duration, opts ...rxgo.Option) RxStream { + o := s.observable.BufferWithTime(rxgo.WithDuration(timespan)).Map(func(_ context.Context, i interface{}) (interface{}, error) { + return i.([]interface{})[len(i.([]interface{}))-1], nil + }) + return ConvertObservable(o) +} + +func (s *RxStreamImpl) Y3Decoder(key string, mold interface{}, opts ...rxgo.Option) RxStream { + codec := codes.NewCodec(key) + + f := func(ctx context.Context, next chan rxgo.Item) { + defer close(next) + observe := s.Observe(opts...) + done := false + + go func() { + for !done { + select { + case <-ctx.Done(): + done = true + return + case item, ok := <-observe: + if !ok { + done = true + return + } + + if item.Error() { + done = true + return + } else { + codec.Decoder(item.V.([]byte)) + } + } + } + + }() + + for !done { + value, _ := codec.Read(mold) + + if value != nil { + if !rxgo.Of(value).SendContext(ctx, next) { + done = true + } + } else { + codec.Refresh(&infiniteWriter{}) + } + } + } + return CreateObservable(f, opts...) +} + +func CreateObservable(f func(ctx context.Context, next chan rxgo.Item), opts ...rxgo.Option) RxStream { + next := make(chan rxgo.Item) + ctx := context.Background() + go f(ctx, next) + return &RxStreamImpl{observable: rxgo.FromChannel(next, opts...)} +} + +func ConvertObservable(observable rxgo.Observable) RxStream { + return &RxStreamImpl{observable: observable} +} + +type infiniteWriter struct { + io.Writer +} + +func (i *infiniteWriter) Write(p []byte) (n int, err error) { + return len(p), nil +} diff --git a/pkg/util/log.go b/pkg/util/log.go deleted file mode 100644 index 1a70bcda3..000000000 --- a/pkg/util/log.go +++ /dev/null @@ -1,146 +0,0 @@ -package util - -import ( - "fmt" - "log" - "os" - "reflect" - "strings" - "sync" - "time" - - "github.com/yomorun/yomo/pkg/env" -) - -type LogLevel uint8 - -const ( - LogLevelNothing LogLevel = iota - LogLevelError - LogLevelInfo - LogLevelDebug -) - -const logEnv = "YOMO_LOG_LEVEL" - -type Logger interface { - SetLogLevel(LogLevel) - SetLogTimeFormat(format string) - WithPrefix(prefix string) Logger - Debug() bool - - Errorf(format string, args ...interface{}) - Infof(format string, args ...interface{}) - Debugf(format string, args ...interface{}) -} - -var DefaultLogger Logger - -type defaultLogger struct { - prefix string - - logLevel LogLevel - timeFormat string -} - -var _ Logger = &defaultLogger{} - -func (l *defaultLogger) SetLogLevel(level LogLevel) { - l.logLevel = level -} - -func (l *defaultLogger) SetLogTimeFormat(format string) { - log.SetFlags(0) - l.timeFormat = format -} - -func (l *defaultLogger) Debugf(format string, args ...interface{}) { - if l.logLevel == LogLevelDebug { - l.logMessage(format, args...) - } -} - -func (l *defaultLogger) Infof(format string, args ...interface{}) { - if l.logLevel >= LogLevelInfo { - l.logMessage(format, args...) - } -} - -func (l *defaultLogger) Errorf(format string, args ...interface{}) { - if l.logLevel >= LogLevelError { - l.logMessage(format, args...) - } -} - -func (l *defaultLogger) logMessage(format string, args ...interface{}) { - var pre string - - if len(l.timeFormat) > 0 { - pre = time.Now().Format(l.timeFormat) + " " - } - if len(l.prefix) > 0 { - pre += l.prefix + " " - } - log.Printf(pre+format, args...) -} - -func (l *defaultLogger) WithPrefix(prefix string) Logger { - if len(l.prefix) > 0 { - prefix = l.prefix + " " + prefix - } - return &defaultLogger{ - logLevel: l.logLevel, - timeFormat: l.timeFormat, - prefix: prefix, - } -} - -func (l *defaultLogger) Debug() bool { - return l.logLevel == LogLevelDebug -} - -func init() { - loadDefaultLogger() -} - -var mux sync.Mutex - -func loadDefaultLogger() { - mux.Lock() - defer mux.Unlock() - if DefaultLogger == nil { - DefaultLogger = &defaultLogger{} - DefaultLogger.SetLogLevel(readLoggingEnv()) - } -} - -func readLoggingEnv() LogLevel { - lvl := strings.ToLower(env.GetString(logEnv, "info")) - switch lvl { - case "": - return LogLevelNothing - case "debug": - return LogLevelDebug - case "info": - return LogLevelInfo - case "error": - return LogLevelError - default: - fmt.Fprintln(os.Stderr, "invalid log level") - return LogLevelNothing - } -} - -func GetLogger(prefix string) Logger { - if DefaultLogger == nil { - loadDefaultLogger() - } - return Logger.WithPrefix(DefaultLogger, prefix) -} - -func GetLoggerOf(obj interface{}) Logger { - if DefaultLogger == nil { - loadDefaultLogger() - } - return Logger.WithPrefix(DefaultLogger, reflect.TypeOf(obj).Name()) -} diff --git a/pkg/util/quic.go b/pkg/util/quic.go deleted file mode 100644 index dc91916ff..000000000 --- a/pkg/util/quic.go +++ /dev/null @@ -1,345 +0,0 @@ -package util - -import ( - "bytes" - "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "io" - "log" - "math/big" - "net" - "time" - - "github.com/lucas-clemente/quic-go" - quicGo "github.com/lucas-clemente/quic-go" - - "github.com/yomorun/yomo-codec-golang/pkg/codes" - "github.com/yomorun/yomo/pkg/plugin" -) - -var logger = GetLogger("yomo::quic") - -// YomoFrameworkStreamWriter is the stream of framework -type YomoFrameworkStreamWriter struct { - Name string - Codec codes.YomoCodec - Plugin plugin.YomoObjectPlugin - io.Writer -} - -func (w YomoFrameworkStreamWriter) Write(b []byte) (c int, e error) { - defer func() { - if err := recover(); err != nil { - logger.Errorf("Write error: %v, input data: %s", err, string(b[:])) - c = 0 - e = err.(error) - } - }() - - var ( - err error = nil - value interface{} - result interface{} - num = 0 - sum = 0 - ) - - w.Codec.Decoder(b) - - for { - value, err = w.Codec.Read(w.Plugin.Mold()) - if err != nil { - logger.Errorf("Codec.Read error: %s", err.Error()) - return sum, nil - } - - if value == nil { - num, err = w.Codec.Refresh(w.Writer) - if err != nil { - logger.Errorf("Codec.Refresh error: %s", err.Error()) - } - return sum + num, nil - } - - result, err = w.process(value) - if err != nil { - logger.Errorf("Plugin.Handle error: %s", err.Error()) - // if plugin handle has error, then write the value of the original - num, err = w.Codec.Write(w.Writer, value, w.Plugin.Mold()) - if err != nil { - logger.Errorf("Codec.Write error: %s", err.Error()) - return 0, err - } - return sum + num, nil - } - - logger.Debugf("Plugin.Handle result: %v", result) //debug: - if result == nil { - continue - } - - num, err = w.Codec.Write(w.Writer, result, w.Plugin.Mold()) - if err != nil { - logger.Errorf("Codec.Write error: %s", err.Error()) - break - } - sum = sum + num - num = 0 - } - - if sum > 0 { - return sum, nil - } - - if num > 0 { - return num, nil - } - return 0, nil -} - -func (w YomoFrameworkStreamWriter) process(value interface{}) (v interface{}, e error) { - defer func() { - if err := recover(); err != nil { - logger.Errorf("Plugin.Handle panic error: %v", err) - e = err.(error) - } - }() - - result, err := w.Plugin.Handle(value) - if err != nil { - logger.Errorf("Plugin.Handle error: %s", err.Error()) - } - return result, nil -} - -// QuicClient create new QUIC client -func QuicClient(endpoint string) (quicGo.Stream, error) { - tlsConf := &tls.Config{ - InsecureSkipVerify: true, // nolint - NextProtos: []string{"hq-29"}, - //NextProtos: []string{"http/1.1"}, - } - - session, err := quicGo.DialAddr(endpoint, tlsConf, &quic.Config{ - MaxIdleTimeout: time.Minute * 10080, - KeepAlive: true, - MaxIncomingStreams: 1000000, - MaxIncomingUniStreams: 1000000, - }) - - if err != nil { - return nil, err - } - - stream, err := session.OpenStreamSync(context.Background()) - if err != nil { - return nil, err - } - - return stream, nil -} - -// QuicServer create a QUIC server -func QuicServer(endpoint string, plugin plugin.YomoObjectPlugin, codec codes.YomoCodec) { - // Lock to use QUIC draft-29 version - conf := &quic.Config{ - Versions: []quicGo.VersionNumber{0xff00001d}, - MaxIdleTimeout: time.Minute * 10080, - KeepAlive: true, - MaxIncomingStreams: 1000000, - MaxIncomingUniStreams: 1000000, - } - listener, err := quicGo.ListenAddr(endpoint, GenerateTLSConfig(endpoint), conf) - - if err != nil { - panic(err) - } - - var n = 0 - for { - n = n + 1 - logger.Debugf("QuicServer::loop[%v] Accept before", n) - session, err := listener.Accept(context.Background()) - if err != nil { - logger.Errorf("QuicServer::Accept error: %s", err.Error()) - continue - } - logger.Debugf("QuicServer::loop[%v] Accept after: %v", n, session.ConnectionState()) - - logger.Debugf("QuicServer::loop[%v] AcceptStream before", n) - stream, err := session.AcceptStream(context.Background()) - if err != nil { - logger.Errorf("QuicServer::AcceptStream error: %s", err.Error()) - continue - } - logger.Debugf("QuicServer::loop[%v] AcceptStream after: %v", n, stream.StreamID()) - logger.Infof("QuicServer::Establish new Stream: StreamID=%v", stream.StreamID()) - - go func() { - go monitorContextErr(session, stream) - yStream := YomoFrameworkStreamWriter{plugin.Name(), codec, plugin, stream} - _, err = CopyTo(yStream, stream) - //_, err = io.Copy(yStream, stream) - if err != nil { - closeSession(session, stream) - } - }() - } -} - -func monitorContextErr(session quicGo.Session, stream quicGo.Stream) { - for { - var err error = nil - if session.Context().Err() != nil { - err = session.Context().Err() - logger.Errorf("session context error: %v", err) - } - if stream.Context().Err() != nil { - err = stream.Context().Err() - logger.Errorf("stream context error: %v", err) - } - if err != nil { - closeSession(session, stream) - break - } - time.Sleep(5 * time.Second) - } - -} - -func closeSession(session quicGo.Session, stream quicGo.Stream) { - var err error - - // close stream - streamID := stream.StreamID() - err = stream.Close() - if err != nil { - logger.Errorf("stream[%v] close error: %s", streamID, err.Error()) - } else { - logger.Infof("stream[%v] closed", streamID) - } - - // close session - err = session.CloseWithError(0, "close session") - if err != nil { - logger.Errorf("close session error: %s", err.Error()) - } -} - -// GenerateTLSConfig Setup a bare-bones TLS config for the server -func GenerateTLSConfig(host ...string) *tls.Config { - tlsCert, _ := Certificate(host...) - - return &tls.Config{ - Certificates: []tls.Certificate{tlsCert}, - NextProtos: []string{"hq-29"}, - //NextProtos: []string{"http/1.1"}, - } -} - -func Certificate(host ...string) (tls.Certificate, error) { - priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - return tls.Certificate{}, err - } - - notBefore := time.Now() - notAfter := notBefore.Add(time.Hour * 24 * 365) - - serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) - serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) - if err != nil { - return tls.Certificate{}, err - } - - template := x509.Certificate{ - SerialNumber: serialNumber, - Subject: pkix.Name{ - Organization: []string{"YoMo"}, - }, - NotBefore: notBefore, - NotAfter: notAfter, - - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - } - - for _, h := range host { - if ip := net.ParseIP(h); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, h) - } - } - - template.IsCA = true - template.KeyUsage |= x509.KeyUsageCertSign - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) - if err != nil { - return tls.Certificate{}, err - } - - // create public key - certOut := bytes.NewBuffer(nil) - err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - if err != nil { - return tls.Certificate{}, err - } - - // create private key - keyOut := bytes.NewBuffer(nil) - b, err := x509.MarshalECPrivateKey(priv) - if err != nil { - return tls.Certificate{}, err - } - err = pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) - if err != nil { - return tls.Certificate{}, err - } - - return tls.X509KeyPair(certOut.Bytes(), keyOut.Bytes()) -} - -func CopyTo(dst io.Writer, src io.Reader) (written int64, err error) { - var buf []byte - size := 32 * 1024 - if l, ok := src.(*io.LimitedReader); ok && int64(size) > l.N { - if l.N < 1 { - size = 1 - } else { - size = int(l.N) - } - } - buf = make([]byte, size) - - for { - nr, er := src.Read(buf) - if nr > 0 { - nw, ew := dst.Write(buf[0:nr]) - if nw > 0 { - written += int64(nw) - } - if ew != nil { - err = ew - log.Printf("dst.Write error: %s", ew.Error()) - break - } - } - if er != nil { - if er != io.EOF { - err = er - } - log.Printf("src.Read error: %s", er.Error()) - break - } - } - return written, err -} diff --git a/pkg/yomo/run.go b/pkg/yomo/run.go deleted file mode 100644 index b46e856d0..000000000 --- a/pkg/yomo/run.go +++ /dev/null @@ -1,172 +0,0 @@ -package yomo - -import ( - "fmt" - "io" - "log" - "time" - - y3 "github.com/yomorun/yomo-codec-golang" - "github.com/yomorun/yomo-codec-golang/pkg/codes" - "github.com/yomorun/yomo-codec-golang/pkg/codes/packetstructure" - - "github.com/yomorun/yomo-codec-golang/pkg/packetutils" - - "github.com/yomorun/yomo/configs" - - "github.com/yomorun/yomo/pkg/pprof" - - "github.com/yomorun/yomo/pkg/plugin" - "github.com/yomorun/yomo/pkg/util" - - "github.com/yomorun/yomo/internal/framework" -) - -var logger = util.GetLogger("yomo::run") - -// Run a server for YomoObjectPlugin -func Run(plugin plugin.YomoObjectPlugin, endpoint string) { - logger.Infof("plugin service [%s] start... [%s]", plugin.Name(), endpoint) - - // pprof - go pprof.Run() - - // activation service - framework.NewServer(endpoint, plugin) -} - -// RunStream run a server for YomoStreamPlugin -func RunStream(plugin plugin.YomoStreamPlugin, endpoint string) { - logger.Infof("plugin service [%s] start... [%s]", plugin.Name(), endpoint) - - // activation service - panic("not impl") -} - -// RunDev makes test plugin connect to a demo YoMo server -func RunDev(plugin plugin.YomoObjectPlugin, endpoint string) { - RunDevWith(plugin, endpoint, OutputPacketPrinter) -} - -type OutputFormatter int32 - -const ( - OutputHexString OutputFormatter = 0 - OutputPacketPrinter OutputFormatter = 1 - OutputEchoData OutputFormatter = 2 - OutputThermometerData OutputFormatter = 3 -) - -// RunDev makes test plugin connect to a demo YoMo server. OutputFormatter: OutputHexString/OutputPacketPrinter/OutputEchoData -func RunDevWith(plugin plugin.YomoObjectPlugin, endpoint string, formatter OutputFormatter) { - go func() { - logger.Infof("plugin service [%s] start... [%s]", plugin.Name(), endpoint) - - // activation service - framework.NewServer(endpoint, plugin) - }() - - yomoEchoClient, err := util.QuicClient(configs.DefaultEchoConf.EchoServerAddr) - //yomoEchoClient, err := util.QuicClient("localhost:11520") - if err != nil { - panic(err) - } - - yomoPluginClient, err := util.QuicClient(endpoint) - if err != nil { - panic(err) - } - - go io.Copy(yomoPluginClient, yomoEchoClient) // nolint - - // select formatter - var w io.Writer - switch formatter { - case OutputHexString: - w = &hexStringFormatter{} - case OutputPacketPrinter: - w = &packetPrinterFormatter{} - case OutputEchoData: - w = &echoDataFormatter{} - case OutputThermometerData: - w = &thermometerDataFormatter{} - default: - w = &packetPrinterFormatter{} - } - go util.CopyTo(w, yomoPluginClient) // nolint - - for { - time.Sleep(time.Second) - _, err = yomoEchoClient.Write([]byte("ping")) - if err != nil { - log.Fatal(err) - } - } -} - -// hexStringFormatter -type hexStringFormatter struct { - io.Writer -} - -func (w *hexStringFormatter) Write(b []byte) (int, error) { - fmt.Printf("%v:\t %s\n", time.Now().Format("2006-01-02 15:04:05"), packetutils.FormatBytes(b)) // debug: - return 0, nil -} - -// packetPrinterFormatter -type packetPrinterFormatter struct { - io.Writer -} - -func (w *packetPrinterFormatter) Write(b []byte) (int, error) { - res, _, _ := y3.DecodeNodePacket(b) - fmt.Printf("%v:\t", time.Now().Format("2006-01-02 15:04:05")) // debug: - packetutils.PrintNodePacket(res) - fmt.Println() - return 0, nil -} - -// echoDataFormatter -type echoDataFormatter struct { - io.Writer -} - -func (w *echoDataFormatter) Write(b []byte) (int, error) { - var mold = echoData{} - res, _, _ := y3.DecodeNodePacket(b) - _ = packetstructure.Decode(res, &mold) - fmt.Printf("%v:\t %v\n", time.Now().Format("2006-01-02 15:04:05"), mold) // debug: - return 0, nil -} - -type echoData struct { - Id int32 `yomo:"0x10"` - Name string `yomo:"0x11"` - Test test `yomo:"0x20"` -} - -type test struct { - Tag []string `yomo:"0x13"` -} - -// thermometerDataFormatter -type thermometerDataFormatter struct { - io.Writer -} - -func (w *thermometerDataFormatter) Write(b []byte) (int, error) { - var mold = []thermometerData{} - - proto := codes.NewProtoCodec(packetutils.KeyOf("0x20")) - proto.UnmarshalStruct(b, &mold) - fmt.Printf("%v:\t %v\n", time.Now().Format("2006-01-02 15:04:05"), mold) // debug: - return 0, nil -} - -type thermometerData struct { - Id string `yomo:"0x10"` - Temperature float32 `yomo:"0x11"` - Humidity float32 `yomo:"0x12"` - Stored bool `yomo:"0x13"` -}