diff --git a/.github/workflows/build-oss.yml b/.github/workflows/build-oss.yml index 2191ce13f1..06c821048c 100644 --- a/.github/workflows/build-oss.yml +++ b/.github/workflows/build-oss.yml @@ -147,7 +147,7 @@ jobs: ignore-unfixed: 'true' - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@39d8d7e78f59cf6b40ac3b9fbebef0c753d7c9e5 # v2.2.2 + uses: github/codeql-action/upload-sarif@8775e868027fa230df8586bdf502bbd9b618a477 # v2.2.3 continue-on-error: true with: sarif_file: 'trivy-results-${{ inputs.image }}.sarif' diff --git a/.github/workflows/build-plus.yml b/.github/workflows/build-plus.yml index 062093d03c..1a9b9a174e 100644 --- a/.github/workflows/build-plus.yml +++ b/.github/workflows/build-plus.yml @@ -153,7 +153,7 @@ jobs: ignore-unfixed: 'true' - name: Upload Trivy scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@39d8d7e78f59cf6b40ac3b9fbebef0c753d7c9e5 # v2.2.2 + uses: github/codeql-action/upload-sarif@8775e868027fa230df8586bdf502bbd9b618a477 # v2.2.3 continue-on-error: true with: sarif_file: 'trivy-results-${{ inputs.image }}.sarif' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index efdf462555..40e23ad274 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,7 +36,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@39d8d7e78f59cf6b40ac3b9fbebef0c753d7c9e5 # v2.2.2 + uses: github/codeql-action/init@8775e868027fa230df8586bdf502bbd9b618a477 # v2.2.3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -47,7 +47,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@39d8d7e78f59cf6b40ac3b9fbebef0c753d7c9e5 # v2.2.2 + uses: github/codeql-action/autobuild@8775e868027fa230df8586bdf502bbd9b618a477 # v2.2.3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -61,4 +61,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@39d8d7e78f59cf6b40ac3b9fbebef0c753d7c9e5 # v2.2.2 + uses: github/codeql-action/analyze@8775e868027fa230df8586bdf502bbd9b618a477 # v2.2.3 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 1c61d348f2..5f3fa9cac9 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -53,6 +53,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@39d8d7e78f59cf6b40ac3b9fbebef0c753d7c9e5 # v2.2.2 + uses: github/codeql-action/upload-sarif@8775e868027fa230df8586bdf502bbd9b618a477 # v2.2.3 with: sarif_file: results.sarif diff --git a/build/Dockerfile b/build/Dockerfile index 7204916c36..f1268275ef 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -26,8 +26,8 @@ FROM nginx:1.23.3-alpine AS alpine RUN --mount=type=bind,from=alpine-opentracing-lib,target=/tmp/ot/ \ apk add --no-cache libcap libstdc++ \ - # temp fix for CVE-2022-44617 - && apk upgrade --no-cache libxpm \ + # temp fix for CVE-2022-44617 and CVE-2023-0286 + && apk upgrade --no-cache libxpm libssl3 libcrypto3 \ && cp -av /tmp/ot/usr/local/lib/libopentracing.so* /tmp/ot/usr/local/lib/libjaegertracing*so* /tmp/ot/usr/local/lib/libzipkin*so* /tmp/ot/usr/local/lib/libdd*so* /tmp/ot/usr/local/lib/libyaml*so* /usr/local/lib/ \ && cp -av /tmp/ot/usr/lib/nginx/modules/ngx_http_opentracing_module.so /usr/lib/nginx/modules/ \ && ldconfig /usr/local/lib/ @@ -43,6 +43,8 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/apk/cert.pem,mode=0644 \ wget -nv -O /etc/apk/keys/nginx_signing.rsa.pub https://cs.nginx.com/static/keys/nginx_signing.rsa.pub \ && printf "%s\n" "https://pkgs.nginx.com/plus/${NGINX_PLUS_VERSION}/alpine/v$(grep -E -o '^[0-9]+\.[0-9]+' /etc/alpine-release)/main" >> /etc/apk/repositories \ && apk add --no-cache libcap nginx-plus nginx-plus-module-njs nginx-plus-module-opentracing libcurl \ + # temp fix for CVE-2023-0286 + && apk upgrade --no-cache libssl3 libcrypto3 \ && cp -av /tmp/ot/usr/local/lib/libjaegertracing*so* /tmp/ot/usr/local/lib/libzipkin*so* /tmp/ot/usr/local/lib/libdd*so* /tmp/ot/usr/local/lib/libyaml*so* /usr/local/lib/ \ && ldconfig /usr/local/lib/ @@ -122,25 +124,24 @@ COPY --link --chown=101:0 LICENSE /licenses/ ############################################# Base image for UBI with NGINX Plus ############################################# -FROM redhat/ubi8:8.6 AS ubi-plus +FROM redhat/ubi9-minimal AS ubi-plus ARG NGINX_PLUS_VERSION SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode=0644 \ --mount=type=secret,id=nginx-repo.key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ - dnf --nodocs install -y shadow-utils ca-certificates \ - # temp fix for CVE-2022-1304 CVE-2016-3709, CVE-2022-42898, CVE-2022-42010, CVE-2022-43680, CVE-2022-3821, CVE-2021-46848 and CVE-2022-35737 - && dnf --nodocs upgrade -y libcom_err libxml2 krb5-libs dbus expat systemd libtasn1 sqlite-libs \ + microdnf --nodocs install -y shadow-utils \ && groupadd --system --gid 101 nginx \ && useradd --system --gid nginx --no-create-home --home-dir /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \ && rpm --import https://cs.nginx.com/static/keys/nginx_signing.key \ - && curl -fsSL "https://cs.nginx.com/static/files/nginx-plus-$(grep -E -o '[0-9]+\.[0-9]+' /etc/redhat-release | cut -d"." -f1).repo" | tr 0 1 > /etc/yum.repos.d/nginx-plus.repo \ + && curl -fsSL "https://cs.nginx.com/static/files/plus-$(grep -E -o '[0-9]+\.[0-9]+' /etc/redhat-release | cut -d"." -f1).repo" | tr 0 1 > /etc/yum.repos.d/nginx-plus.repo \ && sed -i "0,/centos/s;;${NGINX_PLUS_VERSION}/centos;" /etc/yum.repos.d/nginx-plus.repo \ - && dnf --nodocs install -y nginx-plus nginx-plus-module-njs - + && microdnf --nodocs install -y nginx-plus nginx-plus-module-njs \ + && microdnf remove -y shadow-utils \ + && microdnf clean all ############################################# Base image for UBI with NGINX Plus and App Protect WAF/DoS ############################################# -FROM ubi-plus as ubi-plus-nap +FROM redhat/ubi8:8.6 as ubi-plus-nap ARG NGINX_PLUS_VERSION ARG NAP_MODULES @@ -148,6 +149,17 @@ RUN --mount=type=secret,id=nginx-repo.crt,dst=/etc/ssl/nginx/nginx-repo.crt,mode --mount=type=secret,id=nginx-repo.key,dst=/etc/ssl/nginx/nginx-repo.key,mode=0644 \ --mount=type=secret,id=rhel_license,dst=/tmp/rhel_license,mode=0644 \ source /tmp/rhel_license \ + ## the code below is duplicated from the ubi-plus image because NAP doesn't support UBI versions newer than 8.6 + dnf --nodocs install -y shadow-utils ca-certificates \ + # temp fix for CVE-2022-1304 CVE-2016-3709, CVE-2022-42898, CVE-2022-42010, CVE-2022-43680, CVE-2022-3821, CVE-2021-46848, CVE-2022-35737 and CVE-2022-47629 + && dnf --nodocs upgrade -y libcom_err libxml2 krb5-libs dbus expat systemd libtasn1 sqlite-libs libksba \ + && groupadd --system --gid 101 nginx \ + && useradd --system --gid nginx --no-create-home --home-dir /nonexistent --comment "nginx user" --shell /bin/false --uid 101 nginx \ + && rpm --import https://cs.nginx.com/static/keys/nginx_signing.key \ + && curl -fsSL "https://cs.nginx.com/static/files/nginx-plus-$(grep -E -o '[0-9]+\.[0-9]+' /etc/redhat-release | cut -d"." -f1).repo" | tr 0 1 > /etc/yum.repos.d/nginx-plus.repo \ + && sed -i "0,/centos/s;;${NGINX_PLUS_VERSION}/centos;" /etc/yum.repos.d/nginx-plus.repo \ + && dnf --nodocs install -y nginx-plus nginx-plus-module-njs \ + ## end of duplicated code && subscription-manager register --org=${RHEL_ORGANIZATION} --activationkey=${RHEL_ACTIVATION_KEY} || true \ && subscription-manager attach \ && dnf config-manager --set-enabled codeready-builder-for-rhel-8-x86_64-rpms \ diff --git a/docs/content/technical-specifications.md b/docs/content/technical-specifications.md index c1054f48fb..ab17a9ba32 100644 --- a/docs/content/technical-specifications.md +++ b/docs/content/technical-specifications.md @@ -67,7 +67,7 @@ NGINX Plus images are available through the F5 Container registry `private-regis |Debian-based image with App Protect WAF | ``debian:11-slim`` | NGINX Plus App Protect WAF, JavaScript and OpenTracing modules, OpenTracing tracers for Jaeger, Zipkin and Datadog | `nginx-ic-nap/nginx-plus-ingress:3.0.1` | amd64 | |Debian-based image with App Protect DoS | ``debian:11-slim`` | NGINX Plus App Protect DoS, JavaScript module and OpenTracing modules, OpenTracing tracers for Jaeger, Zipkin and Datadog | `nginx-ic-dos/nginx-plus-ingress:3.0.1` | amd64 | |Debian-based image with App Protect WAF and DoS | ``debian:11-slim`` | NGINX Plus App Protect WAF, DoS, JavaScript and OpenTracing modules, OpenTracing tracers for Jaeger, Zipkin and Datadog | `nginx-ic-nap-dos/nginx-plus-ingress:3.0.1` | amd64 | -|Ubi-based image | ``redhat/ubi8`` | NGINX Plus JavaScript module | `nginx-ic/nginx-plus-ingress:3.0.1-ubi` | arm64, amd64, s390x | +|Ubi-based image | ``redhat/ubi9-minimal`` | NGINX Plus JavaScript module | `nginx-ic/nginx-plus-ingress:3.0.1-ubi` | arm64, amd64, s390x | |Ubi-based image with App Protect WAF | ``redhat/ubi8`` | NGINX Plus App Protect WAF and JavaScript modules | `nginx-ic-nap/nginx-plus-ingress:3.0.1-ubi` | amd64 | |Ubi-based image with App Protect DoS | ``redhat/ubi8`` | NGINX Plus App Protect DoS and JavaScript modules | `nginx-ic-dos/nginx-plus-ingress:3.0.1-ubi` | amd64 | |Ubi-based image with App Protect WAF and DoS | ``redhat/ubi8`` | NGINX Plus App Protect WAF, DoS and JavaScript modules | `nginx-ic-nap-dos/nginx-plus-ingress:3.0.1-ubi` | amd64 | diff --git a/go.mod b/go.mod index 08fcf6f70f..19faee2ced 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/aws/aws-sdk-go-v2/config v1.18.12 - github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.1 + github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.2 github.com/cert-manager/cert-manager v1.11.0 github.com/go-chi/chi/v5 v5.0.8 github.com/golang-jwt/jwt/v4 v4.4.3 diff --git a/go.sum b/go.sum index c3e7b8d68c..cb75e3d17f 100644 --- a/go.sum +++ b/go.sum @@ -47,7 +47,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.18.12 h1:fKs/I4wccmfrNRO9rdrbMO1NgLxct6H9rNMiPdBxHWw= @@ -56,18 +55,16 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.12 h1:Cb+HhuEnV19zHRaYYVglwvdHGMJ github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFuMO22HkV5VWGLBvmCLBCLPivUAmpdpnp4Vs= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= -github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.1 h1:IOjpqwEHMYPVfiqnH/auHvhz69/SGHYo/tFBkax5O0o= -github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.1/go.mod h1:DSuypbY6jb7WZSxrLuCgd7ouB5uRQ+Hg5wbt0GmgRcc= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.2 h1:7vuSkPqVqwBwSV0OJD71qqWOEFr3Hh1K0e2yOQ/JWwQ= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.2/go.mod h1:vrZVsmrC7QRNBK/W8nplI0tfJDvMl6DZAUT/pkFJiws= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= diff --git a/internal/k8s/controller.go b/internal/k8s/controller.go index 0312d81be8..6a50c9ea53 100644 --- a/internal/k8s/controller.go +++ b/internal/k8s/controller.go @@ -3567,6 +3567,9 @@ func getEndpointsFromEndpointSlicesForSubselectedPods(targetPort int32, pods []* continue } for _, endpoint := range endpointSlice.Endpoints { + if !*endpoint.Conditions.Ready { + continue + } for _, address := range endpoint.Addresses { if pod.Status.PodIP == address { addr := ipv6SafeAddrPort(pod.Status.PodIP, targetPort) @@ -3718,6 +3721,9 @@ func (lbc *LoadBalancerController) getEndpointsForPortFromEndpointSlices(endpoin for _, endpointSlicePort := range endpointSlice.Ports { if *endpointSlicePort.Port == targetPort { for _, endpoint := range endpointSlice.Endpoints { + if !*endpoint.Conditions.Ready { + continue + } for _, endpointAddress := range endpoint.Addresses { address := ipv6SafeAddrPort(endpointAddress, *endpointSlicePort.Port) podEndpoint := podEndpoint{ diff --git a/internal/k8s/controller_test.go b/internal/k8s/controller_test.go index 4629e2f22f..b1b352edf8 100644 --- a/internal/k8s/controller_test.go +++ b/internal/k8s/controller_test.go @@ -484,6 +484,8 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsInOneEndpointSlice(t * Name: "foo", } + endpointReady := true + tests := []struct { desc string svc api_v1.Service @@ -526,11 +528,17 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsInOneEndpointSlice(t * Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, { Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -563,6 +571,7 @@ func TestGetEndpointsFromEndpointSlices_TwoDifferentEndpointsInOnEndpointSlice(t Number: 8080, Name: "foo", } + endpointReady := true tests := []struct { desc string @@ -609,11 +618,17 @@ func TestGetEndpointsFromEndpointSlices_TwoDifferentEndpointsInOnEndpointSlice(t Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, { Addresses: []string{ "5.6.7.8", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -647,6 +662,8 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsAcrossTwoEndpointSlice Name: "foo", } + endpointReady := true + tests := []struct { desc string svc api_v1.Service @@ -695,14 +712,197 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsAcrossTwoEndpointSlice Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, + }, + { + Addresses: []string{ + "5.6.7.8", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, + }, + }, + }, + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "1.2.3.4", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, + }, + { + Addresses: []string{ + "10.0.0.1", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotEndpoints, err := lbc.getEndpointsForPortFromEndpointSlices(test.svcEndpointSlices, backendServicePort, &test.svc) + if err != nil { + t.Fatal(err) + } + if result := unorderedEqual(gotEndpoints, test.expectedEndpoints); !result { + t.Errorf("lbc.getEndpointsForPortFromEndpointSlices() got %v, want %v", + gotEndpoints, test.expectedEndpoints) + } + }) + } +} + +func TestGetEndpointsFromEndpointSlices_TwoDifferentEndpointsInOnEndpointSliceOneEndpointNotReady(t *testing.T) { + endpointPort := int32(8080) + + lbc := LoadBalancerController{ + isNginxPlus: true, + } + + backendServicePort := networking.ServiceBackendPort{ + Number: 8080, + Name: "foo", + } + endpointReadyTrue := true + endpointReadyFalse := false + + tests := []struct { + desc string + svc api_v1.Service + svcEndpointSlices []discovery_v1.EndpointSlice + expectedEndpoints []podEndpoint + }{ + { + desc: "two different endpoints in one endpoint slice", + svc: api_v1.Service{ + TypeMeta: meta_v1.TypeMeta{}, + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee-svc", + Namespace: "default", + }, + Spec: api_v1.ServiceSpec{ + Ports: []api_v1.ServicePort{ + { + Name: "foo", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + }, + Status: api_v1.ServiceStatus{}, + }, + expectedEndpoints: []podEndpoint{ + { + Address: "1.2.3.4:8080", + }, + }, + svcEndpointSlices: []discovery_v1.EndpointSlice{ + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "1.2.3.4", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyTrue, + }, }, { Addresses: []string{ "5.6.7.8", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyFalse, + }, }, }, }, + }, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotEndpoints, err := lbc.getEndpointsForPortFromEndpointSlices(test.svcEndpointSlices, backendServicePort, &test.svc) + if err != nil { + t.Fatal(err) + } + if result := unorderedEqual(gotEndpoints, test.expectedEndpoints); !result { + t.Errorf("lbc.getEndpointsForPortFromEndpointSlices() got %v, want %v", + gotEndpoints, test.expectedEndpoints) + } + }) + } +} + +func TestGetEndpointsFromEndpointSlices_TwoDifferentEndpointsAcrossTwoEndpointSlicesOneEndpointNotReady(t *testing.T) { + endpointPort := int32(8080) + + lbc := LoadBalancerController{ + isNginxPlus: true, + } + + backendServicePort := networking.ServiceBackendPort{ + Number: 8080, + Name: "foo", + } + + endpointReadyTrue := true + endpointReadyFalse := false + + tests := []struct { + desc string + svc api_v1.Service + svcEndpointSlices []discovery_v1.EndpointSlice + expectedEndpoints []podEndpoint + }{ + { + desc: "duplicate endpoints across two endpointslices", + svc: api_v1.Service{ + TypeMeta: meta_v1.TypeMeta{}, + ObjectMeta: meta_v1.ObjectMeta{ + Name: "coffee-svc", + Namespace: "default", + }, + Spec: api_v1.ServiceSpec{ + Ports: []api_v1.ServicePort{ + { + Name: "foo", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + }, + Status: api_v1.ServiceStatus{}, + }, + expectedEndpoints: []podEndpoint{ + { + Address: "1.2.3.4:8080", + }, + }, + svcEndpointSlices: []discovery_v1.EndpointSlice{ { Ports: []discovery_v1.EndpointPort{ { @@ -714,11 +914,26 @@ func TestGetEndpointsFromEndpointSlices_DuplicateEndpointsAcrossTwoEndpointSlice Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyTrue, + }, }, + }, + }, + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ { Addresses: []string{ "10.0.0.1", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyFalse, + }, }, }, }, @@ -862,7 +1077,7 @@ func TestGetEndpointsFromEndpointSlices_ErrorsOnNoEndpointSlicesFound(t *testing func TestGetEndpointSlicesBySubselectedPods_FindOnePodInOneEndpointSlice(t *testing.T) { endpointPort := int32(8080) - + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -911,6 +1126,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInOneEndpointSlice(t *test Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -931,7 +1149,7 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInOneEndpointSlice(t *test func TestGetEndpointSlicesBySubselectedPods_FindOnePodInTwoEndpointSlicesWithDuplicateEndpoints(t *testing.T) { endpointPort := int32(8080) - + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -980,6 +1198,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInTwoEndpointSlicesWithDup Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -994,6 +1215,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInTwoEndpointSlicesWithDup Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -1014,7 +1238,7 @@ func TestGetEndpointSlicesBySubselectedPods_FindOnePodInTwoEndpointSlicesWithDup func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInOneEndpointSlice(t *testing.T) { endpointPort := int32(8080) - + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -1084,11 +1308,17 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInOneEndpointSlice(t *tes Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, { Addresses: []string{ "5.6.7.8", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -1109,7 +1339,7 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInOneEndpointSlice(t *tes func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInTwoEndpointSlices(t *testing.T) { endpointPort := int32(8080) - + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -1179,6 +1409,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInTwoEndpointSlices(t *te Addresses: []string{ "1.2.3.4", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -1193,6 +1426,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInTwoEndpointSlices(t *te Addresses: []string{ "5.6.7.8", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, }, @@ -1211,9 +1447,208 @@ func TestGetEndpointSlicesBySubselectedPods_FindTwoPodsInTwoEndpointSlices(t *te } } -func TestGetEndpointSlicesBySubselectedPods_FindNoPods(t *testing.T) { +func TestGetEndpointSlicesBySubselectedPods_FindOnePodEndpointInOneEndpointSliceWithOneEndpointNotReady(t *testing.T) { endpointPort := int32(8080) + endpointReadyTrue := true + endpointReadyFalse := false + boolPointer := func(b bool) *bool { return &b } + tests := []struct { + desc string + targetPort int32 + svcEndpointSlices []discovery_v1.EndpointSlice + pods []*api_v1.Pod + expectedEndpoints []podEndpoint + }{ + { + desc: "find two pods in one endpointslice", + targetPort: 8080, + expectedEndpoints: []podEndpoint{ + { + Address: "1.2.3.4:8080", + MeshPodOwner: configs.MeshPodOwner{ + OwnerType: "deployment", + OwnerName: "deploy-1", + }, + }, + }, + pods: []*api_v1.Pod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + OwnerReferences: []meta_v1.OwnerReference{ + { + Kind: "Deployment", + Name: "deploy-1", + Controller: boolPointer(true), + }, + }, + }, + Status: api_v1.PodStatus{ + PodIP: "1.2.3.4", + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + OwnerReferences: []meta_v1.OwnerReference{ + { + Kind: "Deployment", + Name: "deploy-1", + Controller: boolPointer(true), + }, + }, + }, + Status: api_v1.PodStatus{ + PodIP: "5.6.7.8", + }, + }, + }, + svcEndpointSlices: []discovery_v1.EndpointSlice{ + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "1.2.3.4", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyTrue, + }, + }, + { + Addresses: []string{ + "5.6.7.8", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyFalse, + }, + }, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotEndpoints := getEndpointsFromEndpointSlicesForSubselectedPods(test.targetPort, test.pods, test.svcEndpointSlices) + + if result := unorderedEqual(gotEndpoints, test.expectedEndpoints); !result { + t.Errorf("getEndpointsFromEndpointSlicesForSubselectedPods() = got %v, want %v", gotEndpoints, test.expectedEndpoints) + } + }) + } +} + +func TestGetEndpointSlicesBySubselectedPods_FindOnePodEndpointInTwoEndpointSlicesWithOneEndpointNotReady(t *testing.T) { + endpointPort := int32(8080) + endpointReadyTrue := true + endpointReadyFalse := false + boolPointer := func(b bool) *bool { return &b } + tests := []struct { + desc string + targetPort int32 + svcEndpointSlices []discovery_v1.EndpointSlice + pods []*api_v1.Pod + expectedEndpoints []podEndpoint + }{ + { + desc: "find two pods in two endpointslices", + targetPort: 8080, + expectedEndpoints: []podEndpoint{ + { + Address: "1.2.3.4:8080", + MeshPodOwner: configs.MeshPodOwner{ + OwnerType: "deployment", + OwnerName: "deploy-1", + }, + }, + }, + pods: []*api_v1.Pod{ + { + ObjectMeta: meta_v1.ObjectMeta{ + OwnerReferences: []meta_v1.OwnerReference{ + { + Kind: "Deployment", + Name: "deploy-1", + Controller: boolPointer(true), + }, + }, + }, + Status: api_v1.PodStatus{ + PodIP: "1.2.3.4", + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + OwnerReferences: []meta_v1.OwnerReference{ + { + Kind: "Deployment", + Name: "deploy-1", + Controller: boolPointer(true), + }, + }, + }, + Status: api_v1.PodStatus{ + PodIP: "5.6.7.8", + }, + }, + }, + svcEndpointSlices: []discovery_v1.EndpointSlice{ + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "1.2.3.4", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyTrue, + }, + }, + }, + }, + { + Ports: []discovery_v1.EndpointPort{ + { + Port: &endpointPort, + }, + }, + Endpoints: []discovery_v1.Endpoint{ + { + Addresses: []string{ + "5.6.7.8", + }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReadyFalse, + }, + }, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + gotEndpoints := getEndpointsFromEndpointSlicesForSubselectedPods(test.targetPort, test.pods, test.svcEndpointSlices) + + if result := unorderedEqual(gotEndpoints, test.expectedEndpoints); !result { + t.Errorf("getEndpointsFromEndpointSlicesForSubselectedPods() = got %v, want %v", gotEndpoints, test.expectedEndpoints) + } + }) + } +} + +func TestGetEndpointSlicesBySubselectedPods_FindNoPods(t *testing.T) { + endpointPort := int32(8080) + endpointReady := true boolPointer := func(b bool) *bool { return &b } tests := []struct { desc string @@ -1254,6 +1689,9 @@ func TestGetEndpointSlicesBySubselectedPods_FindNoPods(t *testing.T) { Addresses: []string{ "5.4.3.2", }, + Conditions: discovery_v1.EndpointConditions{ + Ready: &endpointReady, + }, }, }, },