Documentation ¶
Index ¶
- Constants
- Variables
- func GetNodeDetails(ctx context.Context, r client.Reader, nodeName string) (string, string, int, error)
- func IsNodeReadyAndSchedulable(node *corev1.Node) bool
- func NewGameServerForGameServerBuild(gsb *mpsv1alpha1.GameServerBuild, portRegistry *PortRegistry) (*mpsv1alpha1.GameServer, error)
- func NewPodForGameServer(gs *mpsv1alpha1.GameServer, ...) *corev1.Pod
- type AllocateArgs
- type AllocationApiServer
- type ByState
- type GameServerBuildReconciler
- type GameServerExpectations
- type GameServerForQueue
- type GameServerQueue
- func (h GameServerQueue) Len() int
- func (h GameServerQueue) Less(i, j int) bool
- func (h *GameServerQueue) Pop() interface{}
- func (h *GameServerQueue) PopFromQueue() *GameServerForQueue
- func (h *GameServerQueue) Push(x interface{})
- func (h *GameServerQueue) PushToQueue(gs *GameServerForQueue)
- func (h GameServerQueue) Swap(i, j int)
- type GameServerQueueForBuild
- type GameServerReconciler
- type GameServersQueue
- type MutexMap
- type PortRegistry
- func (pr *PortRegistry) DeregisterPorts(namespace, name string) ([]int32, error)
- func (pr *PortRegistry) GetNewPorts(namespace, name string, count int) ([]int32, error)
- func (pr *PortRegistry) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)
- func (pr *PortRegistry) SetupWithManager(mgr ctrl.Manager) error
- type RequestMultiplayerServerResponse
Constants ¶
const ( InitContainerName = "initcontainer" GameServerKind = "GameServer" GameServerBuildKind = "GameServerBuild" DataVolumeName = "gsdkdata" DataVolumeMountPath = "/gsdkdata" DataVolumeMountPathWin = "c:\\gsdkdata" RandStringSize = 5 LabelBuildID = "BuildID" LabelBuildName = "BuildName" LabelOwningGameServer = "mps.playfab.com/OwningGameServer" LabelOwningOperator = "mps.playfab.com/OwningOperator" LabelNodeName = "NodeName" GsdkConfigFile = DataVolumeMountPath + "/Config/gsdkConfig.json" GsdkConfigFileWin = DataVolumeMountPathWin + "\\Config\\gsdkConfig.json" LogDirectory = DataVolumeMountPath + "/GameLogs/" LogDirectoryWin = DataVolumeMountPathWin + "\\GameLogs\\" CertificatesDirectory = DataVolumeMountPath + "/GameCertificates" CertificatesDirectoryWin = DataVolumeMountPathWin + "\\GameCertificates" DaemonSetPort int32 = 56001 LabelGameServerNode string = "mps.playfab.com/gameservernode" )
const ( ActiveServerStatus = "active" StandingByServerStatus = "standingby" InitializingServerStatus = "initializing" PendingServerStatus = "pending" )
const SafeToEvictPodAttribute string = "cluster-autoscaler.kubernetes.io/safe-to-evict"
Variables ¶
var ( GameServersCreatedCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "gameservers_created_total", Help: "Number of GameServers created", }, []string{"BuildName"}, ) GameServersSessionEndedCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "gameservers_sessionended_total", Help: "Number of GameServer sessions ended", }, []string{"BuildName"}, ) GameServersCrashedCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "gameservers_crashed_total", Help: "Number of GameServers crashed", }, []string{"BuildName"}, ) GameServersUnhealthyCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "gameservers_unhealthy_total", Help: "Number of GameServers marked as Unhealthy", }, []string{"BuildName"}, ) GameServersDeletedCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "gameservers_deleted_total", Help: "Number of GameServers deleted", }, []string{"BuildName"}, ) CurrentGameServerGauge = registry.NewGaugeVec( prometheus.GaugeOpts{ Namespace: "thundernetes", Name: "gameservers_current_state_per_build", Help: "Gameserver gauges by state per build", }, []string{"BuildName", "state"}, ) AllocationsCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "allocations_total", Help: "Number of GameServers allocations", }, []string{"BuildName"}, ) AllocationsTimeTakenDuration = registry.NewGaugeVec( prometheus.GaugeOpts{ Namespace: "thundernetes", Name: "allocations_time_taken_duration", Help: "Average time it took to allocate a GameServer", }, []string{"BuildName"}, ) AllocationsRetriesCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "allocations_retried", Help: "The number of times allocation had to be retried", }, []string{"BuildName"}, ) Allocations429ErrorsCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "allocations_429", Help: "The number of 429 (too many requests) errors during allocation", }, []string{"BuildName"}, ) Allocations404ErrorsCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "allocations_404", Help: "The number of 404 (not found) errors during allocation", }, []string{"BuildName"}, ) Allocations500ErrorsCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "allocations_500", Help: "The number of 500 (internal) errors during allocation", }, []string{"BuildName"}, ) Allocations409ErrorsCounter = registry.NewCounterVec( prometheus.CounterOpts{ Namespace: "thundernetes", Name: "allocations_409", Help: "The number of 409 (request conflict) errors during allocation", }, []string{"BuildName"}, ) )
Functions ¶
func GetNodeDetails ¶ added in v0.4.0
func GetNodeDetails(ctx context.Context, r client.Reader, nodeName string) (string, string, int, error)
GetNodeDetails returns the Public IP of the node and the node age in days if the Node does not have a Public IP, method returns the internal one
func IsNodeReadyAndSchedulable ¶ added in v0.3.0
IsNodeReadyAndSchedulable returns true if the node is ready and schedulable
func NewGameServerForGameServerBuild ¶
func NewGameServerForGameServerBuild(gsb *mpsv1alpha1.GameServerBuild, portRegistry *PortRegistry) (*mpsv1alpha1.GameServer, error)
NewGameServerForGameServerBuild creates a GameServer for a GameServerBuild
func NewPodForGameServer ¶
func NewPodForGameServer(gs *mpsv1alpha1.GameServer, initContainerImageLinux, initContainerImageWin string) *corev1.Pod
NewPodForGameServer returns a Kubernetes Pod struct for a specified GameServer Pod has the same name as the GameServer It also sets a label called "GameServer" with the value of the corresponding GameServer resource
Types ¶
type AllocateArgs ¶ added in v0.4.0
type AllocateArgs struct { SessionID string `json:"sessionID"` BuildID string `json:"buildID"` SessionCookie string `json:"sessionCookie"` InitialPlayers []string `json:"initialPlayers"` }
AllocateArgs contains information necessary to allocate a GameServer
type AllocationApiServer ¶ added in v0.4.0
type AllocationApiServer struct { Client client.Client // CrtBytes is the PEM-encoded certificate CrtBytes []byte // KeyBytes is the PEM-encoded key KeyBytes []byte // contains filtered or unexported fields }
AllocationApiServer is a helper struct that implements manager.Runnable interface so it can be added to our Manager
func NewAllocationApiServer ¶ added in v0.4.0
func NewAllocationApiServer(crt, key []byte, cl client.Client, port int32) *AllocationApiServer
func (*AllocationApiServer) Reconcile ¶ added in v0.4.0
Reconcile gets triggered when there is a change on a game server object
func (*AllocationApiServer) SetupWithManager ¶ added in v0.4.0
func (s *AllocationApiServer) SetupWithManager(mgr ctrl.Manager) error
SetupWithManager sets up the allocation API controller with the manager
func (*AllocationApiServer) Start ¶ added in v0.4.0
func (s *AllocationApiServer) Start(ctx context.Context) error
Start starts the HTTP(S) allocation API service if user has provided public/private cert details, it will create a TLS-auth HTTPS server otherwise it will create a HTTP server with no auth
type ByState ¶ added in v0.5.0
type ByState []mpsv1alpha1.GameServer
ByState is a slice of GameServers
type GameServerBuildReconciler ¶
type GameServerBuildReconciler struct { client.Client Scheme *k8sruntime.Scheme PortRegistry *PortRegistry Recorder record.EventRecorder // contains filtered or unexported fields }
GameServerBuildReconciler reconciles a GameServerBuild object
func NewGameServerBuildReconciler ¶ added in v0.6.0
func NewGameServerBuildReconciler(mgr manager.Manager, portRegistry *PortRegistry) *GameServerBuildReconciler
NewGameServerBuildReconciler returns a pointer to a new GameServerBuildReconciler
func (*GameServerBuildReconciler) Reconcile ¶
func (r *GameServerBuildReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)
Reconcile is part of the main kubernetes reconciliation loop which aims to move the current state of the cluster closer to the desired state. For more details, check Reconcile and its Result here: - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile
func (*GameServerBuildReconciler) SetupWithManager ¶
func (r *GameServerBuildReconciler) SetupWithManager(mgr ctrl.Manager) error
SetupWithManager sets up the controller with the Manager.
type GameServerExpectations ¶ added in v0.6.0
type GameServerExpectations struct {
// contains filtered or unexported fields
}
func NewGameServerExpectations ¶ added in v0.6.0
func NewGameServerExpectations(c client.Reader) *GameServerExpectations
NewGameServerExpectations returns a pointer to a new GameServerExpectations struct
type GameServerForQueue ¶ added in v0.4.0
type GameServerForQueue struct { Name string Namespace string BuildID string NodeAge int ResourceVersion string }
GameServerForQueue is a helper struct that encapsulates all the details we need from a GameServer object in order to store it on the queue
type GameServerQueue ¶ added in v0.4.0
type GameServerQueue []*GameServerForQueue
GameServerQueue implements a PriorityQueue for GameServer objects GameServers are sorted in the queue in ascending order based on the NodeAge field this queue is used by the allocation algorithm, to prioritize allocations on the Nodes that are newer based on https://pkg.go.dev/container/heap
func (GameServerQueue) Len ¶ added in v0.4.0
func (h GameServerQueue) Len() int
Len returns the number of elements in the heap
func (GameServerQueue) Less ¶ added in v0.4.0
func (h GameServerQueue) Less(i, j int) bool
Less returns true if the GameServerForHeap with index i is in a newer Node (smaller NodeAge) compared to the GameServerForHeap with index j
func (*GameServerQueue) Pop ¶ added in v0.4.0
func (h *GameServerQueue) Pop() interface{}
Pop pops the top interface{} element off the heap this is written just to help implement the heap interface PopFromQueue should be used instead
func (*GameServerQueue) PopFromQueue ¶ added in v0.4.0
func (h *GameServerQueue) PopFromQueue() *GameServerForQueue
PopFromQueue pops the top GameServerForHeap off the heap It should be used instead of heap.Pop
func (*GameServerQueue) Push ¶ added in v0.4.0
func (h *GameServerQueue) Push(x interface{})
Push pushes a interface{} element onto the heap this is written just to help implement the heap interface PopFromQueue should be used instead
func (*GameServerQueue) PushToQueue ¶ added in v0.4.0
func (h *GameServerQueue) PushToQueue(gs *GameServerForQueue)
PushToQueue pushes a GameServerForHeap onto the heap It should be used instead of heap.Push
func (GameServerQueue) Swap ¶ added in v0.4.0
func (h GameServerQueue) Swap(i, j int)
Swap swaps the GameServerForHeap with index i and the GameServerForHeap with index j
type GameServerQueueForBuild ¶ added in v0.4.0
type GameServerQueueForBuild struct {
// contains filtered or unexported fields
}
GameServerQueueForBuild encapsulates a queue of GameServerForQueue for a specific GameServerBuild also contains a map of all the GameServers for that GameServerBuild
func NewGameServersPerBuildQueue ¶ added in v0.4.0
func NewGameServersPerBuildQueue() *GameServerQueueForBuild
NewGameServersPerBuildQueue returns a new priority queue for a single GameServerBuild
func (*GameServerQueueForBuild) PopFromQueue ¶ added in v0.4.0
func (gsqb *GameServerQueueForBuild) PopFromQueue() *GameServerForQueue
PopFromQueue pops the top GameServerForQueue off the queue
func (*GameServerQueueForBuild) PushToQueue ¶ added in v0.4.0
func (gsqb *GameServerQueueForBuild) PushToQueue(gs *GameServerForQueue)
PushToQueue pushes a GameServerForQueue onto the queue
func (*GameServerQueueForBuild) RemoveFromQueue ¶ added in v0.4.0
func (gsqb *GameServerQueueForBuild) RemoveFromQueue(namespace, name string)
RemoveFromQueue removes a GameServer from the queue based on the provided namespace/name tuple
type GameServerReconciler ¶
type GameServerReconciler struct { client.Client Scheme *k8sruntime.Scheme Recorder record.EventRecorder PortRegistry *PortRegistry InitContainerImageLinux string InitContainerImageWin string GetNodeDetailsProvider func(ctx context.Context, r client.Reader, nodeName string) (string, string, int, error) // we abstract this for testing purposes }
GameServerReconciler reconciles a GameServer object
func NewGameServerReconciler ¶ added in v0.6.0
func NewGameServerReconciler(mgr manager.Manager, portRegistry *PortRegistry, getNodeDetailsProvider func(ctx context.Context, r client.Reader, nodeName string) (string, string, int, error), initContainerImageLinux string, initContainerImageWin string) *GameServerReconciler
NewGameServerReconciler returns a pointer to a new GameServerReconciler
func (*GameServerReconciler) Reconcile ¶
func (r *GameServerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error)
Reconcile is part of the main kubernetes reconciliation loop which aims to move the current state of the cluster closer to the desired state. For more details, check Reconcile and its Result here: - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile
func (*GameServerReconciler) SetupWithManager ¶
func (r *GameServerReconciler) SetupWithManager(mgr ctrl.Manager) error
SetupWithManager sets up the controller with the Manager.
type GameServersQueue ¶ added in v0.4.0
type GameServersQueue struct {
// contains filtered or unexported fields
}
GameServersQueue encapsulates a map of GameServersPerBuildHeaps essentially a set of PriorityQueues, one for each GameServerBuild
func NewGameServersQueue ¶ added in v0.4.0
func NewGameServersQueue() *GameServersQueue
NewGameServersQueue returns a new GameServersQueue
func (*GameServersQueue) PopFromQueue ¶ added in v0.4.0
func (gsq *GameServersQueue) PopFromQueue(buildID string) *GameServerForQueue
PopFromQueue pops the top GameServerForHeap off the queue
func (*GameServersQueue) PushToQueue ¶ added in v0.4.0
func (gsq *GameServersQueue) PushToQueue(gs *GameServerForQueue)
PushToQueue pushes a GameServerForQueue onto the queue
func (*GameServersQueue) RemoveFromQueue ¶ added in v0.4.0
func (gsq *GameServersQueue) RemoveFromQueue(namespace, name string)
RemoveFromQueue removes a GameServer from the queue based on the provided namespace/name tuple
type MutexMap ¶ added in v0.4.0
type MutexMap struct {
// contains filtered or unexported fields
}
Simple async map implementation using a mutex used to manage the expected GameServer creations and deletions
type PortRegistry ¶
type PortRegistry struct { HostPortsUsage map[int32]int // Number of times each HostPort in the [Min,Max] range is used HostPortsPerGameServer map[string][]int32 // Map of GameServer namespace/names to the list of ports that are assigned to it NodeCount int // the number of Ready and Schedulable nodes in the cluster Min int32 // Minimum Port Max int32 // Maximum Port FreePortsCount int // the number of free ports. Originally it equals [Min,Max] * NodeCount // contains filtered or unexported fields }
PortRegistry implements a custom map for the port registry
func NewPortRegistry ¶
func NewPortRegistry(client client.Client, gameServers *mpsv1alpha1.GameServerList, min, max int32, nodeCount int, useSpecificNodePool bool, setupLog logr.Logger) (*PortRegistry, error)
NewPortRegistry initializes the map[port]counter that holds the port registry The way that this works is the following: We keep a map (HostPortsUsage) of all the port numbers every time a new port is requested, we check if the counter for this port is less than the number of Nodes if it is, we increase it by one. If not, we check the next port. the nextPortNumber facilitates getting the next port (port+1), since getting the same port again would cause the GameServer Pod to be placed on a different Node, to avoid collision. This would have a negative impact in cases where we want as many GameServers as possible on the same Node. We also set up a Kubernetes Watch for the Nodes so that when a new Node is added or removed to the cluster, we modify the NodeCount variable
func (*PortRegistry) DeregisterPorts ¶ added in v0.6.0
func (pr *PortRegistry) DeregisterPorts(namespace, name string) ([]int32, error)
DeregisterPorts deregisters all host ports so they can be re-used by additional game servers
func (*PortRegistry) GetNewPorts ¶ added in v0.6.0
func (pr *PortRegistry) GetNewPorts(namespace, name string, count int) ([]int32, error)
GetNewPorts returns and registers a slice of ports with "count" length that will be used by a GameServer It returns an error if there are no available ports You may wonder what happens if two GameServer Pods get assigned the same HostPort We will not have a collision, since Kubernetes is pretty smart and will place the Pod on a different Node, to prevent it
func (*PortRegistry) Reconcile ¶ added in v0.3.0
Reconcile runs when a Node is created/deleted or the node status changes
func (*PortRegistry) SetupWithManager ¶ added in v0.3.0
func (pr *PortRegistry) SetupWithManager(mgr ctrl.Manager) error
SetupWithManager registers the PortRegistry controller with the manager we care to watch for changes in the Node objects, only if they are "Ready" and "Schedulable"
type RequestMultiplayerServerResponse ¶ added in v0.4.0
RequestMultiplayerServerResponse contains details that are returned on a successful GameServer allocation call