Go client for the API, lobbyctl tool
This commit is contained in:
parent
0276465876
commit
5b7459afb5
4
Makefile
4
Makefile
@ -8,6 +8,6 @@ clean:
|
|||||||
.PHONY: build
|
.PHONY: build
|
||||||
build:
|
build:
|
||||||
mkdir -p ./bin
|
mkdir -p ./bin
|
||||||
export CGO_ENABLED=0
|
export CGO_ENABLED=0 && go build -o ./bin/lobbyd daemon/*.go
|
||||||
go build -o ./bin/lobbyd daemon/*.go
|
export CGO_ENABLED=0 && go build -o ./bin/lobbyctl ctl/*.go
|
||||||
|
|
||||||
|
176
client/main.go
Normal file
176
client/main.go
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"github.com/rosti-cz/server_lobby/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Encapsulation of Lobby's client code
|
||||||
|
type LobbyClient struct {
|
||||||
|
Proto string
|
||||||
|
Host string
|
||||||
|
Port uint
|
||||||
|
Token string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LobbyClient) init() {
|
||||||
|
if len(l.Proto) == 0 {
|
||||||
|
l.Host = "http"
|
||||||
|
}
|
||||||
|
if len(l.Host) == 0 {
|
||||||
|
l.Host = "localhost"
|
||||||
|
}
|
||||||
|
if l.Port == 0 {
|
||||||
|
l.Port = 1313
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calls the backend API with given method, path and request body and returns status code, response body and error if there is any.
|
||||||
|
// Method can be GET, POST or DELETE.
|
||||||
|
// Path should start with / and it can contain query parameters too.
|
||||||
|
func (l *LobbyClient) call(method, path, body string) (uint, string, error) {
|
||||||
|
client := resty.New().R()
|
||||||
|
|
||||||
|
if len(l.Token) != 0 {
|
||||||
|
client = client.SetHeader("Authorization", fmt.Sprintf("Token %s", l.Token))
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.ToUpper(method) == "GET" {
|
||||||
|
resp, err := client.Get(fmt.Sprintf("%s://%s:%d%s", l.Proto, l.Host, l.Port, path))
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
return uint(resp.StatusCode()), string(resp.Body()), nil
|
||||||
|
} else if strings.ToUpper(method) == "POST" {
|
||||||
|
resp, err := client.SetBody(body).Post(fmt.Sprintf("%s://%s:%d%s", l.Proto, l.Host, l.Port, path))
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
return uint(resp.StatusCode()), string(resp.Body()), nil
|
||||||
|
} else if strings.ToUpper(method) == "DELETE" {
|
||||||
|
resp, err := client.SetBody(body).Delete(fmt.Sprintf("%s://%s:%d%s", l.Proto, l.Host, l.Port, path))
|
||||||
|
if err != nil {
|
||||||
|
return 0, "", err
|
||||||
|
}
|
||||||
|
return uint(resp.StatusCode()), string(resp.Body()), nil
|
||||||
|
} else {
|
||||||
|
return 0, "", errors.New("unsupported method")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns discovery object of local machine
|
||||||
|
func (l *LobbyClient) GetDiscovery() (server.Discovery, error) {
|
||||||
|
l.init()
|
||||||
|
|
||||||
|
var discovery server.Discovery
|
||||||
|
|
||||||
|
path := "/v1/discovery"
|
||||||
|
method := "GET"
|
||||||
|
|
||||||
|
status, body, err := l.call(method, path, "")
|
||||||
|
if err != nil {
|
||||||
|
return discovery, err
|
||||||
|
}
|
||||||
|
if status != 200 {
|
||||||
|
return discovery, fmt.Errorf("non-200 response: %s", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(body), &discovery)
|
||||||
|
if err != nil {
|
||||||
|
return discovery, fmt.Errorf("response parsing error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return discovery, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns all registered discovery packets
|
||||||
|
func (l *LobbyClient) GetDiscoveries() ([]server.Discovery, error) {
|
||||||
|
l.init()
|
||||||
|
|
||||||
|
path := "/v1/discoveries"
|
||||||
|
method := "GET"
|
||||||
|
|
||||||
|
var discoveries []server.Discovery
|
||||||
|
|
||||||
|
status, body, err := l.call(method, path, "")
|
||||||
|
if err != nil {
|
||||||
|
return discoveries, err
|
||||||
|
}
|
||||||
|
if status != 200 {
|
||||||
|
return discoveries, fmt.Errorf("non-200 response: %s", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(body), &discoveries)
|
||||||
|
if err != nil {
|
||||||
|
return discoveries, fmt.Errorf("response parsing error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return discoveries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find discoveries by their labels
|
||||||
|
func (l *LobbyClient) FindByLabels(labels server.Labels) (server.Discoveries, error) {
|
||||||
|
l.init()
|
||||||
|
|
||||||
|
path := fmt.Sprintf("/v1/discoveries?labels=%s", strings.Join(labels.StringSlice(), ","))
|
||||||
|
method := "GET"
|
||||||
|
|
||||||
|
var discoveries server.Discoveries
|
||||||
|
|
||||||
|
status, body, err := l.call(method, path, "")
|
||||||
|
if err != nil {
|
||||||
|
return discoveries, err
|
||||||
|
}
|
||||||
|
if status != 200 {
|
||||||
|
return discoveries, fmt.Errorf("non-200 response: %s", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(body), &discoveries)
|
||||||
|
if err != nil {
|
||||||
|
return discoveries, fmt.Errorf("response parsing error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return discoveries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds runtime labels for the local machine
|
||||||
|
func (l *LobbyClient) AddLabels(labels server.Labels) error {
|
||||||
|
l.init()
|
||||||
|
|
||||||
|
path := "/v1/labels"
|
||||||
|
method := "POST"
|
||||||
|
|
||||||
|
status, body, err := l.call(method, path, strings.Join(labels.StringSlice(), "\n"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status != 200 {
|
||||||
|
return fmt.Errorf("non-200 response: %s", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes runtime labels of the local machine
|
||||||
|
func (l *LobbyClient) DeleteLabels(labels server.Labels) error {
|
||||||
|
l.init()
|
||||||
|
|
||||||
|
path := "/v1/labels"
|
||||||
|
method := "DELETE"
|
||||||
|
|
||||||
|
status, body, err := l.call(method, path, strings.Join(labels.StringSlice(), "\n"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if status != 200 {
|
||||||
|
return fmt.Errorf("non-200 response: %s", body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
29
ctl/config.go
Normal file
29
ctl/config.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/kelseyhightower/envconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config keeps info about configuration of this daemon
|
||||||
|
type Config struct {
|
||||||
|
Token string `envconfig:"TOKEN" required:"false"` // Authentication token, if empty auth is disabled
|
||||||
|
Proto string `envconfig:"PROTOCOL" required:"false" default:"http"` // selected http or https protocols, default is http
|
||||||
|
Host string `envconfig:"HOST" required:"false" default:"127.0.0.1"` // IP address or hostname where lobbyd is listening
|
||||||
|
Port uint `envconfig:"PORT" required:"false" default:"1313"` // Same thing but the port part
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfig return configuration created based on environment variables
|
||||||
|
func GetConfig() *Config {
|
||||||
|
var config Config
|
||||||
|
|
||||||
|
err := envconfig.Process("", &config)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config
|
||||||
|
}
|
43
ctl/format.go
Normal file
43
ctl/format.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/rosti-cz/server_lobby/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printDiscovery(discovery server.Discovery) {
|
||||||
|
fmt.Printf("Hostname:\n %s\n", discovery.Hostname)
|
||||||
|
|
||||||
|
if len(discovery.Labels) > 0 {
|
||||||
|
fmt.Printf("Labels:\n")
|
||||||
|
for _, label := range discovery.Labels {
|
||||||
|
fmt.Printf(" %s\n", label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printDiscoveries(discoveries []server.Discovery) {
|
||||||
|
maxHostnameWidth := 0
|
||||||
|
for _, discovery := range discoveries {
|
||||||
|
if len(discovery.Hostname) > maxHostnameWidth {
|
||||||
|
maxHostnameWidth = len(discovery.Hostname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, discovery := range discoveries {
|
||||||
|
if len(discovery.Labels) == 0 {
|
||||||
|
fmt.Println(discovery.Hostname)
|
||||||
|
} else {
|
||||||
|
hostname := fmt.Sprintf("%"+strconv.Itoa(maxHostnameWidth)+"s", discovery.Hostname)
|
||||||
|
fmt.Printf("%s %s\n", hostname, discovery.Labels[0].String())
|
||||||
|
if len(discovery.Labels) > 1 {
|
||||||
|
for _, label := range discovery.Labels[1:] {
|
||||||
|
fmt.Printf("%"+strconv.Itoa(maxHostnameWidth+4)+"s%s\n", " ", label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
117
ctl/main.go
Normal file
117
ctl/main.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rosti-cz/server_lobby/client"
|
||||||
|
"github.com/rosti-cz/server_lobby/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Usage() {
|
||||||
|
flag.Usage()
|
||||||
|
fmt.Println("")
|
||||||
|
fmt.Println("Commands:")
|
||||||
|
fmt.Println(" discovery returns discovery packet of the server where the client is connected to")
|
||||||
|
fmt.Println(" discoveries returns list of all registered discovery packets")
|
||||||
|
fmt.Println(" labels add LABEL [LABEL] ... adds new runtime labels")
|
||||||
|
fmt.Println(" labels del LABEL [LABEL] ... deletes runtime labels")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config := GetConfig()
|
||||||
|
|
||||||
|
// Setup flags
|
||||||
|
proto := flag.String("proto", "", "Select HTTP or HTTPS protocol")
|
||||||
|
host := flag.String("host", "", "Hostname or IP address of lobby daemon")
|
||||||
|
port := flag.Uint("port", 0, "Port of lobby daemon")
|
||||||
|
token := flag.String("token", "", "Token needed to communicate lobby daemon, if empty auth is disabled")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Replace empty values from flags by values from environment variables
|
||||||
|
if *proto == "" {
|
||||||
|
proto = &config.Proto
|
||||||
|
}
|
||||||
|
if *host == "" {
|
||||||
|
host = &config.Host
|
||||||
|
}
|
||||||
|
if *port == 0 {
|
||||||
|
port = &config.Port
|
||||||
|
}
|
||||||
|
if *token == "" {
|
||||||
|
token = &config.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
if *proto != "http" && *proto != "https" {
|
||||||
|
fmt.Println("Protocol can be only http or https")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup lobby client library
|
||||||
|
client := client.LobbyClient{
|
||||||
|
Proto: strings.ToLower(*proto),
|
||||||
|
Host: *host,
|
||||||
|
Port: *port,
|
||||||
|
Token: *token,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process rest of the arguments
|
||||||
|
if len(flag.Args()) == 0 {
|
||||||
|
Usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch flag.Args()[0] {
|
||||||
|
case "discoveries":
|
||||||
|
discoveries, err := client.GetDiscoveries()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
printDiscoveries(discoveries)
|
||||||
|
case "discovery":
|
||||||
|
discovery, err := client.GetDiscovery()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
printDiscovery(discovery)
|
||||||
|
case "labels":
|
||||||
|
if len(flag.Args()) < 3 {
|
||||||
|
fmt.Println("ERROR: not enough arguments for labels command")
|
||||||
|
fmt.Println("")
|
||||||
|
Usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := server.Labels{}
|
||||||
|
labelsString := flag.Args()[2:]
|
||||||
|
for _, labelString := range labelsString {
|
||||||
|
labels = append(labels, server.Label(labelString))
|
||||||
|
}
|
||||||
|
|
||||||
|
if flag.Args()[1] == "add" {
|
||||||
|
err := client.AddLabels(labels)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: %v\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
} else if flag.Args()[1] == "del" {
|
||||||
|
err := client.DeleteLabels(labels)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: %v\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("ERROR: wrong labels subcommand\n\n")
|
||||||
|
Usage()
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
Usage()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2
go.mod
2
go.mod
@ -4,6 +4,7 @@ go 1.16
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||||
|
github.com/go-resty/resty/v2 v2.6.0
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/kelseyhightower/envconfig v1.4.0
|
github.com/kelseyhightower/envconfig v1.4.0
|
||||||
github.com/labstack/echo v3.3.10+incompatible
|
github.com/labstack/echo v3.3.10+incompatible
|
||||||
@ -13,6 +14,5 @@ require (
|
|||||||
github.com/shirou/gopsutil/v3 v3.21.7
|
github.com/shirou/gopsutil/v3 v3.21.7
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/valyala/fasttemplate v1.2.1 // indirect
|
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
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
)
|
)
|
||||||
|
2
go.sum
2
go.sum
@ -6,6 +6,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
|
|||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
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 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
|
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/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
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.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.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
@ -9,3 +9,14 @@ func (l Label) String() string {
|
|||||||
|
|
||||||
// Labels stores multiple Label records
|
// Labels stores multiple Label records
|
||||||
type Labels []Label
|
type Labels []Label
|
||||||
|
|
||||||
|
// StringSlice return slice of Label as strings
|
||||||
|
func (l *Labels) StringSlice() []string {
|
||||||
|
labelsString := []string{}
|
||||||
|
|
||||||
|
for _, label := range *l {
|
||||||
|
labelsString = append(labelsString, label.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return labelsString
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user