Skip to content
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

allocator draft #28

Merged
merged 6 commits into from
Oct 16, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions tools/allocator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Thundernetes allocator tool

Thanks for using the allocator tool for thundernetes. To use it:
- `kubectl` is required to be in $PATH. for more information, please refer to the following [guide](https://kubernetes.io/docs/tasks/tools/#kubectl)
- Compile the main.go file (optional to provide a meaningful name like allocator, thunderallocator or something similar).
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add go run main.go to compile it

- Once you the executable, you can:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "Once you run"

- run the executable with no argument for some help and details
- `list` which will provide the available servers.
- `allocate <build-id> <session-id> [tls-public] [tls-private]` where the tls certificates are optional, but build and session ID are mandatory. Please note that providing the certs as env variables is also supported; if so, please name them TLS_PUBLIC for the cert file and TLS_PRIVATE for the key file.
3 changes: 3 additions & 0 deletions tools/allocator/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/playfab/thundernetes/tools/allocator

go 1.16
215 changes: 215 additions & 0 deletions tools/allocator/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package main

import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"strings"
);

type AllocationResult struct {
IPV4Address string `json:"IPv4Address"`
SessionID string `json:"SessionID"`
};

var (
ip string
certFile string
keyFile string
tlsSet bool
ar *AllocationResult
);

func main () {
args := os.Args

if len(args) == 1 {
fmt.Println("Usage of the allocator tool (is required to have"+
" kubectl on your $PATH)")
fmt.Println("\t- allocate <build-id> <session-id> [tls-public] [tls-private]"+
" # Initialize a server with the given paramaters (if tls certs"+
" are not on the TLS_PUBLIC / TLS_PRIVATE env variables, please"+
onlyralphie marked this conversation as resolved.
Show resolved Hide resolved
" provide them via argument)")
fmt.Println("\t- list # Returns the available Game Servers")
} else if strings.Compare(args[1], "allocate") == 0 {
fmt.Println("Beginning the allocate process")

cmd := exec.Command("kubectl","get","svc","-n","thundernetes-system","thundernetes-controller-manager",
onlyralphie marked this conversation as resolved.
Show resolved Hide resolved
"-o","jsonpath='{.status.loadBalancer.ingress[0].ip}'")

output, err := cmd.CombinedOutput()

if err != nil {
log.Println("Is required to have kubectl on your $PATH")
onlyralphie marked this conversation as resolved.
Show resolved Hide resolved
log.Fatal(string(output))

}

if len(args) < 5 { // if no more arguments are provided
if certFile == "" || keyFile == "" { // If the env vars are not set
tlsSet = false
} else { // the env vars are set
tlsSet = true
}
} else { // all the arguments are provided
tlsSet = true
}

if len(output) < 3 { // basically if we don't have a valid IP
if tlsSet == true {
ip = "https://127.0.0.1"
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
ar, err = allocateTls(ip, args[2], args[3], cert)

if err != nil {
log.Panic(err)
}
} else {
ip = "http://127.0.0.1"
ar, err = allocateNoTls(ip, args[2], args[3])

if err != nil {
log.Panic(err)
}
}
} else { // if we retrieve the ip correctly
ip = string(output)

if tlsSet == true {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
ar, err = allocateTls(ip, args[2], args[3], cert)

if err != nil {
log.Panic(err)
}
} else {
ar, err = allocateNoTls(ip, args[2], args[3])

if err != nil {
log.Panic(err)
}
}
}

log.Println("IP address: "+ ar.IPV4Address+". Session ID: "+ar.SessionID);

} else if strings.Compare(args[1], "list") == 0 {
fmt.Println("Listing the available game servers")
cmd := exec.Command("kubectl", "get", "gs")
output, err := cmd.CombinedOutput()

if err != nil {
fmt.Println(string(output))
log.Fatal("Error while fetching the servers: ", err)
log.Println("It is required to have kubectl on your $PATH")
fmt.Println("Please, make sure you have your cluster configured properly")
}

fmt.Println(string(output))
} else {
fmt.Println("Sorry, but the commad "+args[1]+" is not recognized")
}

fmt.Println("\nThanks for using the thundernetes allocator tool")

}

func allocateTls(ip string, buildID string, sessionID string, cert tls.Certificate) (*AllocationResult, error) {
tlsConfig := &tls.Config{
InsecureSkipVerify: true,
Certificates: []tls.Certificate{cert},
}

transport := &http.Transport{TLSClientConfig: tlsConfig}
client := &http.Client{Transport: transport}

postBody, _ := json.Marshal(map[string]interface{}{
"buildID": buildID,
"sessionID": sessionID,
"sessionCookie": "coolRandomCookie",
"initialPlayers": []string{"player1", "player2"},
})

postBodyBytes := bytes.NewBuffer(postBody)
resp, err := client.Post(ip+":5000/api/v1/allocate", "application/json", postBodyBytes)

//Handle Error
if err != nil {
return nil, err
}

defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%d", resp.StatusCode)
}

//Read the response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}

ar := &AllocationResult{}
json.Unmarshal(body, ar)

if ar.IPV4Address == "" {
return nil, fmt.Errorf("invalid IPV4Address %s", ar.IPV4Address)
}

if ar.SessionID != sessionID {
return nil, fmt.Errorf("invalid SessionID %s", ar.SessionID)
}

return ar, nil
}

func allocateNoTls(ip string, buildID string, sessionID string) (*AllocationResult, error) {

transport := &http.Transport{}
client := &http.Client{Transport: transport}

postBody, _ := json.Marshal(map[string]interface{}{
"buildID": buildID,
"sessionID": sessionID,
"sessionCookie": "coolRandomCookie",
"initialPlayers": []string{"player1", "player2"},
})

postBodyBytes := bytes.NewBuffer(postBody)
resp, err := client.Post(ip+":5000/api/v1/allocate", "application/json", postBodyBytes)

//Handle Error
if err != nil {
return nil, err
}

defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%d", resp.StatusCode)
}

//Read the response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}

ar := &AllocationResult{}
json.Unmarshal(body, ar)

if ar.IPV4Address == "" {
return nil, fmt.Errorf("invalid IPV4Address %s", ar.IPV4Address)
}

if ar.SessionID != sessionID {
return nil, fmt.Errorf("invalid SessionID %s", ar.SessionID)
}

return ar, nil
}