diff --git a/install/helm/agones/defaultfeaturegates.yaml b/install/helm/agones/defaultfeaturegates.yaml index 09997cc46c..3d767ba9f2 100644 --- a/install/helm/agones/defaultfeaturegates.yaml +++ b/install/helm/agones/defaultfeaturegates.yaml @@ -24,6 +24,7 @@ PlayerAllocationFilter: false PlayerTracking: false # Dev features +FeatureAutopilotPassthroughPort: true # Example feature Example: false diff --git a/pkg/apis/agones/v1/gameserver.go b/pkg/apis/agones/v1/gameserver.go index 7b629fcfac..ff944d05e4 100644 --- a/pkg/apis/agones/v1/gameserver.go +++ b/pkg/apis/agones/v1/gameserver.go @@ -123,6 +123,9 @@ const ( // GameServerPodLabel is the label that the name of the GameServer // is set on the Pod the GameServer controls GameServerPodLabel = agones.GroupName + "/gameserver" + // GameServerPortPolicyPodLabel is the label to identify the port policy + // of the pod + GameServerPortPolicyPodLabel = agones.GroupName + "/port" // GameServerContainerAnnotation is the annotation that stores // which container is the container that runs the dedicated game server GameServerContainerAnnotation = agones.GroupName + "/container" diff --git a/pkg/cloudproduct/gke/gke.go b/pkg/cloudproduct/gke/gke.go index 25d0c7921a..a598db0f1f 100644 --- a/pkg/cloudproduct/gke/gke.go +++ b/pkg/cloudproduct/gke/gke.go @@ -155,11 +155,21 @@ func (*gkeAutopilot) ValidateScheduling(ss apis.SchedulingStrategy, fldPath *fie } func (*gkeAutopilot) MutateGameServerPod(gss *agonesv1.GameServerSpec, pod *corev1.Pod) error { + setPassthroughLabel(gss, pod) setPrimaryContainer(pod, gss.Container) podSpecSeccompUnconfined(&pod.Spec) return nil } +// setPassthroughLabel sets the agones.dev/port: "autopilot-passthrough" label to the game server container. +// This will help to back the container port from the allocated port using an objectSelector of this label +// in GameServers that are using Passthrough Port Policy +func setPassthroughLabel(gs *agonesv1.GameServerSpec, pod *corev1.Pod) { + if runtime.FeatureEnabled(runtime.FeatureAutopilotPassthroughPort) && hasPortPolicy(gs, agonesv1.Passthrough) { + pod.ObjectMeta.Labels[agonesv1.GameServerPortPolicyPodLabel] = "autopilot-passthrough" + } +} + // setPrimaryContainer sets the autopilot.gke.io/primary-container annotation to the game server container. // This acts as a hint to Autopilot for which container to add resources to during resource adjustment. // See https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-resource-requests#autopilot-resource-management @@ -223,6 +233,15 @@ func setEvictionNoExtended(ev *agonesv1.Eviction, pod *corev1.Pod) error { return nil } +func hasPortPolicy(gs *agonesv1.GameServerSpec, portPolicy agonesv1.PortPolicy) bool { + for _, p := range gs.Ports { + if p.PortPolicy == portPolicy { + return true + } + } + return false +} + type autopilotPortAllocator struct { minPort int32 maxPort int32 diff --git a/pkg/cloudproduct/gke/gke_test.go b/pkg/cloudproduct/gke/gke_test.go index 622eaae9c3..8b90e49702 100644 --- a/pkg/cloudproduct/gke/gke_test.go +++ b/pkg/cloudproduct/gke/gke_test.go @@ -14,10 +14,12 @@ package gke import ( + "fmt" "testing" "agones.dev/agones/pkg/apis" agonesv1 "agones.dev/agones/pkg/apis/agones/v1" + "agones.dev/agones/pkg/util/runtime" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" @@ -222,6 +224,100 @@ func TestPodSeccompUnconfined(t *testing.T) { } } +func TestSetPassthroughLabel(t *testing.T) { + for name, tc := range map[string]struct { + pod *corev1.Pod + wantPod *corev1.Pod + ports []agonesv1.GameServerPort + features string + }{ + "gameserver with with Passthrough port policy adds label to pod": { + features: fmt.Sprintf("%s=true", runtime.FeatureAutopilotPassthroughPort), + + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + }, + ports: []agonesv1.GameServerPort{ + { + Name: "awesome-udp", + PortPolicy: agonesv1.Passthrough, + ContainerPort: 1234, + Protocol: corev1.ProtocolUDP, + }, + }, + wantPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + Labels: map[string]string{ + agonesv1.GameServerPortPolicyPodLabel: "autopilot-passthrough", + }, + }, + }, + }, + "gameserver with Static port policy does not add label to pod": { + features: fmt.Sprintf("%s=true", runtime.FeatureAutopilotPassthroughPort), + + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + }, + ports: []agonesv1.GameServerPort{ + { + Name: "awesome-udp", + PortPolicy: agonesv1.Static, + ContainerPort: 1234, + Protocol: corev1.ProtocolUDP, + }, + }, + wantPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + }, + }, + "gameserver, no feature gate, with Passthrough port policy does not add label to pod": { + features: fmt.Sprintf("%s=false", runtime.FeatureAutopilotPassthroughPort), + + pod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + }, + ports: []agonesv1.GameServerPort{ + { + Name: "awesome-udp", + PortPolicy: agonesv1.Passthrough, + ContainerPort: 1234, + Protocol: corev1.ProtocolUDP, + }, + }, + wantPod: &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{}, + Labels: map[string]string{}, + }, + }, + }, + } { + t.Run(name, func(t *testing.T) { + runtime.FeatureTestMutex.Lock() + defer runtime.FeatureTestMutex.Unlock() + require.NoError(t, runtime.ParseFeatures(tc.features)) + gs := (&autopilotPortAllocator{minPort: 7000, maxPort: 8000}).Allocate(&agonesv1.GameServer{Spec: agonesv1.GameServerSpec{Ports: tc.ports}}) + pod := tc.pod.DeepCopy() + setPassthroughLabel(&gs.Spec, pod) + assert.Equal(t, tc.wantPod, pod) + }) + } +} + func TestSetEvictionNoExtended(t *testing.T) { emptyPodAnd := func(f func(*corev1.Pod)) *corev1.Pod { pod := &corev1.Pod{ diff --git a/pkg/util/runtime/features.go b/pkg/util/runtime/features.go index f4a3e56037..486d64ea50 100644 --- a/pkg/util/runtime/features.go +++ b/pkg/util/runtime/features.go @@ -58,6 +58,9 @@ const ( //////////////// // Dev features + // FeatureAutopilotPassthroughPort is a feature flag that enables/disables Passthrough Port Policy. + FeatureAutopilotPassthroughPort Feature = "PassthroughPortPolicy" + //////////////// // Example feature @@ -109,6 +112,7 @@ var ( FeaturePlayerTracking: false, // Dev features + FeatureAutopilotPassthroughPort: true, // Example feature FeatureExample: false,