github.com/icyphox/x@v0.0.355-0.20220311094250-029bd783e8b8/healthx/handler.go (about) 1 /* 2 * Copyright © 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io> 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * @author Aeneas Rekkas <aeneas+oss@aeneas.io> 17 * @copyright 2015-2018 Aeneas Rekkas <aeneas+oss@aeneas.io> 18 * @license Apache-2.0 19 */ 20 21 package healthx 22 23 import ( 24 "net/http" 25 26 "github.com/julienschmidt/httprouter" 27 28 "github.com/ory/herodot" 29 ) 30 31 const ( 32 // AliveCheckPath is the path where information about the life state of the instance is provided. 33 AliveCheckPath = "/health/alive" 34 // ReadyCheckPath is the path where information about the rady state of the instance is provided. 35 ReadyCheckPath = "/health/ready" 36 // VersionPath is the path where information about the software version of the instance is provided. 37 VersionPath = "/version" 38 ) 39 40 // RoutesToObserve returns a string of all the available routes of this module. 41 func RoutesToObserve() []string { 42 return []string{ 43 AliveCheckPath, 44 ReadyCheckPath, 45 VersionPath, 46 } 47 } 48 49 // ReadyChecker should return an error if the component is not ready yet. 50 type ReadyChecker func(r *http.Request) error 51 52 // ReadyCheckers is a map of ReadyCheckers. 53 type ReadyCheckers map[string]ReadyChecker 54 55 // NoopReadyChecker is always ready. 56 func NoopReadyChecker() error { 57 return nil 58 } 59 60 // Handler handles HTTP requests to health and version endpoints. 61 type Handler struct { 62 H herodot.Writer 63 VersionString string 64 ReadyChecks ReadyCheckers 65 } 66 67 // NewHandler instantiates a handler. 68 func NewHandler( 69 h herodot.Writer, 70 version string, 71 readyChecks ReadyCheckers, 72 ) *Handler { 73 return &Handler{ 74 H: h, 75 VersionString: version, 76 ReadyChecks: readyChecks, 77 } 78 } 79 80 type router interface { 81 GET(path string, handle httprouter.Handle) 82 } 83 84 // SetHealthRoutes registers this handler's routes for health checking. 85 func (h *Handler) SetHealthRoutes(r router, shareErrors bool) { 86 r.GET(AliveCheckPath, h.Alive) 87 r.GET(ReadyCheckPath, h.Ready(shareErrors)) 88 } 89 90 // SetHealthRoutes registers this handler's routes for health checking. 91 func (h *Handler) SetVersionRoutes(r router) { 92 r.GET(VersionPath, h.Version) 93 } 94 95 // Alive returns an ok status if the instance is ready to handle HTTP requests. 96 // 97 // swagger:route GET /health/alive health isInstanceAlive 98 // 99 // Check alive status 100 // 101 // This endpoint returns a 200 status code when the HTTP server is up running. 102 // This status does currently not include checks whether the database connection is working. 103 // 104 // If the service supports TLS Edge Termination, this endpoint does not require the 105 // `X-Forwarded-Proto` header to be set. 106 // 107 // Be aware that if you are running multiple nodes of this service, the health status will never 108 // refer to the cluster state, only to a single instance. 109 // 110 // Produces: 111 // - application/json 112 // 113 // Responses: 114 // 200: healthStatus 115 // 500: genericError 116 func (h *Handler) Alive(rw http.ResponseWriter, r *http.Request, _ httprouter.Params) { 117 h.H.Write(rw, r, &swaggerHealthStatus{ 118 Status: "ok", 119 }) 120 } 121 122 // Ready returns an ok status if the instance is ready to handle HTTP requests and all ReadyCheckers are ok. 123 // 124 // swagger:route GET /health/ready health isInstanceReady 125 // 126 // Check readiness status 127 // 128 // This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g. 129 // the database) are responsive as well. 130 // 131 // If the service supports TLS Edge Termination, this endpoint does not require the 132 // `X-Forwarded-Proto` header to be set. 133 // 134 // Be aware that if you are running multiple nodes of this service, the health status will never 135 // refer to the cluster state, only to a single instance. 136 // 137 // Produces: 138 // - application/json 139 // 140 // Responses: 141 // 200: healthStatus 142 // 503: healthNotReadyStatus 143 func (h *Handler) Ready(shareErrors bool) httprouter.Handle { 144 return func(rw http.ResponseWriter, r *http.Request, _ httprouter.Params) { 145 var notReady = swaggerNotReadyStatus{ 146 Errors: map[string]string{}, 147 } 148 149 for n, c := range h.ReadyChecks { 150 if err := c(r); err != nil { 151 if shareErrors { 152 notReady.Errors[n] = err.Error() 153 } else { 154 notReady.Errors[n] = "error may contain sensitive information and was obfuscated" 155 } 156 } 157 } 158 159 if len(notReady.Errors) > 0 { 160 h.H.WriteCode(rw, r, http.StatusServiceUnavailable, notReady) 161 return 162 } 163 164 h.H.Write(rw, r, &swaggerHealthStatus{ 165 Status: "ok", 166 }) 167 } 168 } 169 170 // Version returns this service's versions. 171 // 172 // swagger:route GET /version version getVersion 173 // 174 // Get service version 175 // 176 // This endpoint returns the service version typically notated using semantic versioning. 177 // 178 // If the service supports TLS Edge Termination, this endpoint does not require the 179 // `X-Forwarded-Proto` header to be set. 180 // 181 // Be aware that if you are running multiple nodes of this service, the health status will never 182 // refer to the cluster state, only to a single instance. 183 // 184 // Produces: 185 // - application/json 186 // 187 // Responses: 188 // 200: version 189 func (h *Handler) Version(rw http.ResponseWriter, r *http.Request, _ httprouter.Params) { 190 h.H.Write(rw, r, &swaggerVersion{ 191 Version: h.VersionString, 192 }) 193 }