agones.dev/agones@v1.53.0/pkg/apis/agones/v1/common.go (about)

     1  // Copyright 2019 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package v1
    16  
    17  import (
    18  	"math"
    19  
    20  	apivalidation "k8s.io/apimachinery/pkg/api/validation"
    21  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    22  	metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
    23  	"k8s.io/apimachinery/pkg/labels"
    24  	"k8s.io/apimachinery/pkg/runtime/schema"
    25  	"k8s.io/apimachinery/pkg/util/validation"
    26  	"k8s.io/apimachinery/pkg/util/validation/field"
    27  )
    28  
    29  // Block of const Error messages and GameServerAllocation Counter actions
    30  const (
    31  	ErrContainerRequired        = "Container is required when using multiple containers in the pod template"
    32  	ErrHostPort                 = "HostPort cannot be specified with a Dynamic or Passthrough PortPolicy"
    33  	ErrPortPolicyStatic         = "PortPolicy must be Static"
    34  	ErrContainerPortRequired    = "ContainerPort must be defined for Dynamic and Static PortPolicies"
    35  	ErrContainerPortPassthrough = "ContainerPort cannot be specified with Passthrough PortPolicy"
    36  	ErrContainerNameInvalid     = "Container must be empty or the name of a container in the pod template"
    37  	// GameServerPriorityIncrement is a Counter Action that indiciates the Counter's Count should be incremented at Allocation.
    38  	GameServerPriorityIncrement string = "Increment"
    39  	// GameServerPriorityDecrement is a Counter Action that indiciates the Counter's Count should be decremented at Allocation.
    40  	GameServerPriorityDecrement string = "Decrement"
    41  	// GameServerPriorityCounter is a Type for sorting Game Servers by Counter
    42  	GameServerPriorityCounter string = "Counter"
    43  	// GameServerPriorityList is a Type for sorting Game Servers by List
    44  	GameServerPriorityList string = "List"
    45  	// GameServerPriorityAscending is a Priority Order where the smaller count is preferred in sorting.
    46  	GameServerPriorityAscending string = "Ascending"
    47  	// GameServerPriorityDescending is a Priority Order where the larger count is preferred in sorting.
    48  	GameServerPriorityDescending string = "Descending"
    49  )
    50  
    51  // AggregatedPlayerStatus stores total player tracking values
    52  type AggregatedPlayerStatus struct {
    53  	Count    int64 `json:"count"`
    54  	Capacity int64 `json:"capacity"`
    55  }
    56  
    57  // AggregatedCounterStatus stores total and allocated Counter tracking values
    58  type AggregatedCounterStatus struct {
    59  	AllocatedCount    int64 `json:"allocatedCount"`
    60  	AllocatedCapacity int64 `json:"allocatedCapacity"`
    61  	Count             int64 `json:"count"`
    62  	Capacity          int64 `json:"capacity"`
    63  }
    64  
    65  // AggregatedListStatus stores total and allocated List tracking values
    66  type AggregatedListStatus struct {
    67  	AllocatedCount    int64 `json:"allocatedCount"`
    68  	AllocatedCapacity int64 `json:"allocatedCapacity"`
    69  	Count             int64 `json:"count"`
    70  	Capacity          int64 `json:"capacity"`
    71  }
    72  
    73  // crd is an interface to get Name and Kind of CRD
    74  type crd interface {
    75  	GetName() string
    76  	GetObjectKind() schema.ObjectKind
    77  }
    78  
    79  // validateName Check NameSize of a CRD
    80  func validateName(c crd, fldPath *field.Path) field.ErrorList {
    81  	var allErrs field.ErrorList
    82  	name := c.GetName()
    83  	// make sure the Name of a Fleet does not oversize the Label size in GSS and GS
    84  	if len(name) > validation.LabelValueMaxLength {
    85  		allErrs = append(allErrs, field.TooLongMaxLength(fldPath.Child("name"), name, 63))
    86  	}
    87  	return allErrs
    88  }
    89  
    90  // gsSpec is an interface which contains all necessary
    91  // functions to perform common validations against it
    92  type gsSpec interface {
    93  	GetGameServerSpec() *GameServerSpec
    94  }
    95  
    96  // validateGSSpec Check GameServerSpec of a CRD
    97  // Used by Fleet and GameServerSet
    98  func validateGSSpec(apiHooks APIHooks, gs gsSpec, fldPath *field.Path) field.ErrorList {
    99  	gsSpec := gs.GetGameServerSpec()
   100  	gsSpec.ApplyDefaults()
   101  	allErrs := gsSpec.Validate(apiHooks, "", fldPath)
   102  	return allErrs
   103  }
   104  
   105  // validateObjectMeta Check ObjectMeta specification
   106  // Used by Fleet, GameServerSet and GameServer
   107  func validateObjectMeta(objMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
   108  	allErrs := metav1validation.ValidateLabels(objMeta.Labels, fldPath.Child("labels"))
   109  	allErrs = append(allErrs, apivalidation.ValidateAnnotations(objMeta.Annotations, fldPath.Child("annotations"))...)
   110  	return allErrs
   111  }
   112  
   113  // AllocationOverflow specifies what labels and/or annotations to apply on Allocated GameServers
   114  // if the desired number of the underlying `GameServerSet` drops below the number of Allocated GameServers
   115  // attached to it.
   116  type AllocationOverflow struct {
   117  	// Labels to be applied to the `GameServer`
   118  	// +optional
   119  	Labels map[string]string `json:"labels,omitempty"`
   120  	// Annotations to be applied to the `GameServer`
   121  	// +optional
   122  	Annotations map[string]string `json:"annotations,omitempty"`
   123  }
   124  
   125  // Validate validates the label and annotation values
   126  func (ao *AllocationOverflow) Validate(fldPath *field.Path) field.ErrorList {
   127  	allErrs := metav1validation.ValidateLabels(ao.Labels, fldPath.Child("labels"))
   128  	allErrs = append(allErrs, apivalidation.ValidateAnnotations(ao.Annotations, fldPath.Child("annotations"))...)
   129  	return allErrs
   130  }
   131  
   132  // CountMatches returns the number of Allocated GameServers that match the labels and annotations, and
   133  // the set of GameServers left over.
   134  func (ao *AllocationOverflow) CountMatches(list []*GameServer) (int32, []*GameServer) {
   135  	count := int32(0)
   136  	var rest []*GameServer
   137  	labelSelector := labels.Set(ao.Labels).AsSelector()
   138  	annotationSelector := labels.Set(ao.Annotations).AsSelector()
   139  
   140  	for _, gs := range list {
   141  		if gs.Status.State != GameServerStateAllocated {
   142  			continue
   143  		}
   144  		if !labelSelector.Matches(labels.Set(gs.ObjectMeta.Labels)) {
   145  			rest = append(rest, gs)
   146  			continue
   147  		}
   148  		if !annotationSelector.Matches(labels.Set(gs.ObjectMeta.Annotations)) {
   149  			rest = append(rest, gs)
   150  			continue
   151  		}
   152  		count++
   153  	}
   154  
   155  	return count, rest
   156  }
   157  
   158  // Apply applies the labels and annotations to the passed in GameServer
   159  func (ao *AllocationOverflow) Apply(gs *GameServer) {
   160  	if ao.Annotations != nil {
   161  		if gs.ObjectMeta.Annotations == nil {
   162  			gs.ObjectMeta.Annotations = map[string]string{}
   163  		}
   164  		for k, v := range ao.Annotations {
   165  			gs.ObjectMeta.Annotations[k] = v
   166  		}
   167  	}
   168  	if ao.Labels != nil {
   169  		if gs.ObjectMeta.Labels == nil {
   170  			gs.ObjectMeta.Labels = map[string]string{}
   171  		}
   172  		for k, v := range ao.Labels {
   173  			gs.ObjectMeta.Labels[k] = v
   174  		}
   175  	}
   176  }
   177  
   178  // Priority is a sorting option for GameServers with Counters or Lists based on the available capacity,
   179  // i.e. the current Capacity value, minus either the Count value or List length.
   180  type Priority struct {
   181  	// Type: Sort by a "Counter" or a "List".
   182  	Type string `json:"type"`
   183  	// Key: The name of the Counter or List. If not found on the GameServer, has no impact.
   184  	Key string `json:"key"`
   185  	// Order: Sort by "Ascending" or "Descending". "Descending" a bigger available capacity is preferred.
   186  	// "Ascending" would be smaller available capacity is preferred.
   187  	// The default sort order is "Ascending"
   188  	Order string `json:"order"`
   189  }
   190  
   191  // SafeAdd prevents overflow by limiting the sum to math.MaxInt64.
   192  func SafeAdd(x, y int64) int64 {
   193  	if x > math.MaxInt64-y {
   194  		return math.MaxInt64
   195  	}
   196  	return x + y
   197  }