diff --git a/README.md b/README.md index fff4ca3..dfdfeaf 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ servers instead. Each server runs one or more instances of lobby daemon and it r about its hostname and configured labels. Labels are similar what you could know from AWS. It's basically alternative to resources' tags feature -with the only different that you can use this anywhere. Every server sends something called +with the only different that you can use this anywhere including AWS. Every server sends something called "discovery packet" which is basically a json that looks like this: ```json @@ -19,14 +19,14 @@ with the only different that you can use this anywhere. Every server sends somet "service:smtp", "public_ip4:1.2.3.4", "public_ip6:2a03::1" - ], - "last_check": 1630612478 + ] } ``` The packet contains information what's the server's hostname and then list of labels describing -what's running on it and what are the IP addresses. What's in the labels is completely up to you -but in some use-cases (Node Exporter API endpoint) it expects "NAME:VALUE" format. +what's running on it, what are the IP addresses, services, directories to backup or server's location for example. +What's in the labels is completely up to you but in some use-cases (Node Exporter API endpoint) it +expects "NAME:VALUE" format. The labels can be configured via environment variables but also as files located in */etc/lobby/labels* (configurable path) so it can dynamically change. Another way is to use @@ -88,22 +88,29 @@ To test if local instance is running call this: There are other config directives you can use to fine-tune lobbyd to exactly what you need. -| Environment variable | Type | Default | Required | Note | -| ----------------------- | ------ | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -| TOKEN | string | | no | Authentication token for API, if empty auth is disabled | -| HOST | string | 127.0.0.1 | no | IP address used for the REST server to listen | -| PORT | int | 1313 | no | Port related to the address above | -| NATS_URL | string | | yes | NATS URL used to connect to the NATS server | -| NATS_DISCOVERY_CHANNEL | string | lobby.discovery | no | Channel where the keep-alive packets are sent | -| LABELS | string | | no | List of labels, labels should be separated by comma | -| LABELS_PATH | string | /etc/lobby/labels | no | Path where filesystem based labels are located, one label per line, filename is not important for lobby | -| RUNTIME_LABELS_FILENAME | string | _runtime | no | Filename for file created in LabelsPath where runtime labels will be added | -| HOSTNAME | string | | no | Override local machine's hostname | -| CLEAN_EVERY | int | 15 | no | How often to clean the list of discovered servers to get rid of the not alive ones [secs] | -| KEEP_ALIVE | int | 5 | no | how often to send the keep-alive discovery message with all available information [secs] | -| TTL | int | 30 | no | After how many secs is discovery record considered as invalid | -| NODE_EXPORTER_PORT | int | 9100 | no | Default port where node_exporter listens on all registered servers, this is used when the special prometheus labels doesn't contain port | -| REGISTER | bool | true | no | If true (default) then local instance is registered with other instance (discovery packet is sent regularly), if false the daemon runs only as a client | +| Environment variable | Type | Default | Required | Note | +| ----------------------- | ------ | ----------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| TOKEN | string | | no | Authentication token for API, if empty auth is disabled | +| HOST | string | 127.0.0.1 | no | IP address used for the REST server to listen | +| PORT | int | 1313 | no | Port related to the address above | +| DISABLE_API | bool | false | no | If true API interface won't start | +| DRIVER | string | NATS | yes | Selects which driver is used to exchange the discovery packets. | +| NATS_URL | string | | yes (NATS driver) | NATS URL used to connect to the NATS server | +| NATS_DISCOVERY_CHANNEL | string | lobby.discovery | no | Channel where the keep-alive packets are sent | +| REDIS_HOST | string | 127.0.0.1" | no | Redis host | +| REDIS_PORT | uint16 | 6379 | no | Redis port | +| REDIS_DB | string | 0 | no | Redis DB | +| REDIS_CHANNEL | string | lobby:discovery | no | Redis channel | +| REDIS_PASSWORD | string | | no | Redis password | +| LABELS | string | | no | List of labels, labels should be separated by comma | +| LABELS_PATH | string | /etc/lobby/labels | no | Path where filesystem based labels are located, one label per line, filename is not important for lobby | +| RUNTIME_LABELS_FILENAME | string | _runtime | no | Filename for file created in LabelsPath where runtime labels will be added | +| HOSTNAME | string | | no | Override local machine's hostname | +| CLEAN_EVERY | int | 15 | no | How often to clean the list of discovered servers to get rid of the not alive ones [secs] | +| KEEP_ALIVE | int | 5 | no | how often to send the keep-alive discovery message with all available information [secs] | +| TTL | int | 30 | no | After how many secs is discovery record considered as invalid | +| NODE_EXPORTER_PORT | int | 9100 | no | Default port where node_exporter listens on all registered servers, this is used when the special prometheus labels doesn't contain port | +| REGISTER | bool | true | no | If true (default) then local instance is registered with other instance (discovery packet is sent regularly), if false the daemon runs only as a client | ### Service discovery for Prometheus @@ -197,12 +204,25 @@ If there is an error the error message is returned as plain text. * Golang client is part of this repository. * There is also [Python client](https://github.com/by-cx/lobby-python) available. +## Notes + +I wanted to use SQS or SNS as backend but when I checked the services I found out +it wouldn't work. SNS would require open HTTP server whicn is hard to do in our +infrastructure and I couldn't find a way how SQS could deliver every message +to all instances of lobbyd. + +Instead I decided to implement Redis because it's much easier to use for +development and testing. But there is no reason why it couldn't work in production +too. + + ## TODO * [X] Tests * [ ] Command hooks - script or list of scripts that are triggered when discovery status has changed * [ ] Support for multiple active backend drivers -* [ ] SNS driver +* [ ] Redis driver +* [X] Remove the 5 secs waiting when daemon is stopped * [X] API to allow add labels at runtime diff --git a/client/main.go b/client/main.go index 064bb8b..8c04534 100644 --- a/client/main.go +++ b/client/main.go @@ -6,8 +6,8 @@ import ( "fmt" "strings" + "github.com/by-cx/lobby/server" "github.com/go-resty/resty/v2" - "github.com/rosti-cz/server_lobby/server" ) // Encapsulation of Lobby's client code diff --git a/common/types.go b/common/types.go index 58a90d7..294c7a6 100644 --- a/common/types.go +++ b/common/types.go @@ -1,6 +1,6 @@ package common -import "github.com/rosti-cz/server_lobby/server" +import "github.com/by-cx/lobby/server" // Listener is a function that returns received discovery type Listener func(server.Discovery) diff --git a/ctl/format.go b/ctl/format.go index dd07e6d..cc4fddc 100644 --- a/ctl/format.go +++ b/ctl/format.go @@ -7,8 +7,8 @@ import ( "strconv" "strings" + "github.com/by-cx/lobby/server" "github.com/fatih/color" - "github.com/rosti-cz/server_lobby/server" ) func printDiscovery(discovery server.Discovery) { diff --git a/ctl/main.go b/ctl/main.go index d3454e3..3761674 100644 --- a/ctl/main.go +++ b/ctl/main.go @@ -6,8 +6,8 @@ import ( "os" "strings" - "github.com/rosti-cz/server_lobby/client" - "github.com/rosti-cz/server_lobby/server" + "github.com/by-cx/lobby/client" + "github.com/by-cx/lobby/server" ) func Usage() { diff --git a/daemon/config.go b/daemon/config.go index c9d7899..c0c098a 100644 --- a/daemon/config.go +++ b/daemon/config.go @@ -3,8 +3,8 @@ package main import ( "log" + "github.com/by-cx/lobby/server" "github.com/kelseyhightower/envconfig" - "github.com/rosti-cz/server_lobby/server" ) // Config keeps info about configuration of this daemon @@ -12,8 +12,15 @@ type Config struct { Token string `envconfig:"TOKEN" required:"false"` // Authentication token, if empty auth is disabled Host string `envconfig:"HOST" required:"false" default:"127.0.0.1"` // IP address used for the REST server to listen Port uint16 `envconfig:"PORT" required:"false" default:"1313"` // Port related to the address above - NATSURL string `envconfig:"NATS_URL" required:"true"` // NATS URL used to connect to the NATS server + DisableAPI bool `envconfig:"DISABLE_API" required:"false" default:"false"` // If true API interface won't start + Driver string `envconfig:"DRIVER" required:"false" default:"NATS"` // Select driver to use to communicate with the group of nodes. The possible values are NATS and Redis + NATSURL string `envconfig:"NATS_URL" required:"false"` // NATS URL used to connect to the NATS server NATSDiscoveryChannel string `envconfig:"NATS_DISCOVERY_CHANNEL" required:"false" default:"lobby.discovery"` // Channel where the kepp alive packets are sent + RedisHost string `envconfig:"REDIS_HOST" required:"false" default:"127.0.0.1"` // Redis host + RedisPort uint16 `envconfig:"REDIS_PORT" required:"false" default:"6379"` // Redis port + RedisDB uint `envconfig:"REDIS_DB" required:"false" default:"0"` // Redis DB + RedisChannel string `envconfig:"REDIS_CHANNEL" required:"false" default:"lobby:discovery"` // Redis channel + RedisPassword string `envconfig:"REDIS_PASSWORD" required:"false" default:""` // Redis password Labels server.Labels `envconfig:"LABELS" required:"false" default:""` // List of labels LabelsPath string `envconfig:"LABELS_PATH" required:"false" default:"/etc/lobby/labels"` // Path where filesystem based labels are located RuntimeLabelsFilename string `envconfig:"RUNTIME_LABELS_FILENAME" required:"false" default:"_runtime"` // Filename for file created in LabelsPath where runtime labels will be added @@ -34,5 +41,13 @@ func GetConfig() *Config { log.Fatal(err.Error()) } + if config.Driver != "Redis" && config.Driver != "NATS" { + log.Fatal("ERROR: the only supported drivers are Redis and NATS (default)") + } + + if config.Driver == "NATS" && len(config.NATSURL) == 0 { + log.Fatal("ERROR: NATS_URL cannot be empty when driver is set to NATS") + } + return &config } diff --git a/daemon/handlers.go b/daemon/handlers.go index 0f362b6..235dbe6 100644 --- a/daemon/handlers.go +++ b/daemon/handlers.go @@ -6,8 +6,8 @@ import ( "net/http" "strings" + "github.com/by-cx/lobby/server" "github.com/labstack/echo" - "github.com/rosti-cz/server_lobby/server" ) func listHandler(c echo.Context) error { diff --git a/daemon/main.go b/daemon/main.go index 7f3c9cd..be20766 100644 --- a/daemon/main.go +++ b/daemon/main.go @@ -9,11 +9,12 @@ import ( "syscall" "time" + "github.com/by-cx/lobby/common" + "github.com/by-cx/lobby/nats_driver" + "github.com/by-cx/lobby/redis_driver" + "github.com/by-cx/lobby/server" "github.com/labstack/echo" "github.com/labstack/echo/middleware" - "github.com/rosti-cz/server_lobby/common" - "github.com/rosti-cz/server_lobby/nats_driver" - "github.com/rosti-cz/server_lobby/server" ) var discoveryStorage server.Discoveries = server.Discoveries{} @@ -41,12 +42,27 @@ func init() { } // Setup driver - driver = &nats_driver.Driver{ - NATSUrl: config.NATSURL, - NATSDiscoveryChannel: config.NATSDiscoveryChannel, + if config.Driver == "NATS" { + driver = &nats_driver.Driver{ + NATSUrl: config.NATSURL, + NATSDiscoveryChannel: config.NATSDiscoveryChannel, - LogChannel: discoveryStorage.LogChannel, + LogChannel: discoveryStorage.LogChannel, + } + } else if config.Driver == "Redis" { + driver = &redis_driver.Driver{ + Host: config.RedisHost, + Port: uint(config.RedisPort), + Password: config.RedisPassword, + Channel: config.RedisChannel, + DB: uint(config.RedisDB), + + LogChannel: discoveryStorage.LogChannel, + } + } else { + log.Fatalf("unsupported driver %s", config.Driver) } + } // cleanDiscoveryPool clears the local server map and keeps only the alive servers @@ -147,12 +163,14 @@ func main() { e.Use(middleware.Recover()) // Routes - e.GET("/", listHandler) - e.GET("/v1/discovery", getIdentificationHandler) - e.GET("/v1/discoveries", listHandler) - e.POST("/v1/labels", addLabelsHandler) - e.DELETE("/v1/labels", deleteLabelsHandler) - e.GET("/v1/prometheus/:name", prometheusHandler) + if config.DisableAPI { + e.GET("/", listHandler) + e.GET("/v1/discovery", getIdentificationHandler) + e.GET("/v1/discoveries", listHandler) + e.POST("/v1/labels", addLabelsHandler) + e.DELETE("/v1/labels", deleteLabelsHandler) + e.GET("/v1/prometheus/:name", prometheusHandler) + } // ------------------------------ // Termination signals processing diff --git a/daemon/prometheus.go b/daemon/prometheus.go index 2d9755c..69868e0 100644 --- a/daemon/prometheus.go +++ b/daemon/prometheus.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - "github.com/rosti-cz/server_lobby/server" + "github.com/by-cx/lobby/server" ) // [ diff --git a/go.mod b/go.mod index 36c5dbf..c49ebdf 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,21 @@ -module github.com/rosti-cz/server_lobby +module github.com/by-cx/lobby go 1.16 require ( github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/fatih/color v1.12.0 + github.com/go-redis/redis v6.15.9+incompatible github.com/go-resty/resty/v2 v2.6.0 - github.com/golang/protobuf v1.5.2 // indirect github.com/kelseyhightower/envconfig v1.4.0 github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 // indirect github.com/nats-io/nats-server/v2 v2.4.0 // indirect github.com/nats-io/nats.go v1.12.0 + github.com/onsi/gomega v1.16.0 // indirect github.com/shirou/gopsutil/v3 v3.21.7 github.com/stretchr/testify v1.7.0 github.com/valyala/fasttemplate v1.2.1 // indirect + golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect google.golang.org/protobuf v1.27.1 // indirect ) diff --git a/go.sum b/go.sum index 745d476..4a8ce7d 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,23 @@ github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-resty/resty/v2 v2.6.0 h1:joIR5PNLM2EFqqESUjCMGXrWmXNHEU9CEiK813oKYS4= github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -24,6 +32,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s= @@ -54,12 +63,24 @@ github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/shirou/gopsutil/v3 v3.21.7 h1:PnTqQamUjwEDSgn+nBGu0qSDV/CfvyiR/gwTH3i7HTU= github.com/shirou/gopsutil/v3 v3.21.7/go.mod h1:RGl11Y7XMTQPmHh8F0ayC6haKNBgH4PXMJuTAcMOlz4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tklauser/go-sysconf v0.3.7 h1:HT7h4+536gjqeq1ZIJPgOl1rg1XFatQGVZWp7Py53eg= @@ -71,36 +92,63 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -113,6 +161,13 @@ google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+Rur google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/nats_driver/main.go b/nats_driver/main.go index cd78853..8135112 100644 --- a/nats_driver/main.go +++ b/nats_driver/main.go @@ -4,11 +4,12 @@ import ( "encoding/json" "fmt" + "github.com/by-cx/lobby/common" + "github.com/by-cx/lobby/server" "github.com/nats-io/nats.go" - "github.com/rosti-cz/server_lobby/common" - "github.com/rosti-cz/server_lobby/server" ) +// NATS drivers is used to send discovery packet to other nodes into the group via NATS messenging protocol. type Driver struct { NATSUrl string NATSDiscoveryChannel string @@ -65,18 +66,22 @@ func (d *Driver) Init() error { return nil } +// Close is called when all is done. func (d *Driver) Close() error { return d.nc.Drain() } +// RegisterSubscribeFunction sets the function that will process the incoming messages func (d *Driver) RegisterSubscribeFunction(listener common.Listener) { d.subscribeListener = listener } +// RegisterUnsubscribeFunction sets the function that will process the goodbye incoming messages func (d *Driver) RegisterUnsubscribeFunction(listener common.Listener) { d.unsubscribeListener = listener } +// SendDiscoveryPacket send discovery packet to the group. func (d *Driver) SendDiscoveryPacket(discovery server.Discovery) error { envelope := discoveryEnvelope{ Discovery: discovery, @@ -94,6 +99,7 @@ func (d *Driver) SendDiscoveryPacket(discovery server.Discovery) error { return nil } +// SendGoodbyePacket deregister node from the group. It tells everybody that it's going to die. func (d *Driver) SendGoodbyePacket(discovery server.Discovery) error { envelope := discoveryEnvelope{ Discovery: discovery, diff --git a/nats_driver/types.go b/nats_driver/types.go index 5d1e14c..c4561ad 100644 --- a/nats_driver/types.go +++ b/nats_driver/types.go @@ -3,7 +3,7 @@ package nats_driver import ( "encoding/json" - "github.com/rosti-cz/server_lobby/server" + "github.com/by-cx/lobby/server" ) // discoveryEnvelope adds a message to the standard discovery format. The message diff --git a/redis_driver/main.go b/redis_driver/main.go new file mode 100644 index 0000000..62e26d9 --- /dev/null +++ b/redis_driver/main.go @@ -0,0 +1,142 @@ +package redis_driver + +import ( + "encoding/json" + + "github.com/by-cx/lobby/common" + "github.com/by-cx/lobby/server" + "github.com/go-redis/redis" + + "fmt" +) + +// Redis drivers is used to send discovery packet to other nodes into the group via Redis's pubsub protocol. +type Driver struct { + Host string + Port uint + Password string + Channel string + DB uint + + LogChannel chan string + + subscribeListener common.Listener + unsubscribeListener common.Listener + + redis *redis.Client +} + +// handler is called asynchronously so and because it cannot log directly to stderr there +// is channel called LogChannel that can be used to log error messages from this +func (d *Driver) handler(payload string) { + message := discoveryEnvelope{} + err := json.Unmarshal([]byte(payload), &message) + if err != nil && d.LogChannel != nil { + d.LogChannel <- fmt.Errorf("decoding message error: %v", err).Error() + } + + err = message.Discovery.Validate() + if err != nil && d.LogChannel != nil { + d.LogChannel <- fmt.Errorf("validation error: %v", err).Error() + } + + if message.Message == "hi" { + d.subscribeListener(message.Discovery) + } else if message.Message == "goodbye" { + d.unsubscribeListener(message.Discovery) + } else { + if d.LogChannel != nil { + d.LogChannel <- "incompatible message" + } + } +} + +// Init connect to Redis, subscribes to the channel and waits for the messages. +// It runs goroutine in background that listens for new messages. +func (d *Driver) Init() error { + if d.LogChannel == nil { + return fmt.Errorf("please initiate LogChannel variable") + } + + if len(d.Host) == 0 { + return fmt.Errorf("parameter Host cannot be empty") + } + + if len(d.Channel) == 0 { + return fmt.Errorf("pattern cannot be empty") + } + + if d.Port <= 0 || d.Port > 65536 { + return fmt.Errorf("port has to be in range of 0-65536") + } + + d.redis = redis.NewClient(&redis.Options{ + Addr: fmt.Sprintf("%s:%d", d.Host, d.Port), + Password: d.Password, + DB: int(d.DB), + }) + + pubsub := d.redis.Subscribe(d.Channel) + + go func(pubsub *redis.PubSub, d *Driver) { + channel := pubsub.Channel() + + for message := range channel { + d.handler(message.Payload) + } + }(pubsub, d) + + return nil +} + +// Close is called when all is done. +func (d *Driver) Close() error { + return d.redis.Close() +} + +// RegisterSubscribeFunction sets the function that will process the incoming messages +func (d *Driver) RegisterSubscribeFunction(listener common.Listener) { + d.subscribeListener = listener +} + +// RegisterUnsubscribeFunction sets the function that will process the goodbye incoming messages +func (d *Driver) RegisterUnsubscribeFunction(listener common.Listener) { + d.unsubscribeListener = listener +} + +// SendDiscoveryPacket send discovery packet to the group. +func (d *Driver) SendDiscoveryPacket(discovery server.Discovery) error { + envelope := discoveryEnvelope{ + Discovery: discovery, + Message: "hi", + } + + data, err := envelope.Bytes() + if err != nil { + return fmt.Errorf("sending discovery formating message error: %v", err) + } + + err = d.redis.Publish(d.Channel, data).Err() + if err != nil { + return fmt.Errorf("sending discovery error: %v", err) + } + return nil +} + +// SendGoodbyePacket deregister node from the group. It tells everybody that it's going to die. +func (d *Driver) SendGoodbyePacket(discovery server.Discovery) error { + envelope := discoveryEnvelope{ + Discovery: discovery, + Message: "goodbye", + } + + data, err := envelope.Bytes() + if err != nil { + return fmt.Errorf("sending discovery formating message error: %v", err) + } + err = d.redis.Publish(d.Channel, data).Err() + if err != nil { + return fmt.Errorf("sending discovery error: %v", err) + } + return nil +} diff --git a/redis_driver/types.go b/redis_driver/types.go new file mode 100644 index 0000000..c155b09 --- /dev/null +++ b/redis_driver/types.go @@ -0,0 +1,20 @@ +package redis_driver + +import ( + "encoding/json" + + "github.com/by-cx/lobby/server" +) + +// discoveryEnvelope adds a message to the standard discovery format. The message +// can be "hi" or "goodbye" where "hi" is used when the node is sending keep alive +// packets and "goodbye" means the node is leaving. +type discoveryEnvelope struct { + Discovery server.Discovery `json:"discovery"` + Message string `json:"message"` // can be hi or goodbye +} + +func (e *discoveryEnvelope) Bytes() ([]byte, error) { + body, err := json.Marshal(e) + return body, err +}