sigs.k8s.io/controller-runtime@v0.18.2/pkg/healthz/healthz_test.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     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  
    17  package healthz_test
    18  
    19  import (
    20  	"errors"
    21  	"net/http"
    22  	"net/http/httptest"
    23  
    24  	. "github.com/onsi/ginkgo/v2"
    25  	. "github.com/onsi/gomega"
    26  	"sigs.k8s.io/controller-runtime/pkg/healthz"
    27  )
    28  
    29  const (
    30  	contentType = "text/plain; charset=utf-8"
    31  )
    32  
    33  func requestTo(handler http.Handler, dest string) *httptest.ResponseRecorder {
    34  	req, err := http.NewRequest("GET", dest, nil)
    35  	Expect(err).NotTo(HaveOccurred())
    36  	resp := httptest.NewRecorder()
    37  	handler.ServeHTTP(resp, req)
    38  
    39  	return resp
    40  }
    41  
    42  var _ = Describe("Healthz Handler", func() {
    43  	Describe("the aggregated endpoint", func() {
    44  		It("should return healthy if all checks succeed", func() {
    45  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
    46  				"ok1": healthz.Ping,
    47  				"ok2": healthz.Ping,
    48  			}}
    49  
    50  			resp := requestTo(handler, "/")
    51  			Expect(resp.Code).To(Equal(http.StatusOK))
    52  		})
    53  
    54  		It("should return unhealthy if at least one check fails", func() {
    55  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
    56  				"ok1": healthz.Ping,
    57  				"bad1": func(req *http.Request) error {
    58  					return errors.New("blech")
    59  				},
    60  			}}
    61  
    62  			resp := requestTo(handler, "/")
    63  			Expect(resp.Code).To(Equal(http.StatusInternalServerError))
    64  		})
    65  
    66  		It("should ingore excluded checks when determining health", func() {
    67  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
    68  				"ok1": healthz.Ping,
    69  				"bad1": func(req *http.Request) error {
    70  					return errors.New("blech")
    71  				},
    72  			}}
    73  
    74  			resp := requestTo(handler, "/?exclude=bad1")
    75  			Expect(resp.Code).To(Equal(http.StatusOK))
    76  		})
    77  
    78  		It("should be fine if asked to exclude a check that doesn't exist", func() {
    79  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
    80  				"ok1": healthz.Ping,
    81  				"ok2": healthz.Ping,
    82  			}}
    83  
    84  			resp := requestTo(handler, "/?exclude=nonexistant")
    85  			Expect(resp.Code).To(Equal(http.StatusOK))
    86  		})
    87  
    88  		Context("when verbose output is requested with ?verbose=true", func() {
    89  			It("should return verbose output for ok cases", func() {
    90  				handler := &healthz.Handler{Checks: map[string]healthz.Checker{
    91  					"ok1": healthz.Ping,
    92  					"ok2": healthz.Ping,
    93  				}}
    94  
    95  				resp := requestTo(handler, "/?verbose=true")
    96  				Expect(resp.Code).To(Equal(http.StatusOK))
    97  				Expect(resp.Header().Get("Content-Type")).To(Equal(contentType))
    98  				Expect(resp.Body.String()).To(Equal("[+]ok1 ok\n[+]ok2 ok\nhealthz check passed\n"))
    99  			})
   100  
   101  			It("should return verbose output for failures", func() {
   102  				handler := &healthz.Handler{Checks: map[string]healthz.Checker{
   103  					"ok1": healthz.Ping,
   104  					"bad1": func(req *http.Request) error {
   105  						return errors.New("blech")
   106  					},
   107  				}}
   108  
   109  				resp := requestTo(handler, "/?verbose=true")
   110  				Expect(resp.Code).To(Equal(http.StatusInternalServerError))
   111  				Expect(resp.Header().Get("Content-Type")).To(Equal(contentType))
   112  				Expect(resp.Body.String()).To(Equal("[-]bad1 failed: reason withheld\n[+]ok1 ok\nhealthz check failed\n"))
   113  			})
   114  		})
   115  
   116  		It("should return non-verbose output when healthy and not specified as verbose", func() {
   117  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
   118  				"ok1": healthz.Ping,
   119  				"ok2": healthz.Ping,
   120  			}}
   121  
   122  			resp := requestTo(handler, "/")
   123  			Expect(resp.Header().Get("Content-Type")).To(Equal(contentType))
   124  			Expect(resp.Body.String()).To(Equal("ok"))
   125  
   126  		})
   127  
   128  		It("should always be verbose if a check fails", func() {
   129  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
   130  				"ok1": healthz.Ping,
   131  				"bad1": func(req *http.Request) error {
   132  					return errors.New("blech")
   133  				},
   134  			}}
   135  
   136  			resp := requestTo(handler, "/")
   137  			Expect(resp.Header().Get("Content-Type")).To(Equal(contentType))
   138  			Expect(resp.Body.String()).To(Equal("[-]bad1 failed: reason withheld\n[+]ok1 ok\nhealthz check failed\n"))
   139  		})
   140  
   141  		It("should always return a ping endpoint if no other ones are present", func() {
   142  			resp := requestTo(&healthz.Handler{}, "/?verbose=true")
   143  			Expect(resp.Code).To(Equal(http.StatusOK))
   144  			Expect(resp.Header().Get("Content-Type")).To(Equal(contentType))
   145  			Expect(resp.Body.String()).To(Equal("[+]ping ok\nhealthz check passed\n"))
   146  		})
   147  	})
   148  
   149  	Describe("the per-check endpoints", func() {
   150  		It("should return ok if the requested check is healthy", func() {
   151  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
   152  				"okcheck": healthz.Ping,
   153  			}}
   154  
   155  			resp := requestTo(handler, "/okcheck")
   156  			Expect(resp.Code).To(Equal(http.StatusOK))
   157  			Expect(resp.Header().Get("Content-Type")).To(Equal(contentType))
   158  			Expect(resp.Body.String()).To(Equal("ok"))
   159  		})
   160  
   161  		It("should return an error if the requested check is unhealthy", func() {
   162  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
   163  				"failcheck": func(req *http.Request) error {
   164  					return errors.New("blech")
   165  				},
   166  			}}
   167  
   168  			resp := requestTo(handler, "/failcheck")
   169  			Expect(resp.Code).To(Equal(http.StatusInternalServerError))
   170  			Expect(resp.Header().Get("Content-Type")).To(Equal(contentType))
   171  			Expect(resp.Body.String()).To(Equal("internal server error: blech\n"))
   172  		})
   173  
   174  		It("shouldn't take other checks into account", func() {
   175  			handler := &healthz.Handler{Checks: map[string]healthz.Checker{
   176  				"failcheck": func(req *http.Request) error {
   177  					return errors.New("blech")
   178  				},
   179  				"okcheck": healthz.Ping,
   180  			}}
   181  
   182  			By("checking the bad endpoint and expecting it to fail")
   183  			resp := requestTo(handler, "/failcheck")
   184  			Expect(resp.Code).To(Equal(http.StatusInternalServerError))
   185  
   186  			By("checking the good endpoint and expecting it to succeed")
   187  			resp = requestTo(handler, "/okcheck")
   188  			Expect(resp.Code).To(Equal(http.StatusOK))
   189  		})
   190  
   191  		It("should return non-found for paths that don't match a checker", func() {
   192  			handler := &healthz.Handler{}
   193  
   194  			resp := requestTo(handler, "/doesnotexist")
   195  			Expect(resp.Code).To(Equal(http.StatusNotFound))
   196  		})
   197  
   198  		It("should always return a ping endpoint if no other ones are present", func() {
   199  			resp := requestTo(&healthz.Handler{}, "/ping")
   200  			Expect(resp.Code).To(Equal(http.StatusOK))
   201  		})
   202  	})
   203  })