[
  {
    "path": ".gitignore",
    "content": ".idea\n.vscode\ncoverage.out\nfrontApp\nlogServiceApp\nmailerServiceApp\nauthApp\nbrokerApp\nlistener\nfrontEndLinux"
  },
  {
    "path": "Caddyfile",
    "content": "{\n    email   you@gmail.com\n}\n\n(static) {\n\t@static {\n\t\tfile\n\t\tpath *.ico *.css *.js *.gif *.jpg *.jpeg *.png *.svg *.woff *.json\n\t}\n\theader @static Cache-Control max-age=5184000\n}\n\n(security) {\n\theader {\n\t\t# enable HSTS\n\t\tStrict-Transport-Security max-age=31536000;\n\t\t# disable clients from sniffing the media type\n\t\tX-Content-Type-Options nosniff\n\t\t# keep referrer data off of HTTP connections\n\t\tReferrer-Policy no-referrer-when-downgrade\n\t}\n}\n\nlocalhost:80 {\n\tencode zstd gzip\n\timport static\n\n\treverse_proxy  http://front-end:8081\n}\n\nbackend:80 {\n\treverse_proxy http://broker-service:8080\n}"
  },
  {
    "path": "LICENSE.md",
    "content": "# MIT License\n\n### Copyright (c) 2022 Trevor Sawler\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "FRONT_END_BINARY=frontApp\nLOGGER_BINARY=logServiceApp\nBROKER_BINARY=brokerApp\nAUTH_BINARY=authApp\nLISTENER_BINARY=listener\nMAIL_BINARY=mailerServiceApp\nAUTH_VERSION=1.0.0\nBROKER_VERSION=1.0.0\nLISTENER_VERSION=1.0.2\nMAIL_VERSION=1.0.0\nLOGGER_VERSION=1.0.0\n\n## up: starts all containers in the background without forcing build\nup:\n\t@echo \"Starting docker images...\"\n\tdocker-compose up -d\n\t@echo \"Docker images started!\"\n\n## down: stop docker compose\ndown:\n\t@echo \"Stopping docker images...\"\n\tdocker-compose down\n\t@echo \"Docker stopped!\"\n\n## build_dockerfiles: builds all dockerfile images\nbuild_dockerfiles: build_auth build_broker build_listener build_logger build_mail front_end_linux\n\t@echo \"Building dockerfiles...\"\n\tdocker build -f front-end.dockerfile -t tsawler/front-end .\n\tdocker build -f authentication-service.dockerfile -t tsawler/authentication:${AUTH_VERSION} .\n\tdocker build -f broker-service.dockerfile -t tsawler/broker:1.0.0 .\n\tdocker build -f listener-service.dockerfile -t tsawler/listener:1.0.2 .\n\tdocker build -f mail-service.dockerfile -t tsawler/mail:1.0.0 .\n\tdocker build -f logger-service.dockerfile -t tsawler/logger:1.0.0 .\n\n## push_dockerfiles: pushes tagged versions to docker hub\npush_dockerfiles: build_dockerfiles\n\tdocker push tsawler/authentication:${AUTH_VERSION}\n\tdocker push tsawler/broker:${BROKER_VERSION}\n\tdocker push tsawler/listener:${LISTENER_VERSION}\n\tdocker push tsawler/mail:${MAIL_VERSION}\n\tdocker push tsawler/logger:${LOGGER_VERSION}\n\t@echo \"Done!\"\n\n## front_end_linux: builds linux executable for front end\nfront_end_linux:\n\t@echo \"Building linux version of front end...\"\n\tcd front-end && env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o frontEndLinux ./cmd/web\n\t@echo \"Done!\"\n\n## swarm_up: starts the swarm\nswarm_up:\n\t@echo \"Starting swarm...\"\n\tdocker stack deploy -c swarm.yml myapp\n\n## swarm_down: stops the swarm\nswarm_down:\n\t@echo \"Stopping swarm...\"\n\tdocker stack rm myapp\n\n## build_auth: builds the authentication binary as a linux executable\nbuild_auth:\n\t@echo \"Building authentication binary..\"\n\tcd authentication-service && env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${AUTH_BINARY} ./cmd/api\n\t@echo \"Authentication binary built!\"\n\n## build_logger: builds the logger binary as a linux executable\nbuild_logger:\n\t@echo \"Building logger binary...\"\n\tcd logger-service && env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${LOGGER_BINARY} ./cmd/web\n\t@echo \"Logger binary built!\"\n\n## build_broker: builds the broker binary as a linux executable\nbuild_broker:\n\t@echo \"Building broker binary...\"\n\tcd broker-service && env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${BROKER_BINARY} ./cmd/api\n\t@echo \"Broker binary built!\"\n\n## build_listener: builds the listener binary as a linux executable\nbuild_listener:\n\t@echo \"Building listener binary...\"\n\tcd listener-service && env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${LISTENER_BINARY} .\n\t@echo \"Listener binary built!\"\n\n## build_mail: builds the mail binary as a linux executable\nbuild_mail:\n\t@echo \"Building mailer binary...\"\n\tcd mail-service && env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${MAIL_BINARY} ./cmd/api\n\t@echo \"Mailer binary built!\"\n\n\n## up_build: stops docker-compose (if running), builds all projects and starts docker compose\nup_build: build_auth build_broker build_listener build_logger build_mail\n\t@echo \"Stopping docker images (if running...)\"\n\tdocker-compose down\n\t@echo \"Building (when required) and starting docker images...\"\n\tdocker-compose up --build -d\n\t@echo \"Docker images built and started!\"\n\n## auth: stops authentication-service, removes docker image, builds service, and starts it\nauth: build_auth\n\t@echo \"Building authentication-service docker image...\"\n\t- docker-compose stop authentication-service\n\t- docker-compose rm -f authentication-service\n\tdocker-compose up --build -d authentication-service\n\tdocker-compose start authentication-service\n\t@echo \"authentication-service built and started!\"\n\n## broker: stops broker-service, removes docker image, builds service, and starts it\nbroker: build_broker\n\t@echo \"Building broker-service docker image...\"\n\t- docker-compose stop broker-service\n\t- docker-compose rm -f broker-service\n\tdocker-compose up --build -d broker-service\n\tdocker-compose start broker-service\n\t@echo \"broker-service rebuilt and started!\"\n\n## logger: stops logger-service, removes docker image, builds service, and starts it\nlogger: build_logger\n\t@echo \"Building logger-service docker image...\"\n\t- docker-compose stop logger-service\n\t- docker-compose rm -f logger-service\n\tdocker-compose up --build -d logger-service\n\tdocker-compose start logger-service\n\t@echo \"broker-service rebuilt and started!\"\n\n## mail: stops mail-service, removes docker image, builds service, and starts it\nmail: build_mail\n\t@echo \"Building mail-service docker image...\"\n\t- docker-compose stop mail-service\n\t- docker-compose rm -f mail-service\n\tdocker-compose up --build -d mail-service\n\tdocker-compose start mail-service\n\t@echo \"mail-service rebuilt and started!\"\n\n## listener: stops listener-service, removes docker image, builds service, and starts it\nlistener: build_listener\n\t@echo \"Building listener-service docker image...\"\n\t- docker-compose stop listener-service\n\t- docker-compose rm -f listener-service\n\tdocker-compose up --build -d listener-service\n\tdocker-compose start listener-service\n\t@echo \"listener-service rebuilt and started!\"\n\n## start: starts the front end\nstart:\n\t@echo \"Starting front end\"\n\tcd front-end && go build -o ${FRONT_END_BINARY} ./cmd/web\n\tcd front-end && ./${FRONT_END_BINARY} &\n\n## stop: stop the front end\nstop:\n\t@echo \"Stopping front end...\"\n\t@-pkill -SIGTERM -f \"./${FRONT_END_BINARY}\"\n\t@echo \"Stopped front end!\"\n\n## test: runs all tests\ntest:\n\t@echo \"Testing...\"\n\tgo test -v ./...\n\n## clean: runs go clean and deletes binaries\nclean:\n\t@echo \"Cleaning...\"\n\t@cd broker-service && rm -f ${BROKER_BINARY}\n\t@cd broker-service && go clean\n\t@cd listener-service && rm -f ${LISTENER_BINARY}\n\t@cd listener-service && go clean\n\t@cd authentication-service && rm -f ${AUTH_BINARY}\n\t@cd authentication-service && go clean\n\t@cd mail-service && rm -f ${MAIL_BINARY}\n\t@cd mail-service && go clean\n\t@cd logger-service && rm -f ${LOGGER_BINARY}\n\t@cd logger-service && go clean\n\t@cd front-end && go clean\n\t@cd front-end && rm -f ${FRONT_END_BINARY}\n\t@echo \"Cleaned!\"\n\n## help: displays help\nhelp: Makefile\n\t@echo \" Choose a command:\"\n\t@sed -n 's/^##//p' $< | column -t -s ':' |  sed -e 's/^/ /'"
  },
  {
    "path": "README.md",
    "content": "[![Version](https://img.shields.io/badge/goversion-1.18.x-blue.svg)](https://golang.org)\n[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/tsawler/goblender/master/LICENSE)\n\n# Working with Microservices in Go\n\nThis is the source code for the Udemy course **Working with Microservices and Go**. This project\nconsists of a number of loosely coupled microservices, all written in Go:\n\n- broker-service: an optional single entry point to connect to all services from one place (accepts JSON;\nsends JSON, makes calls via gRPC, and pushes to RabbitMQ)\n- authentication-service: authenticates users against a Postgres database (accepts JSON)\n- logger-service: logs important events to a MongoDB database (accepts RPC, gRPC, and JSON)\n- queue-listener-service: consumes messages from amqp (RabbitMQ) and initiates actions based on payload (sends via RPC)\n- mail-service: sends email (accepts JSON)\n\nAll services (except the broker) register their access urls with etcd, and renew their leases automatically.\nThis allows us to implement a simple service discovery system, where all service URLs are accessible with\n\"service maps\" in the Config type used to share application configuration in the broker service.\n\nIn addition to the microservices, the included `docker-compose.yml` at the root level of the project\nstarts the following services:\n\n- Postgresql - used by the authentication service to store user accounts\n- MongoDB - used by the logger service to save logs from all services\n- etcd - used for service discovery\n- mailhog - used as a fake mail server to work with the mail service\n\n## Running the project\nFrom the root level of the project, execute this command (this assumes that you have \n[GNU make](https://www.gnu.org/software/make/) and a recent version\nof [Docker](https://www.docker.com/products/docker-desktop) installed on your machine):\n\n~~~\nmake up_build \n~~~\n\nIf the code has not changed, subsequent runs can just be `make up`.\n\nThen start the front end:\n\n~~~\nmake start\n~~~\n\nHit the front end with your web browser at `http://localhost:80`. You can also access a web \nfront end to the logger service by going to `http://localhost:8082` (or whatever port you\nspecify in the `docker-compose.yml file`).\n\nTo stop everything:\n\n~~~\nmake stop\nmake down\n~~~\n\nWhile working on code, you can rebuild just the service you are working on by\nexecuting\n\n`make auth`\n\nWhere `auth` is one of the services:\n\n- auth\n- broker\n- logger\n- listener\n- mail\n\nAll make commands:\n\n~~~\ntcs@Grendel go-microservices % make help\n Choose a command:\n  up               starts all containers in the background without forcing build\n  down             stop docker compose\n  build_auth       builds the authentication binary as a linux executable\n  build_logger     builds the logger binary as a linux executable\n  build_broker     builds the broker binary as a linux executable\n  build_listener   builds the listener binary as a linux executable\n  build_mail       builds the mail binary as a linux executable\n  up_build         stops docker-compose (if running), builds all projects and starts docker compose\n  auth             stops authentication-service, removes docker image, builds service, and starts it\n  broker           stops broker-service, removes docker image, builds service, and starts it\n  logger           stops logger-service, removes docker image, builds service, and starts it\n  mail             stops mail-service, removes docker image, builds service, and starts it\n  listener         stops listener-service, removes docker image, builds service, and starts it\n  start            starts the front end\n  stop             stop the front end\n  test             runs all tests\n  clean            runs go clean and deletes binaries\n  help             displays help\n~~~"
  },
  {
    "path": "authentication-service/cmd/api/discover.go",
    "content": "package main\n\n//\n//// registerService registers the correct entry for this service in etcd\n//func (app *Config) registerService() {\n//\tcli, _ := connectToEtcd()\n//\tkv := clientv3.NewKV(cli)\n//\n//\tapp.Etcd = cli\n//\n//\tlease := clientv3.NewLease(cli)\n//\tgrantResp, err := lease.Grant(context.TODO(), 10)\n//\tif err != nil {\n//\t\tlog.Println(\"Error creating lease\", err)\n//\t}\n//\n//\t// insert something with the lease\n//\t_, err = kv.Put(context.TODO(), fmt.Sprintf(\"/auth/%s\", app.randomString(32)), \"authentication-service\", clientv3.WithLease(grantResp.ID))\n//\tif err != nil {\n//\t\tlog.Println(\"Error inserting using lease\", err)\n//\t}\n//\n//\t// keep lease alive\n//\tkalRes, err := lease.KeepAlive(context.TODO(), grantResp.ID)\n//\tif err != nil {\n//\t\tlog.Println(\"Error with keepalive\", err)\n//\t}\n//\tgo app.listenToKeepAlive(kalRes)\n//}\n//\n//// listenToKeepAlive just consumes channel responses from etcd's KeepAlive method\n//func (app *Config) listenToKeepAlive(kalRes <-chan *clientv3.LeaseKeepAliveResponse) {\n//\tdefer func() {\n//\t\tif r := recover(); r != nil {\n//\t\t\tlog.Println(\"Error\", fmt.Sprintf(\"%v\", r))\n//\t\t}\n//\t}()\n//\n//\t// the only reason this exists is to consume the response from etcd's KeepAlive, because\n//\t// if we don't, unexpected behaviour is the result.\n//\tfor {\n//\t\t_ = <-kalRes\n//\t}\n//}\n//\n//// connectToEtcd tries to connect to etcd, for up to 30 seconds\n//func connectToEtcd() (*clientv3.Client, error) {\n//\tvar cli *clientv3.Client\n//\tvar counts = 0\n//\n//\tfor {\n//\t\tc, err := clientv3.New(clientv3.Config{Endpoints: []string{\"etcd:2379\"},\n//\t\t\tDialTimeout: 5 * time.Second,\n//\t\t})\n//\t\tif err != nil {\n//\t\t\tfmt.Println(\"etcd not ready...\")\n//\t\t\tcounts++\n//\t\t} else {\n//\t\t\tfmt.Println()\n//\t\t\tcli = c\n//\t\t\tbreak\n//\t\t}\n//\n//\t\tif counts > 15 {\n//\t\t\treturn nil, err\n//\t\t}\n//\t\tfmt.Println(\"Backing off for 2 seconds...\")\n//\t\ttime.Sleep(2 * time.Second)\n//\t\tcontinue\n//\t}\n//\tlog.Println(\"Connected to etcd!\")\n//\treturn cli, nil\n//}\n"
  },
  {
    "path": "authentication-service/cmd/api/handlers.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n)\n\n// Authenticate accepts a json payload and attempts to authenticate a user\nfunc (app *Config) Authenticate(w http.ResponseWriter, r *http.Request) {\n\tvar requestPayload struct {\n\t\tEmail    string `json:\"email\"`\n\t\tPassword string `json:\"password\"`\n\t}\n\n\terr := app.readJSON(w, r, &requestPayload)\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// validate against database\n\tuser, err := app.Models.User.GetByEmail(requestPayload.Email)\n\tif err != nil {\n\t\t_ = app.errorJSON(w, errors.New(\"invalid credentials\"), http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\tvalid, err := user.PasswordMatches(requestPayload.Password)\n\tif err != nil || !valid {\n\t\t_ = app.errorJSON(w, errors.New(\"invalid credentials\"), http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// log request\n\terr = app.logRequest(\"authentication\", fmt.Sprintf(\"%s logged in\", user.Email))\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err, http.StatusBadRequest)\n\t}\n\n\tpayload := jsonResponse{\n\t\tError:   false,\n\t\tMessage: fmt.Sprintf(\"Logged in user %s\", requestPayload.Email),\n\t\t//Data: User{\n\t\t//\tID:        1,\n\t\t//\tFirstName: \"Jack\",\n\t\t//\tLastName:  \"Smith\",\n\t\t//\tEmail:     \"jack@smith.com\",\n\t\t//\tActive:    1,\n\t\t//},\n\t\tData: user,\n\t}\n\n\t_ = app.writeJSON(w, http.StatusAccepted, payload)\n}\n\nfunc (app *Config) logRequest(name, data string) error {\n\tvar entry struct {\n\t\tName string `json:\"name\"`\n\t\tData string `json:\"data\"`\n\t}\n\tentry.Name = name\n\tentry.Data = data\n\n\tjsonData, _ := json.MarshalIndent(entry, \"\", \"\\t\")\n\tlogServiceURL := \"http://logger-service/log\"\n\n\trequest, err := http.NewRequest(\"POST\", logServiceURL, bytes.NewBuffer(jsonData))\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\t_, err = client.Do(request)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "authentication-service/cmd/api/helpers.go",
    "content": "package main\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n)\n\nconst randomStringSource = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0987654321_+\"\n\n// jsonResponse is the type used for sending JSON around\ntype jsonResponse struct {\n\tError   bool   `json:\"error\"`\n\tMessage string `json:\"message\"`\n\tData    any    `json:\"data,omitempty\"`\n}\n\n// readJSON tries to read the body of a request and converts it into JSON\nfunc (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error {\n\tmaxBytes := 1048576 // one megabyte\n\tr.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))\n\n\tdec := json.NewDecoder(r.Body)\n\terr := dec.Decode(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = dec.Decode(&struct{}{})\n\tif err != io.EOF {\n\t\treturn errors.New(\"body must have only a single json value\")\n\t}\n\n\treturn nil\n}\n\n// writeJSON takes a response status code and arbitrary data and writes a json response to the client\nfunc (app *Config) writeJSON(w http.ResponseWriter, status int, data any, headers ...http.Header) error {\n\tout, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(headers) > 0 {\n\t\tfor key, value := range headers[0] {\n\t\t\tw.Header()[key] = value\n\t\t}\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(status)\n\t_, err = w.Write(out)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// errorJSON takes an error, and optionally a response status code, and generates and sends\n// a json error response\nfunc (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error {\n\tstatusCode := http.StatusBadRequest\n\n\tif len(status) > 0 {\n\t\tstatusCode = status[0]\n\t}\n\n\tvar payload jsonResponse\n\tpayload.Error = true\n\tpayload.Message = err.Error()\n\n\treturn app.writeJSON(w, statusCode, payload)\n}\n\n// randomString returns a random string of letters of length n\nfunc (app *Config) randomString(n int) string {\n\ts, r := make([]rune, n), []rune(randomStringSource)\n\tfor i := range s {\n\t\tp, _ := rand.Prime(rand.Reader, len(r))\n\t\tx, y := p.Uint64(), uint64(len(r))\n\t\ts[i] = r[x%y]\n\t}\n\treturn string(s)\n}\n"
  },
  {
    "path": "authentication-service/cmd/api/main.go",
    "content": "package main\n\nimport (\n\t\"authentication/data\"\n\t\"database/sql\"\n\t\"fmt\"\n\t_ \"github.com/jackc/pgconn\"\n\t_ \"github.com/jackc/pgx/v4\"\n\t_ \"github.com/jackc/pgx/v4/stdlib\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n)\n\nconst webPort = \"80\"\n\nvar counts int64\n\ntype Config struct {\n\tDB     *sql.DB\n\tModels data.Models\n\t//Etcd   *clientv3.Client\n}\n\nfunc main() {\n\tlog.Println(\"---------------------------------------------\")\n\tlog.Println(\"Attempting to connect to Postgres...\")\n\t// connect to the database\n\tconn := connectToDB()\n\tif conn == nil {\n\t\tlog.Panic(\"can't connect to postgres!\")\n\t}\n\n\tapp := Config{\n\t\tDB:     conn,\n\t\tModels: data.New(conn),\n\t}\n\n\t//app.registerService()\n\t//defer app.Etcd.Close()\n\n\tsrv := &http.Server{\n\t\tAddr:    fmt.Sprintf(\":%s\", webPort),\n\t\tHandler: app.routes(),\n\t}\n\n\tlog.Printf(\"Starting authentication end service on port %s\\n\", webPort)\n\terr := srv.ListenAndServe()\n\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\nfunc connectToDB() *sql.DB {\n\t// connect to postgres\n\tdsn := os.Getenv(\"DSN\")\n\n\tfor {\n\t\tconnection, err := openDB(dsn)\n\t\tif err != nil {\n\t\t\tlog.Println(\"Postgres not ready...\")\n\t\t\tcounts++\n\t\t} else {\n\t\t\tlog.Println(\"Connected to database!\")\n\t\t\treturn connection\n\t\t}\n\n\t\tif counts > 10 {\n\t\t\tlog.Println(err)\n\t\t\treturn nil\n\t\t}\n\n\t\tlog.Println(\"Backing off for two seconds...\")\n\t\ttime.Sleep(2 * time.Second)\n\t\tcontinue\n\t}\n}\n\nfunc openDB(dsn string) (*sql.DB, error) {\n\tdb, err := sql.Open(\"pgx\", dsn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = db.Ping()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn db, nil\n}\n"
  },
  {
    "path": "authentication-service/cmd/api/routes.go",
    "content": "package main\n\nimport (\n\t\"github.com/go-chi/chi/v5\"\n\t\"github.com/go-chi/chi/v5/middleware\"\n\t\"github.com/go-chi/cors\"\n\t\"net/http\"\n)\n\nfunc (app *Config) routes() http.Handler {\n\tmux := chi.NewRouter()\n\n\t// specify who is allowed to connect to our API service\n\tmux.Use(cors.Handler(cors.Options{\n\t\tAllowedOrigins:   []string{\"*\"},\n\t\tAllowedMethods:   []string{\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"},\n\t\tAllowedHeaders:   []string{\"Accept\", \"Authorization\", \"Content-Type\", \"X-CSRF-Token\"},\n\t\tExposedHeaders:   []string{\"Link\"},\n\t\tAllowCredentials: true,\n\t\tMaxAge:           300,\n\t}))\n\n\tmux.Use(middleware.Heartbeat(\"/ping\"))\n\n\tmux.Post(\"/authenticate\", app.Authenticate)\n\n\treturn mux\n}\n"
  },
  {
    "path": "authentication-service/data/models.go",
    "content": "package data\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"log\"\n\t\"time\"\n\n\t\"golang.org/x/crypto/bcrypt\"\n)\n\nconst dbTimeout = time.Second * 3\n\nvar db *sql.DB\n\n// New is the function used to create an instance of the data package. It returns the type\n// Model, which embeds all the types we want to be available to our application.\nfunc New(dbPool *sql.DB) Models {\n\tdb = dbPool\n\n\treturn Models{\n\t\tUser: User{},\n\t}\n}\n\n// Models is the type for this package. Note that any model that is included as a member\n// in this type is available to us throughout the application, anywhere that the\n// app variable is used, provided that the model is also added in the New function.\ntype Models struct {\n\tUser User\n}\n\n// User is the structure which holds one user from the database.\ntype User struct {\n\tID        int       `json:\"id\"`\n\tEmail     string    `json:\"email\"`\n\tFirstName string    `json:\"first_name,omitempty\"`\n\tLastName  string    `json:\"last_name,omitempty\"`\n\tPassword  string    `json:\"-\"`\n\tActive    int       `json:\"active\"`\n\tCreatedAt time.Time `json:\"created_at\"`\n\tUpdatedAt time.Time `json:\"updated_at\"`\n}\n\n// GetAll returns a slice of all users, sorted by last name\nfunc (u *User) GetAll() ([]*User, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), dbTimeout)\n\tdefer cancel()\n\n\tquery := `select id, email, first_name, last_name, password, user_active, created_at, updated_at\n\tfrom users order by last_name`\n\n\trows, err := db.QueryContext(ctx, query)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar users []*User\n\n\tfor rows.Next() {\n\t\tvar user User\n\t\terr := rows.Scan(\n\t\t\t&user.ID,\n\t\t\t&user.Email,\n\t\t\t&user.FirstName,\n\t\t\t&user.LastName,\n\t\t\t&user.Password,\n\t\t\t&user.Active,\n\t\t\t&user.CreatedAt,\n\t\t\t&user.UpdatedAt,\n\t\t)\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error scanning\", err)\n\t\t\treturn nil, err\n\t\t}\n\n\t\tusers = append(users, &user)\n\t}\n\n\treturn users, nil\n}\n\n// GetByEmail returns one user by email\nfunc (u *User) GetByEmail(email string) (*User, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), dbTimeout)\n\tdefer cancel()\n\n\tquery := `select id, email, first_name, last_name, password, user_active, created_at, updated_at from users where email = $1`\n\n\tvar user User\n\trow := db.QueryRowContext(ctx, query, email)\n\n\terr := row.Scan(\n\t\t&user.ID,\n\t\t&user.Email,\n\t\t&user.FirstName,\n\t\t&user.LastName,\n\t\t&user.Password,\n\t\t&user.Active,\n\t\t&user.CreatedAt,\n\t\t&user.UpdatedAt,\n\t)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &user, nil\n}\n\n// GetOne returns one user by id\nfunc (u *User) GetOne(id int) (*User, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), dbTimeout)\n\tdefer cancel()\n\n\tquery := `select id, email, first_name, last_name, password, user_active, created_at, updated_at from users where id = $1`\n\n\tvar user User\n\trow := db.QueryRowContext(ctx, query, id)\n\n\terr := row.Scan(\n\t\t&user.ID,\n\t\t&user.Email,\n\t\t&user.FirstName,\n\t\t&user.LastName,\n\t\t&user.Password,\n\t\t&user.Active,\n\t\t&user.CreatedAt,\n\t\t&user.UpdatedAt,\n\t)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &user, nil\n}\n\n// Update updates one user in the database, using the information\n// stored in the receiver u\nfunc (u *User) Update() error {\n\tctx, cancel := context.WithTimeout(context.Background(), dbTimeout)\n\tdefer cancel()\n\n\tstmt := `update users set\n\t\temail = $1,\n\t\tfirst_name = $2,\n\t\tlast_name = $3,\n\t\tuser_active = $4,\n\t\tupdated_at = $5\n\t\twhere id = $6\n\t`\n\n\t_, err := db.ExecContext(ctx, stmt,\n\t\tu.Email,\n\t\tu.FirstName,\n\t\tu.LastName,\n\t\tu.Active,\n\t\ttime.Now(),\n\t\tu.ID,\n\t)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Delete deletes one user from the database, by User.ID\nfunc (u *User) Delete() error {\n\tctx, cancel := context.WithTimeout(context.Background(), dbTimeout)\n\tdefer cancel()\n\n\tstmt := `delete from users where id = $1`\n\n\t_, err := db.ExecContext(ctx, stmt, u.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// DeleteByID deletes one user from the database, by ID\nfunc (u *User) DeleteByID(id int) error {\n\tctx, cancel := context.WithTimeout(context.Background(), dbTimeout)\n\tdefer cancel()\n\n\tstmt := `delete from users where id = $1`\n\n\t_, err := db.ExecContext(ctx, stmt, id)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Insert inserts a new user into the database, and returns the ID of the newly inserted row\nfunc (u *User) Insert(user User) (int, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), dbTimeout)\n\tdefer cancel()\n\n\thashedPassword, err := bcrypt.GenerateFromPassword([]byte(user.Password), 12)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tvar newID int\n\tstmt := `insert into users (email, first_name, last_name, password, user_active, created_at, updated_at)\n\t\tvalues ($1, $2, $3, $4, $5, $6, $7) returning id`\n\n\terr = db.QueryRowContext(ctx, stmt,\n\t\tuser.Email,\n\t\tuser.FirstName,\n\t\tuser.LastName,\n\t\thashedPassword,\n\t\tuser.Active,\n\t\ttime.Now(),\n\t\ttime.Now(),\n\t).Scan(&newID)\n\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn newID, nil\n}\n\n// ResetPassword is the method we will use to change a user's password.\nfunc (u *User) ResetPassword(password string) error {\n\tctx, cancel := context.WithTimeout(context.Background(), dbTimeout)\n\tdefer cancel()\n\n\thashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tstmt := `update users set password = $1 where id = $2`\n\t_, err = db.ExecContext(ctx, stmt, hashedPassword, u.ID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// PasswordMatches uses Go's bcrypt package to compare a user supplied password\n// with the hash we have stored for a given user in the database. If the password\n// and hash match, we return true; otherwise, we return false.\nfunc (u *User) PasswordMatches(plainText string) (bool, error) {\n\terr := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(plainText))\n\tif err != nil {\n\t\tswitch {\n\t\tcase errors.Is(err, bcrypt.ErrMismatchedHashAndPassword):\n\t\t\t// invalid password\n\t\t\treturn false, nil\n\t\tdefault:\n\t\t\treturn false, err\n\t\t}\n\t}\n\n\treturn true, nil\n}\n"
  },
  {
    "path": "authentication-service/go.mod",
    "content": "module authentication\n\ngo 1.18\n\nrequire (\n\tgithub.com/go-chi/chi/v5 v5.0.8\n\tgithub.com/go-chi/cors v1.2.1\n\tgithub.com/jackc/pgconn v1.14.0\n\tgithub.com/jackc/pgx/v4 v4.18.1\n\tgo.etcd.io/etcd/client/v3 v3.5.2\n\tgolang.org/x/crypto v0.6.0\n)\n\nrequire (\n\tgithub.com/coreos/go-semver v0.3.0 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.3.2 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/jackc/chunkreader/v2 v2.0.1 // indirect\n\tgithub.com/jackc/pgio v1.0.0 // indirect\n\tgithub.com/jackc/pgpassfile v1.0.0 // indirect\n\tgithub.com/jackc/pgproto3/v2 v2.3.2 // indirect\n\tgithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect\n\tgithub.com/jackc/pgtype v1.14.0 // indirect\n\tgo.etcd.io/etcd/api/v3 v3.5.2 // indirect\n\tgo.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgo.uber.org/multierr v1.8.0 // indirect\n\tgo.uber.org/zap v1.21.0 // indirect\n\tgolang.org/x/net v0.6.0 // indirect\n\tgolang.org/x/sys v0.5.0 // indirect\n\tgolang.org/x/text v0.7.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f // indirect\n\tgoogle.golang.org/grpc v1.45.0 // indirect\n\tgoogle.golang.org/protobuf v1.28.0 // indirect\n)\n"
  },
  {
    "path": "authentication-service/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=\ngithub.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=\ngithub.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=\ngithub.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=\ngithub.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=\ngithub.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=\ngithub.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=\ngithub.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=\ngithub.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=\ngithub.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=\ngithub.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=\ngithub.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=\ngithub.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=\ngithub.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=\ngithub.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=\ngithub.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=\ngithub.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=\ngithub.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=\ngithub.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=\ngithub.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=\ngithub.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=\ngithub.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=\ngithub.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ=\ngithub.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=\ngithub.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q=\ngithub.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E=\ngithub.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=\ngithub.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=\ngithub.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=\ngithub.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=\ngithub.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=\ngithub.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=\ngithub.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=\ngithub.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=\ngithub.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=\ngithub.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=\ngithub.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=\ngithub.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=\ngithub.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0=\ngithub.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=\ngithub.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=\ngithub.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=\ngithub.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=\ngithub.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=\ngithub.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=\ngithub.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=\ngithub.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=\ngithub.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=\ngithub.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=\ngithub.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=\ngithub.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=\ngithub.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=\ngithub.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=\ngithub.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=\ngithub.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=\ngithub.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=\ngithub.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0=\ngithub.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE=\ngithub.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=\ngithub.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=\ngithub.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=\ngithub.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=\ngithub.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=\ngithub.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=\ngo.etcd.io/etcd/api/v3 v3.5.2 h1:tXok5yLlKyuQ/SXSjtqHc4uzNaMqZi2XsoSPr/LlJXI=\ngo.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2 h1:4hzqQ6hIb3blLyQ8usCU4h3NghkqcsohEQ3o3VetYxE=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/v3 v3.5.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA=\ngo.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=\ngo.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=\ngo.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=\ngo.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=\ngo.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=\ngo.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=\ngolang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=\ngolang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=\ngolang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=\ngolang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=\ngoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f h1:9ug+SpnUXKl5LogY3yp9GHWUjUDCnFv4NjiP7yxS6Q4=\ngoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "authentication-service/users.sql",
    "content": "\n\n--\n-- Name: user_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres\n--\n\nCREATE SEQUENCE public.user_id_seq\n    START WITH 1\n    INCREMENT BY 1\n    NO MINVALUE\n    NO MAXVALUE\n    CACHE 1;\n\n\nALTER TABLE public.user_id_seq OWNER TO postgres;\n\nSET default_tablespace = '';\n\nSET default_table_access_method = heap;\n\n--\n-- Name: users; Type: TABLE; Schema: public; Owner: postgres\n--\n\nCREATE TABLE public.users (\n    id integer DEFAULT nextval('public.user_id_seq'::regclass) NOT NULL,\n    email character varying(255),\n    first_name character varying(255),\n    last_name character varying(255),\n    password character varying(60),\n    user_active integer DEFAULT 0,\n    created_at timestamp without time zone,\n    updated_at timestamp without time zone\n);\n\n\nALTER TABLE public.users OWNER TO postgres;\n\n--\n-- Name: user_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres\n--\n\nSELECT pg_catalog.setval('public.user_id_seq', 1, true);\n\n\n--\n-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres\n--\n\nALTER TABLE ONLY public.users\n    ADD CONSTRAINT users_pkey PRIMARY KEY (id);\n\n\nINSERT INTO \"public\".\"users\"(\"email\",\"first_name\",\"last_name\",\"password\",\"user_active\",\"created_at\",\"updated_at\")\nVALUES\n(E'admin@example.com',E'Admin',E'User',E'$2a$12$1zGLuYDDNvATh4RA4avbKuheAMpb1svexSzrQm7up.bnpwQHs0jNe',1,E'2022-03-14 00:00:00',E'2022-03-14 00:00:00');\n\n\n\n\n"
  },
  {
    "path": "authentication-service.dockerfile",
    "content": "FROM alpine:latest\nRUN mkdir /app\n\nCOPY authentication-service/authApp /app\n\n# Run the server executable\nCMD [ \"/app/authApp\" ]"
  },
  {
    "path": "broker-service/Makefile",
    "content": "gen:\n\tprotoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative logs/logs.proto\n"
  },
  {
    "path": "broker-service/cmd/api/discover.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\t\"time\"\n)\n\n//func (app *Config) getServiceURLs() {\n//\tkv := clientv3.NewKV(app.Etcd)\n//\tapp.MailServiceURLs = make(map[string]string)\n//\tapp.LogServiceURLs = make(map[string]string)\n//\tapp.AuthServiceURLs = make(map[string]string)\n//\n//\tprefixes := []string{\"/mail/\", \"/logger/\", \"/auth/\"}\n//\n//\t// range through all the services we want to discover\n//\tfor _, curPrefix := range prefixes {\n//\t\tgetResp, err := kv.Get(context.TODO(), curPrefix, clientv3.WithPrefix())\n//\t\tif err != nil {\n//\t\t\tlog.Println(err)\n//\t\t}\n//\n//\t\tfor _, k := range getResp.Kvs {\n//\t\t\t//log.Println(\"Key\", string(k.Key))\n//\t\t\t//log.Println(\"Adding\", string(k.Value), \"to\", curPrefix, \"service map; key was\", string(k.Key))\n//\t\t\tswitch curPrefix {\n//\t\t\tcase \"/mail/\":\n//\t\t\t\tapp.MailServiceURLs[string(k.Value)] = \"\"\n//\t\t\tcase \"/logger/\":\n//\t\t\t\tapp.LogServiceURLs[string(k.Value)] = \"\"\n//\t\t\tcase \"/auth/\":\n//\t\t\t\tapp.AuthServiceURLs[string(k.Value)] = \"\"\n//\t\t\t}\n//\t\t}\n//\t}\n//}\n\n// watchEtcd runs in the background, looking for changes in etcd. When it finds changes\n// hosts, it updates the appropriate map in the *Config receiver.\n//func (app *Config) watchEtcd() {\n//\tfor {\n//\t\t// watch for service changes\n//\t\twatchKey := app.Etcd.Watch(context.Background(), \"/mail/\", clientv3.WithPrefix())\n//\t\tfor resp := range watchKey {\n//\t\t\tfor _, item := range resp.Events {\n//\t\t\t\t// get our values as strings so that we can work with them\n//\t\t\t\teventType := item.Type.String()\n//\t\t\t\tkey := string(item.Kv.Key)\n//\t\t\t\tvalue := string(item.Kv.Value)\n//\t\t\t\tvar deleteURL = false\n//\t\t\t\tif strings.Contains(eventType, \"DELETE\") {\n//\t\t\t\t\tdeleteURL = true\n//\t\t\t\t}\n//\n//\t\t\t\t// add to or remove from service maps (using url as key, and empty string as value)\n//\t\t\t\tswitch {\n//\t\t\t\tcase strings.HasPrefix(key, \"mail\"):\n//\t\t\t\t\t// mail\n//\t\t\t\t\tif deleteURL {\n//\t\t\t\t\t\tlog.Println(\"Removing\", value, \"from mail service map\")\n//\t\t\t\t\t\tdelete(app.MailServiceURLs, key)\n//\t\t\t\t\t} else {\n//\t\t\t\t\t\tlog.Println(\"Adding\", value, \"to mail service map\")\n//\t\t\t\t\t\tapp.MailServiceURLs[value] = \"\"\n//\t\t\t\t\t}\n//\n//\t\t\t\tcase strings.HasPrefix(key, \"logger\"):\n//\t\t\t\t\t// logger\n//\t\t\t\t\tif deleteURL {\n//\t\t\t\t\t\tdelete(app.LogServiceURLs, key)\n//\t\t\t\t\t} else {\n//\t\t\t\t\t\tapp.LogServiceURLs[value] = \"\"\n//\t\t\t\t\t}\n//\n//\t\t\t\tcase strings.HasPrefix(key, \"auth\"):\n//\t\t\t\t\t// authentication\n//\t\t\t\t\tif deleteURL {\n//\t\t\t\t\t\tdelete(app.AuthServiceURLs, key)\n//\t\t\t\t\t} else {\n//\t\t\t\t\t\tapp.AuthServiceURLs[value] = \"\"\n//\t\t\t\t\t}\n//\t\t\t\t}\n//\t\t\t}\n//\t\t}\n//\t}\n//}\n\n// GetServiceURL will get a service's url from those listed as available in etcd\n//func (app *Config) GetServiceURL(serviceType string) string {\n//\tvar serviceURL string\n//\n//\t// get service URL from etcd\n//\tswitch serviceType {\n//\tcase \"mail\":\n//\t\tserviceURL = getUrlFromMap(app.MailServiceURLs)\n//\tcase \"logger\":\n//\t\tserviceURL = getUrlFromMap(app.LogServiceURLs)\n//\tcase \"auth\":\n//\t\tserviceURL = getUrlFromMap(app.AuthServiceURLs)\n//\t}\n//\n//\treturn serviceURL\n//}\n\n// getUrlFromMap returns a random value from available urls in\n// service maps. Since maps are never guaranteed to be in the same order,\n// grabbing the first value is sufficient for our purposes.\nfunc getUrlFromMap(m map[string]string) string {\n\tvar u string\n\tfor k := range m {\n\t\tu = k\n\t\tbreak\n\t}\n\treturn u\n}\n\n// connectToEtcd tries to connect to etcd, for up to 30 seconds\nfunc connectToEtcd() (*clientv3.Client, error) {\n\tvar cli *clientv3.Client\n\tvar counts = 0\n\n\tfor {\n\t\tc, err := clientv3.New(clientv3.Config{Endpoints: []string{\"etcd:2379\"},\n\t\t\tDialTimeout: 5 * time.Second,\n\t\t})\n\t\tif err != nil {\n\t\t\tfmt.Println(\"etcd not ready...\")\n\t\t\tcounts++\n\t\t} else {\n\t\t\tfmt.Println()\n\t\t\tcli = c\n\t\t\tbreak\n\t\t}\n\n\t\tif counts > 15 {\n\t\t\treturn nil, err\n\t\t}\n\t\tfmt.Println(\"Backing off for 2 seconds...\")\n\t\ttime.Sleep(2 * time.Second)\n\t\tcontinue\n\t}\n\tfmt.Println(\"Connected to etcd!\")\n\treturn cli, nil\n}\n"
  },
  {
    "path": "broker-service/cmd/api/handlers.go",
    "content": "package main\n\nimport (\n\t\"broker/event\"\n\t\"broker/logs\"\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"google.golang.org/grpc\"\n\t\"google.golang.org/grpc/credentials/insecure\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/rpc\"\n\t\"time\"\n)\n\nconst loggerGRPCAddress = \"logger-service:50001\"\n\n// Payload is the type for data we push into RabbitMQ\ntype Payload struct {\n\tName string `json:\"name\"`\n\tData any    `json:\"data\"`\n}\n\n// RequestPayload is the type describing the data that we received\n// from the user's browser. We embed a custom type for each of the\n// possible payloads (mail, auth, and log).\ntype RequestPayload struct {\n\tAction string      `json:\"action\"`\n\tMail   MailPayload `json:\"mail,omitempty\"`\n\tAuth   AuthPayload `json:\"auth,omitempty\"`\n\tLog    LogPayload  `json:\"log,omitempty\"`\n}\n\n// AuthPayload is the type embedded in RequestPayload for auth\ntype AuthPayload struct {\n\tEmail    string `json:\"email\"`\n\tPassword string `json:\"password\"`\n}\n\n// LogPayload is the type embedded in RequestPayload for logging\ntype LogPayload struct {\n\tName string `json:\"name\"`\n\tData string `json:\"data\"`\n}\n\n// MailPayload is the type embedded in RequestPayload for sending email\ntype MailPayload struct {\n\tFrom    string `json:\"from\"`\n\tTo      string `json:\"to\"`\n\tSubject string `json:\"subject\"`\n\tMessage string `json:\"message\"`\n}\n\n// Broker is a simple test handler for the broker\nfunc (app *Config) Broker(w http.ResponseWriter, r *http.Request) {\n\terr := app.pushToQueue(\"broker_hit\", r.RemoteAddr)\n\tif err != nil {\n\t\tlog.Println(err)\n\t}\n\n\tvar payload jsonResponse\n\tpayload.Message = \"Received request\"\n\n\tout, _ := json.MarshalIndent(payload, \"\", \"\\t\")\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusAccepted)\n\t_, _ = w.Write(out)\n}\n\n// HandleSubmission handles a JSON payload that describes an action to take,\n// processes it, and sends it where it needs to go\nfunc (app *Config) HandleSubmission(w http.ResponseWriter, r *http.Request) {\n\tvar requestPayload RequestPayload\n\n\terr := app.readJSON(w, r, &requestPayload)\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err)\n\t\treturn\n\t}\n\n\tswitch requestPayload.Action {\n\tcase \"mail\":\n\t\tapp.sendMail(w, requestPayload.Mail)\n\tcase \"auth\":\n\t\tapp.authenticate(w, requestPayload.Auth)\n\tcase \"log\":\n\t\tapp.logItemViaRPC(w, requestPayload.Log)\n\tdefault:\n\t\t_ = app.errorJSON(w, errors.New(\"unknown action\"))\n\t}\n}\n\nfunc (app *Config) logViaJSON(w http.ResponseWriter, entry LogPayload) {\n\tjsonData, _ := json.MarshalIndent(entry, \"\", \"\\t\")\n\tlogServiceURL := \"http://logger-service/log\"\n\n\trequest, err := http.NewRequest(\"POST\", logServiceURL, bytes.NewBuffer(jsonData))\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err, http.StatusBadRequest)\n\t\treturn\n\t}\n\tdefer response.Body.Close()\n\n\t// make sure we get back the right status code\n\tif response.StatusCode != http.StatusAccepted {\n\t\t_ = app.errorJSON(w, errors.New(\"error calling logger service\"), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// send json back to our end user\n\tvar payload jsonResponse\n\tpayload.Error = false\n\tpayload.Message = \"Logged!\"\n\n\t_ = app.writeJSON(w, http.StatusAccepted, payload)\n}\n\n// sendMail sends an email through the mail-service. It receives a json payload\n// of type requestPayload, with MailPayload embedded.\nfunc (app *Config) sendMail(w http.ResponseWriter, msg MailPayload) {\n\tjsonData, _ := json.MarshalIndent(msg, \"\", \"\\t\")\n\n\t// call the mail service; we need a request, so let's build one, and populate\n\t// its body with the jsonData we just created. First we get the correct server\n\t// to call from our service map.\n\t//mailServiceURL := fmt.Sprintf(\"http://%s/send\", app.GetServiceURL(\"mail\"))\n\tmailServiceURL := fmt.Sprintf(\"http://%s/send\", \"mail-service\")\n\n\t// now post to the mail service\n\trequest, err := http.NewRequest(\"POST\", mailServiceURL, bytes.NewBuffer(jsonData))\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\n\tclient := &http.Client{}\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err, http.StatusBadRequest)\n\t\treturn\n\t}\n\tdefer response.Body.Close()\n\n\t// make sure we get back the right status code\n\tif response.StatusCode != http.StatusAccepted {\n\t\t_ = app.errorJSON(w, errors.New(\"error calling mail service\"), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// send json back to our end user\n\tvar payload jsonResponse\n\tpayload.Error = false\n\tpayload.Message = \"Message sent to \" + msg.To\n\n\tout, _ := json.MarshalIndent(payload, \"\", \"\\t\")\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(http.StatusAccepted)\n\t_, _ = w.Write(out)\n\n}\n\n// authenticate tries to log a user in through the authentication-service. It receives a json payload\n// of type requestPayload, with AuthPayload embedded.\nfunc (app *Config) authenticate(w http.ResponseWriter, a AuthPayload) {\n\t// create json we'll send to the authentication-service\n\tjsonData, _ := json.MarshalIndent(a, \"\", \"\\t\")\n\n\t// call the authentication-service; we need a request, so let's build one, and populate\n\t// its body with the jsonData we just created. First we get the correct url for our\n\t// auth service from our service map.\n\t//authServiceURL := fmt.Sprintf(\"http://%s/authenticate\", app.GetServiceURL(\"auth\"))\n\tauthServiceURL := fmt.Sprintf(\"http://%s/authenticate\", \"authentication-service\")\n\n\t// now build the request and set header\n\trequest, err := http.NewRequest(\"POST\", authServiceURL, bytes.NewBuffer(jsonData))\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\n\t// call the service\n\tclient := &http.Client{}\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err, http.StatusBadRequest)\n\t\treturn\n\t}\n\tdefer response.Body.Close()\n\n\t// make sure we get back the right status code\n\tif response.StatusCode == http.StatusUnauthorized {\n\t\t_ = app.errorJSON(w, errors.New(\"invalid credentials\"), http.StatusUnauthorized)\n\t\treturn\n\t} else if response.StatusCode != http.StatusAccepted {\n\t\t_ = app.errorJSON(w, errors.New(\"error calling auth service\"), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// create variable we'll read the response.Body from the authentication-service into\n\tvar jsonFromService jsonResponse\n\n\t// decode the json we get from the authentication-service into our variable\n\terr = json.NewDecoder(response.Body).Decode(&jsonFromService)\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// did not authenticate successfully\n\tif jsonFromService.Error {\n\t\t// log it\n\t\t_ = app.pushToQueue(\"authentication\", fmt.Sprintf(\"invalid login for %s\", a.Email))\n\t\t// send error JSON back\n\t\t_ = app.errorJSON(w, err, http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\t// valid login, so send it to the logger service via RabbitMQ\n\t_ = app.pushToQueue(\"authentication\", fmt.Sprintf(\"valid login for %s\", a.Email))\n\n\t// send json back to our end user, with user info embedded\n\tvar payload jsonResponse\n\tpayload.Error = false\n\tpayload.Message = \"Authenticated!\"\n\tpayload.Data = jsonFromService.Data\n\n\t_ = app.writeJSON(w, http.StatusAccepted, payload)\n}\n\n// logItem logs an event using the logger-service. It makes the call by pushing the data to RabbitMQ.\nfunc (app *Config) logItem(w http.ResponseWriter, l LogPayload) {\n\terr := app.pushToQueue(l.Name, l.Data)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\t_ = app.errorJSON(w, err)\n\t}\n\n\t// send json back to our end user\n\tvar payload jsonResponse\n\tpayload.Error = false\n\tpayload.Message = \"logged\"\n\n\t_ = app.writeJSON(w, http.StatusAccepted, payload)\n}\n\ntype RPCPayload struct {\n\tName string\n\tData string\n}\n\nfunc (app *Config) logItemViaRPC(w http.ResponseWriter, l LogPayload) {\n\tclient, err := rpc.Dial(\"tcp\", \"logger-service:5001\")\n\tif err != nil {\n\t\tapp.errorJSON(w, err)\n\t\treturn\n\t}\n\n\trpcPayload := RPCPayload{\n\t\tName: l.Name,\n\t\tData: l.Data,\n\t}\n\n\tvar result string\n\terr = client.Call(\"RPCServer.LogInfo\", rpcPayload, &result)\n\tif err != nil {\n\t\tapp.errorJSON(w, err)\n\t\treturn\n\t}\n\n\tpayload := jsonResponse{\n\t\tError:   false,\n\t\tMessage: result,\n\t}\n\n\tapp.writeJSON(w, http.StatusAccepted, payload)\n}\n\n// pushToQueue pushes a message into RabbitMQ\nfunc (app *Config) pushToQueue(name, msg string) error {\n\temitter, err := event.NewEventEmitter(app.Rabbit)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn err\n\t}\n\n\tpayload := Payload{\n\t\tName: name,\n\t\tData: msg,\n\t}\n\n\tj, _ := json.MarshalIndent(&payload, \"\", \"    \")\n\terr = emitter.Push(string(j), \"log.INFO\")\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// LogViaGRPC takes a JSON payload and logs it using gRPC as the transport\nfunc (app *Config) LogViaGRPC(w http.ResponseWriter, r *http.Request) {\n\tvar requestPayload RequestPayload\n\n\terr := app.readJSON(w, r, &requestPayload)\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err)\n\t\treturn\n\t}\n\n\tconn, err := grpc.Dial(loggerGRPCAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock())\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err)\n\t\treturn\n\t}\n\tdefer conn.Close()\n\n\tc := logs.NewLogServiceClient(conn)\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second)\n\tdefer cancel()\n\t_, err = c.WriteLog(ctx, &logs.LogRequest{\n\t\tLogEntry: &logs.Log{\n\t\t\tName: requestPayload.Log.Name,\n\t\t\tData: requestPayload.Log.Data,\n\t\t},\n\t})\n\tif err != nil {\n\t\t_ = app.errorJSON(w, err)\n\t\treturn\n\t}\n\n\tvar payload jsonResponse\n\tpayload.Error = false\n\tpayload.Message = \"logged\"\n\n\t_ = app.writeJSON(w, http.StatusAccepted, payload)\n}\n"
  },
  {
    "path": "broker-service/cmd/api/helpers.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n)\n\ntype jsonResponse struct {\n\tError   bool   `json:\"error\"`\n\tMessage string `json:\"message\"`\n\tData    any    `json:\"data,omitempty\"`\n}\n\n// readJSON tries to read the body of a request and converts it into JSON\nfunc (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error {\n\tmaxBytes := 1048576 // one megabyte\n\tr.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))\n\n\tdec := json.NewDecoder(r.Body)\n\terr := dec.Decode(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = dec.Decode(&struct{}{})\n\tif err != io.EOF {\n\t\treturn errors.New(\"body must have only a single json value\")\n\t}\n\n\treturn nil\n}\n\n// writeJSON takes a response status code and arbitrary data and writes a json response to the client\nfunc (app *Config) writeJSON(w http.ResponseWriter, status int, data any, headers ...http.Header) error {\n\tout, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(headers) > 0 {\n\t\tfor key, value := range headers[0] {\n\t\t\tw.Header()[key] = value\n\t\t}\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(status)\n\t_, err = w.Write(out)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// errorJSON takes an error, and optionally a response status code, and generates and sends\n// a json error response\nfunc (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error {\n\tstatusCode := http.StatusBadRequest\n\n\tif len(status) > 0 {\n\t\tstatusCode = status[0]\n\t}\n\n\tvar payload jsonResponse\n\tpayload.Error = true\n\tpayload.Message = err.Error()\n\n\treturn app.writeJSON(w, statusCode, payload)\n}\n"
  },
  {
    "path": "broker-service/cmd/api/main.go",
    "content": "package main\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"time\"\n)\n\n// webPort the port that we listen on for api calls\nconst webPort = \"80\"\n\n// Config is the type we'll use as a receiver to share application\n// configuration around our app.\ntype Config struct {\n\tRabbit         *amqp.Connection\n\tEtcd           *clientv3.Client\n\tLogServiceURLs map[string]string\n\t//MailServiceURLs map[string]string\n\t//AuthServiceURLs map[string]string\n}\n\nfunc main() {\n\t// don't continue until rabbitmq is ready\n\trabbitConn, err := connectToRabbit()\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(1)\n\t}\n\tdefer rabbitConn.Close()\n\n\t// don't continue until etcd is ready\n\t//etcConn, err := connectToEtcd()\n\t//if err != nil {\n\t//\tfmt.Println(err)\n\t//\tos.Exit(1)\n\t//}\n\t//defer etcConn.Close()\n\n\tapp := Config{\n\t\tRabbit: rabbitConn,\n\t\t//Etcd:   etcConn,\n\t}\n\n\t// get service urls\n\t//app.getServiceURLs()\n\n\t// watch service urls\n\t//go app.watchEtcd()\n\n\tlog.Println(\"Starting broker service on port\", webPort)\n\n\t// define the http server\n\tsrv := &http.Server{\n\t\tAddr:    fmt.Sprintf(\":%s\", webPort),\n\t\tHandler: app.routes(),\n\t}\n\n\t// start the server\n\terr = srv.ListenAndServe()\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\n// connectToRabbit tries to connect to RabbitMQ, for up to 30 seconds\nfunc connectToRabbit() (*amqp.Connection, error) {\n\tvar rabbitConn *amqp.Connection\n\tvar counts int64\n\tvar rabbitURL = os.Getenv(\"RABBIT_URL\")\n\n\tfor {\n\t\tconnection, err := amqp.Dial(rabbitURL)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"rabbitmq not ready...\")\n\t\t\tcounts++\n\t\t} else {\n\t\t\tfmt.Println()\n\t\t\trabbitConn = connection\n\t\t\tbreak\n\t\t}\n\n\t\tif counts > 15 {\n\t\t\tfmt.Println(err)\n\t\t\treturn nil, errors.New(\"cannot connect to rabbit\")\n\t\t}\n\t\tfmt.Println(\"Backing off for 2 seconds...\")\n\t\ttime.Sleep(2 * time.Second)\n\t\tcontinue\n\t}\n\tfmt.Println(\"Connected to RabbitMQ!\")\n\treturn rabbitConn, nil\n}\n"
  },
  {
    "path": "broker-service/cmd/api/routes.go",
    "content": "package main\n\nimport (\n\t\"github.com/go-chi/chi/v5\"\n\t\"github.com/go-chi/chi/v5/middleware\"\n\t\"github.com/go-chi/cors\"\n\t\"net/http\"\n)\n\nfunc (app *Config) routes() http.Handler {\n\tmux := chi.NewRouter()\n\n\t// specify who is allowed to connect to our API service\n\tmux.Use(cors.Handler(cors.Options{\n\t\tAllowedOrigins:   []string{\"https://*\", \"http://*\"},\n\t\tAllowedMethods:   []string{\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"},\n\t\tAllowedHeaders:   []string{\"Accept\", \"Authorization\", \"Content-Type\", \"X-CSRF-Token\"},\n\t\tExposedHeaders:   []string{\"Link\"},\n\t\tAllowCredentials: true,\n\t\tMaxAge:           300,\n\t}))\n\n\t// a heartbeat route, to ensure things are up\n\tmux.Use(middleware.Heartbeat(\"/ping\"))\n\n\t// this route is just to ensure things work, and is never\n\t// used after that\n\tmux.Get(\"/\", app.Broker)\n\n\tmux.Post(\"/\", app.Broker)\n\n\t// grpc route\n\tmux.Post(\"/log-grpc\", app.LogViaGRPC)\n\n\t// a route for everything\n\tmux.Post(\"/handle\", app.HandleSubmission)\n\n\treturn mux\n}\n"
  },
  {
    "path": "broker-service/event/emitter.go",
    "content": "package event\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\n// Emitter for publishing AMQP events\ntype Emitter struct {\n\tconnection *amqp.Connection\n}\n\nfunc (e *Emitter) setup() error {\n\tchannel, err := e.connection.Channel()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\tdefer channel.Close()\n\treturn declareExchange(channel)\n}\n\n// Push (Publish) a specified message to the AMQP exchange\nfunc (e *Emitter) Push(event string, severity string) error {\n\tchannel, err := e.connection.Channel()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer channel.Close()\n\n\tlog.Println(\"Pushing to\", getExchangeName())\n\n\terr = channel.PublishWithContext(\n\t\tcontext.Background(),\n\t\tgetExchangeName(),\n\t\tseverity,\n\t\tfalse,\n\t\tfalse,\n\t\tamqp.Publishing{\n\t\t\tContentType: \"text/plain\",\n\t\t\tBody:        []byte(event),\n\t\t},\n\t)\n\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn err\n\t}\n\tlog.Printf(\"Sending message: %s -> %s\", event, getExchangeName())\n\treturn nil\n}\n\n// NewEventEmitter returns a new event.Emitter object\n// ensuring that the object is initialised, without error\nfunc NewEventEmitter(conn *amqp.Connection) (Emitter, error) {\n\temitter := Emitter{\n\t\tconnection: conn,\n\t}\n\n\terr := emitter.setup()\n\tif err != nil {\n\t\treturn Emitter{}, err\n\t}\n\n\treturn emitter, nil\n}\n"
  },
  {
    "path": "broker-service/event/event.go",
    "content": "package event\n\nimport (\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\nfunc getExchangeName() string {\n\treturn \"logs_topic\"\n}\n\nfunc declareExchange(ch *amqp.Channel) error {\n\treturn ch.ExchangeDeclare(\n\t\tgetExchangeName(), // name\n\t\t\"topic\",           // type\n\t\ttrue,              // durable\n\t\tfalse,             // auto-deleted\n\t\tfalse,             // internal\n\t\tfalse,             // no-wait\n\t\tnil,               // arguments\n\t)\n}\n"
  },
  {
    "path": "broker-service/go.mod",
    "content": "module broker\n\ngo 1.18\n\nrequire (\n\tgithub.com/go-chi/chi/v5 v5.0.8\n\tgithub.com/go-chi/cors v1.2.1\n\tgithub.com/rabbitmq/amqp091-go v1.7.0\n\tgo.etcd.io/etcd/client/v3 v3.5.7\n\tgoogle.golang.org/grpc v1.53.0\n\tgoogle.golang.org/protobuf v1.28.1\n)\n\nrequire (\n\tgithub.com/coreos/go-semver v0.3.1 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.5.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgo.etcd.io/etcd/api/v3 v3.5.7 // indirect\n\tgo.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect\n\tgo.uber.org/atomic v1.10.0 // indirect\n\tgo.uber.org/multierr v1.9.0 // indirect\n\tgo.uber.org/zap v1.24.0 // indirect\n\tgolang.org/x/net v0.7.0 // indirect\n\tgolang.org/x/sys v0.5.0 // indirect\n\tgolang.org/x/text v0.7.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 // indirect\n)\n"
  },
  {
    "path": "broker-service/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=\ngithub.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=\ngithub.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=\ngithub.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=\ngithub.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=\ngithub.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=\ngithub.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=\ngithub.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=\ngithub.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=\ngithub.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=\ngithub.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/rabbitmq/amqp091-go v1.3.0 h1:A/QuHiNw7LMCJsxx9iZn5lrIz6OrhIn7Dfk5/1YatWM=\ngithub.com/rabbitmq/amqp091-go v1.3.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=\ngithub.com/rabbitmq/amqp091-go v1.3.2 h1:zezKg1S58+q/9Ej7DIqFL6TP6NGMyGPb4ykEm4n94cY=\ngithub.com/rabbitmq/amqp091-go v1.3.2/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=\ngithub.com/rabbitmq/amqp091-go v1.7.0 h1:V5CF5qPem5OGSnEo8BoSbsDGwejg6VUJsKEdneaoTUo=\ngithub.com/rabbitmq/amqp091-go v1.7.0/go.mod h1:wfClAtY0C7bOHxd3GjmF26jEHn+rR/0B3+YV+Vn9/NI=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.etcd.io/etcd/api/v3 v3.5.2 h1:tXok5yLlKyuQ/SXSjtqHc4uzNaMqZi2XsoSPr/LlJXI=\ngo.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=\ngo.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY=\ngo.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2 h1:4hzqQ6hIb3blLyQ8usCU4h3NghkqcsohEQ3o3VetYxE=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY=\ngo.etcd.io/etcd/client/v3 v3.5.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA=\ngo.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o=\ngo.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4=\ngo.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=\ngo.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=\ngo.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=\ngo.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=\ngo.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=\ngo.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\ngo.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=\ngo.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=\ngolang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=\ngoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f h1:9ug+SpnUXKl5LogY3yp9GHWUjUDCnFv4NjiP7yxS6Q4=\ngoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 h1:/cadn7taPtPlCgiWNetEPsle7jgnlad2R7gR5MXB6dM=\ngoogle.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\ngoogle.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=\ngoogle.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=\ngoogle.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "broker-service/logs/logs.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.27.1\n// \tprotoc        v3.19.4\n// source: logs/logs.proto\n\npackage logs\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Log struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tData string `protobuf:\"bytes,2,opt,name=data,proto3\" json:\"data,omitempty\"`\n}\n\nfunc (x *Log) Reset() {\n\t*x = Log{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_logs_logs_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Log) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Log) ProtoMessage() {}\n\nfunc (x *Log) ProtoReflect() protoreflect.Message {\n\tmi := &file_logs_logs_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Log.ProtoReflect.Descriptor instead.\nfunc (*Log) Descriptor() ([]byte, []int) {\n\treturn file_logs_logs_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Log) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Log) GetData() string {\n\tif x != nil {\n\t\treturn x.Data\n\t}\n\treturn \"\"\n}\n\ntype LogRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tLogEntry *Log `protobuf:\"bytes,1,opt,name=logEntry,proto3\" json:\"logEntry,omitempty\"`\n}\n\nfunc (x *LogRequest) Reset() {\n\t*x = LogRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_logs_logs_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *LogRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*LogRequest) ProtoMessage() {}\n\nfunc (x *LogRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_logs_logs_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use LogRequest.ProtoReflect.Descriptor instead.\nfunc (*LogRequest) Descriptor() ([]byte, []int) {\n\treturn file_logs_logs_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *LogRequest) GetLogEntry() *Log {\n\tif x != nil {\n\t\treturn x.LogEntry\n\t}\n\treturn nil\n}\n\ntype LogResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tResult string `protobuf:\"bytes,1,opt,name=result,proto3\" json:\"result,omitempty\"`\n}\n\nfunc (x *LogResponse) Reset() {\n\t*x = LogResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_logs_logs_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *LogResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*LogResponse) ProtoMessage() {}\n\nfunc (x *LogResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_logs_logs_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use LogResponse.ProtoReflect.Descriptor instead.\nfunc (*LogResponse) Descriptor() ([]byte, []int) {\n\treturn file_logs_logs_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *LogResponse) GetResult() string {\n\tif x != nil {\n\t\treturn x.Result\n\t}\n\treturn \"\"\n}\n\nvar File_logs_logs_proto protoreflect.FileDescriptor\n\nvar file_logs_logs_proto_rawDesc = []byte{\n\t0x0a, 0x0f, 0x6c, 0x6f, 0x67, 0x73, 0x2f, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x12, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x22, 0x2d, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12,\n\t0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,\n\t0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,\n\t0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x33, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71,\n\t0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79,\n\t0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c, 0x6f,\n\t0x67, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x25, 0x0a, 0x0b, 0x4c,\n\t0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65,\n\t0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75,\n\t0x6c, 0x74, 0x32, 0x3f, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,\n\t0x12, 0x31, 0x0a, 0x08, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x12, 0x10, 0x2e, 0x6c,\n\t0x6f, 0x67, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11,\n\t0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,\n\t0x65, 0x22, 0x00, 0x42, 0x07, 0x5a, 0x05, 0x2f, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72,\n\t0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_logs_logs_proto_rawDescOnce sync.Once\n\tfile_logs_logs_proto_rawDescData = file_logs_logs_proto_rawDesc\n)\n\nfunc file_logs_logs_proto_rawDescGZIP() []byte {\n\tfile_logs_logs_proto_rawDescOnce.Do(func() {\n\t\tfile_logs_logs_proto_rawDescData = protoimpl.X.CompressGZIP(file_logs_logs_proto_rawDescData)\n\t})\n\treturn file_logs_logs_proto_rawDescData\n}\n\nvar file_logs_logs_proto_msgTypes = make([]protoimpl.MessageInfo, 3)\nvar file_logs_logs_proto_goTypes = []interface{}{\n\t(*Log)(nil),         // 0: logs.Log\n\t(*LogRequest)(nil),  // 1: logs.LogRequest\n\t(*LogResponse)(nil), // 2: logs.LogResponse\n}\nvar file_logs_logs_proto_depIdxs = []int32{\n\t0, // 0: logs.LogRequest.logEntry:type_name -> logs.Log\n\t1, // 1: logs.LogService.WriteLog:input_type -> logs.LogRequest\n\t2, // 2: logs.LogService.WriteLog:output_type -> logs.LogResponse\n\t2, // [2:3] is the sub-list for method output_type\n\t1, // [1:2] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_logs_logs_proto_init() }\nfunc file_logs_logs_proto_init() {\n\tif File_logs_logs_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_logs_logs_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Log); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_logs_logs_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*LogRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_logs_logs_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*LogResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_logs_logs_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   3,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_logs_logs_proto_goTypes,\n\t\tDependencyIndexes: file_logs_logs_proto_depIdxs,\n\t\tMessageInfos:      file_logs_logs_proto_msgTypes,\n\t}.Build()\n\tFile_logs_logs_proto = out.File\n\tfile_logs_logs_proto_rawDesc = nil\n\tfile_logs_logs_proto_goTypes = nil\n\tfile_logs_logs_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "broker-service/logs/logs.proto",
    "content": "syntax = \"proto3\";\n\npackage logs;\n\noption go_package = \"/logs\";\n\nmessage Log{\n  string name = 1;\n  string data =2;\n}\n\nmessage LogRequest {\n  Log logEntry = 1;\n}\n\nmessage LogResponse{\n  string result = 1;\n}\n\nservice LogService{\n  rpc WriteLog(LogRequest) returns (LogResponse) {}\n}\n"
  },
  {
    "path": "broker-service/logs/logs_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.2.0\n// - protoc             v3.19.4\n// source: logs/logs.proto\n\npackage logs\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// LogServiceClient is the client API for LogService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype LogServiceClient interface {\n\tWriteLog(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error)\n}\n\ntype logServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewLogServiceClient(cc grpc.ClientConnInterface) LogServiceClient {\n\treturn &logServiceClient{cc}\n}\n\nfunc (c *logServiceClient) WriteLog(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error) {\n\tout := new(LogResponse)\n\terr := c.cc.Invoke(ctx, \"/logs.LogService/WriteLog\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// LogServiceServer is the server API for LogService service.\n// All implementations must embed UnimplementedLogServiceServer\n// for forward compatibility\ntype LogServiceServer interface {\n\tWriteLog(context.Context, *LogRequest) (*LogResponse, error)\n\tmustEmbedUnimplementedLogServiceServer()\n}\n\n// UnimplementedLogServiceServer must be embedded to have forward compatible implementations.\ntype UnimplementedLogServiceServer struct {\n}\n\nfunc (UnimplementedLogServiceServer) WriteLog(context.Context, *LogRequest) (*LogResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method WriteLog not implemented\")\n}\nfunc (UnimplementedLogServiceServer) mustEmbedUnimplementedLogServiceServer() {}\n\n// UnsafeLogServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to LogServiceServer will\n// result in compilation errors.\ntype UnsafeLogServiceServer interface {\n\tmustEmbedUnimplementedLogServiceServer()\n}\n\nfunc RegisterLogServiceServer(s grpc.ServiceRegistrar, srv LogServiceServer) {\n\ts.RegisterService(&LogService_ServiceDesc, srv)\n}\n\nfunc _LogService_WriteLog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(LogRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(LogServiceServer).WriteLog(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/logs.LogService/WriteLog\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(LogServiceServer).WriteLog(ctx, req.(*LogRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// LogService_ServiceDesc is the grpc.ServiceDesc for LogService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar LogService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"logs.LogService\",\n\tHandlerType: (*LogServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"WriteLog\",\n\t\t\tHandler:    _LogService_WriteLog_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"logs/logs.proto\",\n}\n"
  },
  {
    "path": "broker-service.dockerfile",
    "content": "# The base go-image\nFROM alpine:latest\nRUN mkdir /app\n\nCOPY broker-service/brokerApp /app\n\n# Run the server executable\nCMD [ \"/app/brokerApp\" ]"
  },
  {
    "path": "caddy.dockerfile",
    "content": "FROM caddy:2.4.6-alpine\n\nCOPY Caddyfile /etc/caddy/Caddyfile\n"
  },
  {
    "path": "db-data/.gitignore",
    "content": "*\n!*/\n!.gitignore\n!.gitkeep"
  },
  {
    "path": "db-data/.gitkeep",
    "content": ""
  },
  {
    "path": "docker-compose.yml",
    "content": "version: '3'\n\nservices:\n\n#  # broker-service - main entry point; we call this from the front end\n#  broker-service:\n#    build:\n#      context: .\n#      dockerfile: ./broker-service.dockerfile\n#    restart: always\n#    ports:\n#      - \"8080:80\"\n#    deploy:\n#      mode: replicated\n#      replicas: 1\n#    environment:\n#      RABBIT_URL: \"amqp://guest:guest@rabbitmq\"\n#\n#  # listener-service - watches rabbitmq for messages\n#  listener-service:\n#    build:\n#      context: .\n#      dockerfile: ./listener-service.dockerfile\n#    deploy:\n#      mode: replicated\n#      replicas: 1\n#    environment:\n#      RABBIT_URL: \"amqp://guest:guest@rabbitmq\"\n#\n#  # authentication-service - handles user auth\n#  authentication-service:\n#    build:\n#      context: .\n#      dockerfile: ./authentication-service.dockerfile\n#    restart: always\n#    ports:\n#      - \"8081:80\"\n#    deploy:\n#      mode: replicated\n#      replicas: 1\n#    environment:\n#      DSN: \"host=postgres port=5432 user=postgres password=password dbname=users sslmode=disable timezone=UTC connect_timeout=5\"\n#\n#  # logger-service: a service to store logs\n#  logger-service:\n#    build:\n#      context: .\n#      dockerfile: ./logger-service.dockerfile\n#    restart: always\n#    ports:\n#      - \"8082:80\"\n#    deploy:\n#      mode: replicated\n#      replicas: 1\n#    volumes:\n#      - ./logger-service/templates/:/app/templates\n#\n#  # mail-service - handles sending mail\n#  mail-service:\n#    build:\n#      context: .\n#      dockerfile: ./mail-service.dockerfile\n#    restart: always\n#    deploy:\n#      mode: replicated\n#      replicas: 1\n#    environment:\n#      MAIL_DOMAIN: localhost\n#      MAIL_HOST: mailhog\n#      MAIL_PORT: 1025\n#      MAIL_ENCRYPTION: none\n#      MAIL_USERNAME: \"\"\n#      MAIL_PASSWORD: \"\"\n#      FROM_NAME: \"John Smith\"\n#      FROM_ADDRESS: john.smith@example.com\n#\n#  # rabbitmq: the rabbitmq server\n#  rabbitmq:\n#    image: 'rabbitmq:3.9-alpine'\n#    ports:\n#      - \"5672:5672\"\n#    deploy:\n#      mode: replicated\n#      replicas: 1\n#    volumes:\n#      - ./db-data/rabbitmq/:/var/lib/rabbitmq/\n#\n#  # mailhog: a fake smtp server with a web interface\n#  mailhog:\n#    image: 'mailhog/mailhog:latest'\n#    ports:\n#      - \"1025:1025\"\n#      - \"8025:8025\"\n#    deploy:\n#      mode: replicated\n#      replicas: 1\n#\n  # mongo: start MongoDB and ensure that data is stored to a mounted volume\n  mongo:\n    image: 'mongo:4.2.17-bionic'\n    ports:\n      - \"27017:27017\"\n#    restart: always\n    deploy:\n      mode: replicated\n      replicas: 1\n    environment:\n      MONGO_INITDB_DATABASE: logs\n      MONGO_INITDB_ROOT_USERNAME: admin\n      MONGO_INITDB_ROOT_PASSWORD: password\n    volumes:\n      - ./db-data/mongo/:/data/db\n\n  # postgres: start Postgres, and ensure that data is stored to a mounted volume\n  postgres:\n    image: 'postgres:14.2'\n    ports:\n      - \"5432:5432\"\n    restart: always\n    deploy:\n      mode: replicated\n      replicas: 1\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: password\n      POSTGRES_DB: users\n    volumes:\n      - ./db-data/postgres/:/var/lib/postgresql/data/\n\n  # etcd: start etcd server\n#  etcd:\n#    image: docker.io/bitnami/etcd:3\n#    environment:\n#      - ALLOW_NONE_AUTHENTICATION=yes\n#    deploy:\n#      mode: replicated\n#      replicas: 1\n#    volumes:\n#      - ./db-data/etcd/:/bitnami/etcd\n\n"
  },
  {
    "path": "front-end/cmd/web/main.go",
    "content": "package main\n\nimport (\n\t\"embed\"\n\t\"fmt\"\n\t\"html/template\"\n\t\"log\"\n\t\"net/http\"\n)\n\nfunc main() {\n\t// the handler to display our page\n\thttp.HandleFunc(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\trender(w, \"test.page.gohtml\")\n\t})\n\n\t// start the web server\n\tfmt.Println(\"Starting front end service on port 80\")\n\terr := http.ListenAndServe(\":80\", nil)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\n//go:embed templates\nvar templateFS embed.FS\n\n// render generates a page of html from our template files\nfunc render(w http.ResponseWriter, t string) {\n\t// all the required templates for any page\n\tpartials := []string{\n\t\t\"templates/base.layout.gohtml\",\n\t\t\"templates/header.partial.gohtml\",\n\t\t\"templates/footer.partial.gohtml\",\n\t}\n\n\t// append the template we received as a parameter\n\tvar templateSlice []string\n\ttemplateSlice = append(templateSlice, fmt.Sprintf(\"templates/%s\", t))\n\n\tfor _, x := range partials {\n\t\ttemplateSlice = append(templateSlice, x)\n\t}\n\n\t// parse the templates\n\t//tmpl, err := template.ParseFiles(templateSlice...)\n\ttmpl, err := template.ParseFS(templateFS, templateSlice...)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// execute the template\n\tif err := tmpl.Execute(w, nil); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t}\n}\n"
  },
  {
    "path": "front-end/cmd/web/templates/base.layout.gohtml",
    "content": "{{define \"base\" }}\n    <!doctype html>\n    <html lang=\"en\">\n\n    {{template \"header\" .}}\n\n    <body>\n\n    {{block \"content\" .}}\n\n    {{end}}\n\n    {{block \"js\" .}}\n\n    {{end}}\n\n    {{template \"footer\" .}}\n\n    </body>\n    </html>\n\n{{end}}"
  },
  {
    "path": "front-end/cmd/web/templates/footer.partial.gohtml",
    "content": "{{define \"footer\"}}\n    <div class=\"container\">\n        <div class=\"row\">\n            <div class=\"col text-center\">\n                <hr>\n                <small class=\"text-muted\">Copyright &copy; GoCode.ca</small>\n            </div>\n        </div>\n    </div>\n{{end}}"
  },
  {
    "path": "front-end/cmd/web/templates/header.partial.gohtml",
    "content": "{{define \"header\"}}\n\n    <head>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\"\n              content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n        <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n        <title>Microservices in Go</title>\n        <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css\" rel=\"stylesheet\" integrity=\"sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3\" crossorigin=\"anonymous\">\n\n    </head>\n\n{{end}}"
  },
  {
    "path": "front-end/cmd/web/templates/test.page.gohtml",
    "content": "{{template \"base\" .}}\n\n{{define \"content\" }}\n    <div class=\"container\">\n        <div class=\"row\">\n            <div class=\"col\">\n                <h1 class=\"mt-5\">Test Go microservices</h1>\n                <hr>\n                <a id=\"brokerBtn\" class=\"btn btn-outline-secondary\" href=\"javascript:void(0)\">Test broker</a>\n                <a id=\"authBrokerBtn\" class=\"btn btn-outline-secondary\" href=\"javascript:void(0)\">Test auth</a>\n                <a id=\"mailBtn\" class=\"btn btn-outline-secondary\" href=\"javascript:void(0)\">Test email</a>\n                <a id=\"logBtn\" class=\"btn btn-outline-secondary\" href=\"javascript:void(0)\">Test log</a>\n                <a id=\"logGBtn\" class=\"btn btn-outline-secondary\" href=\"javascript:void(0)\">Test gRPC log</a>\n                <div id=\"output\" class=\"mt-5\" style=\"outline: 1px solid silver; padding: 2em;\">\n                    <span class=\"text-muted\">Output shows here...</span>\n                </div>\n            </div>\n        </div>\n        <div class=\"row\">\n            <div class=\"col\">\n                <h4 class=\"mt-5\">Sent</h4>\n                <div class=\"mt-1\" style=\"outline: 1px solid silver; padding: 2em;\">\n                    <pre id=\"payload\"><span class=\"text-muted\">Nothing sent yet...</span></pre>\n                </div>\n            </div>\n            <div class=\"col\">\n                <h4 class=\"mt-5\">Received</h4>\n                <div class=\"mt-1\" style=\"outline: 1px solid silver; padding: 2em;\">\n                    <pre id=\"received\"><span class=\"text-muted\">Nothing received yet...</span></pre>\n                </div>\n            </div>\n        </div>\n    </div>\n{{end}}\n\n{{define \"js\"}}\n    <script>\n        let brokerBtn = document.getElementById(\"brokerBtn\");\n        let authBrokerBtn = document.getElementById(\"authBrokerBtn\");\n        let output = document.getElementById(\"output\");\n        let mailBtn = document.getElementById(\"mailBtn\");\n        let logBtn = document.getElementById(\"logBtn\");\n        let logGBtn = document.getElementById(\"logGBtn\");\n        let sent = document.getElementById(\"payload\")\n        let received = document.getElementById(\"received\")\n\n        brokerBtn.addEventListener(\"click\", function () {\n            started();\n\n            const body = {\n                method: 'POST',\n            }\n\n            // send request to main entrypoint for microservices\n            fetch(\"http:\\/\\/localhost:8080\", body)\n                .then((response) => response.json())\n                .then((data) => {\n                    sent.innerHTML = \"empty post request\";\n                    received.innerHTML = JSON.stringify(data, undefined, 4);\n                    if (data.error) {\n                        console.log(data.error)\n                    } else {\n                        output.innerHTML += `<br><strong>Response from broker service</strong>: ${data.message}`;\n                    }\n                    ended();\n                })\n                .catch((error) => {\n                    output.innerHTML += \"<br><br><strong>Error: \" + error;\n                })\n        })\n\n        mailBtn.addEventListener(\"click\", function () {\n            started();\n            const payload = {\n                action: \"mail\",\n                mail: {\n                    from: \"me@here.com\",\n                    to: \"you@there.com\",\n                    subject: \"Test Email\",\n                    message: \"Hello, world! This is my test message.\"\n                }\n            }\n\n            const headers = new Headers();\n            headers.append(\"Content-Type\", \"application/json\");\n\n            const body = {\n                method: 'POST',\n                body: JSON.stringify(payload),\n                headers: headers,\n            }\n\n            // send request to main entrypoint for microservices\n            fetch(\"http:\\/\\/localhost:8080/handle\", body)\n                .then((response) => response.json())\n                .then((data) => {\n                    sent.innerHTML = JSON.stringify(payload, undefined, 4);\n                    received.innerHTML = JSON.stringify(data, undefined, 4);\n                    if (data.error) {\n                        console.log(data.error)\n                        output.innerHTML += \"<br><br><strong>Error: \" + data.message;\n                    } else {\n                        output.innerHTML += `<br><strong>Response from mail service</strong>: ${data.message}`;\n                    }\n                    ended();\n                })\n                .catch((error) => {\n                    output.innerHTML += \"<br><br><strong>Error: \" + error;\n                })\n        })\n\n        authBrokerBtn.addEventListener(\"click\", function () {\n            started();\n\n            const payload = {\n                action: \"auth\",\n                auth: {\n                    email: \"trevor.sawler@gmail.com\",\n                    password: \"verysecret\",\n                }\n            }\n\n            const headers = new Headers();\n            headers.append(\"Content-Type\", \"application/json\");\n\n            const body = {\n                method: 'POST',\n                body: JSON.stringify(payload),\n                headers: headers,\n            }\n\n            fetch(\"http:\\/\\/localhost:8080/handle\", body)\n                .then(response => response.json())\n                .then(data => {\n                    sent.innerHTML = JSON.stringify(payload, undefined, 4);\n                    received.innerHTML = JSON.stringify(data, undefined, 4);\n                    if (data.error) {\n                        output.innerHTML += `<br><strong>Error:</strong> ${data.message}`;\n                    } else {\n                        output.innerHTML += `<br><strong>Response from auth service:</strong> ${data.message}`;\n                        output.innerHTML += `<br>Authenticated user ${data.data.first_name}`;\n                    }\n                    ended();\n                })\n                .catch(error => {\n                    output.innerHTML += \"<br><br><strong>Error: \" + error;\n                })\n        })\n\n        logBtn.addEventListener(\"click\", function () {\n            started();\n\n            const payload = {\n                action: \"log\",\n                log: {\n                    name: \"event\",\n                    data: \"Some kind of data\",\n                }\n            }\n\n            const headers = new Headers();\n            headers.append(\"Content-Type\", \"application/json\");\n\n            const body = {\n                method: 'POST',\n                body: JSON.stringify(payload),\n                headers: headers,\n            }\n\n            fetch(`http://localhost:8080/handle`, body)\n                .then(response => response.json())\n                .then(data => {\n                    sent.innerHTML = JSON.stringify(payload, undefined, 4);\n                    received.innerHTML = JSON.stringify(data, undefined, 4);\n                    if (data.error) {\n                        output.innerHTML += `<br><strong>Error:</strong> ${data.message}`;\n                    } else {\n                        output.innerHTML += `<br><strong>Response from log service:</strong> ${data.message}`;\n                    }\n                    ended();\n                })\n                .catch(error => {\n                    output.innerHTML += \"<br><br><strong>Error: \" + error;\n                })\n        })\n\n        logGBtn.addEventListener(\"click\", function () {\n            started();\n\n            const payload = {\n                action: \"log\",\n                log: {\n                    name: \"event\",\n                    data: \"Some kind of gRPC data\",\n                }\n            }\n\n            const headers = new Headers();\n            headers.append(\"Content-Type\", \"application/json\");\n\n            const body = {\n                method: 'POST',\n                body: JSON.stringify(payload),\n                headers: headers,\n            }\n\n            fetch(`http://localhost:8080/log-grpc`, body)\n                .then(response => response.json())\n                .then(data => {\n                    sent.innerHTML = JSON.stringify(payload, undefined, 4);\n                    received.innerHTML = JSON.stringify(data, undefined, 4);\n                    if (data.error) {\n                        output.innerHTML += `<br><strong>Error:</strong> ${data.message}`;\n                    } else {\n                        output.innerHTML += `<br><strong>Response from log service:</strong> ${data.message}`;\n                    }\n                    ended();\n                })\n                .catch(error => {\n                    output.innerHTML += \"<br><br><strong>Error: \" + error;\n                })\n        })\n\n        function ended() {\n            let now = new Date();\n            output.innerHTML += `<br><strong class=\"text-danger\">Ended:</strong> ${now}...<br>`;\n        }\n\n        function started() {\n            let now = new Date();\n            output.innerHTML = `<strong class=\"text-success\">Started:</strong> ${now}...<br><em>Sending request...</em>`;\n            received.innerHTML = `<span class=\"text-muted\">Nothing received yet...</span>`;\n        }\n\n    </script>\n{{end}}\n"
  },
  {
    "path": "front-end/go.mod",
    "content": "module frontend\n\ngo 1.18\n"
  },
  {
    "path": "front-end.dockerfile",
    "content": "FROM alpine:latest\nRUN mkdir /app\n\nCOPY front-end/frontEndLinux /app\n\n# Run the server executable\nCMD [ \"/app/frontEndLinux\" ]"
  },
  {
    "path": "ingress.yml",
    "content": "apiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: example-ingress\n  annotations:\n    nginx.ingress.kubernetes.io/rewrite-target: /$1\nspec:\n  rules:\n    - host: broker-service.local\n      http:\n        paths:\n          - path: /\n            pathType: Prefix\n            backend:\n              service:\n                name: broker-service\n                port:\n                  number: 8080\n\n"
  },
  {
    "path": "k8s/authentication.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: authentication-service\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: authentication-service\n  template:\n    metadata:\n      labels:\n        app: authentication-service\n    spec:\n      containers:\n      - name: authentication-service\n        image: \"tsawler/authentication-service:1.0.0\"\n        env:\n          - name: DSN\n            value: \"host=host.minikube.internal port=5432 user=postgres password=password dbname=users sslmode=disable timezone=UTC connect_timeout=5\"\n        resources:\n          limits:\n            memory: \"128Mi\"\n            cpu: \"500m\"\n        ports:\n          - containerPort: 80\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: authentication-service\nspec:\n  selector:\n    app: authentication-service\n  ports:\n    - protocol: TCP\n      port: 80\n      targetPort: 80"
  },
  {
    "path": "k8s/broker.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: broker-service\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: broker-service\n  template:\n    metadata:\n      labels:\n        app: broker-service\n    spec:\n      containers:\n      - name: broker-service\n        image: \"tsawler/broker-service:1.0.1\"\n        env:\n          - name: RABBIT_URL\n            value: \"amqp://guest:guest@rabbitmq\"\n        resources:\n          limits:\n            memory: \"128Mi\"\n            cpu: \"500m\"\n        ports:\n          - containerPort: 80\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: broker-service\nspec:\n  selector:\n    app: broker-service\n  ports:\n    - protocol: TCP\n      port: 80\n      name: web-port\n      targetPort: 80\n\n"
  },
  {
    "path": "k8s/listener.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: listener\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: listener\n  template:\n    metadata:\n      labels:\n        app: listener\n    spec:\n      containers:\n      - name: listener\n        image: \"tsawler/listener-service:1.0.0\"\n        env:\n          - name: RABBIT_URL\n            value: \"amqp://guest:guest@rabbitmq\"\n        ports:\n          - containerPort: 80\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: listener\nspec:\n  selector:\n    app: listener\n  ports:\n    - protocol: TCP\n      port: 80\n      targetPort: 80"
  },
  {
    "path": "k8s/logger.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: logger-service\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: logger-service\n  template:\n    metadata:\n      labels:\n        app: logger-service\n    spec:\n      containers:\n      - name: logger\n        image: \"tsawler/logger-service:1.0.1\"\n        env:\n          - name: RABBIT_URL\n            value: \"amqp://guest:guest@rabbitmq\"\n        ports:\n          - containerPort: 80\n          - containerPort: 5001\n          - containerPort: 50001\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: logger-service\nspec:\n  selector:\n    app: logger-service\n  ports:\n    - protocol: TCP\n      port: 80\n      name: web-port\n      targetPort: 80\n    - protocol: TCP\n      port: 5001\n      name: rpc-port\n      targetPort: 5001\n    - protocol:\n      port: 50001\n      name: grpc-port\n      targetPort: 50001"
  },
  {
    "path": "k8s/mail.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: mail-service\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: mail-service\n  template:\n    metadata:\n      labels:\n        app: mail-service\n    spec:\n      containers:\n      - name: mail-service\n        image: \"tsawler/mail-service:1.0.0\"\n        env:\n          - name: MAIL_DOMAIN\n            value: \"localhost\"\n          - name: MAIL_HOST\n            value: \"mailhog\"\n          - name: MAIL_PORT\n            value: \"1025\"\n          - name: MAIL_ENCRYPTION\n            value: \"localhost\"\n          - name: MAIL_USERNAME\n            value: \"\"\n          - name: MAIL_PASSWORD\n            value: \"\"\n          - name: FROM_NAME\n            value: \"John Smith\"\n          - name: FROM_ADDRESS\n            value: \"john.smith@example.com\"\n        ports:\n          - containerPort: 80\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: mail-service\nspec:\n  selector:\n    app: mail-service\n  ports:\n    - protocol: TCP\n      port: 80\n      targetPort: 80"
  },
  {
    "path": "k8s/mailhog.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: mailhog\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: mailhog\n  template:\n    metadata:\n      labels:\n        app: mailhog\n    spec:\n      containers:\n      - name: mailhog\n        image: \"mailhog/mailhog:latest\"\n        ports:\n          - containerPort: 1025\n          - containerPort: 8025\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: mailhog\nspec:\n  selector:\n    app: mailhog\n  ports:\n    - protocol: TCP\n      name: smtp-port\n      port: 1025\n      targetPort: 1025\n    - protocol: TCP\n      name: web-port\n      port: 8025\n      targetPort: 8025"
  },
  {
    "path": "k8s/mongo.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: mongo\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: mongo\n  template:\n    metadata:\n      labels:\n        app: mongo\n    spec:\n      containers:\n      - name: mongo\n        image: \"mongo:4.2.17-bionic\"\n        env:\n          - name: MONGO_INITDB_DATABASE\n            value: \"logs\"\n          - name: MONGO_INITDB_ROOT_USERNAME\n            value: \"admin\"\n          - name: MONGO_INITDB_ROOT_PASSWORD\n            value: \"password\"\n        ports:\n          - containerPort: 27017\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: mongo\nspec:\n  selector:\n    app: mongo\n  ports:\n    - protocol: TCP\n      name: main-port\n      port: 27017\n      targetPort: 27017\n"
  },
  {
    "path": "k8s/rabbitmq.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: rabbitmq\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: rabbitmq\n  template:\n    metadata:\n      labels:\n        app: rabbitmq\n    spec:\n      containers:\n      - name: rabbitmq\n        image: \"rabbitmq:3.9-alpine\"\n        ports:\n          - containerPort: 5672\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: rabbitmq\nspec:\n  selector:\n    app: rabbitmq\n  ports:\n    - protocol: TCP\n      port: 5672\n      targetPort: 5672\n"
  },
  {
    "path": "k8s.md",
    "content": "# K8S\n\n```\nminikube start --nodes=2\n\nminikube status\ndocker ps\nkubectl get nodes\nkubectl get pods -A\nkubectl get pods\nkubectl apply -f deployment.yml # or kubectl apply -f <folder>\nkubectl get pods\nkubectl get svc\nkubectl get deployments\nminikube dashboard\n```\n\n# hit the app\n```\nkubectl expose deployment broker --type=LoadBalancer --port=8080 --target-port=80\nminikube tunnel\n```\n\n# stop service/app\n```\nkubectl delete deployments <deployment>\nkubectl delete services <service>\n```\n\n# stop minikube\n```\nminikube stop\n```\n"
  },
  {
    "path": "listener-service/go.mod",
    "content": "module github.com/tsawler/go-rabbit\n\ngo 1.18\n\nrequire github.com/rabbitmq/amqp091-go v1.7.0\n"
  },
  {
    "path": "listener-service/go.sum",
    "content": "github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rabbitmq/amqp091-go v1.3.0 h1:A/QuHiNw7LMCJsxx9iZn5lrIz6OrhIn7Dfk5/1YatWM=\ngithub.com/rabbitmq/amqp091-go v1.3.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=\ngithub.com/rabbitmq/amqp091-go v1.3.2 h1:zezKg1S58+q/9Ej7DIqFL6TP6NGMyGPb4ykEm4n94cY=\ngithub.com/rabbitmq/amqp091-go v1.3.2/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=\ngithub.com/rabbitmq/amqp091-go v1.7.0 h1:V5CF5qPem5OGSnEo8BoSbsDGwejg6VUJsKEdneaoTUo=\ngithub.com/rabbitmq/amqp091-go v1.7.0/go.mod h1:wfClAtY0C7bOHxd3GjmF26jEHn+rR/0B3+YV+Vn9/NI=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "listener-service/lib/event/consumer.go",
    "content": "package event\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/rpc\"\n\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\n// Consumer is the type used for receiving AMPQ events\ntype Consumer struct {\n\tconn      *amqp.Connection\n\tqueueName string\n}\n\n// NewConsumer returns a new Consumer\nfunc NewConsumer(conn *amqp.Connection) (Consumer, error) {\n\tconsumer := Consumer{\n\t\tconn: conn,\n\t}\n\terr := consumer.setup()\n\tif err != nil {\n\t\treturn Consumer{}, err\n\t}\n\n\treturn consumer, nil\n}\n\n// setup opens a channel and declares the exchange\nfunc (consumer *Consumer) setup() error {\n\tchannel, err := consumer.conn.Channel()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn declareExchange(channel)\n}\n\n// Payload is the type used for pushing events to RabbitMQ\ntype Payload struct {\n\tName string `json:\"name\"`\n\tData string `json:\"data\"`\n}\n\n// Listen will listen for all new queue publications\nfunc (consumer *Consumer) Listen(topics []string) error {\n\tch, err := consumer.conn.Channel()\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer ch.Close()\n\n\tq, err := declareRandomQueue(ch)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, s := range topics {\n\t\terr = ch.QueueBind(\n\t\t\tq.Name,\n\t\t\ts,\n\t\t\tgetExchangeName(),\n\t\t\tfalse,\n\t\t\tnil,\n\t\t)\n\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t\treturn err\n\t\t}\n\t}\n\n\tmessages, err := ch.Consume(q.Name, \"\", true, false, false, false, nil)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tforever := make(chan bool)\n\tgo func() {\n\t\tfor d := range messages {\n\t\t\t// get the JSON payload and unmarshal it into a variable\n\t\t\tvar payload Payload\n\t\t\t_ = json.Unmarshal(d.Body, &payload)\n\n\t\t\t// do something with the payload\n\t\t\tgo handlePayload(payload)\n\t\t}\n\t}()\n\n\tlog.Printf(\"[*] Waiting for message [Exchange, Queue][%s, %s].\", getExchangeName(), q.Name)\n\t<-forever\n\treturn nil\n}\n\n// handlePayload takes an action based on the name of an event in the queue\nfunc handlePayload(payload Payload) {\n\t// logic to process payload goes in here\n\tswitch payload.Name {\n\tcase \"broker_hit\":\n\t\t// just a test to make sure everything works\n\t\tres, err := rpcPushToLogger(\"LogInfo\", payload)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t}\n\t\tfmt.Println(\"Response from RPC:\", res)\n\n\tcase \"auth\", \"authentication\":\n\t\t// we are trying to authenticate someone\n\t\terr := authenticate(payload)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t}\n\n\t// you can have as many cases as you want here, but naturally you'll have to write the logic\n\t// to connect to a given microservice\n\n\tdefault:\n\t\t// log whatever we get\n\t\tres, err := rpcPushToLogger(\"LogInfo\", payload)\n\t\tif err != nil {\n\t\t\tlog.Println(err)\n\t\t}\n\t\tfmt.Println(\"Response from RPC:\", res)\n\t}\n}\n\n// rpcPushToLogger pushes data to the logger-service via RPC, where\n// it gets stored into a mongo database\nfunc rpcPushToLogger(function string, data any) (string, error) {\n\tc, err := rpc.Dial(\"tcp\", \"logger-service:5001\")\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn \"\", err\n\t}\n\n\tfmt.Println(\"Connected via rpc...\")\n\tvar result string\n\terr = c.Call(\"RPCServer.\"+function, data, &result)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn result, nil\n}\n\n// authenticate is a stub that we'll never actually use, but it is here\n// as we get used to how to interact with services\nfunc authenticate(payload Payload) error {\n\t// TODO actually authenticate via JSON\n\tlog.Printf(\"Got payload of %v\", payload)\n\treturn nil\n}\n\nfunc logEvent(entry Payload) error {\n\tjsonData, _ := json.MarshalIndent(entry, \"\", \"\\t\")\n\n\tlogServiceURL := \"http://logger-service/log\"\n\n\trequest, err := http.NewRequest(\"POST\", logServiceURL, bytes.NewBuffer(jsonData))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\tclient := &http.Client{}\n\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer response.Body.Close()\n\n\tif response.StatusCode != http.StatusAccepted {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "listener-service/lib/event/event.go",
    "content": "package event\n\nimport (\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n)\n\nfunc getExchangeName() string {\n\treturn \"logs_topic\"\n}\n\n// declareRandomQueue just creates a random queue\nfunc declareRandomQueue(ch *amqp.Channel) (amqp.Queue, error) {\n\treturn ch.QueueDeclare(\n\t\t\"\",    // name\n\t\tfalse, // durable\n\t\tfalse, // delete when unused\n\t\ttrue,  // exclusive\n\t\tfalse, // no-wait\n\t\tnil,   // arguments\n\t)\n}\n\n// declareExchange creates an exchange, with a name\nfunc declareExchange(ch *amqp.Channel) error {\n\treturn ch.ExchangeDeclare(\n\t\tgetExchangeName(), // name\n\t\t\"topic\",           // type\n\t\ttrue,              // durable\n\t\tfalse,             // auto-deleted\n\t\tfalse,             // internal\n\t\tfalse,             // no-wait\n\t\tnil,               // arguments\n\t)\n}\n"
  },
  {
    "path": "listener-service/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\tamqp \"github.com/rabbitmq/amqp091-go\"\n\t\"github.com/tsawler/go-rabbit/lib/event\"\n\t\"log\"\n\t\"math\"\n\t\"os\"\n\t\"time\"\n)\n\nfunc main() {\n\t// try to connect to RabbitMQ\n\trabbitConn, err := connect()\n\tif err != nil {\n\t\tlog.Println(err)\n\t\tos.Exit(1)\n\t}\n\tdefer rabbitConn.Close()\n\n\t// start listening for messages\n\tlog.Println(\"Listening for and consuming RabbitMQ messages...\")\n\n\t// create a new consumer\n\tconsumer, err := event.NewConsumer(rabbitConn)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\t// consumer.Listen watches the queue and consumes events for all the provided topics.\n\terr = consumer.Listen([]string{\"log.INFO\", \"log.WARNING\", \"log.ERROR\"})\n\tif err != nil {\n\t\tlog.Println(err)\n\t}\n}\n\n// connect tries to connect to RabbitMQ, and delays between attempts.\n// If we can't connect after 5 tries (with increasing delays), return an error\nfunc connect() (*amqp.Connection, error) {\n\tvar counts int64\n\tvar backOff = 1 * time.Second\n\tvar connection *amqp.Connection\n\tvar rabbitURL = os.Getenv(\"RABBIT_URL\")\n\n\t// don't continue until rabbitmq is ready\n\tfor {\n\t\tc, err := amqp.Dial(rabbitURL)\n\t\tif err != nil {\n\t\t\tfmt.Println(\"RabbitMQ not yet ready...\")\n\t\t\tcounts++\n\t\t} else {\n\t\t\t// we have a connection to rabbitmq, so set connection = c and break out of\n\t\t\t// this loop\n\t\t\tconnection = c\n\t\t\tfmt.Println()\n\t\t\tbreak\n\t\t}\n\n\t\tif counts > 5 {\n\t\t\t// if we can't connect after five tries, something is wrong...\n\t\t\tfmt.Println(err)\n\t\t\treturn nil, err\n\t\t}\n\t\tfmt.Printf(\"Backing off for %d seconds...\\n\", int(math.Pow(float64(counts), 2)))\n\t\tbackOff = time.Duration(math.Pow(float64(counts), 2)) * time.Second\n\t\ttime.Sleep(backOff)\n\t\tcontinue\n\t}\n\treturn connection, nil\n}\n"
  },
  {
    "path": "listener-service.dockerfile",
    "content": "FROM alpine:latest\nRUN mkdir /app\n\nCOPY listener-service/listener /app\n\n# Run the server executable\nCMD [ \"/app/listener\" ]"
  },
  {
    "path": "logger-service/Makefile",
    "content": "gen:\n\tprotoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative logs/logs.proto\n"
  },
  {
    "path": "logger-service/cmd/web/discover.go",
    "content": "package main\n\n//// registerService registers the correct entry for this service in etcd\n//func (app *Config) registerService() {\n//\t// get a connection to etcd\n//\tcli, _ := connectToEtcd()\n//\tkv := clientv3.NewKV(cli)\n//\n//\tapp.Etcd = cli\n//\n//\t// create a lease that lasts 10 seconds\n//\tlease := clientv3.NewLease(cli)\n//\tgrantResp, err := lease.Grant(context.TODO(), 10)\n//\tif err != nil {\n//\t\tlog.Println(\"Error creating lease\", err)\n//\t}\n//\n//\t// insert something with the lease\n//\t_, err = kv.Put(context.TODO(), fmt.Sprintf(\"/logger/%s\", app.randomString(32)), \"logger-service\", clientv3.WithLease(grantResp.ID))\n//\tif err != nil {\n//\t\tlog.Println(\"Error inserting using lease\", err)\n//\t}\n//\n//\t// keep lease alive\n//\tkalRes, err := lease.KeepAlive(context.TODO(), grantResp.ID)\n//\tif err != nil {\n//\t\tlog.Println(\"Error with keepalive\", err)\n//\t}\n//\tgo app.listenToKeepAlive(kalRes)\n//}\n//\n//// listenToKeepAlive consumes the responses from etcd, or the lease will not work as expected.\n//// We don't have to do anything with them, but we have to consume them.\n//func (app *Config) listenToKeepAlive(kalRes <-chan *clientv3.LeaseKeepAliveResponse) {\n//\tdefer func() {\n//\t\tif r := recover(); r != nil {\n//\t\t\tlog.Println(\"Error\", fmt.Sprintf(\"%v\", r))\n//\t\t}\n//\t}()\n//\n//\tfor {\n//\t\t_ = <-kalRes\n//\t}\n//}\n//\n//// connectToEtcd tries to connect to etcd, for up to 30 seconds\n//func connectToEtcd() (*clientv3.Client, error) {\n//\tvar cli *clientv3.Client\n//\tvar counts = 0\n//\n//\tfor {\n//\t\tc, err := clientv3.New(clientv3.Config{Endpoints: []string{\"etcd:2379\"},\n//\t\t\tDialTimeout: 5 * time.Second,\n//\t\t})\n//\t\tif err != nil {\n//\t\t\tfmt.Println(\"etcd not ready...\")\n//\t\t\tcounts++\n//\t\t} else {\n//\t\t\tfmt.Println()\n//\t\t\tcli = c\n//\t\t\tbreak\n//\t\t}\n//\n//\t\tif counts > 15 {\n//\t\t\treturn nil, err\n//\t\t}\n//\t\tfmt.Println(\"Backing off for 2 seconds...\")\n//\t\ttime.Sleep(2 * time.Second)\n//\t\tcontinue\n//\t}\n//\tlog.Println(\"Connected to etcd!\")\n//\treturn cli, nil\n//}\n"
  },
  {
    "path": "logger-service/cmd/web/grpc.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"google.golang.org/grpc\"\n\t\"log\"\n\t\"log-service/data\"\n\t\"log-service/logs\"\n\t\"net\"\n)\n\n// LogServer is type used for writing to the log via gRPC. Note that we embed the\n// data.Models type, so we have access to Mongo.\ntype LogServer struct {\n\tlogs.UnimplementedLogServiceServer\n\tModels data.Models\n}\n\n// WriteLog writes the log after receiving a call from a gRPC client. This function\n// must exist, and is defined in logs/logs.proto, in the \"service LogService\" bit\n// at the end of the file.\nfunc (l *LogServer) WriteLog(ctx context.Context, req *logs.LogRequest) (*logs.LogResponse, error) {\n\tinput := req.GetLogEntry()\n\tlog.Println(\"Log:\", input.Name, input.Data)\n\n\t// write the log\n\tlogEntry := data.LogEntry{\n\t\tName: input.Name,\n\t\tData: input.Data,\n\t}\n\n\terr := l.Models.LogEntry.Insert(logEntry)\n\tif err != nil {\n\t\tres := &logs.LogResponse{Result: \"failed:\"}\n\t\treturn res, err\n\t}\n\n\t// return response\n\tres := &logs.LogResponse{Result: \"logged!\"}\n\n\treturn res, nil\n}\n\n// gRPCListen starts the gRPC server\nfunc (app *Config) gRPCListen() {\n\tlis, err := net.Listen(\"tcp\", fmt.Sprintf(\":%s\", gRpcPort))\n\tif err != nil {\n\t\tlog.Fatalf(\"Failed to listen: %v\", err)\n\t}\n\n\ts := grpc.NewServer()\n\n\t// register the service, handing it models (so we can write to the database)\n\tlogs.RegisterLogServiceServer(s, &LogServer{Models: app.Models})\n\n\tlog.Printf(\"gRPC server started at port %s\", gRpcPort)\n\n\tif err := s.Serve(lis); err != nil {\n\t\tlog.Fatalf(\"Failed to serve: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "logger-service/cmd/web/handlers.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/go-chi/chi/v5\"\n\t\"io\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\n// authServiceURL is the url to the authentication service. Since we're using\n// Docker, we specify the appropriate entry from docker-compose.yml\nconst authServiceURL = \"http://authentication-service/authenticate\"\n\n// JSONPayload is the type for JSON posted to this API\ntype JSONPayload struct {\n\tName string `json:\"name\"`\n\tData string `json:\"data\"`\n}\n\n// WriteLog is the handler to accept a post request consisting of json payload,\n// and then write it to Mongo\nfunc (app *Config) WriteLog(w http.ResponseWriter, r *http.Request) {\n\t// read json into var\n\tvar requestPayload JSONPayload\n\t_ = app.readJSON(w, r, &requestPayload)\n\n\t// insert the data\n\terr := app.logEvent(requestPayload.Name, requestPayload.Data)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\t_ = app.errorJSON(w, err, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// create the response we'll send back as JSON\n\tresp := jsonResponse{\n\t\tError:   false,\n\t\tMessage: \"logged\",\n\t}\n\n\t// write the response back as JSON\n\t_ = app.writeJSON(w, http.StatusAccepted, resp)\n}\n\n// Logout logs the user out and redirects them to the login page\nfunc (app *Config) Logout(w http.ResponseWriter, r *http.Request) {\n\t// log the event\n\t_ = app.logEvent(\"authentication\", fmt.Sprintf(\"%s logged out of the logger service\", app.Session.GetString(r.Context(), \"email\")))\n\n\t// clean up session\n\t_ = app.Session.Destroy(r.Context())\n\t_ = app.Session.RenewToken(r.Context())\n\n\t// redirect to login page\n\thttp.Redirect(w, r, \"/login\", http.StatusSeeOther)\n}\n\n// LoginPage displays the login page\nfunc (app *Config) LoginPage(w http.ResponseWriter, r *http.Request) {\n\tapp.render(w, r, \"login.page.gohtml\", nil)\n}\n\n// LoginPagePost handles user login. Note that it calls the authentication microservice\nfunc (app *Config) LoginPagePost(w http.ResponseWriter, r *http.Request) {\n\t// it's always good to regenerate the session on login/logout\n\t_ = app.Session.RenewToken(r.Context())\n\n\t// parse the posted form data\n\terr := r.ParseForm()\n\tif err != nil {\n\t\tlog.Println(err)\n\t}\n\n\t// get email and password from form post\n\temail := r.Form.Get(\"email\")\n\tpassword := r.Form.Get(\"password\")\n\n\t// create a variable we'll post to the auth service as JSON\n\tvar requestPayload struct {\n\t\tEmail    string `json:\"email\"`\n\t\tPassword string `json:\"password\"`\n\t}\n\trequestPayload.Email = email\n\trequestPayload.Password = password\n\n\t// create json we'll send to the authentication-service\n\tjsonData, _ := json.MarshalIndent(requestPayload, \"\", \"\\t\")\n\n\t// call the authentication-service; we need a request, so let's build one, and populate\n\t// its body with the jsonData we just created\n\trequest, err := http.NewRequest(\"POST\", authServiceURL, bytes.NewBuffer(jsonData))\n\trequest.Header.Set(\"Content-Type\", \"application/json\")\n\n\tc := &http.Client{}\n\tresponse, err := c.Do(request)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\t_ = app.errorJSON(w, err, http.StatusBadRequest)\n\t\treturn\n\t}\n\tdefer response.Body.Close()\n\n\t// make sure we get back the right status code\n\tif response.StatusCode == http.StatusUnauthorized {\n\t\tlog.Println(\"wrong status code\", response.StatusCode)\n\t\thttp.Redirect(w, r, \"/login\", http.StatusSeeOther)\n\t\treturn\n\t} else if response.StatusCode != http.StatusAccepted {\n\t\tlog.Println(\"did not get status accepted\")\n\t\thttp.Redirect(w, r, \"/login\", http.StatusSeeOther)\n\t\treturn\n\t}\n\n\t// Read the body of the response\n\tbody, err := io.ReadAll(response.Body)\n\tif err != nil {\n\t\tapp.clientError(w, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// define a type that matches the JSON we're getting from the response body\n\ttype userPayload struct {\n\t\tError   bool   `json:\"error\"`\n\t\tMessage string `json:\"message\"`\n\t\tData    struct {\n\t\t\tID        int       `json:\"id\"`\n\t\t\tEmail     string    `json:\"email\"`\n\t\t\tFirstName string    `json:\"first_name\"`\n\t\t\tLastName  string    `json:\"last_name\"`\n\t\t\tActive    int       `json:\"active\"`\n\t\t\tCreatedAt time.Time `json:\"created_at\"`\n\t\t\tUpdatedAt time.Time `json:\"updated_at\"`\n\t\t} `json:\"data\"`\n\t}\n\n\t// declare a variable we can unmarshal the JSON into\n\tvar user userPayload\n\n\t// since we received the request from a remote host with http.Client,\n\t// we need to build a new request with the body we received and pass it to\n\t// app.readJSON\n\treq, _ := http.NewRequest(\"POST\", \"/\", bytes.NewReader(body))\n\n\t// read the JSON\n\terr = app.readJSON(w, req, &user)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\tapp.clientError(w, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\t// log the event\n\t_ = app.logEvent(\"authentication\", fmt.Sprintf(\"%s logged into the logger service\", user.Data.Email))\n\n\t// set up session & log user in\n\tapp.Session.Put(r.Context(), \"userID\", user.Data.ID)\n\tapp.Session.Put(r.Context(), \"email\", user.Data.Email)\n\n\thttp.Redirect(w, r, \"/admin/dashboard\", http.StatusSeeOther)\n}\n\n// Dashboard displays the dashboard page\nfunc (app *Config) Dashboard(w http.ResponseWriter, r *http.Request) {\n\t// get the list of all log entries from mongo\n\tlogs, err := app.Models.LogEntry.All()\n\tif err != nil {\n\t\tlog.Println(\"Error getting all log entries\")\n\t\tapp.clientError(w, http.StatusBadRequest)\n\t}\n\n\ttemplateData := make(map[string]any)\n\ttemplateData[\"logs\"] = logs\n\n\tapp.render(w, r, \"dashboard.page.gohtml\", &TemplateData{\n\t\tData: templateData,\n\t})\n}\n\n// DisplayOne is the handler to display a single log entry\nfunc (app *Config) DisplayOne(w http.ResponseWriter, r *http.Request) {\n\tid := chi.URLParam(r, \"id\")\n\n\tentry, err := app.Models.LogEntry.GetOne(id)\n\tif err != nil {\n\t\tapp.clientError(w, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\ttemplateData := make(map[string]any)\n\ttemplateData[\"entry\"] = entry\n\n\tapp.render(w, r, \"entry.page.gohtml\", &TemplateData{\n\t\tData: templateData,\n\t})\n}\n\n// DeleteAll drops everything in the collection and redirects to the same page\nfunc (app *Config) DeleteAll(w http.ResponseWriter, r *http.Request) {\n\terr := app.Models.LogEntry.DropCollection()\n\tif err != nil {\n\t\tlog.Println(err)\n\t}\n\n\thttp.Redirect(w, r, \"/admin/dashboard\", http.StatusSeeOther)\n}\n\n// UpdateTimeStamp just demos how to update a document\nfunc (app *Config) UpdateTimeStamp(w http.ResponseWriter, r *http.Request) {\n\tid := chi.URLParam(r, \"id\")\n\n\tentry, err := app.Models.LogEntry.GetOne(id)\n\tif err != nil {\n\t\tlog.Println(\"Error getting record:\", err)\n\t\tapp.clientError(w, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tres, err := entry.Update()\n\tif err != nil {\n\t\tlog.Println(\"Error updating\", err)\n\t\tapp.clientError(w, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tlog.Println(\"Result in handler:\", res.ModifiedCount)\n\n\thttp.Redirect(w, r, \"/admin/dashboard\", http.StatusSeeOther)\n}\n"
  },
  {
    "path": "logger-service/cmd/web/helpers.go",
    "content": "package main\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"log-service/data\"\n\t\"net/http\"\n\t\"runtime/debug\"\n\t\"time\"\n)\n\nconst randomStringSource = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0987654321_+\"\n\ntype jsonResponse struct {\n\tError   bool   `json:\"error\"`\n\tMessage string `json:\"message\"`\n\tData    any    `json:\"data,omitempty\"`\n}\n\n// readJSON tries to read the body of a request and converts it into JSON\nfunc (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error {\n\tmaxBytes := 1048576 // one megabyte\n\tr.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))\n\n\tdec := json.NewDecoder(r.Body)\n\terr := dec.Decode(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = dec.Decode(&struct{}{})\n\tif err != io.EOF {\n\t\treturn errors.New(\"body must have only a single json value\")\n\t}\n\n\treturn nil\n}\n\n// writeJSON takes a response status code and arbitrary data and writes a json response to the client\nfunc (app *Config) writeJSON(w http.ResponseWriter, status int, data any, headers ...http.Header) error {\n\tout, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(headers) > 0 {\n\t\tfor key, value := range headers[0] {\n\t\t\tw.Header()[key] = value\n\t\t}\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(status)\n\t_, err = w.Write(out)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// errorJSON takes an error, and optionally a response status code, and generates and sends\n// a json error response\nfunc (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error {\n\tstatusCode := http.StatusBadRequest\n\n\tif len(status) > 0 {\n\t\tstatusCode = status[0]\n\t}\n\n\tvar payload jsonResponse\n\tpayload.Error = true\n\tpayload.Message = err.Error()\n\n\treturn app.writeJSON(w, statusCode, payload)\n}\n\n// isAuthenticated checks to see if a user is authenticated by looking for userID in session\nfunc (app *Config) isAuthenticated(r *http.Request) bool {\n\texists := app.Session.Exists(r.Context(), \"userID\")\n\treturn exists\n}\n\n// clientError just fires back a client error when something goes wrong (bad request)\nfunc (app *Config) clientError(w http.ResponseWriter, status int) {\n\tlog.Println(\"Client error with status of\", status)\n\thttp.Error(w, http.StatusText(status), status)\n}\n\n// serverError just fires back error 500 when something goes wrong\nfunc (app *Config) serverError(w http.ResponseWriter, err error) {\n\ttrace := fmt.Sprintf(\"%s\\n%s\", err.Error(), debug.Stack())\n\tlog.Println(trace)\n\thttp.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)\n}\n\n// logEvent saves an event to the logs collection in Mongo\nfunc (app *Config) logEvent(name, content string) error {\n\tevent := data.LogEntry{\n\t\tName:      name,\n\t\tData:      content,\n\t\tCreatedAt: time.Now(),\n\t}\n\treturn app.Models.LogEntry.Insert(event)\n}\n\n// randomString returns a random string of letters of length n\nfunc (app *Config) randomString(n int) string {\n\ts, r := make([]rune, n), []rune(randomStringSource)\n\tfor i := range s {\n\t\tp, _ := rand.Prime(rand.Reader, len(r))\n\t\tx, y := p.Uint64(), uint64(len(r))\n\t\ts[i] = r[x%y]\n\t}\n\treturn string(s)\n}\n"
  },
  {
    "path": "logger-service/cmd/web/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/alexedwards/scs/v2\"\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org/mongo-driver/mongo/options\"\n\t\"log\"\n\t\"log-service/data\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/rpc\"\n\t\"time\"\n)\n\nvar client *mongo.Client\n\nconst (\n\twebPort  = \"80\"\n\trpcPort  = \"5001\"\n\tmongoURL = \"mongodb://mongo:27017\"\n\tgRpcPort = \"50001\"\n)\n\ntype Config struct {\n\tSession *scs.SessionManager\n\tModels  data.Models\n\tEtcd    *clientv3.Client\n}\n\nfunc main() {\n\t// Connect to Mongo and get a client.\n\tmongoClient, err := connectToMongo()\n\tclient = mongoClient\n\n\t// We'll use this context to disconnect from mongo, since it needs one.\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\n\t// close connection to Mongo when application exits\n\tdefer func() {\n\t\tif err = client.Disconnect(ctx); err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}()\n\n\t// Set up application configuration with session and our Models type,\n\t// which allows us to interact with Mongo.\n\tsession := scs.New()\n\tsession.Lifetime = 24 * time.Hour\n\tsession.Cookie.Persist = true\n\tsession.Cookie.SameSite = http.SameSiteLaxMode\n\tsession.Cookie.Secure = false\n\n\tapp := Config{\n\t\tSession: session,\n\t\tModels:  data.New(client),\n\t}\n\n\t// connect to etcd and register service\n\t//app.registerService()\n\t//defer app.Etcd.Close()\n\n\t// Start webserver in its own GoRoutine\n\tgo app.serve()\n\n\t// Start the gRPC server in its own GoRoutine\n\tgo app.gRPCListen()\n\n\t// Register the RPC server.\n\terr = rpc.Register(new(RPCServer))\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// Listen for RPC connections on port rpcPort\n\tlog.Println(\"Starting RPC Server on port\", rpcPort)\n\tlisten, err := net.Listen(\"tcp\", fmt.Sprintf(\"0.0.0.0:%s\", rpcPort))\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\treturn\n\t}\n\tdefer listen.Close()\n\n\t// this loop executes forever, waiting for connections\n\tfor {\n\t\trpcConn, err := listen.Accept()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tlog.Println(\"Working...\")\n\t\tgo rpc.ServeConn(rpcConn)\n\t}\n}\n\n// serve starts the web server.\nfunc (app *Config) serve() {\n\tsrv := &http.Server{\n\t\tAddr:    fmt.Sprintf(\":%s\", webPort),\n\t\tHandler: app.routes(),\n\t}\n\n\tfmt.Println(\"--------------------------------------\")\n\tfmt.Println(\"Starting logging web service on port\", webPort)\n\terr := srv.ListenAndServe()\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\n// Connect opens a connection to the Mongo database and returns a client.\nfunc connectToMongo() (*mongo.Client, error) {\n\t// create connect options\n\tclientOptions := options.Client().ApplyURI(mongoURL)\n\tclientOptions.SetAuth(options.Credential{\n\t\tUsername: \"admin\",\n\t\tPassword: \"password\",\n\t})\n\n\t// Connect to the MongoDB and return Client instance\n\tc, err := mongo.Connect(context.TODO(), clientOptions)\n\tif err != nil {\n\t\tfmt.Println(\"mongo.Connect() ERROR:\", err)\n\t\treturn nil, err\n\t}\n\n\treturn c, nil\n}\n"
  },
  {
    "path": "logger-service/cmd/web/middleware.go",
    "content": "package main\n\nimport \"net/http\"\n\n// SessionLoad loads and saves the session on every request\nfunc (app *Config) SessionLoad(next http.Handler) http.Handler {\n\treturn app.Session.LoadAndSave(next)\n}\n\n// Auth requires that users be logged in for any route that uses this middleware\nfunc (app *Config) Auth(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tif !app.isAuthenticated(r) {\n\t\t\tapp.Session.Put(r.Context(), \"error\", \"Log in first!\")\n\t\t\thttp.Redirect(w, r, \"/login\", http.StatusSeeOther)\n\t\t\treturn\n\t\t}\n\t\tnext.ServeHTTP(w, r)\n\t})\n}\n"
  },
  {
    "path": "logger-service/cmd/web/render.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"html/template\"\n\t\"net/http\"\n)\n\ntype TemplateData struct {\n\tData            map[string]any\n\tIsAuthenticated int\n}\n\n// addDefaultData adds whatever is specified in this function to all templates as\n// data that can be accessed directly.\nfunc (app *Config) addDefaultData(td *TemplateData, r *http.Request) *TemplateData {\n\tif app.Session.Exists(r.Context(), \"userID\") {\n\t\ttd.IsAuthenticated = 1\n\t}\n\treturn td\n}\n\nfunc (app *Config) render(w http.ResponseWriter, r *http.Request, t string, td *TemplateData) {\n\t// we only have one partial, which is actually a layout, but since all of our pages\n\t// require the layout, we need to include it when we call ParseFiles, below.\n\t// If you have other partials you use in your templates, add them to this slice.\n\tpartials := []string{\n\t\t\"./templates/base.layout.gohtml\",\n\t}\n\n\tvar templateSlice []string\n\ttemplateSlice = append(templateSlice, fmt.Sprintf(\"./templates/%s\", t))\n\n\tfor _, x := range partials {\n\t\ttemplateSlice = append(templateSlice, x)\n\t}\n\n\ttmpl, err := template.ParseFiles(templateSlice...)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\tif td == nil {\n\t\ttd = &TemplateData{}\n\t}\n\ttd = app.addDefaultData(td, r)\n\n\tif err := tmpl.Execute(w, td); err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t}\n}\n"
  },
  {
    "path": "logger-service/cmd/web/routes.go",
    "content": "package main\n\nimport (\n\t\"github.com/go-chi/chi/v5\"\n\t\"github.com/go-chi/chi/v5/middleware\"\n\t\"github.com/go-chi/cors\"\n\t\"net/http\"\n)\n\nfunc (app *Config) routes() http.Handler {\n\tmux := chi.NewRouter()\n\tmux.Use(middleware.Recoverer)\n\tmux.Use(middleware.Heartbeat(\"/ping\"))\n\n\tmux.Mount(\"/\", app.webRouter())\n\tmux.Mount(\"/api\", app.apiRouter())\n\n\treturn mux\n}\n\n// apiRouter is for api routes (no session load)\nfunc (app *Config) apiRouter() http.Handler {\n\tmux := chi.NewRouter()\n\n\t// specify who is allowed to connect to our API service\n\tmux.Use(cors.Handler(cors.Options{\n\t\tAllowedOrigins:   []string{\"https://*\", \"http://*\"},\n\t\tAllowedMethods:   []string{\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"},\n\t\tAllowedHeaders:   []string{\"Accept\", \"Authorization\", \"Content-Type\", \"X-CSRF-Token\"},\n\t\tExposedHeaders:   []string{\"Link\"},\n\t\tAllowCredentials: true,\n\t\tMaxAge:           300,\n\t}))\n\n\tmux.Post(\"/log\", app.WriteLog)\n\treturn mux\n}\n\n// webRouter is for web routes\nfunc (app *Config) webRouter() http.Handler {\n\tmux := chi.NewRouter()\n\tmux.Use(app.SessionLoad)\n\n\tmux.Get(\"/\", app.LoginPage)\n\tmux.Get(\"/login\", app.LoginPage)\n\tmux.Post(\"/login\", app.LoginPagePost)\n\tmux.Get(\"/logout\", app.Logout)\n\n\tmux.Route(\"/admin\", func(mux chi.Router) {\n\t\tmux.Use(app.Auth)\n\t\tmux.Get(\"/dashboard\", app.Dashboard)\n\t\tmux.Get(\"/log-entry/{id}\", app.DisplayOne)\n\t\tmux.Get(\"/delete-all\", app.DeleteAll)\n\t\tmux.Get(\"/update/{id}\", app.UpdateTimeStamp)\n\t})\n\n\treturn mux\n}\n"
  },
  {
    "path": "logger-service/cmd/web/rpc.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"log-service/data\"\n\t\"time\"\n)\n\n// RPCServer is the type for our RPC Server. Methods that take this as a receiver are available\n// over RPC, as long as they are exported.\ntype RPCServer struct{}\n\n// RPCPayload is the type for data we receive from RPC\ntype RPCPayload struct {\n\tName string\n\tData string\n}\n\n// LogInfo writes our payload to mongo\nfunc (r *RPCServer) LogInfo(payload RPCPayload, resp *string) error {\n\tcollection := client.Database(\"logs\").Collection(\"logs\")\n\t_, err := collection.InsertOne(context.TODO(), data.LogEntry{\n\t\tName:      payload.Name,\n\t\tData:      payload.Data,\n\t\tCreatedAt: time.Now(),\n\t})\n\tif err != nil {\n\t\tlog.Println(\"Error writing log in rpc.go, LogInfo\", err)\n\t\treturn err\n\t}\n\n\t// resp is the message sent back to the RPC caller\n\t*resp = \"Processed payload via RPC: \" + payload.Name\n\treturn nil\n}\n"
  },
  {
    "path": "logger-service/data/models.go",
    "content": "package data\n\nimport (\n\t\"context\"\n\t\"go.mongodb.org/mongo-driver/bson\"\n\t\"go.mongodb.org/mongo-driver/bson/primitive\"\n\t\"go.mongodb.org/mongo-driver/mongo\"\n\t\"go.mongodb.org/mongo-driver/mongo/options\"\n\t\"log\"\n\t\"time\"\n)\n\n// client is our mongo client that allows us to perform operations on the Mongo database.\nvar client *mongo.Client\n\n// New is the function used to create an instance of the data package. It returns the type\n// Model, which embeds all the types we want to be available to our application.\nfunc New(mongo *mongo.Client) Models {\n\tclient = mongo\n\n\treturn Models{\n\t\tLogEntry: LogEntry{},\n\t}\n}\n\n// Models is the type for this package. Note that any model that is included as a member\n// in this type is available to us throughout the application, anywhere that the\n// app variable is used, provided that the model is also added in the New function.\ntype Models struct {\n\tLogEntry LogEntry\n}\n\n// LogEntry is the type for all data stored in the logs collection. Note that we specify\n// specific bson values, and we *must* include omitempty on ID, or newly inserted records will\n// have an empty id! We also specify JSON struct tags, even though we don't use them yet. We\n// might in the future.\ntype LogEntry struct {\n\tID        string    `bson:\"_id,omitempty\" json:\"id,omitempty\"`\n\tName      string    `bson:\"name\" json:\"name\"`\n\tData      string    `bson:\"data\" json:\"data\"`\n\tCreatedAt time.Time `bson:\"created_at\" json:\"created_at\"`\n\tUpdatedAt time.Time `bson:\"updated_at\" json:\"updated_at\"`\n}\n\n// Insert puts a document in the logs collection.\nfunc (l *LogEntry) Insert(entry LogEntry) error {\n\tcollection := client.Database(\"logs\").Collection(\"logs\")\n\n\t_, err := collection.InsertOne(context.TODO(), LogEntry{\n\t\tName:      entry.Name,\n\t\tData:      entry.Data,\n\t\tCreatedAt: time.Now(),\n\t\tUpdatedAt: time.Now(),\n\t})\n\tif err != nil {\n\t\tlog.Println(\"Error inserting log entry:\", err)\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// All returns all documents in the logs collection, by descending date/time.\nfunc (l *LogEntry) All() ([]*LogEntry, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\n\tcollection := client.Database(\"logs\").Collection(\"logs\")\n\n\topts := options.Find()\n\topts.SetSort(bson.D{{\"created_at\", -1}})\n\n\tcursor, err := collection.Find(context.TODO(), bson.D{}, opts)\n\tif err != nil {\n\t\tlog.Println(\"Finding all documents ERROR:\", err)\n\t\treturn nil, err\n\t}\n\tdefer cursor.Close(ctx)\n\n\tvar logs []*LogEntry\n\n\tfor cursor.Next(ctx) {\n\t\tvar item LogEntry\n\t\terr := cursor.Decode(&item)\n\t\tif err != nil {\n\t\t\tlog.Println(\"Error scanning log into slice:\", err)\n\t\t\treturn nil, err\n\t\t} else {\n\t\t\tlogs = append(logs, &item)\n\t\t}\n\t}\n\n\treturn logs, nil\n}\n\n// GetOne returns a single document, by ID. Note that we have to convert the parameter id\n// which this function receives to a mongo.ObjectID, which is what Mongo actually requires in\n// order to call the FindOne() function.\nfunc (l *LogEntry) GetOne(id string) (*LogEntry, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\n\tcollection := client.Database(\"logs\").Collection(\"logs\")\n\n\tdocID, err := primitive.ObjectIDFromHex(id)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar entry LogEntry\n\n\terr = collection.FindOne(ctx, bson.M{\"_id\": docID}).Decode(&entry)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &entry, nil\n}\n\n// DropCollection deletes the logs collection and everything in it\nfunc (l *LogEntry) DropCollection() error {\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\n\tcollection := client.Database(\"logs\").Collection(\"logs\")\n\n\tif err := collection.Drop(ctx); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// Update updates on record, by id\nfunc (l *LogEntry) Update() (*mongo.UpdateResult, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)\n\tdefer cancel()\n\n\tdocID, err := primitive.ObjectIDFromHex(l.ID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlog.Println(\"Matching\", l.ID)\n\tcollection := client.Database(\"logs\").Collection(\"logs\")\n\n\tresult, err := collection.UpdateOne(\n\t\tctx,\n\t\tbson.M{\"_id\": docID},\n\t\tbson.D{\n\t\t\t{\"$set\", bson.D{\n\t\t\t\t{\"name\", l.Name},\n\t\t\t\t{\"data\", l.Data},\n\t\t\t\t{\"updated_at\", time.Now()},\n\t\t\t}},\n\t\t},\n\t)\n\tlog.Println(\"Matched:\", result.MatchedCount)\n\tlog.Println(\"Modified:\", result.ModifiedCount)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn result, nil\n}\n"
  },
  {
    "path": "logger-service/go.mod",
    "content": "module log-service\n\ngo 1.18\n\nrequire (\n\tgithub.com/alexedwards/scs/v2 v2.5.0\n\tgithub.com/go-chi/chi/v5 v5.0.8\n\tgithub.com/go-chi/cors v1.2.1\n\tgo.etcd.io/etcd/client/v3 v3.5.7\n\tgo.mongodb.org/mongo-driver v1.11.2\n\tgoogle.golang.org/grpc v1.53.0\n\tgoogle.golang.org/protobuf v1.28.1\n)\n\nrequire (\n\tgithub.com/coreos/go-semver v0.3.1 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.5.0 // indirect\n\tgithub.com/go-stack/stack v1.8.1 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/golang/snappy v0.0.4 // indirect\n\tgithub.com/klauspost/compress v1.16.0 // indirect\n\tgithub.com/montanaflynn/stats v0.7.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/xdg-go/pbkdf2 v1.0.0 // indirect\n\tgithub.com/xdg-go/scram v1.1.2 // indirect\n\tgithub.com/xdg-go/stringprep v1.0.4 // indirect\n\tgithub.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect\n\tgo.etcd.io/etcd/api/v3 v3.5.7 // indirect\n\tgo.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect\n\tgo.uber.org/atomic v1.10.0 // indirect\n\tgo.uber.org/multierr v1.9.0 // indirect\n\tgo.uber.org/zap v1.24.0 // indirect\n\tgolang.org/x/crypto v0.6.0 // indirect\n\tgolang.org/x/net v0.7.0 // indirect\n\tgolang.org/x/sync v0.1.0 // indirect\n\tgolang.org/x/sys v0.5.0 // indirect\n\tgolang.org/x/text v0.7.0 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 // indirect\n)\n"
  },
  {
    "path": "logger-service/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/alexedwards/scs/v2 v2.5.0 h1:zgxOfNFmiJyXG7UPIuw1g2b9LWBeRLh3PjfB9BDmfL4=\ngithub.com/alexedwards/scs/v2 v2.5.0/go.mod h1:ToaROZxyKukJKT/xLcVQAChi5k6+Pn1Gvmdl7h3RRj8=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=\ngithub.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=\ngithub.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=\ngithub.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=\ngithub.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=\ngithub.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=\ngithub.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=\ngithub.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=\ngithub.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=\ngithub.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=\ngithub.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=\ngithub.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=\ngithub.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/klauspost/compress v1.15.1 h1:y9FcTHGyrebwfP0ZZqFiaxTaiDnUrGkJkI+f583BL1A=\ngithub.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=\ngithub.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=\ngithub.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=\ngithub.com/montanaflynn/stats v0.7.0 h1:r3y12KyNxj/Sb/iOE46ws+3mS1+MZca1wlHQFPsY/JU=\ngithub.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=\ngithub.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=\ngithub.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=\ngithub.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=\ngithub.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=\ngithub.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E=\ngithub.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=\ngithub.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=\ngithub.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=\ngithub.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=\ngithub.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs=\ngithub.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=\ngithub.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=\ngithub.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=\ngithub.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=\ngithub.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=\ngithub.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngo.etcd.io/etcd/api/v3 v3.5.2 h1:tXok5yLlKyuQ/SXSjtqHc4uzNaMqZi2XsoSPr/LlJXI=\ngo.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=\ngo.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY=\ngo.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2 h1:4hzqQ6hIb3blLyQ8usCU4h3NghkqcsohEQ3o3VetYxE=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY=\ngo.etcd.io/etcd/client/v3 v3.5.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA=\ngo.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o=\ngo.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4=\ngo.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw=\ngo.mongodb.org/mongo-driver v1.8.4 h1:NruvZPPL0PBcRJKmbswoWSrmHeUvzdxA3GCPfD/NEOA=\ngo.mongodb.org/mongo-driver v1.8.4/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=\ngo.mongodb.org/mongo-driver v1.11.2 h1:+1v2rDQUWNcGW7/7E0Jvdz51V38XXxJfhzbV17aNHCw=\ngo.mongodb.org/mongo-driver v1.11.2/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=\ngo.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=\ngo.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=\ngo.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=\ngo.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=\ngo.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\ngo.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=\ngo.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=\ngolang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=\ngolang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=\ngolang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=\ngolang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f h1:9ug+SpnUXKl5LogY3yp9GHWUjUDCnFv4NjiP7yxS6Q4=\ngoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 h1:/cadn7taPtPlCgiWNetEPsle7jgnlad2R7gR5MXB6dM=\ngoogle.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\ngoogle.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=\ngoogle.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngoogle.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=\ngoogle.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "logger-service/logs/logs.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.27.1\n// \tprotoc        v3.19.4\n// source: logs.proto\n\npackage logs\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Log struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tName string `protobuf:\"bytes,1,opt,name=name,proto3\" json:\"name,omitempty\"`\n\tData string `protobuf:\"bytes,2,opt,name=data,proto3\" json:\"data,omitempty\"`\n}\n\nfunc (x *Log) Reset() {\n\t*x = Log{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_logs_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Log) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Log) ProtoMessage() {}\n\nfunc (x *Log) ProtoReflect() protoreflect.Message {\n\tmi := &file_logs_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Log.ProtoReflect.Descriptor instead.\nfunc (*Log) Descriptor() ([]byte, []int) {\n\treturn file_logs_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Log) GetName() string {\n\tif x != nil {\n\t\treturn x.Name\n\t}\n\treturn \"\"\n}\n\nfunc (x *Log) GetData() string {\n\tif x != nil {\n\t\treturn x.Data\n\t}\n\treturn \"\"\n}\n\ntype LogRequest struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tLogEntry *Log `protobuf:\"bytes,1,opt,name=logEntry,proto3\" json:\"logEntry,omitempty\"`\n}\n\nfunc (x *LogRequest) Reset() {\n\t*x = LogRequest{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_logs_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *LogRequest) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*LogRequest) ProtoMessage() {}\n\nfunc (x *LogRequest) ProtoReflect() protoreflect.Message {\n\tmi := &file_logs_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use LogRequest.ProtoReflect.Descriptor instead.\nfunc (*LogRequest) Descriptor() ([]byte, []int) {\n\treturn file_logs_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *LogRequest) GetLogEntry() *Log {\n\tif x != nil {\n\t\treturn x.LogEntry\n\t}\n\treturn nil\n}\n\ntype LogResponse struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tResult string `protobuf:\"bytes,1,opt,name=result,proto3\" json:\"result,omitempty\"`\n}\n\nfunc (x *LogResponse) Reset() {\n\t*x = LogResponse{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_logs_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *LogResponse) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*LogResponse) ProtoMessage() {}\n\nfunc (x *LogResponse) ProtoReflect() protoreflect.Message {\n\tmi := &file_logs_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use LogResponse.ProtoReflect.Descriptor instead.\nfunc (*LogResponse) Descriptor() ([]byte, []int) {\n\treturn file_logs_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *LogResponse) GetResult() string {\n\tif x != nil {\n\t\treturn x.Result\n\t}\n\treturn \"\"\n}\n\nvar File_logs_proto protoreflect.FileDescriptor\n\nvar file_logs_proto_rawDesc = []byte{\n\t0x0a, 0x0a, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6c, 0x6f,\n\t0x67, 0x73, 0x22, 0x2d, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,\n\t0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a,\n\t0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74,\n\t0x61, 0x22, 0x33, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,\n\t0x25, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,\n\t0x0b, 0x32, 0x09, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x08, 0x6c, 0x6f,\n\t0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x25, 0x0a, 0x0b, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73,\n\t0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18,\n\t0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x32, 0x3f, 0x0a,\n\t0x0a, 0x4c, 0x6f, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x57,\n\t0x72, 0x69, 0x74, 0x65, 0x4c, 0x6f, 0x67, 0x12, 0x10, 0x2e, 0x6c, 0x6f, 0x67, 0x73, 0x2e, 0x4c,\n\t0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6c, 0x6f, 0x67, 0x73,\n\t0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x07,\n\t0x5a, 0x05, 0x2f, 0x6c, 0x6f, 0x67, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_logs_proto_rawDescOnce sync.Once\n\tfile_logs_proto_rawDescData = file_logs_proto_rawDesc\n)\n\nfunc file_logs_proto_rawDescGZIP() []byte {\n\tfile_logs_proto_rawDescOnce.Do(func() {\n\t\tfile_logs_proto_rawDescData = protoimpl.X.CompressGZIP(file_logs_proto_rawDescData)\n\t})\n\treturn file_logs_proto_rawDescData\n}\n\nvar file_logs_proto_msgTypes = make([]protoimpl.MessageInfo, 3)\nvar file_logs_proto_goTypes = []interface{}{\n\t(*Log)(nil),         // 0: logs.Log\n\t(*LogRequest)(nil),  // 1: logs.LogRequest\n\t(*LogResponse)(nil), // 2: logs.LogResponse\n}\nvar file_logs_proto_depIdxs = []int32{\n\t0, // 0: logs.LogRequest.logEntry:type_name -> logs.Log\n\t1, // 1: logs.LogService.WriteLog:input_type -> logs.LogRequest\n\t2, // 2: logs.LogService.WriteLog:output_type -> logs.LogResponse\n\t2, // [2:3] is the sub-list for method output_type\n\t1, // [1:2] is the sub-list for method input_type\n\t1, // [1:1] is the sub-list for extension type_name\n\t1, // [1:1] is the sub-list for extension extendee\n\t0, // [0:1] is the sub-list for field type_name\n}\n\nfunc init() { file_logs_proto_init() }\nfunc file_logs_proto_init() {\n\tif File_logs_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_logs_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Log); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_logs_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*LogRequest); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_logs_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*LogResponse); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_logs_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   3,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   1,\n\t\t},\n\t\tGoTypes:           file_logs_proto_goTypes,\n\t\tDependencyIndexes: file_logs_proto_depIdxs,\n\t\tMessageInfos:      file_logs_proto_msgTypes,\n\t}.Build()\n\tFile_logs_proto = out.File\n\tfile_logs_proto_rawDesc = nil\n\tfile_logs_proto_goTypes = nil\n\tfile_logs_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "logger-service/logs/logs.proto",
    "content": "syntax = \"proto3\";\n\npackage logs;\n\noption go_package = \"/logs\";\n\nmessage Log{\n  string name = 1;\n  string data =2;\n}\n\nmessage LogRequest {\n  Log logEntry = 1;\n}\n\nmessage LogResponse{\n  string result = 1;\n}\n\nservice LogService{\n  rpc WriteLog(LogRequest) returns (LogResponse) {}\n}\n\n"
  },
  {
    "path": "logger-service/logs/logs_grpc.pb.go",
    "content": "// Code generated by protoc-gen-go-grpc. DO NOT EDIT.\n// versions:\n// - protoc-gen-go-grpc v1.2.0\n// - protoc             v3.19.4\n// source: logs.proto\n\npackage logs\n\nimport (\n\tcontext \"context\"\n\tgrpc \"google.golang.org/grpc\"\n\tcodes \"google.golang.org/grpc/codes\"\n\tstatus \"google.golang.org/grpc/status\"\n)\n\n// This is a compile-time assertion to ensure that this generated file\n// is compatible with the grpc package it is being compiled against.\n// Requires gRPC-Go v1.32.0 or later.\nconst _ = grpc.SupportPackageIsVersion7\n\n// LogServiceClient is the client API for LogService service.\n//\n// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.\ntype LogServiceClient interface {\n\tWriteLog(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error)\n}\n\ntype logServiceClient struct {\n\tcc grpc.ClientConnInterface\n}\n\nfunc NewLogServiceClient(cc grpc.ClientConnInterface) LogServiceClient {\n\treturn &logServiceClient{cc}\n}\n\nfunc (c *logServiceClient) WriteLog(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error) {\n\tout := new(LogResponse)\n\terr := c.cc.Invoke(ctx, \"/logs.LogService/WriteLog\", in, out, opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn out, nil\n}\n\n// LogServiceServer is the server API for LogService service.\n// All implementations must embed UnimplementedLogServiceServer\n// for forward compatibility\ntype LogServiceServer interface {\n\tWriteLog(context.Context, *LogRequest) (*LogResponse, error)\n\tmustEmbedUnimplementedLogServiceServer()\n}\n\n// UnimplementedLogServiceServer must be embedded to have forward compatible implementations.\ntype UnimplementedLogServiceServer struct {\n}\n\nfunc (UnimplementedLogServiceServer) WriteLog(context.Context, *LogRequest) (*LogResponse, error) {\n\treturn nil, status.Errorf(codes.Unimplemented, \"method WriteLog not implemented\")\n}\nfunc (UnimplementedLogServiceServer) mustEmbedUnimplementedLogServiceServer() {}\n\n// UnsafeLogServiceServer may be embedded to opt out of forward compatibility for this service.\n// Use of this interface is not recommended, as added methods to LogServiceServer will\n// result in compilation errors.\ntype UnsafeLogServiceServer interface {\n\tmustEmbedUnimplementedLogServiceServer()\n}\n\nfunc RegisterLogServiceServer(s grpc.ServiceRegistrar, srv LogServiceServer) {\n\ts.RegisterService(&LogService_ServiceDesc, srv)\n}\n\nfunc _LogService_WriteLog_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {\n\tin := new(LogRequest)\n\tif err := dec(in); err != nil {\n\t\treturn nil, err\n\t}\n\tif interceptor == nil {\n\t\treturn srv.(LogServiceServer).WriteLog(ctx, in)\n\t}\n\tinfo := &grpc.UnaryServerInfo{\n\t\tServer:     srv,\n\t\tFullMethod: \"/logs.LogService/WriteLog\",\n\t}\n\thandler := func(ctx context.Context, req interface{}) (interface{}, error) {\n\t\treturn srv.(LogServiceServer).WriteLog(ctx, req.(*LogRequest))\n\t}\n\treturn interceptor(ctx, in, info, handler)\n}\n\n// LogService_ServiceDesc is the grpc.ServiceDesc for LogService service.\n// It's only intended for direct use with grpc.RegisterService,\n// and not to be introspected or modified (even as a copy)\nvar LogService_ServiceDesc = grpc.ServiceDesc{\n\tServiceName: \"logs.LogService\",\n\tHandlerType: (*LogServiceServer)(nil),\n\tMethods: []grpc.MethodDesc{\n\t\t{\n\t\t\tMethodName: \"WriteLog\",\n\t\t\tHandler:    _LogService_WriteLog_Handler,\n\t\t},\n\t},\n\tStreams:  []grpc.StreamDesc{},\n\tMetadata: \"logs.proto\",\n}\n"
  },
  {
    "path": "logger-service/logs/readme.md",
    "content": "## gRPC stuff\n\nInstall binaries:\n\n```\ngo install google.golang.org/protobuf/cmd/protoc-gen-go@v1.27\ngo install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2\n```\n\nGenerate proto.\n\nThen, run command (inside logs directory):\n\n```\nprotoc --go_out=. \\\n    --go_opt=paths=source_relative \\\n    --go-grpc_out=. \\\n    --go-grpc_opt=paths=source_relative \\\n    logs.proto \n```"
  },
  {
    "path": "logger-service/templates/base.layout.gohtml",
    "content": "{{define \"base\" }}\n    <!doctype html>\n    <html lang=\"en\">\n    <head>\n        <meta charset=\"UTF-8\">\n        <meta name=\"viewport\"\n              content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n        <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">\n        <title>Logger Service</title>\n        <link href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css\" rel=\"stylesheet\"\n              integrity=\"sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3\"\n              crossorigin=\"anonymous\">\n\n    </head>\n    <body>\n    <nav class=\"navbar navbar-expand-lg navbar-dark bg-dark\">\n        <div class=\"container-fluid\">\n            <a class=\"navbar-brand\" href=\"#\"></a>\n            <button class=\"navbar-toggler\" type=\"button\" data-bs-toggle=\"collapse\"\n                    data-bs-target=\"#navbarSupportedContent\" aria-controls=\"navbarSupportedContent\"\n                    aria-expanded=\"false\" aria-label=\"Toggle navigation\">\n                <span class=\"navbar-toggler-icon\"></span>\n            </button>\n            <div class=\"collapse navbar-collapse\" id=\"navbarSupportedContent\">\n                <ul class=\"navbar-nav me-auto mb-2 mb-lg-0\">\n                    {{if eq .IsAuthenticated 1}}\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link active\" aria-current=\"page\" href=\"/admin/dashboard\">Dashboard</a>\n                        </li>\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link active\" href=\"/logout\">Logout</a>\n                        </li>\n                    {{else}}\n                        <li class=\"nav-item\">\n                            <a class=\"nav-link active\" href=\"/login\">Login</a>\n                        </li>\n                    {{end}}\n                </ul>\n            </div>\n        </div>\n    </nav>\n\n    {{block \"content\" .}}\n\n    {{end}}\n\n    <script src=\"https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js\" integrity=\"sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p\" crossorigin=\"anonymous\"></script>\n    {{block \"js\" .}}\n\n    {{end}}\n\n    </body>\n    </html>\n{{end}}"
  },
  {
    "path": "logger-service/templates/dashboard.page.gohtml",
    "content": "{{template \"base\" .}}\n\n{{define \"content\"}}\n    {{$logs := index .Data \"logs\"}}\n    <div class=\"container\">\n        <div class=\"row\">\n            <div class=\"col\">\n                <h1 class=\"mt-3\">Dashboard</h1>\n                <hr>\n\n                <div class=\"float-end\">\n                    <button data-bs-toggle=\"modal\" data-bs-target=\"#confirmModal\"\n                            class=\"btn btn-outline-danger\">Delete All\n                    </button>\n                </div>\n\n                <table class=\"table table-compact table-striped\">\n                    <thead>\n                    <tr>\n                        <th>ID</th>\n                        <th>Name</th>\n                        <th>Created</th>\n                        <th>Updated</th>\n                        <th>Update</th>\n                    </tr>\n                    </thead>\n                    <tbody>\n                    {{if gt (len $logs) 0}}\n                        {{range $logs}}\n                            <tr>\n                                <td><a href=\"/admin/log-entry/{{.ID}}\">{{.ID}}</a></td>\n                                <td>{{.Name}}</td>\n                                <td>{{.CreatedAt.Format \"2006-01-02 03:04:05\"}} UTC</td>\n                                <td>{{.UpdatedAt.Format \"2006-01-02 03:04:05\"}} UTC</td>\n                                <td><a href=\"/admin/update/{{.ID}}\">Update timestamp</a></td>\n                            </tr>\n                        {{end}}\n                    {{else}}\n                        <tr>\n                            <td colspan=\"5\">No entries</td>\n                        </tr>\n                    {{end}}\n                    </tbody>\n                </table>\n\n            </div>\n        </div>\n    </div>\n\n    <div class=\"modal\" tabindex=\"-1\" id=\"confirmModal\">\n        <div class=\"modal-dialog\">\n            <div class=\"modal-content\">\n                <div class=\"modal-header\">\n                    <h5 class=\"modal-title\">Delete all?</h5>\n                    <button type=\"button\" class=\"btn-close\" data-bs-dismiss=\"modal\" aria-label=\"Close\"></button>\n                </div>\n                <div class=\"modal-body\">\n                    <p>Are you sure you want to delete all entries?</p>\n                </div>\n                <div class=\"modal-footer\">\n                    <button type=\"button\" class=\"btn btn-secondary\" data-bs-dismiss=\"modal\">Cancel</button>\n                    <button id=\"deleteAll\" type=\"button\" class=\"btn btn-danger\">Delete everything</button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n{{end}}\n\n{{define \"js\"}}\n    <script>\n        document.getElementById(\"deleteAll\").addEventListener(\"click\", function () {\n            location.href = \"/admin/delete-all\";\n        })\n    </script>\n{{end}}"
  },
  {
    "path": "logger-service/templates/entry.page.gohtml",
    "content": "{{template \"base\" .}}\n\n{{define \"content\"}}\n    {{$entry := index .Data \"entry\"}}\n    <div class=\"container\">\n        <div class=\"row\">\n            <div class=\"col\">\n                <h1 class=\"mt-3\">Log Entry</h1>\n                <hr>\n\n                <div>\n                    <strong>ID:</strong> {{$entry.ID}}<br>\n                    <strong>Event:</strong> {{$entry.Name}}<br>\n                    <strong>Date/Time:</strong> {{$entry.CreatedAt.Format \"2006-01-02 03:04:05\" }} UTC<br>\n                    <strong>Details:</strong><br>\n                    <hr>\n                    {{$entry.Data}}\n                </div>\n            </div>\n        </div>\n    </div>\n{{end}}"
  },
  {
    "path": "logger-service/templates/login.page.gohtml",
    "content": "{{template \"base\" .}}\n\n{{define \"content\"}}\n    <div class=\"container\">\n        <div class=\"row\">\n            <div class=\"col-md-8 offset-md-2\">\n                <h1 class=\"mt-3\">Login</h1>\n                <hr>\n                <form class=\"needs-validation\" novalidate method=\"post\" action=\"/login\" autocomplete=\"off\">\n                    <div class=\"mb-3\">\n                        <label for=\"email\" class=\"form-label\">Email address</label>\n                        <input type=\"email\" name=\"email\" class=\"form-control\"\n                               autocomplete=\"email-new\" id=\"email\" required>\n                    </div>\n                    <div class=\"mb-3\">\n                        <label for=\"pass\" class=\"form-label\">Password</label>\n                        <input type=\"password\" name=\"password\" class=\"form-control\" id=\"pass\" required>\n                    </div>\n                    <button type=\"submit\" class=\"btn btn-primary\">Log In</button>\n                </form>\n            </div>\n        </div>\n    </div>\n{{end}}\n\n{{define \"js\"}}\n<script>\n    (function () {\n        'use strict'\n\n        let forms = document.querySelectorAll('.needs-validation')\n\n        Array.prototype.slice.call(forms)\n            .forEach(function (form) {\n                form.addEventListener('submit', function (event) {\n                    if (!form.checkValidity()) {\n                        event.preventDefault()\n                        event.stopPropagation()\n                    }\n\n                    form.classList.add('was-validated')\n                }, false)\n            })\n    })()\n</script>\n{{end}}"
  },
  {
    "path": "logger-service.dockerfile",
    "content": "FROM alpine:latest\nRUN mkdir /app\nRUN mkdir /templates\n\nCOPY logger-service/logServiceApp /app\nCOPY logger-service/templates/. /templates\n\n# Run the server executable\nCMD [ \"/app/logServiceApp\" ]"
  },
  {
    "path": "mail-service/cmd/api/discover.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\t\"log\"\n\t\"time\"\n)\n\n// registerService registers the correct entry for this service in etcd\nfunc (app *Config) registerService() {\n\tcli, _ := connectToEtcd()\n\tkv := clientv3.NewKV(cli)\n\n\tapp.Etcd = cli\n\n\tlease := clientv3.NewLease(cli)\n\tgrantResp, err := lease.Grant(context.TODO(), 10)\n\tif err != nil {\n\t\tlog.Println(\"Error creating lease\", err)\n\t}\n\n\t// insert something with the lease\n\t_, err = kv.Put(context.TODO(), fmt.Sprintf(\"/mail/%s\", app.randomString(32)), \"mail-service\", clientv3.WithLease(grantResp.ID))\n\tif err != nil {\n\t\tlog.Println(\"Error inserting using lease\", err)\n\t}\n\n\t// keep lease alive\n\tkalRes, err := lease.KeepAlive(context.TODO(), grantResp.ID)\n\tif err != nil {\n\t\tlog.Println(\"Error with keepalive\", err)\n\t}\n\tgo app.listenToKeepAlive(kalRes)\n}\n\nfunc (app *Config) listenToKeepAlive(kalRes <-chan *clientv3.LeaseKeepAliveResponse) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tlog.Println(\"Error\", fmt.Sprintf(\"%v\", r))\n\t\t}\n\t}()\n\n\tfor {\n\t\t_ = <-kalRes\n\t}\n}\n\n// connectToEtcd tries to connect to etcd, for up to 30 seconds\nfunc connectToEtcd() (*clientv3.Client, error) {\n\tvar cli *clientv3.Client\n\tvar counts = 0\n\n\tfor {\n\t\tc, err := clientv3.New(clientv3.Config{Endpoints: []string{\"etcd:2379\"},\n\t\t\tDialTimeout: 5 * time.Second,\n\t\t})\n\t\tif err != nil {\n\t\t\tfmt.Println(\"etcd not ready...\")\n\t\t\tcounts++\n\t\t} else {\n\t\t\tfmt.Println()\n\t\t\tcli = c\n\t\t\tbreak\n\t\t}\n\n\t\tif counts > 15 {\n\t\t\treturn nil, err\n\t\t}\n\t\tfmt.Println(\"Backing off for 2 seconds...\")\n\t\ttime.Sleep(2 * time.Second)\n\t\tcontinue\n\t}\n\tlog.Println(\"Connected to etcd!\")\n\treturn cli, nil\n}\n"
  },
  {
    "path": "mail-service/cmd/api/handlers.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"net/http\"\n)\n\n// SendMail receives a json payload for a message, and sends it\nfunc (app *Config) SendMail(w http.ResponseWriter, r *http.Request) {\n\ttype mailMessage struct {\n\t\tFrom    string `json:\"from\"`\n\t\tTo      string `json:\"to\"`\n\t\tSubject string `json:\"subject\"`\n\t\tMessage string `json:\"message\"`\n\t}\n\n\tvar requestPayload mailMessage\n\n\terr := app.readJSON(w, r, &requestPayload)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\t_ = app.errorJSON(w, err)\n\t\treturn\n\t}\n\n\tmsg := Message{\n\t\tFrom:    requestPayload.From,\n\t\tTo:      requestPayload.To,\n\t\tSubject: requestPayload.Subject,\n\t\tData:    requestPayload.Message,\n\t}\n\n\terr = app.Mailer.SendSMTPMessage(msg)\n\tif err != nil {\n\t\tlog.Println(err)\n\t\t_ = app.errorJSON(w, err)\n\t\treturn\n\t}\n\n\tpayload := jsonResponse{\n\t\tError:   false,\n\t\tMessage: \"sent to \" + requestPayload.To,\n\t}\n\n\t_ = app.writeJSON(w, http.StatusAccepted, payload)\n}\n"
  },
  {
    "path": "mail-service/cmd/api/helpers.go",
    "content": "package main\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n)\n\nconst randomStringSource = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0987654321_+\"\n\ntype jsonResponse struct {\n\tError   bool   `json:\"error\"`\n\tMessage string `json:\"message\"`\n\tData    any    `json:\"data,omitempty\"`\n}\n\n// readJSON tries to read the body of a request and converts it into JSON\nfunc (app *Config) readJSON(w http.ResponseWriter, r *http.Request, data any) error {\n\tmaxBytes := 1048576 // one megabyte\n\tr.Body = http.MaxBytesReader(w, r.Body, int64(maxBytes))\n\n\tdec := json.NewDecoder(r.Body)\n\terr := dec.Decode(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = dec.Decode(&struct{}{})\n\tif err != io.EOF {\n\t\treturn errors.New(\"body must have only a single json value\")\n\t}\n\n\treturn nil\n}\n\n// writeJSON takes a response status code and arbitrary data and writes a json response to the client\nfunc (app *Config) writeJSON(w http.ResponseWriter, status int, data any, headers ...http.Header) error {\n\tout, err := json.Marshal(data)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(headers) > 0 {\n\t\tfor key, value := range headers[0] {\n\t\t\tw.Header()[key] = value\n\t\t}\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tw.WriteHeader(status)\n\t_, err = w.Write(out)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// errorJSON takes an error, and optionally a response status code, and generates and sends\n// a json error response\nfunc (app *Config) errorJSON(w http.ResponseWriter, err error, status ...int) error {\n\tstatusCode := http.StatusBadRequest\n\n\tif len(status) > 0 {\n\t\tstatusCode = status[0]\n\t}\n\n\tvar payload jsonResponse\n\tpayload.Error = true\n\tpayload.Message = err.Error()\n\n\treturn app.writeJSON(w, statusCode, payload)\n}\n\n// randomString returns a random string of letters of length n\nfunc (app *Config) randomString(n int) string {\n\ts, r := make([]rune, n), []rune(randomStringSource)\n\tfor i := range s {\n\t\tp, _ := rand.Prime(rand.Reader, len(r))\n\t\tx, y := p.Uint64(), uint64(len(r))\n\t\ts[i] = r[x%y]\n\t}\n\treturn string(s)\n}\n"
  },
  {
    "path": "mail-service/cmd/api/mailer.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"github.com/vanng822/go-premailer/premailer\"\n\tmail \"github.com/xhit/go-simple-mail/v2\"\n\t\"html/template\"\n\t\"time\"\n)\n\n// Mail holds the information necessary to connect to an SMTP server\ntype Mail struct {\n\tDomain      string\n\tHost        string\n\tPort        int\n\tUsername    string\n\tPassword    string\n\tEncryption  string\n\tFromAddress string\n\tFromName    string\n}\n\n// Message is the type for an email message\ntype Message struct {\n\tFrom        string\n\tFromName    string\n\tTo          string\n\tSubject     string\n\tAttachments []string\n\tData        any\n\tDataMap     map[string]any\n}\n\n// SendSMTPMessage builds and sends an email message using SMTP. This is called by ListenForMail,\n// and can also be called directly when necessary\nfunc (m *Mail) SendSMTPMessage(msg Message) error {\n\tif msg.From == \"\" {\n\t\tmsg.From = m.FromAddress\n\t}\n\n\tif msg.FromName == \"\" {\n\t\tmsg.FromName = m.FromName\n\t}\n\n\tdata := map[string]any{\n\t\t\"message\": msg.Data,\n\t}\n\tmsg.DataMap = data\n\n\tformattedMessage, err := m.buildHTMLMessage(msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tplainMessage, err := m.buildPlainTextMessage(msg)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tserver := mail.NewSMTPClient()\n\tserver.Host = m.Host\n\tserver.Port = m.Port\n\tserver.Username = m.Username\n\tserver.Password = m.Password\n\tserver.Encryption = m.getEncryption(m.Encryption)\n\tserver.KeepAlive = false\n\tserver.ConnectTimeout = 10 * time.Second\n\tserver.SendTimeout = 10 * time.Second\n\n\tsmtpClient, err := server.Connect()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\temail := mail.NewMSG()\n\temail.SetFrom(msg.From).\n\t\tAddTo(msg.To).\n\t\tSetSubject(msg.Subject)\n\n\temail.SetBody(mail.TextPlain, plainMessage)\n\temail.AddAlternative(mail.TextHTML, formattedMessage)\n\n\t// add attachments, if any\n\tif len(msg.Attachments) > 0 {\n\t\tfor _, x := range msg.Attachments {\n\t\t\temail.AddAttachment(x)\n\t\t}\n\t}\n\n\terr = email.Send(smtpClient)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// buildHTMLMessage creates the html version of the message\nfunc (m *Mail) buildHTMLMessage(msg Message) (string, error) {\n\ttemplateToRender := \"./templates/mail.html.tmpl\"\n\n\tt, err := template.New(\"email-html\").ParseFiles(templateToRender)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvar tpl bytes.Buffer\n\tif err = t.ExecuteTemplate(&tpl, \"body\", msg.DataMap); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tformattedMessage := tpl.String()\n\tformattedMessage, err = m.inlineCSS(formattedMessage)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn formattedMessage, nil\n}\n\n// buildPlainTextMessage creates the plaintext version of the message\nfunc (m *Mail) buildPlainTextMessage(msg Message) (string, error) {\n\ttemplateToRender := \"./templates/mail.plain.tmpl\"\n\tt, err := template.New(\"email-plain\").ParseFiles(templateToRender)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tvar tplPlain bytes.Buffer\n\tif err = t.ExecuteTemplate(&tplPlain, \"body\", msg.DataMap); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tplainMessage := tplPlain.String()\n\n\treturn plainMessage, nil\n}\n\n// getEncryption returns the appropriate encryption type based on a string value\nfunc (m *Mail) getEncryption(e string) mail.Encryption {\n\tswitch e {\n\tcase \"tls\":\n\t\treturn mail.EncryptionSTARTTLS\n\tcase \"ssl\":\n\t\treturn mail.EncryptionSSLTLS\n\tcase \"none\", \"\":\n\t\treturn mail.EncryptionNone\n\tdefault:\n\t\treturn mail.EncryptionSTARTTLS\n\t}\n}\n\n// inlineCSS takes html input as a string, and inlines css where possible\nfunc (m *Mail) inlineCSS(s string) (string, error) {\n\toptions := premailer.Options{\n\t\tRemoveClasses:     false,\n\t\tCssToAttributes:   false,\n\t\tKeepBangImportant: true,\n\t}\n\tprem, err := premailer.NewPremailerFromString(s, &options)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\thtml, err := prem.Transform()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn html, nil\n}\n"
  },
  {
    "path": "mail-service/cmd/api/main.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\tclientv3 \"go.etcd.io/etcd/client/v3\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"strconv\"\n)\n\nconst webPort = \"80\"\n\n// Config is the application Config, shared with functions by using it as a receiver\ntype Config struct {\n\tMailer Mail\n\tEtcd   *clientv3.Client\n}\n\nfunc main() {\n\t// create our configuration\n\tapp := Config{\n\t\tMailer: createMail(),\n\t}\n\n\tlog.Println(\"Starting mail-service on port\", webPort)\n\n\t// define a server that listens on port 80 and uses our routes()\n\tsrv := &http.Server{\n\t\tAddr:    fmt.Sprintf(\":%s\", webPort),\n\t\tHandler: app.routes(),\n\t}\n\n\t// connect to etcd and register service\n\tapp.registerService()\n\tdefer app.Etcd.Close()\n\n\terr := srv.ListenAndServe()\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n}\n\n// createMail creates a variable of type Mail and populates its values.\n// Typically, this kind of information comes from the environment, or from\n// command line parameters.\nfunc createMail() Mail {\n\tport, _ := strconv.Atoi(os.Getenv(\"MAIL_PORT\"))\n\ts := Mail{\n\t\tDomain:      os.Getenv(\"MAIL_DOMAIN\"),\n\t\tHost:        os.Getenv(\"MAIL_HOST\"),\n\t\tPort:        port,\n\t\tUsername:    os.Getenv(\"MAIL_USERNAME\"),\n\t\tPassword:    os.Getenv(\"MAIL_PASSWORD\"),\n\t\tEncryption:  os.Getenv(\"MAIL_ENCRYPTION\"),\n\t\tFromName:    os.Getenv(\"FROM_NAME\"),\n\t\tFromAddress: os.Getenv(\"FROM_ADDRESS\"),\n\t}\n\n\treturn s\n}\n"
  },
  {
    "path": "mail-service/cmd/api/routes.go",
    "content": "package main\n\nimport (\n\t\"github.com/go-chi/chi/v5\"\n\t\"github.com/go-chi/chi/v5/middleware\"\n\t\"github.com/go-chi/cors\"\n\t\"net/http\"\n)\n\nfunc (app *Config) routes() http.Handler {\n\tmux := chi.NewRouter()\n\tmux.Use(middleware.Heartbeat(\"/ping\"))\n\n\t// specify who is allowed to connect to our API service\n\tmux.Use(cors.Handler(cors.Options{\n\t\tAllowedOrigins:   []string{\"http://*\", \"https://*\"},\n\t\tAllowedMethods:   []string{\"GET\", \"POST\", \"PUT\", \"DELETE\", \"OPTIONS\"},\n\t\tAllowedHeaders:   []string{\"Accept\", \"Authorization\", \"Content-Type\", \"X-CSRF-Token\"},\n\t\tExposedHeaders:   []string{\"Link\"},\n\t\tAllowCredentials: true,\n\t\tMaxAge:           300,\n\t}))\n\n\tmux.Post(\"/send\", app.SendMail)\n\n\treturn mux\n}\n"
  },
  {
    "path": "mail-service/go.mod",
    "content": "module mail-service\n\ngo 1.18\n\nrequire (\n\tgithub.com/go-chi/chi/v5 v5.0.7\n\tgithub.com/go-chi/cors v1.2.0\n\tgithub.com/vanng822/go-premailer v1.20.1\n\tgithub.com/xhit/go-simple-mail/v2 v2.11.0\n\tgo.etcd.io/etcd/client/v3 v3.5.2\n)\n\nrequire (\n\tgithub.com/PuerkitoBio/goquery v1.8.0 // indirect\n\tgithub.com/andybalholm/cascadia v1.3.1 // indirect\n\tgithub.com/coreos/go-semver v0.3.0 // indirect\n\tgithub.com/coreos/go-systemd/v22 v22.3.2 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/go-test/deep v1.0.8 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/gorilla/css v1.0.0 // indirect\n\tgithub.com/stretchr/testify v1.7.0 // indirect\n\tgithub.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect\n\tgithub.com/vanng822/css v1.0.1 // indirect\n\tgo.etcd.io/etcd/api/v3 v3.5.2 // indirect\n\tgo.etcd.io/etcd/client/pkg/v3 v3.5.2 // indirect\n\tgo.uber.org/atomic v1.9.0 // indirect\n\tgo.uber.org/multierr v1.8.0 // indirect\n\tgo.uber.org/zap v1.21.0 // indirect\n\tgolang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect\n\tgolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f // indirect\n\tgoogle.golang.org/grpc v1.45.0 // indirect\n\tgoogle.golang.org/protobuf v1.28.0 // indirect\n)\n"
  },
  {
    "path": "mail-service/go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=\ngithub.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=\ngithub.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=\ngithub.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=\ngithub.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=\ngithub.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=\ngithub.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=\ngithub.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=\ngithub.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=\ngithub.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=\ngithub.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=\ngithub.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=\ngithub.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=\ngithub.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=\ngithub.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=\ngithub.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=\ngithub.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=\ngithub.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=\ngithub.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=\ngithub.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=\ngithub.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=\ngithub.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=\ngithub.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=\ngithub.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=\ngithub.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=\ngithub.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=\ngithub.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 h1:PM5hJF7HVfNWmCjMdEfbuOBNXSVF2cMFGgQTPdKCbwM=\ngithub.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns=\ngithub.com/unrolled/render v1.0.3/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM=\ngithub.com/vanng822/css v1.0.1 h1:10yiXc4e8NI8ldU6mSrWmSWMuyWgPr9DZ63RSlsgDw8=\ngithub.com/vanng822/css v1.0.1/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w=\ngithub.com/vanng822/go-premailer v1.20.1 h1:2LTSIULXxNV5IOB5BSD3dlfOG95cq8qqExtRZMImTGA=\ngithub.com/vanng822/go-premailer v1.20.1/go.mod h1:RAxbRFp6M/B171gsKu8dsyq+Y5NGsUUvYfg+WQWusbE=\ngithub.com/vanng822/r2router v0.0.0-20150523112421-1023140a4f30/go.mod h1:1BVq8p2jVr55Ost2PkZWDrG86PiJ/0lxqcXoAcGxvWU=\ngithub.com/xhit/go-simple-mail/v2 v2.11.0 h1:o/056V50zfkO3Mm5tVdo9rG3ryg4ZmJ2XW5GMinHfVs=\ngithub.com/xhit/go-simple-mail/v2 v2.11.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=\ngo.etcd.io/etcd/api/v3 v3.5.2 h1:tXok5yLlKyuQ/SXSjtqHc4uzNaMqZi2XsoSPr/LlJXI=\ngo.etcd.io/etcd/api/v3 v3.5.2/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2 h1:4hzqQ6hIb3blLyQ8usCU4h3NghkqcsohEQ3o3VetYxE=\ngo.etcd.io/etcd/client/pkg/v3 v3.5.2/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=\ngo.etcd.io/etcd/client/v3 v3.5.2 h1:WdnejrUtQC4nCxK0/dLTMqKOB+U5TP/2Ya0BJL+1otA=\ngo.etcd.io/etcd/client/v3 v3.5.2/go.mod h1:kOOaWFFgHygyT0WlSmL8TJiXmMysO/nNUlEsSsN6W4o=\ngo.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=\ngo.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=\ngo.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=\ngo.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=\ngo.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=\ngo.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=\ngo.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=\ngo.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=\ngo.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=\ngo.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=\ngo.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=\ngo.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=\ngo.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=\ngolang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=\ngolang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=\ngolang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=\ngolang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=\ngolang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=\ngolang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q=\ngolang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=\ngolang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=\ngoogle.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb h1:0m9wktIpOxGw+SSKmydXWB3Z3GTfcPP6+q75HCQa6HI=\ngoogle.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=\ngoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f h1:9ug+SpnUXKl5LogY3yp9GHWUjUDCnFv4NjiP7yxS6Q4=\ngoogle.golang.org/genproto v0.0.0-20220328150716-24ca77f39d1f/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=\ngoogle.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=\ngoogle.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=\ngoogle.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=\ngoogle.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=\ngoogle.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "mail-service/templates/mail.html.tmpl",
    "content": "{{define \"body\"}}\n    <!doctype html>\n    <html lang=\"en\">\n\n    <head>\n        <meta name=\"viewport\" content=\"width=device-width\"/>\n        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n        <title></title>\n    </head>\n\n    <body>\n\n    <p>{{.message}}</p>\n\n    </body>\n\n    </html>\n{{end}}"
  },
  {
    "path": "mail-service/templates/mail.plain.tmpl",
    "content": "{{define \"body\"}}\n    {{.message}}\n{{end}}"
  },
  {
    "path": "mail-service.dockerfile",
    "content": "FROM alpine:latest\nRUN mkdir /app\nRUN mkdir /templates\n\nCOPY mail-service/mailerServiceApp /app\nCOPY mail-service/templates /templates\n\n# Run the server executable\nCMD [ \"/app/mailerServiceApp\" ]"
  },
  {
    "path": "multistage-dockerfiles/authentication-service.dockerfile",
    "content": "# The base go-image\nFROM golang:1.18-alpine as builder\n\n# create a directory for the app\nRUN mkdir /app\n\n# copy all files from the current directory to the app directory\nCOPY authentication-service/. /app\n\n# set working directory\nWORKDIR /app\n\n# build executable\nRUN CGO_ENABLED=0 go build -o authApp .\n\nRUN chmod +x /app/authApp\n\n# create a tiny image for use\nFROM alpine:latest\nRUN mkdir /app\n\nCOPY --from=builder /app/authApp /app\n\n# Run the server executable\nCMD [ \"/app/authApp\" ]"
  },
  {
    "path": "multistage-dockerfiles/broker-service.dockerfile",
    "content": "# The base go-image\nFROM golang:1.18-alpine as builder\n\n# create a directory for the app\nRUN mkdir /app\n\n# copy all files from the current directory to the app directory\nCOPY broker-service/. /app\n\n# set working directory\nWORKDIR /app\n\n# build executable\nRUN CGO_ENABLED=0 go build -o brokerApp ./cmd/api\n\nRUN chmod +x /app/brokerApp\n\n# create a tiny image for use\nFROM alpine:latest\nRUN mkdir /app\n\nCOPY --from=builder /app/brokerApp /app\n\n# Run the server executable\nCMD [ \"/app/brokerApp\" ]"
  },
  {
    "path": "multistage-dockerfiles/listener-service.dockerfile",
    "content": "# The base go-image\nFROM golang:1.18-alpine as builder\n \n# create a directory for the app\nRUN mkdir /app\n \n# copy all files from the current directory to the app directory\nCOPY listener-service /app\n \n# set working directory\nWORKDIR /app\n \n# build executable\nRUN CGO_ENABLED=0 go build -o listener .\n\nRUN chmod +x /app/listener\n\n# create a tiny image for use\nFROM alpine:latest\nRUN mkdir /app\n\nCOPY --from=builder /app/listener /app\n\n# Run the server executable\nCMD [ \"/app/listener\" ]"
  },
  {
    "path": "multistage-dockerfiles/logger-service.dockerfile",
    "content": "# The base go-image\nFROM golang:1.18-alpine as builder\n\n# create a directory for the app\nRUN mkdir /app\n\n# copy all files from the current directory to the app directory\nCOPY logger-service/. /app\n\n# set working directory\nWORKDIR /app\n\n# build executable\nRUN CGO_ENABLED=0 go build -o logServiceApp ./cmd/web\n\nRUN chmod +x /app/logServiceApp\n\n# create a tiny image for use\nFROM alpine:latest\nRUN mkdir /app\nRUN mkdir /templates\n\nCOPY --from=builder /app/logServiceApp /app\nCOPY --from=builder /app/templates/. /templates\n\n# Run the server executable\nCMD [ \"/app/logServiceApp\" ]"
  },
  {
    "path": "multistage-dockerfiles/mail-service.dockerfile",
    "content": "# The base go-image\nFROM golang:1.18-alpine as builder\n\n# create a directory for the app\nRUN mkdir /app\n\n# copy all files from the current directory to the app directory\nCOPY mail-service/. /app\n\n# set working directory\nWORKDIR /app\n\n# build executable\nRUN CGO_ENABLED=0 go build -o mailerServiceApp .\n\nRUN chmod +x /app/mailerServiceApp\n\n# create a tiny image for use\nFROM alpine:latest\nRUN mkdir /app\nRUN mkdir /templates\n\nCOPY --from=builder /app/mailerServiceApp /app\nCOPY --from=builder /app/templates /templates\n\n# Run the server executable\nCMD [ \"/app/mailerServiceApp\" ]"
  },
  {
    "path": "postgres.yml",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: postgres\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: postgres\n  template:\n    metadata:\n      labels:\n        app: postgres\n    spec:\n      containers:\n      - name: postgres\n        image: \"postgres:14.0\"\n        env:\n          - name: POSTGRES_USER\n            value: \"postgres\"\n          - name: POSTGRES_PASSWORD\n            value: \"password\"\n          - name: \"POSTGRES_DB\"\n            value: \"users\"\n        ports:\n          - containerPort: 5432\n\n---\n\napiVersion: v1\nkind: Service\nmetadata:\n  name: postgres\nspec:\n  selector:\n    app: postgres\n  ports:\n    - protocol: TCP\n      port: 5432\n      targetPort: 5432"
  },
  {
    "path": "swarm.md",
    "content": "# Docker swarm\n\n\n## Build images:\n```bash\ndocker build -f front-end.dockerfile -t tsawler/front-end:tag1 .\ndocker push tsawler/front-end:tag1\n```\n\n## Manage\n\n```bash\ndocker swarm init\ndocker swarm join-token worker\ndocker swarm join-token manager\ndocker stack deploy -c <stack>.yml <name>\ndocker service ls\nwatch docker service ls\ndocker service scale <name>=<instances>\n```\n\n## Updating (pull image and scale first)\n```bash\ndocker service update --image tsawler/listener:1.0.1 myapp_listener-service\n```\n\n## Bringing swarm down\nEasy method:\n```bash\ndocker stack rm myapp\n```\nTo stop them, scale all services to 0, or just type\n```bash\ndocker swarm leave\n```"
  },
  {
    "path": "swarm.yml",
    "content": "version: '3'\n\nservices:\n\n  caddy:\n    image: tsawler/micro-caddy:1.0.0\n    deploy:\n      mode: replicated\n      replicas: 1\n      placement:\n        constraints:\n          - node.hostname == docker-desktop\n    volumes:\n      - caddy_data:/data\n      - caddy_config:/config\n    ports:\n      - \"80:80\"\n      - \"444:443\"\n\n  front-end:\n    image: tsawler/front-end:1.0.0\n    deploy:\n      mode: replicated\n      replicas: 1\n\n  # broker-service - main entry point; we call this from the front end\n  broker-service:\n    image: tsawler/broker-service:1.0.1\n    ports:\n      - \"8080:80\"\n    deploy:\n      mode: replicated\n      replicas: 1\n\n  # listener-service - watches rabbitmq for messages\n  listener-service:\n    image: tsawler/listener-service:1.0.0\n    deploy:\n      mode: replicated\n      replicas: 2\n\n  # authentication-service - handles user auth\n  authentication-service:\n    image: tsawler/authentication-service:1.0.0\n    deploy:\n      mode: replicated\n      replicas: 1\n    environment:\n      DSN: \"host=postgres port=5432 user=postgres password=password dbname=users sslmode=disable timezone=UTC connect_timeout=5\"\n\n  # logger-service: a service to store logs\n  logger-service:\n    image: tsawler/logger-service:1.0.0\n    ports:\n      - \"8082:80\"\n    deploy:\n      mode: replicated\n      replicas: 1\n    volumes:\n      - ./logger-service/templates/:/app/templates\n\n  # mail-service - handles sending mail\n  mail-service:\n    image: tsawler/mail-service:1.0.0\n    deploy:\n      mode: replicated\n      replicas: 1\n    environment:\n      MAIL_DOMAIN: localhost\n      MAIL_HOST: mailhog\n      MAIL_PORT: 1025\n      MAIL_ENCRYPTION: none\n      MAIL_USERNAME: \"\"\n      MAIL_PASSWORD: \"\"\n      FROM_NAME: \"John Smith\"\n      FROM_ADDRESS: john.smith@example.com\n\n  # rabbitmq: the rabbitmq server\n  rabbitmq:\n    image: 'rabbitmq:3-management'\n    deploy:\n      mode: global\n    volumes:\n      - ./db-data/rabbitmq/:/var/lib/rabbitmq/\n\n  # mailhog: a fake smtp server with a web interface\n  mailhog:\n    image: 'mailhog/mailhog:latest'\n    ports:\n      - \"8025:8025\"\n    deploy:\n      mode: global\n\n  # mongo: start MongoDB and ensure that data is stored to a mounted volume\n  mongo:\n    image: 'mongo:4.2.17-bionic'\n    ports:\n      - \"27017:27017\"\n    deploy:\n      mode: global\n    environment:\n      MONGO_INITDB_DATABASE: logs\n      MONGO_INITDB_ROOT_USERNAME: admin\n      MONGO_INITDB_ROOT_PASSWORD: password\n    volumes:\n      - ./db-data/mongo/:/data/db\n\n  # postgres: start Postgres, and ensure that data is stored to a mounted volume\n  postgres:\n    image: 'postgres:14.2'\n    ports:\n      - \"5432:5432\"\n    deploy:\n      mode: global\n    environment:\n      POSTGRES_USER: postgres\n      POSTGRES_PASSWORD: password\n      POSTGRES_DB: users\n    volumes:\n      - ./db-data/postgres/:/var/lib/postgresql/data/\n\n  # etcd: start etcd server\n  etcd:\n    image: docker.io/bitnami/etcd:3\n    environment:\n      - ALLOW_NONE_AUTHENTICATION=yes\n    deploy:\n      mode: global\n    volumes:\n      - ./db-data/etcd/:/bitnami/etcd\n\nvolumes:\n  caddy_data:\n    external: true\n  caddy_config:"
  }
]