@@ -32,6 +32,7 @@ import (
32
32
"contrib.go.opencensus.io/exporter/prometheus"
33
33
"contrib.go.opencensus.io/exporter/stackdriver"
34
34
"github.com/GoogleCloudPlatform/cloudsql-proxy/v2/cloudsql"
35
+ "github.com/GoogleCloudPlatform/cloudsql-proxy/v2/internal/healthcheck"
35
36
"github.com/GoogleCloudPlatform/cloudsql-proxy/v2/internal/log"
36
37
"github.com/GoogleCloudPlatform/cloudsql-proxy/v2/internal/proxy"
37
38
"github.com/spf13/cobra"
@@ -76,6 +77,7 @@ type Command struct {
76
77
telemetryProject string
77
78
telemetryPrefix string
78
79
prometheusNamespace string
80
+ healthCheck bool
79
81
httpPort string
80
82
}
81
83
@@ -157,7 +159,6 @@ When this flag is not set, there is no limit.`)
157
159
to close after receiving a TERM signal. The proxy will shut
158
160
down when the number of open connections reaches 0 or when
159
161
the maximum time has passed. Defaults to 0s.` )
160
-
161
162
cmd .PersistentFlags ().StringVar (& c .telemetryProject , "telemetry-project" , "" ,
162
163
"Enable Cloud Monitoring and Cloud Trace integration with the provided project ID." )
163
164
cmd .PersistentFlags ().BoolVar (& c .disableTraces , "disable-traces" , false ,
@@ -172,12 +173,16 @@ the maximum time has passed. Defaults to 0s.`)
172
173
"Enable Prometheus for metric collection using the provided namespace" )
173
174
cmd .PersistentFlags ().StringVar (& c .httpPort , "http-port" , "9090" ,
174
175
"Port for the Prometheus server to use" )
176
+ cmd .PersistentFlags ().BoolVar (& c .healthCheck , "health-check" , false ,
177
+ `Enables HTTP endpoints /startup, /liveness, and /readiness
178
+ that report on the proxy's health. Endpoints are available on localhost
179
+ only. Uses the port specified by the http-port flag.` )
175
180
cmd .PersistentFlags ().StringVar (& c .conf .APIEndpointURL , "sqladmin-api-endpoint" , "" ,
176
181
"When set, the proxy uses this url as the API endpoint for all Cloud SQL Admin API requests.\n Example: https://sqladmin.googleapis.com" )
177
182
cmd .PersistentFlags ().StringVar (& c .conf .QuotaProject , "quota-project" , "" ,
178
183
`Specifies the project to use for Cloud SQL Admin API quota tracking.
179
184
The IAM principal must have the "serviceusage.services.use" permission
180
- for the given project. See https://cloud.google.com/service-usage/docs/overview and
185
+ for the given project. See https://cloud.google.com/service-usage/docs/overview and
181
186
https://cloud.google.com/storage/docs/requester-pays` )
182
187
183
188
// Global and per instance flags
@@ -225,18 +230,18 @@ func parseConfig(cmd *Command, conf *proxy.Config, args []string) error {
225
230
return newBadCommandError ("cannot specify --credentials-file and --gcloud-auth flags at the same time" )
226
231
}
227
232
228
- if userHasSet ("http-port" ) && ! userHasSet ("prometheus-namespace" ) {
229
- return newBadCommandError ( "cannot specify --http-port without --prometheus-namespace" )
233
+ if userHasSet ("http-port" ) && ! userHasSet ("prometheus-namespace" ) && ! userHasSet ( "health-check" ) {
234
+ cmd . logger . Infof ( "Ignoring --http-port because --prometheus-namespace or --health-check was not set " )
230
235
}
231
236
232
237
if ! userHasSet ("telemetry-project" ) && userHasSet ("telemetry-prefix" ) {
233
- cmd .logger .Infof ("Ignoring telementry-prefix as telemetry-project was not set" )
238
+ cmd .logger .Infof ("Ignoring -- telementry-prefix because -- telemetry-project was not set" )
234
239
}
235
240
if ! userHasSet ("telemetry-project" ) && userHasSet ("disable-metrics" ) {
236
- cmd .logger .Infof ("Ignoring disable-metrics as telemetry-project was not set" )
241
+ cmd .logger .Infof ("Ignoring -- disable-metrics because -- telemetry-project was not set" )
237
242
}
238
243
if ! userHasSet ("telemetry-project" ) && userHasSet ("disable-traces" ) {
239
- cmd .logger .Infof ("Ignoring disable-traces as telemetry-project was not set" )
244
+ cmd .logger .Infof ("Ignoring -- disable-traces because -- telemetry-project was not set" )
240
245
}
241
246
242
247
if userHasSet ("sqladmin-api-endpoint" ) && conf .APIEndpointURL != "" {
@@ -364,9 +369,8 @@ func runSignalWrapper(cmd *Command) error {
364
369
ctx , cancel := context .WithCancel (cmd .Context ())
365
370
defer cancel ()
366
371
367
- // Configure Cloud Trace and/or Cloud Monitoring based on command
368
- // invocation. If a project has not been enabled, no traces or metrics are
369
- // enabled.
372
+ // Configure collectors before the proxy has started to ensure we are
373
+ // collecting metrics before *ANY* Cloud SQL Admin API calls are made.
370
374
enableMetrics := ! cmd .disableMetrics
371
375
enableTraces := ! cmd .disableTraces
372
376
if cmd .telemetryProject != "" && (enableMetrics || enableTraces ) {
@@ -394,40 +398,22 @@ func runSignalWrapper(cmd *Command) error {
394
398
}()
395
399
}
396
400
397
- shutdownCh := make (chan error )
398
-
401
+ var (
402
+ needsHTTPServer bool
403
+ mux = http .NewServeMux ()
404
+ )
399
405
if cmd .prometheusNamespace != "" {
406
+ needsHTTPServer = true
400
407
e , err := prometheus .NewExporter (prometheus.Options {
401
408
Namespace : cmd .prometheusNamespace ,
402
409
})
403
410
if err != nil {
404
411
return err
405
412
}
406
- mux := http .NewServeMux ()
407
413
mux .Handle ("/metrics" , e )
408
- addr := fmt .Sprintf ("localhost:%s" , cmd .httpPort )
409
- server := & http.Server {Addr : addr , Handler : mux }
410
- go func () {
411
- select {
412
- case <- ctx .Done ():
413
- // Give the HTTP server a second to shutdown cleanly.
414
- ctx2 , _ := context .WithTimeout (context .Background (), time .Second )
415
- if err := server .Shutdown (ctx2 ); err != nil {
416
- cmd .logger .Errorf ("failed to shutdown Prometheus HTTP server: %v\n " , err )
417
- }
418
- }
419
- }()
420
- go func () {
421
- err := server .ListenAndServe ()
422
- if err == http .ErrServerClosed {
423
- return
424
- }
425
- if err != nil {
426
- shutdownCh <- fmt .Errorf ("failed to start prometheus HTTP server: %v" , err )
427
- }
428
- }()
429
414
}
430
415
416
+ shutdownCh := make (chan error )
431
417
// watch for sigterm / sigint signals
432
418
signals := make (chan os.Signal , 1 )
433
419
signal .Notify (signals , syscall .SIGTERM , syscall .SIGINT )
@@ -465,18 +451,55 @@ func runSignalWrapper(cmd *Command) error {
465
451
cmd .logger .Errorf ("The proxy has encountered a terminal error: %v" , err )
466
452
return err
467
453
case p = <- startCh :
454
+ cmd .logger .Infof ("The proxy has started successfully and is ready for new connections!" )
468
455
}
469
- cmd .logger .Infof ("The proxy has started successfully and is ready for new connections!" )
470
- defer p .Close ()
471
456
defer func () {
472
457
if cErr := p .Close (); cErr != nil {
473
458
cmd .logger .Errorf ("error during shutdown: %v" , cErr )
474
459
}
475
460
}()
476
461
477
- go func () {
478
- shutdownCh <- p .Serve (ctx )
479
- }()
462
+ notify := func () {}
463
+ if cmd .healthCheck {
464
+ needsHTTPServer = true
465
+ hc := healthcheck .NewCheck (p , cmd .logger )
466
+ mux .HandleFunc ("/startup" , hc .HandleStartup )
467
+ mux .HandleFunc ("/readiness" , hc .HandleReadiness )
468
+ mux .HandleFunc ("/liveness" , hc .HandleLiveness )
469
+ notify = hc .NotifyStarted
470
+ }
471
+
472
+ // Start the HTTP server if anything requiring HTTP is specified.
473
+ if needsHTTPServer {
474
+ server := & http.Server {
475
+ Addr : fmt .Sprintf ("localhost:%s" , cmd .httpPort ),
476
+ Handler : mux ,
477
+ }
478
+ // Start the HTTP server.
479
+ go func () {
480
+ err := server .ListenAndServe ()
481
+ if err == http .ErrServerClosed {
482
+ return
483
+ }
484
+ if err != nil {
485
+ shutdownCh <- fmt .Errorf ("failed to start HTTP server: %v" , err )
486
+ }
487
+ }()
488
+ // Handle shutdown of the HTTP server gracefully.
489
+ go func () {
490
+ select {
491
+ case <- ctx .Done ():
492
+ // Give the HTTP server a second to shutdown cleanly.
493
+ ctx2 , cancel := context .WithTimeout (context .Background (), time .Second )
494
+ defer cancel ()
495
+ if err := server .Shutdown (ctx2 ); err != nil {
496
+ cmd .logger .Errorf ("failed to shutdown Prometheus HTTP server: %v\n " , err )
497
+ }
498
+ }
499
+ }()
500
+ }
501
+
502
+ go func () { shutdownCh <- p .Serve (ctx , notify ) }()
480
503
481
504
err := <- shutdownCh
482
505
switch {
0 commit comments