Skip to content

Commit 66cdb02

Browse files
feat: basic ingress controller health checks implementation (#1227)
## Summary Injects the bootstrap config health checks into pomerium core. This will force the health probes to wait to succeed until the bootstrap controller has run successfully at least once. This has the intended side of effect of always failing on startup if the ingress controller fails to construct the pomerium config from the pomerium CR - but that would be expected. Adds the health check probes to deployment and the probes kustomize file. Requires pomerium/pomerium#5822 ## Related issues [ENG-2850](https://linear.app/pomerium/issue/ENG-2850/implement-ingress-controller-health-checks) <!-- For example... Fixes #159 --> ## Checklist - [X] reference any related issues - [X] updated docs - docs PR will follow to pomerium/documentation - [X] updated unit tests - [X] updated UPGRADING.md - [X] add appropriate tag (`improvement` / `bug` / etc) - [X] ready for review
1 parent 6c2a715 commit 66cdb02

10 files changed

Lines changed: 62 additions & 17 deletions

File tree

cmd/all_in_one.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/pomerium/pomerium/config"
2626
"github.com/pomerium/pomerium/pkg/grpc/databroker"
2727
"github.com/pomerium/pomerium/pkg/grpcutil"
28+
"github.com/pomerium/pomerium/pkg/health"
2829
"github.com/pomerium/pomerium/pkg/netutil"
2930
"github.com/pomerium/pomerium/pkg/telemetry/trace"
3031

@@ -35,6 +36,7 @@ import (
3536
"github.com/pomerium/ingress-controller/pomerium"
3637
pomerium_ctrl "github.com/pomerium/ingress-controller/pomerium/ctrl"
3738
"github.com/pomerium/ingress-controller/util"
39+
health_ctrl "github.com/pomerium/ingress-controller/util/health"
3840
)
3941

4042
type allCmdOptions struct {
@@ -47,10 +49,12 @@ type allCmdOptions struct {
4749
configControllerShutdownTimeout time.Duration
4850
// metricsBindAddress must be externally accessible host:port
4951
metricsBindAddress string `validate:"required,hostname_port"`
50-
serverAddr string `validate:"required,hostname_port"`
51-
sshAddr string
52-
httpRedirectAddr string `validate:"required,hostname_port"`
53-
deriveTLS string `validate:"required,hostname"`
52+
// healthProbeBindAddress must be externally accessible host:port
53+
healthProbeBindAddress string
54+
serverAddr string `validate:"required,hostname_port"`
55+
sshAddr string
56+
httpRedirectAddr string `validate:"required,hostname_port"`
57+
deriveTLS string `validate:"required,hostname"`
5458
}
5559

5660
type allCmdParam struct {
@@ -118,6 +122,7 @@ func (s *allCmd) setupFlags() error {
118122
flags.BoolVar(&s.debugPomerium, debugPomerium, false, "enable debug logging for pomerium")
119123
flags.BoolVar(&s.debugEnvoy, debugEnvoy, false, "enable debug logging for envoy")
120124
flags.StringVar(&s.metricsBindAddress, metricsBindAddress, "", "host:port for aggregate metrics. host is mandatory")
125+
flags.StringVar(&s.healthProbeBindAddress, healthProbeBindAddress, "127.0.0.1:28080", "host:port for http health probes")
121126
flags.StringVar(&s.adminBindAddr, debugAdminBindAddr, "", "host:port for admin server")
122127
flags.StringVar(&s.serverAddr, "server-addr", ":8443", "the address the HTTPS server would bind to")
123128
flags.StringVar(&s.sshAddr, "ssh-addr", "", "the address the SSH server would bind to")
@@ -195,6 +200,10 @@ func (s *allCmdParam) run(ctx context.Context) error {
195200
defer cancel()
196201

197202
ctx = trace.NewContext(ctx, trace.NewSyncClient(nil))
203+
ctx = health.Context(
204+
ctx,
205+
health_ctrl.SettingsBootstrapReconciler,
206+
)
198207

199208
eg, ctx := errgroup.WithContext(ctx)
200209
cfgCtl := util.NewRestartOnChange[*config.Config]()
@@ -244,6 +253,7 @@ func (s *allCmdParam) makeBootstrapConfig(opt allCmdOptions) error {
244253
s.ingressMetricsAddr = fmt.Sprintf("localhost:%s", ports[7])
245254

246255
s.cfg.Options.MetricsAddr = opt.metricsBindAddress
256+
s.cfg.Options.HealthCheckAddr = opt.healthProbeBindAddress
247257

248258
s.cfg.MetricsScrapeEndpoints = []config.MetricsScrapeEndpoint{
249259
{
@@ -383,11 +393,11 @@ func (s *allCmdParam) runBootstrapConfigController(ctx context.Context, reconcil
383393
if err != nil {
384394
return fmt.Errorf("manager: %w", err)
385395
}
386-
name := "bootstrap"
396+
name := settings.ControllerNameBootstrap
387397
if host, err := os.Hostname(); err == nil {
388398
name = fmt.Sprintf("%s pod/%s", name, host)
389399
}
390-
if err := settings.NewSettingsController(mgr, reconciler, s.settings, name, false); err != nil {
400+
if err := settings.NewSettingsController(mgr, reconciler, s.settings, name, false, health_ctrl.SettingsBootstrapReconciler); err != nil {
391401
return fmt.Errorf("settings controller: %w", err)
392402
}
393403
return mgr.Start(ctx)

config/pomerium/deployment/args.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ spec:
1212
- --pomerium-config=global
1313
- --update-status-from-service=$(POMERIUM_NAMESPACE)/pomerium-proxy
1414
- --metrics-bind-address=$(POD_IP):9090
15+
- --health-probe-bind-address=$(POD_IP):28080
1516
env:
1617
- name: POMERIUM_NAMESPACE
1718
valueFrom:

config/pomerium/deployment/healthcheck.yaml

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,21 @@ spec:
77
spec:
88
containers:
99
- name: pomerium
10+
startupProbe:
11+
httpGet:
12+
path: /startupz
13+
port : 28080
1014
livenessProbe:
1115
httpGet:
1216
path: /healthz
13-
port: 8081
17+
port: 28080
1418
initialDelaySeconds: 15
15-
periodSeconds: 20
19+
periodSeconds: 60
20+
failureThreshold: 10
1621
readinessProbe:
1722
httpGet:
1823
path: /readyz
19-
port: 8081
20-
initialDelaySeconds: 5
21-
periodSeconds: 10
24+
port: 28080
25+
initialDelaySeconds: 15
26+
periodSeconds: 60
27+
failureThreshold: 5

controllers/config_controller.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/pomerium/ingress-controller/controllers/reporter"
2121
"github.com/pomerium/ingress-controller/controllers/settings"
2222
"github.com/pomerium/ingress-controller/pomerium"
23+
health_ctrl "github.com/pomerium/ingress-controller/util/health"
2324
)
2425

2526
const (
@@ -78,7 +79,7 @@ func (c *Controller) RunLeased(ctx context.Context) (err error) {
7879
return fmt.Errorf("create ingress controller: %w", err)
7980
}
8081
if c.GlobalSettings != nil {
81-
if err = settings.NewSettingsController(mgr, c.ConfigReconciler, *c.GlobalSettings, "pomerium-crd", true); err != nil {
82+
if err = settings.NewSettingsController(mgr, c.ConfigReconciler, *c.GlobalSettings, "pomerium-crd", true, health_ctrl.SettingsReconciler); err != nil {
8283
return fmt.Errorf("create settings controller: %w", err)
8384
}
8485
} else {

controllers/settings/controller.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"sigs.k8s.io/controller-runtime/pkg/predicate"
1616

1717
pom_cfg "github.com/pomerium/pomerium/config"
18+
"github.com/pomerium/pomerium/pkg/health"
1819

1920
icsv1 "github.com/pomerium/ingress-controller/apis/ingress/v1"
2021
"github.com/pomerium/ingress-controller/controllers/deps"
@@ -25,6 +26,12 @@ import (
2526
"github.com/pomerium/ingress-controller/util/generic"
2627
)
2728

29+
const (
30+
// ControllerNameBootstrap is a default name for the bootstrap config settings
31+
// controller
32+
ControllerNameBootstrap = "bootstrap"
33+
)
34+
2835
type settingsController struct {
2936
// key kind/name of a settings object to watch, all others would be ignored
3037
key model.Key
@@ -38,6 +45,8 @@ type settingsController struct {
3845
reporter.MultiPomeriumStatusReporter
3946
// emitWarnings related to configuration. as there are multiple controllers running, not all should report
4047
emitWarnings bool
48+
49+
ctrlCheck health.Check
4150
}
4251

4352
// NewSettingsController creates and registers a new controller for
@@ -48,6 +57,7 @@ func NewSettingsController(
4857
name types.NamespacedName,
4958
controllerName string,
5059
emitWarnings bool,
60+
check health.Check,
5161
) error {
5262
if name.Namespace != "" {
5363
return fmt.Errorf("pomerium CRD is cluster-scoped")
@@ -80,8 +90,8 @@ func NewSettingsController(
8090
&reporter.SettingsLogReporter{},
8191
},
8292
emitWarnings: emitWarnings,
93+
ctrlCheck: check,
8394
}
84-
8595
secretKind := generic.GVKForType[*corev1.Secret](mgr.GetScheme()).Kind
8696
err := ctrl.NewControllerManagedBy(mgr).
8797
Named(controllerName).
@@ -120,6 +130,8 @@ func (c *settingsController) Reconcile(ctx context.Context, req ctrl.Request) (c
120130
c.SettingsRejected(ctx, &cfg.Pomerium, err)
121131
return ctrl.Result{Requeue: true}, fmt.Errorf("get settings: %w", err)
122132
}
133+
// bootstrap config must at least construct a valid config once to be considered running
134+
health.ReportRunning(c.ctrlCheck)
123135

124136
if deprecations, err := icsv1.GetDeprecations(&cfg.Pomerium.Spec); err != nil {
125137
logger.Error(err, "checking config for deprecations")

controllers/settings/integration_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
controllers_mock "github.com/pomerium/ingress-controller/controllers/mock"
2727
"github.com/pomerium/ingress-controller/controllers/settings"
2828
"github.com/pomerium/ingress-controller/pomerium"
29+
health_ctrl "github.com/pomerium/ingress-controller/util/health"
2930
)
3031

3132
var (
@@ -97,7 +98,7 @@ func (s *ControllerTestSuite) createTestController(ctx context.Context, reconcil
9798
Scheme: s.Environment.Scheme,
9899
})
99100
s.NoError(err)
100-
s.NoError(settings.NewSettingsController(mgr, reconciler, name, "test", false))
101+
s.NoError(settings.NewSettingsController(mgr, reconciler, name, "test", false, health_ctrl.SettingsReconciler))
101102

102103
go func() {
103104
if err = mgr.Start(ctx); err != nil && ctx.Err() == nil {

deployment.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,7 @@ spec:
962962
- --pomerium-config=global
963963
- --update-status-from-service=$(POMERIUM_NAMESPACE)/pomerium-proxy
964964
- --metrics-bind-address=$(POD_IP):9090
965+
- --health-probe-bind-address=$(POD_IP):28080
965966
env:
966967
- name: TMPDIR
967968
value: /tmp

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ require (
1717
github.com/martinlindhe/base36 v1.1.1
1818
github.com/open-policy-agent/opa v1.8.0
1919
github.com/pomerium/csrf v1.7.0
20-
github.com/pomerium/pomerium v0.28.1-0.20250911195602-b3eb7b5ee438
20+
github.com/pomerium/pomerium v0.28.1-0.20250915161701-b1114f9fb1d7
2121
github.com/rs/zerolog v1.34.0
2222
github.com/sergi/go-diff v1.4.0
2323
github.com/spf13/cobra v1.10.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,8 +651,8 @@ github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 h1:3YQY1sb5
651651
github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524/go.mod h1:7fGbUYJnU8RcxZJvUvhukOIBv1G7LWDAHMfDxAf5+Y0=
652652
github.com/pomerium/envoy-custom v1.34.5-p1 h1:6ES5N96BnztDhgeTbsvvCTacHrzZkrrqeR/c2JZMb7E=
653653
github.com/pomerium/envoy-custom v1.34.5-p1/go.mod h1:+wpbZvum83bq/OD4cp9/8IZiMV6boBkwDhlFPLOoWoI=
654-
github.com/pomerium/pomerium v0.28.1-0.20250911195602-b3eb7b5ee438 h1:d5CfZCT5pFh7QAW3hOvkQNFrV3GwG9j0+hFLdbn44SQ=
655-
github.com/pomerium/pomerium v0.28.1-0.20250911195602-b3eb7b5ee438/go.mod h1:q1/EiUkvJ6GouvqkaY1lSOX4dKjYkze2AGA5Fz8gr2A=
654+
github.com/pomerium/pomerium v0.28.1-0.20250915161701-b1114f9fb1d7 h1:86oP9xj2gW0uh0Y3gAcHxMeu+GMb5x3EDmQsoW6lifk=
655+
github.com/pomerium/pomerium v0.28.1-0.20250915161701-b1114f9fb1d7/go.mod h1:q1/EiUkvJ6GouvqkaY1lSOX4dKjYkze2AGA5Fz8gr2A=
656656
github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46 h1:NRTg8JOXCxcIA1lAgD74iYud0rbshbWOB3Ou4+Huil8=
657657
github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46/go.mod h1:QqZmx6ZgPxz18va7kqoT4t/0yJtP7YFIDiT/W2n2fZ4=
658658
github.com/pomerium/webauthn v0.0.0-20240603205124-0428df511172 h1:TqoPqRgXSHpn+tEJq6H72iCS5pv66j3rPprThUEZg0E=

util/health/check.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Package health encapsulates extensions to pomerium core's pkg/health
2+
package health
3+
4+
import (
5+
"github.com/pomerium/pomerium/pkg/health"
6+
)
7+
8+
const (
9+
// SettingsBootstrapReconciler checks that the bootstrap reconciler has run once successfully
10+
SettingsBootstrapReconciler = health.Check("controller.settings.reconciler.bootstrap")
11+
// SettingsReconciler checks that the leased settings reconciler has run
12+
SettingsReconciler = health.Check("controller.settings.reconciler")
13+
)

0 commit comments

Comments
 (0)