jason 1 년 전
커밋
3a5871f4d7
47개의 변경된 파일4633개의 추가작업 그리고 0개의 파일을 삭제
  1. 0 0
      CHANGELOG.md
  2. 5 0
      Dockerfile.in
  3. 27 0
      Makefile
  4. 11 0
      README.md
  5. 1 0
      VERSION
  6. 45 0
      app.spec.in
  7. 3 0
      conf/app.conf
  8. 3 0
      conf/app.conf.in
  9. 17 0
      conf/app.service.in
  10. 15 0
      conf/app.yaml
  11. 15 0
      conf/app.yaml.in
  12. 119 0
      conf/common.json
  13. 55 0
      conf/common.yaml
  14. 93 0
      config/config.go
  15. 8 0
      consts/common.go
  16. 15 0
      errors/error.go
  17. 16 0
      go.mod
  18. 595 0
      go.sum
  19. 45 0
      imagebuild.sh
  20. 59 0
      impl/rcvr.go
  21. 605 0
      impl/v1/check/check.go
  22. 30 0
      impl/v1/check/field_check.go
  23. 41 0
      impl/v1/task/offline_task.go
  24. 35 0
      impl/v1/task/task_list.go
  25. 148 0
      main.go
  26. 120 0
      model/pagination.go
  27. 52 0
      model/sy_brand.go
  28. 100 0
      parser/config.go
  29. 65 0
      parser/database.go
  30. 129 0
      parser/etcd.go
  31. 30 0
      parser/init.go
  32. 65 0
      parser/logger.go
  33. 67 0
      parser/redis.go
  34. 24 0
      pb/ReadMe.md
  35. 240 0
      pb/adm_task_service.pb.go
  36. 17 0
      pb/adm_task_service.proto
  37. 435 0
      pb/v1/task.pb.go
  38. 49 0
      pb/v1/task.proto
  39. 76 0
      protogen.sh
  40. 87 0
      rpmbuild.sh
  41. 92 0
      sh/app.in
  42. 152 0
      sh/init-multi-mode
  43. 682 0
      sh/start-stop-functions
  44. 24 0
      tests/client.go
  45. 24 0
      tests/v1_test.go
  46. 20 0
      tests/v2_test.go
  47. 77 0
      utils/field.go

+ 0 - 0
CHANGELOG.md


+ 5 - 0
Dockerfile.in

@@ -0,0 +1,5 @@
+FROM hb.autocareai.cn/library/busybox:v1
+
+WORKDIR /demo
+COPY %APP_NAME% .
+CMD ["./%APP_NAME%"]

+ 27 - 0
Makefile

@@ -0,0 +1,27 @@
+# Go parameters
+GOCMD=go
+GOBUILD=$(GOCMD) build
+GOCLEAN=$(GOCMD) clean
+GOTEST=$(GOCMD) test
+GOGET=$(GOCMD) get
+
+SERVICE_NAME = $(shell pwd |sed 's/^\(.*\)[/]//' )
+PROJECT_NAME=${SERVICE_NAME}
+BIN_DIR=/usr/sbin
+CP_CMD=/usr/bin/cp
+COMMAND=${SERVICE_NAME}
+
+all: build-go
+
+build-go:
+	go build -o $(PROJECT_NAME) -v  -ldflags "-X main.Version=$(version) -X main.GitCommit=`git rev-parse HEAD`"
+test:
+	$(GOTEST) -v ./...
+install:build
+	$(CP_CMD) $(COMMAND) $(DESTDIR)$(BIN_DIR)
+clean:
+	$(GOCLEAN)
+	rm -f $(PROJECT_NAME)
+
+
+

+ 11 - 0
README.md

@@ -0,0 +1,11 @@
+# adm-management
+
+### 一、概括说明
+本工程为基于 [gRPC](https://grpc.io/) 框架的微服务demo。
+
+### 二、环境搭建
+开发搭建流程请前往 [TAPD说明文档](https://www.tapd.cn/52529947/markdown_wikis/?#1152529947001000876) 查阅
+
+### 三、软件要求
+1. go版本 >= 1.13,[链接下载](https://golang.org/dl/)
+2. 操作系统:`Windows`/`Linux`/`Darwin(MacOS)`

+ 1 - 0
VERSION

@@ -0,0 +1 @@
+0.1

+ 45 - 0
app.spec.in

@@ -0,0 +1,45 @@
+Summary:  %APP_NAME% micro service
+Name: %APP_NAME%
+Version: stable
+Release: l
+License: user EULA
+Source: %APP_NAME%-%VERSION%.tgz
+Group: utimes/microservice
+BuildRoot: /tmp
+
+%define __debug_install_post   \
+   %{_rpmconfigdir}/find-debuginfo.sh %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\
+%{nil}
+
+%description
+%prep
+%setup -c
+
+%install
+echo %VERSION%
+mkdir -p ${RPM_BUILD_ROOT}/opt/%SERVICE_NAME%/bin
+install ./%APP_NAME% ${RPM_BUILD_ROOT}/opt/%SERVICE_NAME%/bin
+
+
+mkdir -p ${RPM_BUILD_ROOT}/etc/init.d/
+install ./sh/app ${RPM_BUILD_ROOT}/etc/init.d/%SERVICE_NAME%
+
+mkdir -p ${RPM_BUILD_ROOT}/etc/%SERVICE_NAME%/lib/
+install ./sh/init-multi-mode ${RPM_BUILD_ROOT}/etc/%SERVICE_NAME%/lib/
+install ./sh/start-stop-functions ${RPM_BUILD_ROOT}/etc/%SERVICE_NAME%/lib/
+install ./conf/app.conf ${RPM_BUILD_ROOT}/etc/%SERVICE_NAME%/app.conf
+
+mkdir -p ${RPM_BUILD_ROOT}/usr/lib/systemd/system/
+install ./conf/%SERVICE_NAME%.service ${RPM_BUILD_ROOT}/usr/lib/systemd/system/
+
+%files
+/opt/%SERVICE_NAME%/bin/%APP_NAME%
+/etc/init.d/%SERVICE_NAME%
+/etc/%SERVICE_NAME%/lib/start-stop-functions
+/etc/%SERVICE_NAME%/lib/init-multi-mode
+/etc/%SERVICE_NAME%/app.conf
+/usr/lib/systemd/system/%SERVICE_NAME%.service
+
+%post
+#systemctl start %SERVICE_NAME%
+#systemctl enable %SERVICE_NAME%

+ 3 - 0
conf/app.conf

@@ -0,0 +1,3 @@
+serve_addr=127.0.0.1:10004
+etcd_addrs=47.103.201.156:2379
+discovery_type=etcd

+ 3 - 0
conf/app.conf.in

@@ -0,0 +1,3 @@
+serve_addr="%SERVE_ADDR%"
+etcd_addrs="%ETCD_ADDRS%"
+discovery_type=%DISCOVERY_TYPE%

+ 17 - 0
conf/app.service.in

@@ -0,0 +1,17 @@
+[Unit]
+Description=Start %APP_NAME% Server
+Requires=network-online.target
+# We disable the wants service, because it spams the log files
+
+[Service]
+ExecStart=/etc/init.d/%APP_NAME% start
+ExecStop=/etc/init.d/%APP_NAME% stop
+Type=forking
+# We disable PIDFile= because it doesn't work with multi-mode configurations
+#PIDFile=/var/run/camera.pid
+
+TimeoutStopSec=300
+
+[Install]
+WantedBy=multi-user.target
+

+ 15 - 0
conf/app.yaml

@@ -0,0 +1,15 @@
+runMode: dev
+rpc:
+  prefix: adm 
+  admTask:
+    serviceName: adm-task
+    serviceIp: 127.0.0.1
+    servicePort: 10004
+etcd:
+  addrs:
+    - 47.103.201.156:2379
+encryptKey: abcdefg12345678
+log:
+  path: logs/
+projectName: adm
+discoveryType: etcd

+ 15 - 0
conf/app.yaml.in

@@ -0,0 +1,15 @@
+runMode: %RUNMODE%
+rpc:
+  prefix: adm 
+  ADMManagement:
+    serviceName: %APP_NAME%
+    serviceIp: %SERVE_ADDR%
+    servicePort: %SERVE_PORT%
+etcd:
+  addrs:
+  - "%ETCD_ADDR%:%ETCD_PORT%"
+encryptKey: %ENCRYPT_KEY%
+log:
+  path: %LOG_DIR%
+projectName: adm
+discoveryType: %DISCOVERY_TYPE%

+ 119 - 0
conf/common.json

@@ -0,0 +1,119 @@
+{
+  "feild_loop_time":120,
+  "ods_rabbitmq":{
+    "addr":"47.100.68.39:5672",
+    "username":"admin",
+    "passwrod":"syznno1",
+    "vhost":"",
+    "exchange_name":"ods-ex",
+    "queue_name":"ods-queue",
+    "route_bind_key":"ods",
+    "consumer_count":"3"
+  },
+  "dws_rabbitmq":{
+    "addr":"47.100.68.39:5672",
+    "username":"admin",
+    "passwrod":"syznno1",
+    "vhost":"",
+    "exchange_name":"dws-ex",
+    "queue_name":"dws-queue",
+    "route_bind_key":"dws",
+    "consumer_count":"3"
+  },
+  "ads_rabbitmq":{
+    "addr":"47.100.68.39:5672",
+    "username":"admin",
+    "passwrod":"syznno1",
+    "vhost":"",
+    "exchange_name":"ads-ex",
+    "queue_name":"ads-queue",
+    "route_bind_key":"ads",
+    "consumer_count":"3"
+  },
+  "mysql":{
+    "addr":"47.103.201.156:3306",
+    "charset":"utf8",
+    "default_db":"default",
+    "max_conn":"100",
+    "max_idle":"10",
+    "password":"1353406",
+    "user":"root"
+  },
+  "redis":{
+    "addr":"47.103.130.208:63779",
+    "addrs":"47.103.130.208:63779",
+    "connect_timeout":"2",
+    "default_db":"0",
+    "idle_timeout":"600",
+    "is_cluster":"false",
+    "max_active":"20000",
+    "max_idle":"50",
+    "max_retries":"1",
+    "min_idle_conns":"10",
+    "network":"tcp",
+    "password":"dip_prv",
+    "pool_size":"100",
+    "read_timeout":"2",
+    "wait_flag":"true",
+    "write_timeout":"2"
+  },
+  "rpc":{
+    "prefix": "adm",
+    "adm_dws":{
+      "log":{
+        "disable_stacktrace":"true",
+        "max_age":"30",
+        "max_backups":"7",
+        "max_size":"100"
+      },
+      "log_mode":"info",
+      "mysql_db":"dip-dws",
+      "name":"adm_dws",
+      "redis_db":"4",
+      "scheme":"tcp",
+      "update_interval":"60",
+      "service_name":"dip-access-log-svc",
+      "service_port":"60005"
+    },
+    "adm_ods":{
+      "log":{
+        "disable_stacktrace":"true",
+        "max_age":"30",
+        "max_backups":"7",
+        "max_size":"100"
+      },
+      "log_mode":"info",
+      "mysql_db":"dip-ods",
+      "name":"dip_admin",
+      "redis_db":"4",
+      "scheme":"tcp",
+      "update_interval":"60",
+      "service_name":"dip-admin-svc",
+      "service_port":"60003"
+    },
+    "adm_ads":{
+      "log":{
+        "disable_stacktrace":"true",
+        "max_age":"30",
+        "max_backups":"7",
+        "max_size":"100"
+      },
+      "log_mode":"info",
+      "mysql_db":"dip-ads",
+      "name":"dip_auth_check",
+      "redis_db":"4",
+      "scheme":"tcp",
+      "update_interval":"60",
+      "service_name":"dip-auth-check-svc",
+      "service_port":"60006"
+    },
+    "adm_task":{
+      "service_name": "adm-task",
+      "service_ip": "127.0.0.1",
+      "service_port": "10004",
+      "mysql_db":"db_adm_management",
+      "redis_db":"4"
+    }
+  },
+  "run_mode":"dev"
+}

+ 55 - 0
conf/common.yaml

@@ -0,0 +1,55 @@
+runMode: dev
+k8s: 'false'
+log:
+  path: logs
+  maxBackups: '7'
+  maxSize: '100'
+  maxAge: '30'
+  level: debug
+  stacktrace: 'false'
+mysql:
+  user: root
+  password: 1353406
+  addr: 47.103.201.156:3306
+  db: "adm_management"
+  charset: utf8
+  maxIdle: '10'
+  maxConn: '100'
+  logMode: 'true'
+redis:
+  addrs:
+  - 127.0.0.1:6379
+  password: ''
+  db: '0'
+  poolSize: '100'
+  minIdleConns: '10'
+  maxRetries: '1'
+  cluster: 'false'
+elastic:
+  addrs:
+  - http://127.0.0.1:9200
+  sniff: 'false'
+mongo:
+  addr: 127.0.0.1:27017
+  user: root
+  password: root
+rpc:
+  prefix: adm
+  keepalive:
+    clientTime: '10'
+    clientTimeout: '1'
+    serverTime: '5'
+    serverMinitime: '5'
+    serverTimeout: '1'
+  ADMManagement:
+    serviceName: adm-management
+    servicePort: '10001'
+    serviceIp: 127.0.0.1
+    mysqlDb: ''
+    redisDb: '0'
+    logLevel: debug
+    logStacktrace: 'false'
+  admTask:
+    serviceName: adm-task
+    serviceIp: 127.0.0.1
+    servicePort: 10004

+ 93 - 0
config/config.go

@@ -0,0 +1,93 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package config
+
+import "encoding/json"
+
+type LogConfig struct {
+	Path       string      `mapstructure:"path"`
+	Level      string      `mapstructure:"level"`
+	MaxSize    json.Number `mapstructure:"max_size"`
+	MaxBackups json.Number `mapstructure:"max_backups"`
+	MaxAge     json.Number `mapstructure:"max_age"`
+	Stacktrace string      `mapstructure:"stacktrace"`
+}
+
+type MysqlConfig struct {
+	User     string      `mapstructure:"user"`
+	Password string      `mapstructure:"password"`
+	Addr     string      `mapstructure:"addr"`
+	Db       string      `mapstructure:"db"`
+	Charset  string      `mapstructure:"charset"`
+	MaxIdle  json.Number `mapstructure:"max_idle"`
+	MaxConn  json.Number `mapstructure:"max_conn"`
+	LogMode  string      `mapstructure:"log_mode"`
+}
+
+type RedisConfig struct {
+	Addrs        string      `mapstructure:"addrs"`
+	Password     string      `mapstructure:"password"`
+	Db           json.Number `mapstructure:"db"`
+	PoolSize     json.Number `mapstructure:"pool_size"`
+	MinIdleConns json.Number `mapstructure:"min_idle_conns"`
+	MaxRetries   json.Number `mapstructure:"max_retries"`
+	IsCluster    string      `mapstructure:"is_cluster"`
+}
+
+type ElasticConfig struct {
+	Addrs string `mapstructure:"addrs"`
+	Sniff string `mapstructure:"sniff"`
+}
+
+type RPCNode struct {
+	ServiceName   string      `mapstructure:"service_name"`
+	ServicePort   json.Number `mapstructure:"service_port"`
+	ServiceIp     string      `mapstructure:"service_ip"`
+	MysqlDb       string      `mapstructure:"mysql_db"`
+	RedisDb       json.Number `mapstructure:"redis_db"`
+	LogLevel      string      `mapstructure:"log_level"`
+	LogStacktrace string      `mapstructure:"log_stacktrace"`
+}
+
+type KeepaliveConfig struct {
+	ClientTime     json.Number `mapstructure:"client_time"`
+	ClientTimeout  json.Number `mapstructure:"client_timeout"`
+	ServerTime     json.Number `mapstructure:"server_time"`
+	ServerTimeout  json.Number `mapstructure:"server_timeout"`
+	ServerMiniTime json.Number `mapstructure:"server_mini_time"`
+}
+
+type RPCConfig struct {
+	Prefix    string          `mapstructure:"prefix"`
+	Keepalive KeepaliveConfig `mapstructure:"keepalive"`
+	AdmTask   RPCNode         `mapstructure:"adm_task"`
+}
+
+type MongoConfig struct {
+	User     string `mapstructure:"user"`
+	Password string `mapstructure:"password"`
+	Addr     string `mapstructure:"addr"`
+}
+
+type OssConfig struct {
+	Endpoint string `mapstructure:"endpoint"`
+	Id       string `mapstructure:"id"`
+	Secret   string `mapstructure:"secret"`
+	Bucket   string `mapstructure:"bucket"`
+}
+
+type Configure struct {
+	// 基础配置
+	RunMode string    `mapstructure:"run_mode" mapstructure:"run_mode"`
+	Log     LogConfig `mapstructure:"log"`
+	// 按需配置
+	Mysql   MysqlConfig   `mapstructure:"mysql"`
+	Mongo   MongoConfig   `mapstructure:"mongo"`
+	Redis   RedisConfig   `mapstructure:"redis"`
+	Elastic ElasticConfig `mapstructure:"elastic"`
+	Oss     OssConfig     `mapstructure:"oss"`
+	// 所要启用的服务
+	Rpc           RPCConfig   `mapstructure:"rpc"`
+	FeildLoopTime json.Number `mapstructure:"feild_loop_time"`
+}

+ 8 - 0
consts/common.go

@@ -0,0 +1,8 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package consts
+
+const (
+	PageSize = 10
+)

+ 15 - 0
errors/error.go

@@ -0,0 +1,15 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package errors
+
+import (
+	"google.golang.org/grpc/status"
+)
+
+var (
+	// 通用错误
+	SystemError  = status.Error(10001, "系统错误")
+	ServiceError = status.Error(10002, "内部服务错误")
+	ParmsError   = status.Error(20003, "参数错误")
+)

+ 16 - 0
go.mod

@@ -0,0 +1,16 @@
+module gadm-task
+
+go 1.13
+
+require (
+	github.com/fsnotify/fsnotify v1.4.9
+	github.com/golang/protobuf v1.5.2
+	github.com/json-iterator/go v1.1.11
+	github.com/satori/go.uuid v1.2.0
+	github.com/spf13/viper v1.6.2
+	gitlab.sydip.com/repo/gopkgs v1.1.1
+	go.etcd.io/etcd/client/v3 v3.5.4
+	google.golang.org/grpc v1.39.0
+	gopkg.in/ini.v1 v1.51.0
+	gorm.io/gorm v1.21.12
+)

+ 595 - 0
go.sum

@@ -0,0 +1,595 @@
+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=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
+github.com/abronan/valkeyrie v0.0.0-20190822142731-f2e1850dc905/go.mod h1:hTreU6x9m2IP2h8e0TGrSzAXSCI3lxic8/JT5CMknjY=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
+github.com/aliyun/aliyun-oss-go-sdk v2.0.7+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
+github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
+github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
+github.com/anacrolix/envpprof v1.0.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
+github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
+github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
+github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
+github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
+github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
+github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk=
+github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
+github.com/anacrolix/utp v0.0.0-20180219060659-9e0e1d1d0572/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aws/aws-sdk-go v1.16.23/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+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/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
+github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
+github.com/cenk/backoff v2.2.1+incompatible/go.mod h1:7FtoeaSnHoZnmZzz47cM35Y9nSW7tNyaidugnHTaFDE=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+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/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
+github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
+github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
+github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-jump v0.0.0-20170409065014-e1f439676b57/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/docker/libkv v0.2.1/go.mod h1:r5hEwHwW8dr0TFBYGCarMNbrQOiwL1xoqDYZ/JqoTK0=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/edwingeng/doublejump v0.0.0-20190102103700-461a0155c7be/go.mod h1:sqbHCF7b7eMiCtiwNY5+2bqhT+Zx6Duj2VU5WigITOQ=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
+github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
+github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
+github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
+github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
+github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
+github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/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/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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+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/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grandcat/zeroconf v0.0.0-20190424104450-85eadb44205c/go.mod h1:YjKB0WsLXlMkO9p+wGTCoPIDGRJH0mz7E526PxkQVxI=
+github.com/grokify/html-strip-tags-go v0.0.0-20200322061010-ea0c1cf2f119/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
+github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
+github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
+github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/kavu/go_reuseport v1.4.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
+github.com/lucas-clemente/quic-go v0.11.0/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
+github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
+github.com/marten-seemann/quic-conn v0.0.0-20190827120552-a06e62da55b7/go.mod h1:BTUDloYEkSYt9Yf98rSVDnIFrSJc04H3/6FfE+lsjNg=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.0 h1:XaTQmdKecIbwNHpzOIy0XMoEG5bmv/n0OVyaF1NKUdo=
+github.com/onsi/ginkgo v1.10.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg=
+github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+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/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rpcx-ecosystem/quic-conn v0.0.0-20190830034555-28ef71d38a15/go.mod h1:jk/xizLAYKg5ft2h5IAqThQuxtMRUE6dL93liAh4WEk=
+github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/rubyist/circuitbreaker v2.2.1+incompatible/go.mod h1:Ycs3JgJADPuzJDwffe12k6BZT8hxVi6lFK+gWYJLN4A=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
+github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/serialx/hashring v0.0.0-20180504054112-49a4782e9908/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/smallnest/libkv-etcdv3-store v0.0.0-20190827025031-6b33215321f0/go.mod h1:SYR75Tf6jJoZ8wZca3fqyzXorUyOwnorUYIKv4T+u9Q=
+github.com/smallnest/rpcx v0.0.0-20190830042331-e50181fc75b4/go.mod h1:mMw5Jh8gV9ongiVM90AGxXLUa+x4lPwHPjG/N//OsFE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
+github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
+github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
+github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
+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.1/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/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e/go.mod h1:B4+Kq1u5FlULTjFSM707Q6e/cOHFv0z/6QRoxubDIQ8=
+github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
+github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
+github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
+github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
+github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/valyala/fastrand v1.0.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
+github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
+github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/xtaci/kcp-go v5.4.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
+github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+gitlab.sydip.com/repo/gopkgs v1.1.1 h1:H/zFJ9ntVHIP+39iX0PJQ7Az6E02sidftrELJWYPOfA=
+gitlab.sydip.com/repo/gopkgs v1.1.1/go.mod h1:rpqokCDwaCmz2GjUlLGKGgNXlQFoxiM6e85+4vcEJ/I=
+go.etcd.io/bbolt v1.3.1-etcd.8/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v3.3.13+incompatible h1:jCejD5EMnlGxFvcGRyEV4VGlENZc7oPQX6o0t7n3xbw=
+go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
+go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
+go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
+go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
+go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
+go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
+go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.14.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+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-20190313153728-d0100b6bd8b3/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-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-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-20190522155817-f3200d17e092/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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds=
+golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+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-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/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-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-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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+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=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+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/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f h1:YORWxaStkWBnWgELOHTmDrqNlFXuVGEbhwbB5iK94bQ=
+google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
+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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
+google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
+gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/olivere/elastic.v5 v5.0.84/go.mod h1:LXF6q9XNBxpMqrcgax95C6xyARXWbbCXUrtTxrNrxJI=
+gopkg.in/redis.v5 v5.2.9/go.mod h1:6gtv0/+A4iM08kdRfocWYB3bLX2tebpNtfKlFT6H4mY=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
+gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/mysql v1.1.1 h1:yr1bpyqiwuSPJ4aGGUX9nu46RHXlF8RASQVb1QQNcvo=
+gorm.io/driver/mysql v1.1.1/go.mod h1:KdrTanmfLPPyAOeYGyG+UpDys7/7eeWT1zCq+oekYnU=
+gorm.io/gorm v1.21.9/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
+gorm.io/gorm v1.21.12 h1:3fQM0Eiz7jcJEhPggHEpoYnsGZqynMzverL77DV40RM=
+gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/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=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

+ 45 - 0
imagebuild.sh

@@ -0,0 +1,45 @@
+#!/bin/bash
+
+set -e
+
+show_usage="args: [--image-name=]"
+
+#-o或--options选项后面是可接受的短选项,如ab:c::,表示可接受的短选项为-a -b -c,
+#其中-a选项不接参数,-b选项后必须接参数,-c选项的参数为可选的
+#-l或--long选项后面是可接受的长选项,用逗号分开,冒号的意义同短选项。
+#-n选项后接选项解析错误时提示的脚本名字
+ARGS=`getopt -o -h --long help,image-name: -n "$0" -- "$@"`
+
+if [ $? != 0 ]; then
+    exit 1
+fi
+
+#将规范化后的命令行参数分配至位置参数($1,$2,...)
+eval set -- "${ARGS}"
+
+while [ -n "$1" ]
+do
+    case "$1" in
+        -h|--help)
+            echo "$show_usage"; exit 1 ;;
+        --image-name)
+            IMAGE_NAME=$2; shift ;;
+        *) shift ;;
+    esac
+done
+
+PROJECT_PATH=$(cd `dirname $0`; pwd)
+APP_NAME="${PROJECT_PATH##*/}"
+VERSION=`cat VERSION`
+make version=$VERSION
+
+DOCKERFILE=Dockerfile
+
+
+if [ -z $IMAGE_NAME ];then
+    IMAGE_NAME=$APP_NAME
+fi
+
+sed -e "s/%APP_NAME%/$APP_NAME/g" ${DOCKERFILE}.in > ${DOCKERFILE}
+docker build . -t $IMAGE_NAME
+

