MF-1556 - Move the most used functions in main.go to internal package (#1601)

* MF-1525 - Add graceful stop for HTTP and GRPC servers (#1548)

* Add : errgroup to cmd/auth

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : Handle graceful stop for auth service
Remove : errgroups from auth service

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : Wait till server shutdown

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Change : instead of waitgroup changed to errgroups

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change : KillSignalHandler return type to error

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Empty Commit

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : Context to http server shutdown
Rename : varaible from proto to protocol

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change : to default log level

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : Sign-off

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: graceful stop of http and grpc server

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Fix: typos and caps

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: Signed-off

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Rename: Func KillSignalHandler to SignalHandler
Add: SIGABRT

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Fix: auth service

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: timeout for grpc gracefulstop
Fix: typos

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: .vscode folder to git ignore

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change: variable name to stopWaitTime

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove: .vscode folder

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove: .vscode from .gitignore

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : logger to handlers

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : New line at end of .gitignore file

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Fix : variable naming
Add : graceful stop for timescale

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Remove : unsued NATS library from import

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Move: "https" and "https" to moved to const var

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Move: "http" and "https" to moved to const var

Signed-off-by: Arvindh <arvindh91@gmail.com>

* update:  branch with master

Signed-off-by: Arvindh <arvindh91@gmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: httpserver and grpcsever

Signed-off-by: Arvindh <arvindh91@gmail.com>

* MF-1588 - Update Subscriber interface (#1598)

* Initial commit

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Update subscriber interface

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Add tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* check subscription map

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Check topic id after topic

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* reword description

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Setup empty queue

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change mqtt implementation

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Switch statements

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Simplify

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change mqtt subscriber

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Protect subscription map

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Fix subscription

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Set client id

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Format

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change delete method

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* move http and grpc server functions

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Move Keto and Jaeger

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add metrics and auth

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* MF - 1590 - Fix fetching list of users with a zero limit (#1594)

* Add max and min limit size

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Format

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Format

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* NOISSUE - Retrieve client key on cert issuing (#1607)

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix bug (#1604)

Signed-off-by: zhangchuanfeng <654300242@qq.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Rename service name

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Change metrics method

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Rename

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Rename

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Rename package name

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* 🚚 Rename Keto and Jaeger functions

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify grpc service

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* 🚚 rename apiutil to initutil

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

*  coap server

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* 🚚 rename

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* 🚚 Rename

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* ♻️ rename packages

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* ♻️ remove mf prefix

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* 🚚 rename server error

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove dead code

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* MF - 1416 - Queue Abstraction for Mainflux & RabbitMQ Support (#1562)

* MF-1263 - Move repeating errors to the separate package (#1540)

* MF-1263 - Mv duplicated errors to pkg/errors

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Revert test build flags

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix merge

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix comment

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Fix auth members list response (#1555)

* NOISSUE - Fix auth members list response

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Move group type next to page details

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Rm membersRes

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1261 - Use StatusUnauthorized for authn and StatusForbidden for authz (#1538)

* MF-1261 - Use StatusUnauthorized for authn and StatusForbidden for authz

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* ErrExternalKey typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Rename ErrUnauthorizedAcces -> ErrAuthentication

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix bootstrap error

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix status code in openapi

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix test description

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix test description

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix test description

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add errors cases

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix status codes

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add gRPC stutus code

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix tests description

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix openapi and encodeError

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix grpc message

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix test descriptions

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Revert sdk error

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1059 - Add TLS support for email (#1560)

* Use gomail package for sending emails

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

* remove print err

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

* Add vendor

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

* Rename email structure
remove logger

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

* typo in var name

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

* rename var

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

* remove MF_EMAIL_SECRET

Signed-off-by: Ivan Milosevic <iva@blokovi.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Refactor MQTT subscriber (#1561)

* correct suscriber interface validator + refactore token error handling

Signed-off-by: tzzed <zerouali.t@gmail.com>

* apply review suggestion

Signed-off-by: tzzed <zerouali.t@gmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1257 - Access messages from readers endpoint with user access token (#1470)

* remove owner id

Signed-off-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>

* add user auth for db reader

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* add user auth for db reader

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* enable mongodb reader for user token reading

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* use uuid check for auth switch between thing key and user tok

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* enable user token reading

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* revert to correct version

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* fix endpoint test, add additional tests

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* remove logs,dead code

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* fix logging messages

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* remove auth interface, add authorization header type

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* update api doc

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* remove unused package

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* some refactor of cases for authorization switch

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* correct description in openapi

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* fix endpoint test to match auth service change

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* some rename

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* initialize auth url

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* add env variables for auth service

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* fix spelling

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* Things prefix and no prefix for Thing authorization, Bearer for user

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* update readme file

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* fix default things grpc port

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* enable user reading for timescaledb

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* remove not used error

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* improve errors

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* refactor authorize

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* add chanID check

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* inline some error checking

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* fixing errors

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* fixing errors

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* improve test case description

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* remove test code

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* dont inline

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* refactor a bit encodeError

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* remove unused error

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* remove unused error

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* fix things auth grpc url

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

* rename variables for header prefix

Signed-off-by: mteodor <mirko.teodorovic@gmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Initial commit of adding rabbitmq broker

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Initial commit of adding rabbitmq broker

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Initial commit for tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Bump up tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Add more tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Add go routines

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Initial commit of adding rabbitmq broker

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Initial commit for tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Bump up tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Add more tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Add go routines

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Fix tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Fix with wait groups

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* unsubscribe to stop delivering messages

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Remove exclusivity

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1551 - Fix Cobra usage commands and clean unnecessary struct types (#1558)

* MF-1551 - Fix Cobra usage commands and clean unnecessary struct types

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Use linux syntax for cmd usage description

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix cmd.Use

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Separate Keto hosts for read and write (#1563)

* Separate keto hosts for read and write

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

* update readme with new envars

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

* rename read connection name

Signed-off-by: Ivan Milosevic <iva@blokovi.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Update dependencies (#1564)

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1240 - Return to service transport layer only service errors (#1559)

* MF-1240 - Return to service transport layer only service errors

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Remove unecessary errors

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Rm duplicated errors and fix transport

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Revert http endpoint_test

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix conflict

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Implement cancel mechanisms

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Queuename as parameter

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Queuename as parameter

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1469 - Indicate proper authentication scheme in Authorization header (#1523)

* MF-1469 - Indicate proper authentication scheme in Authorization header

Signed-off-by: Stefan Kovacevic <jen2tri@gmail.com>

* Fixing the remarks on the last push

Signed-off-by: Stefan Kovacevic <jen2tri@gmail.com>

* Remove Bearer prefix in all services and fix tests

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix remarks

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

Co-authored-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Add nats wrapper for COAP (#1569)

* Add nats wrapper for COAP

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Pass pubsub as argument

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Defer close connection

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Defer close connection

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Rename endpoint to topic

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1348 - Add transport errors logging (#1544)

* MF-1348 - Add go-kit transport level logging

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix reviews

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix reviews

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix merge

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix remark

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix go test flags

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Use httputil errors in things and http service

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix SDK tests

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Use httputil errors in certs and provision service

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Use httputil errors in consumers service

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* General renaming and add ErrMissingToken

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Rename httputil -> apiutil and use errors in users servive

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Use apiutil errors in auth, bootstrap, readers, things and twins

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Replace errors.Contain by comparison

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix remarks

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Simplify validateID

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Simplify validateID

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Simplify and rename ExtractAuthToken -> ExtractBearerToken

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix readers

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix auth key test and remarks

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Improve comment

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Simplify validateUUID check

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1567 - Use Bearer, Thing or Basic scheme in Authorization header (#1568)

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1565 - Document Bearer, Thing and Basic Authorization header (#1566)

* MF-1565 - Document Bearer Authorization header

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix auth, bootstrap, http and readers openapi

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix openapi

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add enc key for bootstrap

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Use global security

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix bearer formats

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Polish descriptions

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix boostrap and typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1575 Add 'Name' field to ListMembers response in things svc (#1576)

Signed-off-by: Ivan Balboteo <ivan@submer.com>

Co-authored-by: Ivan Balboteo <ivan@submer.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1580 - Influxdb Writer changes format of update-time to string (#1581)

* - MF-1580
- Modified consumers/writers/influxdb/fields.go
- influxdb-writer used to update data type of update-time to string
- Commented line 12 of consumers/writers/influxdb/fields.go to resolve
  uneccessary data type conversion issue

Signed-off-by: Hasan Tariq <hasantariqashraf@gmail.com>

* - MF-1580
- Removed strconv package from consumers/writers/influxdb/fields.go since it is no longer needed
- Removed line 12 from consumers/writers/influxdb/fields.go
- Replaced retrun value of updateTime with msg.UpdateTime (line 16 in
  fields.go)

Signed-off-by: Hasan Tariq <hasantariqashraf@gmail.com>

* Fix InflxuDB readers

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

Co-authored-by: Hasan Tariq <hasant@plcgroup.com>
Co-authored-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Unify MF_INFLUX_READER_DB_HOST and MF_INFLUX_WRITER_DB_HOST envars (#1585)

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Fix CoAP adapter (#1572)

* Revert "NOISSUE - Add nats wrapper for COAP (#1569)"

This reverts commit cc5d5195ab.

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix CoAP adapter

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Update CoAP observation cancel

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix observe

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix GET handling

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Revert authorization

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Use constants instead of magic numbers

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Remove an empty line

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Extract special observe value to constant

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1582 - Fix lora-adapter MQTT client (#1583)

* MF-1582 - Fix lora-adapter MQTT clien

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add timeout config to the mqtt subscriber

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Rm comment

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add sub timeout

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Update changelog and readme for release 0.13.0 (#1592)

* Update release example

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Update changelog and examples for 0.13.0 release

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Update VerneMQ release (#1593)

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Update changelog for release 0.13.0 (#1595)

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* unexport constants

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change routingkey

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Remove wait groups

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* protecting map

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Add publisher to pubsub

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change proto library

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Fix typos

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Reduce pubsub tests based on implementation

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Remove channel cancel

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Export constant

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Move invariant statements out of loop for cassandra-writer (#1596)

Signed-off-by: fuzhy <fuzhy1997@outlook.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Embedding publisher into pubsub

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Naming publisher

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Fix Nginx entrypoint script (#1597)

* Fix Nginx entrypoint script

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Update dependencies

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix NginX entrypoint

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Revert Makefile changes

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1525 - Add graceful stop for HTTP and GRPC servers (#1548)

* Add : errgroup to cmd/auth

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : Handle graceful stop for auth service
Remove : errgroups from auth service

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : Wait till server shutdown

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Change : instead of waitgroup changed to errgroups

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change : KillSignalHandler return type to error

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Empty Commit

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : Context to http server shutdown
Rename : varaible from proto to protocol

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change : to default log level

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : Sign-off

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: graceful stop of http and grpc server

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Fix: typos and caps

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: Signed-off

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Rename: Func KillSignalHandler to SignalHandler
Add: SIGABRT

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Fix: auth service

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: timeout for grpc gracefulstop
Fix: typos

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: .vscode folder to git ignore

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change: variable name to stopWaitTime

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove: .vscode folder

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove: .vscode from .gitignore

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : logger to handlers

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add : New line at end of .gitignore file

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Fix : variable naming
Add : graceful stop for timescale

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Remove : unsued NATS library from import

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Move: "https" and "https" to moved to const var

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Move: "http" and "https" to moved to const var

Signed-off-by: Arvindh <arvindh91@gmail.com>

* update:  branch with master

Signed-off-by: Arvindh <arvindh91@gmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF-1588 - Update Subscriber interface (#1598)

* Initial commit

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Update subscriber interface

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Add tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* check subscription map

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Check topic id after topic

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* reword description

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Setup empty queue

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change mqtt implementation

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Switch statements

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Simplify

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change mqtt subscriber

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Protect subscription map

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Fix subscription

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Set client id

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Format

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change delete method

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Update rabbitmq subscriber interface

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* using publisher composition

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change contenttype

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* rename topic for publish and subscribe

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change errors to lower case

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change errors to lower case

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* export errors

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* MF - 1590 - Fix fetching list of users with a zero limit (#1594)

* Add max and min limit size

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Format

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Format

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* NOISSUE - Retrieve client key on cert issuing (#1607)

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* fix bug (#1604)

Signed-off-by: zhangchuanfeng <654300242@qq.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* queue per subscription

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* queue per subscription

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Change routing method

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Direct method with one exchange to many queues, one consumer per queue

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* ♻️ Not casting data

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* ✏️ Fix typo

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* ♻️ remove passed queue name

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* 🔥 removing echange kind

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Combine tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Refactor unsubscribe method

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Fix merge conflict

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

*  sub and unsub to dummy topic

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* generate client id from topic and ID

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Rename topicID to clientID

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* update tests

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Reuse clientID

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Fix typos

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

* Seperate testpublish and testpubsub

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>

Co-authored-by: Manuel Imperiale <manuel.Imperiale@gmail.com>
Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Co-authored-by: Ivan Milošević <iva@blokovi.com>
Co-authored-by: __touk__ <zerouali.t@gmail.com>
Co-authored-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
Co-authored-by: stefankovacevic123 <jen2tri@gmail.com>
Co-authored-by: ibalboteo <ivanbalboteo@gmail.com>
Co-authored-by: Ivan Balboteo <ivan@submer.com>
Co-authored-by: Hasan98-git <67228396+Hasan98-git@users.noreply.github.com>
Co-authored-by: Hasan Tariq <hasant@plcgroup.com>
Co-authored-by: fuzhy <fuzhy1997@outlook.com>
Co-authored-by: Arvindh <30824765+arvindh123@users.noreply.github.com>
Co-authored-by: 张传峰 <59160162+zhang-chuanfeng@users.noreply.github.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* NOISSUE - Fix Groups SDK (#1609)

* Fix Groups SDK

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix CLI

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* NOISSUE - Fix CI script (#1613)

* Fix CI script

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix linter errors

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add timeout to linter

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* NOISSUE - Make application/json content-type valid in http-adapter (#1606)

* NOISSUE - Make application/json content-type valid in http-adapter

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add test

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Add CBOR content-type

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix naming

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix naming

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix CI

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix CI flag

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix CI install

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Upgrade grpc version

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* Fix typo

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

* rm cli

Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>

Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Specify size of channel

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* NOISSUE - fix pull request template typo (#1616)

* Fix typo

Signed-off-by: Filip Bugarski <filipbugarski@gmail.com>

* Change link

Signed-off-by: fbugarski <filipbugarski@gmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: load configuration function

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change: load config from env with pkg caarlos0/env

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change: mfdatabase to internaldb

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Add: httpserver and grpcsever

Signed-off-by: Arvindh <arvindh91@gmail.com>

move http and grpc server functions

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Move Keto and Jaeger

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Add metrics and auth

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Rename service name

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Change metrics method

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Rename

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Rename

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Rename package name

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

🚚 Rename Keto and Jaeger functions

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

unify grpc service

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

🚚 rename apiutil to initutil

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

 coap server

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

🚚 rename

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

🚚 Rename

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

♻️ rename packages

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

♻️ remove mf prefix

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

🚚 rename server error

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

remove dead code

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

NOISSUE - Fix CI script (#1613)

* Fix CI script

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix linter errors

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Add timeout to linter

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Specify size of channel

Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>

Add: load configuration function

Signed-off-by: Arvindh <arvindh91@gmail.com>

change: load config from env with pkg caarlos0/env

Signed-off-by: Arvindh <arvindh91@gmail.com>

* change: mfdatabase to internaldb

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: merge resolve error

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: merge resolve error

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: merge resolve error

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove: unused variables

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: address variable in servers

Signed-off-by: Arvindh <arvindh91@gmail.com>

* move: postgres connect to internal

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: client wrapper for most used

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: client wrapper for env

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify : auth, bootstrap, cassandra reader

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify : bootstrap code

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify : cassandra writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: struct tag to envDefault

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: grpc prefix

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: env parser

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: alt prefix

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: env default tag

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: auth grpc config

Signed-off-by: Arvindh <arvindh91@gmail.com>

* changes: internal approch and service start

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify: http adapter service

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove: unused code in http adapter"

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: config environment variable tags

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify: timescale writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify: timescale reader

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify: thing main.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify: smtp-notifier

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify: smpp-notifier

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify: postgres reader and writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify: twins

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify certs main.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify certs main.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify coap main.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify lora main.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix fatalf

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify mqtt main.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify mqtt main.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify ocpua adapter main.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix case

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify ws_adapter

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify ws_adapter

Signed-off-by: Arvindh <arvindh91@gmail.com>

* unify ws_adapter

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add : comment and spacing

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: lint errors

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: lint errors

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: main.go config load

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: main.go config load

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: auth main.go keto config

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove: package internal/sqlxt

Signed-off-by: Arvindh <arvindh91@gmail.com>

* code format : internal/client/grpc/connect.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: inline code

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: code format

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: inline and code format

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: moved to single block

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: moved to single block

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: export function comments

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: export function comments

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: export function comments

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: export function comments

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: export function comments

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remane: newtracer.go to tracer.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* renamee: authClient.go and thingsClient.go to client.go

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove space

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: jaeger default value

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: cassander config default values

Signed-off-by: Arvindh <arvindh91@gmail.com>

* rename file

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: postgres client config default values

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add setup with default config

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: mongo client config default values

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add: postgres default db name in services

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for auth

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for bootstrap

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for cassandra-reader

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for cassandra-writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for certs

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for coap

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for http-adapter

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for influx-reader

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for influx-writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for lora

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for mongodb-reader

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for mongodb-writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for mqtt

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for opcua

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for postgres-reader

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for postgres-writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for smpp-notifier

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for smtp-notifier

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for things

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for timescale-reader

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for timescale-writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for twins

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for users

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: environment variable default for ws

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: unused variables

Signed-off-by: Arvindh <arvindh91@gmail.com>

* empty commit

Signed-off-by: Arvindh <arvindh91@gmail.com>

* add comments

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: redis env variables

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: adapter ports and postgres db name

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: adapter ports

Signed-off-by: Arvindh <arvindh91@gmail.com>

* comments aligned

Signed-off-by: Arvindh <arvindh91@gmail.com>

* rename cassandra session variable

Signed-off-by: Arvindh <arvindh91@gmail.com>

* rename influxdb and influx to influxDB

Signed-off-by: Arvindh <arvindh91@gmail.com>

* rename EsConsumername to ESConsumerName

Signed-off-by: Arvindh <arvindh91@gmail.com>

* made comments consistant

Signed-off-by: Arvindh <arvindh91@gmail.com>

* made comments consistant & remove empty lines

Signed-off-by: Arvindh <arvindh91@gmail.com>

* made comments consistant & renmae function

Signed-off-by: Arvindh <arvindh91@gmail.com>

* made comments

Signed-off-by: Arvindh <arvindh91@gmail.com>

* comments added

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix bootstrap

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix empty env var

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove : unused variable

Signed-off-by: Arvindh <arvindh91@gmail.com>

* update: env parser library

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: mongodb reader and writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* fix: cassandra reader and writer

Signed-off-by: Arvindh <arvindh91@gmail.com>

* rename: directory

Signed-off-by: Arvindh <arvindh91@gmail.com>

* rename: variable

Signed-off-by: Arvindh <arvindh91@gmail.com>

* remove: unused librar

Signed-off-by: Arvindh <arvindh91@gmail.com>

* Format code and remove unused comments

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Fix tests

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* Move test URL construction out of the loop

Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>

* remove end dot in single line comments

Signed-off-by: Arvindh <arvindh91@gmail.com>

* empty

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
Signed-off-by: 0x6f736f646f <blackd0t@protonmail.com>
Signed-off-by: Manuel Imperiale <manuel.imperiale@gmail.com>
Signed-off-by: zhangchuanfeng <654300242@qq.com>
Signed-off-by: dusanb94 <dusan.borovcanin@mainflux.com>
Signed-off-by: fbugarski <filipbugarski@gmail.com>
Co-authored-by: Dušan Borovčanin <dusan.borovcanin@mainflux.com>
Co-authored-by: Drasko DRASKOVIC <drasko.draskovic@gmail.com>
Co-authored-by: b1ackd0t <blackd0t@protonmail.com>
Co-authored-by: Manuel Imperiale <manuel.Imperiale@gmail.com>
Co-authored-by: 张传峰 <59160162+zhang-chuanfeng@users.noreply.github.com>
Co-authored-by: Ivan Milošević <iva@blokovi.com>
Co-authored-by: __touk__ <zerouali.t@gmail.com>
Co-authored-by: Mirko Teodorovic <mirko.teodorovic@gmail.com>
Co-authored-by: stefankovacevic123 <jen2tri@gmail.com>
Co-authored-by: ibalboteo <ivanbalboteo@gmail.com>
Co-authored-by: Ivan Balboteo <ivan@submer.com>
Co-authored-by: Hasan98-git <67228396+Hasan98-git@users.noreply.github.com>
Co-authored-by: Hasan Tariq <hasant@plcgroup.com>
Co-authored-by: fuzhy <fuzhy1997@outlook.com>
Co-authored-by: Filip Bugarski <filipbugarski@gmail.com>
This commit is contained in:
Arvindh
2023-02-03 22:06:18 +05:30
committed by GitHub
parent ada5813f47
commit d00d13dabb
112 changed files with 3658 additions and 5959 deletions
+1 -1
View File
@@ -57,7 +57,7 @@ default values.
| Variable | Description | Default |
|-------------------------------|--------------------------------------------------------------------------|----------------|
| MF_AUTH_LOG_LEVEL | Service level (debug, info, warn, error) | error |
| MF_AUTH_LOG_LEVEL | Service level (debug, info, warn, error) | info |
| MF_AUTH_DB_HOST | Database host address | localhost |
| MF_AUTH_DB_PORT | Database host port | 5432 |
| MF_AUTH_DB_USER | Database user | mainflux |
+7 -44
View File
@@ -3,45 +3,11 @@
package postgres
import (
"fmt"
import migrate "github.com/rubenv/sql-migrate"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
// Config defines the options that are used when connecting to a PostgreSQL instance
type Config struct {
Host string
Port string
User string
Pass string
Name string
SSLMode string
SSLCert string
SSLKey string
SSLRootCert string
}
// Connect creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate failure.
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, err
}
if err := migrateDB(db); err != nil {
return nil, err
}
return db, nil
}
func migrateDB(db *sqlx.DB) error {
migrations := &migrate.MemoryMigrationSource{
// Migration of Auth service
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "auth_1",
@@ -56,9 +22,9 @@ func migrateDB(db *sqlx.DB) error {
PRIMARY KEY (id, issuer_id)
)`,
`CREATE EXTENSION IF NOT EXISTS LTREE`,
`CREATE TABLE IF NOT EXISTS groups (
`CREATE TABLE IF NOT EXISTS groups (
id VARCHAR(254) UNIQUE NOT NULL,
parent_id VARCHAR(254),
parent_id VARCHAR(254),
owner_id VARCHAR(254),
name VARCHAR(254) NOT NULL,
description VARCHAR(1024),
@@ -80,7 +46,7 @@ func migrateDB(db *sqlx.DB) error {
)`,
`CREATE INDEX path_gist_idx ON groups USING GIST (path);`,
`CREATE OR REPLACE FUNCTION inherit_group()
RETURNS trigger
RETURNS trigger
LANGUAGE PLPGSQL
AS
$$
@@ -112,7 +78,4 @@ func migrateDB(db *sqlx.DB) error {
},
},
}
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
return err
}
+4 -4
View File
@@ -12,9 +12,9 @@ import (
"os"
"testing"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/auth/postgres"
authRepo "github.com/mainflux/mainflux/auth/postgres"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
dockertest "github.com/ory/dockertest/v3"
)
@@ -49,7 +49,7 @@ func TestMain(m *testing.M) {
log.Fatalf("Could not connect to docker: %s", err)
}
dbConfig := postgres.Config{
dbConfig := pgClient.Config{
Host: "localhost",
Port: port,
User: "test",
@@ -61,7 +61,7 @@ func TestMain(m *testing.M) {
SSLRootCert: "",
}
if db, err = postgres.Connect(dbConfig); err != nil {
if db, err = pgClient.SetupDB(dbConfig, *authRepo.Migration()); err != nil {
log.Fatalf("Could not setup test DB connection: %s", err)
}
+2 -3
View File
@@ -37,7 +37,7 @@ The service is configured using the environment variables presented in the follo
| Variable | Description | Default |
|-------------------------------|-------------------------------------------------------------------------|----------------------------------|
| MF_BOOTSTRAP_LOG_LEVEL | Log level for Bootstrap (debug, info, warn, error) | error |
| MF_BOOTSTRAP_LOG_LEVEL | Log level for Bootstrap (debug, info, warn, error) | info |
| MF_BOOTSTRAP_DB_HOST | Database host address | localhost |
| MF_BOOTSTRAP_DB_PORT | Database host port | 5432 |
| MF_BOOTSTRAP_DB_USER | Database user | mainflux |
@@ -53,8 +53,7 @@ The service is configured using the environment variables presented in the follo
| MF_BOOTSTRAP_PORT | Bootstrap service HTTP port | 8180 |
| MF_BOOTSTRAP_SERVER_CERT | Path to server certificate in pem format | |
| MF_BOOTSTRAP_SERVER_KEY | Path to server key in pem format | |
| MF_SDK_BASE_URL | Base url for Mainflux SDK | http://localhost |
| MF_SDK_THINGS_PREFIX | SDK prefix for Things service | |
| MF_THINGS_URL | Base url for Mainflux Things | http://localhost |
| MF_THINGS_ES_URL | Things service event source URL | localhost:6379 |
| MF_THINGS_ES_PASS | Things service event source password | |
| MF_THINGS_ES_DB | Things service event source database | 0 |
+4 -44
View File
@@ -3,47 +3,11 @@
package postgres
import (
"fmt"
import migrate "github.com/rubenv/sql-migrate"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
// Config defines the options that are used when connecting to a PostgreSQL instance
type Config struct {
Host string
Port string
User string
Pass string
Name string
SSLMode string
SSLCert string
SSLKey string
SSLRootCert string
}
// Connect creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate
// failure.
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, err
}
if err := migrateDB(db); err != nil {
return nil, err
}
return db, nil
}
func migrateDB(db *sqlx.DB) error {
migrations := &migrate.MemoryMigrationSource{
// Migration of bootstrap service
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "configs_1",
@@ -102,8 +66,4 @@ func migrateDB(db *sqlx.DB) error {
},
},
}
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
return err
}
+4 -4
View File
@@ -8,9 +8,9 @@ import (
"os"
"testing"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/bootstrap/postgres"
bootstrapRepo "github.com/mainflux/mainflux/bootstrap/postgres"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/logger"
dockertest "github.com/ory/dockertest/v3"
@@ -50,7 +50,7 @@ func TestMain(m *testing.M) {
testLog.Error(fmt.Sprintf("Could not connect to docker: %s", err))
}
dbConfig := postgres.Config{
dbConfig := pgClient.Config{
Host: "localhost",
Port: port,
User: "test",
@@ -62,7 +62,7 @@ func TestMain(m *testing.M) {
SSLRootCert: "",
}
if db, err = postgres.Connect(dbConfig); err != nil {
if db, err = pgClient.SetupDB(dbConfig, *bootstrapRepo.Migration()); err != nil {
testLog.Error(fmt.Sprintf("Could not setup test DB connection: %s", err))
}
+1 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/certs"
"github.com/mainflux/mainflux/logger"
+4 -53
View File
@@ -5,57 +5,11 @@
package postgres
import (
"errors"
"fmt"
import migrate "github.com/rubenv/sql-migrate"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
const primaryKey = "primary_key"
// ErrMigrate indicates error during database migrations.
var ErrMigrate = errors.New("error executing database migrations")
// Config defines the options that are used when connecting to a PostgreSQL instance
type Config struct {
Host string
Port string
User string
Pass string
Name string
SSLMode string
SSLCert string
SSLKey string
SSLRootCert string
}
// Connect creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate
// failure.
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, err
}
if err := migrateDB(db); err != nil {
mErr, ok := err.(*migrate.TxError)
if ok && mErr.Migration.Id == primaryKey {
return db, ErrMigrate
}
return nil, err
}
return db, nil
}
func migrateDB(db *sqlx.DB) error {
migrations := &migrate.MemoryMigrationSource{
// Migration of Certs service
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "certs_1",
@@ -74,7 +28,4 @@ func migrateDB(db *sqlx.DB) error {
},
},
}
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
return err
}
+3 -2
View File
@@ -10,6 +10,7 @@ import (
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/certs/postgres"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/logger"
dockertest "github.com/ory/dockertest/v3"
)
@@ -49,7 +50,7 @@ func TestMain(m *testing.M) {
testLog.Error(fmt.Sprintf("Could not connect to docker: %s", err))
}
dbConfig := postgres.Config{
dbConfig := pgClient.Config{
Host: "localhost",
Port: port,
User: "test",
@@ -61,7 +62,7 @@ func TestMain(m *testing.M) {
SSLRootCert: "",
}
if db, err = postgres.Connect(dbConfig); err != nil {
if db, err = pgClient.SetupDB(dbConfig, *postgres.Migration()); err != nil {
testLog.Error(fmt.Sprintf("Could not setup test DB connection: %s", err))
}
+2 -28
View File
@@ -5,8 +5,6 @@ package certs
import (
"context"
"crypto/tls"
"crypto/x509"
"time"
"github.com/mainflux/mainflux"
@@ -46,43 +44,19 @@ type Service interface {
RevokeCert(ctx context.Context, token, serialID string) (Revoke, error)
}
// Config defines the service parameters
type Config struct {
LogLevel string
ClientTLS bool
CaCerts string
HTTPPort string
ServerCert string
ServerKey string
CertsURL string
JaegerURL string
AuthURL string
AuthTimeout time.Duration
SignTLSCert tls.Certificate
SignX509Cert *x509.Certificate
SignRSABits int
SignHoursValid string
PKIHost string
PKIPath string
PKIRole string
PKIToken string
}
type certsService struct {
auth mainflux.AuthServiceClient
certsRepo Repository
sdk mfsdk.SDK
conf Config
pki pki.Agent
}
// New returns new Certs service.
func New(auth mainflux.AuthServiceClient, certs Repository, sdk mfsdk.SDK, config Config, pki pki.Agent) Service {
// New returns new Certs service
func New(auth mainflux.AuthServiceClient, certs Repository, sdk mfsdk.SDK, pki pki.Agent) Service {
return &certsService{
certsRepo: certs,
sdk: sdk,
auth: auth,
conf: config,
pki: pki,
}
}
+2 -25
View File
@@ -9,7 +9,6 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http/httptest"
"os"
"strconv"
@@ -42,19 +41,11 @@ const (
ttl = "1h"
certNum = 10
cfgLogLevel = "error"
cfgClientTLS = false
cfgServerCert = ""
cfgServerKey = ""
cfgCertsURL = "http://localhost"
cfgJaegerURL = ""
cfgAuthURL = "localhost:8181"
cfgAuthTimeout = "1s"
caPath = "../docker/ssl/certs/ca.crt"
caKeyPath = "../docker/ssl/certs/ca.key"
cfgSignHoursValid = "24h"
cfgSignRSABits = 2048
)
func newService(tokens map[string]string) (certs.Service, error) {
@@ -80,23 +71,9 @@ func newService(tokens map[string]string) (certs.Service, error) {
return nil, err
}
c := certs.Config{
LogLevel: cfgLogLevel,
ClientTLS: cfgClientTLS,
ServerCert: cfgServerCert,
ServerKey: cfgServerKey,
CertsURL: cfgCertsURL,
JaegerURL: cfgJaegerURL,
AuthURL: cfgAuthURL,
SignTLSCert: tlsCert,
SignX509Cert: caCert,
SignHoursValid: cfgSignHoursValid,
SignRSABits: cfgSignRSABits,
}
pki := mocks.NewPkiAgent(tlsCert, caCert, cfgSignHoursValid, authTimeout)
return certs.New(auth, repo, sdk, c, pki), nil
return certs.New(auth, repo, sdk, pki), nil
}
func newThingsService(auth mainflux.AuthServiceClient) things.Service {
@@ -413,7 +390,7 @@ func loadCertificates(caPath, caKeyPath string) (tls.Certificate, *x509.Certific
return tlsCert, caCert, errors.Wrap(err, err)
}
b, err := ioutil.ReadFile(caPath)
b, err := os.ReadFile(caPath)
if err != nil {
return tlsCert, caCert, err
}
+92 -270
View File
@@ -3,15 +3,10 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/auth"
@@ -20,217 +15,137 @@ import (
httpapi "github.com/mainflux/mainflux/auth/api/http"
"github.com/mainflux/mainflux/auth/jwt"
"github.com/mainflux/mainflux/auth/keto"
"github.com/mainflux/mainflux/auth/postgres"
authPg "github.com/mainflux/mainflux/auth/postgres"
"github.com/mainflux/mainflux/auth/tracing"
"github.com/mainflux/mainflux/internal"
grpcClient "github.com/mainflux/mainflux/internal/clients/grpc"
jaegerClient "github.com/mainflux/mainflux/internal/clients/jaeger"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
grpcserver "github.com/mainflux/mainflux/internal/server/grpc"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/uuid"
"github.com/opentracing/opentracing-go"
acl "github.com/ory/keto/proto/ory/keto/acl/v1alpha1"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
stopWaitTime = 5 * time.Second
httpProtocol = "http"
httpsProtocol = "https"
defLogLevel = "error"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "auth"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defHTTPPort = "8180"
defGRPCPort = "8181"
defSecret = "auth"
defServerCert = ""
defServerKey = ""
defJaegerURL = ""
defKetoReadHost = "mainflux-keto"
defKetoWriteHost = "mainflux-keto"
defKetoReadPort = "4466"
defKetoWritePort = "4467"
defLoginDuration = "10h"
envLogLevel = "MF_AUTH_LOG_LEVEL"
envDBHost = "MF_AUTH_DB_HOST"
envDBPort = "MF_AUTH_DB_PORT"
envDBUser = "MF_AUTH_DB_USER"
envDBPass = "MF_AUTH_DB_PASS"
envDB = "MF_AUTH_DB"
envDBSSLMode = "MF_AUTH_DB_SSL_MODE"
envDBSSLCert = "MF_AUTH_DB_SSL_CERT"
envDBSSLKey = "MF_AUTH_DB_SSL_KEY"
envDBSSLRootCert = "MF_AUTH_DB_SSL_ROOT_CERT"
envHTTPPort = "MF_AUTH_HTTP_PORT"
envGRPCPort = "MF_AUTH_GRPC_PORT"
envSecret = "MF_AUTH_SECRET"
envServerCert = "MF_AUTH_SERVER_CERT"
envServerKey = "MF_AUTH_SERVER_KEY"
envJaegerURL = "MF_JAEGER_URL"
envKetoReadHost = "MF_KETO_READ_REMOTE_HOST"
envKetoWriteHost = "MF_KETO_WRITE_REMOTE_HOST"
envKetoReadPort = "MF_KETO_READ_REMOTE_PORT"
envKetoWritePort = "MF_KETO_WRITE_REMOTE_PORT"
envLoginDuration = "MF_AUTH_LOGIN_TOKEN_DURATION"
svcName = "auth"
envPrefix = "MF_AUTH_"
envPrefixHttp = "MF_AUTH_HTTP_"
envPrefixGrpc = "MF_AUTH_GRPC_"
defDB = "auth"
defSvcHttpPort = "8180"
defSvcGrpcPort = "8181"
)
type config struct {
logLevel string
dbConfig postgres.Config
httpPort string
grpcPort string
secret string
serverCert string
serverKey string
jaegerURL string
ketoReadHost string
ketoWriteHost string
ketoWritePort string
ketoReadPort string
loginDuration time.Duration
LogLevel string `env:"MF_AUTH_LOG_LEVEL" envDefault:"info"`
Secret string `env:"MF_AUTH_SECRET" envDefault:"auth"`
KetoReadHost string `env:"MF_KETO_READ_REMOTE_HOST" envDefault:"mainflux-keto"`
KetoReadPort string `env:"MF_KETO_READ_REMOTE_PORT" envDefault:"4466"`
KetoWriteHost string `env:"MF_KETO_WRITE_REMOTE_HOST" envDefault:"mainflux-keto"`
KetoWritePort string `env:"MF_KETO_WRITE_REMOTE_PORT" envDefault:"4467"`
LoginDuration time.Duration `env:"MF_AUTH_LOGIN_TOKEN_DURATION" envDefault:"10h"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
// Create auth service configurations
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
db := connectToDB(cfg.dbConfig, logger)
// Create new postgres client
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *authPg.Migration(), dbConfig)
if err != nil {
log.Fatalf("failed to setup postgres database : %s", err.Error())
}
defer db.Close()
tracer, closer := initJaeger("auth", cfg.jaegerURL, logger)
defer closer.Close()
dbTracer, dbCloser := initJaeger("auth_db", cfg.jaegerURL, logger)
// Create new tracer for database
dbTracer, dbCloser, err := jaegerClient.NewTracer("auth_db", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer dbCloser.Close()
readerConn, writerConn := initKeto(cfg.ketoReadHost, cfg.ketoReadPort, cfg.ketoWriteHost, cfg.ketoWritePort, logger)
// Create new keto reader grpc client
readerConn, _, err := grpcClient.Connect(grpcClient.Config{ClientTLS: false, URL: fmt.Sprintf("%s:%s", cfg.KetoReadHost, cfg.KetoReadPort)})
if err != nil {
log.Fatalf("failed to connect to keto gRPC: %s", err.Error())
}
svc := newService(db, dbTracer, cfg.secret, logger, readerConn, writerConn, cfg.loginDuration)
// Create new keto writer grpc client
writerConn, _, err := grpcClient.Connect(grpcClient.Config{ClientTLS: false, URL: fmt.Sprintf("%s:%s", cfg.KetoWriteHost, cfg.KetoWritePort)})
if err != nil {
log.Fatalf("failed to connect to keto gRPC: %s", err.Error())
}
svc := newService(db, dbTracer, cfg.Secret, logger, readerConn, writerConn, cfg.LoginDuration)
// Create new HTTP Server
tracer, closer, err := jaegerClient.NewTracer("auth", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer closer.Close()
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, httpapi.MakeHandler(svc, tracer, logger), logger)
// Create new grpc server
grpcServerConfig := server.Config{Port: defSvcGrpcPort}
if err := env.Parse(&grpcServerConfig, env.Options{Prefix: envPrefixGrpc, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s gRPC server configuration : %s", svcName, err.Error())
}
registerAuthServiceServer := func(srv *grpc.Server) {
mainflux.RegisterAuthServiceServer(srv, grpcapi.NewServer(tracer, svc))
}
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerAuthServiceServer, logger)
// Start servers
g.Go(func() error {
return startHTTPServer(ctx, tracer, svc, cfg.httpPort, cfg.serverCert, cfg.serverKey, logger)
return hs.Start()
})
g.Go(func() error {
return startGRPCServer(ctx, tracer, svc, cfg.grpcPort, cfg.serverCert, cfg.serverKey, logger)
return gs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Authentication service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs, gs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("Authentication service terminated: %s", err))
}
}
func loadConfig() config {
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
loginDuration, err := time.ParseDuration(mainflux.Env(envLoginDuration, defLoginDuration))
if err != nil {
log.Fatal(err)
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
dbConfig: dbConfig,
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
grpcPort: mainflux.Env(envGRPCPort, defGRPCPort),
secret: mainflux.Env(envSecret, defSecret),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
ketoReadHost: mainflux.Env(envKetoReadHost, defKetoReadHost),
ketoWriteHost: mainflux.Env(envKetoWriteHost, defKetoWriteHost),
ketoReadPort: mainflux.Env(envKetoReadPort, defKetoReadPort),
ketoWritePort: mainflux.Env(envKetoWritePort, defKetoWritePort),
loginDuration: loginDuration,
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger: %s", err))
os.Exit(1)
}
return tracer, closer
}
func initKeto(hostReadAddress, readPort, hostWriteAddress, writePort string, logger logger.Logger) (readerConnection, writerConnection *grpc.ClientConn) {
readConn, err := grpc.Dial(fmt.Sprintf("%s:%s", hostReadAddress, readPort), grpc.WithInsecure())
if err != nil {
logger.Error(fmt.Sprintf("Failed to dial %s:%s for Keto Read Service: %s", hostReadAddress, readPort, err))
os.Exit(1)
}
writeConn, err := grpc.Dial(fmt.Sprintf("%s:%s", hostWriteAddress, writePort), grpc.WithInsecure())
if err != nil {
logger.Error(fmt.Sprintf("Failed to dial %s:%s for Keto Write Service: %s", hostWriteAddress, writePort, err))
os.Exit(1)
}
return readConn, writeConn
}
func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
db, err := postgres.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err))
os.Exit(1)
}
return db
}
func newService(db *sqlx.DB, tracer opentracing.Tracer, secret string, logger logger.Logger, readerConn, writerConn *grpc.ClientConn, duration time.Duration) auth.Service {
database := postgres.NewDatabase(db)
keysRepo := tracing.New(postgres.New(database), tracer)
database := authPg.NewDatabase(db)
keysRepo := tracing.New(authPg.New(database), tracer)
groupsRepo := postgres.NewGroupRepo(database)
groupsRepo := authPg.NewGroupRepo(database)
groupsRepo = tracing.GroupRepositoryMiddleware(tracer, groupsRepo)
pa := keto.NewPolicyAgent(acl.NewCheckServiceClient(readerConn), acl.NewWriteServiceClient(writerConn), acl.NewReadServiceClient(readerConn))
@@ -240,102 +155,9 @@ func newService(db *sqlx.DB, tracer opentracing.Tracer, secret string, logger lo
svc := auth.New(keysRepo, groupsRepo, idProvider, t, pa, duration)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "auth",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "auth",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, tracer opentracing.Tracer, svc auth.Service, port string, certFile string, keyFile string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
server := &http.Server{Addr: p, Handler: httpapi.MakeHandler(svc, tracer, logger)}
errCh := make(chan error)
protocol := httpProtocol
switch {
case certFile != "" || keyFile != "":
logger.Info(fmt.Sprintf("Authentication service started using https, cert %s key %s, exposed port %s", certFile, keyFile, port))
go func() {
errCh <- server.ListenAndServeTLS(certFile, keyFile)
}()
protocol = httpsProtocol
default:
logger.Info(fmt.Sprintf("Authentication service started using http, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Authentication %s service error occurred during shutdown at %s: %s", protocol, p, err))
return fmt.Errorf("Authentication %s service error occurred during shutdown at %s: %w", protocol, p, err)
}
logger.Info(fmt.Sprintf("Authentication %s service shutdown of http at %s", protocol, p))
return nil
case err := <-errCh:
return err
}
}
func startGRPCServer(ctx context.Context, tracer opentracing.Tracer, svc auth.Service, port string, certFile string, keyFile string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
listener, err := net.Listen("tcp", p)
if err != nil {
return fmt.Errorf("failed to listen on port %s: %w", port, err)
}
var server *grpc.Server
switch {
case certFile != "" || keyFile != "":
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
if err != nil {
return fmt.Errorf("failed to load auth certificates: %w", err)
}
logger.Info(fmt.Sprintf("Authentication gRPC service started using https on port %s with cert %s key %s", port, certFile, keyFile))
server = grpc.NewServer(grpc.Creds(creds))
default:
logger.Info(fmt.Sprintf("Authentication gRPC service started using http on port %s", port))
server = grpc.NewServer()
}
mainflux.RegisterAuthServiceServer(server, grpcapi.NewServer(tracer, svc))
logger.Info(fmt.Sprintf("Authentication gRPC service started, exposed port %s", port))
go func() {
errCh <- server.Serve(listener)
}()
select {
case <-ctx.Done():
c := make(chan bool)
go func() {
defer close(c)
server.GracefulStop()
}()
select {
case <-c:
case <-time.After(stopWaitTime):
}
logger.Info(fmt.Sprintf("Authentication gRPC service shutdown at %s", p))
return nil
case err := <-errCh:
return err
}
}
+87 -317
View File
@@ -4,366 +4,136 @@ package main
import (
"context"
"crypto/aes"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
rediscons "github.com/mainflux/mainflux/bootstrap/redis/consumer"
redisprod "github.com/mainflux/mainflux/bootstrap/redis/producer"
"github.com/mainflux/mainflux/logger"
opentracing "github.com/opentracing/opentracing-go"
"golang.org/x/sync/errgroup"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
r "github.com/go-redis/redis/v8"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/bootstrap"
api "github.com/mainflux/mainflux/bootstrap/api"
"github.com/mainflux/mainflux/bootstrap/postgres"
mflog "github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
bootstrapPg "github.com/mainflux/mainflux/bootstrap/postgres"
rediscons "github.com/mainflux/mainflux/bootstrap/redis/consumer"
redisprod "github.com/mainflux/mainflux/bootstrap/redis/producer"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
redisClient "github.com/mainflux/mainflux/internal/clients/redis"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
mfsdk "github.com/mainflux/mainflux/pkg/sdk/go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"golang.org/x/sync/errgroup"
)
const (
stopWaitTime = 5 * time.Second
httpProtocol = "http"
httpsProtocol = "https"
defLogLevel = "error"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "bootstrap"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defEncryptKey = "12345678910111213141516171819202"
defClientTLS = "false"
defCACerts = ""
defPort = "8180"
defServerCert = ""
defServerKey = ""
defThingsURL = "http://localhost"
defThingsESURL = "localhost:6379"
defThingsESPass = ""
defThingsESDB = "0"
defESURL = "localhost:6379"
defESPass = ""
defESDB = "0"
defESConsumerName = "bootstrap"
defJaegerURL = ""
defAuthURL = "localhost:8181"
defAuthTimeout = "1s"
envLogLevel = "MF_BOOTSTRAP_LOG_LEVEL"
envDBHost = "MF_BOOTSTRAP_DB_HOST"
envDBPort = "MF_BOOTSTRAP_DB_PORT"
envDBUser = "MF_BOOTSTRAP_DB_USER"
envDBPass = "MF_BOOTSTRAP_DB_PASS"
envDB = "MF_BOOTSTRAP_DB"
envDBSSLMode = "MF_BOOTSTRAP_DB_SSL_MODE"
envDBSSLCert = "MF_BOOTSTRAP_DB_SSL_CERT"
envDBSSLKey = "MF_BOOTSTRAP_DB_SSL_KEY"
envDBSSLRootCert = "MF_BOOTSTRAP_DB_SSL_ROOT_CERT"
envEncryptKey = "MF_BOOTSTRAP_ENCRYPT_KEY"
envClientTLS = "MF_BOOTSTRAP_CLIENT_TLS"
envCACerts = "MF_BOOTSTRAP_CA_CERTS"
envPort = "MF_BOOTSTRAP_PORT"
envServerCert = "MF_BOOTSTRAP_SERVER_CERT"
envServerKey = "MF_BOOTSTRAP_SERVER_KEY"
envThingsURL = "MF_THINGS_URL"
envThingsESURL = "MF_THINGS_ES_URL"
envThingsESPass = "MF_THINGS_ES_PASS"
envThingsESDB = "MF_THINGS_ES_DB"
envESURL = "MF_BOOTSTRAP_ES_URL"
envESPass = "MF_BOOTSTRAP_ES_PASS"
envESDB = "MF_BOOTSTRAP_ES_DB"
envESConsumerName = "MF_BOOTSTRAP_EVENT_CONSUMER"
envJaegerURL = "MF_JAEGER_URL"
envAuthURL = "MF_AUTH_GRPC_URL"
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
svcName = "bootstrap"
envPrefix = "MF_BOOTSTRAP_"
envPrefixES = "MF_BOOTSTRAP_ES_"
envPrefixHttp = "MF_BOOTSTRAP_HTTP_"
defDB = "bootstrap"
defSvcHttpPort = "8180"
)
type config struct {
logLevel string
dbConfig postgres.Config
clientTLS bool
encKey []byte
caCerts string
httpPort string
serverCert string
serverKey string
thingsURL string
esThingsURL string
esThingsPass string
esThingsDB string
esURL string
esPass string
esDB string
esConsumerName string
jaegerURL string
authURL string
authTimeout time.Duration
LogLevel string `env:"MF_BOOTSTRAP_LOG_LEVEL" envDefault:"info"`
EncKey string `env:"MF_BOOTSTRAP_ENCRYPT_KEY" envDefault:"12345678910111213141516171819202"`
ESConsumerName string `env:"MF_BOOTSTRAP_EVENT_CONSUMER" envDefault:"bootstrap"`
ThingsURL string `env:"MF_THINGS_URL" envDefault:"http://localhost"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := mflog.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatal(err.Error())
}
// Create new postgres client
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *bootstrapPg.Migration(), dbConfig)
if err != nil {
log.Fatal(err.Error())
}
defer db.Close()
// Create new redis client for bootstrap event store
esClient, err := redisClient.Setup(envPrefixES)
if err != nil {
log.Fatalf("failed to setup %s bootstrap event store redis client : %s", svcName, err.Error())
}
defer esClient.Close()
// Create new auth grpc client api
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err)
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
// Create new service
svc := newService(auth, db, logger, esClient, cfg)
// Create an new HTTP server
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, bootstrap.NewConfigReader([]byte(cfg.EncKey)), logger), logger)
// Start servers
g.Go(func() error {
return hs.Start()
})
g.Go(func() error {
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
// Subscribe to things event store
thingsESClient, err := redisClient.Setup(envPrefixES)
if err != nil {
log.Fatalf(err.Error())
}
defer thingsESClient.Close()
db := connectToDB(cfg.dbConfig, logger)
defer db.Close()
thingsESConn := connectToRedis(cfg.esThingsURL, cfg.esThingsPass, cfg.esThingsDB, logger)
defer thingsESConn.Close()
esClient := connectToRedis(cfg.esURL, cfg.esPass, cfg.esDB, logger)
defer esClient.Close()
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
authConn := connectToAuth(cfg, logger)
defer authConn.Close()
auth := authapi.NewClient(authTracer, authConn, cfg.authTimeout)
svc := newService(auth, db, logger, esClient, cfg)
g.Go(func() error {
return startHTTPServer(ctx, svc, cfg, logger)
})
go subscribeToThingsES(svc, thingsESConn, cfg.esConsumerName, logger)
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Bootstrap service shutdown by signal: %s", sig))
}
return nil
})
go subscribeToThingsES(svc, thingsESClient, cfg.ESConsumerName, logger)
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("Bootstrap service terminated: %s", err))
}
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
tls = false
}
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
authTimeout, err := time.ParseDuration(mainflux.Env(envAuthTimeout, defAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envAuthTimeout, err.Error())
}
encKey, err := hex.DecodeString(mainflux.Env(envEncryptKey, defEncryptKey))
if err != nil {
log.Fatalf("Invalid %s value: %s", envEncryptKey, err.Error())
}
if err := os.Unsetenv(envEncryptKey); err != nil {
log.Fatalf("Unable to unset %s value: %s", envEncryptKey, err.Error())
}
if _, err := aes.NewCipher(encKey); err != nil {
log.Fatalf("Invalid %s value: %s", envEncryptKey, err.Error())
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
dbConfig: dbConfig,
clientTLS: tls,
encKey: encKey,
caCerts: mainflux.Env(envCACerts, defCACerts),
httpPort: mainflux.Env(envPort, defPort),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
thingsURL: mainflux.Env(envThingsURL, defThingsURL),
esThingsURL: mainflux.Env(envThingsESURL, defThingsESURL),
esThingsPass: mainflux.Env(envThingsESPass, defThingsESPass),
esThingsDB: mainflux.Env(envThingsESDB, defThingsESDB),
esURL: mainflux.Env(envESURL, defESURL),
esPass: mainflux.Env(envESPass, defESPass),
esDB: mainflux.Env(envESDB, defESDB),
esConsumerName: mainflux.Env(envESConsumerName, defESConsumerName),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
authURL: mainflux.Env(envAuthURL, defAuthURL),
authTimeout: authTimeout,
}
}
func connectToDB(cfg postgres.Config, logger mflog.Logger) *sqlx.DB {
db, err := postgres.Connect(cfg)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err))
os.Exit(1)
}
return db
}
func connectToRedis(redisURL, redisPass, redisDB string, logger mflog.Logger) *r.Client {
db, err := strconv.Atoi(redisDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to redis: %s", err))
os.Exit(1)
}
return r.NewClient(&r.Options{
Addr: redisURL,
Password: redisPass,
DB: db,
})
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func newService(auth mainflux.AuthServiceClient, db *sqlx.DB, logger mflog.Logger, esClient *r.Client, cfg config) bootstrap.Service {
thingsRepo := postgres.NewConfigRepository(db, logger)
func newService(auth mainflux.AuthServiceClient, db *sqlx.DB, logger logger.Logger, esClient *r.Client, cfg config) bootstrap.Service {
repoConfig := bootstrapPg.NewConfigRepository(db, logger)
config := mfsdk.Config{
ThingsURL: cfg.thingsURL,
ThingsURL: cfg.ThingsURL,
}
sdk := mfsdk.NewSDK(config)
svc := bootstrap.New(auth, thingsRepo, sdk, cfg.encKey)
svc := bootstrap.New(auth, repoConfig, sdk, []byte(cfg.EncKey))
svc = redisprod.NewEventStoreMiddleware(svc, esClient)
svc = api.NewLoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "bootstrap",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "bootstrap",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.authURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
return conn
}
func startHTTPServer(ctx context.Context, svc bootstrap.Service, cfg config, logger mflog.Logger) error {
p := fmt.Sprintf(":%s", cfg.httpPort)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, bootstrap.NewConfigReader(cfg.encKey), logger)}
errCh := make(chan error)
protocol := httpProtocol
switch {
case cfg.serverCert != "" || cfg.serverKey != "":
logger.Info(fmt.Sprintf("Bootstrap service started using https on port %s with cert %s key %s",
cfg.httpPort, cfg.serverCert, cfg.serverKey))
go func() {
errCh <- server.ListenAndServeTLS(cfg.serverCert, cfg.serverKey)
}()
protocol = httpsProtocol
default:
logger.Info(fmt.Sprintf("Bootstrap service started using http on port %s", cfg.httpPort))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Bootstrap %s service error occurred during shutdown at %s: %s", protocol, p, err))
return fmt.Errorf("bootstrap %s service error occurred during shutdown at %s: %w", protocol, p, err)
}
logger.Info(fmt.Sprintf("Bootstrap %s service shutdown of http at %s", protocol, p))
return nil
case err := <-errCh:
return err
}
}
func subscribeToThingsES(svc bootstrap.Service, client *r.Client, consumer string, logger mflog.Logger) {
func subscribeToThingsES(svc bootstrap.Service, client *r.Client, consumer string, logger logger.Logger) {
eventStore := rediscons.NewEventStore(svc, client, consumer, logger)
logger.Info("Subscribed to Redis Event Store")
if err := eventStore.Subscribe(context.Background(), "mainflux.things"); err != nil {
+57 -264
View File
@@ -6,312 +6,105 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/gocql/gocql"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/internal"
cassandraClient "github.com/mainflux/mainflux/internal/clients/cassandra"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/readers"
"github.com/mainflux/mainflux/readers/api"
"github.com/mainflux/mainflux/readers/cassandra"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
stopWaitTime = 5 * time.Second
sep = ","
defLogLevel = "error"
defPort = "8180"
defCluster = "127.0.0.1"
defKeyspace = "mainflux"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDBPort = "9042"
defClientTLS = "false"
defCACerts = ""
defServerCert = ""
defServerKey = ""
defJaegerURL = ""
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
defUsersAuthURL = "localhost:8181"
defUsersAuthTimeout = "1s"
envLogLevel = "MF_CASSANDRA_READER_LOG_LEVEL"
envPort = "MF_CASSANDRA_READER_PORT"
envCluster = "MF_CASSANDRA_READER_DB_CLUSTER"
envKeyspace = "MF_CASSANDRA_READER_DB_KEYSPACE"
envDBUser = "MF_CASSANDRA_READER_DB_USER"
envDBPass = "MF_CASSANDRA_READER_DB_PASS"
envDBPort = "MF_CASSANDRA_READER_DB_PORT"
envClientTLS = "MF_CASSANDRA_READER_CLIENT_TLS"
envCACerts = "MF_CASSANDRA_READER_CA_CERTS"
envServerCert = "MF_CASSANDRA_READER_SERVER_CERT"
envServerKey = "MF_CASSANDRA_READER_SERVER_KEY"
envJaegerURL = "MF_JAEGER_URL"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
envUsersAuthURL = "MF_AUTH_GRPC_URL"
envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
svcName = "cassandra-reader"
envPrefix = "MF_CASSANDRA_READER_"
envPrefixHttp = "MF_CASSANDRA_READER_HTTP_"
defSvcHttpPort = "8180"
)
type config struct {
logLevel string
port string
dbCfg cassandra.DBConfig
clientTLS bool
caCerts string
serverCert string
serverKey string
jaegerURL string
thingsAuthURL string
usersAuthURL string
thingsAuthTimeout time.Duration
usersAuthTimeout time.Duration
LogLevel string `env:"MF_CASSANDRA_READER_LOG_LEVEL" envDefault:"info"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
// Create cassandra reader service configurations
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s service configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
session := connectToCassandra(cfg.dbCfg, logger)
defer session.Close()
// Create new thing grpc client
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer tcHandler.Close()
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
conn := connectToThings(cfg, logger)
defer conn.Close()
// Create new auth grpc client
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
// Create new cassandra client
csdSession, err := cassandraClient.Setup(envPrefix)
if err != nil {
log.Fatal(err.Error())
}
defer csdSession.Close()
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
// Create new service
repo := newService(csdSession, logger)
authConn := connectToAuth(cfg, logger)
defer authConn.Close()
// Create new http server
httpServerConfig := server.Config{Port: defSvcHttpPort}
auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout)
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
repo := newService(session, logger)
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, logger), logger)
// Start servers
g.Go(func() error {
return startHTTPServer(ctx, repo, tc, auth, cfg, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Cassandra reader service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("Cassandra reader service terminated: %s", err))
}
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
logger.Info("Connecting to auth via gRPC")
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.usersAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.usersAuthURL))
return conn
}
func loadConfig() config {
dbPort, err := strconv.Atoi(mainflux.Env(envDBPort, defDBPort))
if err != nil {
log.Fatal(err)
}
dbCfg := cassandra.DBConfig{
Hosts: strings.Split(mainflux.Env(envCluster, defCluster), sep),
Keyspace: mainflux.Env(envKeyspace, defKeyspace),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Port: dbPort,
}
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
usersAuthTimeout, err := time.ParseDuration(mainflux.Env(envUsersAuthTimeout, defUsersAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
dbCfg: dbCfg,
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
usersAuthURL: mainflux.Env(envUsersAuthURL, defUsersAuthURL),
usersAuthTimeout: usersAuthTimeout,
thingsAuthTimeout: authTimeout,
}
}
func connectToCassandra(dbCfg cassandra.DBConfig, logger logger.Logger) *gocql.Session {
session, err := cassandra.Connect(dbCfg)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to Cassandra cluster: %s", err))
os.Exit(1)
}
return session
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.thingsAuthURL))
return conn
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func newService(session *gocql.Session, logger logger.Logger) readers.MessageRepository {
repo := cassandra.New(session)
func newService(csdSession *gocql.Session, logger logger.Logger) readers.MessageRepository {
repo := cassandra.New(csdSession)
repo = api.LoggingMiddleware(repo, logger)
repo = api.MetricsMiddleware(
repo,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "cassandra",
Subsystem: "message_reader",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "cassandra",
Subsystem: "message_reader",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("cassandra", "message_reader")
repo = api.MetricsMiddleware(repo, counter, latency)
return repo
}
func startHTTPServer(ctx context.Context, repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, cfg config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(repo, tc, ac, "cassandra-reader", logger)}
switch {
case cfg.serverCert != "" || cfg.serverKey != "":
logger.Info(fmt.Sprintf("Cassandra reader service started using https on port %s with cert %s key %s", cfg.port, cfg.serverCert, cfg.serverKey))
go func() {
errCh <- server.ListenAndServeTLS(cfg.serverCert, cfg.serverKey)
}()
default:
logger.Info(fmt.Sprintf("Cassandra reader service started, exposed port %s", cfg.port))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Cassandra reader service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("cassandra reader service error occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Cassandra reader service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+51 -125
View File
@@ -7,175 +7,101 @@ import (
"context"
"fmt"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/gocql/gocql"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/consumers"
"github.com/mainflux/mainflux/consumers/writers/api"
"github.com/mainflux/mainflux/consumers/writers/cassandra"
"github.com/mainflux/mainflux/internal"
cassandraClient "github.com/mainflux/mainflux/internal/clients/cassandra"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"
)
const (
svcName = "cassandra-writer"
sep = ","
stopWaitTime = 5 * time.Second
defBrokerURL = "nats://localhost:4222"
defLogLevel = "error"
defPort = "8180"
defCluster = "127.0.0.1"
defKeyspace = "mainflux"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDBPort = "9042"
defConfigPath = "/config.toml"
envBrokerURL = "MF_BROKER_URL"
envLogLevel = "MF_CASSANDRA_WRITER_LOG_LEVEL"
envPort = "MF_CASSANDRA_WRITER_PORT"
envCluster = "MF_CASSANDRA_WRITER_DB_CLUSTER"
envKeyspace = "MF_CASSANDRA_WRITER_DB_KEYSPACE"
envDBUser = "MF_CASSANDRA_WRITER_DB_USER"
envDBPass = "MF_CASSANDRA_WRITER_DB_PASS"
envDBPort = "MF_CASSANDRA_WRITER_DB_PORT"
envConfigPath = "MF_CASSANDRA_WRITER_CONFIG_PATH"
svcName = "cassandra-writer"
envPrefix = "MF_CASSANDRA_WRITER_"
envPrefixHttp = "MF_CASSANDRA_WRITER_HTTP_"
defSvcHttpPort = "8180"
)
type config struct {
brokerURL string
logLevel string
port string
configPath string
dbCfg cassandra.DBConfig
LogLevel string `env:"MF_CASSANDRA_WRITER_LOG_LEVEL" envDefault:"info"`
ConfigPath string `env:"MF_CASSANDRA_WRITER_CONFIG_PATH" envDefault:"/config.toml"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
// Create new cassandra writer service configurations
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s service configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
pubSub, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
// Create new to cassandra client
csdSession, err := cassandraClient.SetupDB(envPrefix, cassandra.Table)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatal(err.Error())
}
defer csdSession.Close()
// Create new cassandra-writer repo
repo := newService(csdSession, logger)
// Create new pub sub broker
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pubSub.Close()
session := connectToCassandra(cfg.dbCfg, logger)
defer session.Close()
repo := newService(session, logger)
if err := consumers.Start(svcName, pubSub, repo, cfg.configPath, logger); err != nil {
// Start new consumer
if err := consumers.Start(svcName, pubSub, repo, cfg.ConfigPath, logger); err != nil {
logger.Error(fmt.Sprintf("Failed to create Cassandra writer: %s", err))
}
go startHTTPServer(ctx, cfg.port, logger)
// Create new http server
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefix, AltPrefix: envPrefixHttp}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svcName), logger)
// Start servers
g.Go(func() error {
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Cassandra writer service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("Cassandra writer service terminated: %s", err))
}
}
func loadConfig() config {
dbPort, err := strconv.Atoi(mainflux.Env(envDBPort, defDBPort))
if err != nil {
log.Fatal(err)
}
dbCfg := cassandra.DBConfig{
Hosts: strings.Split(mainflux.Env(envCluster, defCluster), sep),
Keyspace: mainflux.Env(envKeyspace, defKeyspace),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Port: dbPort,
}
return config{
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
configPath: mainflux.Env(envConfigPath, defConfigPath),
dbCfg: dbCfg,
}
}
func connectToCassandra(dbCfg cassandra.DBConfig, logger logger.Logger) *gocql.Session {
session, err := cassandra.Connect(dbCfg)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to Cassandra cluster: %s", err))
os.Exit(1)
}
return session
}
func newService(session *gocql.Session, logger logger.Logger) consumers.Consumer {
repo := cassandra.New(session)
repo = api.LoggingMiddleware(repo, logger)
repo = api.MetricsMiddleware(
repo,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "cassandra",
Subsystem: "message_writer",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "cassandra",
Subsystem: "message_writer",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("cassandra", "message_writer")
repo = api.MetricsMiddleware(repo, counter, latency)
return repo
}
func startHTTPServer(ctx context.Context, port string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svcName)}
logger.Info(fmt.Sprintf("Cassandra writer service started, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Cassandra writer service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("cassandra writer service error occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Cassandra writer service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+67 -308
View File
@@ -7,99 +7,35 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/go-redis/redis/v8"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/certs"
"github.com/mainflux/mainflux/certs/api"
vault "github.com/mainflux/mainflux/certs/pki"
"github.com/mainflux/mainflux/certs/postgres"
certsPg "github.com/mainflux/mainflux/certs/postgres"
"github.com/mainflux/mainflux/internal"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"github.com/jmoiron/sqlx"
mflog "github.com/mainflux/mainflux/logger"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/pkg/errors"
mfsdk "github.com/mainflux/mainflux/pkg/sdk/go"
jconfig "github.com/uber/jaeger-client-go/config"
)
const (
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "certs"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defClientTLS = "false"
defCACerts = ""
defPort = "8204"
defServerCert = ""
defServerKey = ""
defCertsURL = "http://localhost"
defThingsURL = "http://things:8182"
defJaegerURL = ""
defAuthURL = "localhost:8181"
defAuthTimeout = "1s"
defSignCAPath = "ca.crt"
defSignCAKeyPath = "ca.key"
defSignHoursValid = "2048h"
defSignRSABits = ""
defVaultHost = ""
defVaultRole = "mainflux"
defVaultToken = ""
defVaultPKIIntPath = "pki_int"
envPort = "MF_CERTS_HTTP_PORT"
envLogLevel = "MF_CERTS_LOG_LEVEL"
envDBHost = "MF_CERTS_DB_HOST"
envDBPort = "MF_CERTS_DB_PORT"
envDBUser = "MF_CERTS_DB_USER"
envDBPass = "MF_CERTS_DB_PASS"
envDB = "MF_CERTS_DB"
envDBSSLMode = "MF_CERTS_DB_SSL_MODE"
envDBSSLCert = "MF_CERTS_DB_SSL_CERT"
envDBSSLKey = "MF_CERTS_DB_SSL_KEY"
envDBSSLRootCert = "MF_CERTS_DB_SSL_ROOT_CERT"
envClientTLS = "MF_CERTS_CLIENT_TLS"
envCACerts = "MF_CERTS_CA_CERTS"
envServerCert = "MF_CERTS_SERVER_CERT"
envServerKey = "MF_CERTS_SERVER_KEY"
envCertsURL = "MF_SDK_CERTS_URL"
envJaegerURL = "MF_JAEGER_URL"
envAuthURL = "MF_AUTH_GRPC_URL"
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
envThingsURL = "MF_THINGS_URL"
envSignCAPath = "MF_CERTS_SIGN_CA_PATH"
envSignCAKey = "MF_CERTS_SIGN_CA_KEY_PATH"
envSignHoursValid = "MF_CERTS_SIGN_HOURS_VALID"
envSignRSABits = "MF_CERTS_SIGN_RSA_BITS"
envVaultHost = "MF_CERTS_VAULT_HOST"
envVaultPKIIntPath = "MF_VAULT_PKI_INT_PATH"
envVaultRole = "MF_VAULT_CA_ROLE_NAME"
envVaultToken = "MF_VAULT_TOKEN"
svcName = "certs"
envPrefix = "MF_CERTS_"
envPrefixHttp = "MF_CERTS_HTTP_"
defDB = "certs"
defSvcHttpPort = "8204"
)
var (
@@ -110,36 +46,35 @@ var (
)
type config struct {
logLevel string
dbConfig postgres.Config
clientTLS bool
caCerts string
httpPort string
serverCert string
serverKey string
certsURL string
thingsURL string
jaegerURL string
authURL string
authTimeout time.Duration
LogLevel string `env:"MF_CERTS_LOG_LEVEL" envDefault:"info"`
CertsURL string `env:"MF_SDK_CERTS_URL" envDefault:"http://localhost"`
ThingsURL string `env:"MF_THINGS_URL" envDefault:"http://things:8182"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
// Sign and issue certificates without 3rd party PKI
signCAPath string
signCAKeyPath string
signRSABits int
signHoursValid string
SignCAPath string `env:"MF_CERTS_SIGN_CA_PATH" envDefault:"ca.crt"`
SignCAKeyPath string `env:"MF_CERTS_SIGN_CA_KEY_PATH" envDefault:"ca.key"`
// used in pki mock , need to clean up certs in separate PR
SignRSABits int `env:"MF_CERTS_SIGN_RSA_BITS," envDefault:""`
SignHoursValid string `env:"MF_CERTS_SIGN_HOURS_VALID" envDefault:"2048h"`
// 3rd party PKI API access settings
pkiPath string
pkiToken string
pkiHost string
pkiRole string
PkiHost string `env:"MF_CERTS_VAULT_HOST" envDefault:""`
PkiPath string `env:"MF_VAULT_PKI_INT_PATH" envDefault:"pki_int"`
PkiRole string `env:"MF_VAULT_CA_ROLE_NAME" envDefault:"mainflux"`
PkiToken string `env:"MF_VAULT_TOKEN" envDefault:""`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := mflog.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
@@ -149,38 +84,43 @@ func main() {
logger.Error("Failed to load CA certificates for issuing client certs")
}
if cfg.pkiHost == "" {
if cfg.PkiHost == "" {
log.Fatalf("No host specified for PKI engine")
}
pkiClient, err := vault.NewVaultClient(cfg.pkiToken, cfg.pkiHost, cfg.pkiPath, cfg.pkiRole)
pkiClient, err := vault.NewVaultClient(cfg.PkiToken, cfg.PkiHost, cfg.PkiPath, cfg.PkiRole)
if err != nil {
log.Fatalf("Failed to configure client for PKI engine")
log.Fatalf("failed to configure client for PKI engine")
}
db := connectToDB(cfg.dbConfig, logger)
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *certsPg.Migration(), dbConfig)
if err != nil {
log.Fatal(err)
}
defer db.Close()
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
authConn := connectToAuth(cfg, logger)
defer authConn.Close()
auth := authapi.NewClient(authTracer, authConn, cfg.authTimeout)
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
svc := newService(auth, db, logger, nil, tlsCert, caCert, cfg, pkiClient)
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, svc, cfg, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Certs service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -188,223 +128,42 @@ func main() {
}
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
tls = false
}
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
authTimeout, err := time.ParseDuration(mainflux.Env(envAuthTimeout, defAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envAuthTimeout, err.Error())
}
signRSABits, err := strconv.Atoi(mainflux.Env(envSignRSABits, defSignRSABits))
if err != nil {
log.Fatalf("Invalid %s value: %s", envSignRSABits, err.Error())
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
dbConfig: dbConfig,
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
httpPort: mainflux.Env(envPort, defPort),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
certsURL: mainflux.Env(envCertsURL, defCertsURL),
thingsURL: mainflux.Env(envThingsURL, defThingsURL),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
authURL: mainflux.Env(envAuthURL, defAuthURL),
authTimeout: authTimeout,
signCAKeyPath: mainflux.Env(envSignCAKey, defSignCAKeyPath),
signCAPath: mainflux.Env(envSignCAPath, defSignCAPath),
signHoursValid: mainflux.Env(envSignHoursValid, defSignHoursValid),
signRSABits: signRSABits,
pkiToken: mainflux.Env(envVaultToken, defVaultToken),
pkiPath: mainflux.Env(envVaultPKIIntPath, defVaultPKIIntPath),
pkiRole: mainflux.Env(envVaultRole, defVaultRole),
pkiHost: mainflux.Env(envVaultHost, defVaultHost),
}
}
func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
db, err := postgres.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err))
os.Exit(1)
}
return db
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.authURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
return conn
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func newService(auth mainflux.AuthServiceClient, db *sqlx.DB, logger mflog.Logger, esClient *redis.Client, tlsCert tls.Certificate, x509Cert *x509.Certificate, cfg config, pkiAgent vault.Agent) certs.Service {
certsRepo := postgres.NewRepository(db, logger)
certsConfig := certs.Config{
LogLevel: cfg.logLevel,
ClientTLS: cfg.clientTLS,
CaCerts: cfg.caCerts,
HTTPPort: cfg.httpPort,
ServerCert: cfg.serverCert,
ServerKey: cfg.serverKey,
CertsURL: cfg.certsURL,
JaegerURL: cfg.jaegerURL,
AuthURL: cfg.authURL,
AuthTimeout: cfg.authTimeout,
SignTLSCert: tlsCert,
SignX509Cert: x509Cert,
SignHoursValid: cfg.signHoursValid,
SignRSABits: cfg.signRSABits,
PKIToken: cfg.pkiToken,
PKIHost: cfg.pkiHost,
PKIPath: cfg.pkiPath,
PKIRole: cfg.pkiRole,
}
func newService(auth mainflux.AuthServiceClient, db *sqlx.DB, logger logger.Logger, esClient *redis.Client, tlsCert tls.Certificate, x509Cert *x509.Certificate, cfg config, pkiAgent vault.Agent) certs.Service {
certsRepo := certsPg.NewRepository(db, logger)
config := mfsdk.Config{
CertsURL: cfg.certsURL,
ThingsURL: cfg.thingsURL,
CertsURL: cfg.CertsURL,
ThingsURL: cfg.ThingsURL,
}
sdk := mfsdk.NewSDK(config)
svc := certs.New(auth, certsRepo, sdk, certsConfig, pkiAgent)
svc := certs.New(auth, certsRepo, sdk, pkiAgent)
svc = api.NewLoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "certs",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "certs",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, svc certs.Service, cfg config, logger mflog.Logger) error {
p := fmt.Sprintf(":%s", cfg.httpPort)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, logger)}
switch {
case cfg.serverCert != "" || cfg.serverKey != "":
logger.Info(fmt.Sprintf("Certs service started using https on port %s with cert %s key %s", cfg.httpPort, cfg.serverCert, cfg.serverKey))
go func() {
errCh <- server.ListenAndServeTLS(cfg.serverCert, cfg.serverKey)
}()
default:
logger.Info(fmt.Sprintf("Certs service started using http on port %s", cfg.httpPort))
go func() {
errCh <- http.ListenAndServe(p, api.MakeHandler(svc, logger))
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Certs service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("certs service error occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Certs service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
func loadCertificates(conf config) (tls.Certificate, *x509.Certificate, error) {
var tlsCert tls.Certificate
var caCert *x509.Certificate
if conf.signCAPath == "" || conf.signCAKeyPath == "" {
if conf.SignCAPath == "" || conf.SignCAKeyPath == "" {
return tlsCert, caCert, nil
}
if _, err := os.Stat(conf.signCAPath); os.IsNotExist(err) {
if _, err := os.Stat(conf.SignCAPath); os.IsNotExist(err) {
return tlsCert, caCert, errCACertificateNotExist
}
if _, err := os.Stat(conf.signCAKeyPath); os.IsNotExist(err) {
if _, err := os.Stat(conf.SignCAKeyPath); os.IsNotExist(err) {
return tlsCert, caCert, errCAKeyNotExist
}
tlsCert, err := tls.LoadX509KeyPair(conf.signCAPath, conf.signCAKeyPath)
tlsCert, err := tls.LoadX509KeyPair(conf.SignCAPath, conf.SignCAKeyPath)
if err != nil {
return tlsCert, caCert, errors.Wrap(errFailedCertLoading, err)
}
b, err := ioutil.ReadFile(conf.signCAPath)
b, err := os.ReadFile(conf.SignCAPath)
if err != nil {
return tlsCert, caCert, errors.Wrap(errFailedCertLoading, err)
}
+47 -191
View File
@@ -6,86 +6,61 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/coap"
"github.com/mainflux/mainflux/coap/api"
"github.com/mainflux/mainflux/internal"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
coapserver "github.com/mainflux/mainflux/internal/server/coap"
httpserver "github.com/mainflux/mainflux/internal/server/http"
logger "github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
opentracing "github.com/opentracing/opentracing-go"
gocoap "github.com/plgd-dev/go-coap/v2"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
stopWaitTime = 5 * time.Second
defPort = "5683"
defBrokerURL = "nats://localhost:4222"
defLogLevel = "error"
defClientTLS = "false"
defCACerts = ""
defJaegerURL = ""
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
envPort = "MF_COAP_ADAPTER_PORT"
envBrokerURL = "MF_BROKER_URL"
envLogLevel = "MF_COAP_ADAPTER_LOG_LEVEL"
envClientTLS = "MF_COAP_ADAPTER_CLIENT_TLS"
envCACerts = "MF_COAP_ADAPTER_CA_CERTS"
envJaegerURL = "MF_JAEGER_URL"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
svcName = "coap_adapter"
envPrefix = "MF_COAP_ADAPTER_"
envPrefixHttp = "MF_COAP_ADAPTER_HTTP_"
envPrefixCoap = "MF_COAP_ADAPTER_COAP_"
defSvcHttpPort = "5683"
defSvcCoapPort = "5683"
)
type config struct {
port string
brokerURL string
logLevel string
clientTLS bool
caCerts string
jaegerURL string
thingsAuthURL string
thingsAuthTimeout time.Duration
LogLevel string `env:"MF_INFLUX_READER_LOG_LEVEL" envDefault:"info"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
conn := connectToThings(cfg, logger)
defer conn.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
nps, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatal(err.Error())
}
defer tcHandler.Close()
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
nps, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer nps.Close()
@@ -93,151 +68,32 @@ func main() {
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "coap_adapter",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "coap_adapter",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHTTPHandler(), logger)
coapServerConfig := server.Config{Port: defSvcCoapPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixCoap, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s CoAP server configuration : %s", svcName, err.Error())
}
cs := coapserver.New(ctx, cancel, svcName, coapServerConfig, api.MakeCoAPHandler(svc, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, cfg.port, logger)
return hs.Start()
})
g.Go(func() error {
return startCOAPServer(ctx, cfg, svc, nil, logger)
return cs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("CoAP adapter service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs, cs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("CoAP adapter service terminated: %s", err))
}
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
return config{
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
port: mainflux.Env(envPort, defPort),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
thingsAuthTimeout: authTimeout,
}
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
return conn
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func startHTTPServer(ctx context.Context, port string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHTTPHandler()}
logger.Info(fmt.Sprintf("CoAP service started, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("CoAP adapter service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("CoAP adapter service error occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("CoAP adapter service shutdown at %s", p))
return nil
case err := <-errCh:
return err
}
}
func startCOAPServer(ctx context.Context, cfg config, svc coap.Service, auth mainflux.ThingsServiceClient, l logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.port)
errCh := make(chan error)
l.Info(fmt.Sprintf("CoAP adapter service started, exposed port %s", cfg.port))
go func() {
errCh <- gocoap.ListenAndServe("udp", p, api.MakeCoAPHandler(svc, l))
}()
select {
case <-ctx.Done():
l.Info(fmt.Sprintf("CoAP adapter service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+50 -173
View File
@@ -6,218 +6,95 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
"google.golang.org/grpc/credentials"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/mainflux/mainflux"
adapter "github.com/mainflux/mainflux/http"
"github.com/mainflux/mainflux/http/api"
"github.com/mainflux/mainflux/internal"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
jaegerClient "github.com/mainflux/mainflux/internal/clients/jaeger"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
"github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
)
const (
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defClientTLS = "false"
defCACerts = ""
defPort = "8180"
defBrokerURL = "nats://localhost:4222"
defJaegerURL = ""
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
envLogLevel = "MF_HTTP_ADAPTER_LOG_LEVEL"
envClientTLS = "MF_HTTP_ADAPTER_CLIENT_TLS"
envCACerts = "MF_HTTP_ADAPTER_CA_CERTS"
envPort = "MF_HTTP_ADAPTER_PORT"
envBrokerURL = "MF_BROKER_URL"
envJaegerURL = "MF_JAEGER_URL"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
svcName = "http_adapter"
envPrefix = "MF_HTTP_ADAPTER_"
envPrefixHttp = "MF_HTTP_ADAPTER_HTTP_"
defSvcHttpPort = "8180"
)
type config struct {
brokerURL string
logLevel string
port string
clientTLS bool
caCerts string
jaegerURL string
thingsAuthURL string
thingsAuthTimeout time.Duration
LogLevel string `env:"MF_HTTP_ADAPTER_LOG_LEVEL" envDefault:"info"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s service configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
conn := connectToThings(cfg, logger)
defer conn.Close()
tracer, closer := initJaeger("http_adapter", cfg.jaegerURL, logger)
defer closer.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
pub, err := brokers.NewPublisher(cfg.brokerURL)
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatal(err.Error())
}
defer tcHandler.Close()
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
pub, err := brokers.NewPublisher(cfg.BrokerURL)
if err != nil {
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pub.Close()
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
svc := adapter.New(pub, tc)
svc := newService(pub, tc, logger)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "http_adapter",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "http_adapter",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
tracer, closer, err := jaegerClient.NewTracer("http_adapter", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer closer.Close()
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, tracer, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, svc, cfg, logger, tracer)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("HTTP adapter service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("HTTP adapter service terminated: %s", err))
}
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
return config{
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
thingsAuthTimeout: authTimeout,
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
return conn
}
func startHTTPServer(ctx context.Context, svc adapter.Service, cfg config, logger logger.Logger, tracer opentracing.Tracer) error {
p := fmt.Sprintf(":%s", cfg.port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, tracer, logger)}
logger.Info(fmt.Sprintf("HTTP adapter service started on port %s", cfg.port))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("HTTP adapter service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("http adapter service error occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("HTTP adapter service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
func newService(pub messaging.Publisher, tc mainflux.ThingsServiceClient, logger logger.Logger) adapter.Service {
svc := adapter.New(pub, tc)
svc = api.LoggingMiddleware(svc, logger)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
+51 -261
View File
@@ -3,133 +3,89 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
influxdata "github.com/influxdata/influxdb/client/v2"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
influxDBClient "github.com/mainflux/mainflux/internal/clients/influxdb"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/readers"
"github.com/mainflux/mainflux/readers/api"
"github.com/mainflux/mainflux/readers/influxdb"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defPort = "8180"
defDB = "mainflux"
defDBHost = "localhost"
defDBPort = "8086"
defDBUser = "mainflux"
defDBPass = "mainflux"
defClientTLS = "false"
defCACerts = ""
defServerCert = ""
defServerKey = ""
defJaegerURL = ""
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
defUsersAuthURL = "localhost:8181"
defUsersAuthTimeout = "1s"
envLogLevel = "MF_INFLUX_READER_LOG_LEVEL"
envPort = "MF_INFLUX_READER_PORT"
envDB = "MF_INFLUXDB_DB"
envDBHost = "MF_INFLUXDB_HOST"
envDBPort = "MF_INFLUXDB_PORT"
envDBUser = "MF_INFLUXDB_ADMIN_USER"
envDBPass = "MF_INFLUXDB_ADMIN_PASSWORD"
envClientTLS = "MF_INFLUX_READER_CLIENT_TLS"
envCACerts = "MF_INFLUX_READER_CA_CERTS"
envServerCert = "MF_INFLUX_READER_SERVER_CERT"
envServerKey = "MF_INFLUX_READER_SERVER_KEY"
envJaegerURL = "MF_JAEGER_URL"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
envAuthURL = "MF_AUTH_GRPC_URL"
envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
svcName = "influxdb-reader"
envPrefix = "MF_INFLUX_READER_"
envPrefixHttp = "MF_INFLUX_READER_HTTP_"
envPrefixInfluxdb = "MF_INFLUXDB_"
defSvcHttpPort = "8180"
)
type config struct {
logLevel string
port string
dbName string
dbHost string
dbPort string
dbUser string
dbPass string
clientTLS bool
caCerts string
serverCert string
serverKey string
jaegerURL string
thingsAuthURL string
usersAuthURL string
thingsAuthTimeout time.Duration
usersAuthTimeout time.Duration
LogLevel string `env:"MF_INFLUX_READER_LOG_LEVEL" envDefault:"info"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg, clientCfg := loadConfigs()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
conn := connectToThings(cfg, logger)
defer conn.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
authConn := connectToAuth(cfg, logger)
defer authConn.Close()
auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout)
client, err := influxdata.NewHTTPClient(clientCfg)
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
logger.Error(fmt.Sprintf("Failed to create InfluxDB client: %s", err))
os.Exit(1)
log.Fatal(err.Error())
}
defer tcHandler.Close()
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
influxDBConfig := influxDBClient.Config{}
if err := env.Parse(&influxDBConfig, env.Options{Prefix: envPrefixInfluxdb}); err != nil {
log.Fatalf("failed to load InfluxDB client configuration from environment variable : %s", err.Error())
}
client, err := influxDBClient.Connect(influxDBConfig)
if err != nil {
log.Fatalf("failed to connect to InfluxDB : %s", err.Error())
}
defer client.Close()
repo := newService(client, cfg.dbName, logger)
repo := newService(client, influxDBConfig.DbName, logger)
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, repo, tc, auth, cfg, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("InfluxDB reader service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -137,177 +93,11 @@ func main() {
}
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
logger.Info("Connecting to auth via gRPC")
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.usersAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
logger.Info("Established gRPC connection to auth via gRPC")
return conn
}
func loadConfigs() (config, influxdata.HTTPConfig) {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
userAuthTimeout, err := time.ParseDuration(mainflux.Env(envUsersAuthTimeout, defUsersAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
cfg := config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
dbName: mainflux.Env(envDB, defDB),
dbHost: mainflux.Env(envDBHost, defDBHost),
dbPort: mainflux.Env(envDBPort, defDBPort),
dbUser: mainflux.Env(envDBUser, defDBUser),
dbPass: mainflux.Env(envDBPass, defDBPass),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
thingsAuthTimeout: authTimeout,
usersAuthURL: mainflux.Env(envAuthURL, defUsersAuthURL),
usersAuthTimeout: userAuthTimeout,
}
clientCfg := influxdata.HTTPConfig{
Addr: fmt.Sprintf("http://%s:%s", cfg.dbHost, cfg.dbPort),
Username: cfg.dbUser,
Password: cfg.dbPass,
}
return cfg, clientCfg
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
logger.Info("connecting to things via gRPC")
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.thingsAuthURL))
return conn
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func newService(client influxdata.Client, dbName string, logger logger.Logger) readers.MessageRepository {
repo := influxdb.New(client, dbName)
repo = api.LoggingMiddleware(repo, logger)
repo = api.MetricsMiddleware(
repo,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "influxdb",
Subsystem: "message_reader",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "influxdb",
Subsystem: "message_reader",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("influxdb", "message_reader")
repo = api.MetricsMiddleware(repo, counter, latency)
return repo
}
func startHTTPServer(ctx context.Context, repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, cfg config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(repo, tc, ac, "influxdb-reader", logger)}
switch {
case cfg.serverCert != "" || cfg.serverKey != "":
logger.Info(fmt.Sprintf("InfluxDB reader service started using https on port %s with cert %s key %s",
cfg.port, cfg.serverCert, cfg.serverKey))
go func() {
errCh <- server.ListenAndServeTLS(cfg.serverCert, cfg.serverKey)
}()
default:
logger.Info(fmt.Sprintf("InfluxDB reader service started, exposed port %s", cfg.port))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("InfluxDB reader service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("influxDB reader service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("InfluxDB reader service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+44 -123
View File
@@ -7,105 +7,84 @@ import (
"context"
"fmt"
"log"
"net/http"
"os"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
influxdata "github.com/influxdata/influxdb/client/v2"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/consumers"
"github.com/mainflux/mainflux/consumers/writers/api"
"github.com/mainflux/mainflux/consumers/writers/influxdb"
"github.com/mainflux/mainflux/internal"
influxDBClient "github.com/mainflux/mainflux/internal/clients/influxdb"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"
)
const (
svcName = "influxdb-writer"
stopWaitTime = 5 * time.Second
defBrokerURL = "nats://localhost:4222"
defLogLevel = "error"
defPort = "8180"
defDB = "mainflux"
defDBHost = "localhost"
defDBPort = "8086"
defDBUser = "mainflux"
defDBPass = "mainflux"
defConfigPath = "/config.toml"
envBrokerURL = "MF_BROKER_URL"
envLogLevel = "MF_INFLUX_WRITER_LOG_LEVEL"
envPort = "MF_INFLUX_WRITER_PORT"
envDB = "MF_INFLUXDB_DB"
envDBHost = "MF_INFLUXDB_HOST"
envDBPort = "MF_INFLUXDB_PORT"
envDBUser = "MF_INFLUXDB_ADMIN_USER"
envDBPass = "MF_INFLUXDB_ADMIN_PASSWORD"
envConfigPath = "MF_INFLUX_WRITER_CONFIG_PATH"
svcName = "influxdb-writer"
envPrefix = "MF_INFLUX_WRITER_"
envPrefixHttp = "MF_INFLUX_WRITER_HTTP_"
envPrefixInfluxdb = "MF_INFLUXDB_"
defSvcHttpPort = "8180"
)
type config struct {
brokerURL string
logLevel string
port string
dbName string
dbHost string
dbPort string
dbUser string
dbPass string
configPath string
LogLevel string `env:"MF_INFLUX_WRITER_LOG_LEVEL" envDefault:"info"`
ConfigPath string `env:"MF_INFLUX_WRITER_CONFIG_PATH" envDefault:"/config.toml"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
}
func main() {
cfg, clientCfg := loadConfigs()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
pubSub, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pubSub.Close()
client, err := influxdata.NewHTTPClient(clientCfg)
influxDBConfig := influxDBClient.Config{}
if err := env.Parse(&influxDBConfig, env.Options{Prefix: envPrefixInfluxdb}); err != nil {
log.Fatalf("failed to load InfluxDB client configuration from environment variable : %s", err.Error())
}
client, err := influxDBClient.Connect(influxDBConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to create InfluxDB client: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to InfluxDB : %s", err.Error())
}
defer client.Close()
repo := influxdb.New(client, cfg.dbName)
repo := newService(client, influxDBConfig.DbName, logger)
counter, latency := makeMetrics()
repo = api.LoggingMiddleware(repo, logger)
repo = api.MetricsMiddleware(repo, counter, latency)
if err := consumers.Start(svcName, pubSub, repo, cfg.configPath, logger); err != nil {
logger.Error(fmt.Sprintf("Failed to start InfluxDB writer: %s", err))
os.Exit(1)
if err := consumers.Start(svcName, pubSub, repo, cfg.ConfigPath, logger); err != nil {
log.Fatalf("failed to start InfluxDB writer: %s", err.Error())
}
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svcName), logger)
g.Go(func() error {
return startHTTPService(ctx, cfg.port, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("InfluxDB reader service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -113,68 +92,10 @@ func main() {
}
}
func loadConfigs() (config, influxdata.HTTPConfig) {
cfg := config{
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
dbName: mainflux.Env(envDB, defDB),
dbHost: mainflux.Env(envDBHost, defDBHost),
dbPort: mainflux.Env(envDBPort, defDBPort),
dbUser: mainflux.Env(envDBUser, defDBUser),
dbPass: mainflux.Env(envDBPass, defDBPass),
configPath: mainflux.Env(envConfigPath, defConfigPath),
}
clientCfg := influxdata.HTTPConfig{
Addr: fmt.Sprintf("http://%s:%s", cfg.dbHost, cfg.dbPort),
Username: cfg.dbUser,
Password: cfg.dbPass,
}
return cfg, clientCfg
}
func makeMetrics() (*kitprometheus.Counter, *kitprometheus.Summary) {
counter := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "influxdb",
Subsystem: "message_writer",
Name: "request_count",
Help: "Number of database inserts.",
}, []string{"method"})
latency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "influxdb",
Subsystem: "message_writer",
Name: "request_latency_microseconds",
Help: "Total duration of inserts in microseconds.",
}, []string{"method"})
return counter, latency
}
func startHTTPService(ctx context.Context, port string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svcName)}
logger.Info(fmt.Sprintf("InfluxDB writer service started, exposed port %s", p))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("InfluxDB writer service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("influxDB writer service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("InfluxDB writer service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
func newService(client influxdata.Client, dbName string, logger logger.Logger) consumers.Consumer {
repo := influxdb.New(client, dbName)
repo = api.LoggingMiddleware(repo, logger)
counter, latency := internal.MakeMetrics("influxdb", "message_writer")
repo = api.MetricsMiddleware(repo, counter, latency)
return repo
}
+61 -161
View File
@@ -7,61 +7,34 @@ import (
"context"
"fmt"
"log"
"net/http"
"os"
"strconv"
"time"
mqttPaho "github.com/eclipse/paho.mqtt.golang"
r "github.com/go-redis/redis/v8"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/internal"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/lora"
"github.com/mainflux/mainflux/lora/api"
"github.com/mainflux/mainflux/lora/mqtt"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
"golang.org/x/sync/errgroup"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
redisClient "github.com/mainflux/mainflux/internal/clients/redis"
"github.com/mainflux/mainflux/lora/redis"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)
const (
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defHTTPPort = "8180"
defLoraMsgURL = "tcp://localhost:1883"
defBrokerURL = "nats://localhost:4222"
defLoraMsgTopic = "application/+/device/+/event/up"
defLoraMsgUser = ""
defLoraMsgPass = ""
defLoraMsgTimeout = "30s"
defESURL = "localhost:6379"
defESPass = ""
defESDB = "0"
defESConsumerName = "lora"
defRouteMapURL = "localhost:6379"
defRouteMapPass = ""
defRouteMapDB = "0"
envHTTPPort = "MF_LORA_ADAPTER_HTTP_PORT"
envLoraMsgURL = "MF_LORA_ADAPTER_MESSAGES_URL"
envBrokerURL = "MF_BROKER_URL"
envLoraMsgTopic = "MF_LORA_ADAPTER_MESSAGES_TOPIC"
envLoraMsgUser = "MF_LORA_ADAPTER_MESSAGES_USER"
envLoraMsgPass = "MF_LORA_ADAPTER_MESSAGES_PASS"
envLoraMsgTimeout = "MF_LORA_ADAPTER_MESSAGES_TIMEOUT"
envLogLevel = "MF_LORA_ADAPTER_LOG_LEVEL"
envESURL = "MF_THINGS_ES_URL"
envESPass = "MF_THINGS_ES_PASS"
envESDB = "MF_THINGS_ES_DB"
envESConsumerName = "MF_LORA_ADAPTER_EVENT_CONSUMER"
envRouteMapURL = "MF_LORA_ADAPTER_ROUTE_MAP_URL"
envRouteMapPass = "MF_LORA_ADAPTER_ROUTE_MAP_PASS"
envRouteMapDB = "MF_LORA_ADAPTER_ROUTE_MAP_DB"
svcName = "lora-adapter"
envPrefix = "MF_LORA_ADAPTER_"
envPrefixHttp = "MF_LORA_ADAPTER_HTTP_"
envPrefixRouteMap = "MF_LORA_ADAPTER_ROUTE_MAP_"
envPrefixThingsES = "MF_THINGS_ES_"
defSvcHttpPort = "8180"
thingsRMPrefix = "thing"
channelsRMPrefix = "channel"
@@ -69,114 +42,72 @@ const (
)
type config struct {
httpPort string
loraMsgURL string
brokerURL string
loraMsgUser string
loraMsgPass string
loraMsgTopic string
loraMsgTimeout time.Duration
logLevel string
esURL string
esPass string
esDB string
esConsumerName string
routeMapURL string
routeMapPass string
routeMapDB string
LogLevel string `env:"MF_BOOTSTRAP_LOG_LEVEL" envDefault:"info"`
LoraMsgURL string `env:"MF_LORA_ADAPTER_MESSAGES_URL" envDefault:"tcp://localhost:1883"`
LoraMsgUser string `env:"MF_LORA_ADAPTER_MESSAGES_USER" envDefault:""`
LoraMsgPass string `env:"MF_LORA_ADAPTER_MESSAGES_PASS" envDefault:""`
LoraMsgTopic string `env:"MF_LORA_ADAPTER_MESSAGES_TOPIC" envDefault:"application/+/device/+/event/up"`
LoraMsgTimeout time.Duration `env:"MF_LORA_ADAPTER_MESSAGES_TIMEOUT" envDefault:"30s"`
ESConsumerName string `env:"MF_LORA_ADAPTER_EVENT_CONSUMER" envDefault:"lora"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
rmConn := connectToRedis(cfg.routeMapURL, cfg.routeMapPass, cfg.routeMapDB, logger)
rmConn, err := redisClient.Setup(envPrefixRouteMap)
if err != nil {
log.Fatalf("failed to setup route map redis client : %s", err.Error())
}
defer rmConn.Close()
esConn := connectToRedis(cfg.esURL, cfg.esPass, cfg.esDB, logger)
defer esConn.Close()
pub, err := brokers.NewPublisher(cfg.brokerURL)
pub, err := brokers.NewPublisher(cfg.BrokerURL)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pub.Close()
thingsRM := newRouteMapRepository(rmConn, thingsRMPrefix, logger)
chansRM := newRouteMapRepository(rmConn, channelsRMPrefix, logger)
connsRM := newRouteMapRepository(rmConn, connsRMPrefix, logger)
svc := newService(pub, rmConn, thingsRMPrefix, channelsRMPrefix, connsRMPrefix, logger)
svc := lora.New(pub, thingsRM, chansRM, connsRM)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "lora_adapter",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "lora_adapter",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
esConn, err := redisClient.Setup(envPrefixThingsES)
if err != nil {
log.Fatalf("failed to setup things event store redis client : %s", err.Error())
}
defer esConn.Close()
mqttConn := connectToMQTTBroker(cfg.loraMsgURL, cfg.loraMsgUser, cfg.loraMsgPass, cfg.loraMsgTimeout, logger)
mqttConn := connectToMQTTBroker(cfg.LoraMsgURL, cfg.LoraMsgUser, cfg.LoraMsgPass, cfg.LoraMsgTimeout, logger)
go subscribeToLoRaBroker(svc, mqttConn, cfg.loraMsgTimeout, cfg.loraMsgTopic, logger)
go subscribeToThingsES(svc, esConn, cfg.esConsumerName, logger)
go subscribeToLoRaBroker(svc, mqttConn, cfg.LoraMsgTimeout, cfg.LoraMsgTopic, logger)
go subscribeToThingsES(svc, esConn, cfg.ESConsumerName, logger)
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(), logger)
g.Go(func() error {
return startHTTPServer(ctx, cfg, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("LoRa adapter shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("LoRa adapter terminated: %s", err))
}
}
func loadConfig() config {
mqttTimeout, err := time.ParseDuration(mainflux.Env(envLoraMsgTimeout, defLoraMsgTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envLoraMsgTimeout, err.Error())
}
return config{
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
loraMsgURL: mainflux.Env(envLoraMsgURL, defLoraMsgURL),
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
loraMsgTopic: mainflux.Env(envLoraMsgTopic, defLoraMsgTopic),
loraMsgUser: mainflux.Env(envLoraMsgUser, defLoraMsgUser),
loraMsgPass: mainflux.Env(envLoraMsgPass, defLoraMsgPass),
loraMsgTimeout: mqttTimeout,
logLevel: mainflux.Env(envLogLevel, defLogLevel),
esURL: mainflux.Env(envESURL, defESURL),
esPass: mainflux.Env(envESPass, defESPass),
esDB: mainflux.Env(envESDB, defESDB),
esConsumerName: mainflux.Env(envESConsumerName, defESConsumerName),
routeMapURL: mainflux.Env(envRouteMapURL, defRouteMapURL),
routeMapPass: mainflux.Env(envRouteMapPass, defRouteMapPass),
routeMapDB: mainflux.Env(envRouteMapDB, defRouteMapDB),
}
}
func connectToMQTTBroker(url, user, password string, timeout time.Duration, logger logger.Logger) mqttPaho.Client {
@@ -188,40 +119,23 @@ func connectToMQTTBroker(url, user, password string, timeout time.Duration, logg
logger.Info("Connected to Lora MQTT broker")
})
opts.SetConnectionLostHandler(func(c mqttPaho.Client, err error) {
logger.Error(fmt.Sprintf("MQTT connection lost: %s", err.Error()))
os.Exit(1)
log.Fatalf("MQTT connection lost: %s", err.Error())
})
client := mqttPaho.NewClient(opts)
if token := client.Connect(); token.WaitTimeout(timeout) && token.Error() != nil {
logger.Error(fmt.Sprintf("Failed to connect to Lora MQTT broker: %s", token.Error()))
os.Exit(1)
log.Fatalf("failed to connect to Lora MQTT broker: %s", token.Error())
}
return client
}
func connectToRedis(redisURL, redisPass, redisDB string, logger logger.Logger) *r.Client {
db, err := strconv.Atoi(redisDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to redis: %s", err))
os.Exit(1)
}
return r.NewClient(&r.Options{
Addr: redisURL,
Password: redisPass,
DB: db,
})
}
func subscribeToLoRaBroker(svc lora.Service, mc mqttPaho.Client, timeout time.Duration, topic string, logger logger.Logger) {
mqtt := mqtt.NewBroker(svc, mc, timeout, logger)
logger.Info("Subscribed to Lora MQTT broker")
if err := mqtt.Subscribe(topic); err != nil {
logger.Error(fmt.Sprintf("Failed to subscribe to Lora MQTT broker: %s", err))
os.Exit(1)
log.Fatalf("failed to subscribe to Lora MQTT broker: %s", err.Error())
}
}
@@ -238,29 +152,15 @@ func newRouteMapRepository(client *r.Client, prefix string, logger logger.Logger
return redis.NewRouteMapRepository(client, prefix)
}
func startHTTPServer(ctx context.Context, cfg config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.httpPort)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler()}
func newService(pub messaging.Publisher, rmConn *r.Client, thingsRMPrefix, channelsRMPrefix, connsRMPrefix string, logger logger.Logger) lora.Service {
thingsRM := newRouteMapRepository(rmConn, thingsRMPrefix, logger)
chansRM := newRouteMapRepository(rmConn, channelsRMPrefix, logger)
connsRM := newRouteMapRepository(rmConn, connsRMPrefix, logger)
logger.Info(fmt.Sprintf("LoRa-adapter service started, exposed port %s", cfg.httpPort))
go func() {
errCh <- http.ListenAndServe(p, api.MakeHandler())
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("LoRa-adapter service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("LoRa-adapter service error occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("LoRa-adapter service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
svc := lora.New(pub, thingsRM, chansRM, connsRM)
svc = api.LoggingMiddleware(svc, logger)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
+49 -255
View File
@@ -6,123 +6,84 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
mongoClient "github.com/mainflux/mainflux/internal/clients/mongo"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/readers"
"github.com/mainflux/mainflux/readers/api"
"github.com/mainflux/mainflux/readers/mongodb"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defPort = "8180"
defDB = "mainflux"
defDBHost = "localhost"
defDBPort = "27017"
defClientTLS = "false"
defCACerts = ""
defServerCert = ""
defServerKey = ""
defJaegerURL = ""
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
defUsersAuthURL = "localhost:8181"
defUsersAuthTimeout = "1s"
envLogLevel = "MF_MONGO_READER_LOG_LEVEL"
envPort = "MF_MONGO_READER_PORT"
envDB = "MF_MONGO_READER_DB"
envDBHost = "MF_MONGO_READER_DB_HOST"
envDBPort = "MF_MONGO_READER_DB_PORT"
envClientTLS = "MF_MONGO_READER_CLIENT_TLS"
envCACerts = "MF_MONGO_READER_CA_CERTS"
envServerCert = "MF_MONGO_READER_SERVER_CERT"
envServerKey = "MF_MONGO_READER_SERVER_KEY"
envJaegerURL = "MF_JAEGER_URL"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
envUsersAuthURL = "MF_AUTH_GRPC_URL"
envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
svcName = "mongodb-reader"
envPrefix = "MF_MONGO_READER_"
envPrefixDB = "MF_MONGO_READER_DB_"
envPrefixHttp = "MF_MONGO_READER_HTTP_"
defSvcHttpPort = "8180"
)
type config struct {
logLevel string
port string
dbName string
dbHost string
dbPort string
clientTLS bool
caCerts string
serverCert string
serverKey string
jaegerURL string
thingsAuthURL string
usersAuthURL string
thingsAuthTimeout time.Duration
usersAuthTimeout time.Duration
LogLevel string `env:"MF_MONGO_READER_LOG_LEVEL" envDefault:"info"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfigs()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
conn := connectToThings(cfg, logger)
defer conn.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
authConn := connectToAuth(cfg, logger)
defer authConn.Close()
auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout)
db := connectToMongoDB(cfg.dbHost, cfg.dbPort, cfg.dbName, logger)
db, err := mongoClient.Setup(envPrefixDB)
if err != nil {
log.Fatalf("failed to setup mongo database : %s", err.Error())
}
repo := newService(db, logger)
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer tcHandler.Close()
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, repo, tc, auth, cfg, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("MongoDB reader service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -131,178 +92,11 @@ func main() {
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
logger.Info("Connecting to auth via gRPC")
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.usersAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Established gRPC connection to auth via gRPC: %s", cfg.usersAuthURL))
return conn
}
func loadConfigs() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
usersAuthTimeout, err := time.ParseDuration(mainflux.Env(envUsersAuthTimeout, defUsersAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
dbName: mainflux.Env(envDB, defDB),
dbHost: mainflux.Env(envDBHost, defDBHost),
dbPort: mainflux.Env(envDBPort, defDBPort),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
usersAuthURL: mainflux.Env(envUsersAuthURL, defUsersAuthURL),
thingsAuthTimeout: authTimeout,
usersAuthTimeout: usersAuthTimeout,
}
}
func connectToMongoDB(host, port, name string, logger logger.Logger) *mongo.Database {
addr := fmt.Sprintf("mongodb://%s:%s", host, port)
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(addr))
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to database: %s", err))
os.Exit(1)
}
return client.Database(name)
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.thingsAuthURL))
return conn
}
func newService(db *mongo.Database, logger logger.Logger) readers.MessageRepository {
repo := mongodb.New(db)
repo = api.LoggingMiddleware(repo, logger)
repo = api.MetricsMiddleware(
repo,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "mongodb",
Subsystem: "message_reader",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "mongodb",
Subsystem: "message_reader",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("mongodb", "message_reader")
repo = api.MetricsMiddleware(repo, counter, latency)
return repo
}
func startHTTPServer(ctx context.Context, repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, cfg config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(repo, tc, ac, "mongodb-reader", logger)}
switch {
case cfg.serverCert != "" || cfg.serverKey != "":
logger.Info(fmt.Sprintf("Mongo reader service started using https on port %s with cert %s key %s",
cfg.port, cfg.serverCert, cfg.serverKey))
go func() {
errCh <- server.ListenAndServeTLS(cfg.serverCert, cfg.serverKey)
}()
default:
logger.Info(fmt.Sprintf("Mongo reader service started, exposed port %s", cfg.port))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("MongoDB reader service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("mongodb reader service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("MongoDB reader service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+40 -112
View File
@@ -7,162 +7,90 @@ import (
"context"
"fmt"
"log"
"net/http"
"os"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/consumers"
"github.com/mainflux/mainflux/consumers/writers/api"
"github.com/mainflux/mainflux/consumers/writers/mongodb"
"github.com/mainflux/mainflux/internal"
mongoClient "github.com/mainflux/mainflux/internal/clients/mongo"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"golang.org/x/sync/errgroup"
)
const (
svcName = "mongodb-writer"
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defBrokerURL = "nats://localhost:4222"
defPort = "8180"
defDB = "mainflux"
defDBHost = "localhost"
defDBPort = "27017"
defConfigPath = "/config.toml"
envBrokerURL = "MF_BROKER_URL"
envLogLevel = "MF_MONGO_WRITER_LOG_LEVEL"
envPort = "MF_MONGO_WRITER_PORT"
envDB = "MF_MONGO_WRITER_DB"
envDBHost = "MF_MONGO_WRITER_DB_HOST"
envDBPort = "MF_MONGO_WRITER_DB_PORT"
envConfigPath = "MF_MONGO_WRITER_CONFIG_PATH"
svcName = "mongodb-writer"
envPrefix = "MF_MONGO_WRITER_"
envPrefixDB = "MF_MONGO_WRITER_DB_"
envPrefixHttp = "MF_MONGO_WRITER_HTTP_"
defSvcHttpPort = "8180"
)
type config struct {
brokerURL string
logLevel string
port string
dbName string
dbHost string
dbPort string
configPath string
LogLevel string `env:"MF_MONGO_WRITER_LOG_LEVEL" envDefault:"info"`
ConfigPath string `env:"MF_MONGO_WRITER_CONFIG_PATH" envDefault:"/config.toml"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
}
func main() {
cfg := loadConfigs()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatal(err)
}
pubSub, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pubSub.Close()
addr := fmt.Sprintf("mongodb://%s:%s", cfg.dbHost, cfg.dbPort)
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(addr))
db, err := mongoClient.Setup(envPrefixDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to database: %s", err))
os.Exit(1)
log.Fatalf("failed to setup mongo database : %s", err.Error())
}
db := client.Database(cfg.dbName)
repo := mongodb.New(db)
repo := newService(db, logger)
counter, latency := makeMetrics()
repo = api.LoggingMiddleware(repo, logger)
repo = api.MetricsMiddleware(repo, counter, latency)
if err := consumers.Start(svcName, pubSub, repo, cfg.configPath, logger); err != nil {
logger.Error(fmt.Sprintf("Failed to start MongoDB writer: %s", err))
os.Exit(1)
if err := consumers.Start(svcName, pubSub, repo, cfg.ConfigPath, logger); err != nil {
log.Fatalf("failed to start MongoDB writer: %s", err.Error())
}
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svcName), logger)
g.Go(func() error {
return startHTTPService(ctx, cfg.port, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("MongoDB reader service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("MongoDB writer service terminated: %s", err))
}
}
func loadConfigs() config {
return config{
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
dbName: mainflux.Env(envDB, defDB),
dbHost: mainflux.Env(envDBHost, defDBHost),
dbPort: mainflux.Env(envDBPort, defDBPort),
configPath: mainflux.Env(envConfigPath, defConfigPath),
}
}
func makeMetrics() (*kitprometheus.Counter, *kitprometheus.Summary) {
counter := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "mongodb",
Subsystem: "message_writer",
Name: "request_count",
Help: "Number of database inserts.",
}, []string{"method"})
latency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "mongodb",
Subsystem: "message_writer",
Name: "request_latency_microseconds",
Help: "Total duration of inserts in microseconds.",
}, []string{"method"})
return counter, latency
}
func startHTTPService(ctx context.Context, port string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svcName)}
logger.Info(fmt.Sprintf("MongoDB writer service started, exposed port %s", p))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("MongoDB writer service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("mongodb writer service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("MongoDB writer service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
func newService(db *mongo.Database, logger logger.Logger) consumers.Consumer {
repo := mongodb.New(db)
repo = api.LoggingMiddleware(repo, logger)
counter, latency := internal.MakeMetrics("mongodb", "message_writer")
repo = api.MetricsMiddleware(repo, counter, latency)
return repo
}
+62 -226
View File
@@ -4,16 +4,15 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/go-redis/redis/v8"
"github.com/mainflux/mainflux"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
redisClient "github.com/mainflux/mainflux/internal/clients/redis"
"github.com/mainflux/mainflux/internal/env"
mflog "github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/mqtt"
mqttredis "github.com/mainflux/mainflux/mqtt/redis"
@@ -22,170 +21,113 @@ import (
"github.com/mainflux/mainflux/pkg/messaging"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
mqttpub "github.com/mainflux/mainflux/pkg/messaging/mqtt"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
mp "github.com/mainflux/mproxy/pkg/mqtt"
"github.com/mainflux/mproxy/pkg/session"
ws "github.com/mainflux/mproxy/pkg/websocket"
opentracing "github.com/opentracing/opentracing-go"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
svcName = "mqtt"
defLogLevel = "error"
defMQTTPort = "1883"
defMQTTTargetHost = "0.0.0.0"
defMQTTTargetPort = "1883"
defMQTTForwarderTimeout = "30s" // 30 seconds
defMQTTTargetHealthCheck = ""
defHTTPPort = "8080"
defHTTPTargetHost = "localhost"
defHTTPTargetPort = "8080"
defHTTPTargetPath = "/mqtt"
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
defBrokerURL = "nats://localhost:4222"
defJaegerURL = ""
defClientTLS = "false"
defCACerts = ""
defInstance = ""
defESURL = "localhost:6379"
defESPass = ""
defESDB = "0"
defAuthcacheURL = "localhost:6379"
defAuthCachePass = ""
defAuthCacheDB = "0"
envLogLevel = "MF_MQTT_ADAPTER_LOG_LEVEL"
envMQTTPort = "MF_MQTT_ADAPTER_MQTT_PORT"
envMQTTTargetHost = "MF_MQTT_ADAPTER_MQTT_TARGET_HOST"
envMQTTTargetPort = "MF_MQTT_ADAPTER_MQTT_TARGET_PORT"
envMQTTTargetHealthCheck = "MF_MQTT_ADAPTER_MQTT_TARGET_HEALTH_CHECK"
envMQTTForwarderTimeout = "MF_MQTT_ADAPTER_FORWARDER_TIMEOUT"
envHTTPPort = "MF_MQTT_ADAPTER_WS_PORT"
envHTTPTargetHost = "MF_MQTT_ADAPTER_WS_TARGET_HOST"
envHTTPTargetPort = "MF_MQTT_ADAPTER_WS_TARGET_PORT"
envHTTPTargetPath = "MF_MQTT_ADAPTER_WS_TARGET_PATH"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
envBrokerURL = "MF_BROKER_URL"
envJaegerURL = "MF_JAEGER_URL"
envClientTLS = "MF_MQTT_ADAPTER_CLIENT_TLS"
envCACerts = "MF_MQTT_ADAPTER_CA_CERTS"
envInstance = "MF_MQTT_ADAPTER_INSTANCE"
envESURL = "MF_MQTT_ADAPTER_ES_URL"
envESPass = "MF_MQTT_ADAPTER_ES_PASS"
envESDB = "MF_MQTT_ADAPTER_ES_DB"
envAuthCacheURL = "MF_AUTH_CACHE_URL"
envAuthCachePass = "MF_AUTH_CACHE_PASS"
envAuthCacheDB = "MF_AUTH_CACHE_DB"
svcName = "mqtt"
envPrefix = "MF_MQTT_ADAPTER_"
envPrefixES = "MF_MQTT_ADAPTER_ES_"
envPrefixAuthCache = "MF_AUTH_CACHE_"
)
type config struct {
mqttPort string
mqttTargetHost string
mqttTargetPort string
mqttForwarderTimeout time.Duration
mqttTargetHealthCheck string
httpPort string
httpTargetHost string
httpTargetPort string
httpTargetPath string
jaegerURL string
logLevel string
thingsURL string
thingsAuthURL string
thingsAuthTimeout time.Duration
brokerURL string
clientTLS bool
caCerts string
instance string
esURL string
esPass string
esDB string
authURL string
authPass string
authDB string
LogLevel string `env:"MF_MQTT_ADAPTER_LOG_LEVEL" envDefault:"info"`
MqttPort string `env:"MF_MQTT_ADAPTER_MQTT_PORT" envDefault:"1883"`
MqttTargetHost string `env:"MF_MQTT_ADAPTER_MQTT_TARGET_HOST" envDefault:"localhost"`
MqttTargetPort string `env:"MF_MQTT_ADAPTER_MQTT_TARGET_PORT" envDefault:"1883"`
MqttForwarderTimeout time.Duration `env:"MF_MQTT_ADAPTER_FORWARDER_TIMEOUT" envDefault:"30s"`
MqttTargetHealthCheck string `env:"MF_MQTT_ADAPTER_MQTT_TARGET_HEALTH_CHECK" envDefault:""`
HttpPort string `env:"MF_MQTT_ADAPTER_WS_PORT" envDefault:"8080"`
HttpTargetHost string `env:"MF_MQTT_ADAPTER_WS_TARGET_HOST" envDefault:"localhost"`
HttpTargetPort string `env:"MF_MQTT_ADAPTER_WS_TARGET_PORT" envDefault:"8080"`
HttpTargetPath string `env:"MF_MQTT_ADAPTER_WS_TARGET_PATH" envDefault:"/mqtt"`
Instance string `env:"MF_MQTT_ADAPTER_INSTANCE" envDefault:""`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := mflog.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := mflog.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
if cfg.mqttTargetHealthCheck != "" {
if cfg.MqttTargetHealthCheck != "" {
notify := func(e error, next time.Duration) {
logger.Info(fmt.Sprintf("Broker not ready: %s, next try in %s", e.Error(), next))
}
err := backoff.RetryNotify(healthcheck(cfg), backoff.NewExponentialBackOff(), notify)
if err != nil {
logger.Info(fmt.Sprintf("MQTT healthcheck limit exceeded, exiting. %s ", err.Error()))
os.Exit(1)
log.Fatalf("MQTT healthcheck limit exceeded, exiting. %s ", err.Error())
}
}
conn := connectToThings(cfg, logger)
defer conn.Close()
ec := connectToRedis(cfg.esURL, cfg.esPass, cfg.esDB, logger)
defer ec.Close()
nps, err := brokers.NewPubSub(cfg.brokerURL, "mqtt", logger)
nps, err := brokers.NewPubSub(cfg.BrokerURL, "mqtt", logger)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer nps.Close()
mpub, err := mqttpub.NewPublisher(fmt.Sprintf("%s:%s", cfg.mqttTargetHost, cfg.mqttTargetPort), cfg.mqttForwarderTimeout)
mpub, err := mqttpub.NewPublisher(fmt.Sprintf("%s:%s", cfg.MqttTargetHost, cfg.MqttTargetPort), cfg.MqttForwarderTimeout)
if err != nil {
logger.Error(fmt.Sprintf("Failed to create MQTT publisher: %s", err))
os.Exit(1)
log.Fatalf("failed to create MQTT publisher: %s", err.Error())
}
fwd := mqtt.NewForwarder(brokers.SubjectAllChannels, logger)
if err := fwd.Forward(svcName, nps, mpub); err != nil {
logger.Error(fmt.Sprintf("Failed to forward message broker messages: %s", err))
os.Exit(1)
log.Fatalf("failed to forward message broker messages: %s", err)
}
np, err := brokers.NewPublisher(cfg.brokerURL)
np, err := brokers.NewPublisher(cfg.BrokerURL)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer np.Close()
es := mqttredis.NewEventStore(ec, cfg.instance)
ec, err := redisClient.Setup(envPrefixES)
if err != nil {
log.Fatalf("failed to setup %s event store redis client : %s", svcName, err.Error())
}
defer ec.Close()
ac := connectToRedis(cfg.authURL, cfg.authPass, cfg.authDB, logger)
es := mqttredis.NewEventStore(ec, cfg.Instance)
ac, err := redisClient.Setup(envPrefixAuthCache)
if err != nil {
log.Fatalf("failed to setup %s event store redis client : %s", svcName, err.Error())
}
defer ac.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer tcHandler.Close()
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
authClient := auth.New(ac, tc)
// Event handler for MQTT hooks
h := mqtt.NewHandler([]messaging.Publisher{np}, es, logger, authClient)
logger.Info(fmt.Sprintf("Starting MQTT proxy on port %s", cfg.mqttPort))
logger.Info(fmt.Sprintf("Starting MQTT proxy on port %s", cfg.MqttPort))
g.Go(func() error {
return proxyMQTT(ctx, cfg, logger, h)
})
logger.Info(fmt.Sprintf("Starting MQTT over WS proxy on port %s", cfg.httpPort))
logger.Info(fmt.Sprintf("Starting MQTT over WS proxy on port %s", cfg.HttpPort))
g.Go(func() error {
return proxyWS(ctx, cfg, logger, h)
})
@@ -203,115 +145,9 @@ func main() {
}
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
mqttTimeout, err := time.ParseDuration(mainflux.Env(envMQTTForwarderTimeout, defMQTTForwarderTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envMQTTForwarderTimeout, err.Error())
}
return config{
mqttPort: mainflux.Env(envMQTTPort, defMQTTPort),
mqttTargetHost: mainflux.Env(envMQTTTargetHost, defMQTTTargetHost),
mqttTargetPort: mainflux.Env(envMQTTTargetPort, defMQTTTargetPort),
mqttForwarderTimeout: mqttTimeout,
mqttTargetHealthCheck: mainflux.Env(envMQTTTargetHealthCheck, defMQTTTargetHealthCheck),
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
httpTargetHost: mainflux.Env(envHTTPTargetHost, defHTTPTargetHost),
httpTargetPort: mainflux.Env(envHTTPTargetPort, defHTTPTargetPort),
httpTargetPath: mainflux.Env(envHTTPTargetPath, defHTTPTargetPath),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
thingsAuthTimeout: authTimeout,
thingsURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
instance: mainflux.Env(envInstance, defInstance),
esURL: mainflux.Env(envESURL, defESURL),
esPass: mainflux.Env(envESPass, defESPass),
esDB: mainflux.Env(envESDB, defESDB),
authURL: mainflux.Env(envAuthCacheURL, defAuthcacheURL),
authPass: mainflux.Env(envAuthCachePass, defAuthCachePass),
authDB: mainflux.Env(envAuthCacheDB, defAuthCacheDB),
}
}
func initJaeger(svcName, url string, logger mflog.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToThings(cfg config, logger mflog.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
return conn
}
func connectToRedis(redisURL, redisPass, redisDB string, logger mflog.Logger) *redis.Client {
db, err := strconv.Atoi(redisDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to redis: %s", err))
os.Exit(1)
}
return redis.NewClient(&redis.Options{
Addr: redisURL,
Password: redisPass,
DB: db,
})
}
func proxyMQTT(ctx context.Context, cfg config, logger mflog.Logger, handler session.Handler) error {
address := fmt.Sprintf(":%s", cfg.mqttPort)
target := fmt.Sprintf("%s:%s", cfg.mqttTargetHost, cfg.mqttTargetPort)
address := fmt.Sprintf(":%s", cfg.MqttPort)
target := fmt.Sprintf("%s:%s", cfg.MqttTargetHost, cfg.MqttTargetPort)
mp := mp.New(address, target, handler, logger)
errCh := make(chan error)
@@ -326,17 +162,17 @@ func proxyMQTT(ctx context.Context, cfg config, logger mflog.Logger, handler ses
case err := <-errCh:
return err
}
}
func proxyWS(ctx context.Context, cfg config, logger mflog.Logger, handler session.Handler) error {
target := fmt.Sprintf("%s:%s", cfg.httpTargetHost, cfg.httpTargetPort)
wp := ws.New(target, cfg.httpTargetPath, "ws", handler, logger)
target := fmt.Sprintf("%s:%s", cfg.HttpTargetHost, cfg.HttpTargetPort)
wp := ws.New(target, cfg.HttpTargetPath, "ws", handler, logger)
http.Handle("/mqtt", wp.Handler())
errCh := make(chan error)
go func() {
errCh <- wp.Listen(cfg.httpPort)
errCh <- wp.Listen(cfg.HttpPort)
}()
select {
@@ -350,12 +186,12 @@ func proxyWS(ctx context.Context, cfg config, logger mflog.Logger, handler sessi
func healthcheck(cfg config) func() error {
return func() error {
res, err := http.Get(cfg.mqttTargetHealthCheck)
res, err := http.Get(cfg.MqttTargetHealthCheck)
if err != nil {
return err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
body, err := io.ReadAll(res.Body)
if err != nil {
return err
}
+52 -144
View File
@@ -7,61 +7,31 @@ import (
"context"
"fmt"
"log"
"net/http"
"os"
"strconv"
"time"
r "github.com/go-redis/redis/v8"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/internal"
redisClient "github.com/mainflux/mainflux/internal/clients/redis"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/opcua"
"github.com/mainflux/mainflux/opcua/api"
"github.com/mainflux/mainflux/opcua/db"
"github.com/mainflux/mainflux/opcua/gopcua"
"github.com/mainflux/mainflux/opcua/redis"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
"golang.org/x/sync/errgroup"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)
const (
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defHTTPPort = "8180"
defOPCIntervalMs = "1000"
defOPCPolicy = ""
defOPCMode = ""
defOPCCertFile = ""
defOPCKeyFile = ""
defBrokerURL = "nats://localhost:4222"
defESURL = "localhost:6379"
defESPass = ""
defESDB = "0"
defESConsumerName = "opcua"
defRouteMapURL = "localhost:6379"
defRouteMapPass = ""
defRouteMapDB = "0"
envLogLevel = "MF_OPCUA_ADAPTER_LOG_LEVEL"
envHTTPPort = "MF_OPCUA_ADAPTER_HTTP_PORT"
envOPCIntervalMs = "MF_OPCUA_ADAPTER_INTERVAL_MS"
envOPCPolicy = "MF_OPCUA_ADAPTER_POLICY"
envOPCMode = "MF_OPCUA_ADAPTER_MODE"
envOPCCertFile = "MF_OPCUA_ADAPTER_CERT_FILE"
envOPCKeyFile = "MF_OPCUA_ADAPTER_KEY_FILE"
envBrokerURL = "MF_BROKER_URL"
envESURL = "MF_THINGS_ES_URL"
envESPass = "MF_THINGS_ES_PASS"
envESDB = "MF_THINGS_ES_DB"
envESConsumerName = "MF_OPCUA_ADAPTER_EVENT_CONSUMER"
envRouteMapURL = "MF_OPCUA_ADAPTER_ROUTE_MAP_URL"
envRouteMapPass = "MF_OPCUA_ADAPTER_ROUTE_MAP_PASS"
envRouteMapDB = "MF_OPCUA_ADAPTER_ROUTE_MAP_DB"
svcName = "opc-ua-adapter"
envPrefix = "MF_OPCUA_ADAPTER_"
envPrefixES = "MF_OPCUA_ADAPTER_ES_"
envPrefixHttp = "MF_OPCUA_ADAPTER_HTTP_"
envPrefixRouteMap = "MF_OPCUA_ADAPTER_ROUTE_MAP_"
defSvcHttpPort = "8180"
thingsRMPrefix = "thing"
channelsRMPrefix = "channel"
@@ -69,43 +39,49 @@ const (
)
type config struct {
httpPort string
opcuaConfig opcua.Config
brokerURL string
logLevel string
esURL string
esPass string
esDB string
esConsumerName string
routeMapURL string
routeMapPass string
routeMapDB string
LogLevel string `env:"MF_OPCUA_ADAPTER_LOG_LEVEL" envDefault:"info"`
ESConsumerName string `env:"MF_OPCUA_ADAPTER_EVENT_CONSUMER" envDefault:""`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
}
func main() {
cfg := loadConfig()
httpCtx, httpCancel := context.WithCancel(context.Background())
g, httpCtx := errgroup.WithContext(httpCtx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
opcConfig := opcua.Config{}
if err := env.Parse(&opcConfig); err != nil {
log.Fatalf("failed to load %s opcua client configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
rmConn := connectToRedis(cfg.routeMapURL, cfg.routeMapPass, cfg.routeMapDB, logger)
rmConn, err := redisClient.Setup(envPrefixRouteMap)
if err != nil {
log.Fatalf("failed to setup %s bootstrap event store redis client : %s", svcName, err.Error())
}
defer rmConn.Close()
thingRM := newRouteMapRepositoy(rmConn, thingsRMPrefix, logger)
chanRM := newRouteMapRepositoy(rmConn, channelsRMPrefix, logger)
connRM := newRouteMapRepositoy(rmConn, connectionRMPrefix, logger)
esConn := connectToRedis(cfg.esURL, cfg.esPass, cfg.esDB, logger)
esConn, err := redisClient.Setup(envPrefixES)
if err != nil {
log.Fatalf("failed to setup %s bootstrap event store redis client : %s", svcName, err.Error())
}
defer esConn.Close()
pubSub, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pubSub.Close()
@@ -113,37 +89,23 @@ func main() {
sub := gopcua.NewSubscriber(ctx, pubSub, thingRM, chanRM, connRM, logger)
browser := gopcua.NewBrowser(ctx, logger)
svc := opcua.New(sub, browser, thingRM, chanRM, connRM, cfg.opcuaConfig, logger)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "opc_adapter",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "opc_adapter",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
svc := newService(sub, browser, thingRM, chanRM, connRM, opcConfig, logger)
go subscribeToStoredSubs(sub, cfg.opcuaConfig, logger)
go subscribeToThingsES(svc, esConn, cfg.esConsumerName, logger)
go subscribeToStoredSubs(sub, opcConfig, logger)
go subscribeToThingsES(svc, esConn, cfg.ESConsumerName, logger)
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(httpCtx, httpCancel, svcName, httpServerConfig, api.MakeHandler(svc, logger), logger)
g.Go(func() error {
return startHTTPServer(httpCtx, svc, cfg, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(httpCtx); sig != nil {
httpCancel()
logger.Info(fmt.Sprintf("OPC-UA adapter service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(httpCtx, httpCancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -151,43 +113,6 @@ func main() {
}
}
func loadConfig() config {
oc := opcua.Config{
Interval: mainflux.Env(envOPCIntervalMs, defOPCIntervalMs),
Policy: mainflux.Env(envOPCPolicy, defOPCPolicy),
Mode: mainflux.Env(envOPCMode, defOPCMode),
CertFile: mainflux.Env(envOPCCertFile, defOPCCertFile),
KeyFile: mainflux.Env(envOPCKeyFile, defOPCKeyFile),
}
return config{
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
opcuaConfig: oc,
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
esURL: mainflux.Env(envESURL, defESURL),
esPass: mainflux.Env(envESPass, defESPass),
esDB: mainflux.Env(envESDB, defESDB),
esConsumerName: mainflux.Env(envESConsumerName, defESConsumerName),
routeMapURL: mainflux.Env(envRouteMapURL, defRouteMapURL),
routeMapPass: mainflux.Env(envRouteMapPass, defRouteMapPass),
routeMapDB: mainflux.Env(envRouteMapDB, defRouteMapDB),
}
}
func connectToRedis(redisURL, redisPass, redisDB string, logger logger.Logger) *r.Client {
db, err := strconv.Atoi(redisDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to redis: %s", err))
os.Exit(1)
}
return r.NewClient(&r.Options{
Addr: redisURL,
Password: redisPass,
DB: db,
})
}
func subscribeToStoredSubs(sub opcua.Subscriber, cfg opcua.Config, logger logger.Logger) {
// Get all stored subscriptions
nodes, err := db.ReadAll()
@@ -218,28 +143,11 @@ func newRouteMapRepositoy(client *r.Client, prefix string, logger logger.Logger)
return redis.NewRouteMapRepository(client, prefix)
}
func startHTTPServer(ctx context.Context, svc opcua.Service, cfg config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.httpPort)
func newService(sub opcua.Subscriber, browser opcua.Browser, thingRM, chanRM, connRM opcua.RouteMapRepository, opcuaConfig opcua.Config, logger logger.Logger) opcua.Service {
svc := opcua.New(sub, browser, thingRM, chanRM, connRM, opcuaConfig, logger)
svc = api.LoggingMiddleware(svc, logger)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, logger)}
logger.Info(fmt.Sprintf("OPC-UA adapter service started, exposed port %s", cfg.httpPort))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("OPC-UA adapter service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("OPC-UA adapter service error occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("OPC-UA adapter service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
return svc
}
+50 -254
View File
@@ -6,129 +6,89 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/readers"
"github.com/mainflux/mainflux/readers/api"
"github.com/mainflux/mainflux/readers/postgres"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
svcName = "postgres-reader"
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defPort = "8180"
defClientTLS = "false"
defCACerts = ""
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "mainflux"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defJaegerURL = ""
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
defUsersAuthURL = "localhost:8181"
defUsersAuthTimeout = "1s"
envLogLevel = "MF_POSTGRES_READER_LOG_LEVEL"
envPort = "MF_POSTGRES_READER_PORT"
envClientTLS = "MF_POSTGRES_READER_CLIENT_TLS"
envCACerts = "MF_POSTGRES_READER_CA_CERTS"
envDBHost = "MF_POSTGRES_READER_DB_HOST"
envDBPort = "MF_POSTGRES_READER_DB_PORT"
envDBUser = "MF_POSTGRES_READER_DB_USER"
envDBPass = "MF_POSTGRES_READER_DB_PASS"
envDB = "MF_POSTGRES_READER_DB"
envDBSSLMode = "MF_POSTGRES_READER_DB_SSL_MODE"
envDBSSLCert = "MF_POSTGRES_READER_DB_SSL_CERT"
envDBSSLKey = "MF_POSTGRES_READER_DB_SSL_KEY"
envDBSSLRootCert = "MF_POSTGRES_READER_DB_SSL_ROOT_CERT"
envJaegerURL = "MF_JAEGER_URL"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
envUsersAuthURL = "MF_AUTH_GRPC_URL"
envUsersAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
svcName = "postgres-reader"
envPrefix = "MF_POSTGRES_READER_"
envPrefixHttp = "MF_POSTGRES_READER_HTTP_"
defDB = "messages"
defSvcHttpPort = "8180"
)
type config struct {
logLevel string
port string
clientTLS bool
caCerts string
dbConfig postgres.Config
jaegerURL string
thingsAuthURL string
usersAuthURL string
thingsAuthTimeout time.Duration
usersAuthTimeout time.Duration
LogLevel string `env:"MF_POSTGRES_READER_LOG_LEVEL" envDefault:"info"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
conn := connectToThings(cfg, logger)
defer conn.Close()
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer tcHandler.Close()
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
authConn := connectToAuth(cfg, logger)
defer authConn.Close()
auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout)
db := connectToDB(cfg.dbConfig, logger)
dbConfig := pgClient.Config{Name: defDB}
if err := dbConfig.LoadEnv(envPrefix); err != nil {
log.Fatal(err.Error())
}
db, err := pgClient.Connect(dbConfig)
if err != nil {
log.Fatalf("failed to setup postgres database : %s", err.Error())
}
defer db.Close()
repo := newService(db, logger)
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, repo, tc, auth, cfg.port, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Postgres reader service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -136,175 +96,11 @@ func main() {
}
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
logger.Info("Connecting to auth via gRPC")
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.usersAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Established gRPC connection to auth via gRPC: %s", cfg.usersAuthURL))
return conn
}
func loadConfig() config {
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
usersAuthTimeout, err := time.ParseDuration(mainflux.Env(envUsersAuthTimeout, defUsersAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
dbConfig: dbConfig,
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
usersAuthURL: mainflux.Env(envUsersAuthURL, defUsersAuthURL),
thingsAuthTimeout: authTimeout,
usersAuthTimeout: usersAuthTimeout,
}
}
func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
db, err := postgres.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to Postgres: %s", err))
os.Exit(1)
}
return db
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Established gRPC connection to things via gRPC: %s", cfg.thingsAuthURL))
return conn
}
func newService(db *sqlx.DB, logger logger.Logger) readers.MessageRepository {
svc := postgres.New(db)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "postgres",
Subsystem: "message_reader",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "postgres",
Subsystem: "message_reader",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("postgres", "message_reader")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, port string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(repo, tc, ac, svcName, logger)}
logger.Info(fmt.Sprintf("Postgres reader service started, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Postgres reader service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("postgres reader service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Postgres reader service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+40 -130
View File
@@ -7,182 +7,92 @@ import (
"context"
"fmt"
"log"
"net/http"
"os"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/consumers"
"github.com/mainflux/mainflux/consumers/writers/api"
"github.com/mainflux/mainflux/consumers/writers/postgres"
writerPg "github.com/mainflux/mainflux/consumers/writers/postgres"
"github.com/mainflux/mainflux/internal"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"
)
const (
svcName = "postgres-writer"
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defBrokerURL = "nats://localhost:4222"
defPort = "8180"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "mainflux"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defConfigPath = "/config.toml"
envBrokerURL = "MF_BROKER_URL"
envLogLevel = "MF_POSTGRES_WRITER_LOG_LEVEL"
envPort = "MF_POSTGRES_WRITER_PORT"
envDBHost = "MF_POSTGRES_WRITER_DB_HOST"
envDBPort = "MF_POSTGRES_WRITER_DB_PORT"
envDBUser = "MF_POSTGRES_WRITER_DB_USER"
envDBPass = "MF_POSTGRES_WRITER_DB_PASS"
envDB = "MF_POSTGRES_WRITER_DB"
envDBSSLMode = "MF_POSTGRES_WRITER_DB_SSL_MODE"
envDBSSLCert = "MF_POSTGRES_WRITER_DB_SSL_CERT"
envDBSSLKey = "MF_POSTGRES_WRITER_DB_SSL_KEY"
envDBSSLRootCert = "MF_POSTGRES_WRITER_DB_SSL_ROOT_CERT"
envConfigPath = "MF_POSTGRES_WRITER_CONFIG_PATH"
svcName = "postgres-writer"
envPrefix = "MF_POSTGRES_WRITER_"
envPrefixHttp = "MF_POSTGRES_WRITER_HTTP_"
defDB = "messages"
defSvcHttpPort = "8180"
)
type config struct {
brokerURL string
logLevel string
port string
configPath string
dbConfig postgres.Config
LogLevel string `env:"MF_POSTGRES_WRITER_LOG_LEVEL" envDefault:"info"`
ConfigPath string `env:"MF_POSTGRES_WRITER_CONFIG_PATH" envDefault:"/config.toml"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
pubSub, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pubSub.Close()
db := connectToDB(cfg.dbConfig, logger)
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *writerPg.Migration(), dbConfig)
if err != nil {
log.Fatal(err)
}
defer db.Close()
repo := newService(db, logger)
if err = consumers.Start(svcName, pubSub, repo, cfg.configPath, logger); err != nil {
logger.Error(fmt.Sprintf("Failed to create Postgres writer: %s", err))
if err = consumers.Start(svcName, pubSub, repo, cfg.ConfigPath, logger); err != nil {
log.Fatalf("failed to create Postgres writer: %s", err.Error())
}
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svcName), logger)
g.Go(func() error {
return startHTTPServer(ctx, cfg.port, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Postgres writer service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
logger.Error(fmt.Sprintf("Postgres writer service terminated: %s", err))
}
}
func loadConfig() config {
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
return config{
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
configPath: mainflux.Env(envConfigPath, defConfigPath),
dbConfig: dbConfig,
}
}
func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
db, err := postgres.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to Postgres: %s", err))
os.Exit(1)
}
return db
}
func newService(db *sqlx.DB, logger logger.Logger) consumers.Consumer {
svc := postgres.New(db)
svc := writerPg.New(db)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "postgres",
Subsystem: "message_writer",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "postgres",
Subsystem: "message_writer",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("postgres", "message_writer")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, port string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svcName)}
logger.Info(fmt.Sprintf("Postgres writer service started, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Postgres writer service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("postgres writer service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Postgres writer service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+8 -43
View File
@@ -5,13 +5,13 @@ import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"reflect"
"strconv"
"time"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
mfSDK "github.com/mainflux/mainflux/pkg/sdk/go"
@@ -22,7 +22,7 @@ import (
)
const (
stopWaitTime = 5 * time.Second
svcName = "provision"
defLogLevel = "error"
defConfigFile = "config.toml"
@@ -110,16 +110,15 @@ func main() {
svc := provision.New(cfg, SDK, logger)
svc = api.NewLoggingMiddleware(svc, logger)
httpServerConfig := server.Config{Host: "", Port: cfg.Server.HTTPPort, KeyFile: cfg.Server.ServerKey, CertFile: cfg.Server.ServerCert}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, svc, cfg, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Provision service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -128,40 +127,6 @@ func main() {
}
func startHTTPServer(ctx context.Context, svc provision.Service, cfg provision.Config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.Server.HTTPPort)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, logger)}
switch {
case cfg.Server.ServerCert != "" || cfg.Server.ServerKey != "":
logger.Info(fmt.Sprintf("Provision service started using https on port %s with cert %s key %s",
cfg.Server.HTTPPort, cfg.Server.ServerCert, cfg.Server.ServerKey))
go func() {
errCh <- server.ListenAndServeTLS(cfg.Server.ServerCert, cfg.Server.ServerKey)
}()
default:
logger.Info(fmt.Sprintf("Provision service started using http on port %s", cfg.Server.HTTPPort))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Provision service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("provision service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Provision service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
func loadConfigFromFile(file string) (provision.Config, error) {
_, err := os.Stat(file)
if os.IsNotExist(err) {
+68 -297
View File
@@ -6,172 +6,117 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/consumers"
"github.com/mainflux/mainflux/consumers/notifiers"
"github.com/mainflux/mainflux/consumers/notifiers/api"
"github.com/mainflux/mainflux/consumers/notifiers/postgres"
notifierPg "github.com/mainflux/mainflux/consumers/notifiers/postgres"
"github.com/mainflux/mainflux/internal"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"golang.org/x/sync/errgroup"
mfsmpp "github.com/mainflux/mainflux/consumers/notifiers/smpp"
"github.com/mainflux/mainflux/consumers/notifiers/tracing"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
jaegerClient "github.com/mainflux/mainflux/internal/clients/jaeger"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
"github.com/mainflux/mainflux/pkg/ulid"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
svcName = "smpp-notifier"
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "subscriptions"
defConfigPath = "/config.toml"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defHTTPPort = "8907"
defServerCert = ""
defServerKey = ""
defFrom = ""
defJaegerURL = ""
defBrokerURL = "nats://localhost:4222"
defSmppAddress = ""
defSmppUsername = ""
defSmppPassword = ""
defSmppSystemType = ""
defSmppSrcAddrTON = "0"
defSmppDstAddrTON = "0"
defSmppSrcAddrNPI = "0"
defSmppDstAddrNPI = "0"
defAuthTLS = "false"
defAuthCACerts = ""
defAuthURL = "localhost:8181"
defAuthTimeout = "1s"
envLogLevel = "MF_SMPP_NOTIFIER_LOG_LEVEL"
envDBHost = "MF_SMPP_NOTIFIER_DB_HOST"
envDBPort = "MF_SMPP_NOTIFIER_DB_PORT"
envDBUser = "MF_SMPP_NOTIFIER_DB_USER"
envDBPass = "MF_SMPP_NOTIFIER_DB_PASS"
envDB = "MF_SMPP_NOTIFIER_DB"
envConfigPath = "MF_SMPP_NOTIFIER_WRITER_CONFIG_PATH"
envDBSSLMode = "MF_SMPP_NOTIFIER_DB_SSL_MODE"
envDBSSLCert = "MF_SMPP_NOTIFIER_DB_SSL_CERT"
envDBSSLKey = "MF_SMPP_NOTIFIER_DB_SSL_KEY"
envDBSSLRootCert = "MF_SMPP_NOTIFIER_DB_SSL_ROOT_CERT"
envHTTPPort = "MF_SMPP_NOTIFIER_HTTP_PORT"
envServerCert = "MF_SMPP_NOTIFIER_SERVER_CERT"
envServerKey = "MF_SMPP_NOTIFIER_SERVER_KEY"
envFrom = "MF_SMPP_NOTIFIER_SOURCE_ADDR"
envJaegerURL = "MF_JAEGER_URL"
envBrokerURL = "MF_BROKER_URL"
envSmppAddress = "MF_SMPP_ADDRESS"
envSmppUsername = "MF_SMPP_USERNAME"
envSmppPassword = "MF_SMPP_PASSWORD"
envSmppSystemType = "MF_SMPP_SYSTEM_TYPE"
envSmppSrcAddrTON = "MF_SMPP_SRC_ADDR_TON"
envSmppDstAddrTON = "MF_SMPP_DST_ADDR_TON"
envSmppSrcAddrNPI = "MF_SMPP_SRC_ADDR_NPI"
envSmppDstAddrNPI = "MF_SMPP_DST_ADDR_NPI"
envAuthTLS = "MF_AUTH_CLIENT_TLS"
envAuthCACerts = "MF_AUTH_CA_CERTS"
envAuthURL = "MF_AUTH_GRPC_URL"
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
svcName = "smpp-notifier"
envPrefix = "MF_SMPP_NOTIFIER_"
envPrefixHttp = "MF_SMPP_NOTIFIER_HTTP_"
defDB = "subscriptions"
defSvcHttpPort = "8180"
)
type config struct {
brokerURL string
configPath string
logLevel string
dbConfig postgres.Config
smppConf mfsmpp.Config
from string
httpPort string
serverCert string
serverKey string
jaegerURL string
authTLS bool
authCACerts string
authURL string
authTimeout time.Duration
LogLevel string `env:"MF_SMPP_NOTIFIER_LOG_LEVEL" envDefault:"info"`
From string `env:"MF_SMPP_NOTIFIER_FROM_ADDR" envDefault:""`
ConfigPath string `env:"MF_SMPP_NOTIFIER_CONFIG_PATH" envDefault:"/config.toml"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
db := connectToDB(cfg.dbConfig, logger)
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *notifierPg.Migration(), dbConfig)
if err != nil {
log.Fatal(err)
}
defer db.Close()
pubSub, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
smppConfig := mfsmpp.Config{}
if err := env.Parse(&smppConfig); err != nil {
log.Fatalf("failed to load SMPP configuration from environment : %s", err.Error())
}
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pubSub.Close()
authTracer, closer := initJaeger("auth", cfg.jaegerURL, logger)
defer closer.Close()
auth, close := connectToAuth(cfg, authTracer, logger)
if close != nil {
defer close()
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatalf(err.Error())
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
tracer, closer := initJaeger("smpp-notifier", cfg.jaegerURL, logger)
tracer, closer, err := jaegerClient.NewTracer("smpp-notifier", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer closer.Close()
dbTracer, dbCloser := initJaeger("smpp-notifier_db", cfg.jaegerURL, logger)
dbTracer, dbCloser, err := jaegerClient.NewTracer("smpp-notifier_db", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer dbCloser.Close()
svc := newService(db, dbTracer, auth, cfg, logger)
svc := newService(db, dbTracer, auth, cfg, smppConfig, logger)
if err = consumers.Start(svcName, pubSub, svc, cfg.configPath, logger); err != nil {
logger.Error(fmt.Sprintf("Failed to create Postgres writer: %s", err))
if err = consumers.Start(svcName, pubSub, svc, cfg.ConfigPath, logger); err != nil {
log.Fatalf("failed to create Postgres writer: %s", err.Error())
}
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, tracer, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, tracer, svc, cfg.httpPort, cfg.serverCert, cfg.serverKey, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("SMPP notifier service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -180,189 +125,15 @@ func main() {
}
func loadConfig() config {
authTimeout, err := time.ParseDuration(mainflux.Env(envAuthTimeout, defAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envAuthTimeout, err.Error())
}
tls, err := strconv.ParseBool(mainflux.Env(envAuthTLS, defAuthTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envAuthTLS)
}
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
saton, err := strconv.ParseUint(mainflux.Env(envSmppSrcAddrTON, defSmppSrcAddrTON), 10, 8)
if err != nil {
log.Fatalf("Invalid value passed for %s", envSmppSrcAddrTON)
}
daton, err := strconv.ParseUint(mainflux.Env(envSmppDstAddrTON, defSmppDstAddrTON), 10, 8)
if err != nil {
log.Fatalf("Invalid value passed for %s", envSmppDstAddrTON)
}
sanpi, err := strconv.ParseUint(mainflux.Env(envSmppSrcAddrNPI, defSmppSrcAddrNPI), 10, 8)
if err != nil {
log.Fatalf("Invalid value passed for %s", envSmppSrcAddrNPI)
}
danpi, err := strconv.ParseUint(mainflux.Env(envSmppDstAddrNPI, defSmppDstAddrNPI), 10, 8)
if err != nil {
log.Fatalf("Invalid value passed for %s", envSmppDstAddrNPI)
}
smppConf := mfsmpp.Config{
Address: mainflux.Env(envSmppAddress, defSmppAddress),
Username: mainflux.Env(envSmppUsername, defSmppUsername),
Password: mainflux.Env(envSmppPassword, defSmppPassword),
SystemType: mainflux.Env(envSmppSystemType, defSmppSystemType),
SourceAddrTON: uint8(saton),
DestAddrTON: uint8(daton),
SourceAddrNPI: uint8(sanpi),
DestAddrNPI: uint8(danpi),
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
configPath: mainflux.Env(envConfigPath, defConfigPath),
dbConfig: dbConfig,
smppConf: smppConf,
from: mainflux.Env(envFrom, defFrom),
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
authTLS: tls,
authCACerts: mainflux.Env(envAuthCACerts, defAuthCACerts),
authURL: mainflux.Env(envAuthURL, defAuthURL),
authTimeout: authTimeout,
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
db, err := postgres.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err))
os.Exit(1)
}
return db
}
func connectToAuth(cfg config, tracer opentracing.Tracer, logger logger.Logger) (mainflux.AuthServiceClient, func() error) {
var opts []grpc.DialOption
if cfg.authTLS {
if cfg.authCACerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.authCACerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.authURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
return authapi.NewClient(tracer, conn, cfg.authTimeout), conn.Close
}
func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServiceClient, c config, logger logger.Logger) notifiers.Service {
database := postgres.NewDatabase(db)
repo := tracing.New(postgres.New(database), tracer)
func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServiceClient, c config, sc mfsmpp.Config, logger logger.Logger) notifiers.Service {
database := notifierPg.NewDatabase(db)
repo := tracing.New(notifierPg.New(database), tracer)
idp := ulid.New()
notifier := mfsmpp.New(c.smppConf)
svc := notifiers.New(auth, repo, idp, notifier, c.from)
notifier := mfsmpp.New(sc)
svc := notifiers.New(auth, repo, idp, notifier, c.From)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "notifier",
Subsystem: "smpp",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "notifier",
Subsystem: "smpp",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("notifier", "smpp")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, tracer opentracing.Tracer, svc notifiers.Service, port string, certFile string, keyFile string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, tracer, logger)}
switch {
case certFile != "" || keyFile != "":
logger.Info(fmt.Sprintf("SMPP notifier service started using https, cert %s key %s, exposed port %s", certFile, keyFile, port))
go func() {
errCh <- server.ListenAndServeTLS(certFile, keyFile)
}()
default:
logger.Info(fmt.Sprintf("SMPP notifier service started using http, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("SMPP notifier service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("smpp notifier service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("SMPP notifier service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+69 -278
View File
@@ -6,170 +6,117 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/consumers"
"github.com/mainflux/mainflux/consumers/notifiers"
"github.com/mainflux/mainflux/consumers/notifiers/api"
"github.com/mainflux/mainflux/consumers/notifiers/postgres"
notifierPg "github.com/mainflux/mainflux/consumers/notifiers/postgres"
"github.com/mainflux/mainflux/consumers/notifiers/smtp"
"github.com/mainflux/mainflux/consumers/notifiers/tracing"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
jagerClient "github.com/mainflux/mainflux/internal/clients/jaeger"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/internal/email"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
"github.com/mainflux/mainflux/pkg/ulid"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
svcName = "smtp-notifier"
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "subscriptions"
defConfigPath = "/config.toml"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defHTTPPort = "8906"
defServerCert = ""
defServerKey = ""
defFrom = ""
defJaegerURL = ""
defBrokerURL = "nats://localhost:4222"
defEmailHost = "localhost"
defEmailPort = "25"
defEmailUsername = "root"
defEmailPassword = ""
defEmailFromAddress = ""
defEmailFromName = ""
defEmailTemplate = "email.tmpl"
defAuthTLS = "false"
defAuthCACerts = ""
defAuthURL = "localhost:8181"
defAuthTimeout = "1s"
envLogLevel = "MF_SMTP_NOTIFIER_LOG_LEVEL"
envDBHost = "MF_SMTP_NOTIFIER_DB_HOST"
envDBPort = "MF_SMTP_NOTIFIER_DB_PORT"
envDBUser = "MF_SMTP_NOTIFIER_DB_USER"
envDBPass = "MF_SMTP_NOTIFIER_DB_PASS"
envDB = "MF_SMTP_NOTIFIER_DB"
envConfigPath = "MF_SMTP_NOTIFIER_CONFIG_PATH"
envDBSSLMode = "MF_SMTP_NOTIFIER_DB_SSL_MODE"
envDBSSLCert = "MF_SMTP_NOTIFIER_DB_SSL_CERT"
envDBSSLKey = "MF_SMTP_NOTIFIER_DB_SSL_KEY"
envDBSSLRootCert = "MF_SMTP_NOTIFIER_DB_SSL_ROOT_CERT"
envHTTPPort = "MF_SMTP_NOTIFIER_PORT"
envServerCert = "MF_SMTP_NOTIFIER_SERVER_CERT"
envServerKey = "MF_SMTP_NOTIFIER_SERVER_KEY"
envFrom = "MF_SMTP_NOTIFIER_FROM_ADDR"
envJaegerURL = "MF_JAEGER_URL"
envBrokerURL = "MF_BROKER_URL"
envEmailHost = "MF_EMAIL_HOST"
envEmailPort = "MF_EMAIL_PORT"
envEmailUsername = "MF_EMAIL_USERNAME"
envEmailPassword = "MF_EMAIL_PASSWORD"
envEmailFromAddress = "MF_EMAIL_FROM_ADDRESS"
envEmailFromName = "MF_EMAIL_FROM_NAME"
envEmailTemplate = "MF_SMTP_NOTIFIER_TEMPLATE"
envAuthTLS = "MF_AUTH_CLIENT_TLS"
envAuthCACerts = "MF_AUTH_CA_CERTS"
envAuthURL = "MF_AUTH_GRPC_URL"
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
svcName = "smtp-notifier"
envPrefix = "MF_SMTP_NOTIFIER_"
envPrefixHttp = "MF_SMTP_NOTIFIER_HTTP_"
defDB = "subscriptions"
defSvcHttpPort = "8180"
)
type config struct {
brokerURL string
configPath string
logLevel string
dbConfig postgres.Config
emailConf email.Config
from string
httpPort string
serverCert string
serverKey string
jaegerURL string
authTLS bool
authCACerts string
authURL string
authTimeout time.Duration
LogLevel string `env:"MF_SMTP_NOTIFIER_LOG_LEVEL" envDefault:"info"`
ConfigPath string `env:"MF_SMTP_NOTIFIER_CONFIG_PATH" envDefault:"/config.toml"`
From string `env:"MF_SMTP_NOTIFIER_FROM_ADDR" envDefault:""`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
db := connectToDB(cfg.dbConfig, logger)
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *notifierPg.Migration(), dbConfig)
if err != nil {
log.Fatal(err)
}
defer db.Close()
pubSub, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
ec := email.Config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load email configuration : %s", err.Error())
}
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err)
}
defer pubSub.Close()
authTracer, closer := initJaeger("auth", cfg.jaegerURL, logger)
defer closer.Close()
auth, close := connectToAuth(cfg, authTracer, logger)
if close != nil {
defer close()
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err)
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
tracer, closer := initJaeger("smtp-notifier", cfg.jaegerURL, logger)
tracer, closer, err := jagerClient.NewTracer("smtp-notifier", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer closer.Close()
dbTracer, dbCloser := initJaeger("smtp-notifier_db", cfg.jaegerURL, logger)
dbTracer, dbCloser, err := jagerClient.NewTracer("smtp-notifier_db", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer dbCloser.Close()
svc := newService(db, dbTracer, auth, cfg, logger)
svc := newService(db, dbTracer, auth, cfg, ec, logger)
if err = consumers.Start(svcName, pubSub, svc, cfg.configPath, logger); err != nil {
logger.Error(fmt.Sprintf("Failed to create Postgres writer: %s", err))
if err = consumers.Start(svcName, pubSub, svc, cfg.ConfigPath, logger); err != nil {
log.Fatalf("failed to create Postgres writer: %s", err)
}
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, tracer, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, tracer, svc, cfg.httpPort, cfg.serverCert, cfg.serverKey, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("SMTP notifier service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -178,177 +125,21 @@ func main() {
}
func loadConfig() config {
authTimeout, err := time.ParseDuration(mainflux.Env(envAuthTimeout, defAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envAuthTimeout, err.Error())
}
tls, err := strconv.ParseBool(mainflux.Env(envAuthTLS, defAuthTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envAuthTLS)
}
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
emailConf := email.Config{
FromAddress: mainflux.Env(envEmailFromAddress, defEmailFromAddress),
FromName: mainflux.Env(envEmailFromName, defEmailFromName),
Host: mainflux.Env(envEmailHost, defEmailHost),
Port: mainflux.Env(envEmailPort, defEmailPort),
Username: mainflux.Env(envEmailUsername, defEmailUsername),
Password: mainflux.Env(envEmailPassword, defEmailPassword),
Template: mainflux.Env(envEmailTemplate, defEmailTemplate),
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
configPath: mainflux.Env(envConfigPath, defConfigPath),
dbConfig: dbConfig,
emailConf: emailConf,
from: mainflux.Env(envFrom, defFrom),
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
authTLS: tls,
authCACerts: mainflux.Env(envAuthCACerts, defAuthCACerts),
authURL: mainflux.Env(envAuthURL, defAuthURL),
authTimeout: authTimeout,
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
db, err := postgres.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err))
os.Exit(1)
}
return db
}
func connectToAuth(cfg config, tracer opentracing.Tracer, logger logger.Logger) (mainflux.AuthServiceClient, func() error) {
var opts []grpc.DialOption
if cfg.authTLS {
if cfg.authCACerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.authCACerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.authURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
return authapi.NewClient(tracer, conn, cfg.authTimeout), conn.Close
}
func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServiceClient, c config, logger logger.Logger) notifiers.Service {
database := postgres.NewDatabase(db)
repo := tracing.New(postgres.New(database), tracer)
func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServiceClient, c config, ec email.Config, logger logger.Logger) notifiers.Service {
database := notifierPg.NewDatabase(db)
repo := tracing.New(notifierPg.New(database), tracer)
idp := ulid.New()
agent, err := email.New(&c.emailConf)
agent, err := email.New(&ec)
if err != nil {
logger.Error(fmt.Sprintf("Failed to create email agent: %s", err))
os.Exit(1)
log.Fatalf("failed to create email agent: %s", err.Error())
}
notifier := smtp.New(agent)
svc := notifiers.New(auth, repo, idp, notifier, c.from)
svc := notifiers.New(auth, repo, idp, notifier, c.From)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "notifier",
Subsystem: "smtp",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "notifier",
Subsystem: "smtp",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("notifier", "smtp")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, tracer opentracing.Tracer, svc notifiers.Service, port string, certFile string, keyFile string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, tracer, logger)}
switch {
case certFile != "" || keyFile != "":
logger.Info(fmt.Sprintf("SMTP notifier service started using https, cert %s key %s, exposed port %s", certFile, keyFile, port))
go func() {
errCh <- server.ListenAndServeTLS(certFile, keyFile)
}()
default:
logger.Info(fmt.Sprintf("SMTP notifier service started using http, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("SMTP notifier service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("smtp notifier service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("SMTP notifier service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+115 -352
View File
@@ -6,179 +6,164 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/go-redis/redis/v8"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
jaegerClient "github.com/mainflux/mainflux/internal/clients/jaeger"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
redisClient "github.com/mainflux/mainflux/internal/clients/redis"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
grpcserver "github.com/mainflux/mainflux/internal/server/grpc"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/uuid"
"github.com/mainflux/mainflux/things"
"github.com/mainflux/mainflux/things/api"
authgrpcapi "github.com/mainflux/mainflux/things/api/auth/grpc"
authhttpapi "github.com/mainflux/mainflux/things/api/auth/http"
thhttpapi "github.com/mainflux/mainflux/things/api/things/http"
"github.com/mainflux/mainflux/things/postgres"
thingsPg "github.com/mainflux/mainflux/things/postgres"
rediscache "github.com/mainflux/mainflux/things/redis"
localusers "github.com/mainflux/mainflux/things/standalone"
"github.com/mainflux/mainflux/things/tracing"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
svcName = "things"
envPrefix = "MF_THINGS_"
envPrefixCache = "MF_THINGS_CACHE_"
envPrefixES = "MF_THINGS_ES_"
envPrefixHttp = "MF_THINGS_HTTP_"
envPrefixAuthHttp = "MF_THINGS_AUTH_HTTP_"
envPrefixAuthGrpc = "MF_THINGS_AUTH_GRPC_"
defDB = "things"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defClientTLS = "false"
defCACerts = ""
defCacheURL = "localhost:6379"
defCachePass = ""
defCacheDB = "0"
defESURL = "localhost:6379"
defESPass = ""
defESDB = "0"
defHTTPPort = "8182"
defAuthHTTPPort = "8989"
defAuthGRPCPort = "8181"
defServerCert = ""
defServerKey = ""
defStandaloneEmail = ""
defStandaloneToken = ""
defJaegerURL = ""
defAuthURL = "localhost:8181"
defAuthTimeout = "1s"
envLogLevel = "MF_THINGS_LOG_LEVEL"
envDBHost = "MF_THINGS_DB_HOST"
envDBPort = "MF_THINGS_DB_PORT"
envDBUser = "MF_THINGS_DB_USER"
envDBPass = "MF_THINGS_DB_PASS"
envDB = "MF_THINGS_DB"
envDBSSLMode = "MF_THINGS_DB_SSL_MODE"
envDBSSLCert = "MF_THINGS_DB_SSL_CERT"
envDBSSLKey = "MF_THINGS_DB_SSL_KEY"
envDBSSLRootCert = "MF_THINGS_DB_SSL_ROOT_CERT"
envClientTLS = "MF_THINGS_CLIENT_TLS"
envCACerts = "MF_THINGS_CA_CERTS"
envCacheURL = "MF_THINGS_CACHE_URL"
envCachePass = "MF_THINGS_CACHE_PASS"
envCacheDB = "MF_THINGS_CACHE_DB"
envESURL = "MF_THINGS_ES_URL"
envESPass = "MF_THINGS_ES_PASS"
envESDB = "MF_THINGS_ES_DB"
envHTTPPort = "MF_THINGS_HTTP_PORT"
envAuthHTTPPort = "MF_THINGS_AUTH_HTTP_PORT"
envAuthGRPCPort = "MF_THINGS_AUTH_GRPC_PORT"
envServerCert = "MF_THINGS_SERVER_CERT"
envServerKey = "MF_THINGS_SERVER_KEY"
envStandaloneEmail = "MF_THINGS_STANDALONE_EMAIL"
envStandaloneToken = "MF_THINGS_STANDALONE_TOKEN"
envJaegerURL = "MF_JAEGER_URL"
envAuthURL = "MF_AUTH_GRPC_URL"
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
defSvcHttpPort = "8182"
defSvcAuthHttpPort = "8989"
defSvcAuthGrpcPort = "8181"
)
type config struct {
logLevel string
dbConfig postgres.Config
clientTLS bool
caCerts string
cacheURL string
cachePass string
cacheDB string
esURL string
esPass string
esDB string
httpPort string
authHTTPPort string
authGRPCPort string
serverCert string
serverKey string
standaloneEmail string
standaloneToken string
jaegerURL string
authURL string
authTimeout time.Duration
LogLevel string `env:"MF_THINGS_LOG_LEVEL" envDefault:"info"`
StandaloneEmail string `env:"MF_THINGS_STANDALONE_EMAIL" envDefault:""`
StandaloneToken string `env:"MF_THINGS_STANDALONE_TOKEN" envDefault:""`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
// Create new things configuration
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s service configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
cacheClient := connectToRedis(cfg.cacheURL, cfg.cachePass, cfg.cacheDB, logger)
esClient := connectToRedis(cfg.esURL, cfg.esPass, cfg.esDB, logger)
db := connectToDB(cfg.dbConfig, logger)
// Create new database for things
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *thingsPg.Migration(), dbConfig)
if err != nil {
log.Fatal(err.Error())
}
defer db.Close()
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
auth, close := createAuthClient(cfg, authTracer, logger)
if close != nil {
defer close()
// Setup new redis cache client
cacheClient, err := redisClient.Setup(envPrefixCache)
if err != nil {
log.Fatal(err.Error())
}
defer cacheClient.Close()
dbTracer, dbCloser := initJaeger("things_db", cfg.jaegerURL, logger)
// Setup new redis event store client
esClient, err := redisClient.Setup(envPrefixES)
if err != nil {
log.Fatal(err.Error())
}
defer esClient.Close()
// Setup new auth grpc client
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err)
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
// Create tracer for things database
dbTracer, dbCloser, err := jaegerClient.NewTracer("things_db", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer dbCloser.Close()
cacheTracer, cacheCloser := initJaeger("things_cache", cfg.jaegerURL, logger)
// Create tracer for things cache
cacheTracer, cacheCloser, err := jaegerClient.NewTracer("things_cache", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer cacheCloser.Close()
// Create new service
svc := newService(auth, dbTracer, cacheTracer, db, cacheClient, esClient, logger)
// Create tracer for HTTP handler things
thingsTracer, thingsCloser, err := jaegerClient.NewTracer("things", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer thingsCloser.Close()
// Create new HTTP server
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s gRPC server configuration : %s", svcName, err.Error())
}
hs1 := httpserver.New(ctx, cancel, "thing-http", httpServerConfig, thhttpapi.MakeHandler(thingsTracer, svc, logger), logger)
// Create new things auth http server
authHttpServerConfig := server.Config{Port: defSvcAuthHttpPort}
if err := env.Parse(&authHttpServerConfig, env.Options{Prefix: envPrefixAuthHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s gRPC server configuration : %s", svcName, err.Error())
}
hs2 := httpserver.New(ctx, cancel, "auth-http", authHttpServerConfig, authhttpapi.MakeHandler(thingsTracer, svc, logger), logger)
// Create new grpc server
registerThingsServiceServer := func(srv *grpc.Server) {
mainflux.RegisterThingsServiceServer(srv, authgrpcapi.NewServer(thingsTracer, svc))
}
grpcServerConfig := server.Config{Port: defSvcAuthGrpcPort}
if err := env.Parse(&grpcServerConfig, env.Options{Prefix: envPrefixAuthGrpc, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s gRPC server configuration : %s", svcName, err.Error())
}
gs := grpcserver.New(ctx, cancel, svcName, grpcServerConfig, registerThingsServiceServer, logger)
//Start all servers
g.Go(func() error {
return startHTTPServer(ctx, "thing-http", thhttpapi.MakeHandler(thingsTracer, svc, logger), cfg.httpPort, cfg, logger)
return hs1.Start()
})
g.Go(func() error {
return hs2.Start()
})
g.Go(func() error {
return gs.Start()
})
g.Go(func() error {
return startHTTPServer(ctx, "auth-http", authhttpapi.MakeHandler(thingsTracer, svc, logger), cfg.authHTTPPort, cfg, logger)
})
g.Go(func() error {
return startGRPCServer(ctx, svc, thingsTracer, cfg, logger)
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Things service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs1, hs2, gs)
})
if err := g.Wait(); err != nil {
@@ -186,141 +171,13 @@ func main() {
}
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envAuthTimeout, defAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envAuthTimeout, err.Error())
}
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
dbConfig: dbConfig,
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
cacheURL: mainflux.Env(envCacheURL, defCacheURL),
cachePass: mainflux.Env(envCachePass, defCachePass),
cacheDB: mainflux.Env(envCacheDB, defCacheDB),
esURL: mainflux.Env(envESURL, defESURL),
esPass: mainflux.Env(envESPass, defESPass),
esDB: mainflux.Env(envESDB, defESDB),
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
authHTTPPort: mainflux.Env(envAuthHTTPPort, defAuthHTTPPort),
authGRPCPort: mainflux.Env(envAuthGRPCPort, defAuthGRPCPort),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
standaloneEmail: mainflux.Env(envStandaloneEmail, defStandaloneEmail),
standaloneToken: mainflux.Env(envStandaloneToken, defStandaloneToken),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
authURL: mainflux.Env(envAuthURL, defAuthURL),
authTimeout: authTimeout,
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToRedis(cacheURL, cachePass string, cacheDB string, logger logger.Logger) *redis.Client {
db, err := strconv.Atoi(cacheDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to cache: %s", err))
os.Exit(1)
}
return redis.NewClient(&redis.Options{
Addr: cacheURL,
Password: cachePass,
DB: db,
})
}
func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
db, err := postgres.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err))
os.Exit(1)
}
return db
}
func createAuthClient(cfg config, tracer opentracing.Tracer, logger logger.Logger) (mainflux.AuthServiceClient, func() error) {
if cfg.standaloneEmail != "" && cfg.standaloneToken != "" {
return localusers.NewAuthService(cfg.standaloneEmail, cfg.standaloneToken), nil
}
conn := connectToAuth(cfg, logger)
return authapi.NewClient(tracer, conn, cfg.authTimeout), conn.Close
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.authURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
return conn
}
func newService(auth mainflux.AuthServiceClient, dbTracer opentracing.Tracer, cacheTracer opentracing.Tracer, db *sqlx.DB, cacheClient *redis.Client, esClient *redis.Client, logger logger.Logger) things.Service {
database := postgres.NewDatabase(db)
database := thingsPg.NewDatabase(db)
thingsRepo := postgres.NewThingRepository(database)
thingsRepo := thingsPg.NewThingRepository(database)
thingsRepo = tracing.ThingRepositoryMiddleware(dbTracer, thingsRepo)
channelsRepo := postgres.NewChannelRepository(database)
channelsRepo := thingsPg.NewChannelRepository(database)
channelsRepo = tracing.ChannelRepositoryMiddleware(dbTracer, channelsRepo)
chanCache := rediscache.NewChannelCache(cacheClient)
@@ -333,102 +190,8 @@ func newService(auth mainflux.AuthServiceClient, dbTracer opentracing.Tracer, ca
svc := things.New(auth, thingsRepo, channelsRepo, chanCache, thingCache, idProvider)
svc = rediscache.NewEventStoreMiddleware(svc, esClient)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "things",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "things",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, typ string, handler http.Handler, port string, cfg config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: handler}
switch {
case cfg.serverCert != "" || cfg.serverKey != "":
logger.Info(fmt.Sprintf("Things %s service started using https on port %s with cert %s key %s",
typ, port, cfg.serverCert, cfg.serverKey))
go func() {
errCh <- server.ListenAndServeTLS(cfg.serverCert, cfg.serverKey)
}()
default:
logger.Info(fmt.Sprintf("Things %s service started using http on port %s", typ, cfg.httpPort))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Things %s service error occurred during shutdown at %s: %s", typ, p, err))
return fmt.Errorf("things %s service occurred during shutdown at %s: %w", typ, p, err)
}
logger.Info(fmt.Sprintf("Things %s service shutdown of http at %s", typ, p))
return nil
case err := <-errCh:
return err
}
}
func startGRPCServer(ctx context.Context, svc things.Service, tracer opentracing.Tracer, cfg config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.authGRPCPort)
errCh := make(chan error)
var server *grpc.Server
listener, err := net.Listen("tcp", p)
if err != nil {
return fmt.Errorf("failed to listen on port %s: %w", cfg.authGRPCPort, err)
}
switch {
case cfg.serverCert != "" || cfg.serverKey != "":
creds, err := credentials.NewServerTLSFromFile(cfg.serverCert, cfg.serverKey)
if err != nil {
return fmt.Errorf("failed to load things certificates: %w", err)
}
logger.Info(fmt.Sprintf("Things gRPC service started using https on port %s with cert %s key %s",
cfg.authGRPCPort, cfg.serverCert, cfg.serverKey))
server = grpc.NewServer(grpc.Creds(creds))
default:
logger.Info(fmt.Sprintf("Things gRPC service started using http on port %s", cfg.authGRPCPort))
server = grpc.NewServer()
}
mainflux.RegisterThingsServiceServer(server, authgrpcapi.NewServer(tracer, svc))
go func() {
errCh <- server.Serve(listener)
}()
select {
case <-ctx.Done():
c := make(chan bool)
go func() {
defer close(c)
server.GracefulStop()
}()
select {
case <-c:
case <-time.After(stopWaitTime):
}
logger.Info(fmt.Sprintf("Things gRPC service shutdown at %s", p))
return nil
case err := <-errCh:
return err
}
}
+52 -243
View File
@@ -6,124 +6,89 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/readers"
"github.com/mainflux/mainflux/readers/api"
"github.com/mainflux/mainflux/readers/timescale"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
svcName = "timescaledb-reader"
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defPort = "8911"
defClientTLS = "false"
defCACerts = ""
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "mainflux"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defJaegerURL = ""
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
envLogLevel = "MF_TIMESCALE_READER_LOG_LEVEL"
envPort = "MF_TIMESCALE_READER_PORT"
envClientTLS = "MF_TIMESCALE_READER_CLIENT_TLS"
envCACerts = "MF_TIMESCALE_READER_CA_CERTS"
envDBHost = "MF_TIMESCALE_READER_DB_HOST"
envDBPort = "MF_TIMESCALE_READER_DB_PORT"
envDBUser = "MF_TIMESCALE_READER_DB_USER"
envDBPass = "MF_TIMESCALE_READER_DB_PASS"
envDB = "MF_TIMESCALE_READER_DB"
envDBSSLMode = "MF_TIMESCALE_READER_DB_SSL_MODE"
envDBSSLCert = "MF_TIMESCALE_READER_DB_SSL_CERT"
envDBSSLKey = "MF_TIMESCALE_READER_DB_SSL_KEY"
envDBSSLRootCert = "MF_TIMESCALE_READER_DB_SSL_ROOT_CERT"
envJaegerURL = "MF_JAEGER_URL"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsAuthTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
svcName = "timescaledb-reader"
envPrefix = "MF_TIMESCALE_READER_"
envPrefixHttp = "MF_TIMESCALE_READER_HTTP_"
defDB = "messages"
defSvcHttpPort = "8180"
)
type config struct {
logLevel string
port string
clientTLS bool
caCerts string
dbConfig timescale.Config
jaegerURL string
thingsAuthURL string
usersAuthURL string
thingsAuthTimeout time.Duration
usersAuthTimeout time.Duration
LogLevel string `env:"MF_TIMESCALE_READER_LOG_LEVEL" envDefault:"info"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s service configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
conn := connectToThings(cfg, logger)
defer conn.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
authConn := connectToAuth(cfg, logger)
defer authConn.Close()
auth := authapi.NewClient(authTracer, authConn, cfg.usersAuthTimeout)
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
db := connectToDB(cfg.dbConfig, logger)
dbConfig := pgClient.Config{Name: defDB}
if err := dbConfig.LoadEnv(envPrefix); err != nil {
log.Fatal(err.Error())
}
db, err := pgClient.Connect(dbConfig)
if err != nil {
log.Fatal(err.Error())
}
defer db.Close()
repo := newService(db, logger)
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err)
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer tcHandler.Close()
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(repo, tc, auth, svcName, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, repo, tc, auth, cfg.port, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Timescale reader service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -131,167 +96,11 @@ func main() {
}
}
func loadConfig() config {
dbConfig := timescale.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsAuthTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsAuthTimeout, err.Error())
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
dbConfig: dbConfig,
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
thingsAuthTimeout: authTimeout,
}
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
logger.Info("Connecting to auth via gRPC")
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.usersAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
logger.Info(fmt.Sprintf("Established gRPC connection to auth via gRPC: %s", cfg.usersAuthURL))
return conn
}
func connectToDB(dbConfig timescale.Config, logger logger.Logger) *sqlx.DB {
db, err := timescale.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to Timescale: %s", err))
os.Exit(1)
}
return db
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
return conn
}
func newService(db *sqlx.DB, logger logger.Logger) readers.MessageRepository {
svc := timescale.New(db)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "timescale",
Subsystem: "message_reader",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "timescale",
Subsystem: "message_reader",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("timescale", "message_reader")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, repo readers.MessageRepository, tc mainflux.ThingsServiceClient, ac mainflux.AuthServiceClient, port string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(repo, tc, ac, svcName, logger)}
logger.Info(fmt.Sprintf("Timescale reader service started, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Timescale reader service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("Timescale reader service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Timescale reader service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+41 -131
View File
@@ -7,100 +7,81 @@ import (
"context"
"fmt"
"log"
"net/http"
"os"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
"github.com/mainflux/mainflux/consumers"
"github.com/mainflux/mainflux/consumers/writers/api"
"github.com/mainflux/mainflux/consumers/writers/timescale"
"github.com/mainflux/mainflux/internal"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
stdprometheus "github.com/prometheus/client_golang/prometheus"
"golang.org/x/sync/errgroup"
)
const (
svcName = "timescaledb-writer"
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defBrokerURL = "nats://localhost:4222"
defPort = "8180"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "mainflux"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defConfigPath = "/config.toml"
envBrokerURL = "MF_BROKER_URL"
envLogLevel = "MF_TIMESCALE_WRITER_LOG_LEVEL"
envPort = "MF_TIMESCALE_WRITER_PORT"
envDBHost = "MF_TIMESCALE_WRITER_DB_HOST"
envDBPort = "MF_TIMESCALE_WRITER_DB_PORT"
envDBUser = "MF_TIMESCALE_WRITER_DB_USER"
envDBPass = "MF_TIMESCALE_WRITER_DB_PASS"
envDB = "MF_TIMESCALE_WRITER_DB"
envDBSSLMode = "MF_TIMESCALE_WRITER_DB_SSL_MODE"
envDBSSLCert = "MF_TIMESCALE_WRITER_DB_SSL_CERT"
envDBSSLKey = "MF_TIMESCALE_WRITER_DB_SSL_KEY"
envDBSSLRootCert = "MF_TIMESCALE_WRITER_DB_SSL_ROOT_CERT"
envConfigPath = "MF_TIMESCALE_WRITER_CONFIG_PATH"
svcName = "timescaledb-writer"
envPrefix = "MF_TIMESCALE_WRITER_"
envPrefixHttp = "MF_TIMESCALE_WRITER_HTTP_"
defDB = "messages"
defSvcHttpPort = "8180"
)
type config struct {
brokerURL string
logLevel string
port string
configPath string
dbConfig timescale.Config
LogLevel string `env:"MF_TIMESCALE_WRITER_LOG_LEVEL" envDefault:"info"`
ConfigPath string `env:"MF_TIMESCALE_WRITER_CONFIG_PATH" envDefault:"/config.toml"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
if err != nil {
log.Fatalf(err.Error())
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s service configuration : %s", svcName, err.Error())
}
pubSub, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatal(err.Error())
}
defer pubSub.Close()
db := connectToDB(cfg.dbConfig, logger)
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *timescale.Migration(), dbConfig)
if err != nil {
log.Fatal(err.Error())
}
defer db.Close()
repo := newService(db, logger)
if err = consumers.Start(svcName, pubSub, repo, cfg.configPath, logger); err != nil {
logger.Error(fmt.Sprintf("Failed to create Timescale writer: %s", err))
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pubSub.Close()
if err = consumers.Start(svcName, pubSub, repo, cfg.ConfigPath, logger); err != nil {
log.Fatalf("failed to create Timescale writer: %s", err.Error())
}
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svcName), logger)
g.Go(func() error {
return startHTTPServer(ctx, cfg.port, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Timescale writer service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -108,81 +89,10 @@ func main() {
}
}
func loadConfig() config {
dbConfig := timescale.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
return config{
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
port: mainflux.Env(envPort, defPort),
configPath: mainflux.Env(envConfigPath, defConfigPath),
dbConfig: dbConfig,
}
}
func connectToDB(dbConfig timescale.Config, logger logger.Logger) *sqlx.DB {
db, err := timescale.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to Postgres: %s", err))
os.Exit(1)
}
return db
}
func newService(db *sqlx.DB, logger logger.Logger) consumers.Consumer {
svc := timescale.New(db)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "timescale",
Subsystem: "message_writer",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "timescale",
Subsystem: "message_writer",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("timescale", "message_writer")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startHTTPServer(ctx context.Context, port string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svcName)}
logger.Info(fmt.Sprintf("Timescale writer service started, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("timescale writer service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("timescale writer service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Timescale writer service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+73 -255
View File
@@ -6,20 +6,20 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/go-redis/redis/v8"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
jaegerClient "github.com/mainflux/mainflux/internal/clients/jaeger"
mongoClient "github.com/mainflux/mainflux/internal/clients/mongo"
redisClient "github.com/mainflux/mainflux/internal/clients/redis"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
"github.com/mainflux/mainflux/pkg/uuid"
@@ -31,128 +31,104 @@ import (
rediscache "github.com/mainflux/mainflux/twins/redis"
"github.com/mainflux/mainflux/twins/tracing"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"go.mongodb.org/mongo-driver/mongo"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
svcName = "twins"
queue = "twins"
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defHTTPPort = "8180"
defJaegerURL = ""
defServerCert = ""
defServerKey = ""
defDB = "mainflux-twins"
defDBHost = "localhost"
defDBPort = "27017"
defCacheURL = "localhost:6379"
defCachePass = ""
defCacheDB = "0"
defStandaloneEmail = ""
defStandaloneToken = ""
defClientTLS = "false"
defCACerts = ""
defChannelID = ""
defBrokerURL = "nats://localhost:4222"
defAuthURL = "localhost:8181"
defAuthTimeout = "1s"
envLogLevel = "MF_TWINS_LOG_LEVEL"
envHTTPPort = "MF_TWINS_HTTP_PORT"
envJaegerURL = "MF_JAEGER_URL"
envServerCert = "MF_TWINS_SERVER_CERT"
envServerKey = "MF_TWINS_SERVER_KEY"
envDB = "MF_TWINS_DB"
envDBHost = "MF_TWINS_DB_HOST"
envDBPort = "MF_TWINS_DB_PORT"
envCacheURL = "MF_TWINS_CACHE_URL"
envCachePass = "MF_TWINS_CACHE_PASS"
envCacheDB = "MF_TWINS_CACHE_DB"
envStandaloneEmail = "MF_TWINS_STANDALONE_EMAIL"
envStandaloneToken = "MF_TWINS_STANDALONE_TOKEN"
envClientTLS = "MF_TWINS_CLIENT_TLS"
envCACerts = "MF_TWINS_CA_CERTS"
envChannelID = "MF_TWINS_CHANNEL_ID"
envBrokerURL = "MF_BROKER_URL"
envAuthURL = "MF_AUTH_GRPC_URL"
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
svcName = "twins"
queue = "twins"
envPrefix = "MF_TWINS_"
envPrefixHttp = "MF_TWINS_HTTP_"
envPrefixCache = "MF_TWINS_CACHE_"
defSvcHttpPort = "8180"
)
type config struct {
logLevel string
httpPort string
jaegerURL string
serverCert string
serverKey string
dbCfg twmongodb.Config
cacheURL string
cachePass string
cacheDB string
standaloneEmail string
standaloneToken string
clientTLS bool
caCerts string
channelID string
brokerURL string
authURL string
authTimeout time.Duration
LogLevel string `env:"MF_TWINS_LOG_LEVEL" envDefault:"info"`
StandaloneEmail string `env:"MF_TWINS_STANDALONE_EMAIL" envDefault:""`
StandaloneToken string `env:"MF_TWINS_STANDALONE_TOKEN" envDefault:""`
ChannelID string `env:"MF_TWINS_CHANNEL_ID" envDefault:""`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg, env.Options{Prefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
cacheClient := connectToRedis(cfg.cacheURL, cfg.cachePass, cfg.cacheDB, logger)
cacheTracer, cacheCloser := initJaeger("twins_cache", cfg.jaegerURL, logger)
cacheClient, err := redisClient.Setup(envPrefixCache)
if err != nil {
log.Fatalf(err.Error())
}
defer cacheClient.Close()
cacheTracer, cacheCloser, err := jaegerClient.NewTracer("twins_cache", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer cacheCloser.Close()
db, err := twmongodb.Connect(cfg.dbCfg, logger)
db, err := mongoClient.Setup(envPrefix)
if err != nil {
logger.Error(err.Error())
os.Exit(1)
log.Fatalf("failed to setup postgres database : %s", err.Error())
}
dbTracer, dbCloser, err := jaegerClient.NewTracer("twins_db", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
dbTracer, dbCloser := initJaeger("twins_db", cfg.jaegerURL, logger)
defer dbCloser.Close()
authTracer, authCloser := initJaeger("auth", cfg.jaegerURL, logger)
defer authCloser.Close()
auth, _ := createAuthClient(cfg, authTracer, logger)
var auth mainflux.AuthServiceClient
switch cfg.StandaloneEmail != "" && cfg.StandaloneToken != "" {
case true:
auth = localusers.NewAuthService(cfg.StandaloneEmail, cfg.StandaloneToken)
default:
authServiceClient, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err.Error())
}
defer authHandler.Close()
auth = authServiceClient
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
}
pubSub, err := brokers.NewPubSub(cfg.brokerURL, queue, logger)
pubSub, err := brokers.NewPubSub(cfg.BrokerURL, queue, logger)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatalf("failed to connect to message broker: %s", err.Error())
}
defer pubSub.Close()
svc := newService(svcName, pubSub, cfg.channelID, auth, dbTracer, db, cacheTracer, cacheClient, logger)
svc := newService(svcName, pubSub, cfg.ChannelID, auth, dbTracer, db, cacheTracer, cacheClient, logger)
tracer, closer := initJaeger("twins", cfg.jaegerURL, logger)
tracer, closer, err := jaegerClient.NewTracer("twins", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer closer.Close()
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, twapi.MakeHandler(tracer, svc, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, twapi.MakeHandler(tracer, svc, logger), cfg.httpPort, cfg, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Twins service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -160,116 +136,6 @@ func main() {
}
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envAuthTimeout, defAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envAuthTimeout, err.Error())
}
dbCfg := twmongodb.Config{
Name: mainflux.Env(envDB, defDB),
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
dbCfg: dbCfg,
cacheURL: mainflux.Env(envCacheURL, defCacheURL),
cachePass: mainflux.Env(envCachePass, defCachePass),
cacheDB: mainflux.Env(envCacheDB, defCacheDB),
standaloneEmail: mainflux.Env(envStandaloneEmail, defStandaloneEmail),
standaloneToken: mainflux.Env(envStandaloneToken, defStandaloneToken),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
channelID: mainflux.Env(envChannelID, defChannelID),
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
authURL: mainflux.Env(envAuthURL, defAuthURL),
authTimeout: authTimeout,
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func createAuthClient(cfg config, tracer opentracing.Tracer, logger logger.Logger) (mainflux.AuthServiceClient, func() error) {
if cfg.standaloneEmail != "" && cfg.standaloneToken != "" {
return localusers.NewAuthService(cfg.standaloneEmail, cfg.standaloneToken), nil
}
conn := connectToAuth(cfg, logger)
return authapi.NewClient(tracer, conn, cfg.authTimeout), conn.Close
}
func connectToAuth(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.authURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
return conn
}
func connectToRedis(cacheURL, cachePass, cacheDB string, logger logger.Logger) *redis.Client {
db, err := strconv.Atoi(cacheDB)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to cache: %s", err))
os.Exit(1)
}
return redis.NewClient(&redis.Options{
Addr: cacheURL,
Password: cachePass,
DB: db,
})
}
func newService(id string, ps messaging.PubSub, chanID string, users mainflux.AuthServiceClient, dbTracer opentracing.Tracer, db *mongo.Database, cacheTracer opentracing.Tracer, cacheClient *redis.Client, logger logger.Logger) twins.Service {
twinRepo := twmongodb.NewTwinRepository(db)
twinRepo = tracing.TwinRepositoryMiddleware(dbTracer, twinRepo)
@@ -283,25 +149,11 @@ func newService(id string, ps messaging.PubSub, chanID string, users mainflux.Au
svc := twins.New(ps, users, twinRepo, twinCache, stateRepo, idProvider, chanID, logger)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "twins",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "twins",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
err := ps.Subscribe(id, brokers.SubjectAllChannels, handle(logger, chanID, svc))
if err != nil {
logger.Error(err.Error())
os.Exit(1)
log.Fatalf(err.Error())
}
return svc
}
@@ -321,40 +173,6 @@ func handle(logger logger.Logger, chanID string, svc twins.Service) handlerFunc
}
}
func startHTTPServer(ctx context.Context, handler http.Handler, port string, cfg config, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: handler}
switch {
case cfg.serverCert != "" || cfg.serverKey != "":
logger.Info(fmt.Sprintf("Twins service started using https on port %s with cert %s key %s",
port, cfg.serverCert, cfg.serverKey))
go func() {
errCh <- server.ListenAndServeTLS(cfg.serverCert, cfg.serverKey)
}()
default:
logger.Info(fmt.Sprintf("Twins service started using http on port %s", cfg.httpPort))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Twins service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("twins service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Twins service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
type handlerFunc func(msg *messaging.Message) error
func (h handlerFunc) Handle(msg *messaging.Message) error {
+83 -308
View File
@@ -6,16 +6,17 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"regexp"
"strconv"
"time"
"github.com/mainflux/mainflux/internal"
authClient "github.com/mainflux/mainflux/internal/clients/grpc/auth"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/internal/email"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/uuid"
"github.com/mainflux/mainflux/users"
@@ -23,153 +24,99 @@ import (
"github.com/mainflux/mainflux/users/emailer"
"github.com/mainflux/mainflux/users/tracing"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
jaegerClient "github.com/mainflux/mainflux/internal/clients/jaeger"
"github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/users/api"
"github.com/mainflux/mainflux/users/postgres"
usersPg "github.com/mainflux/mainflux/users/postgres"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
)
const (
stopWaitTime = 5 * time.Second
defLogLevel = "error"
defDBHost = "localhost"
defDBPort = "5432"
defDBUser = "mainflux"
defDBPass = "mainflux"
defDB = "users"
defDBSSLMode = "disable"
defDBSSLCert = ""
defDBSSLKey = ""
defDBSSLRootCert = ""
defHTTPPort = "8180"
defServerCert = ""
defServerKey = ""
defJaegerURL = ""
defEmailHost = "localhost"
defEmailPort = "25"
defEmailUsername = "root"
defEmailPassword = ""
defEmailFromAddress = ""
defEmailFromName = ""
defEmailTemplate = "email.tmpl"
defAdminEmail = ""
defAdminPassword = ""
defPassRegex = "^.{8,}$"
defTokenResetEndpoint = "/reset-request" // URL where user lands after click on the reset link from email
defAuthTLS = "false"
defAuthCACerts = ""
defAuthURL = "localhost:8181"
defAuthTimeout = "1s"
defSelfRegister = "true" // By default, everybody can create a user. Otherwise, only admin can create a user.
envLogLevel = "MF_USERS_LOG_LEVEL"
envDBHost = "MF_USERS_DB_HOST"
envDBPort = "MF_USERS_DB_PORT"
envDBUser = "MF_USERS_DB_USER"
envDBPass = "MF_USERS_DB_PASS"
envDB = "MF_USERS_DB"
envDBSSLMode = "MF_USERS_DB_SSL_MODE"
envDBSSLCert = "MF_USERS_DB_SSL_CERT"
envDBSSLKey = "MF_USERS_DB_SSL_KEY"
envDBSSLRootCert = "MF_USERS_DB_SSL_ROOT_CERT"
envHTTPPort = "MF_USERS_HTTP_PORT"
envServerCert = "MF_USERS_SERVER_CERT"
envServerKey = "MF_USERS_SERVER_KEY"
envJaegerURL = "MF_JAEGER_URL"
envAdminEmail = "MF_USERS_ADMIN_EMAIL"
envAdminPassword = "MF_USERS_ADMIN_PASSWORD"
envPassRegex = "MF_USERS_PASS_REGEX"
envEmailHost = "MF_EMAIL_HOST"
envEmailPort = "MF_EMAIL_PORT"
envEmailUsername = "MF_EMAIL_USERNAME"
envEmailPassword = "MF_EMAIL_PASSWORD"
envEmailFromAddress = "MF_EMAIL_FROM_ADDRESS"
envEmailFromName = "MF_EMAIL_FROM_NAME"
envEmailTemplate = "MF_EMAIL_TEMPLATE"
envTokenResetEndpoint = "MF_TOKEN_RESET_ENDPOINT"
envAuthTLS = "MF_AUTH_CLIENT_TLS"
envAuthCACerts = "MF_AUTH_CA_CERTS"
envAuthURL = "MF_AUTH_GRPC_URL"
envAuthTimeout = "MF_AUTH_GRPC_TIMEOUT"
envSelfRegister = "MF_USERS_ALLOW_SELF_REGISTER"
svcName = "users"
envPrefix = "MF_USERS_"
envPrefixHttp = "MF_USERS_HTTP_"
defDB = "users"
defSvcHttpPort = "8180"
)
type config struct {
logLevel string
dbConfig postgres.Config
emailConf email.Config
httpPort string
serverCert string
serverKey string
jaegerURL string
resetURL string
authTLS bool
authCACerts string
authURL string
authTimeout time.Duration
adminEmail string
adminPassword string
passRegex *regexp.Regexp
selfRegister bool
LogLevel string `env:"MF_USERS_LOG_LEVEL" envDefault:"info"`
AdminEmail string `env:"MF_USERS_ADMIN_EMAIL" envDefault:""`
AdminPassword string `env:"MF_USERS_ADMIN_PASSWORD" envDefault:""`
PassRegexText string `env:"MF_USERS_PASS_REGEX" envDefault:"^.{8,}$"`
SelfRegister bool `env:"MF_USERS_ALLOW_SELF_REGISTER" envDefault:"true"`
ResetURL string `env:"MF_TOKEN_RESET_ENDPOINT" envDefault:"email.tmpl"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
PassRegex *regexp.Regexp
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s configuration : %s", svcName, err.Error())
}
passRegex, err := regexp.Compile(cfg.PassRegexText)
if err != nil {
log.Fatalf("Invalid password validation rules %s\n", cfg.PassRegexText)
}
cfg.PassRegex = passRegex
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
db := connectToDB(cfg.dbConfig, logger)
defer db.Close()
authTracer, closer := initJaeger("auth", cfg.jaegerURL, logger)
defer closer.Close()
auth, close := connectToAuth(cfg, authTracer, logger)
if close != nil {
defer close()
ec := email.Config{}
if err := env.Parse(&ec); err != nil {
log.Fatalf("failed to load email configuration : %s", err.Error())
}
tracer, closer := initJaeger("users", cfg.jaegerURL, logger)
defer closer.Close()
dbConfig := pgClient.Config{Name: defDB}
db, err := pgClient.SetupWithConfig(envPrefix, *usersPg.Migration(), dbConfig)
if err != nil {
log.Fatal(err)
}
defer db.Close()
dbTracer, dbCloser := initJaeger("users_db", cfg.jaegerURL, logger)
auth, authHandler, err := authClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
log.Fatal(err)
}
defer authHandler.Close()
logger.Info("Successfully connected to auth grpc server " + authHandler.Secure())
dbTracer, dbCloser, err := jaegerClient.NewTracer("auth_db", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer dbCloser.Close()
svc := newService(db, dbTracer, auth, cfg, logger)
svc := newService(db, dbTracer, auth, cfg, ec, logger)
tracer, closer, err := jaegerClient.NewTracer("users", cfg.JaegerURL)
if err != nil {
log.Fatalf("failed to init Jaeger: %s", err.Error())
}
defer closer.Close()
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, tracer, logger), logger)
g.Go(func() error {
return startHTTPServer(ctx, tracer, svc, cfg.httpPort, cfg.serverCert, cfg.serverKey, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("Users service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -177,162 +124,28 @@ func main() {
}
}
func loadConfig() config {
authTimeout, err := time.ParseDuration(mainflux.Env(envAuthTimeout, defAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envAuthTimeout, err.Error())
}
tls, err := strconv.ParseBool(mainflux.Env(envAuthTLS, defAuthTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envAuthTLS)
}
passRegex, err := regexp.Compile(mainflux.Env(envPassRegex, defPassRegex))
if err != nil {
log.Fatalf("Invalid password validation rules %s\n", envPassRegex)
}
selfRegister, err := strconv.ParseBool(mainflux.Env(envSelfRegister, defSelfRegister))
if err != nil {
log.Fatalf("Invalid %s value: %s", envSelfRegister, err.Error())
}
dbConfig := postgres.Config{
Host: mainflux.Env(envDBHost, defDBHost),
Port: mainflux.Env(envDBPort, defDBPort),
User: mainflux.Env(envDBUser, defDBUser),
Pass: mainflux.Env(envDBPass, defDBPass),
Name: mainflux.Env(envDB, defDB),
SSLMode: mainflux.Env(envDBSSLMode, defDBSSLMode),
SSLCert: mainflux.Env(envDBSSLCert, defDBSSLCert),
SSLKey: mainflux.Env(envDBSSLKey, defDBSSLKey),
SSLRootCert: mainflux.Env(envDBSSLRootCert, defDBSSLRootCert),
}
emailConf := email.Config{
FromAddress: mainflux.Env(envEmailFromAddress, defEmailFromAddress),
FromName: mainflux.Env(envEmailFromName, defEmailFromName),
Host: mainflux.Env(envEmailHost, defEmailHost),
Port: mainflux.Env(envEmailPort, defEmailPort),
Username: mainflux.Env(envEmailUsername, defEmailUsername),
Password: mainflux.Env(envEmailPassword, defEmailPassword),
Template: mainflux.Env(envEmailTemplate, defEmailTemplate),
}
return config{
logLevel: mainflux.Env(envLogLevel, defLogLevel),
dbConfig: dbConfig,
emailConf: emailConf,
httpPort: mainflux.Env(envHTTPPort, defHTTPPort),
serverCert: mainflux.Env(envServerCert, defServerCert),
serverKey: mainflux.Env(envServerKey, defServerKey),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
resetURL: mainflux.Env(envTokenResetEndpoint, defTokenResetEndpoint),
authTLS: tls,
authCACerts: mainflux.Env(envAuthCACerts, defAuthCACerts),
authURL: mainflux.Env(envAuthURL, defAuthURL),
authTimeout: authTimeout,
adminEmail: mainflux.Env(envAdminEmail, defAdminEmail),
adminPassword: mainflux.Env(envAdminPassword, defAdminPassword),
passRegex: passRegex,
selfRegister: selfRegister,
}
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger: %s", err))
os.Exit(1)
}
return tracer, closer
}
func connectToDB(dbConfig postgres.Config, logger logger.Logger) *sqlx.DB {
db, err := postgres.Connect(dbConfig)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to postgres: %s", err))
os.Exit(1)
}
return db
}
func connectToAuth(cfg config, tracer opentracing.Tracer, logger logger.Logger) (mainflux.AuthServiceClient, func() error) {
var opts []grpc.DialOption
if cfg.authTLS {
if cfg.authCACerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.authCACerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to create tls credentials: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
opts = append(opts, grpc.WithInsecure())
logger.Info("gRPC communication is not encrypted")
}
conn, err := grpc.Dial(cfg.authURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to auth service: %s", err))
os.Exit(1)
}
return authapi.NewClient(tracer, conn, cfg.authTimeout), conn.Close
}
func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServiceClient, c config, logger logger.Logger) users.Service {
database := postgres.NewDatabase(db)
func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServiceClient, c config, ec email.Config, logger logger.Logger) users.Service {
database := usersPg.NewDatabase(db)
hasher := bcrypt.New()
userRepo := tracing.UserRepositoryMiddleware(postgres.NewUserRepo(database), tracer)
userRepo := tracing.UserRepositoryMiddleware(usersPg.NewUserRepo(database), tracer)
emailer, err := emailer.New(c.resetURL, &c.emailConf)
emailer, err := emailer.New(c.ResetURL, &ec)
if err != nil {
logger.Error(fmt.Sprintf("Failed to configure e-mailing util: %s", err.Error()))
}
idProvider := uuid.New()
svc := users.New(userRepo, hasher, auth, emailer, idProvider, c.passRegex)
svc := users.New(userRepo, hasher, auth, emailer, idProvider, c.PassRegex)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "users",
Subsystem: "api",
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "users",
Subsystem: "api",
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics(svcName, "api")
svc = api.MetricsMiddleware(svc, counter, latency)
if err := createAdmin(svc, userRepo, c, auth); err != nil {
logger.Error("failed to create admin user: " + err.Error())
os.Exit(1)
log.Fatalf("failed to create admin user: " + err.Error())
}
switch c.selfRegister {
switch c.SelfRegister {
case true:
// If MF_USERS_ALLOW_SELF_REGISTER environment variable is "true",
// everybody can create a new user. Here, check the existence of that
@@ -343,12 +156,10 @@ func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServic
// Add a policy that allows anybody to create a user
apr, err := auth.AddPolicy(context.Background(), &mainflux.AddPolicyReq{Obj: "user", Act: "create", Sub: "*"})
if err != nil {
logger.Error("failed to add the policy related to MF_USERS_ALLOW_SELF_REGISTER: " + err.Error())
os.Exit(1)
log.Fatalf("failed to add the policy related to MF_USERS_ALLOW_SELF_REGISTER: " + err.Error())
}
if !apr.GetAuthorized() {
logger.Error("failed to authorized the policy result related to MF_USERS_ALLOW_SELF_REGISTER: " + errors.ErrAuthorization.Error())
os.Exit(1)
log.Fatalf("failed to authorized the policy result related to MF_USERS_ALLOW_SELF_REGISTER: " + errors.ErrAuthorization.Error())
}
}
default:
@@ -357,12 +168,10 @@ func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServic
// allows everybody to create a new user.
dpr, err := auth.DeletePolicy(context.Background(), &mainflux.DeletePolicyReq{Obj: "user", Act: "create", Sub: "*"})
if err != nil {
logger.Error("failed to delete a policy: " + err.Error())
os.Exit(1)
log.Fatalf("failed to delete a policy: " + err.Error())
}
if !dpr.GetDeleted() {
logger.Error("deleting a policy expected to succeed.")
os.Exit(1)
log.Fatalf("deleting a policy expected to succeed.")
}
}
@@ -371,8 +180,8 @@ func newService(db *sqlx.DB, tracer opentracing.Tracer, auth mainflux.AuthServic
func createAdmin(svc users.Service, userRepo users.UserRepository, c config, auth mainflux.AuthServiceClient) error {
user := users.User{
Email: c.adminEmail,
Password: c.adminPassword,
Email: c.AdminEmail,
Password: c.AdminPassword,
}
if admin, err := userRepo.RetrieveByEmail(context.Background(), user.Email); err == nil {
@@ -415,37 +224,3 @@ func createAdmin(svc users.Service, userRepo users.UserRepository, c config, aut
return nil
}
func startHTTPServer(ctx context.Context, tracer opentracing.Tracer, svc users.Service, port string, certFile string, keyFile string, logger logger.Logger) error {
p := fmt.Sprintf(":%s", port)
errCh := make(chan error)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, tracer, logger)}
switch {
case certFile != "" || keyFile != "":
logger.Info(fmt.Sprintf("Users service started using https, cert %s key %s, exposed port %s", certFile, keyFile, port))
go func() {
errCh <- server.ListenAndServeTLS(certFile, keyFile)
}()
default:
logger.Info(fmt.Sprintf("Users service started using http, exposed port %s", port))
go func() {
errCh <- server.ListenAndServe()
}()
}
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
logger.Error(fmt.Sprintf("Users service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("users service occurred during shutdown at %s: %w", p, err)
}
logger.Info(fmt.Sprintf("Users service shutdown of http at %s", p))
return nil
case err := <-errCh:
return err
}
}
+38 -175
View File
@@ -6,105 +6,79 @@ package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"time"
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
"github.com/mainflux/mainflux"
"golang.org/x/sync/errgroup"
"github.com/mainflux/mainflux/internal"
thingsClient "github.com/mainflux/mainflux/internal/clients/grpc/things"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/internal/server"
httpserver "github.com/mainflux/mainflux/internal/server/http"
logger "github.com/mainflux/mainflux/logger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/messaging"
"github.com/mainflux/mainflux/pkg/messaging/brokers"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
adapter "github.com/mainflux/mainflux/ws"
"github.com/mainflux/mainflux/ws/api"
opentracing "github.com/opentracing/opentracing-go"
stdprometheus "github.com/prometheus/client_golang/prometheus"
jconfig "github.com/uber/jaeger-client-go/config"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
const (
stopWaitTime = 5 * time.Second
defPort = "8190"
defBrokerURL = "nats://localhost:4222"
defLogLevel = "error"
defClientTLS = "false"
defCACerts = ""
defJaegerURL = ""
defThingsAuthURL = "localhost:8183"
defThingsAuthTimeout = "1s"
envPort = "MF_WS_ADAPTER_PORT"
envBrokerURL = "MF_BROKER_URL"
envLogLevel = "MF_WS_ADAPTER_LOG_LEVEL"
envClientTLS = "MF_WS_ADAPTER_CLIENT_TLS"
envCACerts = "MF_WS_ADAPTER_CA_CERTS"
envJaegerURL = "MF_JAEGER_URL"
envThingsAuthURL = "MF_THINGS_AUTH_GRPC_URL"
envThingsTimeout = "MF_THINGS_AUTH_GRPC_TIMEOUT"
svcName = "ws-adapter"
envPrefix = "MF_WS_ADAPTER_"
envPrefixHttp = "MF_WS_ADAPTER_HTTP_"
defSvcHttpPort = "8190"
)
type config struct {
port string
brokerURL string
logLevel string
clientTLS bool
caCerts string
jaegerURL string
thingsAuthURL string
thingsAuthTimeout time.Duration
LogLevel string `env:"MF_WS_ADAPTER_LOG_LEVEL" envDefault:"info"`
BrokerURL string `env:"MF_BROKER_URL" envDefault:"nats://localhost:4222"`
JaegerURL string `env:"MF_JAEGER_URL" envDefault:"localhost:6831"`
}
func main() {
cfg := loadConfig()
ctx, cancel := context.WithCancel(context.Background())
g, ctx := errgroup.WithContext(ctx)
logger, err := logger.New(os.Stdout, cfg.logLevel)
cfg := config{}
if err := env.Parse(&cfg); err != nil {
log.Fatalf("failed to load %s service configuration : %s", svcName, err.Error())
}
logger, err := logger.New(os.Stdout, cfg.LogLevel)
if err != nil {
log.Fatalf(err.Error())
}
conn := connectToThings(cfg, logger)
defer conn.Close()
thingsTracer, thingsCloser := initJaeger("things", cfg.jaegerURL, logger)
defer thingsCloser.Close()
tc := thingsapi.NewClient(conn, thingsTracer, cfg.thingsAuthTimeout)
nps, err := brokers.NewPubSub(cfg.brokerURL, "", logger)
tc, tcHandler, err := thingsClient.Setup(envPrefix, cfg.JaegerURL)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to message broker: %s", err))
os.Exit(1)
log.Fatal(err.Error())
}
defer internal.Close(logger, tcHandler)
logger.Info("Successfully connected to things grpc server " + tcHandler.Secure())
nps, err := brokers.NewPubSub(cfg.BrokerURL, "", logger)
if err != nil {
log.Fatalf("Failed to connect to message broker: %s", err.Error())
}
defer nps.Close()
svc := newService(tc, nps, logger)
httpServerConfig := server.Config{Port: defSvcHttpPort}
if err := env.Parse(&httpServerConfig, env.Options{Prefix: envPrefixHttp, AltPrefix: envPrefix}); err != nil {
log.Fatalf("failed to load %s HTTP server configuration : %s", svcName, err.Error())
}
hs := httpserver.New(ctx, cancel, svcName, httpServerConfig, api.MakeHandler(svc, logger), logger)
g.Go(func() error {
return startWSServer(ctx, cfg, svc, logger)
return hs.Start()
})
g.Go(func() error {
if sig := errors.SignalHandler(ctx); sig != nil {
cancel()
logger.Info(fmt.Sprintf("WS adapter service shutdown by signal: %s", sig))
}
return nil
return server.StopSignalHandler(ctx, cancel, logger, svcName, hs)
})
if err := g.Wait(); err != nil {
@@ -112,121 +86,10 @@ func main() {
}
}
func loadConfig() config {
tls, err := strconv.ParseBool(mainflux.Env(envClientTLS, defClientTLS))
if err != nil {
log.Fatalf("Invalid value passed for %s\n", envClientTLS)
}
authTimeout, err := time.ParseDuration(mainflux.Env(envThingsTimeout, defThingsAuthTimeout))
if err != nil {
log.Fatalf("Invalid %s value: %s", envThingsTimeout, err.Error())
}
return config{
brokerURL: mainflux.Env(envBrokerURL, defBrokerURL),
port: mainflux.Env(envPort, defPort),
logLevel: mainflux.Env(envLogLevel, defLogLevel),
clientTLS: tls,
caCerts: mainflux.Env(envCACerts, defCACerts),
jaegerURL: mainflux.Env(envJaegerURL, defJaegerURL),
thingsAuthURL: mainflux.Env(envThingsAuthURL, defThingsAuthURL),
thingsAuthTimeout: authTimeout,
}
}
func connectToThings(cfg config, logger logger.Logger) *grpc.ClientConn {
var opts []grpc.DialOption
if cfg.clientTLS {
if cfg.caCerts != "" {
tpc, err := credentials.NewClientTLSFromFile(cfg.caCerts, "")
if err != nil {
logger.Error(fmt.Sprintf("Failed to load certs: %s", err))
os.Exit(1)
}
opts = append(opts, grpc.WithTransportCredentials(tpc))
}
} else {
logger.Info("gRPC communication is not encrypted")
opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}
conn, err := grpc.Dial(cfg.thingsAuthURL, opts...)
if err != nil {
logger.Error(fmt.Sprintf("Failed to connect to things service: %s", err))
os.Exit(1)
}
return conn
}
func initJaeger(svcName, url string, logger logger.Logger) (opentracing.Tracer, io.Closer) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil)
}
tracer, closer, err := jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
if err != nil {
logger.Error(fmt.Sprintf("Failed to init Jaeger client: %s", err))
os.Exit(1)
}
return tracer, closer
}
func newService(tc mainflux.ThingsServiceClient, nps messaging.PubSub, logger logger.Logger) adapter.Service {
svc := adapter.New(tc, nps)
svc = api.LoggingMiddleware(svc, logger)
svc = api.MetricsMiddleware(
svc,
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: "ws_adapter",
Subsystem: "api",
Name: "reqeust_count",
Help: "Number of requests received",
}, []string{"method"}),
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: "ws_adapter",
Subsystem: "api",
Name: "request_latency_microsecond",
Help: "Total duration of requests in microseconds",
}, []string{"method"}),
)
counter, latency := internal.MakeMetrics("ws_adapter", "api")
svc = api.MetricsMiddleware(svc, counter, latency)
return svc
}
func startWSServer(ctx context.Context, cfg config, svc adapter.Service, l logger.Logger) error {
p := fmt.Sprintf(":%s", cfg.port)
errCh := make(chan error, 2)
server := &http.Server{Addr: p, Handler: api.MakeHandler(svc, l)}
l.Info(fmt.Sprintf("WS adapter service started, exposed port %s", cfg.port))
go func() {
errCh <- server.ListenAndServe()
}()
select {
case <-ctx.Done():
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := server.Shutdown(ctxShutdown); err != nil {
l.Error(fmt.Sprintf("WS adapter service error occurred during shutdown at %s: %s", p, err))
return fmt.Errorf("WS adapter service error occurred during shutdown at %s: %w", p, err)
}
l.Info(fmt.Sprintf("WS adapter service shutdown at %s", p))
return nil
case err := <-errCh:
return err
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ default values.
|--------------------------------|--------------------------------------------------------|-----------------------|
| MF_COAP_ADAPTER_PORT | Service listening port | 5683 |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_COAP_ADAPTER_LOG_LEVEL | Service log level | error |
| MF_COAP_ADAPTER_LOG_LEVEL | Service log level | info |
| MF_COAP_ADAPTER_CLIENT_TLS | Flag that indicates if TLS should be turned on | false |
| MF_COAP_ADAPTER_CA_CERTS | Path to trusted CAs in PEM format | |
| MF_COAP_ADAPTER_PING_PERIOD | Hours between 1 and 24 to ping client with ACK message | 12 |
+3 -43
View File
@@ -3,47 +3,10 @@
package postgres
import (
"fmt"
import migrate "github.com/rubenv/sql-migrate"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
// Config defines the options that are used when connecting to a PostgreSQL instance
type Config struct {
Host string
Port string
User string
Pass string
Name string
SSLMode string
SSLCert string
SSLKey string
SSLRootCert string
}
// Connect creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate
// failure.
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, err
}
if err := migrateDB(db); err != nil {
return nil, err
}
return db, nil
}
func migrateDB(db *sqlx.DB) error {
migrations := &migrate.MemoryMigrationSource{
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "subscriptions_1",
@@ -62,7 +25,4 @@ func migrateDB(db *sqlx.DB) error {
},
},
}
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
return err
}
+3 -3
View File
@@ -11,9 +11,9 @@ import (
"os"
"testing"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/consumers/notifiers/postgres"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/pkg/ulid"
dockertest "github.com/ory/dockertest/v3"
)
@@ -52,7 +52,7 @@ func TestMain(m *testing.M) {
log.Fatalf("Could not connect to docker: %s", err)
}
dbConfig := postgres.Config{
dbConfig := pgClient.Config{
Host: "localhost",
Port: port,
User: "test",
@@ -64,7 +64,7 @@ func TestMain(m *testing.M) {
SSLRootCert: "",
}
if db, err = postgres.Connect(dbConfig); err != nil {
if db, err = pgClient.SetupDB(dbConfig, *postgres.Migration()); err != nil {
log.Fatalf("Could not setup test DB connection: %s", err)
}
+1 -1
View File
@@ -10,7 +10,7 @@ default values.
| Variable | Description | Default |
| ------------------------------------| --------------------------------------------------------------- ----- | --------------------- |
| MF_SMPP_NOTIFIER_LOG_LEVEL | Log level for SMPP Notifier (debug, info, warn, error) | error |
| MF_SMPP_NOTIFIER_LOG_LEVEL | Log level for SMPP Notifier (debug, info, warn, error) | info |
| MF_SMPP_NOTIFIER_DB_HOST | Database host address | localhost |
| MF_SMPP_NOTIFIER_DB_PORT | Database host port | 5432 |
| MF_SMPP_NOTIFIER_DB_USER | Database user | mainflux |
+8 -8
View File
@@ -9,13 +9,13 @@ import (
// Config represents SMPP transmitter configuration.
type Config struct {
Address string
Username string
Password string
SystemType string
SourceAddrTON uint8
SourceAddrNPI uint8
DestAddrTON uint8
DestAddrNPI uint8
Address string `env:"MF_SMPP_ADDRESS" envDefault:""`
Username string `env:"MF_SMPP_USERNAME" envDefault:""`
Password string `env:"MF_SMPP_PASSWORD" envDefault:""`
SystemType string `env:"MF_SMPP_SYSTEM_TYPE" envDefault:""`
SourceAddrTON uint8 `env:"MF_SMPP_SRC_ADDR_TON" envDefault:"0"`
SourceAddrNPI uint8 `env:"MF_SMPP_DST_ADDR_TON" envDefault:"0"`
DestAddrTON uint8 `env:"MF_SMPP_SRC_ADDR_NPI" envDefault:"0"`
DestAddrNPI uint8 `env:"MF_SMPP_DST_ADDR_NPI" envDefault:"0"`
TLS *tls.Config
}
+1 -1
View File
@@ -10,7 +10,7 @@ default values.
| Variable | Description | Default |
| --------------------------------- | ----------------------------------------------------------------------- | --------------------- |
| MF_SMTP_NOTIFIER_LOG_LEVEL | Log level for SMT Notifier (debug, info, warn, error) | error |
| MF_SMTP_NOTIFIER_LOG_LEVEL | Log level for SMT Notifier (debug, info, warn, error) | info |
| MF_SMTP_NOTIFIER_DB_HOST | Database host address | localhost |
| MF_SMTP_NOTIFIER_DB_PORT | Database host port | 5432 |
| MF_SMTP_NOTIFIER_DB_USER | Database user | mainflux |
+1 -1
View File
@@ -11,7 +11,7 @@ default values.
| Variable | Description | Default |
| -------------------------------- | ----------------------------------------------------------------------- | --------------------- |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_CASSANDRA_WRITER_LOG_LEVEL | Log level for Cassandra writer (debug, info, warn, error) | error |
| MF_CASSANDRA_WRITER_LOG_LEVEL | Log level for Cassandra writer (debug, info, warn, error) | info |
| MF_CASSANDRA_WRITER_PORT | Service HTTP port | 8180 |
| MF_CASSANDRA_WRITER_DB_CLUSTER | Cassandra cluster comma separated addresses | 127.0.0.1 |
| MF_CASSANDRA_WRITER_DB_KEYSPACE | Cassandra keyspace name | mainflux |
+5 -2
View File
@@ -10,6 +10,7 @@ import (
"github.com/gofrs/uuid"
"github.com/mainflux/mainflux/consumers/writers/cassandra"
casClient "github.com/mainflux/mainflux/internal/clients/cassandra"
"github.com/mainflux/mainflux/pkg/transformers/json"
"github.com/mainflux/mainflux/pkg/transformers/senml"
"github.com/stretchr/testify/assert"
@@ -34,11 +35,13 @@ var (
)
func TestSaveSenml(t *testing.T) {
session, err := cassandra.Connect(cassandra.DBConfig{
session, err := casClient.Connect(casClient.Config{
Hosts: []string{addr},
Keyspace: keyspace,
})
require.Nil(t, err, fmt.Sprintf("failed to connect to Cassandra: %s", err))
err = casClient.InitDB(session, cassandra.Table)
require.Nil(t, err, fmt.Sprintf("failed to initialize to Cassandra: %s", err))
repo := cassandra.New(session)
now := time.Now().Unix()
msg := senml.Message{
@@ -74,7 +77,7 @@ func TestSaveSenml(t *testing.T) {
}
func TestSaveJSON(t *testing.T) {
session, err := cassandra.Connect(cassandra.DBConfig{
session, err := casClient.Connect(casClient.Config{
Hosts: []string{addr},
Keyspace: keyspace,
})
+2 -35
View File
@@ -3,10 +3,9 @@
package cassandra
import "github.com/gocql/gocql"
const (
table = `CREATE TABLE IF NOT EXISTS messages (
// Table contains query for default table created in cassandra db
Table = `CREATE TABLE IF NOT EXISTS messages (
id uuid,
channel text,
subtopic text,
@@ -35,35 +34,3 @@ const (
PRIMARY KEY (channel, created, id)
) WITH CLUSTERING ORDER BY (created DESC)`
)
// DBConfig contains Cassandra DB specific parameters.
type DBConfig struct {
Hosts []string
Keyspace string
User string
Pass string
Port int
}
// Connect establishes connection to the Cassandra cluster.
func Connect(cfg DBConfig) (*gocql.Session, error) {
cluster := gocql.NewCluster(cfg.Hosts...)
cluster.Keyspace = cfg.Keyspace
cluster.Consistency = gocql.Quorum
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: cfg.User,
Password: cfg.Pass,
}
cluster.Port = cfg.Port
session, err := cluster.CreateSession()
if err != nil {
return nil, err
}
if err := session.Query(table).Exec(); err != nil {
return nil, err
}
return session, nil
}
+2 -2
View File
@@ -9,7 +9,7 @@ import (
"testing"
"github.com/gocql/gocql"
"github.com/mainflux/mainflux/consumers/writers/cassandra"
casClient "github.com/mainflux/mainflux/internal/clients/cassandra"
log "github.com/mainflux/mainflux/logger"
dockertest "github.com/ory/dockertest/v3"
)
@@ -35,7 +35,7 @@ func TestMain(m *testing.M) {
return err
}
session, err := cassandra.Connect(cassandra.DBConfig{
session, err := casClient.Connect(casClient.Config{
Hosts: []string{addr},
Keyspace: keyspace,
})
+1 -1
View File
@@ -11,7 +11,7 @@ default values.
| Variable | Description | Default |
| ----------------------------- | --------------------------------------------------------------------------------- | ---------------------- |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_INFLUX_WRITER_LOG_LEVEL | Log level for InfluxDB writer (debug, info, warn, error) | error |
| MF_INFLUX_WRITER_LOG_LEVEL | Log level for InfluxDB writer (debug, info, warn, error) | info |
| MF_INFLUX_WRITER_PORT | Service HTTP port | 8180 |
| MF_INFLUX_WRITER_DB_HOST | InfluxDB host | localhost |
| MF_INFLUXDB_PORT | Default port of InfluxDB database | 8086 |
+1 -1
View File
@@ -11,7 +11,7 @@ default values.
| Variable | Description | Default |
| ---------------------------- | --------------------------------------------------------------------------------- | ---------------------- |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_MONGO_WRITER_LOG_LEVEL | Log level for MongoDB writer | error |
| MF_MONGO_WRITER_LOG_LEVEL | Log level for MongoDB writer | info |
| MF_MONGO_WRITER_PORT | Service HTTP port | 8180 |
| MF_MONGO_WRITER_DB | Default MongoDB database name | messages |
| MF_MONGO_WRITER_DB_HOST | Default MongoDB database host | localhost |
+2 -2
View File
@@ -11,8 +11,8 @@ default values.
| Variable | Description | Default |
| ----------------------------------- | --------------------------------------------------------------------------------- | ---------------------- |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_POSTGRES_WRITER_LOG_LEVEL | Service log level | error |
| MF_POSTGRES_WRITER_PORT | Service HTTP port | 9104 |
| MF_POSTGRES_WRITER_LOG_LEVEL | Service log level | info |
| MF_POSTGRES_WRITER_PORT | Service HTTP port | 8180 |
| MF_POSTGRES_WRITER_DB_HOST | Postgres DB host | postgres |
| MF_POSTGRES_WRITER_DB_PORT | Postgres DB port | 5432 |
| MF_POSTGRES_WRITER_DB_USER | Postgres user | mainflux |
+4 -43
View File
@@ -3,47 +3,11 @@
package postgres
import (
"fmt"
import migrate "github.com/rubenv/sql-migrate"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
// Config defines the options that are used when connecting to a PostgreSQL instance
type Config struct {
Host string
Port string
User string
Pass string
Name string
SSLMode string
SSLCert string
SSLKey string
SSLRootCert string
}
// Connect creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate
// failure.
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, err
}
if err := migrateDB(db); err != nil {
return nil, err
}
return db, nil
}
func migrateDB(db *sqlx.DB) error {
migrations := &migrate.MemoryMigrationSource{
// Migration of postgres-writer
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "messages_1",
@@ -72,7 +36,4 @@ func migrateDB(db *sqlx.DB) error {
},
},
}
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
return err
}
+3 -3
View File
@@ -11,9 +11,9 @@ import (
"os"
"testing"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/consumers/writers/postgres"
pgclient "github.com/mainflux/mainflux/internal/clients/postgres"
dockertest "github.com/ory/dockertest/v3"
)
@@ -48,7 +48,7 @@ func TestMain(m *testing.M) {
log.Fatalf("Could not connect to docker: %s", err)
}
dbConfig := postgres.Config{
dbConfig := pgclient.Config{
Host: "localhost",
Port: port,
User: "test",
@@ -60,7 +60,7 @@ func TestMain(m *testing.M) {
SSLRootCert: "",
}
db, err = postgres.Connect(dbConfig)
db, err = pgclient.SetupDB(dbConfig, *postgres.Migration())
if err != nil {
log.Fatalf("Could not setup test DB connection: %s", err)
}
+2 -2
View File
@@ -11,8 +11,8 @@ default values.
| Variable | Description | Default |
| ----------------------------------- | --------------------------------------------------------- | ---------------------- |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_TIMESCALE_WRITER_LOG_LEVEL | Service log level | error |
| MF_TIMESCALE_WRITER_PORT | Service HTTP port | 9104 |
| MF_TIMESCALE_WRITER_LOG_LEVEL | Service log level | info |
| MF_TIMESCALE_WRITER_PORT | Service HTTP port | 8180 |
| MF_TIMESCALE_WRITER_DB_HOST | Timescale DB host | timescale |
| MF_TIMESCALE_WRITER_DB_PORT | Timescale DB port | 5432 |
| MF_TIMESCALE_WRITER_DB_USER | Timescale user | mainflux |
+4 -43
View File
@@ -3,47 +3,11 @@
package timescale
import (
"fmt"
import migrate "github.com/rubenv/sql-migrate"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
// Config defines the options that are used when connecting to a TimescaleSQL instance
type Config struct {
Host string
Port string
User string
Pass string
Name string
SSLMode string
SSLCert string
SSLKey string
SSLRootCert string
}
// Connect creates a connection to the TimescaleSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate
// failure.
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, err
}
if err := migrateDB(db); err != nil {
return nil, err
}
return db, nil
}
func migrateDB(db *sqlx.DB) error {
migrations := &migrate.MemoryMigrationSource{
// Migration of timescale-writer
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "messages_1",
@@ -72,7 +36,4 @@ func migrateDB(db *sqlx.DB) error {
},
},
}
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
return err
}
+3 -3
View File
@@ -11,10 +11,10 @@ import (
"os"
"testing"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/consumers/writers/timescale"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
dockertest "github.com/ory/dockertest/v3"
)
@@ -49,7 +49,7 @@ func TestMain(m *testing.M) {
log.Fatalf("Could not connect to docker: %s", err)
}
dbConfig := timescale.Config{
dbConfig := pgClient.Config{
Host: "localhost",
Port: port,
User: "test",
@@ -61,7 +61,7 @@ func TestMain(m *testing.M) {
SSLRootCert: "",
}
db, err = timescale.Connect(dbConfig)
db, err = pgClient.SetupDB(dbConfig, *timescale.Migration())
if err != nil {
log.Fatalf("Could not setup test DB connection: %s", err)
}
+1 -1
View File
@@ -124,7 +124,7 @@ MF_COAP_ADAPTER_PORT=5683
### WS
MF_WS_ADAPTER_LOG_LEVEL=debug
MF_WS_ADAPTER_PORT=8190
MF_WS_ADAPTER_PORT=8186
## Addons Services
### Bootstrap
+1
View File
@@ -3,6 +3,7 @@ module github.com/mainflux/mainflux
go 1.19
require (
github.com/caarlos0/env/v7 v7.0.0
github.com/cenkalti/backoff/v4 v4.1.3
github.com/docker/docker v20.10.21+incompatible
github.com/eclipse/paho.mqtt.golang v1.4.2
+2
View File
@@ -77,6 +77,8 @@ github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCS
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/caarlos0/env/v7 v7.0.0 h1:cyczlTd/zREwSr9ch/mwaDl7Hse7kJuUY8hvHfXu5WI=
github.com/caarlos0/env/v7 v7.0.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
+1 -1
View File
@@ -10,7 +10,7 @@ default values.
| Variable | Description | Default |
| --------------------------- | ------------------------------------------------------------- | --------------------- |
| MF_HTTP_ADAPTER_LOG_LEVEL | Log level for the HTTP Adapter | error |
| MF_HTTP_ADAPTER_LOG_LEVEL | Log level for the HTTP Adapter | info |
| MF_HTTP_ADAPTER_PORT | Service HTTP port | 8180 |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_HTTP_ADAPTER_CLIENT_TLS | Flag that indicates if TLS should be turned on | false |
+72
View File
@@ -0,0 +1,72 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package cassandra
import (
"github.com/gocql/gocql"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/pkg/errors"
)
var (
errConfig = errors.New("failed to load Cassandra configuration")
errConnect = errors.New("failed to connect to Cassandra database")
errInit = errors.New("failed to execute initialization query in Cassandra ")
)
// Config contains Cassandra DB specific parameters
type Config struct {
Hosts []string `env:"DB_CLUSTER" envDefault:"127.0.0.1" envSeparator:","`
Keyspace string `env:"DB_KEYSPACE" envDefault:"mainflux"`
User string `env:"DB_USER" envDefault:""`
Pass string `env:"DB_PASS" envDefault:""`
Port int `env:"DB_PORT" envDefault:"9042"`
}
// Setup load configuration from environment and creates new cassandra connection
func Setup(envPrefix string) (*gocql.Session, error) {
return SetupDB(envPrefix, "")
}
// SetupDB load configuration from environment,
// creates new cassandra connection and executes
// the initial query in database.
func SetupDB(envPrefix string, initQuery string) (*gocql.Session, error) {
config := Config{}
if err := env.Parse(&config, env.Options{Prefix: envPrefix}); err != nil {
return nil, errors.Wrap(errConfig, err)
}
cs, err := Connect(config)
if err != nil {
return nil, err
}
if initQuery != "" {
if err := InitDB(cs, initQuery); err != nil {
return nil, errors.Wrap(errInit, err)
}
}
return cs, nil
}
// Connect establishes connection to the Cassandra cluster.
func Connect(cfg Config) (*gocql.Session, error) {
cluster := gocql.NewCluster(cfg.Hosts...)
cluster.Keyspace = cfg.Keyspace
cluster.Consistency = gocql.Quorum
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: cfg.User,
Password: cfg.Pass,
}
cluster.Port = cfg.Port
cassSess, err := cluster.CreateSession()
if err != nil {
return nil, errors.Wrap(errConnect, err)
}
return cassSess, nil
}
func InitDB(cs *gocql.Session, query string) error {
return cs.Query(query).Exec()
}
+28
View File
@@ -0,0 +1,28 @@
package auth
import (
"github.com/mainflux/mainflux"
authapi "github.com/mainflux/mainflux/auth/api/grpc"
grpcClient "github.com/mainflux/mainflux/internal/clients/grpc"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/pkg/errors"
)
const envAuthGrpcPrefix = "MF_AUTH_GRPC_"
var errGrpcConfig = errors.New("failed to load grpc configuration")
// Setup loads Auth gRPC configuration from environment variable and creates new Auth gRPC API
func Setup(envPrefix, jaegerURL string) (mainflux.AuthServiceClient, grpcClient.ClientHandler, error) {
config := grpcClient.Config{}
if err := env.Parse(&config, env.Options{Prefix: envAuthGrpcPrefix, AltPrefix: envPrefix}); err != nil {
return nil, nil, errors.Wrap(errGrpcConfig, err)
}
c, ch, err := grpcClient.Setup(config, "auth", jaegerURL)
if err != nil {
return nil, nil, err
}
return authapi.NewClient(c.Tracer, c.ClientConn, config.Timeout), ch, nil
}
+119
View File
@@ -0,0 +1,119 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package grpc
import (
"io"
"time"
jaegerClient "github.com/mainflux/mainflux/internal/clients/jaeger"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/opentracing/opentracing-go"
gogrpc "google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
var (
errGrpcConnect = errors.New("failed to connect to grpc server")
errJaeger = errors.New("failed to initialize jaeger ")
errGrpcClose = errors.New("failed to close grpc connection")
errJaegerClose = errors.New("failed to close jaeger connection")
)
type Config struct {
ClientTLS bool `env:"CLIENT_TLS" envDefault:"false"`
CACerts string `env:"CA_CERTS" envDefault:""`
URL string `env:"URL" envDefault:""`
Timeout time.Duration `env:"TIMEOUT" envDefault:"1s"`
}
type ClientHandler interface {
Close() error
IsSecure() bool
Secure() string
}
type Client struct {
*gogrpc.ClientConn
opentracing.Tracer
io.Closer
secure bool
}
var _ ClientHandler = (*Client)(nil)
// NewClientHandler create new client handler for gRPC client
func NewClientHandler(c *Client) ClientHandler {
return c
}
// Connect creates new gRPC client and connect to gRPC server
func Connect(cfg Config) (*gogrpc.ClientConn, bool, error) {
var opts []gogrpc.DialOption
secure := false
tc := insecure.NewCredentials()
if cfg.ClientTLS && cfg.CACerts != "" {
var err error
tc, err = credentials.NewClientTLSFromFile(cfg.CACerts, "")
if err != nil {
return nil, secure, err
}
secure = true
}
opts = append(opts, gogrpc.WithTransportCredentials(tc))
conn, err := gogrpc.Dial(cfg.URL, opts...)
if err != nil {
return nil, secure, err
}
return conn, secure, nil
}
// Setup load gRPC configuration from environment variable, creates new gRPC client and connect to gRPC server
func Setup(config Config, svcName, jaegerURL string) (*Client, ClientHandler, error) {
secure := false
// connect to auth grpc server
grpcClient, secure, err := Connect(config)
if err != nil {
return nil, nil, errors.Wrap(errGrpcConnect, err)
}
// initialize auth tracer for auth grpc client
tracer, tracerCloser, err := jaegerClient.NewTracer(svcName, jaegerURL)
if err != nil {
grpcClient.Close()
return nil, nil, errors.Wrap(errJaeger, err)
}
c := &Client{grpcClient, tracer, tracerCloser, secure}
return c, NewClientHandler(c), nil
}
func (c *Client) Close() error {
var retErr error
err := c.ClientConn.Close()
if err != nil {
retErr = errors.Wrap(errGrpcClose, err)
}
err = c.Closer.Close()
if err != nil {
retErr = errors.Wrap(retErr, errors.Wrap(errJaegerClose, err))
}
return retErr
}
func (c *Client) IsSecure() bool {
return c.secure
}
func (c *Client) Secure() string {
if c.secure {
return "with TLS"
}
return "without TLS"
}
+28
View File
@@ -0,0 +1,28 @@
package things
import (
"github.com/mainflux/mainflux"
grpcClient "github.com/mainflux/mainflux/internal/clients/grpc"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/pkg/errors"
thingsapi "github.com/mainflux/mainflux/things/api/auth/grpc"
)
const envThingsAuthGrpcPrefix = "MF_THINGS_AUTH_GRPC_"
var errGrpcConfig = errors.New("failed to load grpc configuration")
// Setup loads Things gRPC configuration from environment variable and creates new Things gRPC API
func Setup(envPrefix, jaegerURL string) (mainflux.ThingsServiceClient, grpcClient.ClientHandler, error) {
config := grpcClient.Config{}
if err := env.Parse(&config, env.Options{Prefix: envThingsAuthGrpcPrefix, AltPrefix: envPrefix}); err != nil {
return nil, nil, errors.Wrap(errGrpcConfig, err)
}
c, ch, err := grpcClient.Setup(config, "things", jaegerURL)
if err != nil {
return nil, nil, err
}
return thingsapi.NewClient(c.ClientConn, c.Tracer, config.Timeout), ch, nil
}
+53
View File
@@ -0,0 +1,53 @@
package influxdb
import (
"fmt"
"time"
"github.com/influxdata/influxdb/client/v2"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/pkg/errors"
)
var (
errConnect = errors.New("failed to create InfluxDB client")
errConfig = errors.New("failed to load InfluxDB client configuration from environment variable")
)
type Config struct {
Protocol string `env:"PROTOCOL" envDefault:"http"`
Host string `env:"HOST" envDefault:"localhost"`
Port string `env:"PORT" envDefault:"8086"`
Username string `env:"ADMIN_USER" envDefault:"mainflux"`
Password string `env:"ADMIN_PASSWORD" envDefault:"mainflux"`
DbName string `env:"DB" envDefault:"mainflux"`
UserAgent string `env:"USER_AGENT" envDefault:"InfluxDBClient"`
Timeout time.Duration `env:"TIMEOUT"` // Influxdb client configuration by default there is no timeout duration , this field will not have fallback default timeout duration Reference: https://pkg.go.dev/github.com/influxdata/influxdb@v1.10.0/client/v2#HTTPConfig
InsecureSkipVerify bool `env:"INSECURE_SKIP_VERIFY" envDefault:"false"`
}
// Setup load configuration from environment variable, create InfluxDB client and connect to InfluxDB server
func Setup(envPrefix string) (client.HTTPClient, error) {
config := Config{}
if err := env.Parse(&config, env.Options{Prefix: envPrefix}); err != nil {
return nil, errors.Wrap(errConfig, err)
}
return Connect(config)
}
// Connect create InfluxDB client and connect to InfluxDB server
func Connect(config Config) (client.HTTPClient, error) {
address := fmt.Sprintf("%s://%s:%s", config.Protocol, config.Host, config.Port)
clientConfig := client.HTTPConfig{
Addr: address,
Username: config.Username,
Password: config.Password,
UserAgent: config.UserAgent,
Timeout: config.Timeout,
}
client, err := client.NewHTTPClient(clientConfig)
if err != nil {
return nil, errors.Wrap(errConnect, err)
}
return client, nil
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package jaeger
import (
"errors"
"io"
"io/ioutil"
"github.com/opentracing/opentracing-go"
jconfig "github.com/uber/jaeger-client-go/config"
)
var (
errNoUrl = errors.New("URL is empty")
errNoSvcName = errors.New("Service Name is empty")
)
// NewTracer initializes Jaeger
func NewTracer(svcName, url string) (opentracing.Tracer, io.Closer, error) {
if url == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil), errNoUrl
}
if svcName == "" {
return opentracing.NoopTracer{}, ioutil.NopCloser(nil), errNoSvcName
}
return jconfig.Configuration{
ServiceName: svcName,
Sampler: &jconfig.SamplerConfig{
Type: "const",
Param: 1,
},
Reporter: &jconfig.ReporterConfig{
LocalAgentHostPort: url,
LogSpans: true,
},
}.NewTracer()
}
+51
View File
@@ -0,0 +1,51 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package mongodb
import (
"context"
"fmt"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/pkg/errors"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var (
errConfig = errors.New("failed to load mongodb configuration")
errConnect = errors.New("failed to connect to mongodb server")
)
// Config defines the options that are used when connecting to a MongoDB instance
type Config struct {
Host string `env:"HOST" envDefault:"localhost"`
Port string `env:"PORT" envDefault:"27017"`
DB string `env:"DB" envDefault:"messages"`
}
// Connect creates a connection to the MongoDB instance
func Connect(cfg Config) (*mongo.Database, error) {
addr := fmt.Sprintf("mongodb://%s:%s", cfg.Host, cfg.Port)
client, err := mongo.Connect(context.Background(), options.Client().ApplyURI(addr))
if err != nil {
return nil, errors.Wrap(errConnect, err)
}
db := client.Database(cfg.DB)
return db, nil
}
// Setup load configuration from environment, create new MongoDB client and connect to MongoDB server
func Setup(envPrefix string) (*mongo.Database, error) {
cfg := Config{}
if err := env.Parse(&cfg, env.Options{Prefix: envPrefix}); err != nil {
return nil, errors.Wrap(errConfig, err)
}
db, err := Connect(cfg)
if err != nil {
return nil, err
}
return db, nil
}
+86
View File
@@ -0,0 +1,86 @@
package postgres
import (
"fmt"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/pkg/errors"
migrate "github.com/rubenv/sql-migrate"
)
var (
errConfig = errors.New("failed to load postgresql configuration")
errConnect = errors.New("failed to connect to postgresql server")
errMigration = errors.New("failed to apply migrations")
)
type Config struct {
Host string `env:"DB_HOST,notEmpty" envDefault:"localhost"`
Port string `env:"DB_PORT,notEmpty" envDefault:"5432"`
User string `env:"DB_USER,notEmpty" envDefault:"mainflux"`
Pass string `env:"DB_PASS,notEmpty" envDefault:"mainflux"`
Name string `env:"DB" envDefault:""`
SSLMode string `env:"DB_SSL_MODE,notEmpty" envDefault:"disable"`
SSLCert string `env:"DB_SSL_CERT" envDefault:""`
SSLKey string `env:"DB_SSL_KEY" envDefault:""`
SSLRootCert string `env:"DB_SSL_ROOT_CERT" envDefault:""`
}
// Setup creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate failure.
func Setup(prefix string, migrations migrate.MemoryMigrationSource) (*sqlx.DB, error) {
return SetupWithConfig(prefix, migrations, Config{})
}
// SetupWithConfig creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate failure.
func SetupWithConfig(prefix string, migrations migrate.MemoryMigrationSource, defConfig Config) (*sqlx.DB, error) {
cfg := defConfig
if err := env.Parse(&cfg, env.Options{Prefix: prefix}); err != nil {
return nil, errors.Wrap(errConfig, err)
}
return SetupDB(cfg, migrations)
}
// SetupDB creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate failure.
func SetupDB(cfg Config, migrations migrate.MemoryMigrationSource) (*sqlx.DB, error) {
db, err := Connect(cfg)
if err != nil {
return nil, err
}
if err := MigrateDB(db, migrations); err != nil {
return nil, err
}
return db, nil
}
// Connect creates a connection to the PostgreSQL instance
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, errors.Wrap(errConnect, err)
}
return db, nil
}
// MigrateDB applies any unapplied database migrations
func MigrateDB(db *sqlx.DB, migrations migrate.MemoryMigrationSource) error {
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
if err != nil {
return errors.Wrap(errMigration, err)
}
return nil
}
func (c *Config) LoadEnv(prefix string) error {
if err := env.Parse(c, env.Options{Prefix: prefix}); err != nil {
return errors.Wrap(errConfig, err)
}
return nil
}
+51
View File
@@ -0,0 +1,51 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package redis
import (
"strconv"
r "github.com/go-redis/redis/v8"
"github.com/mainflux/mainflux/internal/env"
"github.com/mainflux/mainflux/pkg/errors"
)
var (
errConfig = errors.New("failed to load redis client configuration")
errConnect = errors.New("failed to connect to redis server")
)
// Config of RedisDB
type Config struct {
URL string `env:"URL" envDefault:"localhost:6379"`
Pass string `env:"PASS" envDefault:""`
DB string `env:"DB" envDefault:"0"`
}
// Setup load configuration from environment, creates new RedisDB client and connect to RedisDB Server
func Setup(prefix string) (*r.Client, error) {
cfg := Config{}
if err := env.Parse(&cfg, env.Options{Prefix: prefix}); err != nil {
return nil, errors.Wrap(errConfig, err)
}
client, err := Connect(cfg)
if err != nil {
return nil, errors.Wrap(errConnect, err)
}
return client, nil
}
// Connect create new RedisDB client and connect to RedisDB server
func Connect(cfg Config) (*r.Client, error) {
db, err := strconv.Atoi(cfg.DB)
if err != nil {
return nil, err
}
return r.NewClient(&r.Options{
Addr: cfg.URL,
Password: cfg.Pass,
DB: db,
}), nil
}
+12
View File
@@ -0,0 +1,12 @@
package internal
import (
grpcClient "github.com/mainflux/mainflux/internal/clients/grpc"
logger "github.com/mainflux/mainflux/logger"
)
func Close(log logger.Logger, clientHandler grpcClient.ClientHandler) {
if err := clientHandler.Close(); err != nil {
log.Warn(err.Error())
}
}
+8 -8
View File
@@ -30,15 +30,15 @@ type email struct {
Footer string
}
// Config email agent configuration.
// Config email agent configuration
type Config struct {
Host string
Port string
Username string
Password string
FromAddress string
FromName string
Template string
Host string `env:"MF_EMAIL_HOST" envDefault:"localhost"`
Port string `env:"MF_EMAIL_PORT" envDefault:"25"`
Username string `env:"MF_EMAIL_USERNAME" envDefault:"root"`
Password string `env:"MF_EMAIL_PASSWORD" envDefault:""`
FromAddress string `env:"MF_EMAIL_FROM_ADDRESS" envDefault:""`
FromName string `env:"MF_EMAIL_FROM_NAME" envDefault:""`
Template string `env:"MF_EMAIL_TEMPLATE" envDefault:"email.tmpl"`
}
// Agent for mailing
+10
View File
@@ -0,0 +1,10 @@
package env
// NewConfig gets configuration from environment variable
func NewConfig[T any](opts ...Options) (T, error) {
var cfg T
if err := Parse(&cfg, opts...); err != nil {
return cfg, err
}
return cfg, nil
}
+117
View File
@@ -0,0 +1,117 @@
package env
import (
"github.com/caarlos0/env/v7"
"github.com/mainflux/mainflux/internal/clients/grpc"
"github.com/mainflux/mainflux/internal/server"
)
type Options struct {
// Environment keys and values that will be accessible for the service
Environment map[string]string
// TagName specifies another tagname to use rather than the default env
TagName string
// RequiredIfNoDef automatically sets all env as required if they do not declare 'envDefault'
RequiredIfNoDef bool
// OnSet allows to run a function when a value is set
OnSet env.OnSetFn
// Prefix define a prefix for each key
Prefix string
// AltPrefix define a alternate prefix for each key
AltPrefix string
}
func Parse(v interface{}, opts ...Options) error {
actOpt := []env.Options{}
altPrefix := ""
for _, opt := range opts {
actOpt = append(actOpt, env.Options{
Environment: opt.Environment,
TagName: opt.TagName,
RequiredIfNoDef: opt.RequiredIfNoDef,
OnSet: opt.OnSet,
Prefix: opt.Prefix,
})
if opt.AltPrefix != "" {
altPrefix = opt.AltPrefix
}
}
if altPrefix == "" {
return env.Parse(v, actOpt...)
}
switch cfg := v.(type) {
case *grpc.Config:
return parseGrpcConfig(cfg, altPrefix, actOpt...)
case *server.Config:
return parseServerConfig(cfg, altPrefix, actOpt...)
default:
return env.Parse(v, actOpt...)
}
}
func parseGrpcConfig(cfg *grpc.Config, altPrefix string, opts ...env.Options) error {
if err := env.Parse(cfg, opts...); err != nil {
return err
}
if !cfg.ClientTLS || cfg.CACerts == "" {
altOpts := []env.Options{}
for _, opt := range opts {
if opt.Prefix != "" {
opt.Prefix = altPrefix
}
altOpts = append(altOpts, opt)
}
altCfg := grpc.Config{}
if err := env.Parse(&altCfg, altOpts...); err != nil {
return err
}
if cfg.CACerts == "" && altCfg.CACerts != "" {
cfg.CACerts = altCfg.CACerts
}
if !cfg.ClientTLS && altCfg.ClientTLS {
cfg.ClientTLS = altCfg.ClientTLS
}
}
return nil
}
func parseServerConfig(cfg *server.Config, altPrefix string, opts ...env.Options) error {
copyConfig := cfg
if err := env.Parse(cfg, opts...); err != nil {
return err
}
if cfg.CertFile == "" || cfg.KeyFile == "" || cfg.Port == "" || cfg.Port == copyConfig.Port {
altOpts := []env.Options{}
for _, opt := range opts {
if opt.Prefix != "" {
opt.Prefix = altPrefix
}
altOpts = append(altOpts, opt)
}
altCfg := server.Config{}
if err := env.Parse(&altCfg, altOpts...); err != nil {
return err
}
if cfg.CertFile == "" && altCfg.CertFile != "" {
cfg.CertFile = altCfg.CertFile
}
if cfg.KeyFile == "" && altCfg.KeyFile != "" {
cfg.KeyFile = altCfg.KeyFile
}
if (cfg.Port == "" || cfg.Port == copyConfig.Port) && altCfg.Port != "" {
cfg.Port = altCfg.Port
}
}
return nil
}
+26
View File
@@ -0,0 +1,26 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package internal
import (
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
stdprometheus "github.com/prometheus/client_golang/prometheus"
)
func MakeMetrics(namespace, subsystem string) (*kitprometheus.Counter, *kitprometheus.Summary) {
counter := kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "request_count",
Help: "Number of requests received.",
}, []string{"method"})
latency := kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "request_latency_microseconds",
Help: "Total duration of requests in microseconds.",
}, []string{"method"})
return counter, latency
}
+66
View File
@@ -0,0 +1,66 @@
package coap
import (
"context"
"fmt"
"time"
"github.com/mainflux/mainflux/internal/server"
"github.com/mainflux/mainflux/logger"
gocoap "github.com/plgd-dev/go-coap/v2"
"github.com/plgd-dev/go-coap/v2/mux"
)
const (
stopWaitTime = 5 * time.Second
)
type Server struct {
server.BaseServer
handler mux.HandlerFunc
}
var _ server.Server = (*Server)(nil)
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, handler mux.HandlerFunc, logger logger.Logger) server.Server {
listenFullAddress := fmt.Sprintf("%s:%s", config.Host, config.Port)
return &Server{
BaseServer: server.BaseServer{
Ctx: ctx,
Cancel: cancel,
Name: name,
Address: listenFullAddress,
Config: config,
Logger: logger,
},
handler: handler,
}
}
func (s *Server) Start() error {
errCh := make(chan error)
s.Logger.Info(fmt.Sprintf("%s service started using http, exposed port %s", s.Name, s.Address))
go func() {
errCh <- gocoap.ListenAndServe("udp", s.Address, s.handler)
}()
select {
case <-s.Ctx.Done():
return s.Stop()
case err := <-errCh:
return err
}
}
func (s *Server) Stop() error {
defer s.Cancel()
c := make(chan bool)
defer close(c)
select {
case <-c:
case <-time.After(stopWaitTime):
}
s.Logger.Info(fmt.Sprintf("%s service shutdown of http at %s", s.Name, s.Address))
return nil
}
+94
View File
@@ -0,0 +1,94 @@
package grpc
import (
"context"
"fmt"
"net"
"time"
"github.com/mainflux/mainflux/internal/server"
"github.com/mainflux/mainflux/logger"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
const (
stopWaitTime = 5 * time.Second
)
type Server struct {
server.BaseServer
server *grpc.Server
registerService serviceRegister
}
type serviceRegister func(srv *grpc.Server)
var _ server.Server = (*Server)(nil)
func New(ctx context.Context, cancel context.CancelFunc, name string, config server.Config, registerService serviceRegister, logger logger.Logger) server.Server {
listenFullAddress := fmt.Sprintf("%s:%s", config.Host, config.Port)
return &Server{
BaseServer: server.BaseServer{
Ctx: ctx,
Cancel: cancel,
Name: name,
Address: listenFullAddress,
Config: config,
Logger: logger,
},
registerService: registerService,
}
}
func (s *Server) Start() error {
errCh := make(chan error)
listener, err := net.Listen("tcp", s.Address)
if err != nil {
return fmt.Errorf("failed to listen on port %s: %w", s.Address, err)
}
switch {
case s.Config.CertFile != "" || s.Config.KeyFile != "":
creds, err := credentials.NewServerTLSFromFile(s.Config.CertFile, s.Config.KeyFile)
if err != nil {
return fmt.Errorf("failed to load auth certificates: %w", err)
}
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s with TLS cert %s and key %s", s.Name, s.Address, s.Config.CertFile, s.Config.KeyFile))
s.server = grpc.NewServer(grpc.Creds(creds))
default:
s.Logger.Info(fmt.Sprintf("%s service gRPC server listening at %s without TLS", s.Name, s.Address))
s.server = grpc.NewServer()
}
s.registerService(s.server)
go func() {
errCh <- s.server.Serve(listener)
}()
select {
case <-s.Ctx.Done():
return s.Stop()
case err := <-errCh:
s.Cancel()
return err
}
}
func (s *Server) Stop() error {
defer s.Cancel()
c := make(chan bool)
go func() {
defer close(c)
s.server.GracefulStop()
}()
select {
case <-c:
case <-time.After(stopWaitTime):
}
s.Logger.Info(fmt.Sprintf("%s gRPC service shutdown at %s", s.Name, s.Address))
return nil
}
+76
View File
@@ -0,0 +1,76 @@
package http
import (
"context"
"fmt"
"net/http"
"time"
mfserver "github.com/mainflux/mainflux/internal/server"
"github.com/mainflux/mainflux/logger"
)
const (
stopWaitTime = 5 * time.Second
httpProtocol = "http"
httpsProtocol = "https"
)
type Server struct {
mfserver.BaseServer
server *http.Server
}
var _ mfserver.Server = (*Server)(nil)
func New(ctx context.Context, cancel context.CancelFunc, name string, config mfserver.Config, handler http.Handler, logger logger.Logger) mfserver.Server {
listenFullAddress := fmt.Sprintf("%s:%s", config.Host, config.Port)
server := &http.Server{Addr: listenFullAddress, Handler: handler}
return &Server{
BaseServer: mfserver.BaseServer{
Ctx: ctx,
Cancel: cancel,
Name: name,
Address: listenFullAddress,
Config: config,
Logger: logger,
},
server: server,
}
}
func (s *Server) Start() error {
errCh := make(chan error)
s.Protocol = httpProtocol
switch {
case s.Config.CertFile != "" || s.Config.KeyFile != "":
s.Protocol = httpsProtocol
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s with TLS cert %s and key %s", s.Name, s.Protocol, s.Address, s.Config.CertFile, s.Config.KeyFile))
go func() {
errCh <- s.server.ListenAndServeTLS(s.Config.CertFile, s.Config.KeyFile)
}()
default:
s.Logger.Info(fmt.Sprintf("%s service %s server listening at %s without TLS", s.Name, s.Protocol, s.Address))
go func() {
errCh <- s.server.ListenAndServe()
}()
}
select {
case <-s.Ctx.Done():
return s.Stop()
case err := <-errCh:
return err
}
}
func (s *Server) Stop() error {
defer s.Cancel()
ctxShutdown, cancelShutdown := context.WithTimeout(context.Background(), stopWaitTime)
defer cancelShutdown()
if err := s.server.Shutdown(ctxShutdown); err != nil {
s.Logger.Error(fmt.Sprintf("%s service %s server error occurred during shutdown at %s: %s", s.Name, s.Protocol, s.Address, err))
return fmt.Errorf("%s service %s server error occurred during shutdown at %s: %w", s.Name, s.Protocol, s.Address, err)
}
s.Logger.Info(fmt.Sprintf("%s %s service shutdown of http at %s", s.Name, s.Protocol, s.Address))
return nil
}
+66
View File
@@ -0,0 +1,66 @@
package server
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"github.com/mainflux/mainflux/logger"
)
type Server interface {
Start() error
Stop() error
}
type Config struct {
Host string `env:"HOST" envDefault:""`
Port string `env:"PORT" envDefault:""`
CertFile string `env:"SERVER_CERT" envDefault:""`
KeyFile string `env:"SERVER_KEY" envDefault:""`
}
type BaseServer struct {
Ctx context.Context
Cancel context.CancelFunc
Name string
Address string
Config Config
Logger logger.Logger
Protocol string
}
func stopAllServer(servers ...Server) error {
var err error
for _, server := range servers {
err1 := server.Stop()
if err1 != nil {
if err == nil {
err = fmt.Errorf("%w", err1)
} else {
err = fmt.Errorf("%v ; %w", err, err1)
}
}
}
return err
}
func StopSignalHandler(ctx context.Context, cancel context.CancelFunc, logger logger.Logger, svcName string, servers ...Server) error {
var err error
c := make(chan os.Signal, 2)
signal.Notify(c, syscall.SIGINT, syscall.SIGABRT)
select {
case sig := <-c:
defer cancel()
err = stopAllServer(servers...)
if err != nil {
logger.Error(fmt.Sprintf("%s service error during shutdown: %v", svcName, err))
}
logger.Info(fmt.Sprintf("%s service shutdown by signal: %s", svcName, sig))
return err
case <-ctx.Done():
return nil
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ default values.
| Variable | Description | Default |
|----------------------------------|---------------------------------------|---------------------------------|
| MF_LORA_ADAPTER_HTTP_PORT | Service HTTP port | 8180 |
| MF_LORA_ADAPTER_LOG_LEVEL | Service Log level | error |
| MF_LORA_ADAPTER_LOG_LEVEL | Service Log level | info |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_LORA_ADAPTER_MESSAGES_URL | LoRa adapter MQTT broker URL | tcp://localhost:1883 |
| MF_LORA_ADAPTER_MESSAGES_TOPIC | LoRa adapter MQTT subscriber Topic | application/+/device/+/event/up |
+1 -1
View File
@@ -12,7 +12,7 @@ default values.
| Variable | Description | Default |
|------------------------------------------|------------------------------------------------------------------|-----------------------|
| MF_MQTT_ADAPTER_LOG_LEVEL | mProxy Log level | error |
| MF_MQTT_ADAPTER_LOG_LEVEL | mProxy Log level | info |
| MF_MQTT_ADAPTER_MQTT_PORT | mProxy port | 1883 |
| MF_MQTT_ADAPTER_MQTT_TARGET_HOST | MQTT broker host | 0.0.0.0 |
| MF_MQTT_ADAPTER_MQTT_TARGET_PORT | MQTT broker port | 1883 |
+1 -1
View File
@@ -14,7 +14,7 @@ default values.
| Variable | Description | Default |
|----------------------------------|--------------------------------------------------|----------------------------|
| MF_OPCUA_ADAPTER_HTTP_PORT | Service HTTP port | 8180 |
| MF_OPCUA_ADAPTER_LOG_LEVEL | Service Log level | error |
| MF_OPCUA_ADAPTER_LOG_LEVEL | Service Log level | info |
| MF_BROKER_URL | Message broker instance URL | nats://localhost:4222 |
| MF_OPCUA_ADAPTER_INTERVAL_MS | OPC-UA Server Interval in milliseconds | 1000 |
| MF_OPCUA_ADAPTER_POLICY | OPC-UA Server Policy | |
+5 -5
View File
@@ -46,11 +46,11 @@ type Service interface {
type Config struct {
ServerURI string
NodeID string
Interval string
Policy string
Mode string
CertFile string
KeyFile string
Interval string `env:"MF_OPCUA_ADAPTER_INTERVAL_MS" envDefault:"1000"`
Policy string `env:"MF_OPCUA_ADAPTER_POLICY" envDefault:""`
Mode string `env:"MF_OPCUA_ADAPTER_MODE" envDefault:""`
CertFile string `env:"MF_OPCUA_ADAPTER_CERT_FILE" envDefault:""`
KeyFile string `env:"MF_OPCUA_ADAPTER_KEY_FILE" envDefault:""`
}
var _ Service = (*adapterService)(nil)
+1 -1
View File
@@ -32,7 +32,7 @@ type Node struct {
func Save(serverURI, nodeID string) error {
file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
if err != nil {
errors.Wrap(errWriteFile, err)
return errors.Wrap(errWriteFile, err)
}
csvWriter := csv.NewWriter(file)
csvWriter.Write([]string{serverURI, nodeID})
+1
View File
@@ -10,6 +10,7 @@ default values.
| Variable | Description | Default |
|---------------------------------|-----------------------------------------------------|----------------|
| MF_CASSANDRA_READER_LOG_LEVEL | Service log level | info |
| MF_CASSANDRA_READER_PORT | Service HTTP port | 8180 |
| MF_CASSANDRA_READER_DB_CLUSTER | Cassandra cluster comma separated addresses | 127.0.0.1 |
| MF_CASSANDRA_READER_DB_USER | Cassandra DB username | |
-31
View File
@@ -1,31 +0,0 @@
// Copyright (c) Mainflux
// SPDX-License-Identifier: Apache-2.0
package cassandra
import (
"github.com/gocql/gocql"
)
// DBConfig contains Cassandra DB specific parameters.
type DBConfig struct {
Hosts []string
Keyspace string
User string
Pass string
Port int
}
// Connect establishes connection to the Cassandra cluster.
func Connect(cfg DBConfig) (*gocql.Session, error) {
cluster := gocql.NewCluster(cfg.Hosts...)
cluster.Keyspace = cfg.Keyspace
cluster.Consistency = gocql.Quorum
cluster.Authenticator = gocql.PasswordAuthenticator{
Username: cfg.User,
Password: cfg.Pass,
}
cluster.Port = cfg.Port
return cluster.CreateSession()
}
+6 -2
View File
@@ -8,7 +8,9 @@ import (
"testing"
"time"
"github.com/mainflux/mainflux/consumers/writers/cassandra"
cwriter "github.com/mainflux/mainflux/consumers/writers/cassandra"
casClient "github.com/mainflux/mainflux/internal/clients/cassandra"
"github.com/mainflux/mainflux/pkg/transformers/json"
"github.com/mainflux/mainflux/pkg/transformers/senml"
"github.com/mainflux/mainflux/pkg/uuid"
@@ -45,12 +47,14 @@ var (
)
func TestReadSenml(t *testing.T) {
session, err := creader.Connect(creader.DBConfig{
session, err := casClient.Connect(casClient.Config{
Hosts: []string{addr},
Keyspace: keyspace,
})
require.Nil(t, err, fmt.Sprintf("failed to connect to Cassandra: %s", err))
defer session.Close()
err = casClient.InitDB(session, cassandra.Table)
require.Nil(t, err, fmt.Sprintf("failed to initialize to Cassandra: %s", err))
writer := cwriter.New(session)
chanID, err := idProvider.ID()
@@ -385,7 +389,7 @@ func TestReadSenml(t *testing.T) {
}
func TestReadJSON(t *testing.T) {
session, err := creader.Connect(creader.DBConfig{
session, err := casClient.Connect(casClient.Config{
Hosts: []string{addr},
Keyspace: keyspace,
})
+2 -2
View File
@@ -9,7 +9,7 @@ import (
"testing"
"github.com/gocql/gocql"
"github.com/mainflux/mainflux/consumers/writers/cassandra"
casClient "github.com/mainflux/mainflux/internal/clients/cassandra"
log "github.com/mainflux/mainflux/logger"
dockertest "github.com/ory/dockertest/v3"
)
@@ -35,7 +35,7 @@ func TestMain(m *testing.M) {
return err
}
session, err := cassandra.Connect(cassandra.DBConfig{
session, err := casClient.Connect(casClient.Config{
Hosts: []string{addr},
Keyspace: keyspace,
})
+1
View File
@@ -10,6 +10,7 @@ default values.
| Variable | Description | Default |
|------------------------------|-----------------------------------------------------|----------------|
| MF_INFLUX_READER_LOG_LEVEL | Service log level | info |
| MF_INFLUX_READER_PORT | Service HTTP port | 8180 |
| MF_INFLUXDB_HOST | InfluxDB host | localhost |
| MF_INFLUXDB_PORT | Default port of InfluxDB database | 8086 |
+1
View File
@@ -10,6 +10,7 @@ default values.
| Variable | Description | Default |
|-----------------------------|-----------------------------------------------------|----------------|
| MF_MONGO_READER_LOG_LEVEL | Service log level | info |
| MF_MONGO_READER_PORT | Service HTTP port | 8180 |
| MF_MONGO_READER_DB | MongoDB database name | messages |
| MF_MONGO_READER_DB_HOST | MongoDB database host | localhost |
+1 -1
View File
@@ -10,7 +10,7 @@ default values.
| Variable | Description | Default |
|-------------------------------------|----------------------------------------------|----------------|
| MF_POSTGRES_READER_LOG_LEVEL | Service log level | debug |
| MF_POSTGRES_READER_LOG_LEVEL | Service log level | info |
| MF_POSTGRES_READER_PORT | Service HTTP port | 8180 |
| MF_POSTGRES_READER_CLIENT_TLS | TLS mode flag | false |
| MF_POSTGRES_READER_CA_CERTS | Path to trusted CAs in PEM format | |
-1
View File
@@ -6,7 +6,6 @@ package postgres
import (
"fmt"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5/pgconn"
"github.com/jmoiron/sqlx" // required for DB access
"github.com/jmoiron/sqlx"
"github.com/mainflux/mainflux/pkg/errors"
"github.com/mainflux/mainflux/pkg/transformers/senml"
"github.com/mainflux/mainflux/readers"
+1 -1
View File
@@ -36,9 +36,9 @@ func TestMain(m *testing.M) {
}
port := container.GetPort("5432/tcp")
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
if err = pool.Retry(func() error {
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
db, err = sqlx.Open("pgx", url)
if err != nil {
return err
+2 -2
View File
@@ -10,11 +10,11 @@ default values.
| Variable | Description | Default |
|--------------------------------------|---------------------------------------------|----------------|
| MF_TIMESCALE_READER_LOG_LEVEL | Service log level | debug |
| MF_TIMESCALE_READER_LOG_LEVEL | Service log level | info |
| MF_TIMESCALE_READER_PORT | Service HTTP port | 8180 |
| MF_TIMESCALE_READER_CLIENT_TLS | TLS mode flag | false |
| MF_TIMESCALE_READER_CA_CERTS | Path to trusted CAs in PEM format | |
| MF_TIMESCALE_READER_DB_HOST | Timescale DB host | timescale |
| MF_TIMESCALE_READER_DB_HOST | Timescale DB host | localhost |
| MF_TIMESCALE_READER_DB_PORT | Timescale DB port | 5432 |
| MF_TIMESCALE_READER_DB_USER | Timescale user | mainflux |
| MF_TIMESCALE_READER_DB_PASS | Timescale password | mainflux |
-1
View File
@@ -6,7 +6,6 @@ package timescale
import (
"fmt"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
+1 -1
View File
@@ -36,9 +36,9 @@ func TestMain(m *testing.M) {
}
port := container.GetPort("5432/tcp")
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
if err = pool.Retry(func() error {
url := fmt.Sprintf("host=localhost port=%s user=test dbname=test password=test sslmode=disable", port)
db, err = sqlx.Open("pgx", url)
if err != nil {
return err
+1 -1
View File
@@ -18,7 +18,7 @@ default values.
| Variable | Description | Default |
| -------------------------- | ----------------------------------------------------------------------- | -------------- |
| MF_THINGS_LOG_LEVEL | Log level for Things (debug, info, warn, error) | error |
| MF_THINGS_LOG_LEVEL | Log level for Things (debug, info, warn, error) | info |
| MF_THINGS_DB_HOST | Database host address | localhost |
| MF_THINGS_DB_PORT | Database host port | 5432 |
| MF_THINGS_DB_USER | Database user | mainflux |
+4 -42
View File
@@ -3,47 +3,11 @@
package postgres
import (
"fmt"
import migrate "github.com/rubenv/sql-migrate"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
// Config defines the options that are used when connecting to a PostgreSQL instance
type Config struct {
Host string
Port string
User string
Pass string
Name string
SSLMode string
SSLCert string
SSLKey string
SSLRootCert string
}
// Connect creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate
// failure.
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, err
}
if err := migrateDB(db); err != nil {
return nil, err
}
return db, nil
}
func migrateDB(db *sqlx.DB) error {
migrations := &migrate.MemoryMigrationSource{
// Migration of Things service
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "things_1",
@@ -102,6 +66,4 @@ func migrateDB(db *sqlx.DB) error {
},
}
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
return err
}
+3 -3
View File
@@ -11,8 +11,8 @@ import (
"os"
"testing"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
pgClient "github.com/mainflux/mainflux/internal/clients/postgres"
"github.com/mainflux/mainflux/things/postgres"
dockertest "github.com/ory/dockertest/v3"
)
@@ -54,7 +54,7 @@ func TestMain(m *testing.M) {
log.Fatalf("Could not connect to docker: %s", err)
}
dbConfig := postgres.Config{
dbConfig := pgClient.Config{
Host: "localhost",
Port: port,
User: "test",
@@ -66,7 +66,7 @@ func TestMain(m *testing.M) {
SSLRootCert: "",
}
if db, err = postgres.Connect(dbConfig); err != nil {
if db, err = pgClient.SetupDB(dbConfig, *postgres.Migration()); err != nil {
log.Fatalf("Could not setup test DB connection: %s", err)
}
+2 -2
View File
@@ -14,8 +14,8 @@ default values.
| Variable | Description | Default |
|----------------------------|----------------------------------------------------------------------|-----------------------|
| MF_TWINS_LOG_LEVEL | Log level for twin service (debug, info, warn, error) | error |
| MF_TWINS_HTTP_PORT | Twins service HTTP port | 9021 |
| MF_TWINS_LOG_LEVEL | Log level for twin service (debug, info, warn, error) | info |
| MF_TWINS_HTTP_PORT | Twins service HTTP port | 8180 |
| MF_TWINS_SERVER_CERT | Path to server certificate in PEM format | |
| MF_TWINS_SERVER_KEY | Path to server key in PEM format | |
| MF_JAEGER_URL | Jaeger server URL | |
+1 -1
View File
@@ -18,7 +18,7 @@ default values.
| Variable | Description | Default |
| ------------------------- | ----------------------------------------------------------------------- | -------------- |
| MF_USERS_LOG_LEVEL | Log level for Users (debug, info, warn, error) | error |
| MF_USERS_LOG_LEVEL | Log level for Users (debug, info, warn, error) | info |
| MF_USERS_DB_HOST | Database host address | localhost |
| MF_USERS_DB_PORT | Database host port | 5432 |
| MF_USERS_DB_USER | Database user | mainflux |
+4 -44
View File
@@ -3,48 +3,11 @@
package postgres
import (
"fmt"
import migrate "github.com/rubenv/sql-migrate"
_ "github.com/jackc/pgx/v5/stdlib" // required for SQL access
"github.com/jmoiron/sqlx"
migrate "github.com/rubenv/sql-migrate"
)
// Config defines the options that are used when connecting to a PostgreSQL instance
type Config struct {
Host string
Port string
User string
Pass string
Name string
SSLMode string
SSLCert string
SSLKey string
SSLRootCert string
}
// Connect creates a connection to the PostgreSQL instance and applies any
// unapplied database migrations. A non-nil error is returned to indicate
// failure.
func Connect(cfg Config) (*sqlx.DB, error) {
url := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", cfg.Host, cfg.Port, cfg.User, cfg.Name, cfg.Pass, cfg.SSLMode, cfg.SSLCert, cfg.SSLKey, cfg.SSLRootCert)
db, err := sqlx.Open("pgx", url)
if err != nil {
return nil, err
}
if err := migrateDB(db); err != nil {
return nil, err
}
return db, nil
}
func migrateDB(db *sqlx.DB) error {
migrations := &migrate.MemoryMigrationSource{
// Migration of Users service
func Migration() *migrate.MemoryMigrationSource {
return &migrate.MemoryMigrationSource{
Migrations: []*migrate.Migration{
{
Id: "users_1",
@@ -93,7 +56,4 @@ func migrateDB(db *sqlx.DB) error {
},
},
}
_, err := migrate.Exec(db.DB, "postgres", migrations, migrate.Up)
return err
}

Some files were not shown because too many files have changed in this diff Show More