diff --git a/cmd/pinniped-supervisor/main.go b/cmd/pinniped-supervisor/main.go index b8a59949..346f83ca 100644 --- a/cmd/pinniped-supervisor/main.go +++ b/cmd/pinniped-supervisor/main.go @@ -159,8 +159,15 @@ func run(serverInstallationNamespace string, cfg *supervisor.Config) error { pinnipedinformers.WithNamespace(serverInstallationNamespace), ) + // Serve the /healthz endpoint and make all other paths result in 404. + healthMux := http.NewServeMux() + healthMux.Handle("/healthz", http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + _, _ = writer.Write([]byte("ok")) + })) + dynamicJWKSProvider := jwks.NewDynamicJWKSProvider() - oidProvidersManager := manager.NewManager(http.NotFoundHandler(), dynamicJWKSProvider) + // OIDC endpoints will be served by the oidProvidersManager, and any non-OIDC paths will fallback to the healthMux. + oidProvidersManager := manager.NewManager(healthMux, dynamicJWKSProvider) startControllers(ctx, cfg, oidProvidersManager, dynamicJWKSProvider, kubeClient, pinnipedClient, kubeInformers, pinnipedInformers) //nolint: gosec // Intentionally binding to all network interfaces. @@ -171,18 +178,6 @@ func run(serverInstallationNamespace string, cfg *supervisor.Config) error { defer l.Close() start(ctx, l, oidProvidersManager) - //nolint: gosec // Intentionally binding to all network interfaces. - healthzListener, err := net.Listen("tcp", ":8080") - if err != nil { - return fmt.Errorf("cannot create healthzListener: %w", err) - } - defer healthzListener.Close() - healthzMux := http.NewServeMux() - healthzMux.Handle("/healthz", http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { - _, _ = writer.Write([]byte("ok")) - })) - start(ctx, healthzListener, healthzMux) - klog.InfoS("supervisor is ready", "address", l.Addr().String()) gotSignal := waitForSignal() diff --git a/deploy/supervisor/deployment.yaml b/deploy/supervisor/deployment.yaml index 764c71d7..afa4efd5 100644 --- a/deploy/supervisor/deployment.yaml +++ b/deploy/supervisor/deployment.yaml @@ -84,10 +84,13 @@ spec: mountPath: /etc/config - name: podinfo mountPath: /etc/podinfo + ports: + - containerPort: 80 + protocol: TCP livenessProbe: httpGet: path: /healthz - port: 8080 + port: 80 scheme: HTTP initialDelaySeconds: 2 timeoutSeconds: 15 @@ -96,7 +99,7 @@ spec: readinessProbe: httpGet: path: /healthz - port: 8080 + port: 80 scheme: HTTP initialDelaySeconds: 2 timeoutSeconds: 3 diff --git a/test/integration/supervisor_healthz_test.go b/test/integration/supervisor_healthz_test.go new file mode 100644 index 00000000..1691be40 --- /dev/null +++ b/test/integration/supervisor_healthz_test.go @@ -0,0 +1,53 @@ +// Copyright 2020 the Pinniped contributors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package integration + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "go.pinniped.dev/test/library" +) + +// The Supervisor health endpoint is public because that makes it easier +// for users to create an Ingress for the supervisor on platforms like +// GKE where the Ingress wants to perform a health check. It's somewhere +// between inconvenient and impossible to make that Ingress health check +// happen on a private container port at this time. +// This test checks that it is working and that it is public. +func TestSupervisorHealthz(t *testing.T) { + env := library.IntegrationEnv(t) + + if env.SupervisorHTTPAddress == "" { + t.Skip("PINNIPED_TEST_SUPERVISOR_HTTP_ADDRESS not defined") + } + + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + defer cancel() + + requestHealthEndpoint, err := http.NewRequestWithContext( + ctx, + http.MethodGet, + fmt.Sprintf("http://%s/healthz", env.SupervisorHTTPAddress), + nil, + ) + require.NoError(t, err) + + httpClient := &http.Client{} + response, err := httpClient.Do(requestHealthEndpoint) //nolint:bodyclose + require.NoError(t, err) + require.Equal(t, http.StatusOK, response.StatusCode) + + responseBody, err := ioutil.ReadAll(response.Body) + require.NoError(t, err) + err = response.Body.Close() + require.NoError(t, err) + require.Equal(t, "ok", string(responseBody)) +}