+ 59 - 0
impl/rcvr.go

@@ -0,0 +1,59 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package impl
+
+import (
+	"context"
+
+	"gadm-task/pb"
+	pb_v1 "gadm-task/pb/v1"
+
+	"gadm-task/impl/v1/check"
+	"gadm-task/impl/v1/task"
+
+	"gitlab.sydip.com/repo/gopkgs/tasker/rpctasker"
+	"google.golang.org/grpc"
+)
+
+// 具体实现
+type Rcvr struct{}
+
+func Register(s *grpc.Server) {
+	pb.RegisterAdmTaskServer(s, &Rcvr{})
+}
+
+func (c *Rcvr) GetTaskBySourceCode(ctx context.Context, req *pb_v1.GetTaskBySourceCodeRequest) (reply *pb_v1.GetTaskBySourceCodeReply, err error) {
+	t1 := func() error {
+		reply, err = task.GetTaskBySourceCode(ctx, req)
+		return err
+	}
+
+	return reply, rpctasker.Exec(ctx, t1)
+}
+
+func (c *Rcvr) UpdateOfflineTask(ctx context.Context, req *pb_v1.UpdateOfflineTaskRequest) (reply *pb_v1.UpdateOfflineTaskReply, err error) {
+	t1 := func() error {
+		reply, err = task.UpdateOfflineTask(ctx, req)
+		return err
+	}
+
+	return reply, rpctasker.Exec(ctx, t1)
+}
+func (c *Rcvr) CheckFormat(ctx context.Context, req *pb_v1.CheckFormatRequest) (reply *pb_v1.CheckFormatReply, err error) {
+	t1 := func() error {
+		reply, err = check.CheckFormat(ctx, req)
+		return err
+	}
+
+	return reply, rpctasker.Exec(ctx, t1)
+}
+
+func (c *Rcvr) FieldCheck(ctx context.Context, req *pb_v1.FieldCheckRequest) (reply *pb_v1.FieldCheckReply, err error) {
+	t1 := func() error {
+		reply, err = check.FieldCheck(ctx, req)
+		return err
+	}
+
+	return reply, rpctasker.Exec(ctx, t1)
+}

+ 605 - 0
impl/v1/check/check.go

