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

Passthrough autopilot - added ports array case and updated unit tests #3842

Merged
19 changes: 19 additions & 0 deletions pkg/apis/agones/v1/gameserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ const (
// NodePodIP identifies an IP address from a pod.
NodePodIP corev1.NodeAddressType = "PodIP"

// PassthroughPortAssignmentAnnotation is an annotation to keep track of game server container and its Passthrough ports indices
PassthroughPortAssignmentAnnotation = "agones.dev/container-passthrough-port-assignment"

// True is the string "true" to appease the goconst lint.
True = "true"
// False is the string "false" to appease the goconst lint.
Expand Down Expand Up @@ -738,8 +741,11 @@ func (gs *GameServer) Pod(apiHooks APIHooks, sidecars ...corev1.Container) (*cor
}

gs.podObjectMeta(pod)

passthroughContainerPortMap := make(map[string][]int)
for _, p := range gs.Spec.Ports {
var hostPort int32
portIdx := 0

if !runtime.FeatureEnabled(runtime.FeaturePortPolicyNone) || p.PortPolicy != None {
hostPort = p.HostPort
Expand All @@ -751,14 +757,27 @@ func (gs *GameServer) Pod(apiHooks APIHooks, sidecars ...corev1.Container) (*cor
Protocol: p.Protocol,
}
err := gs.ApplyToPodContainer(pod, *p.Container, func(c corev1.Container) corev1.Container {
portIdx = len(c.Ports)
c.Ports = append(c.Ports, cp)

return c
})
if err != nil {
return nil, err
}
if runtime.FeatureEnabled(runtime.FeatureAutopilotPassthroughPort) && p.PortPolicy == Passthrough {
passthroughContainerPortMap[*p.Container] = append(passthroughContainerPortMap[*p.Container], portIdx)
}
}

if len(passthroughContainerPortMap) != 0 {
containerToPassthroughMapJSON, err := json.Marshal(passthroughContainerPortMap)
if err != nil {
return nil, err
}
pod.ObjectMeta.Annotations[PassthroughPortAssignmentAnnotation] = string(containerToPassthroughMapJSON)
}

// Put the sidecars at the start of the list of containers so that the kubelet starts them first.
containers := make([]corev1.Container, 0, len(sidecars)+len(pod.Spec.Containers))
containers = append(containers, sidecars...)
Expand Down
49 changes: 49 additions & 0 deletions pkg/apis/agones/v1/gameserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,55 @@ func TestGameServerDisableServiceAccount(t *testing.T) {
assert.Equal(t, "/var/run/secrets/kubernetes.io/serviceaccount", pod.Spec.Containers[0].VolumeMounts[0].MountPath)
}

func TestGameServerPassthroughPortAnnotation(t *testing.T) {
t.Parallel()
runtime.FeatureTestMutex.Lock()
defer runtime.FeatureTestMutex.Unlock()
require.NoError(t, runtime.ParseFeatures(string(runtime.FeatureAutopilotPassthroughPort)+"=true"))
containerOne := "containerOne"
containerTwo := "containerTwo"
containerThree := "containerThree"
containerFour := "containerFour"
gs := &GameServer{ObjectMeta: metav1.ObjectMeta{Name: "gameserver", UID: "1234"}, Spec: GameServerSpec{
Container: "containerOne",
Ports: []GameServerPort{
{Name: "defaultDynamicOne", PortPolicy: Dynamic, ContainerPort: 7659, Container: &containerOne},
{Name: "defaultPassthroughOne", PortPolicy: Passthrough, Container: &containerOne},
{Name: "defaultPassthroughTwo", PortPolicy: Passthrough, Container: &containerTwo},
{Name: "defaultDynamicTwo", PortPolicy: Dynamic, ContainerPort: 7654, Container: &containerTwo},
{Name: "defaultDynamicThree", PortPolicy: Dynamic, ContainerPort: 7660, Container: &containerThree},
{Name: "defaultDynamicThree", PortPolicy: Dynamic, ContainerPort: 7661, Container: &containerThree},
{Name: "defaultDynamicThree", PortPolicy: Dynamic, ContainerPort: 7662, Container: &containerThree},
{Name: "defaulPassthroughThree", PortPolicy: Passthrough, Container: &containerThree},
{Name: "defaultPassthroughFour", PortPolicy: Passthrough, Container: &containerFour},
},
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{
vicentefb marked this conversation as resolved.
Show resolved Hide resolved
Containers: []corev1.Container{
{Name: "containerOne", Image: "container/image"},
{Name: "containerTwo", Image: "container/image"},
{Name: "containerThree", Image: "container/image"},
{Name: "containerFour", Image: "container/image"}},
},
},
}}

passthroughContainerPortMap := "{\"containerFour\":[0],\"containerOne\":[1],\"containerThree\":[3],\"containerTwo\":[0]}"

gs.ApplyDefaults()
pod, err := gs.Pod(fakeAPIHooks{})
assert.NoError(t, err)
assert.Len(t, pod.Spec.Containers, 4)
assert.Empty(t, pod.Spec.Containers[0].VolumeMounts)
assert.Equal(t, pod.ObjectMeta.Annotations[PassthroughPortAssignmentAnnotation], passthroughContainerPortMap)

err = gs.DisableServiceAccount(pod)
assert.NoError(t, err)
assert.Len(t, pod.Spec.Containers, 4)
assert.Len(t, pod.Spec.Containers[0].VolumeMounts, 1)
assert.Equal(t, "/var/run/secrets/kubernetes.io/serviceaccount", pod.Spec.Containers[0].VolumeMounts[0].MountPath)
}

func TestGameServerCountPorts(t *testing.T) {
fixture := &GameServer{Spec: GameServerSpec{Ports: []GameServerPort{
{PortPolicy: Dynamic},
Expand Down
18 changes: 16 additions & 2 deletions pkg/gameservers/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,22 @@ func (ext *Extensions) creationMutationHandlerPod(review admissionv1.AdmissionRe

ext.baseLogger.WithField("pod.Name", pod.Name).Debug("creationMutationHandlerPod")

// TODO: We need to deal with case of multiple and mixed type ports before enabling the feature gate.
pod.Spec.Containers[1].Ports[0].ContainerPort = pod.Spec.Containers[1].Ports[0].HostPort
annotation, ok := pod.ObjectMeta.Annotations[agonesv1.PassthroughPortAssignmentAnnotation]
if !ok {
ext.baseLogger.WithField("pod.Name", pod.Name).Info("the agones.dev/container-passthrough-port-assignment annotation is empty and it's unexpected")
return review, nil
vicentefb marked this conversation as resolved.
Show resolved Hide resolved
}

passthroughPortAssignmentMap := make(map[string][]int)
if err := json.Unmarshal([]byte(annotation), &passthroughPortAssignmentMap); err != nil {
return review, errors.Wrapf(err, "could not unmarshal annotation %q (value %q)", passthroughPortAssignmentMap, annotation)
}

for _, container := range pod.Spec.Containers {
for _, portIdx := range passthroughPortAssignmentMap[container.Name] {
container.Ports[portIdx].ContainerPort = container.Ports[portIdx].HostPort
}
}

newPod, err := json.Marshal(pod)
if err != nil {
Expand Down
47 changes: 34 additions & 13 deletions pkg/gameservers/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,10 @@ func TestControllerCreationMutationHandlerPod(t *testing.T) {
}

t.Run("valid pod mutation for Passthrough portPolicy, containerPort should be the same as hostPort", func(t *testing.T) {
gameServerHostPort := float64(newPassthroughPortSingleContainerSpec().Containers[1].Ports[0].HostPort)
fixture := &corev1.Pod{Spec: newPassthroughPortSingleContainerSpec()}
gameServerHostPort0 := float64(newPassthroughPortSingleContainerSpec().Spec.Containers[1].Ports[0].HostPort)
gameServerHostPort2 := float64(newPassthroughPortSingleContainerSpec().Spec.Containers[1].Ports[1].HostPort)
gameServerHostPort3 := float64(newPassthroughPortSingleContainerSpec().Spec.Containers[2].Ports[0].HostPort)
fixture := newPassthroughPortSingleContainerSpec()
raw, err := json.Marshal(fixture)
require.NoError(t, err)
review := admissionv1.AdmissionReview{
Expand All @@ -606,7 +608,9 @@ func TestControllerCreationMutationHandlerPod(t *testing.T) {
}
expected := expected{
patches: []jsonpatch.JsonPatchOperation{
{Operation: "replace", Path: "/spec/containers/1/ports/0/containerPort", Value: gameServerHostPort}},
{Operation: "replace", Path: "/spec/containers/1/ports/0/containerPort", Value: gameServerHostPort0},
{Operation: "replace", Path: "/spec/containers/1/ports/1/containerPort", Value: gameServerHostPort2},
{Operation: "replace", Path: "/spec/containers/2/ports/0/containerPort", Value: gameServerHostPort3}},
}

result, err := ext.creationMutationHandlerPod(review)
Expand Down Expand Up @@ -2275,15 +2279,32 @@ func newSingleContainerSpec() agonesv1.GameServerSpec {
}
}

func newPassthroughPortSingleContainerSpec() corev1.PodSpec {
return corev1.PodSpec{
Containers: []corev1.Container{
{Name: "agones-gameserver-sidecar",
Image: "container/image",
Env: []corev1.EnvVar{{Name: passthroughPortEnvVar, Value: "TRUE"}}},
{Name: "simple-game-server",
Image: "container2/image",
Ports: []corev1.ContainerPort{{HostPort: 7777, ContainerPort: 555}},
Env: []corev1.EnvVar{{Name: passthroughPortEnvVar, Value: "TRUE"}}}},
// Assume container ports 0 and 1 are Passthrough ports for "example-server" container and container port 0 for "example-server-two"
// The annotation would look like autopilot.gke.io/passthrough-port-assignment: '{"example-server":["0","1"], "example-server-two":[0]}'
func newPassthroughPortSingleContainerSpec() corev1.Pod {
passthroughContainerPortMap := "{\"example-server\":[0,1],\"example-server-two\":[0]}"
return corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{agonesv1.PassthroughPortAssignmentAnnotation: passthroughContainerPortMap},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
vicentefb marked this conversation as resolved.
Show resolved Hide resolved
{Name: "agones-gameserver-sidecar",
Image: "container/image",
Env: []corev1.EnvVar{{Name: passthroughPortEnvVar, Value: "TRUE"}}},
{Name: "example-server",
Image: "container2/image",
Ports: []corev1.ContainerPort{
{HostPort: 7777, ContainerPort: 5555},
{HostPort: 7776, ContainerPort: 7797},
{HostPort: 7775, ContainerPort: 7793}},
Env: []corev1.EnvVar{{Name: passthroughPortEnvVar, Value: "TRUE"}}},
{Name: "example-server-two",
Image: "container3/image",
Ports: []corev1.ContainerPort{
{HostPort: 7745, ContainerPort: 7983},
{HostPort: 7312, ContainerPort: 7364}},
Env: []corev1.EnvVar{{Name: passthroughPortEnvVar, Value: "TRUE"}}}},
},
}
}
Loading