mirror of
https://github.com/status-im/consul.git
synced 2025-01-09 13:26:07 +00:00
5fb9df1640
* 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>
294 lines
6.7 KiB
Go
294 lines
6.7 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package agent
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/signal"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestStringHashSHA256(t *testing.T) {
|
|
t.Parallel()
|
|
in := "hello world\n"
|
|
expected := "a948904f2f0f479b8f8197694b30184b0d2ed1c1cd2a1ec0fb85d299a192a447"
|
|
|
|
if out := stringHashSHA256(in); out != expected {
|
|
t.Fatalf("bad: %s expected %s", out, expected)
|
|
}
|
|
}
|
|
|
|
func TestSetFilePermissions(t *testing.T) {
|
|
t.Parallel()
|
|
if runtime.GOOS == "windows" {
|
|
t.SkipNow()
|
|
}
|
|
tempFile := testutil.TempFile(t, "consul")
|
|
path := tempFile.Name()
|
|
|
|
// Bad UID fails
|
|
if err := setFilePermissions(path, "%", "", ""); err == nil {
|
|
t.Fatalf("should fail")
|
|
}
|
|
|
|
// Bad GID fails
|
|
if err := setFilePermissions(path, "", "%", ""); err == nil {
|
|
t.Fatalf("should fail")
|
|
}
|
|
|
|
// Bad mode fails
|
|
if err := setFilePermissions(path, "", "", "%"); err == nil {
|
|
t.Fatalf("should fail")
|
|
}
|
|
|
|
// Allows omitting user/group/mode
|
|
if err := setFilePermissions(path, "", "", ""); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
// Doesn't change mode if not given
|
|
if err := os.Chmod(path, 0700); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if err := setFilePermissions(path, "", "", ""); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
fi, err := os.Stat(path)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if fi.Mode().String() != "-rwx------" {
|
|
t.Fatalf("bad: %s", fi.Mode())
|
|
}
|
|
|
|
// Changes mode if given
|
|
if err := setFilePermissions(path, "", "", "0777"); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
fi, err = os.Stat(path)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if fi.Mode().String() != "-rwxrwxrwx" {
|
|
t.Fatalf("bad: %s", fi.Mode())
|
|
}
|
|
}
|
|
|
|
func TestDurationFixer(t *testing.T) {
|
|
obj := map[string]interface{}{
|
|
"key1": []map[string]interface{}{
|
|
{
|
|
"subkey1": "10s",
|
|
},
|
|
{
|
|
"subkey2": "5d",
|
|
},
|
|
},
|
|
"key2": map[string]interface{}{
|
|
"subkey3": "30s",
|
|
"subkey4": "20m",
|
|
},
|
|
"key3": "11s",
|
|
"key4": "49h",
|
|
}
|
|
expected := map[string]interface{}{
|
|
"key1": []map[string]interface{}{
|
|
{
|
|
"subkey1": 10 * time.Second,
|
|
},
|
|
{
|
|
"subkey2": "5d",
|
|
},
|
|
},
|
|
"key2": map[string]interface{}{
|
|
"subkey3": "30s",
|
|
"subkey4": 20 * time.Minute,
|
|
},
|
|
"key3": "11s",
|
|
"key4": 49 * time.Hour,
|
|
}
|
|
|
|
fixer := NewDurationFixer("key4", "subkey1", "subkey4")
|
|
if err := fixer.FixupDurations(obj); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Ensure we only processed the intended fieldnames
|
|
require.Equal(t, expected, obj)
|
|
}
|
|
|
|
// helperProcessSentinel is a sentinel value that is put as the first
|
|
// argument following "--" and is used to determine if TestHelperProcess
|
|
// should run.
|
|
const helperProcessSentinel = "GO_WANT_HELPER_PROCESS"
|
|
|
|
// helperProcess returns an *exec.Cmd that can be used to execute the
|
|
// TestHelperProcess function below. This can be used to test multi-process
|
|
// interactions.
|
|
func helperProcess(s ...string) (*exec.Cmd, func()) {
|
|
cs := []string{"-test.run=TestHelperProcess", "--", helperProcessSentinel}
|
|
cs = append(cs, s...)
|
|
|
|
cmd := exec.Command(os.Args[0], cs...)
|
|
destroy := func() {
|
|
if p := cmd.Process; p != nil {
|
|
p.Kill()
|
|
}
|
|
}
|
|
|
|
return cmd, destroy
|
|
}
|
|
|
|
// This is not a real test. This is just a helper process kicked off by tests
|
|
// using the helperProcess helper function.
|
|
func TestHelperProcess(t *testing.T) {
|
|
args := os.Args
|
|
for len(args) > 0 {
|
|
if args[0] == "--" {
|
|
args = args[1:]
|
|
break
|
|
}
|
|
|
|
args = args[1:]
|
|
}
|
|
|
|
if len(args) == 0 || args[0] != helperProcessSentinel {
|
|
return
|
|
}
|
|
|
|
defer os.Exit(0)
|
|
args = args[1:] // strip sentinel value
|
|
cmd := args[0]
|
|
|
|
switch cmd {
|
|
case "parent-signal":
|
|
// This subcommand forwards signals to a child process subcommand "print-signal".
|
|
|
|
limitProcessLifetime(2 * time.Minute)
|
|
|
|
cmd, destroy := helperProcess("print-signal")
|
|
defer destroy()
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "child process failed to start: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
doneCh := make(chan struct{})
|
|
defer func() { close(doneCh) }()
|
|
logFn := func(err error) {
|
|
fmt.Fprintf(os.Stderr, "could not forward signal: %s\n", err)
|
|
os.Exit(1)
|
|
}
|
|
ForwardSignals(cmd, logFn, doneCh)
|
|
|
|
if err := cmd.Wait(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "unexpected error waiting for child: %v", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
case "print-signal":
|
|
// This subcommand is instrumented to help verify signals are passed correctly.
|
|
|
|
limitProcessLifetime(2 * time.Minute)
|
|
|
|
ch := make(chan os.Signal, 10)
|
|
signal.Notify(ch, forwardSignals...)
|
|
defer signal.Stop(ch)
|
|
|
|
fmt.Fprintf(os.Stdout, "ready\n")
|
|
|
|
s := <-ch
|
|
|
|
fmt.Fprintf(os.Stdout, "signal: %s\n", s)
|
|
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "Unknown command: %q\n", cmd)
|
|
os.Exit(2)
|
|
}
|
|
}
|
|
|
|
// limitProcessLifetime installs a background goroutine that self-exits after
|
|
// the specified duration elapses to prevent leaking processes from tests that
|
|
// may spawn them.
|
|
func limitProcessLifetime(dur time.Duration) {
|
|
go time.AfterFunc(dur, func() {
|
|
os.Exit(99)
|
|
})
|
|
}
|
|
|
|
func TestForwardSignals(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
for _, s := range forwardSignals {
|
|
t.Run("signal-"+s.String(), func(t *testing.T) {
|
|
testForwardSignal(t, s)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testForwardSignal(t *testing.T, s os.Signal) {
|
|
t.Helper()
|
|
|
|
if s == os.Kill {
|
|
t.Fatalf("you can't forward SIGKILL")
|
|
}
|
|
|
|
// Launch a child process which registers the forwarding signal handler
|
|
// under test and then that in turn launches a grand child process that is
|
|
// our test instrument.
|
|
cmd, destroy := helperProcess("parent-signal")
|
|
defer destroy()
|
|
|
|
cmd.Stderr = os.Stderr
|
|
prc, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
t.Fatalf("could not open stdout pipe for child process: %v", err)
|
|
}
|
|
defer prc.Close()
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
t.Fatalf("child process failed to start: %v", err)
|
|
}
|
|
scan := bufio.NewScanner(prc)
|
|
|
|
// Wait until the grandchild relays back to us that it's ready to receive
|
|
// signals.
|
|
expectLine(t, "ready", scan)
|
|
|
|
// Relay our chosen signal down through the intermediary process.
|
|
if err := cmd.Process.Signal(s); err != nil {
|
|
t.Fatalf("signalling child failed: %v", err)
|
|
}
|
|
|
|
// Verify that the signal we intended made it all the way to the grandchild.
|
|
expectLine(t, "signal: "+s.String(), scan)
|
|
}
|
|
|
|
func expectLine(t *testing.T, expect string, scan *bufio.Scanner) {
|
|
if !scan.Scan() {
|
|
if scan.Err() != nil {
|
|
t.Fatalf("expected to read line %q but failed: %v", expect, scan.Err())
|
|
} else {
|
|
t.Fatalf("expected to read line %q but got no line", expect)
|
|
}
|
|
}
|
|
|
|
if line := scan.Text(); expect != line {
|
|
t.Fatalf("expected to read line %q but got %q", expect, line)
|
|
}
|
|
}
|