@@ -0,0 +1,605 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package check
+
+import (
+	pb_v1 "gadm-task/pb/v1"
+	"context"
+	"fmt"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+	"unicode"
+	"unicode/utf8"
+
+	"google.golang.org/grpc/status"
+)
+
+const (
+	VINLEN      = 17
+	PLATELEN    = 6
+	PLATENEWLEN = 7
+	SF          = "京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新"
+)
+
+var RUNMODE = "prod"
+
+func SetRunmode(runMode string) {
+	RUNMODE = runMode
+}
+
+var vinMap = map[string]int{
+	"0": 0,
+	"1": 1,
+	"2": 2,
+	"3": 3,
+	"4": 4,
+	"5": 5,
+	"6": 6,
+	"7": 7,
+	"8": 8,
+	"9": 9,
+	"A": 1,
+	"B": 2,
+	"C": 3,
+	"D": 4,
+	"E": 5,
+	"F": 6,
+	"G": 7,
+	"H": 8,
+	"J": 1,
+	"K": 2,
+	"L": 3,
+	"M": 4,
+	"N": 5,
+	"P": 7,
+	"R": 9,
+	"S": 2,
+	"T": 3,
+	"U": 4,
+	"V": 5,
+	"W": 6,
+	"X": 7,
+	"Y": 8,
+	"Z": 9,
+}
+
+var vinMatrix = []int{8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2}
+
+func checkVin(vin string) bool {
+	var oldVin9 int32
+	total := 0
+	for i, v := range vin {
+		if i == 8 {
+			oldVin9 = v
+			continue
+		}
+		r := rune(v)
+		total = total + vinMatrix[i]*vinMap[string(r)]
+	}
+
+	left := total % 11
+	newVin9 := ""
+	if left == 10 {
+		newVin9 = "X"
+	} else {
+		newVin9 = fmt.Sprintf("%d", left)
+	}
+
+	r := rune(oldVin9)
+
+	if newVin9 == string(r) {
+		return true
+	} else {
+		return false
+	}
+}
+
+func CheckVinFormat(vin string) (string, error) {
+	vin = strings.TrimSpace(vin)
+	vin = strings.ToUpper(vin)
+	/*if len(vin) != VINLEN {
+		return vin, status.Error(20003, "请求参数格式不对,车架号不为17位")
+	}*/
+	vinLen := len(vin)
+	strconv.Itoa(vinLen)
+
+	r, _ := regexp.Compile(`[0-9A-Z]{` + strconv.Itoa(vinLen) + `}`)
+	if r.MatchString(vin) {
+		if vinLen == 17 && strings.HasPrefix(vin, "L") {
+			check := checkVin(vin)
+			if check {
+				return vin, nil
+			} else {
+				return vin, status.Error(20003, "vin码错误")
+			}
+		}
+		return vin, nil
+	}
+
+	return vin, status.Error(20003, "请求参数格式不对,车架号包含特殊字符")
+
+}
+
+func CheckVinFormat17Bit(vin string) (string, error) {
+	vin = strings.TrimSpace(vin)
+	vin = strings.ToUpper(vin)
+	if len(vin) != VINLEN {
+		return vin, status.Error(20003, "请求参数格式不对,车架号不为17位")
+	}
+
+	r, _ := regexp.Compile(`[0-9A-Z]{17}`)
+	if r.MatchString(vin) {
+		return vin, nil
+	}
+
+	return vin, status.Error(20003, "请求参数格式不对,车架号包含特殊字符")
+
+}
+
+func IsCommonPlateType(plateType string) bool {
+	switch plateType {
+	case "51":
+		return true
+	case "52":
+		return true
+	case "01":
+		return true
+	case "02":
+		return true
+	default:
+		return false
+	}
+}
+
+func isUnknowPlateType(plateType string) bool {
+	switch plateType {
+	case "02":
+		return false
+	case "03":
+		return false
+	case "04":
+		return false
+	case "15":
+		return false
+	case "16":
+		return false
+	case "23":
+		return false
+	case "24":
+		return false
+	case "26":
+		return false
+	case "27":
+		return false
+	default:
+		return true
+	}
+}
+
+func getPlateType(plateNo string) string {
+	switch {
+	case strings.HasPrefix(plateNo, "使"):
+		return "03"
+	case strings.HasSuffix(plateNo, "使"):
+		return "03"
+		// 领馆汽车
+	case strings.HasSuffix(plateNo, "领"):
+		return "04"
+	case strings.HasSuffix(plateNo, "挂"):
+		return "15"
+	case strings.HasSuffix(plateNo, "学"):
+		return "16"
+	case strings.HasSuffix(plateNo, "警"):
+		return "23"
+	case strings.HasSuffix(plateNo, "港"):
+		return "26"
+	case strings.HasSuffix(plateNo, "澳"):
+		return "27"
+	default:
+		return "02"
+	}
+}
+
+func isSupportPlateType(plateType string) bool {
+	switch plateType {
+	case "01":
+		return true
+	case "02":
+		return true
+	case "03":
+		return true
+	case "04":
+		return true
+	case "05":
+		return true
+	case "06":
+		return true
+	case "07":
+		return true
+	case "08":
+		return true
+	case "09":
+		return true
+	case "10":
+		return true
+	case "11":
+		return true
+	case "12":
+		return true
+	case "13":
+		return true
+	case "14":
+		return true
+	case "15":
+		return true
+	case "16":
+		return true
+	case "17":
+		return true
+	case "20":
+		return true
+	case "21":
+		return true
+	case "22":
+		return true
+	case "23":
+		return true
+	case "24":
+		return true
+	case "25":
+		return true
+	case "26":
+		return true
+	case "27":
+		return true
+	case "51":
+		return true
+	case "52":
+		return true
+	case "99":
+		return true
+	default:
+		return false
+	}
+	return false
+}
+
+func isContainSpecialCharactor(plateNumber []byte) bool {
+	if strings.Contains(string(plateNumber), "I") || strings.Contains(string(plateNumber), "O") {
+		return true
+	}
+
+	if getPlateType(string(plateNumber)) != "02" {
+		return false
+	}
+
+	for _, v := range plateNumber {
+		if (v >= 48 && v <= 57) || (v >= 65 && v <= 90) || (v >= 97 && v <= 122) {
+			continue
+		}
+		return true
+	}
+	return false
+}
+
+func ParsePlate(plate string) (string, string) {
+	var index int
+	for i, r := range plate {
+		if !unicode.Is(unicode.Scripts["Han"], r) {
+			index = i
+			break
+		}
+	}
+	sf := plate[0:index]
+	hphm := plate[index:]
+
+	return sf, hphm
+}
+
+func CheckPlateNoFormat(plateNo *string, plateType string) (string, error) {
+	*plateNo = strings.TrimSpace(*plateNo)
+	*plateNo = strings.ToUpper(*plateNo)
+	if RUNMODE != "prod" {
+		if strings.HasPrefix(*plateNo, "测") {
+			return plateType, nil
+		}
+	}
+	if plateType != "" {
+		if !isSupportPlateType(plateType) {
+			return "", status.Error(20003, "参数错误,不支持的车牌类型")
+		}
+	}
+
+	// 判断车牌号码合法性
+	sf, plateNumber := ParsePlate(*plateNo)
+	if len(sf) == 0 {
+		if strings.HasSuffix(*plateNo, "使") {
+			return "03", nil
+		}
+		return plateType, status.Error(20003, "请求参数格式不对,车牌号码格式不正确")
+	} else if sf != "使" {
+		if !strings.Contains(SF, sf) {
+			return plateType, status.Error(20003, "请求参数格式不对,不支持的省份")
+		}
+	}
+
+	// 检查车牌是否包含特殊字符
+	if isContainSpecialCharactor([]byte(plateNumber)) {
+		return plateType, status.Error(20003, "请求参数格式不对,车牌号码包含特殊字符")
+	}
+
+	plateLen := utf8.RuneCountInString(plateNumber)
+
+	if plateType == "" && plateLen == PLATENEWLEN {
+		if plateNumber[1] > 64 && plateNumber[1] < 91 {
+			plateType = "52"
+		} else if plateNumber[PLATENEWLEN-1] > 64 && plateNumber[PLATENEWLEN-1] < 91 {
+			plateType = "51"
+		} else {
+			return plateType, status.Error(20003, "请求参数格式不对,新能源汽车车牌格式不正确")
+		}
+	}
+
+	count := 0
+	for _, v := range plateNumber {
+		if 64 < v && v < 91 {
+			count++
+		}
+	}
+	if strings.HasPrefix(*plateNo, "使") || strings.HasSuffix(*plateNo, "使") {
+		if plateType == "" {
+			return "03", nil
+		} else {
+			if plateType != "03" {
+				return "", status.Error(20003, "请求参数格式不对,车牌和车牌类型不匹配")
+			}
+			return plateType, nil
+		}
+	} else if strings.HasSuffix(*plateNo, "领") {
+		if plateType == "" {
+			return "04", nil
+		} else {
+			if plateType != "04" {
+				return "", status.Error(20003, "请求参数格式不对,车牌和车牌类型不匹配")
+			}
+			return plateType, nil
+		}
+	} else if plateType == "51" || plateType == "52" {
+		if count > 3 {
+			return plateType, status.Error(20003, "请求参数格式不对,新能源车牌号码超过3个字母")
+		} else if plateLen != PLATENEWLEN {
+			return plateType, status.Error(20003, "请求参数格式不对,新能源车牌号码长度不正确")
+		} else {
+			if plateNumber[1] > 64 && plateNumber[1] < 91 {
+				if plateType != "52" {
+					return plateType, status.Error(20003, "请求参数格式不对,新能源车牌号码和车牌类型不匹配")
+				}
+			} else if plateNumber[PLATENEWLEN-1] > 64 && plateNumber[PLATENEWLEN-1] < 91 {
+				if plateType != "51" {
+					return plateType, status.Error(20003, "请求参数格式不对,新能源车牌号码和车牌类型不匹配")
+				}
+			} else {
+				return plateType, status.Error(20003, "请求参数格式不对,新能源汽车车牌格式不正确")
+			}
+		}
+
+	} else {
+		if count > 3 {
+			return plateType, status.Error(20003, "请求参数格式不对,车牌号码超过3个字母")
+		} else if plateLen != PLATELEN {
+			return plateType, status.Error(20003, "请求参数格式不对,车牌号码长度不正确")
+		}
+	}
+
+	if strings.Contains("0123456789", plateNumber[0:1]) {
+		return plateType, status.Error(20003, "请求参数格式不对,号牌号码第一位不能为数字")
+	}
+
+	if plateType == "51" || plateType == "52" {
+		return plateType, nil
+	}
+
+	if plateType != "" && isUnknowPlateType(plateType) {
+		return plateType, nil
+	}
+
+	plateTypeNew := getPlateType(*plateNo)
+
+	if plateType != "" && plateTypeNew != plateType {
+		return plateType, status.Error(20003, "请求参数格式不对,车牌号码和车牌类型不匹配")
+	}
+	// 不返回默认
+	return "", nil
+}
+
+func GetPlateTypeByColor(plateNo, plateColor string) string {
+	switch plateColor {
+	case "0":
+		// 蓝色
+		return "02"
+	case "1":
+		//黄色 01 ,15,16
+		if strings.HasSuffix(plateNo, "挂") {
+			return "15"
+		} else if strings.HasSuffix(plateNo, "学") {
+			return "16"
+		}
+
+		return "01"
+	case "2":
+		// 黑色 03,04,26,27
+		if strings.HasSuffix(plateNo, "使") || strings.HasPrefix(plateNo, "使") {
+			return "03"
+		} else if strings.HasSuffix(plateNo, "领") {
+			return "04"
+		} else if strings.HasSuffix(plateNo, "港") {
+			return "26"
+		} else if strings.HasSuffix(plateNo, "澳") {
+			return "27"
+		}
+		return ""
+	case "3":
+		//白色
+		return "23"
+	case "4":
+		//渐变绿色
+		return "52"
+	case "5":
+		//黄绿渐变色
+		return "51"
+	case "6":
+		//蓝白渐变色
+		return ""
+	case "9":
+		//未确定
+		return ""
+	default:
+		return ""
+	}
+}
+
+func GetPlateColorByPlateType(plateType string) string {
+	switch plateType {
+	case "01":
+		return "1"
+	case "02":
+		return "0"
+	case "03":
+		return "2"
+	case "04":
+		return "2"
+	case "08":
+		return "0"
+	case "09":
+		return "2"
+	case "10":
+		return "2"
+	case "13":
+		return "1"
+	case "15":
+		return "1"
+	case "16":
+		return "1"
+	case "17":
+		return "1"
+	case "23":
+		return "3"
+	case "24":
+		return "3"
+	case "26":
+		return "2"
+	case "27":
+		return "2"
+	case "51":
+		return "5"
+	case "52":
+		return "4"
+	default:
+		return ""
+	}
+
+}
+
+var (
+	idCertMatrix       = []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
+	idCertVerifyCode   = []byte("10X98765432")
+	socialCreditMatrix = []int{1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28}
+	socialCreditMap    = map[int]int{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
+		'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15, 'G': 16, 'H': 17, 'J': 18,
+		'K': 19, 'L': 20, 'M': 21, 'N': 22, 'P': 23, 'Q': 24, 'R': 25, 'T': 26, 'U': 27,
+		'W': 28, 'X': 29, 'Y': 30}
+
+	socialCreditMapKeys   = []int{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'W', 'X', 'Y'}
+	socialCreditMapValues = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}
+)
+
+func CheckIDCert(str string) (realIdCert string, res bool) {
+
+	defer func() {
+		if r := recover(); r != nil {
+			res = false
+		}
+	}()
+
+	idCert := strings.ToUpper(str)
+	idCert = strings.TrimSpace(idCert)
+	ret := idCert
+	if len(idCert) != 18 {
+		return "", false
+	}
+
+	year, err := strconv.Atoi(idCert[6:10])
+
+	if err != nil {
+		return "", false
+	}
+
+	if !(1900 < year && year < 2100) {
+		return "", false
+	}
+
+	check := 0
+	lastLetter := 0
+	for index, value := range []byte(idCert) {
+		if index == 17 {
+			lastLetter = int(value)
+			break
+		}
+
+		if !(value >= 48 && value <= 57) {
+			return "", false
+		}
+
+		v := value - 48
+		check = check + idCertMatrix[index]*int(v)
+	}
+
+	if !((lastLetter >= 48 && lastLetter <= 57) || lastLetter == 'X') {
+		return "", false
+	}
+
+	timeLayout := "20060102"                                     //转化所需模板
+	loc, _ := time.LoadLocation("Local")                         //重要:获取时区
+	_, err = time.ParseInLocation(timeLayout, idCert[6:14], loc) //使用模板在对应时区转化为time.time类型
+
+	if err != nil {
+		return "", false
+	}
+
+	verifyCode := int(idCertVerifyCode[check%11])
+	if lastLetter == verifyCode {
+		return ret, true
+	}
+	return "", false
+}
+
+func CheckFormat(ctx context.Context, req *pb_v1.CheckFormatRequest) (reply *pb_v1.CheckFormatReply, err error) {
+	reply = &pb_v1.CheckFormatReply{}
+	if req.Vin != "" {
+		reply.Vin, err = CheckVinFormat(req.Vin)
+		if err != nil {
+			return reply, err
+		}
+	}
+
+	if req.Idcert != "" {
+		idcert, isOk := CheckIDCert(req.Idcert)
+		if !isOk {
+			return reply, status.Error(20003, "请求参数格式不对,身份证错误")
+		}
+		reply.Idcert = idcert
+	}
+
+	if req.PlateNo != "" {
+		reply.PlateType, err = CheckPlateNoFormat(&req.PlateNo, req.PlateType)
+		if err != nil {
+			return reply, err
+		}
+		reply.PlateNo = req.PlateNo
+	}
+
+	return reply, nil
+}

+ 30 - 0
impl/v1/check/field_check.go

@@ -0,0 +1,30 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package check
+
+import (
+	"gadm-task/errors"
+	"gadm-task/utils"
+	"context"
+	"fmt"
+
+	"google.golang.org/grpc/status"
+
+	pb_v1 "gadm-task/pb/v1"
+)
+
+func FieldCheck(ctx context.Context, req *pb_v1.FieldCheckRequest) (reply *pb_v1.FieldCheckReply, err error) {
+	if req.Content == "" {
+		return nil, errors.ParmsError
+	}
+
+	reply = &pb_v1.FieldCheckReply{}
+	ok, fieldName := utils.CheckField(req.Content)
+	if !ok {
+		return reply, status.Error(20003, fmt.Sprintf("参数错误:字段%s不存在", fieldName))
+	}
+
+	return reply, nil
+
+}

+ 41 - 0
impl/v1/task/offline_task.go

@@ -0,0 +1,41 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package task
+
+import (
+	"gadm-task/errors"
+	"context"
+
+	"gitlab.sydip.com/repo/gopkgs/database"
+
+	pb_v1 "gadm-task/pb/v1"
+)
+
+func UpdateOfflineTask(ctx context.Context, req *pb_v1.UpdateOfflineTaskRequest) (reply *pb_v1.UpdateOfflineTaskReply, err error) {
+	if req.TaskId == 0 {
+		return nil, errors.ServiceError
+	}
+
+	reply = &pb_v1.UpdateOfflineTaskReply{}
+
+	if req.Total != 0 {
+		err = database.DB().Exec("update t_adm_offline_task set total=? where id=?", req.Total, req.TaskId).Error
+		return reply, err
+	}
+
+	if req.FinishCount != 0 {
+		err = database.DB().Exec("update t_adm_offline_task set finish_count=finish_count+? where id=?", req.FinishCount, req.TaskId).Error
+		return reply, err
+	}
+
+	if req.IsFinish != 0 {
+		err = database.DB().Exec("update t_adm_offline_task set is_finish=? where id=?", req.IsFinish, req.TaskId).Error
+		if req.IsFinish == 1 {
+			database.DB().Exec("update t_adm_offline_task set finish_count=total where id=?", req.TaskId)
+		}
+		return reply, err
+	}
+
+	return reply, nil
+}

+ 35 - 0
impl/v1/task/task_list.go

@@ -0,0 +1,35 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package task
+
+import (
+	"gadm-task/errors"
+	"context"
+	"fmt"
+
+	"gorm.io/gorm"
+
+	"gitlab.sydip.com/repo/gopkgs/database"
+
+	pb_v1 "gadm-task/pb/v1"
+)
+
+func GetTaskBySourceCode(ctx context.Context, req *pb_v1.GetTaskBySourceCodeRequest) (reply *pb_v1.GetTaskBySourceCodeReply, err error) {
+	reply = &pb_v1.GetTaskBySourceCodeReply{}
+	if req.SourceCode == "" {
+		fmt.Println("参数错误,source code 为空")
+		return reply, nil
+	}
+
+	reply = &pb_v1.GetTaskBySourceCodeReply{}
+	err = database.DB().Raw("select task_id from t_adm_task_data_relation where is_on=1 and source_code=?", req.SourceCode).Find(&reply.TaskList).Error
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			return reply, nil
+		}
+		return reply, errors.ServiceError
+	}
+
+	return reply, nil
+}

+ 148 - 0
main.go

