From 2f3698430b283a91509e74caa9c099626e7884db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20=C5=A0trauch?= Date: Tue, 31 Aug 2021 16:26:09 +0200 Subject: [PATCH] Initial commit --- .gitignore | 5 ++ daemon/config.go | 26 ++++++++ daemon/identification.go | 19 ++++++ daemon/main.go | 111 ++++++++++++++++++++++++++++++++ daemon/message_handlers.go | 27 ++++++++ go.mod | 17 +++++ go.sum | 109 +++++++++++++++++++++++++++++++ server/main.go | 127 +++++++++++++++++++++++++++++++++++++ 8 files changed, 441 insertions(+) create mode 100644 .gitignore create mode 100644 daemon/config.go create mode 100644 daemon/identification.go create mode 100644 daemon/main.go create mode 100644 daemon/message_handlers.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 server/main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92f0e3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# Files with secrets +*secret* + +# Binaries +lobby_* diff --git a/daemon/config.go b/daemon/config.go new file mode 100644 index 0000000..bc05c57 --- /dev/null +++ b/daemon/config.go @@ -0,0 +1,26 @@ +package main + +import ( + "log" + + "github.com/kelseyhightower/envconfig" +) + +// Config keeps info about configuration of this daemon +type Config struct { + Token string `envconfig:"TOKEN" required:"false"` // not used yet + NATSURL string `envconfig:"NATS_URL" required:"true"` + Labels []string `envconfig:"LABELS" required:"false" default:""` +} + +// GetConfig return configuration created based on environment variables +func GetConfig() *Config { + var config Config + + err := envconfig.Process("", &config) + if err != nil { + log.Fatal(err.Error()) + } + + return &config +} diff --git a/daemon/identification.go b/daemon/identification.go new file mode 100644 index 0000000..a2860aa --- /dev/null +++ b/daemon/identification.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/rosti-cz/server_lobby/server" + "github.com/shirou/gopsutil/v3/host" +) + +func getIdentification() (server.Discovery, error) { + discovery := server.Discovery{} + + info, err := host.Info() + if err != nil { + return discovery, err + } + discovery.Hostname = info.Hostname + discovery.Labels = config.Labels + + return discovery, nil +} diff --git a/daemon/main.go b/daemon/main.go new file mode 100644 index 0000000..0382022 --- /dev/null +++ b/daemon/main.go @@ -0,0 +1,111 @@ +package main + +import ( + "log" + "time" + + "github.com/labstack/echo" + "github.com/labstack/echo/middleware" + "github.com/nats-io/nats.go" + "github.com/rosti-cz/server_lobby/server" +) + +const discoveryChannel = "lobby.discovery" +const cleanEvery = 15 // clean discoveredServers every X seconds +const keepAlive = 15 // sends discovery struct every + +var discoveryStorage server.Discoveries = server.Discoveries{} + +var config Config + +func init() { + discoveryStorage.LogChannel = make(chan string) +} + +// cleanDiscoveryPool clears the local server map and keeps only the alive servers +func cleanDiscoveryPool() { + for { + discoveryStorage.Clean() + time.Sleep(cleanEvery * time.Second) + } + +} + +// sendDisoveryPacket sends discovery packet regularly so the network know we exist +func sendDisoveryPacket(nc *nats.Conn) { + for { + discovery, err := getIdentification() + if err != nil { + log.Printf("sending discovery identification error: %v\n", err) + } + + data, err := discovery.Bytes() + if err != nil { + log.Printf("sending discovery formating message error: %v\n", err) + } + err = nc.Publish(discoveryChannel, data) + if err != nil { + log.Printf("sending discovery error: %v\n", err) + } + time.Sleep(keepAlive * time.Second) + } +} + +// Print logs acquired from disovery storage +func printDiscoveryLogs() { + for { + logMessage := <-discoveryStorage.LogChannel + log.Println(logMessage) + } +} + +func main() { + var err error + + // Closing the logging channel + defer close(discoveryStorage.LogChannel) + + // Load config from environment variables + config = *GetConfig() + + // ------------------------ + // Server discovering stuff + // ------------------------ + + // Connect to the NATS service + nc, err := nats.Connect(config.NATSURL) + if err != nil { + log.Fatalln(err) + } + defer nc.Drain() + + go printDiscoveryLogs() + + // Subscribe + log.Println("> discovery channel") + _, err = nc.Subscribe(discoveryChannel, discoveryHandler) + if err != nil { + log.Fatalln(err) + } + + go cleanDiscoveryPool() + go sendDisoveryPacket(nc) + + // -------- + // REST API + // -------- + e := echo.New() + + // Middleware + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + + // Routes + e.GET("/", func(c echo.Context) error { + discoveries := discoveryStorage.GetAll() + return c.JSONPretty(200, discoveries, " ") + }) + + // Start server + e.Logger.Fatal(e.Start(":1313")) +} diff --git a/daemon/message_handlers.go b/daemon/message_handlers.go new file mode 100644 index 0000000..776da04 --- /dev/null +++ b/daemon/message_handlers.go @@ -0,0 +1,27 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/nats-io/nats.go" + "github.com/rosti-cz/server_lobby/server" +) + +// discoveryHandler accepts discovery message and +func discoveryHandler(m *nats.Msg) { + message := server.Discovery{} + err := json.Unmarshal(m.Data, &message) + if err != nil { + log.Println(fmt.Errorf("decoding message error: %v", err)) + } + + err = message.Validate() + if err != nil { + log.Println(fmt.Errorf("validation error: %v", err)) + } + + discoveryStorage.Add(message) + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..602c0df --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module github.com/rosti-cz/server_lobby + +go 1.16 + +require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + 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/shirou/gopsutil/v3 v3.21.7 + github.com/valyala/fasttemplate v1.2.1 // indirect + golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect + google.golang.org/protobuf v1.27.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4d6eba0 --- /dev/null +++ b/go.sum @@ -0,0 +1,109 @@ +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/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/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/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/minio/highwayhash v1.0.1 h1:dZ6IIu8Z14VlC0VpfKofAhCy74wu/Qb5gcn52yWoz/0= +github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/nats-io/jwt v1.2.2 h1:w3GMTO969dFg+UOKTmmyuu7IGdusK+7Ytlt//OYH/uU= +github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= +github.com/nats-io/jwt/v2 v2.0.3 h1:i/O6cmIsjpcQyWDYNcq2JyZ3/VTF8SJ4JWluI5OhpvI= +github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= +github.com/nats-io/nats-server/v2 v2.4.0 h1:auni7PHiuyXR4BnDPzLVs3iyO7W7XUmZs8J5cjVb2BE= +github.com/nats-io/nats-server/v2 v2.4.0/go.mod h1:TUAhMFYh1VISyY/D4WKJUMuGHg8yHtoUTuxkbiej1lc= +github.com/nats-io/nats.go v1.12.0 h1:n0oZzK2aIZDMKuEiMKJ9qkCUgVY5vTAAksSXtLlz5Xc= +github.com/nats-io/nats.go v1.12.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +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/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.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= +github.com/tklauser/go-sysconf v0.3.7/go.mod h1:JZIdXh4RmBvZDBZ41ld2bGxRV3n4daiiqA3skYhAoQ4= +github.com/tklauser/numcpus v0.2.3 h1:nQ0QYpiritP6ViFhrKYsiv6VVxOpum2Gks5GhnJbS/8= +github.com/tklauser/numcpus v0.2.3/go.mod h1:vpEPS/JC+oZGGQ/My/vJnNsvMDQL6PwOqt8dsCw5j+E= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +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= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +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/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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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/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/xerrors v0.0.0-20191204190536-9bdfabe68543/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= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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/server/main.go b/server/main.go new file mode 100644 index 0000000..330c269 --- /dev/null +++ b/server/main.go @@ -0,0 +1,127 @@ +package server + +import ( + "encoding/json" + "fmt" + "time" +) + +// ---------------- +// Discovery struct +// ---------------- + +const TimeToLife = 60 // when server won't occur in the discovery channel longer than this, it should be considered as not-alive + +// Discovery contains information about a single server and is used for server discovery +type Discovery struct { + Hostname string `json:"hostname"` + Labels []string `json:"labels"` + + // For internal use to check if the server is still alive. + // Contains timestamp of the last check. + LastCheck int64 `json:"last_check"` +} + +// Validate checks all values in the struct if the content is valid +func (d *Discovery) Validate() error { + // TODO: implement + return nil +} + +// IsAlive return true if the server should be considered as alive +func (d *Discovery) IsAlive() bool { + return time.Now().Unix()-d.LastCheck < TimeToLife +} + +func (d *Discovery) Bytes() ([]byte, error) { + data, err := json.Marshal(d) + return data, err +} + +// ----------------- +// Discovery storage +// ----------------- + +// Discoveries helps to store instances of Discovery struct and access them in thread safe mode +type Discoveries struct { + activeServers []Discovery + LogChannel chan string +} + +// Add appends a new discovery/server to the storage +func (d *Discoveries) Add(discovery Discovery) { + if d.Exist(discovery.Hostname) { + d.Refresh(discovery.Hostname) + return + } + + discovery.LastCheck = time.Now().Unix() + d.activeServers = append(d.activeServers, discovery) + if d.LogChannel != nil { + d.LogChannel <- fmt.Sprintf("%s registered", discovery.Hostname) + } +} + +// Refresh updates +func (d *Discoveries) Refresh(hostname string) { + for idx, discovery := range d.activeServers { + if discovery.Hostname == hostname { + d.activeServers[idx].LastCheck = time.Now().Unix() + } + } +} + +// Delete removes server identified by hostname from the storage +func (d *Discoveries) Delete(hostname string) { + if d.LogChannel != nil { + d.LogChannel <- fmt.Sprintf("removing %s", hostname) + } + + newSet := []Discovery{} + for _, server := range d.activeServers { + if server.Hostname != hostname { + newSet = append(newSet, server) + } + } + d.activeServers = newSet +} + +// Exist returns true if server with given hostname exists +func (d *Discoveries) Exist(hostname string) bool { + for _, server := range d.activeServers { + if server.Hostname == hostname { + return true + } + } + return false +} + +// Get returns Discovery struct with the given hostname but it can be also an empty struct if it's not found. Check if hostname is empty or use Exist first to be sure. +func (d *Discoveries) Get(hostname string) Discovery { + for _, server := range d.activeServers { + if server.Hostname == hostname { + return server + } + } + return Discovery{} +} + +// GetAll returns copy of the internal storage +func (d *Discoveries) GetAll() []Discovery { + return d.activeServers +} + +// Clean checks loops over last check values for each discovery object and removes it if it's passed +func (d *Discoveries) Clean() { + newSet := []Discovery{} + for _, server := range d.activeServers { + if server.IsAlive() { + newSet = append(newSet, server) + } else { + if d.LogChannel != nil { + d.LogChannel <- fmt.Sprintf("%s not alive anymore", server.Hostname) + } + } + } + d.activeServers = newSet +}