github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cmd/roachtest/status_server.go (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package main
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  	"io/ioutil"
    17  	"net/http"
    18  	"time"
    19  
    20  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    21  	"github.com/cockroachdb/cockroach/pkg/server/serverpb"
    22  	"github.com/cockroachdb/cockroach/pkg/util/httputil"
    23  	"github.com/cockroachdb/cockroach/pkg/util/retry"
    24  )
    25  
    26  func runStatusServer(ctx context.Context, t *test, c *cluster) {
    27  	c.Put(ctx, cockroach, "./cockroach")
    28  	c.Start(ctx, t)
    29  
    30  	// Get the ids for each node.
    31  	idMap := make(map[int]roachpb.NodeID)
    32  	urlMap := make(map[int]string)
    33  	for i, addr := range c.ExternalAdminUIAddr(ctx, c.All()) {
    34  		var details serverpb.DetailsResponse
    35  		url := `http://` + addr + `/_status/details/local`
    36  		// Use a retry-loop when populating the maps because we might be trying to
    37  		// talk to the servers before they are responding to status requests
    38  		// (resulting in 404's).
    39  		if err := retry.ForDuration(10*time.Second, func() error {
    40  			return httputil.GetJSON(http.Client{}, url, &details)
    41  		}); err != nil {
    42  			t.Fatal(err)
    43  		}
    44  		idMap[i+1] = details.NodeID
    45  		urlMap[i+1] = `http://` + addr
    46  	}
    47  
    48  	// The status endpoints below may take a while to produce their answer, maybe more
    49  	// than the 3 second timeout of the default http client.
    50  	httpClient := httputil.NewClientWithTimeout(15 * time.Second)
    51  
    52  	// get performs an HTTP GET to the specified path for a specific node.
    53  	get := func(base, rel string) []byte {
    54  		url := base + rel
    55  		resp, err := httpClient.Get(context.TODO(), url)
    56  		if err != nil {
    57  			t.Fatalf("could not GET %s - %s", url, err)
    58  		}
    59  		defer resp.Body.Close()
    60  		body, err := ioutil.ReadAll(resp.Body)
    61  		if err != nil {
    62  			t.Fatalf("could not read body for %s - %s", url, err)
    63  		}
    64  		if resp.StatusCode != http.StatusOK {
    65  			t.Fatalf("could not GET %s - statuscode: %d - body: %s", url, resp.StatusCode, body)
    66  		}
    67  		t.l.Printf("OK response from %s\n", url)
    68  		return body
    69  	}
    70  
    71  	// checkNode checks all the endpoints of the status server hosted by node and
    72  	// requests info for the node with otherNodeID. That node could be the same
    73  	// other node, the same node or "local".
    74  	checkNode := func(url string, nodeID, otherNodeID, expectedNodeID roachpb.NodeID) {
    75  		urlIDs := []string{otherNodeID.String()}
    76  		if nodeID == otherNodeID {
    77  			urlIDs = append(urlIDs, "local")
    78  		}
    79  		var details serverpb.DetailsResponse
    80  		for _, urlID := range urlIDs {
    81  			if err := httputil.GetJSON(http.Client{}, url+`/_status/details/`+urlID, &details); err != nil {
    82  				t.Fatalf("unable to parse details - %s", err)
    83  			}
    84  			if details.NodeID != expectedNodeID {
    85  				t.Fatalf("%d calling %s: node ids don't match - expected %d, actual %d",
    86  					nodeID, urlID, expectedNodeID, details.NodeID)
    87  			}
    88  
    89  			get(url, fmt.Sprintf("/_status/gossip/%s", urlID))
    90  			get(url, fmt.Sprintf("/_status/nodes/%s", urlID))
    91  			get(url, fmt.Sprintf("/_status/logfiles/%s", urlID))
    92  			get(url, fmt.Sprintf("/_status/logs/%s", urlID))
    93  			get(url, fmt.Sprintf("/_status/stacks/%s", urlID))
    94  		}
    95  
    96  		get(url, "/_status/vars")
    97  	}
    98  
    99  	// Check local response for the every node.
   100  	for i := 1; i <= c.spec.NodeCount; i++ {
   101  		id := idMap[i]
   102  		checkNode(urlMap[i], id, id, id)
   103  		get(urlMap[i], "/_status/nodes")
   104  	}
   105  
   106  	// Proxy from the first node to the last node.
   107  	firstNode := 1
   108  	lastNode := c.spec.NodeCount
   109  	firstID := idMap[firstNode]
   110  	lastID := idMap[lastNode]
   111  	checkNode(urlMap[firstNode], firstID, lastID, lastID)
   112  
   113  	// And from the last node to the first node.
   114  	checkNode(urlMap[lastNode], lastID, firstID, firstID)
   115  
   116  	// And from the last node to the last node.
   117  	checkNode(urlMap[lastNode], lastID, lastID, lastID)
   118  }