-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add prometheus metrics support #37
base: master
Are you sure you want to change the base?
Changes from 3 commits
856f8d4
99639f0
c106b47
6917fba
1c3a254
e68565e
c233ed5
7372f33
276ab47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,19 +4,17 @@ import ( | |
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net" | ||
"os" | ||
"time" | ||
|
||
"github.com/multiplay/go-svrquery/lib/svrquery" | ||
"github.com/multiplay/go-svrquery/lib/svrquery/protocol" | ||
"github.com/multiplay/go-svrquery/lib/svrsample" | ||
"github.com/multiplay/go-svrquery/lib/svrsample/common" | ||
"log" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Import ordering here is wrong |
||
"os" | ||
) | ||
|
||
func main() { | ||
clientAddr := flag.String("addr", "", "Address to connect to e.g. 127.0.0.1:12345") | ||
proto := flag.String("proto", "", "Protocol e.g. sqp, tf2e, tf2e-v7, tf2e-v8") | ||
proto := flag.String("proto", "", "Protocol e.g. sqp, tf2e, tf2e-v7, tf2e-v8, prom") | ||
key := flag.String("key", "", "Key to use to authenticate") | ||
file := flag.String("file", "", "Bulk file to execute to get basic server information") | ||
serverAddr := flag.String("server", "", "Address to start server e.g. 127.0.0.1:12121, :23232") | ||
|
@@ -39,12 +37,12 @@ func main() { | |
switch { | ||
case *serverAddr != "": | ||
if *proto == "" { | ||
bail(l, "No protocol provided in client mode") | ||
bail(l, "No protocol provided in server mode") | ||
} | ||
serverMode(l, *proto, *serverAddr) | ||
case *clientAddr != "": | ||
if *proto == "" { | ||
bail(l, "Protocol required in server mode") | ||
bail(l, "Protocol required in client mode") | ||
} | ||
queryMode(l, *proto, *clientAddr, *key) | ||
default: | ||
|
@@ -53,24 +51,34 @@ func main() { | |
} | ||
|
||
func queryMode(l *log.Logger, proto, address, key string) { | ||
if err := query(proto, address, key); err != nil { | ||
client, err := getClient(proto, address, key) | ||
if err != nil { | ||
l.Fatal(err) | ||
} | ||
} | ||
defer client.Close() | ||
|
||
func query(proto, address, key string) error { | ||
options := make([]svrquery.Option, 0) | ||
if key != "" { | ||
options = append(options, svrquery.WithKey(key)) | ||
if err := query(client); err != nil { | ||
l.Fatal(err) | ||
} | ||
} | ||
|
||
c, err := svrquery.NewClient(proto, address, options...) | ||
if err != nil { | ||
return err | ||
func getClient(proto, address, key string) (protocol.Client, error) { | ||
switch proto { | ||
case "sqp": | ||
options := make([]svrquery.Option, 0) | ||
if key != "" { | ||
options = append(options, svrquery.WithKey(key)) | ||
} | ||
return svrquery.NewUDPClient(proto, address, options...) | ||
case "prom": | ||
return svrquery.NewHTTPClient(proto, address) | ||
default: | ||
return nil, fmt.Errorf("protocol %s not supported", proto) | ||
} | ||
defer c.Close() | ||
} | ||
|
||
r, err := c.Query() | ||
func query(client protocol.Client) error { | ||
r, err := client.Query() | ||
if err != nil { | ||
return err | ||
} | ||
|
@@ -91,6 +99,12 @@ func serverMode(l *log.Logger, proto, serverAddr string) { | |
|
||
func server(l *log.Logger, proto, address string) error { | ||
l.Printf("Starting sample server using protocol %s on %s", proto, address) | ||
|
||
transport, err := svrsample.GetTransport(proto, address) | ||
if err != nil { | ||
return fmt.Errorf("create transport: %w", err) | ||
} | ||
|
||
responder, err := svrsample.GetResponder(proto, common.QueryState{ | ||
CurrentPlayers: 1, | ||
MaxPlayers: 2, | ||
|
@@ -100,43 +114,16 @@ func server(l *log.Logger, proto, address string) error { | |
Port: 1000, | ||
}) | ||
if err != nil { | ||
return err | ||
return fmt.Errorf("create responder: %w", err) | ||
} | ||
|
||
addr, err := net.ResolveUDPAddr("udp4", address) | ||
// this function will block until the transport is closed | ||
err = transport.Start(responder) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
conn, err := net.ListenUDP("udp4", addr) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for { | ||
buf := make([]byte, 16) | ||
_, to, err := conn.ReadFromUDP(buf) | ||
if err != nil { | ||
l.Println("read from udp", err) | ||
continue | ||
} | ||
|
||
resp, err := responder.Respond(to.String(), buf) | ||
if err != nil { | ||
l.Println("error responding to query", err) | ||
continue | ||
} | ||
|
||
if err = conn.SetWriteDeadline(time.Now().Add(1 * time.Second)); err != nil { | ||
l.Println("error setting write deadline") | ||
continue | ||
} | ||
|
||
if _, err = conn.WriteTo(resp, to); err != nil { | ||
l.Println("error writing response") | ||
} | ||
return fmt.Errorf("transport error") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func bail(l *log.Logger, msg string) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,49 @@ | ||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= | ||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= | ||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= | ||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= | ||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= | ||
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/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= | ||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | ||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= | ||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= | ||
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/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= | ||
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= | ||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= | ||
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= | ||
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= | ||
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= | ||
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= | ||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= | ||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= | ||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= | ||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= | ||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= | ||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | ||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= | ||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,11 +17,16 @@ var ( | |
DefaultNetwork = "udp" | ||
) | ||
|
||
// Option represents a Client option. | ||
type Option func(*Client) error | ||
var ( | ||
_ protocol.Client = (*UDPClient)(nil) | ||
_ protocol.Client = (*HTTPClient)(nil) | ||
) | ||
|
||
// Client provides the ability to query a server. | ||
type Client struct { | ||
// Option represents a UDPClient option. | ||
type Option func(*UDPClient) error | ||
|
||
// UDPClient provides the ability to query a server. | ||
type UDPClient struct { | ||
protocol string | ||
network string | ||
addr string | ||
|
@@ -34,28 +39,28 @@ type Client struct { | |
|
||
// WithKey sets the key used for request by for the client. | ||
func WithKey(key string) Option { | ||
return func(c *Client) error { | ||
return func(c *UDPClient) error { | ||
c.key = key | ||
return nil | ||
} | ||
} | ||
|
||
// WithTimeout sets the read and write timeout for the client. | ||
func WithTimeout(t time.Duration) Option { | ||
return func(c *Client) error { | ||
return func(c *UDPClient) error { | ||
c.timeout = t | ||
return nil | ||
} | ||
} | ||
|
||
// NewClient creates a new client that talks to addr. | ||
func NewClient(proto, addr string, options ...Option) (*Client, error) { | ||
// NewUDPClient creates a new client that talks to addr. | ||
func NewUDPClient(proto, addr string, options ...Option) (*UDPClient, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is going to break users of the library as NewClient is publicly exported and the main way people will interact with the client. Can we either have the struct do both or abstract it internally? |
||
f, err := protocol.Get(proto) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
c := &Client{ | ||
c := &UDPClient{ | ||
protocol: proto, | ||
addr: addr, | ||
network: DefaultNetwork, | ||
|
@@ -81,7 +86,7 @@ func NewClient(proto, addr string, options ...Option) (*Client, error) { | |
} | ||
|
||
// Write implements io.Writer. | ||
func (c *Client) Write(b []byte) (int, error) { | ||
func (c *UDPClient) Write(b []byte) (int, error) { | ||
if err := c.c.SetWriteDeadline(time.Now().Add(c.timeout)); err != nil { | ||
return 0, err | ||
} | ||
|
@@ -90,7 +95,7 @@ func (c *Client) Write(b []byte) (int, error) { | |
} | ||
|
||
// Read implements io.Reader. | ||
func (c *Client) Read(b []byte) (int, error) { | ||
func (c *UDPClient) Read(b []byte) (int, error) { | ||
if err := c.c.SetReadDeadline(time.Now().Add(c.timeout)); err != nil { | ||
return 0, err | ||
} | ||
|
@@ -107,21 +112,66 @@ func (c *Client) Read(b []byte) (int, error) { | |
} | ||
|
||
// Close implements io.Closer. | ||
func (c *Client) Close() error { | ||
func (c *UDPClient) Close() error { | ||
return c.c.Close() | ||
} | ||
|
||
// Key implements protocol.Client. | ||
func (c *Client) Key() string { | ||
// Key implements protocol.UDPClient. | ||
func (c *UDPClient) Key() string { | ||
return c.key | ||
} | ||
|
||
// Address implements protocol.Client. | ||
func (c *Client) Address() string { | ||
// Address implements protocol.UDPClient. | ||
func (c *UDPClient) Address() string { | ||
return c.addr | ||
} | ||
|
||
// Protocol returns the protocol of the client. | ||
func (c *Client) Protocol() string { | ||
func (c *UDPClient) Protocol() string { | ||
return c.protocol | ||
} | ||
|
||
type HTTPClient struct { | ||
protocol.Queryer | ||
address string | ||
} | ||
|
||
func NewHTTPClient(proto, address string) (*HTTPClient, error) { | ||
queryerCreator, err := protocol.Get(proto) | ||
if err != nil { | ||
return nil, err | ||
} | ||
client := &HTTPClient{address: address} | ||
client.Queryer = queryerCreator(client) | ||
|
||
return client, nil | ||
} | ||
|
||
func (c *HTTPClient) Read(p []byte) (n int, err error) { | ||
//TODO implement me | ||
panic("implement me") | ||
} | ||
|
||
func (c *HTTPClient) Write(p []byte) (n int, err error) { | ||
//TODO implement me | ||
panic("implement me") | ||
} | ||
|
||
func (c *HTTPClient) Query() (protocol.Responser, error) { | ||
return c.Queryer.Query() | ||
} | ||
|
||
func (c *HTTPClient) Key() string { | ||
//TODO implement me | ||
panic("implement me") | ||
} | ||
|
||
func (c *HTTPClient) Address() string { | ||
return c.address | ||
} | ||
|
||
// Close implements io.Closer. | ||
func (c *HTTPClient) Close() error { | ||
// no-op | ||
return nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will fix this