Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
5.1 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package monitor
import (
"encoding/json"
"strings"
"sync"
"testing"
"time"
"github.com/hashicorp/consul/agent"
"github.com/mitchellh/cli"
)
func TestMonitorCommand_exitsOnSignalBeforeLinesArrive(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := agent.StartTestAgent(t, agent.TestAgent{})
defer a.Shutdown()
shutdownCh := make(chan struct{})
ui := cli.NewMockUi()
c := New(ui, shutdownCh)
// Only wait for ERR which shouldn't happen so should leave logs empty for a
// while
args := []string{"-http-addr=" + a.HTTPAddr(), "-log-level=ERR"}
// Buffer it so we don't deadlock when blocking send on shutdownCh triggers
// Run to return before we can select on it.
exitCode := make(chan int, 1)
// Run the monitor in another go routine. If this doesn't exit on our "signal"
// then the whole test will hang and we'll panic (to not blow up if people run
// the suite without -timeout)
var wg sync.WaitGroup
wg.Add(1)
go func() {
wg.Done() // Signal that this goroutine is at least running now
exitCode <- c.Run(args)
}()
// Wait for that routine to at least be running
wg.Wait()
// Simulate signal in a few milliseconds without blocking this thread
go func() {
time.Sleep(5 * time.Millisecond)
shutdownCh <- struct{}{}
}()
// Wait for a second - shouldn't take long to handle the signal before we
// panic. We simulate inside the select since the other goroutine might
// already have exited if there was some error and we'd block forever trying
// to write to unbuffered shutdownCh. We don't just buffer it because then it
// doesn't model the real shutdownCh we use for signal watching and could mask
// bugs in the handling.
select {
case ret := <-exitCode:
if ret != 0 {
t.Fatal("command returned with non-zero code")
}
// OK!
case <-time.After(1 * time.Second):
t.Fatal("timed out waiting for exit")
}
}
func TestMonitorCommand_LogJSONValidFlag(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := agent.StartTestAgent(t, agent.TestAgent{})
defer a.Shutdown()
shutdownCh := make(chan struct{})
ui := cli.NewMockUi()
c := New(ui, shutdownCh)
args := []string{"-http-addr=" + a.HTTPAddr(), "-log-json"}
// Buffer it so we don't deadlock when blocking send on shutdownCh triggers
// Run to return before we can select on it.
exitCode := make(chan int, 1)
// Run the monitor in another go routine. If this doesn't exit on our "signal"
// then the whole test will hang and we'll panic (to not blow up if people run
// the suite without -timeout)
var wg sync.WaitGroup
wg.Add(1)
go func() {
wg.Done() // Signal that this goroutine is at least running now
exitCode <- c.Run(args)
}()
// Wait for that routine to at least be running
wg.Wait()
// Simulate signal in a few milliseconds without blocking this thread
go func() {
time.Sleep(5 * time.Millisecond)
shutdownCh <- struct{}{}
}()
// Wait for a second - shouldn't take long to handle the signal before we
// panic. We simulate inside the select since the other goroutine might
// already have exited if there was some error and we'd block forever trying
// to write to unbuffered shutdownCh. We don't just buffer it because then it
// doesn't model the real shutdownCh we use for signal watching and could mask
// bugs in the handling.
select {
case ret := <-exitCode:
if ret != 0 {
t.Fatal("command returned with non-zero code")
}
// OK!
case <-time.After(1 * time.Second):
t.Fatal("timed out waiting for exit")
}
}
func TestMonitorCommand_LogJSONValidFormat(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
a := agent.StartTestAgent(t, agent.TestAgent{})
defer a.Shutdown()
shutdownCh := make(chan struct{})
ui := cli.NewMockUi()
c := New(ui, shutdownCh)
args := []string{"-http-addr=" + a.HTTPAddr(), "-log-json"}
// Buffer it so we don't deadlock when blocking send on shutdownCh triggers
// Run to return before we can select on it.
exitCode := make(chan int, 1)
// Run the monitor in another go routine. If this doesn't exit on our "signal"
// then the whole test will hang and we'll panic (to not blow up if people run
// the suite without -timeout)
var wg sync.WaitGroup
wg.Add(1)
go func() {
wg.Done() // Signal that this goroutine is at least running now
exitCode <- c.Run(args)
}()
// Wait for that routine to at least be running
wg.Wait()
// Read the logs and try to json marshall it
go func() {
time.Sleep(1 * time.Second)
outputs := ui.OutputWriter.String()
for count, output := range strings.Split(outputs, "\n") {
if output != "" && count > 0 {
jsonLog := new(map[string]interface{})
err := json.Unmarshal([]byte(output), jsonLog)
if err != nil {
exitCode <- -1
}
if len(*jsonLog) <= 0 {
exitCode <- 1
}
}
}
shutdownCh <- struct{}{}
}()
select {
case ret := <-exitCode:
if ret != 0 {
t.Fatal("command returned with non-zero code")
}
// OK!
case <-time.After(5 * time.Second):
t.Fatal("timed out waiting for exit")
}
}