diff --git a/.gitignore b/.gitignore index 66fd13c..fc8c26e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ # Dependency directories (remove the comment below to include it) # vendor/ + +.vscode \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6ae176a --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +.PHONY: bootstrap +bootstrap: + go get -u github.com/golangci/golangci-lint/cmd/golangci-lint + go get -u github.com/kyoh86/richgo + go get -v github.com/ramya-rao-a/go-outline + go get -v github.com/mdempsky/gocode + go get -v github.com/uudashr/gopkgs/cmd/gopkgs + go get -v golang.org/x/tools/cmd/goimports + go get -v golang.org/x/tools/cmd/goimports + +.PHONY: lint +lint: fmt + golangci-lint run + +.PHONY: test-flake +test-ci: + @echo "tests:" + richgo test -count=30 -v -cover ./... + +.PHONY: install-golang-ci +lint-ci: + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh + +.PHONY: generate +generate: + protoc --proto_path=orcareduce/model --go_out=orcareduce/model --go_opt=paths=source_relative datamodel.proto + +.PHONY: fmt +fmt: + @echo "fmt:" + scripts/fmt + diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..86aa9e6 --- /dev/null +++ b/OWNERS @@ -0,0 +1 @@ +mkuchenbecker \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e77d2b4 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/mkuchenbecker/orcareduce + +go 1.13 + +require ( + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b + github.com/golang/mock v1.3.1 + github.com/influxdata/influxdb v1.8.0 + github.com/pkg/errors v0.9.1 + github.com/stretchr/testify v1.5.1 + google.golang.org/protobuf v1.24.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e3839d6 --- /dev/null +++ b/go.sum @@ -0,0 +1,355 @@ +cloud.google.com/go v0.26.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.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.43.0/go.mod h1:BOSR3VbTLkk6FDC/TcffxP4NF/FFBGA5ku+jvKOP7pg= +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 v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +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/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +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/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= +github.com/apache/arrow/go/arrow v0.0.0-20191024131854-af6fa24be0db/go.mod h1:VTxUBvSJ3s3eHAg65PNgrsn5BtqCRPdmyXh6rAfdxN0= +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/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= +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-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ= +github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +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/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +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-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +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/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +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= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +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/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +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/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/flux v0.65.0/go.mod h1:BwN2XG2lMszOoquQaFdPET8FRQfrXiZsWmcMO9rkaVY= +github.com/influxdata/influxdb v1.8.0 h1:/X+G+i3udzHVxpBMuXdPZcUbkIE0ouT+6U+CzQTsOys= +github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ= +github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo= +github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE= +github.com/influxdata/promql/v2 v2.12.0/go.mod h1:fxOPu+DY0bqCTCECchSRtWfc+0X19ybifQhZoQNF5D8= +github.com/influxdata/roaring v0.4.13-0.20180809181101-fc520f41fab6/go.mod h1:bSgUQ7q5ZLSO+bKBGqJiCBGAl+9DxyW63zLTujjUlOE= +github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= +github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:Wbbw6tYNvwa5dlB6304Sd+82Z3f7PmVZHVKU637d4po= +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/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= +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/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= +github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg= +github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +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/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.0.3-0.20180606204148-bd9c31933947/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= +github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/term v0.0.0-20180730021639-bffc007b7fd5/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= +github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +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.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +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-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/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +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/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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-20181114220301-adae6a3d119a/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-20190311183353-d8887717615a/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-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-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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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-20180525024113-a5b4c53f6e8b/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/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-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/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-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= +gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +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-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +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-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200108215221-bd8f9a0ef82f/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +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.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +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.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +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= +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-20190418001031-e561f6794a2a/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.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/orcareduce/chaos.go b/orcareduce/chaos.go new file mode 100644 index 0000000..a23b811 --- /dev/null +++ b/orcareduce/chaos.go @@ -0,0 +1,29 @@ +package orcareduce + +// Injector is an object capable of both injecting latency and errors. It's primary use is to +// artificially increase the error rate and latency during testing to help simulate degraded +// performance. +// +// func (foo *Foo) DoSomethingReliable() error { +// if err := foo.ChaosInjector.Error(); err != nil { +// return err +// } +// foo.ChaosInjector.Latency() // Blocks the thread for a period of time. +// ... (remainder of function) +// } +type Injector interface { + Latency() + Error() error +} + +// ErrorInjector is an object that can inject errors into a process or function. It's primary +// use is to inject errors into otherwise reliable functions for testing purposes. +type ErrorInjector interface { + Error() error +} + +// LatencyInjector is an object that blocks the thread for a period of time. It's primary +// use is to inject latency into otherwise performant functions. +type LatencyInjector interface { + Latency() +} diff --git a/orcareduce/chaos/doc.go b/orcareduce/chaos/doc.go new file mode 100644 index 0000000..5b81ac9 --- /dev/null +++ b/orcareduce/chaos/doc.go @@ -0,0 +1,18 @@ +package chaos + +/* +chaos is a package to allow the random insertion of errors and latetency. The purpose of injecting latency and errors +is to build a system with the expectation that any function, no matter how reliable, can become transiently unreliable +and should be tested as such. + +NewDefault() is a basic meta-injector that will both inject chaos errors and latency. Use of this package would be: + +func (foo *Foo) DoSomethingReliable() error { + if err := foo.ChaosInjector.Error(); err != nil { + return err + } + foo.ChaosInjector.Latency() // Blocks the thread for a period of time. + ... (remainder of function) +} + +*/ diff --git a/orcareduce/chaos/errors.go b/orcareduce/chaos/errors.go new file mode 100644 index 0000000..8d1ebdc --- /dev/null +++ b/orcareduce/chaos/errors.go @@ -0,0 +1,41 @@ +package chaos + +import ( + "math/rand" + + "github.com/mkuchenbecker/orcareduce/orcareduce" +) + +type randomStaticErrors struct { + err error + errorRate float64 +} + +func (cfg *randomStaticErrors) Error() error { + if rand.Float64() <= cfg.errorRate { + return cfg.err + } + return nil +} + +func NewRandomStaticErrorInjector(err error, rate float64) orcareduce.ErrorInjector { + return &randomStaticErrors{err: err, errorRate: rate} +} + +type metaErrors struct { + errors []orcareduce.ErrorInjector +} + +func (cfg *metaErrors) Error() error { + for _, injector := range cfg.errors { + err := injector.Error() + if err != nil { + return err + } + } + return nil +} + +func NewMetaErrorInjector(errors []orcareduce.ErrorInjector) orcareduce.ErrorInjector { + return &metaErrors{errors: errors} +} diff --git a/orcareduce/chaos/errors_test.go b/orcareduce/chaos/errors_test.go new file mode 100644 index 0000000..b9b5518 --- /dev/null +++ b/orcareduce/chaos/errors_test.go @@ -0,0 +1,43 @@ +package chaos + +import ( + "fmt" + "math/rand" + "testing" + + "github.com/golang/mock/gomock" + "github.com/mkuchenbecker/orcareduce/orcareduce" + "github.com/mkuchenbecker/orcareduce/orcareduce/mock" + "github.com/stretchr/testify/assert" +) + +func TestRandomStaticErrors_100Percent(t *testing.T) { + errGeneric := fmt.Errorf("error") + errorInjector := NewRandomStaticErrorInjector(errGeneric, 1) + for i := 0; i < 100; i++ { + assert.Equal(t, errGeneric, errorInjector.Error()) + } +} + +func TestRandomStaticErrors_ZeroPercent(t *testing.T) { + errGeneric := fmt.Errorf("error") + errorInjector := NewRandomStaticErrorInjector(errGeneric, 0) + for i := 0; i < 100; i++ { + assert.Equal(t, nil, errorInjector.Error()) + } +} + +func TestMetaErrors(t *testing.T) { + errGeneric := fmt.Errorf("error") + t.Parallel() + rand.Seed(1) + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + mockError0 := mock.NewMockErrorInjector(mockCtrl) + mockError0.EXPECT().Error().Return(nil).Times(1) + mockError1 := mock.NewMockErrorInjector(mockCtrl) + mockError1.EXPECT().Error().Return(errGeneric).Times(1) + + meta := NewMetaErrorInjector([]orcareduce.ErrorInjector{mockError0, mockError1}) + assert.Equal(t, errGeneric, meta.Error()) +} diff --git a/orcareduce/chaos/latency.go b/orcareduce/chaos/latency.go new file mode 100644 index 0000000..a706ffa --- /dev/null +++ b/orcareduce/chaos/latency.go @@ -0,0 +1,72 @@ +package chaos + +import ( + "math/rand" + "time" + + "github.com/mkuchenbecker/orcareduce/orcareduce" +) + +type sleepFunction func(time.Duration) + +type staticLatency struct { + latency time.Duration + sleep sleepFunction +} + +func (cfg *staticLatency) Latency() { + time.Sleep(cfg.latency) +} + +func NewStaticLatency(latency time.Duration) orcareduce.LatencyInjector { + return &staticLatency{ + sleep: time.Sleep, + latency: latency, + } +} + +type dynamicLatency struct { + maxLatency time.Duration + sleep sleepFunction +} + +func (cfg *dynamicLatency) Latency() { + val := rand.Int63n(int64(cfg.maxLatency)) + cfg.sleep(time.Duration(val)) +} + +func NewDynamicLatency(maxLatency time.Duration) orcareduce.LatencyInjector { + return &dynamicLatency{ + sleep: time.Sleep, + maxLatency: maxLatency, + } +} + +type randomLatency struct { + latency orcareduce.LatencyInjector + percent float64 +} + +func (cfg *randomLatency) Latency() { + if rand.Float64() < cfg.percent { + cfg.latency.Latency() + } +} + +func NewRandomLatency(latency orcareduce.LatencyInjector, percent float64) orcareduce.LatencyInjector { + return &randomLatency{latency: latency, percent: percent} +} + +type metaLatency struct { + latency []orcareduce.LatencyInjector +} + +func (this *metaLatency) Latency() { + for _, latency := range this.latency { + latency.Latency() + } +} + +func NewMetaLatency(latency []orcareduce.LatencyInjector) orcareduce.LatencyInjector { + return &metaLatency{latency: latency} +} diff --git a/orcareduce/chaos/latency_test.go b/orcareduce/chaos/latency_test.go new file mode 100644 index 0000000..131c2a2 --- /dev/null +++ b/orcareduce/chaos/latency_test.go @@ -0,0 +1,66 @@ +package chaos + +import ( + "math/rand" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/mkuchenbecker/orcareduce/orcareduce" + "github.com/mkuchenbecker/orcareduce/orcareduce/mock" + "github.com/stretchr/testify/assert" +) + +func TestStaticLatency(t *testing.T) { + t.Parallel() + latency := 100 * time.Millisecond + static := NewStaticLatency(latency).(*staticLatency) + + static.sleep = func(duration time.Duration) { + assert.Equal(t, latency, duration) + } + + static.Latency() +} + +func TestDynamicLatency(t *testing.T) { + t.Parallel() + rand.Seed(1) + maxLatency := 100 * time.Millisecond + dynamic := NewDynamicLatency(maxLatency).(*dynamicLatency) + + dynamic.sleep = func(duration time.Duration) { + assert.True(t, maxLatency >= duration) + } + + dynamic.Latency() +} + +func TestRandomLatency(t *testing.T) { + t.Parallel() + rand.Seed(1) + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + mockLatency := mock.NewMockLatencyInjector(mockCtrl) + + random := NewRandomLatency(mockLatency, 1) + mockLatency.EXPECT().Latency().Times(1) + random.Latency() + + random = NewRandomLatency(mockLatency, 0) + random.Latency() +} + +func TestMetaLatency(t *testing.T) { + t.Parallel() + rand.Seed(1) + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + mockLatency0 := mock.NewMockLatencyInjector(mockCtrl) + mockLatency0.EXPECT().Latency().Times(1) + mockLatency1 := mock.NewMockLatencyInjector(mockCtrl) + mockLatency1.EXPECT().Latency().Times(1) + + meta := NewMetaLatency([]orcareduce.LatencyInjector{mockLatency0, mockLatency1}) + meta.Latency() +} diff --git a/orcareduce/chaos/meta.go b/orcareduce/chaos/meta.go new file mode 100644 index 0000000..7b7746b --- /dev/null +++ b/orcareduce/chaos/meta.go @@ -0,0 +1,41 @@ +package chaos + +import ( + "fmt" + "time" + + "github.com/mkuchenbecker/orcareduce/orcareduce" +) + +type metaInjector struct { + latency orcareduce.LatencyInjector + errors orcareduce.ErrorInjector +} + +func (cfg *metaInjector) Latency() { + cfg.latency.Latency() +} + +func (cfg *metaInjector) Error() error { + return cfg.errors.Error() +} + +func NewDefault() orcareduce.Injector { + return &metaInjector{ + latency: &metaLatency{ + latency: []orcareduce.LatencyInjector{ + NewStaticLatency(20 * time.Millisecond), + NewDynamicLatency(20 * time.Millisecond), + NewRandomLatency(NewDynamicLatency(100*time.Millisecond), 0.1), + NewRandomLatency(NewDynamicLatency(1000*time.Millisecond), 0.01), + NewRandomLatency(NewDynamicLatency(5000*time.Millisecond), 0.001), + NewRandomLatency(NewDynamicLatency(30*time.Second), 0.0001), + }, + }, + errors: &metaErrors{ + errors: []orcareduce.ErrorInjector{ + NewRandomStaticErrorInjector(fmt.Errorf("[chaos] encountered an error"), 0.1), + }, + }, + } +} diff --git a/orcareduce/chaos/meta_test.go b/orcareduce/chaos/meta_test.go new file mode 100644 index 0000000..ce88824 --- /dev/null +++ b/orcareduce/chaos/meta_test.go @@ -0,0 +1,26 @@ +package chaos + +import ( + "math/rand" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestDefaultChaos(t *testing.T) { + rand.Seed(5) // Static seed so the test is deterministic. + chaos := NewDefault() + + now := time.Now() + chaos.Latency() + assert.True(t, time.Since(now) >= time.Millisecond*20) + + errCount := 0 + for i := 0; i < 1000; i++ { + if err := chaos.Error(); err != nil { + errCount++ + } + } + assert.Equal(t, 105, errCount) +} diff --git a/orcareduce/database.go b/orcareduce/database.go new file mode 100644 index 0000000..e69de29 diff --git a/orcareduce/director/director.go b/orcareduce/director/director.go new file mode 100644 index 0000000..c660f48 --- /dev/null +++ b/orcareduce/director/director.go @@ -0,0 +1,76 @@ +package director + +import ( + "math" + "time" + + "github.com/mkuchenbecker/orcareduce/orcareduce" + "github.com/mkuchenbecker/orcareduce/orcareduce/exceptions" + "github.com/pkg/errors" +) + +type config struct { + maxAttempts int +} + +func (c *config) Backoff(attempt int) time.Duration { + return time.Duration(int64(math.Max(float64(attempt), 2))) * time.Second +} + +func (c *config) Attempts() int { + return c.maxAttempts +} + +type director struct { + actor orcareduce.Reactor + cfg config + handler exceptions.Handler + id orcareduce.ID + clock Clock +} + +func (d *director) Direct() (err error) { + defer func() { + err = errors.Wrap(err, "unable to direct") + }() + actor := d.actor + err = actor.Preconditions() + if err != nil { + return exceptions.PreconditionError(err.Error()) + } + err = actor.Act() + if err != nil { + return err + } + err = actor.SignalSuccess() + if err != nil { + return err + } + actor.Notify() + + return nil +} + +func (d *director) Run() (err error) { + defer d.handler.HandlePanic(&err) + runtime := NewRuntime(d.id, d.clock) + defer func() { + err := runtime.Save() + _ = d.handler.HandleError(err) + }() + + for i := 0; i < d.cfg.Attempts(); i++ { + _, endFunc := runtime.StartRun() + err := d.Direct() + endFunc(err, "") + if d.handler.HandleError(err) != nil { + return err + } + d.cfg.Backoff(i) + } + return nil +} + +func (d *director) RunAsync(err chan error) { + err <- d.Run() +} diff --git a/orcareduce/director/influx.go b/orcareduce/director/influx.go new file mode 100644 index 0000000..6cc9f71 --- /dev/null +++ b/orcareduce/director/influx.go @@ -0,0 +1,182 @@ +package director + +import ( + "fmt" + "log" + "time" + + "github.com/influxdata/influxdb/client/v2" + "github.com/mkuchenbecker/orcareduce/orcareduce" +) + +const ( + // MyDB specifies name of database + MyDB = "go_influx" +) + +type Product struct { + ID int `json:"id"` + Name string `json:"name"` + Price float32 `json:"price"` + Image string `json:"image"` + Description string `json:"description"` + Views string +} + +type Run interface { + StartTime() time.Time + EndTime() time.Time + Runtime() time.Duration + Error() error + Message() string + KeyValue() map[string]interface{} + // Add(key string, value interface{}) Run +} + +type EndFunc func(error, string) + +type Runtime interface { + ID() orcareduce.ID + Runs() []Run + StartRun() (Run, EndFunc) + Save() error +} + +type Clock interface { + Now() time.Time +} + +type wallClock struct{} + +func (w wallClock) Now() time.Time { + return time.Now() +} + +func NewClock() Clock { + return wallClock{} +} + +type simpleRuntime struct { + identifier orcareduce.ID + runs []Run + clock Clock +} + +func (this *simpleRuntime) ID() orcareduce.ID { + return this.identifier +} + +func (this *simpleRuntime) Runs() []Run { + return this.runs +} + +func (this *simpleRuntime) StartRun() (Run, EndFunc) { + run := &simpleRun{KV: make(map[string]interface{})} + run.ID = this.identifier.NewChild() + this.runs = append(this.runs, run) + run.Start = this.clock.Now() + return run, func(err error, msg string) { + run.End = this.clock.Now() + run.Err = err + run.Msg = msg + } +} + +func NewRuntime(parent orcareduce.ID, clock Clock) Runtime { + return &simpleRuntime{ + identifier: parent.NewScopedChild("runtime"), + clock: clock, + runs: make([]Run, 0), + } +} + +type simpleRun struct { + ID orcareduce.ID + Start time.Time + End time.Time + Err error + Msg string + KV map[string]interface{} +} + +func (this *simpleRun) StartTime() time.Time { + return this.Start +} +func (this *simpleRun) EndTime() time.Time { + return this.End +} +func (this *simpleRun) Runtime() time.Duration { + return this.End.Sub(this.Start) +} +func (this *simpleRun) Error() error { + return this.Err +} +func (this *simpleRun) Message() string { + return this.Msg +} +func (this *simpleRun) KeyValue() map[string]interface{} { + this.KV["ID"] = this.ID.String() + this.KV["startTime"] = this.StartTime() + this.KV["endTime"] = this.EndTime() + this.KV["runtime"] = this.Runtime() + this.KV["error"] = this.Runtime() + this.KV["Msg"] = this.Message() + return this.KV +} +func (this *simpleRun) Add(key string, value interface{}) Run { + this.KV[key] = value + return this +} + +func (s *simpleRuntime) Save() error { + c, err := client.NewHTTPClient(client.HTTPConfig{ + Addr: "http://localhost:8086", + }) + if err != nil { + return err + } + defer c.Close() + // Create a new point batch + bp, err := client.NewBatchPoints(client.BatchPointsConfig{ + Database: MyDB, + Precision: "ms", + }) + if err != nil { + return err + } + for i, run := range s.Runs() { + tags := map[string]string{ + "runtime": s.ID().String(), + "run": fmt.Sprintf("%d", i), + } + pt, err := client.NewPoint("run", tags, run.KeyValue(), time.Now()) + if err != nil { + log.Fatal(err) + } + bp.AddPoint(pt) + } + if err := c.Write(bp); err != nil { + return err + } + return nil +} + +// queryDB convenience function to query the database +func queryDB(cmd string) (res []client.Result, err error) { + q := client.Query{ + Command: cmd, + Database: MyDB, + } + c, err := client.NewHTTPClient(client.HTTPConfig{ + Addr: "http://localhost:8086", + }) + if response, err := c.Query(q); err == nil { + if response.Error() != nil { + return res, response.Error() + } + res = response.Results + } else { + return res, err + } + return res, nil +} diff --git a/orcareduce/exceptions/types.go b/orcareduce/exceptions/types.go new file mode 100644 index 0000000..db5f695 --- /dev/null +++ b/orcareduce/exceptions/types.go @@ -0,0 +1,18 @@ +package exceptions + +import ( + "fmt" +) + +// PreconditionError is a runtime error that indicates all the preconditions to run a function are not yet satisfied. +// It should generally be used and regarded as a non-serious error that is expected during the normal operation +// of a service or state machine. For example, while its possible to guarentee all preconditions for a function are +// met at the time the function is called it's not possible to guarentee they will still be met at the time of +// preconditions are checked. +type PreconditionError string + +// Error implements the error interface function so PreconditionError can be used +// as an error object. +func (err PreconditionError) Error() string { + return fmt.Sprintf("preconditions not met: %s", string(err)) +} diff --git a/orcareduce/exceptions/types_test.go b/orcareduce/exceptions/types_test.go new file mode 100644 index 0000000..39e0970 --- /dev/null +++ b/orcareduce/exceptions/types_test.go @@ -0,0 +1,12 @@ +package exceptions + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPreconditionError(t *testing.T) { + err := PreconditionError("error") + assert.Equal(t, "preconditions not met: error", err.Error()) +} diff --git a/orcareduce/handler.go b/orcareduce/handler.go new file mode 100644 index 0000000..4ff0eff --- /dev/null +++ b/orcareduce/handler.go @@ -0,0 +1,52 @@ +package orcareduce + +// Handler is an object that handles async tasks and the standard processing of errors accross an application +// and safe running of async tasks in the system. +type Handler interface { + // HandleError logs and stats the parameter error and returns that same error to the caller. + // Supplying a nil error is a no-op. The intent is to be able to have the following: + // + // func foo() error { + // err := bar() + // return handler.HandleError(err) + // } + // + HandleError(error) error + + // HandlePanic captures any panics and populates the parameter err pointer with details about the panic if the + // error is not already set. HandlePanic should be called via defer. + // + // func foo() error { + // var err error + // done := make(chan bool) + // runFunc := func () error { + // defer handler.HandlePanic(&err) // called via defer + // err := bar() // might panic + // done <- true + // } + // go runFunc() + // <- done + // return err + // } + // + HandlePanic(err *error) + + // RunAsync safely runs a RunFunc via a goroutine and returns a SyncFunc. + // + // runFunc := func () error { + // return bar("baz") + // } + // + // syncFunc := handler.RunAsync(runFunc) + // err := syncFunc() + // + // + RunAsync(f RunFunc) SyncFunc +} + +// A SyncFunc is a function with no parameters that returns an error. +// Calling a SyncFunc blocks the thread until the function is called. +type SyncFunc func() error + +// A RunFunc is a function with no parameters that returns an error. +type RunFunc func() error diff --git a/orcareduce/handler/handler.go b/orcareduce/handler/handler.go new file mode 100644 index 0000000..23a5b63 --- /dev/null +++ b/orcareduce/handler/handler.go @@ -0,0 +1,75 @@ +package handler + +import ( + "github.com/mkuchenbecker/orcareduce/orcareduce" + "github.com/mkuchenbecker/orcareduce/orcareduce/exceptions" + "github.com/pkg/errors" +) + +// workflowHandler is a private implementation of the Handler interface. +// It has basic functionality and an internal logger so it can log +// errors it encountners. +type workflowHandler struct { + logger orcareduce.Logger +} + +// HandleError logs an error at the appropriate level and returns +// the error as-is. If err is nil, nothing is logged and nil is +// returned. +func (w workflowHandler) HandleError(err error) (out error) { + out = err + if err == nil { + return nil + } + cause := errors.Cause(err) + switch cause.(type) { + case exceptions.PreconditionError: + w.logger.Infof(err.Error()) + return + default: + w.logger.Error(err, "encountered an error") + return + } +} + +// HandlePanic recovers from a panic, logs that the panic occured, and populates +// the err paramerter with details about the panic if err is supplied and not +// already populated. +func (w workflowHandler) HandlePanic(err *error) { + r := recover() + if r == nil { + return + } + w.logger.Errorf("encountered a panic: %+v", r) + if err == nil { + return + } + if *err == nil { + *err = errors.Errorf("encountered a panic: %+v", r) + } +} + +// RunAsync safely runs a RunFunc ansyncronously. It handles any errors that +// are returned by the RunFunc, and recovers from any panics in the RunFunc. +// The return value SyncFunc is a function that blocks until the RunFunc has +// completed execution. +func (w workflowHandler) RunAsync(f orcareduce.RunFunc) orcareduce.SyncFunc { + done := make(chan error) + go func() { + var err error + defer func() { + done <- err + }() + defer w.HandlePanic(&err) + err = f() + w.HandleError(err) + }() + return func() error { + return <-done + } +} + +// NewHandler makes a Handler using the supplied logger. +func NewHandler(logger orcareduce.Logger) orcareduce.Handler { + return workflowHandler{logger: logger} +} diff --git a/orcareduce/handler/handler_test.go b/orcareduce/handler/handler_test.go new file mode 100644 index 0000000..46b9da1 --- /dev/null +++ b/orcareduce/handler/handler_test.go @@ -0,0 +1,146 @@ +package handler + +import ( + "fmt" + "testing" + "time" + + "github.com/golang/mock/gomock" + "github.com/mkuchenbecker/orcareduce/orcareduce/exceptions" + "github.com/mkuchenbecker/orcareduce/orcareduce/mock" + "github.com/stretchr/testify/assert" +) + +var errGeneric = fmt.Errorf("error") + +func TestHandler_RunAsync(t *testing.T) { + t.Parallel() + + t.Run("SyncFunc Synchronizes", func(t *testing.T) { + t.Parallel() + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + mockLogger := mock.NewMockLogger(mockCtrl) + handler := NewHandler(mockLogger) + + i := 0 + runFunc := func() error { + time.Sleep(10 * time.Millisecond) + i = 1 + return nil + } + + sync := handler.RunAsync(runFunc) + assert.Equal(t, 0, i) + sync() + assert.Equal(t, 1, i) + }) + + t.Run("Panic Handled and Logged", func(t *testing.T) { + t.Parallel() + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + logs := mock.NewMockLogger(mockCtrl) + logs.EXPECT().Errorf("encountered a panic: %+v", gomock.Not(nil)).Times(1) + handler := NewHandler(logs) + + runFunc := func() error { + panic("") + } + sync := handler.RunAsync(runFunc) + sync() + }) +} + +func TestHandler_HandlePanic(t *testing.T) { + t.Parallel() + + t.Run("Function Does Not Panic", func(t *testing.T) { + t.Parallel() + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + mockLogger := mock.NewMockLogger(mockCtrl) + handler := NewHandler(mockLogger) + + noOpFunction := func() error { return nil } + sync := handler.RunAsync(noOpFunction) + assert.NoError(t, sync()) + }) + + t.Run("Panic Logs and Returns Error", func(t *testing.T) { + t.Parallel() + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + logs := mock.NewMockLogger(mockCtrl) + logs.EXPECT().Errorf("encountered a panic: %+v", gomock.Not(nil)).Times(1) + handler := NewHandler(logs) + + runFunc := func() error { + panic("") + } + sync := handler.RunAsync(runFunc) + err := sync() + assert.Error(t, err) + }) + + t.Run("Panic Populates Return Error and Logs Error", func(t *testing.T) { + t.Parallel() + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + logs := mock.NewMockLogger(mockCtrl) + logs.EXPECT().Error(errGeneric, "encountered an error").Times(1) + handler := NewHandler(logs) + + runFunc := func() error { + return errGeneric + } + sync := handler.RunAsync(runFunc) + assert.Equal(t, errGeneric, sync()) + }) + + t.Run("Panic Logs Error Nil Error", func(t *testing.T) { + t.Parallel() + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + logs := mock.NewMockLogger(mockCtrl) + logs.EXPECT().Errorf("encountered a panic: %+v", gomock.Not(nil)).Times(1) + handler := NewHandler(logs) + + runFunc := func() error { + panic("") + } + done := make(chan bool) + go func() { + defer func() { + done <- true + }() + defer handler.HandlePanic(nil) + _ = runFunc() + }() + <-done + }) +} + +func TestHandler_HandleError(t *testing.T) { + t.Parallel() + + t.Run("Precondition Error Logged as Info", func(t *testing.T) { + t.Parallel() + + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + mockLogger := mock.NewMockLogger(mockCtrl) + mockLogger.EXPECT().Infof("preconditions not met: error").Times(1) + handler := NewHandler(mockLogger) + + sourceError := exceptions.PreconditionError(errGeneric.Error()) + err := handler.HandleError(sourceError) + assert.Equal(t, sourceError, err) + }) +} diff --git a/orcareduce/identifier/identifier.go b/orcareduce/identifier/identifier.go new file mode 100644 index 0000000..39309e5 --- /dev/null +++ b/orcareduce/identifier/identifier.go @@ -0,0 +1,77 @@ +package identifier + +import ( + "bytes" + "crypto/sha256" + "fmt" + "strings" + "time" + + "github.com/mkuchenbecker/orcareduce/orcareduce" +) + +type identifier struct { + id string + scope orcareduce.Scope + depth int + parent *identifier +} + +func (this *identifier) Lineage() []orcareduce.ID { + out := make([]orcareduce.ID, 0) + curr := this + for { + out = append(out, curr) + if curr.parent == nil { + break + } + curr = curr.parent + } + return out +} + +func (this *identifier) String() string { + lineage := this.Lineage() + var buf bytes.Buffer + for i := len(lineage) - 1; i >= 0; i-- { + buf.WriteString(fmt.Sprintf("%s(%s)", lineage[i].Scope(), lineage[i].Value())) + buf.WriteString(".") + } + return strings.Trim(buf.String(), ".") +} + +func (this *identifier) Value() string { + return this.id +} + +func (this *identifier) Parent() orcareduce.ID { + return this.parent +} + +func (this *identifier) NewChild() orcareduce.ID { + out := New() + out.depth = this.depth + 1 + out.parent = this + return out +} + +func (this *identifier) Depth() int { + return this.depth +} + +func (this *identifier) Scope() orcareduce.Scope { + return this.scope +} + +func (this *identifier) WithScope(scope orcareduce.Scope) orcareduce.ID { + this.scope = scope + return this +} + +func New() *identifier { + sha := sha256.New() + val := sha.Sum([]byte(time.Now().String())) // Re-hash the parent ID. + return &identifier{ + id: string(val), + } +} diff --git a/orcareduce/identifier/identifier_test.go b/orcareduce/identifier/identifier_test.go new file mode 100644 index 0000000..90b07cd --- /dev/null +++ b/orcareduce/identifier/identifier_test.go @@ -0,0 +1,50 @@ +package identifier + +import ( + "fmt" + "testing" + + "github.com/mkuchenbecker/orcareduce/orcareduce" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIdentifierLineage(t *testing.T) { + id := New().WithScope("parent_scope") + child := id.NewChild().WithScope("child_scope") + grandchild := child.NewChild().WithScope("grandchild_scope") + + lineage := grandchild.Lineage() + require.Equal(t, 3, len(lineage)) + assert.Equal(t, id, lineage[2]) + assert.Equal(t, child, lineage[1]) + assert.Equal(t, grandchild, lineage[0]) +} + +func TestIdentifierValueStringEquivalence(t *testing.T) { + id := identifier{ + id: "bar", + scope: orcareduce.Scope("foo"), + } + assert.Equal(t, "foo(bar)", id.String()) +} + +func TestIdentifierNewChild(t *testing.T) { + id := New().WithScope("parent_scope") + child := id.NewChild().WithScope("child_scope") + grandchild := child.NewChild().WithScope("grandchild_scope") + assert.Equal(t, 0, id.Depth()) + assert.Equal(t, 1, child.Depth()) + assert.Equal(t, 2, grandchild.Depth()) + + assert.Equal(t, id, child.Parent()) + assert.Equal(t, child, grandchild.Parent()) + + assert.Equal(t, fmt.Sprintf("parent_scope(%s).child_scope(%s).grandchild_scope(%s)", + id.Value(), + child.Value(), + grandchild.Value(), + ), + grandchild.String(), + ) +} diff --git a/orcareduce/logger.go b/orcareduce/logger.go new file mode 100644 index 0000000..03120af --- /dev/null +++ b/orcareduce/logger.go @@ -0,0 +1,12 @@ +package orcareduce + +// Logger is an abstraction over a logging library. Its simple as to remain unopinionated on +// the logging mechanism. +type Logger interface { + // Errorf logs the format string and args as severity ERROR. + Errorf(format string, args ...interface{}) + // Error logs an error and message as severity ERROR. + Error(err error, msg string) + // Infof logs a format string and args as severity INFO. + Infof(format string, args ...interface{}) +} diff --git a/orcareduce/logger/logger.go b/orcareduce/logger/logger.go new file mode 100644 index 0000000..6212b4f --- /dev/null +++ b/orcareduce/logger/logger.go @@ -0,0 +1,35 @@ +package logger + +import ( + "github.com/golang/glog" + "github.com/mkuchenbecker/orcareduce/orcareduce" +) + +// DefaultLogger fetches a simple default logger. +// The logger it returns uses glog as a logger backend. +func DefaultLogger() orcareduce.Logger { + return logger{ + errorf: glog.Errorf, + infof: glog.Infof, + } +} + +type logger struct { + errorf func(string, ...interface{}) + infof func(string, ...interface{}) +} + +// Error logs the error and message as severity Error. +func (l logger) Error(err error, msg string) { + l.Errorf("%s: %+v", msg, err) +} + +// Infof logs the format string and args as severity Info. +func (l logger) Infof(format string, args ...interface{}) { + l.infof(format, args...) +} + +// Errorf logs the format string and args as severity Error +func (l logger) Errorf(format string, args ...interface{}) { + l.errorf(format, args...) +} diff --git a/orcareduce/logger/logger_test.go b/orcareduce/logger/logger_test.go new file mode 100644 index 0000000..4d137c8 --- /dev/null +++ b/orcareduce/logger/logger_test.go @@ -0,0 +1,56 @@ +package logger + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLogger_Errorf(t *testing.T) { + expectedFormat := "format %s" + arg := "arg" + + logger := logger{ + errorf: func(format string, args ...interface{}) { + assert.Equal(t, expectedFormat, format) + require.Equal(t, 1, len(args)) + assert.Equal(t, "arg", args[0].(string)) + }, + } + logger.Errorf(expectedFormat, arg) + DefaultLogger().Errorf(expectedFormat, arg) +} + +func TestLogger_Infof(t *testing.T) { + expectedFormat := "format %s" + arg := "arg" + + logger := logger{ + infof: func(format string, args ...interface{}) { + assert.Equal(t, expectedFormat, format) + require.Equal(t, 1, len(args)) + assert.Equal(t, "arg", args[0].(string)) + }, + } + logger.Infof(expectedFormat, arg) + DefaultLogger().Infof(expectedFormat, arg) +} + +func TestLogger_Error(t *testing.T) { + expectedError := fmt.Errorf("error") + expectedFormat := "%s: %+v" + expectedMessage := "msg" + + logger := logger{ + errorf: func(format string, args ...interface{}) { + assert.Equal(t, expectedFormat, format) + require.Equal(t, 2, len(args)) + assert.Equal(t, expectedMessage, args[0].(string)) + assert.Equal(t, expectedError, args[1].(error)) + }, + } + logger.Error(expectedError, expectedMessage) + DefaultLogger().Error(expectedError, expectedMessage) +} diff --git a/orcareduce/main.go b/orcareduce/main.go new file mode 100644 index 0000000..e69de29 diff --git a/orcareduce/mock/mock.go b/orcareduce/mock/mock.go new file mode 100644 index 0000000..9f8eed4 --- /dev/null +++ b/orcareduce/mock/mock.go @@ -0,0 +1,627 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/mkuchenbecker/orcareduce/orcareduce (interfaces: Reactor,Logger,Handler,Precondition,ID,Director,ActorDataSink,Injector,ErrorInjector,LatencyInjector) + +// Package mock is a generated GoMock package. +package mock + +import ( + gomock "github.com/golang/mock/gomock" + orcareduce "github.com/mkuchenbecker/orcareduce/orcareduce" + reflect "reflect" +) + +// MockReactor is a mock of Reactor interface +type MockReactor struct { + ctrl *gomock.Controller + recorder *MockReactorMockRecorder +} + +// MockReactorMockRecorder is the mock recorder for MockReactor +type MockReactorMockRecorder struct { + mock *MockReactor +} + +// NewMockReactor creates a new mock instance +func NewMockReactor(ctrl *gomock.Controller) *MockReactor { + mock := &MockReactor{ctrl: ctrl} + mock.recorder = &MockReactorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockReactor) EXPECT() *MockReactorMockRecorder { + return m.recorder +} + +// Act mocks base method +func (m *MockReactor) Act() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Act") + ret0, _ := ret[0].(error) + return ret0 +} + +// Act indicates an expected call of Act +func (mr *MockReactorMockRecorder) Act() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Act", reflect.TypeOf((*MockReactor)(nil).Act)) +} + +// ID mocks base method +func (m *MockReactor) ID() orcareduce.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ID") + ret0, _ := ret[0].(orcareduce.ID) + return ret0 +} + +// ID indicates an expected call of ID +func (mr *MockReactorMockRecorder) ID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockReactor)(nil).ID)) +} + +// Notify mocks base method +func (m *MockReactor) Notify() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Notify") +} + +// Notify indicates an expected call of Notify +func (mr *MockReactorMockRecorder) Notify() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Notify", reflect.TypeOf((*MockReactor)(nil).Notify)) +} + +// Preconditions mocks base method +func (m *MockReactor) Preconditions() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Preconditions") + ret0, _ := ret[0].(error) + return ret0 +} + +// Preconditions indicates an expected call of Preconditions +func (mr *MockReactorMockRecorder) Preconditions() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Preconditions", reflect.TypeOf((*MockReactor)(nil).Preconditions)) +} + +// SignalSuccess mocks base method +func (m *MockReactor) SignalSuccess() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SignalSuccess") + ret0, _ := ret[0].(error) + return ret0 +} + +// SignalSuccess indicates an expected call of SignalSuccess +func (mr *MockReactorMockRecorder) SignalSuccess() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignalSuccess", reflect.TypeOf((*MockReactor)(nil).SignalSuccess)) +} + +// MockLogger is a mock of Logger interface +type MockLogger struct { + ctrl *gomock.Controller + recorder *MockLoggerMockRecorder +} + +// MockLoggerMockRecorder is the mock recorder for MockLogger +type MockLoggerMockRecorder struct { + mock *MockLogger +} + +// NewMockLogger creates a new mock instance +func NewMockLogger(ctrl *gomock.Controller) *MockLogger { + mock := &MockLogger{ctrl: ctrl} + mock.recorder = &MockLoggerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockLogger) EXPECT() *MockLoggerMockRecorder { + return m.recorder +} + +// Error mocks base method +func (m *MockLogger) Error(arg0 error, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Error", arg0, arg1) +} + +// Error indicates an expected call of Error +func (mr *MockLoggerMockRecorder) Error(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockLogger)(nil).Error), arg0, arg1) +} + +// Errorf mocks base method +func (m *MockLogger) Errorf(arg0 string, arg1 ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Errorf", varargs...) +} + +// Errorf indicates an expected call of Errorf +func (mr *MockLoggerMockRecorder) Errorf(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Errorf", reflect.TypeOf((*MockLogger)(nil).Errorf), varargs...) +} + +// Infof mocks base method +func (m *MockLogger) Infof(arg0 string, arg1 ...interface{}) { + m.ctrl.T.Helper() + varargs := []interface{}{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + m.ctrl.Call(m, "Infof", varargs...) +} + +// Infof indicates an expected call of Infof +func (mr *MockLoggerMockRecorder) Infof(arg0 interface{}, arg1 ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Infof", reflect.TypeOf((*MockLogger)(nil).Infof), varargs...) +} + +// MockHandler is a mock of Handler interface +type MockHandler struct { + ctrl *gomock.Controller + recorder *MockHandlerMockRecorder +} + +// MockHandlerMockRecorder is the mock recorder for MockHandler +type MockHandlerMockRecorder struct { + mock *MockHandler +} + +// NewMockHandler creates a new mock instance +func NewMockHandler(ctrl *gomock.Controller) *MockHandler { + mock := &MockHandler{ctrl: ctrl} + mock.recorder = &MockHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockHandler) EXPECT() *MockHandlerMockRecorder { + return m.recorder +} + +// HandleError mocks base method +func (m *MockHandler) HandleError(arg0 error) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleError", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// HandleError indicates an expected call of HandleError +func (mr *MockHandlerMockRecorder) HandleError(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleError", reflect.TypeOf((*MockHandler)(nil).HandleError), arg0) +} + +// HandlePanic mocks base method +func (m *MockHandler) HandlePanic(arg0 *error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HandlePanic", arg0) +} + +// HandlePanic indicates an expected call of HandlePanic +func (mr *MockHandlerMockRecorder) HandlePanic(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandlePanic", reflect.TypeOf((*MockHandler)(nil).HandlePanic), arg0) +} + +// RunAsync mocks base method +func (m *MockHandler) RunAsync(arg0 orcareduce.RunFunc) orcareduce.SyncFunc { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunAsync", arg0) + ret0, _ := ret[0].(orcareduce.SyncFunc) + return ret0 +} + +// RunAsync indicates an expected call of RunAsync +func (mr *MockHandlerMockRecorder) RunAsync(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunAsync", reflect.TypeOf((*MockHandler)(nil).RunAsync), arg0) +} + +// MockPrecondition is a mock of Precondition interface +type MockPrecondition struct { + ctrl *gomock.Controller + recorder *MockPreconditionMockRecorder +} + +// MockPreconditionMockRecorder is the mock recorder for MockPrecondition +type MockPreconditionMockRecorder struct { + mock *MockPrecondition +} + +// NewMockPrecondition creates a new mock instance +func NewMockPrecondition(ctrl *gomock.Controller) *MockPrecondition { + mock := &MockPrecondition{ctrl: ctrl} + mock.recorder = &MockPreconditionMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockPrecondition) EXPECT() *MockPreconditionMockRecorder { + return m.recorder +} + +// Check mocks base method +func (m *MockPrecondition) Check() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Check") + ret0, _ := ret[0].(error) + return ret0 +} + +// Check indicates an expected call of Check +func (mr *MockPreconditionMockRecorder) Check() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Check", reflect.TypeOf((*MockPrecondition)(nil).Check)) +} + +// MockID is a mock of ID interface +type MockID struct { + ctrl *gomock.Controller + recorder *MockIDMockRecorder +} + +// MockIDMockRecorder is the mock recorder for MockID +type MockIDMockRecorder struct { + mock *MockID +} + +// NewMockID creates a new mock instance +func NewMockID(ctrl *gomock.Controller) *MockID { + mock := &MockID{ctrl: ctrl} + mock.recorder = &MockIDMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockID) EXPECT() *MockIDMockRecorder { + return m.recorder +} + +// Lineage mocks base method +func (m *MockID) Lineage() []orcareduce.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Lineage") + ret0, _ := ret[0].([]orcareduce.ID) + return ret0 +} + +// Lineage indicates an expected call of Lineage +func (mr *MockIDMockRecorder) Lineage() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lineage", reflect.TypeOf((*MockID)(nil).Lineage)) +} + +// NewChild mocks base method +func (m *MockID) NewChild() orcareduce.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewChild") + ret0, _ := ret[0].(orcareduce.ID) + return ret0 +} + +// NewChild indicates an expected call of NewChild +func (mr *MockIDMockRecorder) NewChild() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewChild", reflect.TypeOf((*MockID)(nil).NewChild)) +} + +// NewScopedChild mocks base method +func (m *MockID) NewScopedChild(arg0 orcareduce.Scope) orcareduce.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NewScopedChild", arg0) + ret0, _ := ret[0].(orcareduce.ID) + return ret0 +} + +// NewScopedChild indicates an expected call of NewScopedChild +func (mr *MockIDMockRecorder) NewScopedChild(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewScopedChild", reflect.TypeOf((*MockID)(nil).NewScopedChild), arg0) +} + +// Parent mocks base method +func (m *MockID) Parent() orcareduce.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Parent") + ret0, _ := ret[0].(orcareduce.ID) + return ret0 +} + +// Parent indicates an expected call of Parent +func (mr *MockIDMockRecorder) Parent() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Parent", reflect.TypeOf((*MockID)(nil).Parent)) +} + +// String mocks base method +func (m *MockID) String() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "String") + ret0, _ := ret[0].(string) + return ret0 +} + +// String indicates an expected call of String +func (mr *MockIDMockRecorder) String() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockID)(nil).String)) +} + +// Value mocks base method +func (m *MockID) Value() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Value") + ret0, _ := ret[0].(string) + return ret0 +} + +// Value indicates an expected call of Value +func (mr *MockIDMockRecorder) Value() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Value", reflect.TypeOf((*MockID)(nil).Value)) +} + +// MockDirector is a mock of Director interface +type MockDirector struct { + ctrl *gomock.Controller + recorder *MockDirectorMockRecorder +} + +// MockDirectorMockRecorder is the mock recorder for MockDirector +type MockDirectorMockRecorder struct { + mock *MockDirector +} + +// NewMockDirector creates a new mock instance +func NewMockDirector(ctrl *gomock.Controller) *MockDirector { + mock := &MockDirector{ctrl: ctrl} + mock.recorder = &MockDirectorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockDirector) EXPECT() *MockDirectorMockRecorder { + return m.recorder +} + +// Act mocks base method +func (m *MockDirector) Act() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Act") + ret0, _ := ret[0].(error) + return ret0 +} + +// Act indicates an expected call of Act +func (mr *MockDirectorMockRecorder) Act() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Act", reflect.TypeOf((*MockDirector)(nil).Act)) +} + +// GetNextActor mocks base method +func (m *MockDirector) GetNextActor() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetNextActor") + ret0, _ := ret[0].(error) + return ret0 +} + +// GetNextActor indicates an expected call of GetNextActor +func (mr *MockDirectorMockRecorder) GetNextActor() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNextActor", reflect.TypeOf((*MockDirector)(nil).GetNextActor)) +} + +// ID mocks base method +func (m *MockDirector) ID() orcareduce.ID { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ID") + ret0, _ := ret[0].(orcareduce.ID) + return ret0 +} + +// ID indicates an expected call of ID +func (mr *MockDirectorMockRecorder) ID() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ID", reflect.TypeOf((*MockDirector)(nil).ID)) +} + +// MockActorDataSink is a mock of ActorDataSink interface +type MockActorDataSink struct { + ctrl *gomock.Controller + recorder *MockActorDataSinkMockRecorder +} + +// MockActorDataSinkMockRecorder is the mock recorder for MockActorDataSink +type MockActorDataSinkMockRecorder struct { + mock *MockActorDataSink +} + +// NewMockActorDataSink creates a new mock instance +func NewMockActorDataSink(ctrl *gomock.Controller) *MockActorDataSink { + mock := &MockActorDataSink{ctrl: ctrl} + mock.recorder = &MockActorDataSinkMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockActorDataSink) EXPECT() *MockActorDataSinkMockRecorder { + return m.recorder +} + +// Attempt mocks base method +func (m *MockActorDataSink) Attempt(arg0 orcareduce.Reactor) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Attempt", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Attempt indicates an expected call of Attempt +func (mr *MockActorDataSinkMockRecorder) Attempt(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Attempt", reflect.TypeOf((*MockActorDataSink)(nil).Attempt), arg0) +} + +// Failure mocks base method +func (m *MockActorDataSink) Failure(arg0 orcareduce.Reactor) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Failure", arg0) +} + +// Failure indicates an expected call of Failure +func (mr *MockActorDataSinkMockRecorder) Failure(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Failure", reflect.TypeOf((*MockActorDataSink)(nil).Failure), arg0) +} + +// Success mocks base method +func (m *MockActorDataSink) Success(arg0 orcareduce.Reactor) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Success", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// Success indicates an expected call of Success +func (mr *MockActorDataSinkMockRecorder) Success(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Success", reflect.TypeOf((*MockActorDataSink)(nil).Success), arg0) +} + +// MockInjector is a mock of Injector interface +type MockInjector struct { + ctrl *gomock.Controller + recorder *MockInjectorMockRecorder +} + +// MockInjectorMockRecorder is the mock recorder for MockInjector +type MockInjectorMockRecorder struct { + mock *MockInjector +} + +// NewMockInjector creates a new mock instance +func NewMockInjector(ctrl *gomock.Controller) *MockInjector { + mock := &MockInjector{ctrl: ctrl} + mock.recorder = &MockInjectorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockInjector) EXPECT() *MockInjectorMockRecorder { + return m.recorder +} + +// Error mocks base method +func (m *MockInjector) Error() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Error") + ret0, _ := ret[0].(error) + return ret0 +} + +// Error indicates an expected call of Error +func (mr *MockInjectorMockRecorder) Error() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockInjector)(nil).Error)) +} + +// Latency mocks base method +func (m *MockInjector) Latency() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Latency") +} + +// Latency indicates an expected call of Latency +func (mr *MockInjectorMockRecorder) Latency() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Latency", reflect.TypeOf((*MockInjector)(nil).Latency)) +} + +// MockErrorInjector is a mock of ErrorInjector interface +type MockErrorInjector struct { + ctrl *gomock.Controller + recorder *MockErrorInjectorMockRecorder +} + +// MockErrorInjectorMockRecorder is the mock recorder for MockErrorInjector +type MockErrorInjectorMockRecorder struct { + mock *MockErrorInjector +} + +// NewMockErrorInjector creates a new mock instance +func NewMockErrorInjector(ctrl *gomock.Controller) *MockErrorInjector { + mock := &MockErrorInjector{ctrl: ctrl} + mock.recorder = &MockErrorInjectorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockErrorInjector) EXPECT() *MockErrorInjectorMockRecorder { + return m.recorder +} + +// Error mocks base method +func (m *MockErrorInjector) Error() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Error") + ret0, _ := ret[0].(error) + return ret0 +} + +// Error indicates an expected call of Error +func (mr *MockErrorInjectorMockRecorder) Error() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Error", reflect.TypeOf((*MockErrorInjector)(nil).Error)) +} + +// MockLatencyInjector is a mock of LatencyInjector interface +type MockLatencyInjector struct { + ctrl *gomock.Controller + recorder *MockLatencyInjectorMockRecorder +} + +// MockLatencyInjectorMockRecorder is the mock recorder for MockLatencyInjector +type MockLatencyInjectorMockRecorder struct { + mock *MockLatencyInjector +} + +// NewMockLatencyInjector creates a new mock instance +func NewMockLatencyInjector(ctrl *gomock.Controller) *MockLatencyInjector { + mock := &MockLatencyInjector{ctrl: ctrl} + mock.recorder = &MockLatencyInjectorMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockLatencyInjector) EXPECT() *MockLatencyInjectorMockRecorder { + return m.recorder +} + +// Latency mocks base method +func (m *MockLatencyInjector) Latency() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Latency") +} + +// Latency indicates an expected call of Latency +func (mr *MockLatencyInjectorMockRecorder) Latency() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Latency", reflect.TypeOf((*MockLatencyInjector)(nil).Latency)) +} diff --git a/orcareduce/mockgen.go b/orcareduce/mockgen.go new file mode 100644 index 0000000..8e77b71 --- /dev/null +++ b/orcareduce/mockgen.go @@ -0,0 +1,3 @@ +package orcareduce + +//go:generate mockgen -destination=./mock/mock.go -package=mock github.com/mkuchenbecker/orcareduce/orcareduce Reactor,Logger,Handler,Precondition,ID,Director,ActorDataSink,Injector,ErrorInjector,LatencyInjector diff --git a/orcareduce/model/datamodel.pb.go b/orcareduce/model/datamodel.pb.go new file mode 100644 index 0000000..383e35f --- /dev/null +++ b/orcareduce/model/datamodel.pb.go @@ -0,0 +1,615 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.24.0 +// protoc v3.7.1 +// source: datamodel.proto + +package model + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type ActorStatus int32 + +const ( + ActorStatus_UNKNOWN_ACTOR_STATE ActorStatus = 0 + ActorStatus_PENDING ActorStatus = 1 + ActorStatus_SUCCESS ActorStatus = 2 + ActorStatus_CANCELED ActorStatus = 3 +) + +// Enum value maps for ActorStatus. +var ( + ActorStatus_name = map[int32]string{ + 0: "UNKNOWN_ACTOR_STATE", + 1: "PENDING", + 2: "SUCCESS", + 3: "CANCELED", + } + ActorStatus_value = map[string]int32{ + "UNKNOWN_ACTOR_STATE": 0, + "PENDING": 1, + "SUCCESS": 2, + "CANCELED": 3, + } +) + +func (x ActorStatus) Enum() *ActorStatus { + p := new(ActorStatus) + *p = x + return p +} + +func (x ActorStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ActorStatus) Descriptor() protoreflect.EnumDescriptor { + return file_datamodel_proto_enumTypes[0].Descriptor() +} + +func (ActorStatus) Type() protoreflect.EnumType { + return &file_datamodel_proto_enumTypes[0] +} + +func (x ActorStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ActorStatus.Descriptor instead. +func (ActorStatus) EnumDescriptor() ([]byte, []int) { + return file_datamodel_proto_rawDescGZIP(), []int{0} +} + +type Run_Result int32 + +const ( + Run_UNKNOWN Run_Result = 0 + Run_FAILED_PRECONDITION Run_Result = 1 + Run_FAILED_OPERATION Run_Result = 2 + Run_FAILED_SUCCESS_SIGNAL Run_Result = 3 + Run_SUCCESS Run_Result = 4 +) + +// Enum value maps for Run_Result. +var ( + Run_Result_name = map[int32]string{ + 0: "UNKNOWN", + 1: "FAILED_PRECONDITION", + 2: "FAILED_OPERATION", + 3: "FAILED_SUCCESS_SIGNAL", + 4: "SUCCESS", + } + Run_Result_value = map[string]int32{ + "UNKNOWN": 0, + "FAILED_PRECONDITION": 1, + "FAILED_OPERATION": 2, + "FAILED_SUCCESS_SIGNAL": 3, + "SUCCESS": 4, + } +) + +func (x Run_Result) Enum() *Run_Result { + p := new(Run_Result) + *p = x + return p +} + +func (x Run_Result) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Run_Result) Descriptor() protoreflect.EnumDescriptor { + return file_datamodel_proto_enumTypes[1].Descriptor() +} + +func (Run_Result) Type() protoreflect.EnumType { + return &file_datamodel_proto_enumTypes[1] +} + +func (x Run_Result) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Run_Result.Descriptor instead. +func (Run_Result) EnumDescriptor() ([]byte, []int) { + return file_datamodel_proto_rawDescGZIP(), []int{1, 0} +} + +type Key struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Key) Reset() { + *x = Key{} + if protoimpl.UnsafeEnabled { + mi := &file_datamodel_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Key) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Key) ProtoMessage() {} + +func (x *Key) ProtoReflect() protoreflect.Message { + mi := &file_datamodel_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Key.ProtoReflect.Descriptor instead. +func (*Key) Descriptor() ([]byte, []int) { + return file_datamodel_proto_rawDescGZIP(), []int{0} +} + +func (x *Key) GetValue() string { + if x != nil { + return x.Value + } + return "" +} + +type Run struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActorID *Key `protobuf:"bytes,1,opt,name=ActorID,proto3" json:"ActorID,omitempty"` + DirectorID *Key `protobuf:"bytes,2,opt,name=DirectorID,proto3" json:"DirectorID,omitempty"` + Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` +} + +func (x *Run) Reset() { + *x = Run{} + if protoimpl.UnsafeEnabled { + mi := &file_datamodel_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Run) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Run) ProtoMessage() {} + +func (x *Run) ProtoReflect() protoreflect.Message { + mi := &file_datamodel_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Run.ProtoReflect.Descriptor instead. +func (*Run) Descriptor() ([]byte, []int) { + return file_datamodel_proto_rawDescGZIP(), []int{1} +} + +func (x *Run) GetActorID() *Key { + if x != nil { + return x.ActorID + } + return nil +} + +func (x *Run) GetDirectorID() *Key { + if x != nil { + return x.DirectorID + } + return nil +} + +func (x *Run) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *Run) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +type RunMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Runs []*Run `protobuf:"bytes,1,rep,name=runs,proto3" json:"runs,omitempty"` +} + +func (x *RunMetadata) Reset() { + *x = RunMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_datamodel_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RunMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunMetadata) ProtoMessage() {} + +func (x *RunMetadata) ProtoReflect() protoreflect.Message { + mi := &file_datamodel_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunMetadata.ProtoReflect.Descriptor instead. +func (*RunMetadata) Descriptor() ([]byte, []int) { + return file_datamodel_proto_rawDescGZIP(), []int{2} +} + +func (x *RunMetadata) GetRuns() []*Run { + if x != nil { + return x.Runs + } + return nil +} + +type StateMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StateId *Key `protobuf:"bytes,1,opt,name=state_id,json=stateId,proto3" json:"state_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` +} + +func (x *StateMetadata) Reset() { + *x = StateMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_datamodel_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StateMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StateMetadata) ProtoMessage() {} + +func (x *StateMetadata) ProtoReflect() protoreflect.Message { + mi := &file_datamodel_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StateMetadata.ProtoReflect.Descriptor instead. +func (*StateMetadata) Descriptor() ([]byte, []int) { + return file_datamodel_proto_rawDescGZIP(), []int{3} +} + +func (x *StateMetadata) GetStateId() *Key { + if x != nil { + return x.StateId + } + return nil +} + +func (x *StateMetadata) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *StateMetadata) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +type ActorState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Key *Key `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Status ActorStatus `protobuf:"varint,2,opt,name=status,proto3,enum=orcareduce.model.ActorStatus" json:"status,omitempty"` + Metadata *RunMetadata `protobuf:"bytes,3,opt,name=metadata,proto3" json:"metadata,omitempty"` + Revision uint64 `protobuf:"varint,4,opt,name=revision,proto3" json:"revision,omitempty"` +} + +func (x *ActorState) Reset() { + *x = ActorState{} + if protoimpl.UnsafeEnabled { + mi := &file_datamodel_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActorState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActorState) ProtoMessage() {} + +func (x *ActorState) ProtoReflect() protoreflect.Message { + mi := &file_datamodel_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActorState.ProtoReflect.Descriptor instead. +func (*ActorState) Descriptor() ([]byte, []int) { + return file_datamodel_proto_rawDescGZIP(), []int{4} +} + +func (x *ActorState) GetKey() *Key { + if x != nil { + return x.Key + } + return nil +} + +func (x *ActorState) GetStatus() ActorStatus { + if x != nil { + return x.Status + } + return ActorStatus_UNKNOWN_ACTOR_STATE +} + +func (x *ActorState) GetMetadata() *RunMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *ActorState) GetRevision() uint64 { + if x != nil { + return x.Revision + } + return 0 +} + +var File_datamodel_proto protoreflect.FileDescriptor + +var file_datamodel_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x64, 0x61, 0x74, 0x61, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x10, 0x6f, 0x72, 0x63, 0x61, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2e, 0x6d, 0x6f, + 0x64, 0x65, 0x6c, 0x22, 0x1b, 0x0a, 0x03, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x22, 0x8f, 0x02, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x2f, 0x0a, 0x07, 0x41, 0x63, 0x74, 0x6f, + 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x72, 0x63, 0x61, + 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x4b, 0x65, 0x79, + 0x52, 0x07, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x44, 0x12, 0x35, 0x0a, 0x0a, 0x44, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x6f, 0x72, 0x63, 0x61, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x49, 0x44, + 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x14, + 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x22, 0x6c, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0b, + 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x46, + 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x50, 0x52, 0x45, 0x43, 0x4f, 0x4e, 0x44, 0x49, 0x54, 0x49, + 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x4f, + 0x50, 0x45, 0x52, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x41, + 0x49, 0x4c, 0x45, 0x44, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x49, 0x47, + 0x4e, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, + 0x10, 0x04, 0x22, 0x38, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x29, 0x0a, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x6f, 0x72, 0x63, 0x61, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2e, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x52, 0x04, 0x72, 0x75, 0x6e, 0x73, 0x22, 0x77, 0x0a, 0x0d, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, + 0x08, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x6f, 0x72, 0x63, 0x61, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2e, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc3, 0x01, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x6f, 0x72, 0x63, 0x61, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2e, 0x6d, + 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x35, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, + 0x6f, 0x72, 0x63, 0x61, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x2e, 0x41, 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6f, 0x72, 0x63, 0x61, 0x72, 0x65, 0x64, + 0x75, 0x63, 0x65, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x52, 0x75, 0x6e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2a, 0x4e, 0x0a, 0x0b, 0x41, + 0x63, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x41, 0x43, 0x54, 0x4f, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, + 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, + 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x0c, 0x0a, + 0x08, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x42, 0x12, 0x5a, 0x10, 0x6f, + 0x72, 0x63, 0x61, 0x72, 0x65, 0x64, 0x75, 0x63, 0x65, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_datamodel_proto_rawDescOnce sync.Once + file_datamodel_proto_rawDescData = file_datamodel_proto_rawDesc +) + +func file_datamodel_proto_rawDescGZIP() []byte { + file_datamodel_proto_rawDescOnce.Do(func() { + file_datamodel_proto_rawDescData = protoimpl.X.CompressGZIP(file_datamodel_proto_rawDescData) + }) + return file_datamodel_proto_rawDescData +} + +var file_datamodel_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_datamodel_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_datamodel_proto_goTypes = []interface{}{ + (ActorStatus)(0), // 0: orcareduce.model.ActorStatus + (Run_Result)(0), // 1: orcareduce.model.Run.Result + (*Key)(nil), // 2: orcareduce.model.Key + (*Run)(nil), // 3: orcareduce.model.Run + (*RunMetadata)(nil), // 4: orcareduce.model.RunMetadata + (*StateMetadata)(nil), // 5: orcareduce.model.StateMetadata + (*ActorState)(nil), // 6: orcareduce.model.ActorState +} +var file_datamodel_proto_depIdxs = []int32{ + 2, // 0: orcareduce.model.Run.ActorID:type_name -> orcareduce.model.Key + 2, // 1: orcareduce.model.Run.DirectorID:type_name -> orcareduce.model.Key + 3, // 2: orcareduce.model.RunMetadata.runs:type_name -> orcareduce.model.Run + 2, // 3: orcareduce.model.StateMetadata.state_id:type_name -> orcareduce.model.Key + 2, // 4: orcareduce.model.ActorState.key:type_name -> orcareduce.model.Key + 0, // 5: orcareduce.model.ActorState.status:type_name -> orcareduce.model.ActorStatus + 4, // 6: orcareduce.model.ActorState.metadata:type_name -> orcareduce.model.RunMetadata + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_datamodel_proto_init() } +func file_datamodel_proto_init() { + if File_datamodel_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_datamodel_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Key); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datamodel_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Run); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datamodel_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RunMetadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datamodel_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StateMetadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_datamodel_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActorState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_datamodel_proto_rawDesc, + NumEnums: 2, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_datamodel_proto_goTypes, + DependencyIndexes: file_datamodel_proto_depIdxs, + EnumInfos: file_datamodel_proto_enumTypes, + MessageInfos: file_datamodel_proto_msgTypes, + }.Build() + File_datamodel_proto = out.File + file_datamodel_proto_rawDesc = nil + file_datamodel_proto_goTypes = nil + file_datamodel_proto_depIdxs = nil +} diff --git a/orcareduce/model/datamodel.proto b/orcareduce/model/datamodel.proto new file mode 100644 index 0000000..c0e3028 --- /dev/null +++ b/orcareduce/model/datamodel.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; + +package orcareduce.model; + +option go_package = "orcareduce/model"; + +message Key { + string value = 1; +} + +enum ActorStatus { + UNKNOWN_ACTOR_STATE = 0; + PENDING = 1; + SUCCESS = 2; + CANCELED = 3; +} + +message Run { + Key ActorID = 1; + Key DirectorID = 2; + uint64 timestamp = 3; + enum Result { + UNKNOWN = 0; + FAILED_PRECONDITION = 1; + FAILED_OPERATION = 2; + FAILED_SUCCESS_SIGNAL = 3; + SUCCESS = 4; + } + string error = 4; +} + +message RunMetadata { + repeated Run runs = 1; +} + +message StateMetadata { + Key state_id = 1; + string name = 2; + string description = 3; +} + +message ActorState { + Key key = 1; + ActorStatus status = 2; + RunMetadata metadata = 3; + uint64 revision = 4; +} + diff --git a/orcareduce/orcareduce.go b/orcareduce/orcareduce.go new file mode 100644 index 0000000..669acce --- /dev/null +++ b/orcareduce/orcareduce.go @@ -0,0 +1,40 @@ +package orcareduce + +// Reactor is a repeatable actor. +type Reactor interface { + Preconditions() error + Act() error + SignalSuccess() error + Notify() + ID() ID +} + +type Precondition interface { + Check() error +} + +// ID is a generalized interface for identifying reactors. +type ID interface { + String() string + Value() string + Lineage() []ID + Parent() ID + NewChild() ID + Depth() int + Scope() Scope + WithScope(Scope) ID +} + +type Director interface { + GetNextActor() error + Act() error + ID() ID +} + +type ActorDataSink interface { + Success(Reactor) error + Failure(Reactor) + Attempt(Reactor) error +} + +type Scope string diff --git a/orcareduce/state/datastore.go b/orcareduce/state/datastore.go new file mode 100644 index 0000000..7be1272 --- /dev/null +++ b/orcareduce/state/datastore.go @@ -0,0 +1,104 @@ +package state + +import ( + "sync" + + "github.com/mkuchenbecker/orcareduce/orcareduce" + "github.com/pkg/errors" +) + +type State string + +var ( + PENDING State = "PENDING" + SUCCESS State = "SUCCESS" + FAILURE State = "FAILURE" +) + +type document struct { + actor orcareduce.Reactor + state State + attempt int +} + +type inMemorySink struct { + data map[orcareduce.ID]document + mux sync.RWMutex +} + +func (sink *inMemorySink) Success(r orcareduce.Reactor) (err error) { + defer func() { + err = errors.Wrap(err, "unable to record success") + }() + sink.mux.Lock() + defer sink.mux.Unlock() + val, ok := sink.data[r.ID()] + if !ok { + val = document{actor: r} + } + if val.state == FAILURE { + return errors.New("record already marked as failure") + } + val.state = SUCCESS + sink.data[r.ID()] = val + return nil +} + +func (sink *inMemorySink) Failure(r orcareduce.Reactor) { + sink.mux.Lock() + defer sink.mux.Unlock() + val, ok := sink.data[r.ID()] + if !ok { + val = document{actor: r} + } + if val.state == SUCCESS { + return + } + val.state = FAILURE + sink.data[r.ID()] = val + return +} + +func (sink *inMemorySink) Attempt(r orcareduce.Reactor) error { + sink.mux.Lock() + defer sink.mux.Unlock() + val, ok := sink.data[r.ID()] + if !ok { + val = document{actor: r} + } + if val.state != PENDING { + return errors.New("only make an attempt on pending requests") + } + val.attempt++ + sink.data[r.ID()] = val + return nil +} + +type DatastoreActor struct { + actor orcareduce.Reactor + sink orcareduce.ActorDataSink +} + +func (d *DatastoreActor) SignalSuccess() error { + return d.sink.Success(d) +} + +func (d *DatastoreActor) Preconditions() error { + return d.actor.Preconditions() +} + +func (d *DatastoreActor) Act() error { + err := d.sink.Attempt(d) + if err != nil { + return err + } + return d.Act() +} + +func (d *DatastoreActor) Notify() { + d.actor.Notify() +} + +func (d *DatastoreActor) ID() orcareduce.ID { + return d.ID() +}