From 9f683d2d2b988bf55cc753c52e3fa31868194094 Mon Sep 17 00:00:00 2001 From: Peter Mattis Date: Fri, 31 Aug 2018 22:06:05 -0400 Subject: [PATCH] roachtest: add acceptance/status-server Move the status-server acceptance test to a new acceptance/status-server roachtest. See #29151 Release note: None --- pkg/acceptance/status_server_test.go | 147 --------------------------- pkg/cmd/roachtest/acceptance.go | 1 + pkg/cmd/roachtest/status_server.go | 112 ++++++++++++++++++++ 3 files changed, 113 insertions(+), 147 deletions(-) delete mode 100644 pkg/acceptance/status_server_test.go create mode 100644 pkg/cmd/roachtest/status_server.go diff --git a/pkg/acceptance/status_server_test.go b/pkg/acceptance/status_server_test.go deleted file mode 100644 index 6e9689a5adb8..000000000000 --- a/pkg/acceptance/status_server_test.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2015 The Cockroach Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -// implied. See the License for the specific language governing -// permissions and limitations under the License. - -package acceptance - -import ( - "context" - "fmt" - "io/ioutil" - "net/http" - "testing" - "time" - - "github.com/pkg/errors" - - "github.com/cockroachdb/cockroach/pkg/acceptance/cluster" - "github.com/cockroachdb/cockroach/pkg/roachpb" - "github.com/cockroachdb/cockroach/pkg/server/serverpb" - "github.com/cockroachdb/cockroach/pkg/util/httputil" - "github.com/cockroachdb/cockroach/pkg/util/log" - "github.com/cockroachdb/cockroach/pkg/util/retry" -) - -var retryOptions = retry.Options{ - InitialBackoff: 100 * time.Millisecond, - MaxRetries: 4, - Multiplier: 2, -} - -// get performs an HTTPS GET to the specified path for a specific node. -func get(ctx context.Context, t *testing.T, base, rel string) []byte { - // TODO(bram): #2059: Remove retry logic. - url := base + rel - for r := retry.Start(retryOptions); r.Next(); { - resp, err := cluster.HTTPClient.Get(url) - if err != nil { - log.Infof(ctx, "could not GET %s - %s", url, err) - continue - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Infof(ctx, "could not read body for %s - %s", url, err) - continue - } - if resp.StatusCode != http.StatusOK { - log.Infof(ctx, "could not GET %s - statuscode: %d - body: %s", url, resp.StatusCode, body) - continue - } - if log.V(1) { - log.Infof(ctx, "OK response from %s", url) - } - return body - } - t.Fatalf("There was an error retrieving %s", url) - return []byte("") -} - -// checkNode checks all the endpoints of the status server hosted by node and -// requests info for the node with otherNodeID. That node could be the same -// other node, the same node or "local". -func checkNode( - ctx context.Context, - t *testing.T, - c cluster.Cluster, - i int, - nodeID, - otherNodeID, expectedNodeID roachpb.NodeID, -) { - urlIDs := []string{otherNodeID.String()} - if nodeID == otherNodeID { - urlIDs = append(urlIDs, "local") - } - var details serverpb.DetailsResponse - for _, urlID := range urlIDs { - if err := httputil.GetJSON(cluster.HTTPClient, c.URL(ctx, i)+"/_status/details/"+urlID, &details); err != nil { - t.Fatal(errors.Errorf("unable to parse details - %s", err)) - } - if details.NodeID != expectedNodeID { - t.Fatal(errors.Errorf("%d calling %s: node ids don't match - expected %d, actual %d", nodeID, urlID, expectedNodeID, details.NodeID)) - } - - get(ctx, t, c.URL(ctx, i), fmt.Sprintf("/_status/gossip/%s", urlID)) - get(ctx, t, c.URL(ctx, i), fmt.Sprintf("/_status/nodes/%s", urlID)) - get(ctx, t, c.URL(ctx, i), fmt.Sprintf("/_status/logfiles/%s", urlID)) - get(ctx, t, c.URL(ctx, i), fmt.Sprintf("/_status/logs/%s", urlID)) - get(ctx, t, c.URL(ctx, i), fmt.Sprintf("/_status/stacks/%s", urlID)) - } - - get(ctx, t, c.URL(ctx, i), "/_status/vars") -} - -// TestStatusServer starts up an N node cluster and tests the status server on -// each node. -func TestStatusServer(t *testing.T) { - s := log.Scope(t) - defer s.Close(t) - - RunLocal(t, func(t *testing.T) { - runTestWithCluster(t, testStatusServerInner) - }) -} - -func testStatusServerInner( - ctx context.Context, t *testing.T, c cluster.Cluster, cfg cluster.TestConfig, -) { - // Get the ids for each node. - idMap := make(map[int]roachpb.NodeID) - for i := 0; i < c.NumNodes(); i++ { - var details serverpb.DetailsResponse - if err := httputil.GetJSON(cluster.HTTPClient, c.URL(ctx, i)+"/_status/details/local", &details); err != nil { - t.Fatal(err) - } - idMap[i] = details.NodeID - } - - // Check local response for the every node. - for i := 0; i < c.NumNodes(); i++ { - id := idMap[i] - checkNode(ctx, t, c, i, id, id, id) - get(ctx, t, c.URL(ctx, i), "/_status/nodes") - } - - // Proxy from the first node to the last node. - firstNode := 0 - lastNode := c.NumNodes() - 1 - firstID := idMap[firstNode] - lastID := idMap[lastNode] - checkNode(ctx, t, c, firstNode, firstID, lastID, lastID) - - // And from the last node to the first node. - checkNode(ctx, t, c, lastNode, lastID, firstID, firstID) - - // And from the last node to the last node. - checkNode(ctx, t, c, lastNode, lastID, lastID, lastID) -} diff --git a/pkg/cmd/roachtest/acceptance.go b/pkg/cmd/roachtest/acceptance.go index c10bb8883186..9ed5e5c2b441 100644 --- a/pkg/cmd/roachtest/acceptance.go +++ b/pkg/cmd/roachtest/acceptance.go @@ -32,6 +32,7 @@ func registerAcceptance(r *registry) { {"build-info", runBuildInfo}, {"cli/node-status", runCLINodeStatus}, {"event-log", runEventLog}, + {"status-server", runStatusServer}, } for _, tc := range testCases { spec.SubTests = append(spec.SubTests, testSpec{ diff --git a/pkg/cmd/roachtest/status_server.go b/pkg/cmd/roachtest/status_server.go new file mode 100644 index 000000000000..56ac60701785 --- /dev/null +++ b/pkg/cmd/roachtest/status_server.go @@ -0,0 +1,112 @@ +// Copyright 2018 The Cockroach Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. See the License for the specific language governing +// permissions and limitations under the License. + +package main + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + + "github.com/cockroachdb/cockroach/pkg/roachpb" + "github.com/cockroachdb/cockroach/pkg/server/serverpb" + "github.com/cockroachdb/cockroach/pkg/util/httputil" +) + +func runStatusServer(ctx context.Context, t *test, c *cluster) { + c.Put(ctx, cockroach, "./cockroach") + c.Wipe(ctx) + c.Start(ctx) + + // Get the ids for each node. + idMap := make(map[int]roachpb.NodeID) + urlMap := make(map[int]string) + for i, addr := range c.ExternalAdminUIAddr(ctx, c.All()) { + var details serverpb.DetailsResponse + url := `http://` + addr + `/_status/details/local` + if err := httputil.GetJSON(http.Client{}, url, &details); err != nil { + t.Fatal(err) + } + idMap[i+1] = details.NodeID + urlMap[i+1] = `http://` + addr + } + + // get performs an HTTP GET to the specified path for a specific node. + get := func(base, rel string) []byte { + url := base + rel + resp, err := http.Get(url) + if err != nil { + t.Fatalf("could not GET %s - %s", url, err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatalf("could not read body for %s - %s", url, err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("could not GET %s - statuscode: %d - body: %s", url, resp.StatusCode, body) + } + c.l.Printf("OK response from %s\n", url) + return body + } + + // checkNode checks all the endpoints of the status server hosted by node and + // requests info for the node with otherNodeID. That node could be the same + // other node, the same node or "local". + checkNode := func(url string, nodeID, otherNodeID, expectedNodeID roachpb.NodeID) { + urlIDs := []string{otherNodeID.String()} + if nodeID == otherNodeID { + urlIDs = append(urlIDs, "local") + } + var details serverpb.DetailsResponse + for _, urlID := range urlIDs { + if err := httputil.GetJSON(http.Client{}, url+`/_status/details/`+urlID, &details); err != nil { + t.Fatalf("unable to parse details - %s", err) + } + if details.NodeID != expectedNodeID { + t.Fatalf("%d calling %s: node ids don't match - expected %d, actual %d", + nodeID, urlID, expectedNodeID, details.NodeID) + } + + get(url, fmt.Sprintf("/_status/gossip/%s", urlID)) + get(url, fmt.Sprintf("/_status/nodes/%s", urlID)) + get(url, fmt.Sprintf("/_status/logfiles/%s", urlID)) + get(url, fmt.Sprintf("/_status/logs/%s", urlID)) + get(url, fmt.Sprintf("/_status/stacks/%s", urlID)) + } + + get(url, "/_status/vars") + } + + // Check local response for the every node. + for i := 1; i <= c.nodes; i++ { + id := idMap[i] + checkNode(urlMap[i], id, id, id) + get(urlMap[i], "/_status/nodes") + } + + // Proxy from the first node to the last node. + firstNode := 1 + lastNode := c.nodes + firstID := idMap[firstNode] + lastID := idMap[lastNode] + checkNode(urlMap[firstNode], firstID, lastID, lastID) + + // And from the last node to the first node. + checkNode(urlMap[lastNode], lastID, firstID, firstID) + + // And from the last node to the last node. + checkNode(urlMap[lastNode], lastID, lastID, lastID) +}