diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 870e82ead..ec51f697f 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -1,9 +1,11 @@ package main import ( + "crypto/tls" "encoding/json" "fmt" fmtlog "log" + "net/http" "os" "path/filepath" "reflect" @@ -97,6 +99,43 @@ Complete documentation is available at https://traefik.io`, }, } + healthcheckCmd := &flaeg.Command{ + Name: "healthcheck", + Description: `Calls traefik /ping to check health (web provider must be enabled)`, + Config: traefikConfiguration, + DefaultPointersConfig: traefikPointersConfiguration, + Run: func() error { + if traefikConfiguration.Web == nil { + fmt.Println("Please enable the web provider to use healtcheck.") + os.Exit(1) + } + client := &http.Client{Timeout: 5 * time.Second} + protocol := "http" + if len(traefikConfiguration.Web.CertFile) > 0 { + protocol = "https" + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + client.Transport = tr + } + resp, err := client.Head(protocol + "://" + traefikConfiguration.Web.Address + "/ping") + if err != nil { + fmt.Printf("Error calling healthcheck: %s\n", err) + os.Exit(1) + } + if resp.StatusCode != http.StatusOK { + fmt.Printf("Bad healthcheck status: %s\n", resp.Status) + os.Exit(1) + } + fmt.Printf("OK: %s\n", resp.Request.URL) + os.Exit(0) + return nil + }, + Metadata: map[string]string{ + "parseAllSources": "true", + }, + } + //init flaeg source f := flaeg.New(traefikCmd, os.Args[1:]) //add custom parsers @@ -112,6 +151,7 @@ Complete documentation is available at https://traefik.io`, f.AddCommand(newVersionCmd()) f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration)) f.AddCommand(storeconfigCmd) + f.AddCommand(healthcheckCmd) usedCmd, err := f.GetCommand() if err != nil { diff --git a/docs/basics.md b/docs/basics.md index 8416d37a2..3f8bdbbb9 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -514,6 +514,7 @@ List of Træfik available commands with description :              - `version` : Print version  - `storeconfig` : Store the static traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it. - `bug`: The easiest way to submit a pre-filled issue. +- `healthcheck`: Calls traefik `/ping` to check health. Each command may have related flags. All those related flags will be displayed with : @@ -538,6 +539,18 @@ $ traefik bug See https://www.youtube.com/watch?v=Lyz62L8m93I. +## Command: healthcheck + +This command allows to check the health of Traefik. Its exit status is `0` if Traefik is healthy and `1` if it is unhealthy. +This can be used with Docker [HEALTHCHECK](https://docs.docker.com/engine/reference/builder/#healthcheck) instruction or any other health check orchestration mechanism. + +Note: the `web` provider must be enabled to allow `/ping` calls by the `healthcheck` command. + +```bash +$ traefik healthcheck +OK: http://:8082/ping +``` + # Log Rotation Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal. This allows the logs diff --git a/server/web.go b/server/web.go index cddf6f272..647a7b6bd 100644 --- a/server/web.go +++ b/server/web.go @@ -136,20 +136,27 @@ func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessag safe.Go(func() { var err error - var negroni = negroni.New() + var negroniInstance = negroni.New() if provider.Auth != nil { authMiddleware, err := middlewares.NewAuthenticator(provider.Auth) if err != nil { log.Fatal("Error creating Auth: ", err) } - negroni.Use(authMiddleware) + authMiddlewareWrapper := negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if r.URL.Path == "/ping" { + next.ServeHTTP(w, r) + } else { + authMiddleware.ServeHTTP(w, r, next) + } + }) + negroniInstance.Use(authMiddlewareWrapper) } - negroni.UseHandler(systemRouter) + negroniInstance.UseHandler(systemRouter) if len(provider.CertFile) > 0 && len(provider.KeyFile) > 0 { - err = http.ListenAndServeTLS(provider.Address, provider.CertFile, provider.KeyFile, negroni) + err = http.ListenAndServeTLS(provider.Address, provider.CertFile, provider.KeyFile, negroniInstance) } else { - err = http.ListenAndServe(provider.Address, negroni) + err = http.ListenAndServe(provider.Address, negroniInstance) } if err != nil {