github.com/google/cloudprober@v0.11.3/probes/http/request_test.go (about)

     1  // Copyright 2019-2020 The Cloudprober Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package http
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"testing"
    21  
    22  	"github.com/golang/protobuf/proto"
    23  	configpb "github.com/google/cloudprober/probes/http/proto"
    24  	"github.com/google/cloudprober/probes/options"
    25  	"github.com/google/cloudprober/targets"
    26  	"github.com/google/cloudprober/targets/endpoint"
    27  )
    28  
    29  func TestHostWithPort(t *testing.T) {
    30  	for _, test := range []struct {
    31  		host         string
    32  		port         int
    33  		wantHostPort string
    34  	}{
    35  		{
    36  			host:         "target1.ns.cluster.local",
    37  			wantHostPort: "target1.ns.cluster.local",
    38  		},
    39  		{
    40  			host:         "target1.ns.cluster.local",
    41  			port:         8080,
    42  			wantHostPort: "target1.ns.cluster.local:8080",
    43  		},
    44  	} {
    45  		t.Run(fmt.Sprintf("host:%s,port:%d", test.host, test.port), func(t *testing.T) {
    46  			hostPort := hostWithPort(test.host, test.port)
    47  			if hostPort != test.wantHostPort {
    48  				t.Errorf("hostPort: %s, want: %s", hostPort, test.wantHostPort)
    49  			}
    50  		})
    51  	}
    52  }
    53  
    54  func TestURLHostAndHeaderForTarget(t *testing.T) {
    55  	for _, test := range []struct {
    56  		name            string
    57  		fqdn            string
    58  		probeHostHeader string
    59  		port            int
    60  		wantHostHeader  string
    61  		wantURLHost     string
    62  	}{
    63  		{
    64  			name:            "target1",
    65  			fqdn:            "target1.ns.cluster.local",
    66  			probeHostHeader: "svc.target",
    67  			port:            8080,
    68  			wantHostHeader:  "svc.target",
    69  			wantURLHost:     "target1.ns.cluster.local",
    70  		},
    71  		{
    72  			name:            "target1",
    73  			fqdn:            "target1.ns.cluster.local",
    74  			probeHostHeader: "",
    75  			port:            8080,
    76  			wantHostHeader:  "target1.ns.cluster.local:8080",
    77  			wantURLHost:     "target1.ns.cluster.local",
    78  		},
    79  		{
    80  			name:            "target1",
    81  			fqdn:            "target1.ns.cluster.local",
    82  			probeHostHeader: "",
    83  			port:            0,
    84  			wantHostHeader:  "target1.ns.cluster.local",
    85  			wantURLHost:     "target1.ns.cluster.local",
    86  		},
    87  		{
    88  			name:            "target1",
    89  			fqdn:            "",
    90  			probeHostHeader: "",
    91  			port:            8080,
    92  			wantHostHeader:  "target1:8080",
    93  			wantURLHost:     "target1",
    94  		},
    95  		{
    96  			name:            "target1",
    97  			fqdn:            "",
    98  			probeHostHeader: "",
    99  			port:            0,
   100  			wantHostHeader:  "target1",
   101  			wantURLHost:     "target1",
   102  		},
   103  	} {
   104  		t.Run(fmt.Sprintf("test:%+v", test), func(t *testing.T) {
   105  			target := endpoint.Endpoint{
   106  				Name:   test.name,
   107  				Labels: map[string]string{"fqdn": test.fqdn},
   108  			}
   109  
   110  			hostHeader := hostHeaderForTarget(target, test.probeHostHeader, test.port)
   111  			if hostHeader != test.wantHostHeader {
   112  				t.Errorf("Got host header: %s, want header: %s", hostHeader, test.wantHostHeader)
   113  			}
   114  
   115  			urlHost := urlHostForTarget(target)
   116  			if urlHost != test.wantURLHost {
   117  				t.Errorf("Got URL host: %s, want URL host: %s", urlHost, test.wantURLHost)
   118  			}
   119  		})
   120  	}
   121  }
   122  
   123  func TestRelURLforTarget(t *testing.T) {
   124  	for _, test := range []struct {
   125  		targetURLLabel string
   126  		probeURL       string
   127  		wantRelURL     string
   128  	}{
   129  		{
   130  			// Both set, probe URL wins.
   131  			targetURLLabel: "/target-url",
   132  			probeURL:       "/metrics",
   133  			wantRelURL:     "/metrics",
   134  		},
   135  		{
   136  			// Only target label set.
   137  			targetURLLabel: "/target-url",
   138  			probeURL:       "",
   139  			wantRelURL:     "/target-url",
   140  		},
   141  		{
   142  			// Nothing set, we get nothing.
   143  			targetURLLabel: "",
   144  			probeURL:       "",
   145  			wantRelURL:     "",
   146  		},
   147  	} {
   148  		t.Run(fmt.Sprintf("test:%+v", test), func(t *testing.T) {
   149  			target := endpoint.Endpoint{
   150  				Name:   "test-target",
   151  				Labels: map[string]string{relURLLabel: test.targetURLLabel},
   152  			}
   153  
   154  			relURL := relURLForTarget(target, test.probeURL)
   155  			if relURL != test.wantRelURL {
   156  				t.Errorf("Got URL: %s, want: %s", relURL, test.wantRelURL)
   157  			}
   158  		})
   159  	}
   160  }
   161  
   162  // Following tests are more comprehensive tests for request URL and host header.
   163  type testData struct {
   164  	desc       string
   165  	targetName string
   166  	targetFQDN string // Make target have this "fqdn" label.
   167  	resolvedIP string // IP that will be returned by the test resolve function.
   168  
   169  	// Probe configuration parameters
   170  	resolveFirst bool
   171  	probeHost    string
   172  
   173  	// Used by TestURLHostAndHeaderForTarget to verify URL host, and
   174  	// TestRequestHostAndURL to build URL to verify.
   175  	wantURLHost string
   176  }
   177  
   178  func createRequestAndVerify(t *testing.T, td testData, probePort, targetPort, expectedPort int, resolveF resolveFunc) {
   179  	t.Helper()
   180  
   181  	p := &Probe{
   182  		protocol: "http",
   183  		c: &configpb.ProbeConf{
   184  			ResolveFirst: proto.Bool(td.resolveFirst),
   185  		},
   186  		opts: &options.Options{Targets: targets.StaticTargets(td.targetName)},
   187  	}
   188  
   189  	if probePort != 0 {
   190  		p.c.Port = proto.Int32(int32(probePort))
   191  	}
   192  
   193  	if td.probeHost != "" {
   194  		p.c.Headers = append(p.c.Headers, &configpb.ProbeConf_Header{
   195  			Name:  proto.String("Host"),
   196  			Value: proto.String(td.probeHost),
   197  		})
   198  	}
   199  
   200  	target := endpoint.Endpoint{
   201  		Name: td.targetName,
   202  		Port: targetPort,
   203  		Labels: map[string]string{
   204  			"fqdn": td.targetFQDN,
   205  		},
   206  	}
   207  	req := p.httpRequestForTarget(target, resolveF)
   208  
   209  	wantURL := fmt.Sprintf("http://%s", hostWithPort(td.wantURLHost, expectedPort))
   210  	if req.URL.String() != wantURL {
   211  		t.Errorf("HTTP req URL: %s, wanted: %s", req.URL.String(), wantURL)
   212  	}
   213  
   214  	// Note that we test hostHeaderForTarget independently.
   215  	wantHostHeader := hostHeaderForTarget(target, td.probeHost, expectedPort)
   216  	if req.Host != wantHostHeader {
   217  		t.Errorf("HTTP req.Host: %s, wanted: %s", req.Host, wantHostHeader)
   218  	}
   219  }
   220  
   221  func testRequestHostAndURLWithDifferentPorts(t *testing.T, td testData) {
   222  	t.Helper()
   223  
   224  	var resolveF resolveFunc
   225  	if td.resolveFirst {
   226  		resolveF = func(target string, ipVer int) (net.IP, error) {
   227  			return net.ParseIP(td.resolvedIP), nil
   228  		}
   229  	}
   230  
   231  	for _, ports := range []struct {
   232  		probePort    int
   233  		targetPort   int
   234  		expectedPort int
   235  	}{
   236  		{
   237  			probePort:    0,
   238  			targetPort:   0,
   239  			expectedPort: 0,
   240  		},
   241  		{
   242  			probePort:    8080,
   243  			targetPort:   9313,
   244  			expectedPort: 8080, // probe port wins
   245  		},
   246  		{
   247  			probePort:    0,
   248  			targetPort:   9313,
   249  			expectedPort: 9313, // target port wins
   250  		},
   251  	} {
   252  		t.Run(fmt.Sprintf("%s_probe_port_%d_endpoint_port_%d", td.desc, ports.probePort, ports.targetPort), func(t *testing.T) {
   253  			createRequestAndVerify(t, td, ports.probePort, ports.targetPort, ports.expectedPort, resolveF)
   254  		})
   255  	}
   256  }
   257  
   258  func TestRequestHostAndURL(t *testing.T) {
   259  	tests := []testData{
   260  		{
   261  			desc:        "no_resolve_first,no_probe_host_header",
   262  			targetName:  "test-target.com",
   263  			wantURLHost: "test-target.com",
   264  		},
   265  		{
   266  			desc:        "no_resolve_first,fqdn,no_probe_host_header",
   267  			targetName:  "test-target.com",
   268  			targetFQDN:  "test.svc.cluster.local",
   269  			wantURLHost: "test.svc.cluster.local",
   270  		},
   271  		{
   272  			desc:        "no_resolve_first,host_header",
   273  			targetName:  "test-target.com",
   274  			probeHost:   "test-host",
   275  			wantURLHost: "test-target.com",
   276  		},
   277  		{
   278  			desc:        "ipv6_literal_host,no_probe_host_header",
   279  			targetName:  "2600:2d00:4030:a47:c0a8:210d:0:0", // IPv6 literal host
   280  			wantURLHost: "[2600:2d00:4030:a47:c0a8:210d:0:0]",
   281  		},
   282  		{
   283  			desc:         "resolve_first,no_probe_host_header",
   284  			targetName:   "localhost",
   285  			resolveFirst: true,
   286  			resolvedIP:   "127.0.0.1",
   287  			wantURLHost:  "127.0.0.1",
   288  		},
   289  		{
   290  			desc:         "resolve_first,ipv6,no_probe_host_header",
   291  			targetName:   "localhost",
   292  			resolveFirst: true,
   293  			resolvedIP:   "2600:2d00:4030:a47:c0a8:210d:0:0", // Resolved IP
   294  			wantURLHost:  "[2600:2d00:4030:a47:c0a8:210d::]", // IPv6 literal host
   295  		},
   296  		{
   297  			desc:         "resolve_first,probe_host_header",
   298  			targetName:   "localhost",
   299  			resolveFirst: true,
   300  			probeHost:    "test-host",
   301  			resolvedIP:   "127.0.0.1",
   302  			wantURLHost:  "127.0.0.1",
   303  		},
   304  	}
   305  
   306  	for _, td := range tests {
   307  		t.Run(td.desc, func(t *testing.T) {
   308  			testRequestHostAndURLWithDifferentPorts(t, td)
   309  		})
   310  	}
   311  }