mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
agent: working on setup
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
package agent
|
||||
|
||||
import "io"
|
||||
|
||||
type Agent struct {
|
||||
}
|
||||
|
||||
func NewAgent(config *Config, logOutput io.Writer) (*Agent, error) {
|
||||
a := &Agent{}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *Agent) Leave() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -6,21 +6,24 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/armon/go-metrics"
|
||||
"github.com/hashicorp/go-checkpoint"
|
||||
"github.com/hashicorp/go-syslog"
|
||||
"github.com/hashicorp/logutils"
|
||||
"github.com/hashicorp/vault/helper/flag-slice"
|
||||
"github.com/mitchellh/cli"
|
||||
)
|
||||
|
||||
// gracefulTimeout controls how long we wait before forcefully terminating
|
||||
const gracefulTimeout = 5 * time.Second
|
||||
|
||||
// Command is a Command implementation that runs a Consul agent.
|
||||
// Command is a Command implementation that runs a Nomad agent.
|
||||
// The command will not end unless a shutdown message is sent on the
|
||||
// ShutdownCh. If two messages are sent on the ShutdownCh it will forcibly
|
||||
// exit.
|
||||
@@ -45,7 +48,7 @@ func (c *Command) readConfig() *Config {
|
||||
flags.BoolVar(&dev, "dev", false, "")
|
||||
flags.StringVar(&logLevel, "log-level", "info", "")
|
||||
flags.Usage = func() { c.Ui.Error(c.Help()) }
|
||||
flags.Var((*sliceflag.StringFlag)(&configPath), "config", "config")
|
||||
flags.Var((*AppendSliceValue)(&configPath), "config", "config")
|
||||
if err := flags.Parse(c.args); err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -72,6 +75,13 @@ func (c *Command) readConfig() *Config {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the sub-structs at least exist
|
||||
if config.Client == nil {
|
||||
config.Client = &ClientConfig{}
|
||||
}
|
||||
if config.Server == nil {
|
||||
config.Server = &ServerConfig{}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -97,7 +107,7 @@ func (c *Command) setupLoggers(config *Config) (*GatedWriter, *logWriter, io.Wri
|
||||
// Check if syslog is enabled
|
||||
var syslog io.Writer
|
||||
if config.EnableSyslog {
|
||||
l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, "consul")
|
||||
l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, "nomad")
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Syslog setup failed: %v", err))
|
||||
return nil, nil, nil
|
||||
@@ -117,6 +127,61 @@ func (c *Command) setupLoggers(config *Config) (*GatedWriter, *logWriter, io.Wri
|
||||
return logGate, logWriter, logOutput
|
||||
}
|
||||
|
||||
// setupAgent is used to start the agent and various interfaces
|
||||
func (c *Command) setupAgent(config *Config, logOutput io.Writer) error {
|
||||
c.Ui.Output("Starting Nomad agent...")
|
||||
agent, err := NewAgent(config, logOutput)
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Error starting agent: %s", err))
|
||||
return err
|
||||
}
|
||||
c.agent = agent
|
||||
|
||||
// Setup update checking
|
||||
if !config.DisableUpdateCheck {
|
||||
version := config.Version
|
||||
if config.VersionPrerelease != "" {
|
||||
version += fmt.Sprintf("-%s", config.VersionPrerelease)
|
||||
}
|
||||
updateParams := &checkpoint.CheckParams{
|
||||
Product: "nomad",
|
||||
Version: version,
|
||||
}
|
||||
if !config.DisableAnonymousSignature {
|
||||
updateParams.SignatureFile = filepath.Join(config.DataDir, "checkpoint-signature")
|
||||
}
|
||||
|
||||
// Schedule a periodic check with expected interval of 24 hours
|
||||
checkpoint.CheckInterval(updateParams, 24*time.Hour, c.checkpointResults)
|
||||
|
||||
// Do an immediate check within the next 30 seconds
|
||||
go func() {
|
||||
time.Sleep(randomStagger(30 * time.Second))
|
||||
c.checkpointResults(checkpoint.Check(updateParams))
|
||||
}()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkpointResults is used to handler periodic results from our update checker
|
||||
func (c *Command) checkpointResults(results *checkpoint.CheckResponse, err error) {
|
||||
if err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to check for updates: %v", err))
|
||||
return
|
||||
}
|
||||
if results.Outdated {
|
||||
c.Ui.Error(fmt.Sprintf("Newer Nomad version available: %s", results.CurrentVersion))
|
||||
}
|
||||
for _, alert := range results.Alerts {
|
||||
switch alert.Level {
|
||||
case "info":
|
||||
c.Ui.Info(fmt.Sprintf("Bulletin [%s]: %s (%s)", alert.Level, alert.Message, alert.URL))
|
||||
default:
|
||||
c.Ui.Error(fmt.Sprintf("Bulletin [%s]: %s (%s)", alert.Level, alert.Message, alert.URL))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Command) Run(args []string) int {
|
||||
c.Ui = &cli.PrefixedUi{
|
||||
OutputPrefix: "==> ",
|
||||
@@ -133,7 +198,7 @@ func (c *Command) Run(args []string) int {
|
||||
}
|
||||
|
||||
// Setup the log outputs
|
||||
logGate, _, _ := c.setupLoggers(config)
|
||||
logGate, _, logOutput := c.setupLoggers(config)
|
||||
if logGate == nil {
|
||||
return 1
|
||||
}
|
||||
@@ -144,12 +209,41 @@ func (c *Command) Run(args []string) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// Let the user know things are running
|
||||
c.Ui.Output("Nomad agent running!")
|
||||
// Create the agent
|
||||
if err := c.setupAgent(config, logOutput); err != nil {
|
||||
return 1
|
||||
}
|
||||
defer c.agent.Shutdown()
|
||||
|
||||
// Compile agent information for output later
|
||||
info := make(map[string]string)
|
||||
info["client"] = strconv.FormatBool(config.Client.Enabled)
|
||||
info["log level"] = config.LogLevel
|
||||
info["server"] = strconv.FormatBool(config.Server.Enabled)
|
||||
|
||||
// Sort the keys for output
|
||||
infoKeys := make([]string, 0, len(info))
|
||||
for key := range info {
|
||||
infoKeys = append(infoKeys, key)
|
||||
}
|
||||
sort.Strings(infoKeys)
|
||||
|
||||
// Agent configuration output
|
||||
padding := 18
|
||||
c.Ui.Output("Nomad agent configuration:\n")
|
||||
for _, k := range infoKeys {
|
||||
c.Ui.Info(fmt.Sprintf(
|
||||
"%s%s: %s",
|
||||
strings.Repeat(" ", padding-len(k)),
|
||||
strings.Title(k),
|
||||
info[k]))
|
||||
}
|
||||
c.Ui.Output("")
|
||||
|
||||
// Output the header that the server has started
|
||||
c.Ui.Output("Nomad agent started! Log data will stream in below:\n")
|
||||
|
||||
// Enable log streaming
|
||||
c.Ui.Info("")
|
||||
c.Ui.Output("Log data will now stream in as it occurs:\n")
|
||||
logGate.Flush()
|
||||
|
||||
// Wait for exit
|
||||
|
||||
@@ -41,6 +41,12 @@ type Config struct {
|
||||
LeaveOnTerm bool
|
||||
EnableSyslog bool
|
||||
SyslogFacility string
|
||||
|
||||
DisableUpdateCheck bool
|
||||
DisableAnonymousSignature bool
|
||||
|
||||
Version string
|
||||
VersionPrerelease string
|
||||
}
|
||||
|
||||
type ClientConfig struct {
|
||||
|
||||
20
command/agent/flag_slice_value.go
Normal file
20
command/agent/flag_slice_value.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package agent
|
||||
|
||||
import "strings"
|
||||
|
||||
// AppendSliceValue implements the flag.Value interface and allows multiple
|
||||
// calls to the same variable to append a list.
|
||||
type AppendSliceValue []string
|
||||
|
||||
func (s *AppendSliceValue) String() string {
|
||||
return strings.Join(*s, ",")
|
||||
}
|
||||
|
||||
func (s *AppendSliceValue) Set(value string) error {
|
||||
if *s == nil {
|
||||
*s = make([]string, 0, 1)
|
||||
}
|
||||
|
||||
*s = append(*s, value)
|
||||
return nil
|
||||
}
|
||||
33
command/agent/flag_slice_value_test.go
Normal file
33
command/agent/flag_slice_value_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAppendSliceValue_implements(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = new(AppendSliceValue)
|
||||
if _, ok := raw.(flag.Value); !ok {
|
||||
t.Fatalf("AppendSliceValue should be a Value")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendSliceValueSet(t *testing.T) {
|
||||
sv := new(AppendSliceValue)
|
||||
err := sv.Set("foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
err = sv.Set("bar")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
expected := []string{"foo", "bar"}
|
||||
if !reflect.DeepEqual([]string(*sv), expected) {
|
||||
t.Fatalf("Bad: %#v", sv)
|
||||
}
|
||||
}
|
||||
11
command/agent/util.go
Normal file
11
command/agent/util.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Returns a random stagger interval between 0 and the duration
|
||||
func randomStagger(intv time.Duration) time.Duration {
|
||||
return time.Duration(uint64(rand.Int63()) % uint64(intv))
|
||||
}
|
||||
16
command/agent/util_test.go
Normal file
16
command/agent/util_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRandomStagger(t *testing.T) {
|
||||
intv := time.Minute
|
||||
for i := 0; i < 10; i++ {
|
||||
stagger := randomStagger(intv)
|
||||
if stagger < 0 || stagger >= intv {
|
||||
t.Fatalf("Bad: %v", stagger)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user