2023-04-10 15:36:59 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2018-01-12 01:00:30 +00:00
|
|
|
package nomad
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math/rand"
|
|
|
|
"net"
|
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
2023-01-03 17:25:20 +00:00
|
|
|
"github.com/hashicorp/nomad/ci"
|
2018-01-12 01:00:30 +00:00
|
|
|
"github.com/hashicorp/nomad/command/agent/consul"
|
|
|
|
"github.com/hashicorp/nomad/helper/testlog"
|
|
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
2019-09-04 01:42:41 +00:00
|
|
|
"github.com/hashicorp/nomad/version"
|
2023-03-20 23:32:32 +00:00
|
|
|
testing "github.com/mitchellh/go-testing-interface"
|
2023-03-17 19:11:13 +00:00
|
|
|
"github.com/shoenig/test/must"
|
2018-01-12 01:00:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2022-03-18 12:48:08 +00:00
|
|
|
nodeNumber int32 = 0
|
2018-01-12 01:00:30 +00:00
|
|
|
)
|
|
|
|
|
2023-03-20 23:32:32 +00:00
|
|
|
func TestACLServer(t testing.T, cb func(*Config)) (*Server, *structs.ACLToken, func()) {
|
2019-12-04 00:15:11 +00:00
|
|
|
server, cleanup := TestServer(t, func(c *Config) {
|
2018-01-12 01:00:30 +00:00
|
|
|
c.ACLEnabled = true
|
|
|
|
if cb != nil {
|
|
|
|
cb(c)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
token := mock.ACLManagementToken()
|
2020-12-01 16:11:34 +00:00
|
|
|
err := server.State().BootstrapACLTokens(structs.MsgTypeTestSetup, 1, 0, token)
|
2018-01-12 01:00:30 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("failed to bootstrap ACL token: %v", err)
|
|
|
|
}
|
2019-12-04 00:15:11 +00:00
|
|
|
return server, token, cleanup
|
2018-01-12 01:00:30 +00:00
|
|
|
}
|
|
|
|
|
2023-03-20 23:32:32 +00:00
|
|
|
func TestServer(t testing.T, cb func(*Config)) (*Server, func()) {
|
2022-03-24 18:42:00 +00:00
|
|
|
s, c, err := TestServerErr(t, cb)
|
2023-03-17 19:11:13 +00:00
|
|
|
must.NoError(t, err, must.Sprint("failed to start test server"))
|
2022-03-24 18:42:00 +00:00
|
|
|
return s, c
|
|
|
|
}
|
|
|
|
|
2023-03-30 21:15:05 +00:00
|
|
|
// TestConfigForServer provides a fully functional Config to pass to NewServer()
|
|
|
|
// It can be changed beforehand to induce different behavior such as specific errors.
|
|
|
|
func TestConfigForServer(t testing.T) *Config {
|
|
|
|
t.Helper()
|
|
|
|
|
2018-01-12 01:00:30 +00:00
|
|
|
// Setup the default settings
|
|
|
|
config := DefaultConfig()
|
2021-04-27 20:50:07 +00:00
|
|
|
|
|
|
|
// Setup default enterprise-specific settings, including license
|
|
|
|
defaultEnterpriseTestConfig(config)
|
|
|
|
|
2019-09-04 01:42:41 +00:00
|
|
|
config.Build = version.Version + "+unittest"
|
2018-01-12 01:00:30 +00:00
|
|
|
config.DevMode = true
|
2022-05-25 19:05:30 +00:00
|
|
|
config.DataDir = t.TempDir()
|
2020-10-08 18:27:52 +00:00
|
|
|
config.EnableEventBroker = true
|
2020-03-02 15:29:24 +00:00
|
|
|
config.BootstrapExpect = 1
|
2022-03-18 12:48:08 +00:00
|
|
|
nodeNum := atomic.AddInt32(&nodeNumber, 1)
|
2018-01-12 01:00:30 +00:00
|
|
|
config.NodeName = fmt.Sprintf("nomad-%03d", nodeNum)
|
|
|
|
|
2022-01-06 16:56:13 +00:00
|
|
|
// configure logger
|
2022-03-18 12:48:08 +00:00
|
|
|
config.Logger, config.LogOutput = testlog.HCLoggerNode(t, nodeNum)
|
2020-06-07 13:16:11 +00:00
|
|
|
|
2018-01-12 01:00:30 +00:00
|
|
|
// Tighten the Serf timing
|
|
|
|
config.SerfConfig.MemberlistConfig.BindAddr = "127.0.0.1"
|
|
|
|
config.SerfConfig.MemberlistConfig.SuspicionMult = 2
|
|
|
|
config.SerfConfig.MemberlistConfig.RetransmitMult = 2
|
|
|
|
config.SerfConfig.MemberlistConfig.ProbeTimeout = 50 * time.Millisecond
|
|
|
|
config.SerfConfig.MemberlistConfig.ProbeInterval = 100 * time.Millisecond
|
|
|
|
config.SerfConfig.MemberlistConfig.GossipInterval = 100 * time.Millisecond
|
|
|
|
|
|
|
|
// Tighten the Raft timing
|
|
|
|
config.RaftConfig.LeaderLeaseTimeout = 50 * time.Millisecond
|
|
|
|
config.RaftConfig.HeartbeatTimeout = 50 * time.Millisecond
|
|
|
|
config.RaftConfig.ElectionTimeout = 50 * time.Millisecond
|
|
|
|
config.RaftTimeout = 500 * time.Millisecond
|
|
|
|
|
|
|
|
// Disable Vault
|
|
|
|
f := false
|
|
|
|
config.VaultConfig.Enabled = &f
|
|
|
|
|
2018-02-15 23:48:32 +00:00
|
|
|
// Tighten the autopilot timing
|
|
|
|
config.AutopilotConfig.ServerStabilizationTime = 100 * time.Millisecond
|
|
|
|
config.ServerHealthInterval = 50 * time.Millisecond
|
|
|
|
config.AutopilotInterval = 100 * time.Millisecond
|
|
|
|
|
2019-09-04 11:54:54 +00:00
|
|
|
// Disable consul autojoining: tests typically join servers directly
|
|
|
|
config.ConsulConfig.ServerAutoJoin = &f
|
|
|
|
|
api: implement fuzzy search API
This PR introduces the /v1/search/fuzzy API endpoint, used for fuzzy
searching objects in Nomad. The fuzzy search endpoint routes requests
to the Nomad Server leader, which implements the Search.FuzzySearch RPC
method.
Requests to the fuzzy search API are based on the api.FuzzySearchRequest
object, e.g.
{
"Text": "ed",
"Context": "all"
}
Responses from the fuzzy search API are based on the api.FuzzySearchResponse
object, e.g.
{
"Index": 27,
"KnownLeader": true,
"LastContact": 0,
"Matches": {
"tasks": [
{
"ID": "redis",
"Scope": [
"default",
"example",
"cache"
]
}
],
"evals": [],
"deployment": [],
"volumes": [],
"scaling_policy": [],
"images": [
{
"ID": "redis:3.2",
"Scope": [
"default",
"example",
"cache",
"redis"
]
}
]
},
"Truncations": {
"volumes": false,
"scaling_policy": false,
"evals": false,
"deployment": false
}
}
The API is tunable using the new server.search stanza, e.g.
server {
search {
fuzzy_enabled = true
limit_query = 200
limit_results = 1000
min_term_length = 5
}
}
These values can be increased or decreased, so as to provide more
search results or to reduce load on the Nomad Server. The fuzzy search
API can be disabled entirely by setting `fuzzy_enabled` to `false`.
2021-02-23 20:24:52 +00:00
|
|
|
// Enable fuzzy search API
|
|
|
|
config.SearchConfig = &structs.SearchConfig{
|
|
|
|
FuzzyEnabled: true,
|
|
|
|
LimitQuery: 20,
|
|
|
|
LimitResults: 100,
|
|
|
|
MinTermLength: 2,
|
|
|
|
}
|
|
|
|
|
2023-03-30 21:15:05 +00:00
|
|
|
// Get random ports for RPC and Serf
|
|
|
|
ports := ci.PortAllocator.Grab(2)
|
|
|
|
config.RPCAddr = &net.TCPAddr{
|
|
|
|
IP: []byte{127, 0, 0, 1},
|
|
|
|
Port: ports[0],
|
|
|
|
}
|
|
|
|
config.SerfConfig.MemberlistConfig.BindPort = ports[1]
|
|
|
|
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestServerErr(t testing.T, cb func(*Config)) (*Server, func(), error) {
|
|
|
|
config := TestConfigForServer(t)
|
2018-01-12 01:00:30 +00:00
|
|
|
// Invoke the callback if any
|
|
|
|
if cb != nil {
|
|
|
|
cb(config)
|
|
|
|
}
|
|
|
|
|
2020-07-28 20:12:08 +00:00
|
|
|
cCatalog := consul.NewMockCatalog(config.Logger)
|
|
|
|
cConfigs := consul.NewMockConfigsAPI(config.Logger)
|
|
|
|
cACLs := consul.NewMockACLsAPI(config.Logger)
|
2019-12-06 20:46:46 +00:00
|
|
|
|
2023-03-30 21:15:05 +00:00
|
|
|
var server *Server
|
|
|
|
var err error
|
2018-01-12 01:00:30 +00:00
|
|
|
|
2023-03-30 21:15:05 +00:00
|
|
|
for i := 10; i >= 0; i-- {
|
2018-01-12 01:00:30 +00:00
|
|
|
// Create server
|
2023-03-30 21:15:05 +00:00
|
|
|
server, err = NewServer(config, cCatalog, cConfigs, cACLs)
|
2018-01-12 01:00:30 +00:00
|
|
|
if err == nil {
|
2019-12-04 00:15:11 +00:00
|
|
|
return server, func() {
|
|
|
|
ch := make(chan error)
|
|
|
|
go func() {
|
|
|
|
defer close(ch)
|
|
|
|
|
|
|
|
// Shutdown server
|
2023-03-17 19:11:13 +00:00
|
|
|
err = server.Shutdown()
|
2019-12-04 00:15:11 +00:00
|
|
|
if err != nil {
|
2022-04-02 00:24:02 +00:00
|
|
|
ch <- fmt.Errorf("failed to shutdown server: %w", err)
|
2019-12-04 00:15:11 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case e := <-ch:
|
|
|
|
if e != nil {
|
|
|
|
t.Fatal(e.Error())
|
|
|
|
}
|
|
|
|
case <-time.After(1 * time.Minute):
|
|
|
|
t.Fatal("timed out while shutting down server")
|
|
|
|
}
|
2022-03-24 18:42:00 +00:00
|
|
|
}, nil
|
2023-03-17 19:11:13 +00:00
|
|
|
} else if i > 0 {
|
2018-01-12 01:00:30 +00:00
|
|
|
if server != nil {
|
2019-12-04 00:15:11 +00:00
|
|
|
_ = server.Shutdown()
|
2018-01-12 01:00:30 +00:00
|
|
|
}
|
|
|
|
wait := time.Duration(rand.Int31n(2000)) * time.Millisecond
|
|
|
|
time.Sleep(wait)
|
|
|
|
}
|
2023-03-30 21:15:05 +00:00
|
|
|
|
|
|
|
// if it failed for port reasons, try new ones
|
|
|
|
ports := ci.PortAllocator.Grab(2)
|
|
|
|
config.RPCAddr = &net.TCPAddr{
|
|
|
|
IP: []byte{127, 0, 0, 1},
|
|
|
|
Port: ports[0],
|
|
|
|
}
|
|
|
|
config.SerfConfig.MemberlistConfig.BindPort = ports[1]
|
2018-01-12 01:00:30 +00:00
|
|
|
}
|
|
|
|
|
2023-03-30 21:15:05 +00:00
|
|
|
return nil, nil, fmt.Errorf("error starting test server: %w", err)
|
2018-01-12 01:00:30 +00:00
|
|
|
}
|
|
|
|
|
2023-03-20 23:32:32 +00:00
|
|
|
func TestJoin(t testing.T, servers ...*Server) {
|
2021-06-08 17:47:04 +00:00
|
|
|
for i := 0; i < len(servers)-1; i++ {
|
|
|
|
addr := fmt.Sprintf("127.0.0.1:%d",
|
|
|
|
servers[i].config.SerfConfig.MemberlistConfig.BindPort)
|
|
|
|
|
|
|
|
for j := i + 1; j < len(servers); j++ {
|
|
|
|
num, err := servers[j].Join([]string{addr})
|
2023-03-20 23:32:32 +00:00
|
|
|
must.NoError(t, err)
|
|
|
|
must.Eq(t, 1, num)
|
2018-01-12 01:00:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|