@@ -0,0 +1,148 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package main
+
+import (
+	"gadm-task/impl/v1/check"
+	"gadm-task/utils"
+	"context"
+	"flag"
+	"fmt"
+	"log"
+	"net"
+	"os"
+	"os/signal"
+	"strings"
+	"syscall"
+	"time"
+
+	"gopkg.in/ini.v1"
+
+	"gadm-task/impl"
+	"gadm-task/parser"
+
+	"gitlab.sydip.com/repo/gopkgs/database"
+	"gitlab.sydip.com/repo/gopkgs/tasker"
+
+	"google.golang.org/grpc"
+)
+
+var (
+	appConfigFile = flag.String("appconfig", "/etc/gadm-task/app.conf", "app config file location")
+	configFile    = flag.String("config", "/etc/adm/common.json", "config file location")
+	version       = flag.Bool("version", false, "print the version")
+	GitCommit     = "library-import"
+	Version       = "library-import"
+)
+
+func showVersion() {
+	fmt.Println("Version:  ", Version)
+	fmt.Println("GitCommit:", GitCommit)
+}
+
+func prepare(filename string, discoveryType string) {
+	// 加载配置
+
+	if discoveryType == "k8s" {
+		err := parser.LoadConfig(filename)
+		if err != nil {
+			fmt.Printf("get conf failed, err: %+v\n\n", err)
+			os.Exit(1)
+		}
+	} else {
+		// TODO 从etcd读取配置
+		err := parser.LoadConfig(filename)
+		if err != nil {
+			fmt.Printf("get conf failed, err: %+v\n\n", err)
+			os.Exit(1)
+		}
+	}
+
+	/*err = parser.LoadConfig("conf/app.yaml")
+	if err != nil {
+		fmt.Printf("get conf failed, err: %+v\n\n", err)
+		os.Exit(1)
+	}*/
+	check.SetRunmode(parser.Conf.RunMode)
+	// 注册处理函数
+	// parser.Register(parser.MysqlHandler, parser.RedisHandler, parser.LoggerHandler)
+	parser.Register(parser.LoggerHandler, parser.MysqlHandler)
+	// 执行注册的处理函数
+	parser.Handle()
+
+	// 加载字段管理,启动定时任务
+	utils.FieldLoad()
+}
+
+func run(etcdAddrs []string, serveAddr string) {
+	// 开始监听
+	parser.EctdHandler(etcdAddrs, serveAddr)
+	//serveAddr := fmt.Sprintf("%s:%d", parser.Conf.Rpc.AdmTask.ServiceIp, parser.Conf.Rpc.AdmTask.ServicePort)
+	log.Printf("Listening and serving TCP on %s\n", serveAddr)
+	lis, err := net.Listen("tcp", serveAddr)
+	if err != nil {
+		os.Exit(1)
+	}
+	s := grpc.NewServer()
+	impl.Register(s)
+
+	go func() {
+		if err := s.Serve(lis); err != nil {
+			log.Fatalf("HTTP server listen failed. err: %s\n", err.Error())
+		}
+	}()
+
+	// 优雅关闭服务器
+	sigChan := make(chan os.Signal, 1)
+	// 捕获信号
+	signal.Notify(sigChan, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
+	sigValue := <-sigChan
+	log.Printf("Got a signal:%v", sigValue)
+
+	// 让tasker安全退出
+	tasker.SignalNotify(sigValue)
+
+	// 不管什么行为,都等待5秒退出
+	log.Println("Start to shutdown server...")
+	_, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	s.Stop()
+	log.Println("Shutdown server finished.")
+}
+
+func runPeerToPeer() {
+	// TODO 支持peer to peer 模式
+}
+
+func main() {
+	defer func() {
+		database.Close()
+	}()
+
+	flag.Parse()
+	if *version {
+		showVersion()
+		os.Exit(1)
+	}
+
+	cfg, err := ini.Load(*appConfigFile)
+	if err != nil {
+		fmt.Printf("Fail to read app config file: %v\n\n", err)
+		os.Exit(1)
+	}
+	etcdAddrs := strings.Split(cfg.Section("").Key("etcd_addrs").String(), ",")
+	serveAddr := cfg.Section("").Key("serve_addr").String()
+	discoveryType := cfg.Section("").Key("discovery_type").String()
+
+	prepare(*configFile, discoveryType)
+
+	// go utils.Free()
+	if discoveryType == "k8s" {
+		runPeerToPeer()
+	} else {
+		run(etcdAddrs, serveAddr)
+	}
+
+	return
+}

+ 120 - 0
model/pagination.go

@@ -0,0 +1,120 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package model
+
+import (
+	"strings"
+	"time"
+)
+
+// 游标分页
+type CursorPagination struct {
+	Top         string
+	TopNum      string
+	TopExtra    string
+	Bottom      string
+	BottomNum   string
+	BottomExtra string
+}
+
+func NewCursorPagination() *CursorPagination {
+	return &CursorPagination{}
+}
+
+// 分割分页的top/bottom
+func (c *CursorPagination) SplitTopBottomData(topParam, bottomParam string) {
+	if topParam == "" {
+		c.Top = "0"
+		c.TopNum = "0"
+		c.TopExtra = "0"
+	} else {
+		top := strings.Split(topParam, ",")
+		c.Top = top[0]
+		c.TopNum = top[1]
+		c.TopExtra = top[2]
+	}
+
+	if bottomParam == "" {
+		c.Bottom = "0"
+		c.BottomNum = "0"
+		c.BottomExtra = "0"
+	} else {
+		bottom := strings.Split(bottomParam, ",")
+		c.Bottom = bottom[0]
+		c.BottomNum = bottom[1]
+		c.BottomExtra = bottom[2]
+	}
+}
+
+// 传统分页
+type Pagination struct {
+	CurrentPage int   `json:"current_page"` // 当前页
+	PerPage     int   `json:"per_page"`     // 每一页多少条
+	Total       int   `json:"total"`        // 总条数
+	FirstPage   int   `json:"first_page"`   // 第一页
+	LastPage    int   `json:"last_page"`    // 最后页
+	PrevPage    int   `json:"prev_page"`    // 上一页
+	NextPage    int   `json:"next_page"`    // 下一页
+	Limit       int   `json:"limit"`
+	Offset      int   `json:"offset"`
+	RequestTime int64 `json:"request_time"`
+}
+
+func NewPagination(currentPage, perPage int, requestTime int64) *Pagination {
+	if currentPage == 0 {
+		currentPage = 1
+	}
+	if requestTime == 0 {
+		requestTime = time.Now().Unix()
+	}
+	return &Pagination{
+		CurrentPage: currentPage,
+		PerPage:     perPage,
+		PrevPage:    -1,
+		NextPage:    -1,
+		RequestTime: requestTime,
+	}
+}
+
+// 根据当前页获取分页条数和偏移量
+func (p *Pagination) GetLimitOffset() *Pagination {
+	var num int
+
+	if p.CurrentPage < 1 {
+		num = 0
+	} else {
+		num = p.CurrentPage - 1
+	}
+
+	p.Limit = p.PerPage
+	p.Offset = num * p.PerPage
+
+	return p
+}
+
+// 计算上、下、首、末页
+func (p *Pagination) CalPage(total int) {
+	p.Total = total
+
+	// 第一页
+	if total != 0 {
+		p.FirstPage = 1
+	}
+
+	// 最后页
+	p.LastPage = total / p.PerPage
+	if total%p.PerPage != 0 {
+		p.LastPage += 1
+	}
+
+	// 上一页
+	if p.CurrentPage > 1 {
+		p.PrevPage = p.CurrentPage - 1
+	}
+
+	// 下一页
+	if total > p.PerPage*p.CurrentPage {
+		p.NextPage = p.CurrentPage + 1
+	}
+}

+ 52 - 0
model/sy_brand.go

@@ -0,0 +1,52 @@
+package model
+
+import (
+	"gitlab.sydip.com/repo/gopkgs/database"
+	"gorm.io/gorm"
+)
+
+type SyBrandModel interface {
+	List(db *gorm.DB, pagination *Pagination) ([]DipSyBrand, error)
+	Update(db *gorm.DB, values interface{}) error
+}
+
+type DipSyBrand struct {
+	ID         int64  `gorm:"column:id"          json:"id"          form:"id"`
+	Initial    string `gorm:"column:initial"     json:"initial"     form:"initial"`
+	BrandName  string `gorm:"column:brand_name"  json:"brand_name"  form:"brand_name"`
+	BrandId    string `gorm:"column:brand_id"    json:"brand_id"    form:"brand_id"`
+	Weight     int64  `gorm:"column:weight"      json:"weight"      form:"weight"`
+	HasImg     int64  `gorm:"column:has_img"     json:"has_img"     form:"has_img"`
+	Status     int64  `gorm:"column:status"      json:"status"      form:"status"`
+	UpdateTime string `gorm:"column:update_time" json:"update_time" form:"update_time"`
+}
+
+type defaultSyBrand struct {
+	tableName string
+	fields    string
+}
+
+func NewSyBrand() SyBrandModel {
+	return &defaultSyBrand{
+		tableName: "t_dip_sy_brand",
+		fields:    "id, initial, brand_name, brand_id, weight, has_img, status, update_time",
+	}
+}
+
+func (d *defaultSyBrand) List(db *gorm.DB, pagination *Pagination) ([]DipSyBrand, error) {
+	var res []DipSyBrand
+	err := database.List(db, &res, database.Option{
+		TableName: d.tableName,
+		Fields:    d.fields,
+		Limit:     pagination.Limit,
+		OffSet:    pagination.Offset,
+	})
+
+	return res, err
+}
+
+func (d *defaultSyBrand) Update(db *gorm.DB, values interface{}) error {
+	return database.Update(db, values, database.Option{
+		TableName: d.tableName,
+	})
+}

+ 100 - 0
parser/config.go

@@ -0,0 +1,100 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package parser
+
+import (
+	"gadm-task/config"
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"github.com/fsnotify/fsnotify"
+	"github.com/spf13/viper"
+)
+
+var v *viper.Viper
+var Conf *config.Configure
+
+// LoadConfig 装载配置文件
+func LoadConfig(filename string) error {
+	fmt.Println("file_name:", filename)
+	configPath, configName := filepath.Split(filename)
+	fileList := strings.Split(configName, ".")
+	if len(fileList) < 2 {
+		return fmt.Errorf("%s", "文件格式不正确")
+	}
+
+	configName = fileList[0]
+	fileExt := fileList[1]
+	var err error
+	if fileExt == "json" {
+		err = LoadConfigFromJson(configName, configPath)
+	} else if fileExt == "yaml" || fileExt == "yml" {
+		err = LoadConfigFromYaml(configName, configPath)
+	} else {
+		err = fmt.Errorf("%s", "不支持的文件格式")
+	}
+
+	// 出错直接返回
+	if err != nil {
+		return err
+	}
+
+	// 设置文件监听函数
+	v.OnConfigChange(func(e fsnotify.Event) {
+		fmt.Printf("配置文件有变化, Event:%s \n", e.String())
+		parseConfig()
+		Handle()
+	})
+
+	// 监听
+	v.WatchConfig()
+	fmt.Println(*Conf)
+	return nil
+}
+
+// LoadConfigFromYaml 装载yaml类型的配置文件
+func LoadConfigFromYaml(configName, configPath string) error {
+	v = viper.New()
+	v.SetConfigName(configName)
+	v.AddConfigPath(configPath)
+	//设置配置文件类型
+	v.SetConfigType("yaml")
+
+	if err := v.ReadInConfig(); err != nil {
+		return err
+	}
+
+	return parseConfig()
+}
+
+// LoadConfigFromJson 装载json类型的配置文件
+func LoadConfigFromJson(configName, configPath string) error {
+	v = viper.New()
+	v.SetConfigName(configName)
+	v.AddConfigPath(configPath)
+
+	//设置配置文件类型
+	v.SetConfigType("json")
+	if err := v.ReadInConfig(); err != nil {
+		return err
+	}
+
+	return parseConfig()
+}
+
+func parseConfig() error {
+	c := &config.Configure{}
+	if err := v.Unmarshal(c); err != nil {
+		return err
+	}
+
+	// 如果全局结构体不为空则覆盖部分值
+	if Conf == nil {
+		Conf = c
+		return nil
+	}
+
+	return nil
+}

+ 65 - 0
parser/database.go

@@ -0,0 +1,65 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package parser
+
+import (
+	"gadm-task/config"
+
+	"gitlab.sydip.com/repo/gopkgs/database"
+)
+
+var mysqlConfig config.MysqlConfig
+
+func MysqlHandler(conf *config.Configure) {
+	// mysqlConfig.Addr 不为空表示已经初始化
+	if mysqlConfig.Addr != "" {
+		sqlDB, err := database.DB().DB()
+		if err != nil {
+			return
+		}
+		// 地址,用户名,密码相同比较其他配置是否一致
+		if mysqlConfig.User == conf.Mysql.User &&
+			mysqlConfig.Password == conf.Mysql.Password &&
+			mysqlConfig.Addr == conf.Mysql.Addr {
+			if mysqlConfig.MaxIdle != conf.Mysql.MaxIdle {
+				maxIdleInt, _ := conf.Mysql.MaxIdle.Int64()
+				sqlDB.SetMaxIdleConns(int(maxIdleInt))
+			}
+
+			if mysqlConfig.MaxConn != conf.Mysql.MaxConn {
+				maxConnInt, _ := conf.Mysql.MaxConn.Int64()
+				sqlDB.SetMaxOpenConns(int(maxConnInt))
+			}
+
+			if mysqlConfig.LogMode != conf.Mysql.LogMode {
+				database.DB().Debug()
+			}
+
+			return // 这里直接返回
+		}
+
+		// 地址用户名密码不一致,关闭连接重新连接
+		_ = sqlDB.Close()
+	}
+
+	// 私有db为空使用公共db
+	dbname := conf.Rpc.AdmTask.MysqlDb
+	if dbname == "" {
+		dbname = conf.Mysql.Db
+	}
+
+	maxIdleInt, _ := conf.Mysql.MaxIdle.Int64()
+	maxConnInt, _ := conf.Mysql.MaxConn.Int64()
+	// 连接database
+	database.Setup(conf.Mysql.User,
+		conf.Mysql.Password,
+		conf.Mysql.Addr,
+		dbname,
+		conf.Mysql.Charset,
+		int(maxIdleInt),
+		int(maxConnInt),
+		conf.Mysql.LogMode != "prod")
+
+	mysqlConfig = conf.Mysql
+}

+ 129 - 0
parser/etcd.go

@@ -0,0 +1,129 @@
+package parser
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"time"
+
+	uuid "github.com/satori/go.uuid"
+	clientv3 "go.etcd.io/etcd/client/v3"
+	"go.etcd.io/etcd/client/v3/naming/endpoints"
+)
+
+var cli *clientv3.Client
+
+func GetEtcdClient() *clientv3.Client {
+	return cli
+}
+
+func EctdHandler(etcdAddrs []string, serveAddr string) {
+	var (
+		err error
+	)
+
+	cli, err = clientv3.New(clientv3.Config{
+		Endpoints:   etcdAddrs,
+		DialTimeout: 5 * time.Second,
+	})
+	if err != nil {
+		panic(err)
+	}
+
+	// 注册续租
+	err = relet(Conf.Rpc.AdmTask.ServiceName,
+		serveAddr,
+		Conf.Rpc.Prefix,
+	)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func getValue(addr string) string {
+	return `{"Addr:":"http://` + addr + `"}`
+}
+
+// 续租
+func relet(serviceName, serviceAddr, prefix string) error {
+	var ctx = context.Background()
+	// 创建一个租约
+	lease := clientv3.NewLease(cli)
+	cancelCtx, cancel := context.WithTimeout(ctx, time.Second*3)
+	defer cancel()
+
+	leaseResp, err := lease.Grant(cancelCtx, 3)
+	if err != nil {
+		return err
+	}
+
+	// 长链接
+	leaseChannel, err := lease.KeepAlive(ctx, leaseResp.ID)
+	if err != nil {
+		return err
+	}
+
+	em, err := endpoints.NewManager(cli, prefix)
+	if err != nil {
+		return err
+	}
+
+	cancelCtx, cancel = context.WithTimeout(ctx, time.Second*3)
+	defer cancel()
+
+	if err := em.AddEndpoint(cancelCtx, fmt.Sprintf("%s/%s/%s", prefix, serviceName, uuid.NewV4().String()), endpoints.Endpoint{
+		Addr: serviceAddr,
+	}, clientv3.WithLease(leaseResp.ID)); err != nil {
+		return err
+	}
+
+	log.Println("Register etcd success")
+
+	del := func() {
+		log.Println("Register close")
+
+		cancelCtx, cancel = context.WithTimeout(ctx, time.Second*3)
+		defer cancel()
+		em.DeleteEndpoint(cancelCtx, serviceName)
+
+		lease.Close()
+	}
+
+	// 保持注册状态(连接断开重连)
+	keepRegister(ctx, leaseChannel, del, serviceName, serviceAddr, prefix)
+
+	return nil
+}
+
+func keepRegister(ctx context.Context, leaseChannel <-chan *clientv3.LeaseKeepAliveResponse, cleanFunc func(), serviceName, serviceAddr, prefix string) {
+	go func() {
+		failedCount := 0
+		for {
+			select {
+			case resp := <-leaseChannel:
+				if resp != nil {
+					//log.Println("keep alive success.")
+				} else {
+					log.Println("keep alive failed.")
+					failedCount++
+					for failedCount > 3 {
+						cleanFunc()
+						if err := relet(serviceName, serviceAddr, prefix); err != nil {
+							time.Sleep(time.Second)
+							continue
+						}
+						return
+					}
+					continue
+				}
+			case <-ctx.Done():
+				cleanFunc()
+				cli.Close()
+				return
+			}
+		}
+	}()
+}
+
+func UnRegisterEtcd(serviceName, serviceAddr string) {
+}

+ 30 - 0
parser/init.go

@@ -0,0 +1,30 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package parser
+
+import "gadm-task/config"
+
+// 处理函数的声明
+type Handler func(*config.Configure)
+
+// 处理函数数组
+var handlers []Handler
+
+// Register 注册处理函数
+func Register(handler ...Handler) {
+	handlers = append(handlers, handler...)
+}
+
+// Do 执行处理函数
+func Handle() {
+	// 配置为nil,直接返回
+	if Conf == nil {
+		return
+	}
+
+	// 执行处理函数
+	for _, handler := range handlers {
+		handler(Conf)
+	}
+}

+ 65 - 0
parser/logger.go

@@ -0,0 +1,65 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package parser
+
+import (
+	"gadm-task/config"
+	"fmt"
+
+	"gitlab.sydip.com/repo/gopkgs/logger"
+	"gitlab.sydip.com/repo/gopkgs/tasker"
+)
+
+var logConfig config.LogConfig
+
+func LoggerHandler(conf *config.Configure) {
+	// logConfig.Path 不为空表示已经初始化
+	if logConfig.Path != "" {
+		if logConfig.Path == conf.Log.Path {
+			return
+		} else {
+			// 刷盘
+			// logger.Sync()
+		}
+	}
+
+	// 取出Stacktrace和Level
+	logStacktrace := conf.Rpc.AdmTask.LogStacktrace
+	logLevel := conf.Rpc.AdmTask.LogLevel
+	if logLevel == "" {
+		logLevel = conf.Log.Level
+	}
+
+	ms, _ := conf.Log.MaxSize.Int64()
+	mb, _ := conf.Log.MaxBackups.Int64()
+	ma, _ := conf.Log.MaxAge.Int64()
+	maxSize := int(ms)
+	maxBackups := int(mb)
+	maxAge := int(ma)
+
+	disableStacktrace := (logStacktrace == "true")
+	// 新建全局日志器
+	logger.New(conf.RunMode,
+		logLevel,
+		fmt.Sprintf("%s/%s.log", conf.Log.Path, conf.Rpc.AdmTask.ServiceName),
+		maxSize,
+		maxBackups,
+		maxAge,
+		disableStacktrace,
+		true)
+
+	// access日志器
+	accessLogger := logger.New(conf.RunMode,
+		logLevel,
+		fmt.Sprintf("%s/%s-access.log", conf.Log.Path, conf.Rpc.AdmTask.ServiceName),
+		maxSize,
+		maxBackups,
+		maxAge,
+		disableStacktrace,
+		false)
+
+	// 设置访问日志
+	tasker.SetLogger(accessLogger)
+	logConfig = conf.Log
+}

+ 67 - 0
parser/redis.go

@@ -0,0 +1,67 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package parser
+
+/*
+import (
+	"gadm-task/config"
+	"strings"
+
+	"gitlab.sydip.com/repo/gopkgs/cache"
+)
+
+var redisConfig config.RedisConfig
+
+func RedisHandler(conf *config.Configure) {
+	// redisConfig.Addr 不为空表示已经初始化
+	var redisAddrs []string
+	if len(redisConfig.Addrs) != 0 {
+		redisAddrs = strings.Split(redisConfig.Addrs, ",")
+	}
+	if len(redisConfig.Addrs) != 0 {
+		// 地址或密码变化,关闭当前连接,重新连接
+		if len(redisConfig.Addrs) != len(conf.Redis.Addrs) ||
+			redisConfig.Password != conf.Redis.Password {
+			cache.Close()
+		} else {
+			// 判断addrs数组是否相同
+			addrs := map[string]bool{}
+			for _, v := range redisConfig.Addrs {
+				addrs[v] = true
+			}
+			for _, v := range conf.Redis.Addrs {
+				addrs[v] = true
+			}
+
+			// 地址和密码不变返回
+			if len(redisConfig.Addrs) == len(addrs) {
+				return
+			}
+
+			// 关闭cache
+			cache.Close()
+		}
+	}
+
+	// 私有db不合法使用公共db
+	db := conf.Rpc.AdmTask.RedisDb
+	if db < 0 || db > 15 {
+		db = conf.Redis.DB
+	}
+
+	// 连接redis
+	cache.Setup(conf.Redis.Addrs,
+		conf.Redis.Password,
+		db,
+		conf.Redis.PoolSize,
+		conf.Redis.MinIdleConns,
+		conf.Redis.MaxRetries,
+		conf.Redis.Cluster)
+
+	if conf.RunMode != "prod" {
+		// open 'DEBUG' switcher
+	}
+
+	redisConfig = conf.Redis
+}*/

+ 24 - 0
pb/ReadMe.md

@@ -0,0 +1,24 @@
+### 必要条件
+1. 下载与安装`protoc`工具
+
+    https://github.com/protocolbuffers/protobuf/releases
+
+2. 安装protoc Go插件:`protoc-gen-go`
+
+    方式1(`默认带omitempty`):
+    ```shell
+    go get -u github.com/golang/protobuf/protoc-gen-go
+    ```
+    方式2(`不带omitempty`):
+    ```shell
+    git clone http://gitlab.sydip.com/repo/libs/go/gomodule
+    cd mod /github.com/golang/protobuf@v1.3.4/protoc-gen-go/generator
+    go build
+    cp protoc-gen-go $GOBIN/
+    ```
+
+### 生成说明
+1. 普通生成
+    protoc --go_out=. *.proto
+2. 支持rpc
+    protoc --go_out=plugins=grpc:. *.proto

+ 240 - 0
pb/adm_task_service.pb.go

@@ -0,0 +1,240 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: adm_task_service.proto
+
+// package声明符,用来防止不同的消息类型有命名冲突
+
+package pb
+
+import (
+	v1 "gadm-task/pb/v1"
+	context "context"
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+func init() {
+	proto.RegisterFile("adm_task_service.proto", fileDescriptor_b888879dea74b804)
+}
+
+var fileDescriptor_b888879dea74b804 = []byte{
+	// 220 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4b, 0x4c, 0xc9, 0x8d,
+	0x2f, 0x49, 0x2c, 0xce, 0x8e, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2b, 0x28, 0xca,
+	0x2f, 0xc9, 0x17, 0xe2, 0x2a, 0x48, 0xd2, 0x83, 0x8a, 0x48, 0xf1, 0x96, 0x19, 0xea, 0x83, 0x94,
+	0x40, 0xa4, 0x8c, 0xd6, 0x30, 0x71, 0xb1, 0x3b, 0xa6, 0xe4, 0x86, 0x24, 0x16, 0x67, 0x0b, 0x85,
+	0x72, 0x09, 0xbb, 0xa7, 0x96, 0x80, 0x98, 0x4e, 0x95, 0xc1, 0xf9, 0xa5, 0x45, 0xc9, 0xa9, 0xce,
+	0xf9, 0x29, 0xa9, 0x42, 0x72, 0x7a, 0x65, 0x86, 0x7a, 0x58, 0x24, 0x82, 0x52, 0x0b, 0x4b, 0x53,
+	0x8b, 0x4b, 0xa4, 0x64, 0x70, 0xca, 0x17, 0xe4, 0x54, 0x2a, 0x31, 0x08, 0xf9, 0x73, 0x09, 0x86,
+	0x16, 0xa4, 0x24, 0x96, 0xa4, 0xfa, 0xa7, 0xa5, 0xe5, 0x64, 0xe6, 0xa5, 0x82, 0xed, 0x02, 0x6b,
+	0xc2, 0x10, 0x86, 0x19, 0x29, 0x85, 0x43, 0x16, 0x62, 0xa0, 0x2d, 0x17, 0xb7, 0x73, 0x46, 0x6a,
+	0x72, 0xb6, 0x5b, 0x7e, 0x51, 0x6e, 0x62, 0x89, 0x90, 0x18, 0x48, 0x31, 0x92, 0x00, 0xcc, 0x10,
+	0x11, 0x0c, 0x71, 0x88, 0x76, 0x2b, 0x2e, 0x2e, 0xb7, 0xcc, 0xd4, 0x9c, 0x14, 0xb0, 0x94, 0x90,
+	0x28, 0x48, 0x15, 0x82, 0x0f, 0xd3, 0x2c, 0x8c, 0x2e, 0x0c, 0xd6, 0xeb, 0xc4, 0x1b, 0xc5, 0x9d,
+	0x98, 0x92, 0xab, 0x0b, 0x0a, 0x40, 0xfd, 0x82, 0xa4, 0x24, 0x36, 0x70, 0x20, 0x1a, 0x03, 0x02,
+	0x00, 0x00, 0xff, 0xff, 0x36, 0xca, 0x5d, 0xfc, 0x79, 0x01, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConnInterface
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion6
+
+// AdmTaskClient is the client API for AdmTask service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type AdmTaskClient interface {
+	// Sends a greeting
+	GetTaskBySourceCode(ctx context.Context, in *v1.GetTaskBySourceCodeRequest, opts ...grpc.CallOption) (*v1.GetTaskBySourceCodeReply, error)
+	UpdateOfflineTask(ctx context.Context, in *v1.UpdateOfflineTaskRequest, opts ...grpc.CallOption) (*v1.UpdateOfflineTaskReply, error)
+	CheckFormat(ctx context.Context, in *v1.CheckFormatRequest, opts ...grpc.CallOption) (*v1.CheckFormatReply, error)
+	FieldCheck(ctx context.Context, in *v1.FieldCheckRequest, opts ...grpc.CallOption) (*v1.FieldCheckReply, error)
+}
+
+type admTaskClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewAdmTaskClient(cc grpc.ClientConnInterface) AdmTaskClient {
+	return &admTaskClient{cc}
+}
+
+func (c *admTaskClient) GetTaskBySourceCode(ctx context.Context, in *v1.GetTaskBySourceCodeRequest, opts ...grpc.CallOption) (*v1.GetTaskBySourceCodeReply, error) {
+	out := new(v1.GetTaskBySourceCodeReply)
+	err := c.cc.Invoke(ctx, "/pb.service.AdmTask/GetTaskBySourceCode", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *admTaskClient) UpdateOfflineTask(ctx context.Context, in *v1.UpdateOfflineTaskRequest, opts ...grpc.CallOption) (*v1.UpdateOfflineTaskReply, error) {
+	out := new(v1.UpdateOfflineTaskReply)
+	err := c.cc.Invoke(ctx, "/pb.service.AdmTask/UpdateOfflineTask", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *admTaskClient) CheckFormat(ctx context.Context, in *v1.CheckFormatRequest, opts ...grpc.CallOption) (*v1.CheckFormatReply, error) {
+	out := new(v1.CheckFormatReply)
+	err := c.cc.Invoke(ctx, "/pb.service.AdmTask/CheckFormat", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *admTaskClient) FieldCheck(ctx context.Context, in *v1.FieldCheckRequest, opts ...grpc.CallOption) (*v1.FieldCheckReply, error) {
+	out := new(v1.FieldCheckReply)
+	err := c.cc.Invoke(ctx, "/pb.service.AdmTask/FieldCheck", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// AdmTaskServer is the server API for AdmTask service.
+type AdmTaskServer interface {
+	// Sends a greeting
+	GetTaskBySourceCode(context.Context, *v1.GetTaskBySourceCodeRequest) (*v1.GetTaskBySourceCodeReply, error)
+	UpdateOfflineTask(context.Context, *v1.UpdateOfflineTaskRequest) (*v1.UpdateOfflineTaskReply, error)
+	CheckFormat(context.Context, *v1.CheckFormatRequest) (*v1.CheckFormatReply, error)
+	FieldCheck(context.Context, *v1.FieldCheckRequest) (*v1.FieldCheckReply, error)
+}
+
+// UnimplementedAdmTaskServer can be embedded to have forward compatible implementations.
+type UnimplementedAdmTaskServer struct {
+}
+
+func (*UnimplementedAdmTaskServer) GetTaskBySourceCode(ctx context.Context, req *v1.GetTaskBySourceCodeRequest) (*v1.GetTaskBySourceCodeReply, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetTaskBySourceCode not implemented")
+}
+func (*UnimplementedAdmTaskServer) UpdateOfflineTask(ctx context.Context, req *v1.UpdateOfflineTaskRequest) (*v1.UpdateOfflineTaskReply, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method UpdateOfflineTask not implemented")
+}
+func (*UnimplementedAdmTaskServer) CheckFormat(ctx context.Context, req *v1.CheckFormatRequest) (*v1.CheckFormatReply, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CheckFormat not implemented")
+}
+func (*UnimplementedAdmTaskServer) FieldCheck(ctx context.Context, req *v1.FieldCheckRequest) (*v1.FieldCheckReply, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method FieldCheck not implemented")
+}
+
+func RegisterAdmTaskServer(s *grpc.Server, srv AdmTaskServer) {
+	s.RegisterService(&_AdmTask_serviceDesc, srv)
+}
+
+func _AdmTask_GetTaskBySourceCode_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(v1.GetTaskBySourceCodeRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AdmTaskServer).GetTaskBySourceCode(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/pb.service.AdmTask/GetTaskBySourceCode",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AdmTaskServer).GetTaskBySourceCode(ctx, req.(*v1.GetTaskBySourceCodeRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AdmTask_UpdateOfflineTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(v1.UpdateOfflineTaskRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AdmTaskServer).UpdateOfflineTask(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/pb.service.AdmTask/UpdateOfflineTask",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AdmTaskServer).UpdateOfflineTask(ctx, req.(*v1.UpdateOfflineTaskRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AdmTask_CheckFormat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(v1.CheckFormatRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AdmTaskServer).CheckFormat(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/pb.service.AdmTask/CheckFormat",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AdmTaskServer).CheckFormat(ctx, req.(*v1.CheckFormatRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _AdmTask_FieldCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(v1.FieldCheckRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AdmTaskServer).FieldCheck(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/pb.service.AdmTask/FieldCheck",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AdmTaskServer).FieldCheck(ctx, req.(*v1.FieldCheckRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _AdmTask_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "pb.service.AdmTask",
+	HandlerType: (*AdmTaskServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "GetTaskBySourceCode",
+			Handler:    _AdmTask_GetTaskBySourceCode_Handler,
+		},
+		{
+			MethodName: "UpdateOfflineTask",
+			Handler:    _AdmTask_UpdateOfflineTask_Handler,
+		},
+		{
+			MethodName: "CheckFormat",
+			Handler:    _AdmTask_CheckFormat_Handler,
+		},
+		{
+			MethodName: "FieldCheck",
+			Handler:    _AdmTask_FieldCheck_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "adm_task_service.proto",
+}

+ 17 - 0
pb/adm_task_service.proto

@@ -0,0 +1,17 @@
+syntax = "proto3";
+
+import "v1/task.proto";
+
+// package声明符,用来防止不同的消息类型有命名冲突
+package pb.service;
+// 用于生成指定语言go的包名称
+option go_package = "adm-task/pb";
+
+// The greeting service definition.
+service AdmTask {
+    // Sends a greeting
+    rpc GetTaskBySourceCode (v1.GetTaskBySourceCodeRequest) returns (v1.GetTaskBySourceCodeReply) {}
+    rpc UpdateOfflineTask (v1.UpdateOfflineTaskRequest) returns (v1.UpdateOfflineTaskReply) {}
+    rpc CheckFormat (v1.CheckFormatRequest) returns (v1.CheckFormatReply) {}
+    rpc FieldCheck (v1.FieldCheckRequest) returns (v1.FieldCheckReply){}
+}

+ 435 - 0
pb/v1/task.pb.go

@@ -0,0 +1,435 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: task.proto
+
+// package声明符,用来防止不同的消息类型有命名冲突
+
+package v1
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+// The request message containing the user's name.
+type GetTaskBySourceCodeRequest struct {
+	SourceCode           string   `protobuf:"bytes,1,opt,name=source_code,json=sourceCode,proto3" json:"source_code"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *GetTaskBySourceCodeRequest) Reset()         { *m = GetTaskBySourceCodeRequest{} }
+func (m *GetTaskBySourceCodeRequest) String() string { return proto.CompactTextString(m) }
+func (*GetTaskBySourceCodeRequest) ProtoMessage()    {}
+func (*GetTaskBySourceCodeRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ce5d8dd45b4a91ff, []int{0}
+}
+
+func (m *GetTaskBySourceCodeRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_GetTaskBySourceCodeRequest.Unmarshal(m, b)
+}
+func (m *GetTaskBySourceCodeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_GetTaskBySourceCodeRequest.Marshal(b, m, deterministic)
+}
+func (m *GetTaskBySourceCodeRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GetTaskBySourceCodeRequest.Merge(m, src)
+}
+func (m *GetTaskBySourceCodeRequest) XXX_Size() int {
+	return xxx_messageInfo_GetTaskBySourceCodeRequest.Size(m)
+}
+func (m *GetTaskBySourceCodeRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_GetTaskBySourceCodeRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GetTaskBySourceCodeRequest proto.InternalMessageInfo
+
+func (m *GetTaskBySourceCodeRequest) GetSourceCode() string {
+	if m != nil {
+		return m.SourceCode
+	}
+	return ""
+}
+
+// The response message containing the greetings
+type GetTaskBySourceCodeReply struct {
+	TaskList             []int32  `protobuf:"varint,1,rep,packed,name=task_list,json=taskList,proto3" json:"task_list"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *GetTaskBySourceCodeReply) Reset()         { *m = GetTaskBySourceCodeReply{} }
+func (m *GetTaskBySourceCodeReply) String() string { return proto.CompactTextString(m) }
+func (*GetTaskBySourceCodeReply) ProtoMessage()    {}
+func (*GetTaskBySourceCodeReply) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ce5d8dd45b4a91ff, []int{1}
+}
+
+func (m *GetTaskBySourceCodeReply) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_GetTaskBySourceCodeReply.Unmarshal(m, b)
+}
+func (m *GetTaskBySourceCodeReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_GetTaskBySourceCodeReply.Marshal(b, m, deterministic)
+}
+func (m *GetTaskBySourceCodeReply) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GetTaskBySourceCodeReply.Merge(m, src)
+}
+func (m *GetTaskBySourceCodeReply) XXX_Size() int {
+	return xxx_messageInfo_GetTaskBySourceCodeReply.Size(m)
+}
+func (m *GetTaskBySourceCodeReply) XXX_DiscardUnknown() {
+	xxx_messageInfo_GetTaskBySourceCodeReply.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GetTaskBySourceCodeReply proto.InternalMessageInfo
+
+func (m *GetTaskBySourceCodeReply) GetTaskList() []int32 {
+	if m != nil {
+		return m.TaskList
+	}
+	return nil
+}
+
+type UpdateOfflineTaskRequest struct {
+	TaskId               int32    `protobuf:"varint,1,opt,name=task_id,json=taskId,proto3" json:"task_id"`
+	Total                int32    `protobuf:"varint,2,opt,name=total,proto3" json:"total"`
+	IsFinish             int32    `protobuf:"varint,3,opt,name=is_finish,json=isFinish,proto3" json:"is_finish"`
+	FinishCount          int32    `protobuf:"varint,4,opt,name=finish_count,json=finishCount,proto3" json:"finish_count"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *UpdateOfflineTaskRequest) Reset()         { *m = UpdateOfflineTaskRequest{} }
+func (m *UpdateOfflineTaskRequest) String() string { return proto.CompactTextString(m) }
+func (*UpdateOfflineTaskRequest) ProtoMessage()    {}
+func (*UpdateOfflineTaskRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ce5d8dd45b4a91ff, []int{2}
+}
+
+func (m *UpdateOfflineTaskRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_UpdateOfflineTaskRequest.Unmarshal(m, b)
+}
+func (m *UpdateOfflineTaskRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_UpdateOfflineTaskRequest.Marshal(b, m, deterministic)
+}
+func (m *UpdateOfflineTaskRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_UpdateOfflineTaskRequest.Merge(m, src)
+}
+func (m *UpdateOfflineTaskRequest) XXX_Size() int {
+	return xxx_messageInfo_UpdateOfflineTaskRequest.Size(m)
+}
+func (m *UpdateOfflineTaskRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_UpdateOfflineTaskRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UpdateOfflineTaskRequest proto.InternalMessageInfo
+
+func (m *UpdateOfflineTaskRequest) GetTaskId() int32 {
+	if m != nil {
+		return m.TaskId
+	}
+	return 0
+}
+
+func (m *UpdateOfflineTaskRequest) GetTotal() int32 {
+	if m != nil {
+		return m.Total
+	}
+	return 0
+}
+
+func (m *UpdateOfflineTaskRequest) GetIsFinish() int32 {
+	if m != nil {
+		return m.IsFinish
+	}
+	return 0
+}
+
+func (m *UpdateOfflineTaskRequest) GetFinishCount() int32 {
+	if m != nil {
+		return m.FinishCount
+	}
+	return 0
+}
+
+type UpdateOfflineTaskReply struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *UpdateOfflineTaskReply) Reset()         { *m = UpdateOfflineTaskReply{} }
+func (m *UpdateOfflineTaskReply) String() string { return proto.CompactTextString(m) }
+func (*UpdateOfflineTaskReply) ProtoMessage()    {}
+func (*UpdateOfflineTaskReply) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ce5d8dd45b4a91ff, []int{3}
+}
+
+func (m *UpdateOfflineTaskReply) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_UpdateOfflineTaskReply.Unmarshal(m, b)
+}
+func (m *UpdateOfflineTaskReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_UpdateOfflineTaskReply.Marshal(b, m, deterministic)
+}
+func (m *UpdateOfflineTaskReply) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_UpdateOfflineTaskReply.Merge(m, src)
+}
+func (m *UpdateOfflineTaskReply) XXX_Size() int {
+	return xxx_messageInfo_UpdateOfflineTaskReply.Size(m)
+}
+func (m *UpdateOfflineTaskReply) XXX_DiscardUnknown() {
+	xxx_messageInfo_UpdateOfflineTaskReply.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UpdateOfflineTaskReply proto.InternalMessageInfo
+
+type CheckFormatRequest struct {
+	Vin                  string   `protobuf:"bytes,1,opt,name=vin,proto3" json:"vin"`
+	PlateNo              string   `protobuf:"bytes,2,opt,name=plate_no,json=plateNo,proto3" json:"plate_no"`
+	PlateType            string   `protobuf:"bytes,3,opt,name=plate_type,json=plateType,proto3" json:"plate_type"`
+	Idcert               string   `protobuf:"bytes,4,opt,name=idcert,proto3" json:"idcert"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *CheckFormatRequest) Reset()         { *m = CheckFormatRequest{} }
+func (m *CheckFormatRequest) String() string { return proto.CompactTextString(m) }
+func (*CheckFormatRequest) ProtoMessage()    {}
+func (*CheckFormatRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ce5d8dd45b4a91ff, []int{4}
+}
+
+func (m *CheckFormatRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_CheckFormatRequest.Unmarshal(m, b)
+}
+func (m *CheckFormatRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_CheckFormatRequest.Marshal(b, m, deterministic)
+}
+func (m *CheckFormatRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_CheckFormatRequest.Merge(m, src)
+}
+func (m *CheckFormatRequest) XXX_Size() int {
+	return xxx_messageInfo_CheckFormatRequest.Size(m)
+}
+func (m *CheckFormatRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_CheckFormatRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_CheckFormatRequest proto.InternalMessageInfo
+
+func (m *CheckFormatRequest) GetVin() string {
+	if m != nil {
+		return m.Vin
+	}
+	return ""
+}
+
+func (m *CheckFormatRequest) GetPlateNo() string {
+	if m != nil {
+		return m.PlateNo
+	}
+	return ""
+}
+
+func (m *CheckFormatRequest) GetPlateType() string {
+	if m != nil {
+		return m.PlateType
+	}
+	return ""
+}
+
+func (m *CheckFormatRequest) GetIdcert() string {
+	if m != nil {
+		return m.Idcert
+	}
+	return ""
+}
+
+type CheckFormatReply struct {
+	Vin                  string   `protobuf:"bytes,1,opt,name=vin,proto3" json:"vin"`
+	PlateNo              string   `protobuf:"bytes,2,opt,name=plate_no,json=plateNo,proto3" json:"plate_no"`
+	PlateType            string   `protobuf:"bytes,3,opt,name=plate_type,json=plateType,proto3" json:"plate_type"`
+	Idcert               string   `protobuf:"bytes,4,opt,name=idcert,proto3" json:"idcert"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *CheckFormatReply) Reset()         { *m = CheckFormatReply{} }
+func (m *CheckFormatReply) String() string { return proto.CompactTextString(m) }
+func (*CheckFormatReply) ProtoMessage()    {}
+func (*CheckFormatReply) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ce5d8dd45b4a91ff, []int{5}
+}
+
+func (m *CheckFormatReply) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_CheckFormatReply.Unmarshal(m, b)
+}
+func (m *CheckFormatReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_CheckFormatReply.Marshal(b, m, deterministic)
+}
+func (m *CheckFormatReply) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_CheckFormatReply.Merge(m, src)
+}
+func (m *CheckFormatReply) XXX_Size() int {
+	return xxx_messageInfo_CheckFormatReply.Size(m)
+}
+func (m *CheckFormatReply) XXX_DiscardUnknown() {
+	xxx_messageInfo_CheckFormatReply.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_CheckFormatReply proto.InternalMessageInfo
+
+func (m *CheckFormatReply) GetVin() string {
+	if m != nil {
+		return m.Vin
+	}
+	return ""
+}
+
+func (m *CheckFormatReply) GetPlateNo() string {
+	if m != nil {
+		return m.PlateNo
+	}
+	return ""
+}
+
+func (m *CheckFormatReply) GetPlateType() string {
+	if m != nil {
+		return m.PlateType
+	}
+	return ""
+}
+
+func (m *CheckFormatReply) GetIdcert() string {
+	if m != nil {
+		return m.Idcert
+	}
+	return ""
+}
+
+type FieldCheckRequest struct {
+	Content              string   `protobuf:"bytes,1,opt,name=content,proto3" json:"content"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *FieldCheckRequest) Reset()         { *m = FieldCheckRequest{} }
+func (m *FieldCheckRequest) String() string { return proto.CompactTextString(m) }
+func (*FieldCheckRequest) ProtoMessage()    {}
+func (*FieldCheckRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ce5d8dd45b4a91ff, []int{6}
+}
+
+func (m *FieldCheckRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FieldCheckRequest.Unmarshal(m, b)
+}
+func (m *FieldCheckRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FieldCheckRequest.Marshal(b, m, deterministic)
+}
+func (m *FieldCheckRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FieldCheckRequest.Merge(m, src)
+}
+func (m *FieldCheckRequest) XXX_Size() int {
+	return xxx_messageInfo_FieldCheckRequest.Size(m)
+}
+func (m *FieldCheckRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_FieldCheckRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FieldCheckRequest proto.InternalMessageInfo
+
+func (m *FieldCheckRequest) GetContent() string {
+	if m != nil {
+		return m.Content
+	}
+	return ""
+}
+
+type FieldCheckReply struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *FieldCheckReply) Reset()         { *m = FieldCheckReply{} }
+func (m *FieldCheckReply) String() string { return proto.CompactTextString(m) }
+func (*FieldCheckReply) ProtoMessage()    {}
+func (*FieldCheckReply) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ce5d8dd45b4a91ff, []int{7}
+}
+
+func (m *FieldCheckReply) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_FieldCheckReply.Unmarshal(m, b)
+}
+func (m *FieldCheckReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_FieldCheckReply.Marshal(b, m, deterministic)
+}
+func (m *FieldCheckReply) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_FieldCheckReply.Merge(m, src)
+}
+func (m *FieldCheckReply) XXX_Size() int {
+	return xxx_messageInfo_FieldCheckReply.Size(m)
+}
+func (m *FieldCheckReply) XXX_DiscardUnknown() {
+	xxx_messageInfo_FieldCheckReply.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_FieldCheckReply proto.InternalMessageInfo
+
+func init() {
+	proto.RegisterType((*GetTaskBySourceCodeRequest)(nil), "v1.GetTaskBySourceCodeRequest")
+	proto.RegisterType((*GetTaskBySourceCodeReply)(nil), "v1.GetTaskBySourceCodeReply")
+	proto.RegisterType((*UpdateOfflineTaskRequest)(nil), "v1.UpdateOfflineTaskRequest")
+	proto.RegisterType((*UpdateOfflineTaskReply)(nil), "v1.UpdateOfflineTaskReply")
+	proto.RegisterType((*CheckFormatRequest)(nil), "v1.CheckFormatRequest")
+	proto.RegisterType((*CheckFormatReply)(nil), "v1.CheckFormatReply")
+	proto.RegisterType((*FieldCheckRequest)(nil), "v1.FieldCheckRequest")
+	proto.RegisterType((*FieldCheckReply)(nil), "v1.FieldCheckReply")
+}
+
+func init() {
+	proto.RegisterFile("task.proto", fileDescriptor_ce5d8dd45b4a91ff)
+}
+
+var fileDescriptor_ce5d8dd45b4a91ff = []byte{
+	// 358 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x92, 0x4f, 0x4f, 0xea, 0x40,
+	0x14, 0xc5, 0x53, 0x78, 0xfc, 0xe9, 0xe5, 0xe5, 0x3d, 0x98, 0x18, 0x1c, 0x35, 0x46, 0xec, 0x8a,
+	0x0d, 0x12, 0xe2, 0xc2, 0x95, 0x1b, 0x48, 0x30, 0x26, 0x46, 0x93, 0x8a, 0x1b, 0x37, 0x4d, 0xe9,
+	0x0c, 0x61, 0xc2, 0xd0, 0x19, 0x3b, 0x97, 0xc6, 0x7e, 0x04, 0xbf, 0xb5, 0x99, 0x29, 0x35, 0x9a,
+	0xb0, 0x76, 0xd7, 0x73, 0xce, 0xdc, 0xfe, 0x4e, 0x6f, 0x07, 0x00, 0x63, 0xb3, 0xb9, 0xd2, 0x99,
+	0x42, 0x45, 0x6a, 0xf9, 0x24, 0xb8, 0x85, 0xd3, 0x3b, 0x8e, 0x8b, 0xd8, 0x6c, 0xa6, 0xc5, 0xb3,
+	0xda, 0x65, 0x09, 0x9f, 0x29, 0xc6, 0x43, 0xfe, 0xb6, 0xe3, 0x06, 0xc9, 0x05, 0x74, 0x8c, 0x33,
+	0xa3, 0x44, 0x31, 0x4e, 0xbd, 0x81, 0x37, 0xf4, 0x43, 0x30, 0x5f, 0xe7, 0x82, 0x1b, 0xa0, 0x07,
+	0xc7, 0xb5, 0x2c, 0xc8, 0x19, 0xf8, 0x16, 0x16, 0x49, 0x61, 0x90, 0x7a, 0x83, 0xfa, 0xb0, 0x11,
+	0xb6, 0xad, 0xf1, 0x20, 0x0c, 0x06, 0x1f, 0x1e, 0xd0, 0x17, 0xcd, 0x62, 0xe4, 0x4f, 0xab, 0x95,
+	0x14, 0x29, 0xb7, 0xef, 0xa8, 0xb0, 0xc7, 0xd0, 0x72, 0x93, 0x82, 0x39, 0x64, 0x23, 0x6c, 0x5a,
+	0x79, 0xcf, 0xc8, 0x11, 0x34, 0x50, 0x61, 0x2c, 0x69, 0xcd, 0xd9, 0xa5, 0xb0, 0x20, 0x61, 0xa2,
+	0x95, 0x48, 0x85, 0x59, 0xd3, 0xba, 0x4b, 0xda, 0xc2, 0xcc, 0x9d, 0x26, 0x97, 0xf0, 0xb7, 0x4c,
+	0xa2, 0x44, 0xed, 0x52, 0xa4, 0x7f, 0x5c, 0xde, 0x29, 0xbd, 0x99, 0xb5, 0x02, 0x0a, 0xfd, 0x03,
+	0x55, 0xb4, 0x2c, 0x82, 0x77, 0x20, 0xb3, 0x35, 0x4f, 0x36, 0x73, 0x95, 0x6d, 0x63, 0xac, 0xea,
+	0x75, 0xa1, 0x9e, 0x8b, 0x74, 0xbf, 0x0d, 0xfb, 0x48, 0x4e, 0xa0, 0xad, 0x65, 0x8c, 0x3c, 0x4a,
+	0x95, 0xab, 0xe6, 0x87, 0x2d, 0xa7, 0x1f, 0x15, 0x39, 0x07, 0x28, 0x23, 0x2c, 0x34, 0x77, 0xed,
+	0xfc, 0xd0, 0x77, 0xce, 0xa2, 0xd0, 0x9c, 0xf4, 0xa1, 0x29, 0x58, 0xc2, 0xb3, 0xb2, 0x98, 0x1f,
+	0xee, 0x55, 0x90, 0x43, 0xf7, 0x07, 0xd9, 0x2e, 0xf4, 0x37, 0xb8, 0x23, 0xe8, 0xcd, 0x05, 0x97,
+	0xcc, 0xc1, 0xab, 0x0f, 0xa6, 0xd0, 0x4a, 0x54, 0x8a, 0x3c, 0xc5, 0x3d, 0xbc, 0x92, 0x41, 0x0f,
+	0xfe, 0x7f, 0x3f, 0xae, 0x65, 0x31, 0xed, 0xbe, 0xfe, 0x8b, 0xd9, 0x76, 0x64, 0xff, 0xd8, 0x58,
+	0x2f, 0xc7, 0xf9, 0x64, 0xd9, 0x74, 0xd7, 0xed, 0xfa, 0x33, 0x00, 0x00, 0xff, 0xff, 0xca, 0x26,
+	0x9b, 0x59, 0x7c, 0x02, 0x00, 0x00,
+}

+ 49 - 0
pb/v1/task.proto

@@ -0,0 +1,49 @@
+syntax = "proto3";
+
+// package声明符,用来防止不同的消息类型有命名冲突
+package v1;
+// 用于生成指定语言go的包名称
+option go_package = "adm-task/pb/v1";
+
+// The request message containing the user's name.
+message GetTaskBySourceCodeRequest {
+    string source_code = 1;
+}
+
+// The response message containing the greetings
+message GetTaskBySourceCodeReply {
+    repeated int32 task_list = 1;
+}
+
+message UpdateOfflineTaskRequest{
+    int32 task_id = 1;
+    int32 total = 2;
+    int32 is_finish = 3;
+    int32 finish_count = 4;
+}
+
+message UpdateOfflineTaskReply{
+
+}
+
+message CheckFormatRequest{
+    string vin = 1;
+    string plate_no=2;
+    string plate_type=3;
+    string idcert = 4;
+}
+
+message CheckFormatReply{
+    string vin = 1;
+    string plate_no=2;
+    string plate_type=3;
+    string idcert = 4;
+}
+
+message FieldCheckRequest{
+    string content = 1;
+}
+
+message FieldCheckReply{
+}
+

+ 76 - 0
protogen.sh

@@ -0,0 +1,76 @@
+#!/bin/bash
+
+curentdirname=$(basename "$PWD")
+parentfulldir=$(dirname "$PWD")
+gomodfile=go.mod 
+pbdir=pb # 指定proto所在目录
+
+
+# 判断是否有go.mod文件
+if [ ! -f "$gomodfile" ]; then
+    echo "No '$gomodfile' file!"
+    exit 1
+fi
+
+# 从go.mod中取出工程包名
+packname=`awk 'NR==1{print $2}' $gomodfile`
+
+# 判断当前目录名是否与go.mod中的module 名称相同,不同直接提示错误
+if [ "$packname" != "$curentdirname" ]; then
+    echo "The module name is not the current directory! See 'go.mod'"
+    exit 1
+fi
+
+# 判断是否有pb目录
+if [ ! -d $pbdir ]; then
+    echo "No '$pbdir' directory!"
+    exit 1
+fi
+
+# 取出操作系统
+OS=`uname -s`
+
+# 将pbdir目录下所有一级目录下的所有.proto文件作统一处理并生成.pb.go文件
+dir=$(ls -l $pbdir |awk '/^d/ {print $NF}')
+for i in $dir
+do
+    cd $pbdir/$i
+    # 判断是否有.proto文件
+    num=$(ls *.proto 2> /dev/null | wc -l)
+    if [ $num != 0 ] ; then
+        if [ $OS == "Linux" ]; then
+            # 替换以 package 开头的整行
+            sed -i "s/^package.*$/package $i;/g" *.proto
+            # 替换以 option go_package 开头的整行
+            sed -i "s/^option go_package.*$/option go_package = \"$packname\/$pbdir\/$i\";/g" *.proto
+        elif [ $OS == "Darwin" ]; then
+            # 替换以 package 开头的整行
+            sed -i '' "s/^package.*$/package $i;/g" *.proto
+            # 替换以 option go_package 开头的整行
+            sed -i '' "s/^option go_package.*$/option go_package = \"$packname\/$pbdir\/$i\";/g" *.proto
+        else
+            echo "Unsupport OS: "$OS
+        fi
+
+        protoc --go_out=$parentfulldir *.proto
+    fi
+    cd ../..
+done
+
+cd $pbdir
+# 判断是否有.proto文件
+num=$(ls *.proto 2> /dev/null | wc -l)
+if [ $num != 0 ] ; then
+    if [ $OS == "Linux" ]; then
+        # 替换以 option go_package 开头的整行
+        sed -i "s/^option go_package.*$/option go_package = \"$packname\/$pbdir\";/g" *.proto
+    elif [ $OS == "Darwin" ]; then
+        # 替换以 option go_package 开头的整行
+        sed -i '' "s/^option go_package.*$/option go_package = \"$packname\/$pbdir\";/g" *.proto
+    else
+        echo "Unsupport OS: "$OS
+    fi
+
+    protoc --go_out=plugins=grpc:$parentfulldir *.proto
+fi
+cd ../

+ 87 - 0
rpmbuild.sh

@@ -0,0 +1,87 @@
+#!/bin/bash
+
+set -e
+
+show_usage="args: [--serve-addr=,--etcd-addrs=]"
+
+#-o或--options选项后面是可接受的短选项,如ab:c::,表示可接受的短选项为-a -b -c,
+#其中-a选项不接参数,-b选项后必须接参数,-c选项的参数为可选的
+#-l或--long选项后面是可接受的长选项,用逗号分开,冒号的意义同短选项。
+#-n选项后接选项解析错误时提示的脚本名字
+ARGS=`getopt -o -h --long help,serve-addr:,etcd-addrs: -n "$0" -- "$@"`
+
+if [ $? != 0 ]; then
+    exit 1
+fi
+
+#将规范化后的命令行参数分配至位置参数($1,$2,...)
+eval set -- "${ARGS}"
+
+while [ -n "$1" ]
+do
+    case "$1" in
+        -h|--help)
+            echo "$show_usage"; exit 1 ;;
+        --etcd-addrs)
+            ETCD_ADDRS=$2; shift ;;
+        --serve-addr)
+            SERVE_ADDR=$2; shift ;;
+        *) shift ;;
+    esac
+done
+
+if [ -z $ETCD_ADDRS ];then
+    ETCD_ADDRS="127.0.0.1:2379"
+fi
+
+if [ -z $SERVE_ADDR ];then
+    SERVE_ADDR="0.0.0.0:7004"
+fi
+
+PROJECT_PATH=$(cd `dirname $0`; pwd)
+SERVICE_NAME="${PROJECT_PATH##*/}"
+APP_NAME="${PROJECT_PATH##*/}"
+APP_CONF=conf/app.conf
+DISCOVERY_TYPE="etcd"
+
+#生成配置文件
+sed -e "s/%APP_NAME%/$APP_NAME/g" ${APP_CONF}.in > ${APP_CONF}
+sed -i "s/%ETCD_ADDRS%/$ETCD_ADDRS/g" ${APP_CONF}
+sed -i "s/%DISCOVERY_TYPE%/$DISCOVERY_TYPE/g" ${APP_CONF}
+sed -i "s/%SERVE_ADDR%/$SERVE_ADDR/g" ${APP_CONF}
+
+SPEC_FILE=app.spec
+APP_SERVICE=conf/app.service
+SH_APP=sh/app
+TOOLS_ROOT=`pwd`
+BUILD="${TOOLS_ROOT}/build"
+
+sed -e "s/%APP_NAME%/$APP_NAME/g" ${APP_SERVICE}.in > conf/${SERVICE_NAME}.service
+sed -e "s/%APP_NAME%/$APP_NAME/g" ${SH_APP}.in > ${SH_APP}
+sed -i "s/%SERVICE_NAME%/$SERVICE_NAME/g" ${SH_APP}
+
+VERSION=`cat VERSION`
+make version=$VERSION
+
+mkdir -p $BUILD/buildroot/{BUILD,RPMS,S{RPMS,PECS,OURCES}}
+rm -f $BUILD/buildroot/SOURCES/*.tgz
+
+set +e
+tar czf $BUILD/buildroot/SOURCES/${SERVICE_NAME}-$VERSION.tgz   * .[!.]*
+exitcode=$?
+
+if [ "$exitcode" != "1" ] && [ "$exitcode" != "0" ]; then
+   exit $exitcode
+fi
+set -e
+
+sed -e "s/%VERSION%/$VERSION/g" ${SPEC_FILE}.in > ${SPEC_FILE}
+sed -i "s/%APP_NAME%/$APP_NAME/g" ${SPEC_FILE}
+sed -i "s/%SERVICE_NAME%/$SERVICE_NAME/g" ${SPEC_FILE}
+rpmbuild  -bb ${SPEC_FILE} --define "_topdir $BUILD/buildroot/"
+
+rm -rf ./dist
+mkdir ./dist
+cp -rf $BUILD/buildroot/RPMS/* ./dist
+
+rm -rf $BUILD

+ 92 - 0
sh/app.in

@@ -0,0 +1,92 @@
+#!/bin/bash
+
+# source function library
+. /etc/%APP_NAME%/lib/start-stop-functions
+. /etc/%APP_NAME%/lib/init-multi-mode
+
+
+# Check for missing binaries (stale symlinks should not happen)
+# Note: Special treatment of stop for LSB conformance
+APP_BIN=/opt/%APP_NAME%/bin/%APP_NAME%
+test -x $APP_BIN || { echo "$APP_BIN not installed"; 
+   if [ "$1" = "stop" ]; then exit 0;
+   else exit 5; fi; }
+
+if [ "${MULTI_MODE}" = "YES" -o "${MULTI_MODE}" = "yes" ]; then
+   init_multi_mode $1 $2
+   exit $?
+fi
+
+
+PIDFILE=/var/run/%APP_NAME%.pid
+DAEMON_ARGS="pidFile=$PIDFILE"
+RETVAL=0
+# Return values acc. to LSB for all commands but status:
+# 0       - success
+# 1       - generic or unspecified error
+# 2       - invalid or excess argument(s)
+# 3       - unimplemented feature (e.g. "reload")
+# 4       - user had insufficient privileges
+# 5       - program is not installed
+# 6       - program is not configured
+# 7       - program is not running
+# 8--199  - reserved (8--99 LSB, 100--149 distrib, 150--199 appl)
+# 
+# Note that starting an already running service, stopping
+# or restarting a not-running service as well as the restart
+# with force-reload (in case signaling is not supported) are
+# considered a success.
+
+case "$1" in
+   start)
+
+      echo -n "Starting %APP_NAME%: "
+      ## create subfolder for lock files, on Debian systems needed
+      mkdir -p /var/lock/subsys
+
+      ## Start daemon with startproc(8). If this fails
+      ## the return value is set appropriately by startproc.
+      daemon --pidfile $PIDFILE $APP_BIN &
+      RETVAL=$?
+      echo
+      ;;
+   stop)
+      echo -n "Shutting down %APP_NAME%: "
+      ## Stop daemon with killproc(8) and if this fails
+      ## killproc sets the return value according to LSB.
+
+      killproc -p $PIDFILE $APP_BIN && rm -f /var/lock/subsys/%APP_NAME%
+      RETVAL=$?
+      echo
+      ;;
+   restart)
+      ## Stop the service and regardless of whether it was
+      ## running or not, start it again.
+      $0 stop
+      $0 start
+      RETVAL=$?
+      ;;
+   status)
+      echo -n "Checking for service %APP_NAME%: "
+      ## Check status with checkproc(8), if process is running
+      ## checkproc will return with exit status 0.
+
+      # Return value is slightly different for the status command:
+      # 0 - service up and running
+      # 1 - service dead, but /var/run/  pid  file exists
+      # 2 - service dead, but /var/lock/ lock file exists
+      # 3 - service not running (unused)
+      # 4 - service status unknown :-(
+      # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.)
+
+      status -p $PIDFILE $APP_BIN
+      RETVAL=$?
+      echo
+      ;;
+   *)
+      echo "Usage: $0 {start|stop|status|restart}"
+      exit 3
+      ;;
+   esac
+exit $RETVAL
+

+ 152 - 0
sh/init-multi-mode

@@ -0,0 +1,152 @@
+#!/bin/sh
+
+function init_multi_mode()
+{
+   ACTION=$1
+   CONFIG=$2
+   ERROR=0
+
+   if [ -z $ACTION ]
+   then
+      echo "Usage: $0 {start|stop|status|restart} [CONFIGURATION_NAME]"
+      exit 3
+   fi
+
+   if [ -z $CONFIG ]
+   then
+      for CONFIG_FOLDER in $( ls -d /etc/beegfs/*.d )
+      do
+         if [ -r ${CONFIG_FOLDER}/${SERVICE_NAME}.conf ]
+         then
+            CONFIG=$( basename $CONFIG_FOLDER .d )
+            init_multi_mode_single_configuration $ACTION $CONFIG
+
+            if [ $? -ne 0 ]
+            then
+               ERROR=1
+            fi
+         fi
+      done
+   else
+      if [ -e "/etc/beegfs/${CONFIG}.d" ]
+      then
+         init_multi_mode_single_configuration $ACTION $CONFIG
+         ERROR=$?
+      else
+         echo "Configuration folder /etc/beegfs/${CONFIG}.d doesn't exist."
+         ERROR=1
+      fi
+   fi
+   return $ERROR
+}
+
+function init_multi_mode_single_configuration()
+{
+   ACTION=$1
+   CONFIG=$2
+
+   if [ -z $ACTION ]
+   then
+      echo "Usage: $0 {start|stop|status|restart} [CONFIGURATION_NAME]"
+      exit 3
+   fi
+
+   # Check for existence of needed config file and read it
+   APP_CONFIG=/etc/beegfs/${CONFIG}.d/${SERVICE_NAME}.conf
+   test -r $APP_CONFIG || { echo "$APP_CONFIG not existing";
+      if [ "$1" = "stop" ]; then exit 0;
+      else exit 6; fi; }
+
+   PIDFILE=/var/run/${SERVICE_NAME}-${CONFIG}.pid
+   DAEMON_ARGS="cfgFile=${APP_CONFIG} pidFile=$PIDFILE"
+
+   RETVAL=0
+   # Return values acc. to LSB for all commands but status:
+   # 0	  - success
+   # 1       - generic or unspecified error
+   # 2       - invalid or excess argument(s)
+   # 3       - unimplemented feature (e.g. "reload")
+   # 4       - user had insufficient privileges
+   # 5       - program is not installed
+   # 6       - program is not configured
+   # 7       - program is not running
+   # 8--199  - reserved (8--99 LSB, 100--149 distrib, 150--199 appl)
+   # 
+   # Note that starting an already running service, stopping
+   # or restarting a not-running service as well as the restart
+   # with force-reload (in case signaling is not supported) are
+   # considered a success.
+
+   case "$ACTION" in
+      start)
+         if [ -f "${SYSCONFIG_FILE}" ]; then
+            if [ "${START_SERVICE}" = "NO" -o "${START_SERVICE}" = "no" ]; then
+               echo "${APP_NAME} not set to be started"
+               exit 0
+            fi
+         fi
+
+         echo -n "Starting ${APP_NAME} (${CONFIG}): "
+         ## create subfolder for lock files, on Debian systems needed
+         mkdir -p /var/lock/subsys
+
+         ## Start daemon with startproc(8). If this fails
+         ## the return value is set appropriately by startproc.
+         daemon --pidfile $PIDFILE $APP_BIN $DAEMON_ARGS \
+            && touch /var/lock/subsys/${SERVICE_NAME}-${CONFIG}
+         RETVAL=$?
+
+         if [ ! -e /var/lock/subsys/${SERVICE_NAME} ]
+         then
+            touch /var/lock/subsys/${SERVICE_NAME}
+         fi
+         echo
+         ;;
+      stop)
+         echo -n "Shutting down ${APP_NAME} (${CONFIG}): "
+         ## Stop daemon with killproc(8) and if this fails
+         ## killproc sets the return value according to LSB.
+
+         killproc -p $PIDFILE $APP_BIN && rm -f /var/lock/subsys/${SERVICE_NAME}-${CONFIG}
+         RETVAL=$?
+
+         count=$(find /var/lock/subsys/ -name ${SERVICE_NAME}-* | wc -l)
+         if [ $count -eq 0 ]
+         then
+            rm -f /var/lock/subsys/${SERVICE_NAME}
+         fi
+         echo
+         ;;
+      restart)
+         ## Stop the service and regardless of whether it was
+         ## running or not, start it again.
+         $0 stop $CONFIG
+         $0 start $CONFIG
+         RETVAL=$?
+         # Remember status and be quiet
+         ;;
+      status)
+         echo -n "Checking for service ${APP_NAME} (${CONFIG}): "
+         ## Check status with checkproc(8), if process is running
+         ## checkproc will return with exit status 0.
+
+         # Return value is slightly different for the status command:
+         # 0 - service up and running
+         # 1 - service dead, but /var/run/  pid  file exists
+         # 2 - service dead, but /var/lock/ lock file exists
+         # 3 - service not running (unused)
+         # 4 - service status unknown :-(
+         # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.)
+
+         status_multi -p $PIDFILE $APP_BIN
+         RETVAL=$?
+         echo
+         ;;
+      *)
+         echo "Usage: $0 {start|stop|status|restart} [CONFIGURATION_NAME]"
+         exit 3
+         ;;
+      esac
+   return $RETVAL
+}
+

+ 682 - 0
sh/start-stop-functions

@@ -0,0 +1,682 @@
+#!/bin/bash
+
+# The original version of this file was taken from the CentOS5 
+# initscripts-8.45.30-2.el5.x86_64 package and as this package is licensensed
+# under GPLv2 and GPLv2+ this entire file is provided under those licenses.
+
+# As the CentOS / RedHat version has severe bugs (see for example
+# https://bugzilla.redhat.com/show_bug.cgi?id=703426
+# we (the BeeGFS team) cannot rely on it and modified the original
+# version therefore
+#
+# 2011/05/10 Bernd Schubert <bernd.schubert@itwm.fraunhofer.de>
+
+# -*-Shell-script-*-
+#
+# functions	This file contains functions to be used by most or all
+#		shell scripts in the /etc/init.d directory.
+#
+
+TEXTDOMAIN=initscripts
+
+# Make sure umask is sane
+umask 022
+
+# Set up a default search path.
+PATH="/sbin:/usr/sbin:/bin:/usr/bin"
+export PATH
+
+# Get a sane screen width
+[ -z "${COLUMNS:-}" ] && COLUMNS=80
+
+if [ -z "${CONSOLETYPE:-}" -a -x "/sbin/consoletype" ]; then
+	CONSOLETYPE="`/sbin/consoletype`"
+fi
+
+if [ -f /etc/sysconfig/i18n -a -z "${NOLOCALE:-}" ] ; then
+  . /etc/profile.d/lang.sh
+fi
+
+# Read in our configuration
+if [ -z "${BOOTUP:-}" ]; then
+  if [ -f /etc/sysconfig/init ]; then
+      . /etc/sysconfig/init
+  else
+    # This all seem confusing? Look in /etc/sysconfig/init,
+    # or in /usr/doc/initscripts-*/sysconfig.txt
+    BOOTUP=color
+    RES_COL=60
+    MOVE_TO_COL="echo -en \\033[${RES_COL}G"
+    SETCOLOR_SUCCESS="echo -en \\033[1;32m"
+    SETCOLOR_FAILURE="echo -en \\033[1;31m"
+    SETCOLOR_WARNING="echo -en \\033[1;33m"
+    SETCOLOR_NORMAL="echo -en \\033[0;39m"
+    LOGLEVEL=1
+  fi
+  if [ "$CONSOLETYPE" = "serial" ]; then
+      BOOTUP=serial
+      MOVE_TO_COL=
+      SETCOLOR_SUCCESS=
+      SETCOLOR_FAILURE=
+      SETCOLOR_WARNING=
+      SETCOLOR_NORMAL=
+  fi
+fi
+
+if [ "${BOOTUP:-}" != "verbose" ]; then
+   INITLOG_ARGS="-q"
+else
+   INITLOG_ARGS=
+fi
+
+# Interpret escape sequences in an fstab entry
+fstab_decode_str() {
+	fstab-decode echo "$1"
+}
+
+# Check if $pid (could be plural) are running
+checkpid() {
+	local base=$1
+	shift
+
+	local i
+	local RUNNING_PIDS=`pidof $base`
+
+	# pidof told us the daemon is not running, so we do not need to 
+	# do further check
+	[ -n "$RUNNING_PIDS" ] || return 1
+
+	for pid in $* ; do
+		[ -d "/proc/$pid" ] || continue
+
+		for rp in $RUNNING_PIDS; do
+			# check if `pidof` and provided pid to checkpid() match
+			# if so we found a running pid with the correct name
+			[ $rp -eq $pid ] && return 0
+		done
+	done
+	return 1
+}
+
+__readlink() {
+    ls -bl "$@" 2>/dev/null| awk '{ print $NF }'
+}
+
+# __umount_loop awk_program fstab_file first_msg retry_msg umount_args
+# awk_program should process fstab_file and return a list of fstab-encoded
+# paths; it doesn't have to handle comments in fstab_file.
+__umount_loop() {
+	local remaining sig=
+	local retry=3
+
+	remaining=$(LC_ALL=C awk "/^#/ {next} $1" "$2" | sort -r)
+	while [ -n "$remaining" -a "$retry" -gt 0 ]; do
+		if [ "$retry" -eq 3 ]; then
+			action "$3" fstab-decode umount $5 $remaining
+		else
+			action "$4" fstab-decode umount $5 $remaining
+		fi
+		sleep 2
+		remaining=$(LC_ALL=C awk "/^#/ {next} $1" "$2" | sort -r)
+		[ -z "$remaining" ] && break
+		fstab-decode /sbin/fuser -k -m $sig $remaining >/dev/null
+		sleep 5
+		retry=$(($retry -1))
+		sig=-9
+	done
+}
+
+# Similar to __umount loop above, specialized for loopback devices
+__umount_loopback_loop() {
+	local remaining devremaining sig=
+	local retry=3
+
+	remaining=$(awk '$1 ~ /^\/dev\/loop/ && $2 != "/" {print $2}' /proc/mounts)
+	devremaining=$(awk '$1 ~ /^\/dev\/loop/ && $2 != "/" {print $1}' /proc/mounts)
+	while [ -n "$remaining" -a "$retry" -gt 0 ]; do
+		if [ "$retry" -eq 3 ]; then
+			action $"Unmounting loopback filesystems: " \
+				fstab-decode umount $remaining
+		else
+			action $"Unmounting loopback filesystems (retry):" \
+				fstab-decode umount $remaining
+		fi
+		for dev in $devremaining ; do
+			losetup $dev > /dev/null 2>&1 && \
+				action $"Detaching loopback device $dev: " \
+				losetup -d $dev
+		done
+		remaining=$(awk '$1 ~ /^\/dev\/loop/ && $2 != "/" {print $2}' /proc/mounts)
+		devremaining=$(awk '$1 ~ /^\/dev\/loop/ && $2 != "/" {print $1}' /proc/mounts)
+		[ -z "$remaining" ] && break
+		fstab-decode /sbin/fuser -k -m $sig $remaining >/dev/null
+		sleep 5
+		retry=$(($retry -1))
+		sig=-9
+	done
+}
+
+# __proc_pids {program} [pidfile]
+# Set $pid to pids from /var/run* for {program}.  $pid should be declared
+# local in the caller.
+# Returns LSB exit code for the 'status' action.
+__pids_var_run_running() {
+	local base=${1##*/}
+	local pid_file=${2:-/var/run/$base.pid}
+
+	# checkpid already sets $pid globally, so we can't re-use it for now
+	local pids=
+	if [ -f "$pid_file" ]; then
+		local line=`cat $pid_file | tr '\n' ' '`
+		for entry in $line; do
+			checkpid $base $entry
+			[ $? -eq 0 ] || continue
+			
+			# found a matching pid
+			pids="$pids $entry"			
+		done < "$pid_file"
+
+	        if [ -n "$pids" ]; then
+			pid=$pids # globally we need $pid
+	                return 0
+	        fi
+		return 1 # "Program is dead and /var/run pid file exists"
+	fi
+	return 3 # "Program is not running"
+}
+
+# Output PIDs of matching processes, found using pidof
+__pids_pidof() {
+	pidof -c -o $$ -o $PPID -o %PPID -x "$1" || \
+		pidof -c -o $$ -o $PPID -o %PPID -x "${1##*/}"
+}
+
+
+# A function to start a program.
+daemon() {
+	# Test syntax.
+	local gotbase= force= nicelevel corelimit
+	local pid base= user= nice= bg= pid_file=
+	nicelevel=0
+	while [ "$1" != "${1##[-+]}" ]; do
+	  case $1 in
+	    '')    echo $"$0: Usage: daemon [+/-nicelevel] {program}"
+	           return 1;;
+	    --check)
+		   base=$2
+		   gotbase="yes"
+		   shift 2
+		   ;;
+	    --check=?*)
+	    	   base=${1#--check=}
+		   gotbase="yes"
+		   shift
+		   ;;
+	    --user)
+		   user=$2
+		   shift 2
+		   ;;
+	    --user=?*)
+	           user=${1#--user=}
+		   shift
+		   ;;
+	    --pidfile)
+		   pid_file=$2
+		   shift 2
+		   ;;
+	    --pidfile=?*)
+		   pid_file=${1#--pidfile=}
+		   shift
+		   ;;
+	    --force)
+	    	   force="force"
+		   shift
+		   ;;
+	    [-+][0-9]*)
+	    	   nice="nice -n $1"
+	           shift
+		   ;;
+	    *)     echo $"$0: Usage: daemon [+/-nicelevel] {program}"
+	           return 1;;
+	  esac
+	done
+
+        # Save basename.
+        [ -z "$gotbase" ] && base=${1##*/}
+
+        # See if it's already running. Look *only* at the pid file.
+	__pids_var_run_running "$base" "$pid_file"
+
+	[ -n "$pid" -a -z "$force" ] && return
+
+	# make sure it doesn't core dump anywhere unless requested
+	corelimit="ulimit -S -c ${DAEMON_COREFILE_LIMIT:-0}"
+	
+	# if they set NICELEVEL in /etc/sysconfig/foo, honor it
+	[ -n "${NICELEVEL:-}" ] && nice="nice -n $NICELEVEL"
+	
+	# Echo daemon
+        [ "${BOOTUP:-}" = "verbose" -a -z "${LSB:-}" ] && echo -n " $base"
+
+	# And start it up.
+	if [ -z "$user" ]; then
+	   $nice /bin/bash -c "$corelimit >/dev/null 2>&1 ; $*"
+	else
+	   $nice runuser -s /bin/bash - $user -c "$corelimit >/dev/null 2>&1 ; $*"
+	fi
+	[ "$?" -eq 0 ] && success $"$base startup" || failure $"$base startup"
+}
+
+# reall kill here, return 0 if kill succeeded, 1 if not
+do_killproc()
+{
+	level=$1
+	time=$2
+
+	kill -${level} $pid >/dev/null 2>&1
+	RC=0
+	count_time=0
+	while [ $RC -eq 0 -a $count_time -lt $time ]; do
+		checkpid $base $pid
+		if [ $? -ne 0 ]; then
+			return 0
+		fi
+		sleep 1
+		count=$(($count_time + 1))
+	done
+
+	return 1
+}
+
+remove_pidfile()
+{
+        # Remove pid file if any.
+	if [ -z "$killlevel" ]; then
+            rm -f "${pid_file:-/var/run/$base.pid}"
+	fi
+}
+
+# A function to stop a program.
+killproc() {
+	local RC killlevel= base pid pid_file= delay
+	local pidfile_only=0
+
+	RC=0; delay=3
+	# Test syntax.
+	if [ "$#" -eq 0 ]; then
+		echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]"
+		return 1
+	fi
+	if [ "$1" = "-p" ]; then
+		pid_file=$2
+		shift 2
+	fi
+	if [ "$1" = "-d" ]; then
+		delay=$2
+		shift 2
+	fi
+	
+        
+	# check for second arg to be kill level
+	[ -n "${2:-}" ] && killlevel=$2
+
+        # Save basename.
+        base=${1##*/}
+
+        # Find pid.
+	__pids_var_run_running "$1" "$pid_file"
+	if [ -z "$pid" ]; then
+		pid="$(__pids_pidof "$1")"
+	fi
+
+	if [ -z "$pid" ]; then
+		if [ -n "${LSB:-}" -a -n "$killlevel" ]; then
+			RC=7 # Program is not running
+		else
+			failure $"$base shutdown"
+			RC=0
+		fi
+
+		remove_pidfile
+		return $RC
+	fi
+
+        # Kill it.
+	[ "$BOOTUP" = "verbose" -a -z "${LSB:-}" ] && echo -n "$base "
+	if [ -z "$killlevel" ] ; then
+		# TERM first, then KILL if not dead
+		# use specified level only
+		do_killproc Kill 30
+		[ $? -ne 0 ] || do_killproc KILL 5
+
+	else
+               	do_killproc $killlevel 30 >/dev/null 2>&1
+	fi
+
+	# determine the exit code here after kill (depends on if kill succeeded)
+	checkpid $base $pid
+	RC=$?
+	[ "$RC" -eq 0 ] && failure $"$base shutdown" || success $"$base shutdown"
+	RC=$((! $RC))
+
+	remove_pidfile
+	return $RC
+}
+
+# A function to find the pid of a program. Looks *only* at the pidfile
+pidfileofproc() {
+	local pid
+
+	# Test syntax.
+	if [ "$#" = 0 ] ; then
+		echo $"Usage: pidfileofproc {program}"
+		return 1
+	fi
+
+	__pids_var_run_running "$1"
+	[ -n "$pid" ] && echo $pid
+	return 0
+}
+
+# A function to find the pid of a program.
+pidofproc() {
+	local RC pid pid_file=
+
+	# Test syntax.
+	if [ "$#" = 0 ]; then
+		echo $"Usage: pidofproc [-p pidfile] {program}"
+		return 1
+	fi
+	if [ "$1" = "-p" ]; then
+		pid_file=$2
+		shift 2
+	fi
+	fail_code=3 # "Program is not running"
+
+	# First try "/var/run/*.pid" files
+	__pids_var_run_running "$1" "$pid_file"
+	RC=$?
+	if [ -n "$pid" ]; then
+		echo $pid
+		return 0
+	fi
+
+	[ -n "$pid_file" ] && return $RC
+	__pids_pidof "$1" || return $RC
+}
+
+status() {
+	local base pid pid_file=
+
+	# Test syntax.
+	if [ "$#" = 0 ] ; then
+		echo $"Usage: status [-p pidfile] {program}"
+		return 1
+	fi
+	if [ "$1" = "-p" ]; then
+		pid_file=$2
+		shift 2
+	fi
+	base=${1##*/}
+
+	# First try "pidof"
+	__pids_var_run_running "$1" "$pid_file"
+	RC=$?
+	if [ -z "$pid_file" -a -z "$pid" ]; then
+		pid="$(__pids_pidof "$1")"
+	fi
+	if [ -n "$pid" ]; then
+	        echo $"${base} (pid $pid) is running..."
+	        return 0
+	fi
+
+	case "$RC" in
+		0)
+			echo $"${base} (pid $pid) is running..."
+			return 0
+			;;
+		1)
+	                echo $"${base} dead but pid file exists"
+	                return 1
+			;;
+	esac
+	# See if /var/lock/subsys/${base} exists
+	if [ -f /var/lock/subsys/${base} ]; then
+		echo $"${base} dead but subsys locked"
+		return 2
+	fi
+	echo $"${base} is stopped"
+	return 3
+}
+
+status_multi() {
+	local base pid pid_file=
+
+	# Test syntax.
+	if [ "$#" = 0 ] ; then
+		echo $"Usage: status [-p pidfile] {program}"
+		return 1
+	fi
+	if [ "$1" = "-p" ]; then
+		pid_file=$2
+		shift 2
+	fi
+	base=${1##*/}
+
+	# First try "pidof"
+	__pids_var_run_running "$1" "$pid_file"
+	RC=$?
+	if [ -z "$pid_file" -a -z "$pid" ]; then
+		pid="$(__pids_pidof "$1")"
+	fi
+	if [ -n "$pid" ]; then
+	        echo $"${base} (pid $pid) is running..."
+	        return 0
+	fi
+
+	case "$RC" in
+		0)
+			echo $"${base} (pid $pid) is running..."
+			return 0
+			;;
+		1)
+	                echo $"${base} dead but pid file exists"
+	                return 1
+			;;
+	esac
+	# See if /var/lock/subsys/${base}-$CONFIG exists
+	if [ -f /var/lock/subsys/${base}-$CONFIG ]; then
+		echo $"${base} dead but subsys locked"
+		return 2
+	fi
+	echo $"${base} is stopped"
+	return 3
+}
+
+echo_success() {
+  [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
+  echo -n "["
+  [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS
+  echo -n $"  OK  "
+  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+  echo -n "]"
+  echo -ne "\r"
+  return 0
+}
+
+echo_failure() {
+  [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
+  echo -n "["
+  [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE
+  echo -n $"FAILED"
+  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+  echo -n "]"
+  echo -ne "\r"
+  return 1
+}
+
+echo_passed() {
+  [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
+  echo -n "["
+  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+  echo -n $"PASSED"
+  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+  echo -n "]"
+  echo -ne "\r"
+  return 1
+}
+
+echo_warning() {
+  [ "$BOOTUP" = "color" ] && $MOVE_TO_COL
+  echo -n "["
+  [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING
+  echo -n $"WARNING"
+  [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL
+  echo -n "]"
+  echo -ne "\r"
+  return 1
+}
+
+# Inform the graphical boot of our current state
+update_boot_stage() {
+  if [ "$GRAPHICAL" = "yes" -a -x /usr/bin/rhgb-client ]; then
+    /usr/bin/rhgb-client --update="$1"
+  fi
+  return 0
+}
+
+# Log that something succeeded
+success() {
+  #if [ -z "${IN_INITLOG:-}" ]; then
+  #   initlog $INITLOG_ARGS -n $0 -s "$1" -e 1
+  #fi
+  [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_success
+  return 0
+}
+
+# Log that something failed
+failure() {
+  local rc=$?
+  #if [ -z "${IN_INITLOG:-}" ]; then
+  #   initlog $INITLOG_ARGS -n $0 -s "$1" -e 2
+  #fi
+  [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_failure
+  [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
+  return $rc
+}
+
+# Log that something passed, but may have had errors. Useful for fsck
+passed() {
+  local rc=$?
+  #if [ -z "${IN_INITLOG:-}" ]; then
+  #   initlog $INITLOG_ARGS -n $0 -s "$1" -e 1
+  #fi
+  [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_passed
+  return $rc
+}  
+
+# Log a warning
+warning() {
+  local rc=$?
+  #if [ -z "${IN_INITLOG:-}" ]; then
+  #   initlog $INITLOG_ARGS -n $0 -s "$1" -e 1
+  #fi
+  [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_warning
+  return $rc
+}  
+
+# Run some action. Log its output.
+action() {
+  local STRING rc
+
+  STRING=$1
+  echo -n "$STRING "
+  if [ "${RHGB_STARTED:-}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
+      echo -n "$STRING " > /etc/rhgb/temp/rhgb-console
+  fi
+  shift
+  "$@" && success $"$STRING" || failure $"$STRING"
+  rc=$?
+  echo
+  if [ "${RHGB_STARTED:-}" != "" -a -w /etc/rhgb/temp/rhgb-console ]; then
+      if [ "$rc" = "0" ]; then
+      	echo_success > /etc/rhgb/temp/rhgb-console
+      else
+        echo_failure > /etc/rhgb/temp/rhgb-console
+	[ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
+      fi
+      echo > /etc/rhgb/temp/rhgb-console
+  fi
+  return $rc
+}
+
+# returns OK if $1 contains $2
+strstr() {
+  [ "${1#*$2*}" = "$1" ] && return 1
+  return 0
+}
+
+# Confirm whether we really want to run this service
+confirm() {
+  [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=yes
+  while : ; do 
+      echo -n $"Start service $1 (Y)es/(N)o/(C)ontinue? [Y] "
+      read answer
+      if strstr $"yY" "$answer" || [ "$answer" = "" ] ; then
+         return 0
+      elif strstr $"cC" "$answer" ; then
+	 rm -f /var/run/confirm
+	 [ -x /usr/bin/rhgb-client ] && /usr/bin/rhgb-client --details=no
+         return 2
+      elif strstr $"nN" "$answer" ; then
+         return 1
+      fi
+  done
+}
+
+# resolve a device node to its major:minor numbers in decimal or hex
+get_numeric_dev() {
+(
+    fmt="%d:%d"
+    if [ "$1" == "hex" ]; then
+        fmt="%x:%x"
+    fi
+    ls -lH "$2" | awk '{ sub(/,/, "", $5); printf("'"$fmt"'", $5, $6); }'
+) 2>/dev/null
+}
+
+# find the working name for a running dm device with the same table as one
+# that dmraid would create
+resolve_dm_name() {
+(
+    name="$1"
+
+    line=$(/sbin/dmraid -ay -t --ignorelocking | \
+        egrep -iv "no block devices found|No RAID disks" | \
+        awk -F ':' "{ if (\$1 ~ /^$name$/) { print \$2; }}")
+    for x in $line ; do
+        if [[ "$x" =~ "^/dev/" ]] ; then
+            majmin=$(get_numeric_dev dec $x)
+            line=$(echo "$line" | sed -e "s,$x\( \|$\),$majmin\1,g")
+        fi
+    done
+    line=$(echo "$line" | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//' \
+               -e 's/ core [12] [[:digit:]]\+ / core [12] [[:digit:]]\\+ /')
+    /sbin/dmsetup table | \
+        sed -n -e "s/.*\(no block devices found\|No devices found\).*//" \
+               -e "s/\(^[^:]\+\): $line\( \+$\|$\)/\1/p"
+) 2>/dev/null
+}
+
+# Check whether file $1 is a backup or rpm-generated file and should be ignored
+is_ignored_file() {
+    case "$1" in
+	*~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave)
+	    return 0
+	    ;;
+    esac
+    return 1
+}
+# A sed expression to filter out the files that is_ignored_file recognizes
+__sed_discard_ignored_files='/\(~\|\.bak\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d'
+

+ 24 - 0
tests/client.go

@@ -0,0 +1,24 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package tests
+
+import (
+	"gadm-task/pb"
+	"log"
+
+	"google.golang.org/grpc"
+)
+
+var client pb.GreeterClient
+
+const ServerAddr = "127.0.0.1:10001"
+
+func init() {
+	// Set up a connection to the server.
+	conn, err := grpc.Dial(ServerAddr, grpc.WithInsecure())
+	if err != nil {
+		log.Fatalf("did not connect: %v", err)
+	}
+	client = pb.NewGreeterClient(conn)
+}

+ 24 - 0
tests/v1_test.go

@@ -0,0 +1,24 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package tests
+
+import (
+	"context"
+	v1 "gadm-task/pb/v1"
+	"testing"
+
+	jsoniter "github.com/json-iterator/go"
+)
+
+var json = jsoniter.ConfigCompatibleWithStandardLibrary
+
+func Test_HelloV1(t *testing.T) {
+	r, err := client.SayHelloV1(context.Background(), &v1.HelloRequest{Name: "world"})
+	if err == nil {
+		s, _ := json.MarshalToString(r)
+		t.Log(s)
+	} else {
+		t.Error("failed to call: ", err)
+	}
+}

+ 20 - 0
tests/v2_test.go

@@ -0,0 +1,20 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package tests
+
+import (
+	"context"
+	v2 "gadm-task/pb/v2"
+	"testing"
+)
+
+func Test_HelloV2(t *testing.T) {
+	r, err := client.SayHelloV2(context.Background(), &v2.HelloRequest{Name: "world"})
+	if err == nil {
+		d, _ := json.Marshal(r)
+		t.Log(string(d))
+	} else {
+		t.Error("failed to call: ", err)
+	}
+}

+ 77 - 0
utils/field.go

@@ -0,0 +1,77 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package utils
+
+import (
+	"gadm-task/parser"
+	"encoding/json"
+	"fmt"
+	"os"
+	"sync"
+	"time"
+
+	"gitlab.sydip.com/repo/gopkgs/database"
+)
+
+var settingsMutex *sync.RWMutex
+var filedMap map[string]string
+
+// 构建字段map
+func constructFieldMap() {
+	fmt.Println("constructFieldMap:", time.Now().String())
+	var fieldList []string
+	err := database.DB().Raw("select field from t_adm_field_management").Scan(&fieldList).Error
+	if err != nil {
+		os.Exit(1)
+	}
+
+	settingsMutex.Lock()
+	defer settingsMutex.Unlock()
+	for _, v := range fieldList {
+		filedMap[v] = ""
+	}
+
+	//fmt.Println(filedMap)
+}
+
+// 加载字段
+func FieldLoad() error {
+	settingsMutex = &sync.RWMutex{}
+	filedMap = make(map[string]string)
+	constructFieldMap()
+	go FiledTimer()
+	return nil
+}
+
+func CheckField(content string) (bool, string) {
+	settingsMutex.RLock()
+	defer settingsMutex.RUnlock()
+	checkFieldMap := make(map[string]interface{})
+	err := json.Unmarshal([]byte(content), &checkFieldMap)
+	if err != nil {
+		return true, ""
+	}
+
+	for k, _ := range checkFieldMap {
+		if _, ok := filedMap[k]; !ok {
+			return false, k
+		}
+	}
+	return true, ""
+}
+
+func FiledTimer() {
+	for {
+		// TODO 从配置文件中获取
+
+		loopTime, _ := parser.Conf.FeildLoopTime.Int64()
+		fmt.Println("loop time:", loopTime)
+		if loopTime == 0 {
+			loopTime = 5 * 60
+		}
+
+		time.Sleep(time.Duration(loopTime) * time.Second)
+		constructFieldMap()
+	}
+}