package command
import (
"fmt"
"strings"
"testing"
"time"
"github.com/hashicorp/consul/command/agent"
"github.com/hashicorp/consul/command/base"
"github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/testutil"
"github.com/hashicorp/serf/coordinate"
"github.com/mitchellh/cli"
)
func testRTTCommand ( t * testing . T ) ( * cli . MockUi , * RTTCommand ) {
ui := new ( cli . MockUi )
return ui , & RTTCommand {
Command : base . Command {
Ui : ui ,
Flags : base . FlagSetClientHTTP ,
} ,
}
}
func TestRTTCommand_Implements ( t * testing . T ) {
var _ cli . Command = & RTTCommand { }
}
func TestRTTCommand_Run_BadArgs ( t * testing . T ) {
_ , c := testRTTCommand ( t )
if code := c . Run ( [ ] string { } ) ; code != 1 {
t . Fatalf ( "expected return code 1, got %d" , code )
}
if code := c . Run ( [ ] string { "node1" , "node2" , "node3" } ) ; code != 1 {
t . Fatalf ( "expected return code 1, got %d" , code )
}
if code := c . Run ( [ ] string { "-wan" , "node1" , "node2" } ) ; code != 1 {
t . Fatalf ( "expected return code 1, got %d" , code )
}
if code := c . Run ( [ ] string { "-wan" , "node1.dc1" , "node2" } ) ; code != 1 {
t . Fatalf ( "expected return code 1, got %d" , code )
}
if code := c . Run ( [ ] string { "-wan" , "node1" , "node2.dc1" } ) ; code != 1 {
t . Fatalf ( "expected return code 1, got %d" , code )
}
}
func TestRTTCommand_Run_LAN ( t * testing . T ) {
updatePeriod := 10 * time . Millisecond
a := testAgentWithConfig ( t , func ( c * agent . Config ) {
c . ConsulConfig . CoordinateUpdatePeriod = updatePeriod
} )
defer a . Shutdown ( )
waitForLeader ( t , a . httpAddr )
// Inject some known coordinates.
c1 := coordinate . NewCoordinate ( coordinate . DefaultConfig ( ) )
c2 := c1 . Clone ( )
c2 . Vec [ 0 ] = 0.123
dist_str := fmt . Sprintf ( "%.3f ms" , c1 . DistanceTo ( c2 ) . Seconds ( ) * 1000.0 )
{
req := structs . CoordinateUpdateRequest {
Datacenter : a . config . Datacenter ,
Node : a . config . NodeName ,
Coord : c1 ,
}
var reply struct { }
if err := a . agent . RPC ( "Coordinate.Update" , & req , & reply ) ; err != nil {
t . Fatalf ( "err: %s" , err )
}
}
{
req := structs . RegisterRequest {
Datacenter : a . config . Datacenter ,
Node : "dogs" ,
Address : "127.0.0.2" ,
}
var reply struct { }
if err := a . agent . RPC ( "Catalog.Register" , & req , & reply ) ; err != nil {
t . Fatalf ( "err: %s" , err )
}
}
{
var reply struct { }
req := structs . CoordinateUpdateRequest {
Datacenter : a . config . Datacenter ,
Node : "dogs" ,
Coord : c2 ,
}
if err := a . agent . RPC ( "Coordinate.Update" , & req , & reply ) ; err != nil {
t . Fatalf ( "err: %s" , err )
}
}
// Ask for the RTT of two known nodes
ui , c := testRTTCommand ( t )
args := [ ] string {
"-http-addr=" + a . httpAddr ,
a . config . NodeName ,
"dogs" ,
}
// Wait for the updates to get flushed to the data store.
testutil . WaitForResult ( func ( ) ( bool , error ) {
code := c . Run ( args )
if code != 0 {
return false , fmt . Errorf ( "bad: %d: %#v" , code , ui . ErrorWriter . String ( ) )
}
// Make sure the proper RTT was reported in the output.
expected := fmt . Sprintf ( "rtt: %s" , dist_str )
if ! strings . Contains ( ui . OutputWriter . String ( ) , expected ) {
return false , fmt . Errorf ( "bad: %#v" , ui . OutputWriter . String ( ) )
}
return true , nil
} , func ( err error ) {
t . Fatalf ( "failed to get proper RTT output: %v" , err )
} )
// Default to the agent's node.
{
ui , c := testRTTCommand ( t )
args := [ ] string {
"-http-addr=" + a . httpAddr ,
"dogs" ,
}
code := c . Run ( args )
if code != 0 {
t . Fatalf ( "bad: %d: %#v" , code , ui . ErrorWriter . String ( ) )
}
// Make sure the proper RTT was reported in the output.
expected := fmt . Sprintf ( "rtt: %s" , dist_str )
if ! strings . Contains ( ui . OutputWriter . String ( ) , expected ) {
t . Fatalf ( "bad: %#v" , ui . OutputWriter . String ( ) )
}
}
// Try an unknown node.
{
ui , c := testRTTCommand ( t )
args := [ ] string {
"-http-addr=" + a . httpAddr ,
a . config . NodeName ,
"nope" ,
}
code := c . Run ( args )
if code != 1 {
t . Fatalf ( "bad: %d: %#v" , code , ui . ErrorWriter . String ( ) )
}
}
}
func TestRTTCommand_Run_WAN ( t * testing . T ) {
a := testAgent ( t )
defer a . Shutdown ( )
waitForLeader ( t , a . httpAddr )
node := fmt . Sprintf ( "%s.%s" , a . config . NodeName , a . config . Datacenter )
// We can't easily inject WAN coordinates, so we will just query the
// node with itself.
{
ui , c := testRTTCommand ( t )
args := [ ] string {
"-wan" ,
"-http-addr=" + a . httpAddr ,
node ,
node ,
}
code := c . Run ( args )
if code != 0 {
t . Fatalf ( "bad: %d: %#v" , code , ui . ErrorWriter . String ( ) )
}
// Make sure there was some kind of RTT reported in the output.
if ! strings . Contains ( ui . OutputWriter . String ( ) , "rtt: " ) {
t . Fatalf ( "bad: %#v" , ui . OutputWriter . String ( ) )
}
}
// Default to the agent's node.
{
ui , c := testRTTCommand ( t )
args := [ ] string {
"-wan" ,
"-http-addr=" + a . httpAddr ,
node ,
}
code := c . Run ( args )
if code != 0 {
t . Fatalf ( "bad: %d: %#v" , code , ui . ErrorWriter . String ( ) )
}
// Make sure there was some kind of RTT reported in the output.
if ! strings . Contains ( ui . OutputWriter . String ( ) , "rtt: " ) {
t . Fatalf ( "bad: %#v" , ui . OutputWriter . String ( ) )
}
}
// Try an unknown node.
{
ui , c := testRTTCommand ( t )
args := [ ] string {
"-wan" ,
"-http-addr=" + a . httpAddr ,
node ,
"dc1.nope" ,
}
code := c . Run ( args )
if code != 1 {
t . Fatalf ( "bad: %d: %#v" , code , ui . ErrorWriter . String ( ) )
}
}
}