1 // Package health provides a generic health checking framework. 2 // The health package works expvar style. By importing the package the debug 3 // server is getting a "/debug/health" endpoint that returns the current 4 // status of the application. 5 // If there are no errors, "/debug/health" will return an HTTP 200 status, 6 // together with an empty JSON reply "{}". If there are any checks 7 // with errors, the JSON reply will include all the failed checks, and the 8 // response will be have an HTTP 503 status. 9 // 10 // A Check can either be run synchronously, or asynchronously. We recommend 11 // that most checks are registered as an asynchronous check, so a call to the 12 // "/debug/health" endpoint always returns immediately. This pattern is 13 // particularly useful for checks that verify upstream connectivity or 14 // database status, since they might take a long time to return/timeout. 15 // 16 // # Installing 17 // 18 // To install health, just import it in your application: 19 // 20 // import "github.com/docker/distribution/health" 21 // 22 // You can also (optionally) import "health/api" that will add two convenience 23 // endpoints: "/debug/health/down" and "/debug/health/up". These endpoints add 24 // "manual" checks that allow the service to quickly be brought in/out of 25 // rotation. 26 // 27 // import _ "github.com/docker/distribution/health/api" 28 // 29 // # curl localhost:5001/debug/health 30 // {} 31 // # curl -X POST localhost:5001/debug/health/down 32 // # curl localhost:5001/debug/health 33 // {"manual_http_status":"Manual Check"} 34 // 35 // After importing these packages to your main application, you can start 36 // registering checks. 37 // 38 // # Registering Checks 39 // 40 // The recommended way of registering checks is using a periodic Check. 41 // PeriodicChecks run on a certain schedule and asynchronously update the 42 // status of the check. This allows CheckStatus to return without blocking 43 // on an expensive check. 44 // 45 // A trivial example of a check that runs every 5 seconds and shuts down our 46 // server if the current minute is even, could be added as follows: 47 // 48 // func currentMinuteEvenCheck() error { 49 // m := time.Now().Minute() 50 // if m%2 == 0 { 51 // return errors.New("Current minute is even!") 52 // } 53 // return nil 54 // } 55 // 56 // health.RegisterPeriodicFunc("minute_even", currentMinuteEvenCheck, time.Second*5) 57 // 58 // Alternatively, you can also make use of "RegisterPeriodicThresholdFunc" to 59 // implement the exact same check, but add a threshold of failures after which 60 // the check will be unhealthy. This is particularly useful for flaky Checks, 61 // ensuring some stability of the service when handling them. 62 // 63 // health.RegisterPeriodicThresholdFunc("minute_even", currentMinuteEvenCheck, time.Second*5, 4) 64 // 65 // The lowest-level way to interact with the health package is calling 66 // "Register" directly. Register allows you to pass in an arbitrary string and 67 // something that implements "Checker" and runs your check. If your method 68 // returns an error with nil, it is considered a healthy check, otherwise it 69 // will make the health check endpoint "/debug/health" start returning a 503 70 // and list the specific check that failed. 71 // 72 // Assuming you wish to register a method called "currentMinuteEvenCheck() 73 // error" you could do that by doing: 74 // 75 // health.Register("even_minute", health.CheckFunc(currentMinuteEvenCheck)) 76 // 77 // CheckFunc is a convenience type that implements Checker. 78 // 79 // Another way of registering a check could be by using an anonymous function 80 // and the convenience method RegisterFunc. An example that makes the status 81 // endpoint always return an error: 82 // 83 // health.RegisterFunc("my_check", func() error { 84 // return Errors.new("This is an error!") 85 // })) 86 // 87 // # Examples 88 // 89 // You could also use the health checker mechanism to ensure your application 90 // only comes up if certain conditions are met, or to allow the developer to 91 // take the service out of rotation immediately. An example that checks 92 // database connectivity and immediately takes the server out of rotation on 93 // err: 94 // 95 // updater = health.NewStatusUpdater() 96 // health.RegisterFunc("database_check", func() error { 97 // return updater.Check() 98 // })) 99 // 100 // conn, err := Connect(...) // database call here 101 // if err != nil { 102 // updater.Update(errors.New("Error connecting to the database: " + err.Error())) 103 // } 104 // 105 // You can also use the predefined Checkers that come included with the health 106 // package. First, import the checks: 107 // 108 // import "github.com/docker/distribution/health/checks 109 // 110 // After that you can make use of any of the provided checks. An example of 111 // using a `FileChecker` to take the application out of rotation if a certain 112 // file exists can be done as follows: 113 // 114 // health.Register("fileChecker", health.PeriodicChecker(checks.FileChecker("/tmp/disable"), time.Second*5)) 115 // 116 // After registering the check, it is trivial to take an application out of 117 // rotation from the console: 118 // 119 // # curl localhost:5001/debug/health 120 // {} 121 // # touch /tmp/disable 122 // # curl localhost:5001/debug/health 123 // {"fileChecker":"file exists"} 124 // 125 // FileChecker only accepts absolute or relative file path. It does not work 126 // properly with tilde(~). You should make sure that the application has 127 // proper permission(read and execute permission for directory along with 128 // the specified file path). Otherwise, the FileChecker will report error 129 // and file health check is not ok. 130 // 131 // You could also test the connectivity to a downstream service by using a 132 // "HTTPChecker", but ensure that you only mark the test unhealthy if there 133 // are a minimum of two failures in a row: 134 // 135 // health.Register("httpChecker", health.PeriodicThresholdChecker(checks.HTTPChecker("https://www.google.pt"), time.Second*5, 2)) 136 package health 137