package inject import ( "testing" appmesh "github.com/aws/aws-app-mesh-controller-for-k8s/apis/appmesh/v1beta2" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func Test_proxyMutator_mutate(t *testing.T) { cpuRequests, _ := resource.ParseQuantity("32Mi") memoryRequests, _ := resource.ParseQuantity("10m") vn := &appmesh.VirtualNode{ Spec: appmesh.VirtualNodeSpec{ Listeners: []appmesh.Listener{ { PortMapping: appmesh.PortMapping{ Port: 80, Protocol: "http", }, }, { PortMapping: appmesh.PortMapping{ Port: 443, Protocol: "http", }, }, }, }, } vnWithoutListener := &appmesh.VirtualNode{ Spec: appmesh.VirtualNodeSpec{}, } mutatorConfig := proxyMutatorConfig{ initProxyMutatorConfig: initProxyMutatorConfig{ containerImage: "840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v7-prod", cpuRequests: cpuRequests.String(), memoryRequests: memoryRequests.String(), }, egressIgnoredIPs: "192.168.0.1", } type fields struct { mutatorConfig proxyMutatorConfig vn *appmesh.VirtualNode } type args struct { pod *corev1.Pod } tests := []struct { name string fields fields args args wantPod *corev1.Pod wantErr error }{ { name: "mutate using init container", fields: fields{ mutatorConfig: mutatorConfig, vn: vn, }, args: args{ pod: &corev1.Pod{}, }, wantPod: &corev1.Pod{ Spec: corev1.PodSpec{ InitContainers: []corev1.Container{ { Name: "proxyinit", Image: "840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v7-prod", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{ "NET_ADMIN", }, }, }, Env: []corev1.EnvVar{ { Name: "APPMESH_START_ENABLED", Value: "1", }, { Name: "APPMESH_IGNORE_UID", Value: "1337", }, { Name: "APPMESH_ENVOY_INGRESS_PORT", Value: "15000", }, { Name: "APPMESH_ENVOY_EGRESS_PORT", Value: "15001", }, { Name: "APPMESH_APP_PORTS", Value: "80,443", }, { Name: "APPMESH_EGRESS_IGNORED_IP", Value: "192.168.0.1", }, { Name: "APPMESH_EGRESS_IGNORED_PORTS", Value: "22", }, { Name: "APPMESH_ENABLE_IPV6", Value: "1", }, }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ "cpu": cpuRequests, "memory": memoryRequests, }, }, }, }, }, }, }, { name: "mutate using init container with no listener", fields: fields{ mutatorConfig: mutatorConfig, vn: vnWithoutListener, }, args: args{ pod: &corev1.Pod{}, }, wantPod: &corev1.Pod{ Spec: corev1.PodSpec{ InitContainers: []corev1.Container{ { Name: "proxyinit", Image: "840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v7-prod", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{ "NET_ADMIN", }, }, }, Env: []corev1.EnvVar{ { Name: "APPMESH_START_ENABLED", Value: "1", }, { Name: "APPMESH_IGNORE_UID", Value: "1337", }, { Name: "APPMESH_ENVOY_INGRESS_PORT", Value: "15000", }, { Name: "APPMESH_ENVOY_EGRESS_PORT", Value: "15001", }, { Name: "APPMESH_APP_PORTS", Value: "", }, { Name: "APPMESH_EGRESS_IGNORED_IP", Value: "192.168.0.1", }, { Name: "APPMESH_EGRESS_IGNORED_PORTS", Value: "22", }, { Name: "APPMESH_ENABLE_IPV6", Value: "1", }, }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ "cpu": cpuRequests, "memory": memoryRequests, }, }, }, }, }, }, }, { name: "mutate using init container with ipv6 disabled annotation", fields: fields{ mutatorConfig: mutatorConfig, vn: vnWithoutListener, }, args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/ipv6": "disabled", }, }, }, }, wantPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/ipv6": "disabled", }, }, Spec: corev1.PodSpec{ InitContainers: []corev1.Container{ { Name: "proxyinit", Image: "840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v7-prod", SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{ "NET_ADMIN", }, }, }, Env: []corev1.EnvVar{ { Name: "APPMESH_START_ENABLED", Value: "1", }, { Name: "APPMESH_IGNORE_UID", Value: "1337", }, { Name: "APPMESH_ENVOY_INGRESS_PORT", Value: "15000", }, { Name: "APPMESH_ENVOY_EGRESS_PORT", Value: "15001", }, { Name: "APPMESH_APP_PORTS", Value: "", }, { Name: "APPMESH_EGRESS_IGNORED_IP", Value: "192.168.0.1", }, { Name: "APPMESH_EGRESS_IGNORED_PORTS", Value: "22", }, { Name: "APPMESH_ENABLE_IPV6", Value: "0", }, }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ "cpu": cpuRequests, "memory": memoryRequests, }, }, }, }, }, }, }, { name: "mutate using appMesh CNI", fields: fields{ mutatorConfig: mutatorConfig, vn: vn, }, args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/appmeshCNI": "enabled", }, }, }, }, wantPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/appmeshCNI": "enabled", "appmesh.k8s.aws/ports": "80,443", "appmesh.k8s.aws/egressIgnoredIPs": "192.168.0.1", "appmesh.k8s.aws/egressIgnoredPorts": "22", "appmesh.k8s.aws/proxyEgressPort": "15001", "appmesh.k8s.aws/proxyIngressPort": "15000", "appmesh.k8s.aws/ignoredUID": "1337", "appmesh.k8s.aws/sidecarInjectorWebhook": "enabled", }, }, }, }, { name: "mutate using appMesh CNI with no listeners", fields: fields{ mutatorConfig: mutatorConfig, vn: vnWithoutListener, }, args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/appmeshCNI": "enabled", }, }, }, }, wantPod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/appmeshCNI": "enabled", "appmesh.k8s.aws/ports": "", "appmesh.k8s.aws/egressIgnoredIPs": "192.168.0.1", "appmesh.k8s.aws/egressIgnoredPorts": "22", "appmesh.k8s.aws/proxyEgressPort": "15001", "appmesh.k8s.aws/proxyIngressPort": "15000", "appmesh.k8s.aws/ignoredUID": "1337", "appmesh.k8s.aws/sidecarInjectorWebhook": "enabled", }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &proxyMutator{ mutatorConfig: tt.fields.mutatorConfig, vn: tt.fields.vn, } err := m.mutate(tt.args.pod) if tt.wantErr != nil { assert.EqualError(t, err, tt.wantErr.Error()) } else { assert.NoError(t, err) assert.True(t, cmp.Equal(tt.wantPod, tt.args.pod), "diff", cmp.Diff(tt.wantPod, tt.args.pod)) } }) } } func Test_proxyMutator_getAppPorts(t *testing.T) { type fields struct { vn *appmesh.VirtualNode } type args struct { pod *corev1.Pod } tests := []struct { name string fields fields args args want string }{ { name: "get AppPorts from annotation", fields: fields{ vn: &appmesh.VirtualNode{ Spec: appmesh.VirtualNodeSpec{ Listeners: []appmesh.Listener{ { PortMapping: appmesh.PortMapping{ Port: 80, Protocol: "http", }, }, { PortMapping: appmesh.PortMapping{ Port: 443, Protocol: "http", }, }, }, }, }, }, args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/ports": "8080", }, }, }, }, want: "8080", }, { name: "get AppPorts from VirtualNode with multiple listener", fields: fields{ vn: &appmesh.VirtualNode{ Spec: appmesh.VirtualNodeSpec{ Listeners: []appmesh.Listener{ { PortMapping: appmesh.PortMapping{ Port: 80, Protocol: "http", }, }, { PortMapping: appmesh.PortMapping{ Port: 443, Protocol: "http", }, }, }, }, }, }, args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, }, }, want: "80,443", }, { name: "get AppPorts from VirtualNode with no listener", fields: fields{ vn: &appmesh.VirtualNode{ Spec: appmesh.VirtualNodeSpec{ Listeners: nil, }, }, }, args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, }, }, want: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &proxyMutator{ vn: tt.fields.vn, } got := m.getAppPorts(tt.args.pod) assert.Equal(t, tt.want, got) }) } } func Test_proxyMutator_getEgressIgnoredPorts(t *testing.T) { type args struct { pod *corev1.Pod } tests := []struct { name string args args want string }{ { name: "get EgressIgnoredPorts from annotation", args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/egressIgnoredPorts": "8443,9090", }, }, }, }, want: "8443,9090", }, { name: "get EgressIgnoredPorts by default", args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, }, }, want: "22", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &proxyMutator{} got := m.getEgressIgnoredPorts(tt.args.pod) assert.Equal(t, tt.want, got) }) } } func Test_proxyMutator_isAppMeshCNIEnabled(t *testing.T) { type args struct { pod *corev1.Pod } tests := []struct { name string args args want bool }{ { name: "cni is enabled when annotation appmesh.k8s.aws/appmeshCNI: enabled presents", args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/appmeshCNI": "enabled", }, }, }, }, want: true, }, { name: "cni is enabled when fargate label presents", args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ "eks.amazonaws.com/fargate-profile": "my-fp", }, }, }, }, want: true, }, { name: "cni is not enabled", args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{}, }, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &proxyMutator{} got := m.isAppMeshCNIEnabled(tt.args.pod) assert.Equal(t, tt.want, got) }) } } func Test_proxyMutator_isIPV6Enabled(t *testing.T) { type args struct { pod *corev1.Pod } tests := []struct { name string args args want bool }{ { name: "IPV6 disabled from annotation", args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{ "appmesh.k8s.aws/ipv6": "disabled", }, }, }, }, want: false, }, { name: "no ipv6 annotation", args: args{ pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Annotations: map[string]string{}, }, }, }, want: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &proxyMutator{} got := m.isIPV6Enabled(tt.args.pod) assert.Equal(t, tt.want, got) }) } }