github.com/kiali/kiali@v1.84.0/routing/router_test.go (about)

     1  package routing
     2  
     3  import (
     4  	"io"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"os"
     8  	rpprof "runtime/pprof"
     9  	"testing"
    10  
    11  	"github.com/gorilla/mux"
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"github.com/stretchr/testify/assert"
    14  
    15  	"github.com/kiali/kiali/config"
    16  	"github.com/kiali/kiali/kubernetes/kubetest"
    17  	"github.com/kiali/kiali/prometheus/internalmetrics"
    18  )
    19  
    20  func TestDrawPathProperly(t *testing.T) {
    21  	conf := new(config.Config)
    22  	mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient())
    23  	router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil)
    24  	testRoute(router, "Root", "GET", t)
    25  }
    26  
    27  func testRoute(router *mux.Router, name string, method string, t *testing.T) {
    28  	path := router.Get(name)
    29  
    30  	if path == nil {
    31  		t.Error("path is not registered into router")
    32  	}
    33  
    34  	methods, err := path.GetMethods()
    35  	if err != nil {
    36  		t.Error(err)
    37  	}
    38  
    39  	if len(methods) != 1 && methods[0] != method {
    40  		t.Error("Root path is not registered with method")
    41  	}
    42  }
    43  
    44  func TestWebRootRedirect(t *testing.T) {
    45  	conf := new(config.Config)
    46  	conf.Server.WebRoot = "/test"
    47  
    48  	mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient())
    49  	router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil)
    50  	ts := httptest.NewServer(router)
    51  	defer ts.Close()
    52  
    53  	client := &http.Client{
    54  		CheckRedirect: func(req *http.Request, via []*http.Request) error {
    55  			return http.ErrUseLastResponse
    56  		},
    57  	}
    58  
    59  	resp, err := client.Get(ts.URL + "/")
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	// body, _ := ioutil.ReadAll(resp.Body)
    64  	assert.Equal(t, 302, resp.StatusCode, "Response should redirect to the webroot")
    65  	assert.Equal(t, "/test/", resp.Header.Get("Location"), "Response should redirect to the webroot")
    66  }
    67  
    68  func TestSimpleRoute(t *testing.T) {
    69  	conf := new(config.Config)
    70  
    71  	mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient())
    72  	router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil)
    73  	ts := httptest.NewServer(router)
    74  	defer ts.Close()
    75  
    76  	resp, err := http.Get(ts.URL + "/healthz")
    77  	if err != nil {
    78  		t.Fatal(err)
    79  	}
    80  	assert.Equal(t, 200, resp.StatusCode, "Response should be ok")
    81  
    82  	body, _ := io.ReadAll(resp.Body)
    83  	assert.Equal(t, "", string(body), "Response should be empty")
    84  }
    85  
    86  func TestProfilerRoute(t *testing.T) {
    87  	conf := new(config.Config)
    88  	conf.Server.Profiler.Enabled = true
    89  
    90  	mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient())
    91  	router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil)
    92  	ts := httptest.NewServer(router)
    93  	defer ts.Close()
    94  
    95  	resp, err := http.Get(ts.URL + "/debug/pprof/")
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  	assert.Equal(t, 400, resp.StatusCode, "pprof index should exist but needed credentials")
   100  
   101  	for _, p := range rpprof.Profiles() {
   102  		resp, err = http.Get(ts.URL + "/debug/pprof/" + p.Name())
   103  		if err != nil {
   104  			t.Fatalf("Failed to get profile [%v]: %v", p, err)
   105  		}
   106  		assert.Equal(t, 400, resp.StatusCode, "pprof profile [%v] should exist but needed credentials", p.Name())
   107  	}
   108  	// note we do not test "profile" endpoint - it takes too long and besides that the test framework eventually times out
   109  	for _, p := range []string{"symbol", "trace"} {
   110  		resp, err = http.Get(ts.URL + "/debug/pprof/" + p)
   111  		if err != nil {
   112  			t.Fatalf("Failed to get profile [%v]: %v", p, err)
   113  		}
   114  		assert.Equal(t, 400, resp.StatusCode, "pprof endpoint [%v] should exist but needed credentials", p)
   115  	}
   116  }
   117  
   118  func TestDisabledProfilerRoute(t *testing.T) {
   119  	conf := new(config.Config)
   120  	conf.Server.Profiler.Enabled = false
   121  
   122  	mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient())
   123  	router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil)
   124  	ts := httptest.NewServer(router)
   125  	defer ts.Close()
   126  
   127  	resp, err := http.Get(ts.URL + "/debug/pprof/")
   128  	if err != nil {
   129  		t.Fatal(err)
   130  	}
   131  	assert.Equal(t, 404, resp.StatusCode, "pprof should have been disabled")
   132  
   133  	for _, p := range rpprof.Profiles() {
   134  		resp, err = http.Get(ts.URL + "/debug/pprof/" + p.Name())
   135  		if err != nil {
   136  			t.Fatal(err)
   137  		}
   138  		assert.Equal(t, 404, resp.StatusCode, "pprof should have been disabled [%v]", p.Name())
   139  	}
   140  	for _, p := range []string{"symbol", "trace", "profile"} {
   141  		resp, err = http.Get(ts.URL + "/debug/pprof/" + p)
   142  		if err != nil {
   143  			t.Fatal(err)
   144  		}
   145  		assert.Equal(t, 404, resp.StatusCode, "pprof should have been disabled [%v]", p)
   146  	}
   147  }
   148  
   149  func TestRedirectWithSetWebRootKeepsParams(t *testing.T) {
   150  	oldWd, _ := os.Getwd()
   151  	defer func() { _ = os.Chdir(oldWd) }()
   152  	_ = os.Chdir(os.TempDir())
   153  	_ = os.MkdirAll("./console", 0o777)
   154  	_, _ = os.Create("./console/index.html")
   155  
   156  	conf := new(config.Config)
   157  	conf.Server.WebRoot = "/test"
   158  
   159  	mockClientFactory := kubetest.NewK8SClientFactoryMock(kubetest.NewFakeK8sClient())
   160  	router, _ := NewRouter(conf, nil, mockClientFactory, nil, nil, nil)
   161  	ts := httptest.NewServer(router)
   162  	defer ts.Close()
   163  
   164  	client := &http.Client{
   165  		CheckRedirect: func(req *http.Request, via []*http.Request) error {
   166  			return http.ErrUseLastResponse
   167  		},
   168  	}
   169  
   170  	resp, err := client.Get(ts.URL + "/test")
   171  	if err != nil {
   172  		t.Fatal(err)
   173  	}
   174  	body, _ := io.ReadAll(resp.Body)
   175  	assert.Equal(t, 200, resp.StatusCode, "Response should not redirect")
   176  
   177  	resp, err = client.Get(ts.URL + "/test/")
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  	body2, _ := io.ReadAll(resp.Body)
   182  	assert.Equal(t, 200, resp.StatusCode, string(body2))
   183  
   184  	assert.Equal(t, string(body), string(body2), "Response with and without the trailing slash on the webroot are not the same")
   185  }
   186  
   187  func TestMetricHandlerAPIFailures(t *testing.T) {
   188  	errcodes := []struct {
   189  		Name string
   190  		Code int
   191  	}{
   192  		{Name: "InternalServerError", Code: http.StatusInternalServerError},
   193  		{Name: "StatusServiceUnavailable", Code: http.StatusServiceUnavailable},
   194  	}
   195  
   196  	for _, errcode := range errcodes {
   197  		t.Run(errcode.Name, func(t *testing.T) {
   198  			req, err := http.NewRequest("GET", "/error", nil)
   199  			if err != nil {
   200  				t.Fatal(err)
   201  			}
   202  
   203  			rr := httptest.NewRecorder()
   204  			handler := metricHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   205  				w.WriteHeader(errcode.Code)
   206  			}), Route{Name: "error"})
   207  
   208  			handler.ServeHTTP(rr, req)
   209  
   210  			if status := rr.Code; status != http.StatusInternalServerError && status != http.StatusServiceUnavailable {
   211  				t.Errorf("handler returned wrong status code: got %v want %v",
   212  					status, errcode.Code)
   213  			}
   214  		})
   215  	}
   216  
   217  	registry := prometheus.NewRegistry()
   218  	prometheus.DefaultRegisterer = registry
   219  	internalmetrics.RegisterInternalMetrics()
   220  
   221  	metrics, err := registry.Gather()
   222  	assert.Nil(t, err)
   223  
   224  	for _, m := range metrics {
   225  		if m.GetName() == "kiali_api_failures_total" {
   226  			if m.GetMetric()[0].Counter.GetValue() != 2 {
   227  				t.Errorf("Failure counter metric should have a value of 2: %+v", m)
   228  			}
   229  		}
   230  	}
   231  }