mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
Merge branch 'main' into sso/gh-13120-oidc-login
This commit is contained in:
@@ -309,6 +309,7 @@ func convertServerConfig(agentConfig *Config) (*nomad.Config, error) {
|
||||
conf.RPCAddr.IP = rpcAddr.IP
|
||||
conf.SerfConfig.MemberlistConfig.BindPort = serfAddr.Port
|
||||
conf.SerfConfig.MemberlistConfig.BindAddr = serfAddr.IP.String()
|
||||
conf.SerfConfig.RejoinAfterLeave = agentConfig.Server.RejoinAfterLeave
|
||||
|
||||
// Set up the advertise addresses
|
||||
rpcAddr, err = net.ResolveTCPAddr("tcp", agentConfig.AdvertiseAddrs.RPC)
|
||||
|
||||
@@ -547,7 +547,7 @@ type ServerConfig struct {
|
||||
RetryIntervalHCL string `hcl:"retry_interval" json:"-"`
|
||||
|
||||
// RejoinAfterLeave controls our interaction with the cluster after leave.
|
||||
// When set to false (default), a leave causes Consul to not rejoin
|
||||
// When set to false (default), a leave causes Nomad to not rejoin
|
||||
// the cluster until an explicit join is received. If this is set to
|
||||
// true, we ignore the leave, and rejoin the cluster on start.
|
||||
RejoinAfterLeave bool `hcl:"rejoin_after_leave"`
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"golang.org/x/exp/maps"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
@@ -202,6 +203,7 @@ func connectUpstreams(in []structs.ConsulUpstream) []api.Upstream {
|
||||
Datacenter: upstream.Datacenter,
|
||||
LocalBindAddress: upstream.LocalBindAddress,
|
||||
MeshGateway: connectMeshGateway(upstream.MeshGateway),
|
||||
Config: maps.Clone(upstream.Config),
|
||||
}
|
||||
}
|
||||
return upstreams
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/hashicorp/nomad/helper/pointer"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/shoenig/test/must"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -360,11 +361,11 @@ func TestConnect_connectUpstreams(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
|
||||
t.Run("nil", func(t *testing.T) {
|
||||
require.Nil(t, connectUpstreams(nil))
|
||||
must.Nil(t, connectUpstreams(nil))
|
||||
})
|
||||
|
||||
t.Run("not empty", func(t *testing.T) {
|
||||
require.Equal(t,
|
||||
must.Eq(t,
|
||||
[]api.Upstream{{
|
||||
DestinationName: "foo",
|
||||
LocalBindPort: 8000,
|
||||
@@ -374,6 +375,7 @@ func TestConnect_connectUpstreams(t *testing.T) {
|
||||
LocalBindPort: 9000,
|
||||
Datacenter: "dc2",
|
||||
LocalBindAddress: "127.0.0.2",
|
||||
Config: map[string]any{"connect_timeout_ms": 5000},
|
||||
}},
|
||||
connectUpstreams([]structs.ConsulUpstream{{
|
||||
DestinationName: "foo",
|
||||
@@ -384,6 +386,7 @@ func TestConnect_connectUpstreams(t *testing.T) {
|
||||
LocalBindPort: 9000,
|
||||
Datacenter: "dc2",
|
||||
LocalBindAddress: "127.0.0.2",
|
||||
Config: map[string]any{"connect_timeout_ms": 5000},
|
||||
}}),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1668,6 +1668,7 @@ func apiUpstreamsToStructs(in []*api.ConsulUpstream) []structs.ConsulUpstream {
|
||||
Datacenter: upstream.Datacenter,
|
||||
LocalBindAddress: upstream.LocalBindAddress,
|
||||
MeshGateway: apiMeshGatewayToStructs(upstream.MeshGateway),
|
||||
Config: maps.Clone(upstream.Config),
|
||||
}
|
||||
}
|
||||
return upstreams
|
||||
|
||||
@@ -619,6 +619,12 @@ func Commands(metaPtr *Meta, agentUi cli.Ui) map[string]cli.CommandFactory {
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"operator client-state": func() (cli.Command, error) {
|
||||
return &OperatorClientStateCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
"operator debug": func() (cli.Command, error) {
|
||||
return &OperatorDebugCommand{
|
||||
Meta: meta,
|
||||
|
||||
@@ -33,11 +33,12 @@ type FormatCommand struct {
|
||||
|
||||
errs *multierror.Error
|
||||
|
||||
list bool
|
||||
check bool
|
||||
recursive bool
|
||||
write bool
|
||||
paths []string
|
||||
list bool
|
||||
check bool
|
||||
checkSuccess bool
|
||||
recursive bool
|
||||
write bool
|
||||
paths []string
|
||||
|
||||
stdin io.Reader
|
||||
}
|
||||
@@ -106,7 +107,7 @@ func (f *FormatCommand) Run(args []string) int {
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
f.checkSuccess = true
|
||||
f.parser = hclparse.NewParser()
|
||||
|
||||
color := terminal.IsTerminal(int(os.Stderr.Fd()))
|
||||
@@ -141,6 +142,9 @@ func (f *FormatCommand) Run(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
if !f.checkSuccess {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -253,6 +257,10 @@ func (f *FormatCommand) processFile(path string, r io.Reader) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if f.check {
|
||||
f.checkSuccess = false
|
||||
}
|
||||
}
|
||||
|
||||
if !f.list && !f.write {
|
||||
|
||||
@@ -3,7 +3,6 @@ package command
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -19,15 +18,19 @@ func TestFmtCommand(t *testing.T) {
|
||||
|
||||
const inSuffix = ".in.hcl"
|
||||
const expectedSuffix = ".out.hcl"
|
||||
tests := []string{"nomad", "job"}
|
||||
|
||||
tests := [][]string{
|
||||
{"nomad", "-check"},
|
||||
{"nomad", ""},
|
||||
{"job", "-check"},
|
||||
{"job", ""},
|
||||
}
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
for _, testName := range tests {
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
inFile := filepath.Join("testdata", "fmt", testName+inSuffix)
|
||||
expectedFile := filepath.Join("testdata", "fmt", testName+expectedSuffix)
|
||||
fmtFile := filepath.Join(tmpDir, testName+".hcl")
|
||||
for _, test := range tests {
|
||||
t.Run(test[0]+test[1], func(t *testing.T) {
|
||||
inFile := filepath.Join("testdata", "fmt", test[0]+inSuffix)
|
||||
expectedFile := filepath.Join("testdata", "fmt", test[0]+expectedSuffix)
|
||||
fmtFile := filepath.Join(tmpDir, test[0]+".hcl")
|
||||
|
||||
input, err := os.ReadFile(inFile)
|
||||
require.NoError(t, err)
|
||||
@@ -42,8 +45,16 @@ func TestFmtCommand(t *testing.T) {
|
||||
Meta: Meta{Ui: ui},
|
||||
}
|
||||
|
||||
code := cmd.Run([]string{fmtFile})
|
||||
assert.Equal(t, 0, code)
|
||||
var code int
|
||||
var expectedCode int
|
||||
if test[1] == "-check" {
|
||||
code = cmd.Run([]string{test[1], fmtFile})
|
||||
expectedCode = 1
|
||||
} else {
|
||||
code = cmd.Run([]string{fmtFile})
|
||||
expectedCode = 0
|
||||
}
|
||||
assert.Equal(t, expectedCode, code)
|
||||
|
||||
actual, err := os.ReadFile(fmtFile)
|
||||
require.NoError(t, err)
|
||||
@@ -64,11 +75,25 @@ func TestFmtCommand_FromStdin(t *testing.T) {
|
||||
stdin: stdinFake,
|
||||
}
|
||||
|
||||
if code := cmd.Run([]string{"-"}); code != 0 {
|
||||
t.Fatalf("expected code 0, got %d", code)
|
||||
}
|
||||
tests := []string{"-check", ""}
|
||||
for _, checkFlag := range tests {
|
||||
t.Run(checkFlag, func(t *testing.T) {
|
||||
var code int
|
||||
var expectedCode int
|
||||
|
||||
assert.Contains(t, ui.OutputWriter.String(), string(fmtFixture.golden))
|
||||
if checkFlag == "-check" {
|
||||
code = cmd.Run([]string{checkFlag, "-"})
|
||||
expectedCode = 1
|
||||
} else {
|
||||
code = cmd.Run([]string{"-"})
|
||||
expectedCode = 0
|
||||
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedCode, code)
|
||||
assert.Contains(t, ui.OutputWriter.String(), string(fmtFixture.golden))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFmtCommand_FromWorkingDirectory(t *testing.T) {
|
||||
@@ -86,10 +111,25 @@ func TestFmtCommand_FromWorkingDirectory(t *testing.T) {
|
||||
Meta: Meta{Ui: ui},
|
||||
}
|
||||
|
||||
code := cmd.Run([]string{})
|
||||
tests := []string{"-check", ""}
|
||||
for _, checkFlag := range tests {
|
||||
t.Run(checkFlag, func(t *testing.T) {
|
||||
var code int
|
||||
var expectedCode int
|
||||
|
||||
assert.Equal(t, 0, code)
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", fmtFixture.filename), ui.OutputWriter.String())
|
||||
if checkFlag == "-check" {
|
||||
code = cmd.Run([]string{checkFlag})
|
||||
expectedCode = 1
|
||||
} else {
|
||||
code = cmd.Run([]string{})
|
||||
expectedCode = 0
|
||||
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedCode, code)
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", fmtFixture.filename), ui.OutputWriter.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFmtCommand_FromDirectoryArgument(t *testing.T) {
|
||||
@@ -100,10 +140,25 @@ func TestFmtCommand_FromDirectoryArgument(t *testing.T) {
|
||||
Meta: Meta{Ui: ui},
|
||||
}
|
||||
|
||||
code := cmd.Run([]string{tmpDir})
|
||||
tests := []string{"-check", ""}
|
||||
for _, checkFlag := range tests {
|
||||
t.Run(checkFlag, func(t *testing.T) {
|
||||
var code int
|
||||
var expectedCode int
|
||||
|
||||
assert.Equal(t, 0, code)
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", filepath.Join(tmpDir, fmtFixture.filename)), ui.OutputWriter.String())
|
||||
if checkFlag == "-check" {
|
||||
code = cmd.Run([]string{checkFlag, tmpDir})
|
||||
expectedCode = 1
|
||||
} else {
|
||||
code = cmd.Run([]string{tmpDir})
|
||||
expectedCode = 0
|
||||
|
||||
}
|
||||
|
||||
assert.Equal(t, expectedCode, code)
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", filepath.Join(tmpDir, fmtFixture.filename)), ui.OutputWriter.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFmtCommand_FromFileArgument(t *testing.T) {
|
||||
@@ -113,13 +168,26 @@ func TestFmtCommand_FromFileArgument(t *testing.T) {
|
||||
cmd := &FormatCommand{
|
||||
Meta: Meta{Ui: ui},
|
||||
}
|
||||
|
||||
path := filepath.Join(tmpDir, fmtFixture.filename)
|
||||
|
||||
code := cmd.Run([]string{path})
|
||||
tests := []string{"-check", ""}
|
||||
for _, checkFlag := range tests {
|
||||
t.Run(checkFlag, func(t *testing.T) {
|
||||
var code int
|
||||
var expectedCode int
|
||||
|
||||
assert.Equal(t, 0, code)
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", path), ui.OutputWriter.String())
|
||||
if checkFlag == "-check" {
|
||||
code = cmd.Run([]string{checkFlag, path})
|
||||
expectedCode = 1
|
||||
} else {
|
||||
code = cmd.Run([]string{path})
|
||||
expectedCode = 0
|
||||
|
||||
}
|
||||
assert.Equal(t, expectedCode, code)
|
||||
assert.Equal(t, fmt.Sprintf("%s\n", path), ui.OutputWriter.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFmtCommand_FileDoesNotExist(t *testing.T) {
|
||||
@@ -152,7 +220,7 @@ func TestFmtCommand_InvalidSyntax(t *testing.T) {
|
||||
func fmtFixtureWriteDir(t *testing.T) string {
|
||||
dir := t.TempDir()
|
||||
|
||||
err := ioutil.WriteFile(filepath.Join(dir, fmtFixture.filename), fmtFixture.input, 0644)
|
||||
err := os.WriteFile(filepath.Join(dir, fmtFixture.filename), fmtFixture.input, 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
return dir
|
||||
|
||||
129
command/operator_client_state.go
Normal file
129
command/operator_client_state.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
trstate "github.com/hashicorp/nomad/client/allocrunner/taskrunner/state"
|
||||
"github.com/hashicorp/nomad/client/state"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
type OperatorClientStateCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *OperatorClientStateCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad operator client-state <path_to_nomad_dir>
|
||||
|
||||
Emits a representation of the stored client state in JSON format.
|
||||
`
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
func (c *OperatorClientStateCommand) AutocompleteFlags() complete.Flags {
|
||||
return complete.Flags{}
|
||||
}
|
||||
|
||||
func (c *OperatorClientStateCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictNothing
|
||||
}
|
||||
|
||||
func (c *OperatorClientStateCommand) Synopsis() string {
|
||||
return "Dump the nomad client state"
|
||||
}
|
||||
func (c *OperatorClientStateCommand) Name() string { return "operator client-state" }
|
||||
|
||||
func (c *OperatorClientStateCommand) Run(args []string) int {
|
||||
if len(args) != 1 {
|
||||
c.Ui.Error("This command takes one argument: <nomad-data-dir>")
|
||||
c.Ui.Error(commandErrorText(c))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
logger := hclog.L()
|
||||
db, err := state.NewBoltStateDB(logger, args[0])
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("failed to open client state: %v", err))
|
||||
return 1
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
allocs, _, err := db.GetAllAllocations()
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("failed to get allocations: %v", err))
|
||||
return 1
|
||||
}
|
||||
|
||||
data := map[string]*clientStateAlloc{}
|
||||
for _, alloc := range allocs {
|
||||
allocID := alloc.ID
|
||||
deployState, err := db.GetDeploymentStatus(allocID)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("failed to get deployment status for %s: %v", allocID, err))
|
||||
return 1
|
||||
}
|
||||
|
||||
tasks := map[string]*taskState{}
|
||||
tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
|
||||
for _, jt := range tg.Tasks {
|
||||
ls, rs, err := db.GetTaskRunnerState(allocID, jt.Name)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("failed to get task runner state %s: %v", allocID, err))
|
||||
return 1
|
||||
}
|
||||
|
||||
var ds interface{}
|
||||
if ls.TaskHandle == nil {
|
||||
continue
|
||||
}
|
||||
err = ls.TaskHandle.GetDriverState(&ds)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("failed to parse driver state %s: %v", allocID, err))
|
||||
return 1
|
||||
}
|
||||
|
||||
tasks[jt.Name] = &taskState{
|
||||
LocalState: ls,
|
||||
RemoteState: rs,
|
||||
DriverState: ds,
|
||||
}
|
||||
}
|
||||
|
||||
data[allocID] = &clientStateAlloc{
|
||||
Alloc: alloc,
|
||||
DeployStatus: deployState,
|
||||
Tasks: tasks,
|
||||
}
|
||||
}
|
||||
output := debugOutput{
|
||||
Allocations: data,
|
||||
}
|
||||
bytes, err := json.Marshal(output)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("failed to serialize client state: %v", err))
|
||||
return 1
|
||||
}
|
||||
c.Ui.Output(string(bytes))
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
type debugOutput struct {
|
||||
Allocations map[string]*clientStateAlloc
|
||||
}
|
||||
|
||||
type clientStateAlloc struct {
|
||||
Alloc any
|
||||
DeployStatus any
|
||||
Tasks map[string]*taskState
|
||||
}
|
||||
|
||||
type taskState struct {
|
||||
LocalState *trstate.LocalState
|
||||
RemoteState any
|
||||
DriverState interface{}
|
||||
}
|
||||
29
command/operator_client_state_test.go
Normal file
29
command/operator_client_state_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/nomad/ci"
|
||||
"github.com/mitchellh/cli"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOperatorClientStateCommand(t *testing.T) {
|
||||
ci.Parallel(t)
|
||||
ui := cli.NewMockUi()
|
||||
cmd := &OperatorClientStateCommand{Meta: Meta{Ui: ui}}
|
||||
|
||||
failedCode := cmd.Run([]string{"some", "bad", "args"})
|
||||
require.Equal(t, 1, failedCode)
|
||||
if out := ui.ErrorWriter.String(); !strings.Contains(out, commandErrorText(cmd)) {
|
||||
t.Fatalf("expected help output, got: %s", out)
|
||||
}
|
||||
ui.ErrorWriter.Reset()
|
||||
|
||||
dir := t.TempDir()
|
||||
code := cmd.Run([]string{dir})
|
||||
|
||||
require.Equal(t, 0, code)
|
||||
require.Contains(t, ui.OutputWriter.String(), "{}")
|
||||
}
|
||||
Reference in New Issue
Block a user