consul/command/monitor/monitor_test.go

191 lines
5.1 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
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")
}
}