From 8c8cca714ee6b95a59476bc644bff4e5bdb511ba Mon Sep 17 00:00:00 2001 From: Nic Grobler Date: Wed, 10 Jul 2019 11:13:23 +0200 Subject: [PATCH] initial commit for agent monitor addresses influxdata#5958 --- agent/agent.go | 33 +++- agent/agent_momitor_test.go | 321 ++++++++++++++++++++++++++++++++++++ agent/agent_monitor.go | 288 ++++++++++++++++++++++++++++++++ cmd/telegraf/telegraf.go | 14 +- internal/config/config.go | 35 +++- 5 files changed, 683 insertions(+), 8 deletions(-) create mode 100644 agent/agent_momitor_test.go create mode 100644 agent/agent_monitor.go diff --git a/agent/agent.go b/agent/agent.go index 5421543881097..cf4c30aaee777 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "os" "runtime" "sync" "time" @@ -29,7 +30,7 @@ func NewAgent(config *config.Config) (*Agent, error) { } // Run starts and runs the Agent until the context is done. -func (a *Agent) Run(ctx context.Context) error { +func (a *Agent) Run(ctx context.Context, signalsMonitorAgent chan os.Signal) error { log.Printf("I! [agent] Config: Interval:%s, Quiet:%#v, Hostname:%#v, "+ "Flush Interval:%s", a.Config.Agent.Interval.Duration, a.Config.Agent.Quiet, @@ -57,6 +58,20 @@ func (a *Agent) Run(ctx context.Context) error { startTime := time.Now() + // start internal monitor + log.Printf("D! [agent] Creating agent monitor") + agentMonitor, err := NewAgentMonitor(ctx, a.Config, signalsMonitorAgent, inputC) + if err != nil { + /* + we should NOT cause telegraf agent to fail here - this can be due to nonesense version string (or total lack of one - while testing etc) + log it, and die quietly + */ + log.Printf("D! [agent] agent monitor returned: " + err.Error()) + } else { + log.Printf("D! [agent] Starting agent monitor") + go agentMonitor.Run() //this will look after itself from this point + } + log.Printf("D! [agent] Starting service inputs") err = a.startServiceInputs(ctx, inputC) if err != nil { @@ -142,7 +157,7 @@ func (a *Agent) Run(ctx context.Context) error { } // Test runs the inputs once and prints the output to stdout in line protocol. -func (a *Agent) Test(ctx context.Context, waitDuration time.Duration) error { +func (a *Agent) Test(ctx context.Context, waitDuration time.Duration, signalsMonitorAgent chan os.Signal) error { var wg sync.WaitGroup metricC := make(chan telegraf.Metric) nulC := make(chan telegraf.Metric) @@ -182,6 +197,20 @@ func (a *Agent) Test(ctx context.Context, waitDuration time.Duration) error { } } + // start agent monitor + log.Printf("D! [agent] Creating agent monitor") + agentMonitor, err := NewAgentMonitor(ctx, a.Config, signalsMonitorAgent, metricC) + if err != nil { + /* + we should NOT cause telegraf agent to fail here - this can be due to nonesense version string (or total lack of one - while testing etc) + log it, and die quietly + */ + log.Printf("D! [agent] agent monitor returned: " + err.Error()) + } else { + log.Printf("D! [agent] Starting agent monitor...") + go agentMonitor.Run() //this will look after itself from this point, and operates autonomously. main should not need to do anything else. + } + if hasServiceInputs { log.Printf("D! [agent] Starting service inputs") err := a.startServiceInputs(ctx, metricC) diff --git a/agent/agent_momitor_test.go b/agent/agent_momitor_test.go new file mode 100644 index 0000000000000..b4aedb2e95ede --- /dev/null +++ b/agent/agent_momitor_test.go @@ -0,0 +1,321 @@ +package agent + +import ( + "context" + "os" + "syscall" + "testing" + "time" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/internal/config" + "github.com/influxdata/telegraf/internal/models" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func panicOnReceive(signals chan os.Signal, tick <-chan time.Time) { + for { + select { + case signals <- syscall.SIGINT: + // if we reach this, it means that something was listening on the chan, and should not have been + panic("listener found where none should exist") + case <-tick: + // we're done + return + } + } +} + +func TestExtractNumeric(t *testing.T) { + d, ok, err := extractNumeric("v1") + require.Nil(t, err) + require.Equal(t, true, ok) + require.Equal(t, int64(1), d) + + d, ok, err = extractNumeric("1Garbage2") + require.Nil(t, err) + require.Equal(t, true, ok) + require.Equal(t, int64(1), d) + + d, ok, err = extractNumeric("mon~2keyGa-rbage.") + require.Nil(t, err) + require.Equal(t, false, ok) + require.Equal(t, int64(0), d) + + d, ok, err = extractNumeric("13~123bvdbc1") + require.Nil(t, err) + require.Equal(t, true, ok) + require.Equal(t, int64(13), d) +} + +func TestGetNumericVersion(t *testing.T) { + numerics, err := getNumericVersion("v1.2.3") + require.Nil(t, err) + require.Equal(t, []int64{1, 2, 3}, numerics) + + numerics, err = getNumericVersion("1.2.3~123bvdbc1") + require.Nil(t, err) + require.Equal(t, []int64{1, 2, 3}, numerics) + + numerics, err = getNumericVersion("1.1.9-alpha") + require.Nil(t, err) + require.Equal(t, []int64{1, 1, 9}, numerics) + + numerics, err = getNumericVersion("1.2-banana") + require.NotNil(t, err) + require.Equal(t, "version string is not of expected semantic format: 1.2-banana", err.Error()) + require.Equal(t, []int64{}, numerics) + + numerics, err = getNumericVersion("1.2") + require.NotNil(t, err) + require.Equal(t, "version string is not of expected semantic format: 1.2", err.Error()) + require.Equal(t, []int64{}, numerics) + + numerics, err = getNumericVersion("1.banana.2") + require.NotNil(t, err) + require.Equal(t, "version string contains nonsensical character sequence: 1.banana.2", err.Error()) + require.Equal(t, []int64{}, numerics) +} + +func TestAgentMetaDataCreate(t *testing.T) { + c := config.NewConfig() + outgoing := make(chan telegraf.Metric, 1) + ctx, cancel := context.WithCancel(context.Background()) + signals := make(chan os.Signal) + + _, err := NewAgentMonitor(ctx, c, signals, outgoing) + // no version string exists yet, so object instantiation should have failed + require.NotNil(t, err) + + // so let's add the version, and retry + c.Agent.Version = "1.20.30" + _, err = NewAgentMonitor(ctx, c, signals, outgoing) + require.Nil(t, err) + cancel() +} + +func TestAgentMetaDataRunNoJitter(t *testing.T) { + c := config.NewConfig() + outgoing := make(chan telegraf.Metric, 1) + ctx, cancel := context.WithCancel(context.Background()) + signals := make(chan os.Signal) + c.Agent.Version = "1.20.30" + agentMonitor, err := NewAgentMonitor(ctx, c, signals, outgoing) + // no version string exists yet, so object instantiation should work + require.Nil(t, err) + + // start the monitor + go agentMonitor.Run() + + // grab the "startup signal" - sent on startup + signal := <-outgoing + require.Contains(t, "agent_statechange", signal.Name()) + var expected = map[string]interface{}{ + "state": "started", + } + actual := signal.Fields() + require.Equal(t, expected, actual) + + // grab the version it just sent on startup + metric := <-outgoing + require.Contains(t, "agent_meta_data", metric.Name()) + + // send it an interupt + signals <- syscall.SIGINT + + // grab the signal + metric = <-outgoing + require.Contains(t, "agent_statechange", metric.Name()) + + // cancel the agent, which should cause the agentmonitor to quietly die + cancel() + + // sleep to give cancel() time to work + time.Sleep(500 * time.Millisecond) + + // verify that it is indeed dead by trying to send another signal to it - which should panic + ticker := time.NewTicker(100 * time.Millisecond) + assert.NotPanics(t, func() { panicOnReceive(signals, ticker.C) }) + ticker.Stop() + +} +func TestAgentMetaDataVersion(t *testing.T) { + c := config.NewConfig() + outgoing := make(chan telegraf.Metric, 1) + ctx, cancel := context.WithCancel(context.Background()) + signals := make(chan os.Signal) + // invalid string + c.Agent.Version = "1.banana.orangejuice" + agentMonitor, err := NewAgentMonitor(ctx, c, signals, outgoing) + require.NotNil(t, err) + + // now with a valid version + c.Agent.Version = "1.20.30" + agentMonitor, err = NewAgentMonitor(ctx, c, signals, outgoing) + require.Nil(t, err) + + // change the jitter value to be crazy short - allowing consistent tests as must be shorter than ticker + agentMonitor.jitter = time.Duration(10 * time.Millisecond) + + // start the monitor + go agentMonitor.Run() + + // grab the startup signal - sent on startup + signal := <-outgoing + require.Contains(t, "agent_statechange", signal.Name()) + var expected = map[string]interface{}{ + "state": "started", + } + actual := signal.Fields() + require.Equal(t, expected, actual) + + // grab the version it just sent on startup + metric := <-outgoing + require.Contains(t, "agent_meta_data", metric.Name()) + expected = map[string]interface{}{ + "version_string": "1.20.30", + "major_version": int64(1), + "minor_version": int64(20), + "patch_version": int64(30), + "number_inputs": int64(0), + "number_outputs": int64(0), + } + actual = metric.Fields() + require.Equal(t, expected, actual) + + // cancel the context, which should cause the agentmonitor to quietly die + cancel() + + // sleep to give cancel() time to work + time.Sleep(500 * time.Millisecond) + + // verify that it is indeed dead by trying to send another signal to it - which should panic + ticker := time.NewTicker(100 * time.Millisecond) + assert.NotPanics(t, func() { panicOnReceive(signals, ticker.C) }) + ticker.Stop() + +} + +func TestAgentMetaDataEmptyVersion(t *testing.T) { + c := config.NewConfig() + outgoing := make(chan telegraf.Metric, 1) + ctx, cancel := context.WithCancel(context.Background()) + signals := make(chan os.Signal) + // empty string + c.Agent.Version = "" + // confirm that default of not to ignore empty version string works as expected + agentMonitor, err := NewAgentMonitor(ctx, c, signals, outgoing) + require.NotNil(t, err) + + // now with the setting changed to ignore + c.Agent.IgnoreInvalidVersion = true + agentMonitor, err = NewAgentMonitor(ctx, c, signals, outgoing) + require.Nil(t, err) + + // change the jitter value to be crazy short - allowing consistent tests as must be shorter than ticker + agentMonitor.jitter = time.Duration(10 * time.Millisecond) + + // start the monitor + go agentMonitor.Run() + + // grab the startup signal - sent on startup + signal := <-outgoing + require.Contains(t, "agent_statechange", signal.Name()) + var expected = map[string]interface{}{ + "state": "started", + } + actual := signal.Fields() + require.Equal(t, expected, actual) + + // grab the version it just sent on startup + metric := <-outgoing + require.Contains(t, "agent_meta_data", metric.Name()) + expected = map[string]interface{}{ + "version_string": "none", + "number_inputs": int64(0), + "number_outputs": int64(0), + } + actual = metric.Fields() + require.Equal(t, expected, actual) + + // cancel the context, which should cause the agentmonitor to quietly die + cancel() + + // sleep to give cancel() time to work + time.Sleep(500 * time.Millisecond) + + // verify that it is indeed dead by trying to send another signal to it - which should panic + ticker := time.NewTicker(100 * time.Millisecond) + assert.NotPanics(t, func() { panicOnReceive(signals, ticker.C) }) + ticker.Stop() + +} + +func TestAgentMetaData(t *testing.T) { + c := config.NewConfig() + outgoing := make(chan telegraf.Metric, 1) + ctx, cancel := context.WithCancel(context.Background()) + signals := make(chan os.Signal) + c.Agent.Version = "1.20.30" + + // make our fake inputs here + fi := models.NewRunningInput(&fakeInput{name: "sheeps"}, &models.InputConfig{ + Name: "VeryFakeRunningInput", + }) + + c.Inputs = append(c.Inputs, fi) + + agentMonitor, err := NewAgentMonitor(ctx, c, signals, outgoing) + // no version string exists yet, so object instantiation should work + require.Nil(t, err) + + // change the jitter value to be crazy short - allowing consistent tests as must be shorter than ticker + agentMonitor.jitter = time.Duration(10 * time.Millisecond) + + // start the monitor + go agentMonitor.Run() + + // grab the startup signal - sent on startup + signal := <-outgoing + require.Contains(t, "agent_statechange", signal.Name()) + var expected = map[string]interface{}{ + "state": "started", + } + actual := signal.Fields() + require.Equal(t, expected, actual) + + // now grab meta data + metric := <-outgoing + require.Contains(t, "agent_meta_data", metric.Name()) + expected = map[string]interface{}{ + "version_string": "1.20.30", + "major_version": int64(1), + "minor_version": int64(20), + "patch_version": int64(30), + "number_inputs": int64(1), + "number_outputs": int64(0), + } + actual = metric.Fields() + require.Equal(t, expected, actual) + + // cancel the context, which should cause the agentmonitor to quietly die + cancel() + + // sleep to give cancel() time to work + time.Sleep(500 * time.Millisecond) + + // verify that it is indeed dead by trying to send another signal to it - which should panic + ticker := time.NewTicker(100 * time.Millisecond) + assert.NotPanics(t, func() { panicOnReceive(signals, ticker.C) }) + ticker.Stop() + +} + +type fakeInput struct { + name string +} + +func (f *fakeInput) SampleConfig() string { return f.name } +func (f *fakeInput) Description() string { return "description for: " + f.name } +func (f *fakeInput) Gather(acc telegraf.Accumulator) error { return nil } diff --git a/agent/agent_monitor.go b/agent/agent_monitor.go new file mode 100644 index 0000000000000..dbfc2da542194 --- /dev/null +++ b/agent/agent_monitor.go @@ -0,0 +1,288 @@ +package agent + +import ( + "context" + "errors" + "log" + "os" + "strconv" + "strings" + "sync" + "time" + "unicode" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/internal/config" +) + +type agentMetaData struct { + /* + to allow us to use comparison operators without relying on magic, the version string needs to be + seperated, and numeric in nature. However, to preserve information, we keep the entire version string + */ + versionString string + fields map[string]interface{} + tags map[string]string +} + +func extractNumeric(digitString string) (int64, bool, error) { + /* + basic helper that takes a series of characters, and removes leading + trailing non-numeric chars. + so the following transformations take place: + v1 -> 1 + V12 -> 12 + 123~somestring -> 123 + 1-blsbla-v2 -> 1 + */ + var sb strings.Builder + foundNumeric := false + for i, digit := range digitString { + if unicode.IsDigit(digit) { + foundNumeric = true + sb.WriteRune(digit) + } else { + if i > 0 { + break + } + } + + } + + if !foundNumeric { + // no numerics found, but this is not an error; only means that supplied string contained no numerics + return 0, foundNumeric, nil + } + + d, err := strconv.ParseInt(sb.String(), 0, 64) + if err != nil { + // found something that looks numeric, but failed to parse, which is definitely and error + return 0, foundNumeric, err + } + + return d, foundNumeric, nil +} + +func getNumericVersion(versionString string) ([]int64, error) { + /* + this function verfies that the supplied string represents a semantic version string: + + MAJOR.MINOR.PATCH + + so, the following are considered legal: + + 1.2.3 + 1.2.3-rc1 + + but not: + + 1.2 + 1.banana.2 + 1 + + once we have established that the format is correct, we need to extract the numerics. for example: + + v1.2.3 becomes -> 1.2.3 + 1.2.3~123bvdbc1 -> 1.2.3 + 1.1.9-alpha -> 1.1.9 + + this gives us the 3 fields needed for sorting (the appended, optional pre-release version is useless for sorting) + */ + + numerics := []int64{} + // verify that the version string contains 2 "." characters as we expect to find + dotsFound := strings.Count(versionString, ".") + if dotsFound != 2 { + return numerics, errors.New("version string is not of expected semantic format: " + versionString) + } + + chunks := strings.Split(versionString, ".") + numericsFound := 0 + // now loop through and strip any non-numeric characters + for _, c := range chunks { + d, found, err := extractNumeric(c) + if err != nil { + // this is a parse fail - need to bubble this up + return numerics, err + } + if found { + numericsFound++ + numerics = append(numerics, d) + } + } + + /* + sanity check that chunks only contains numerics, so avoid stuff like this: + 1.donkey.20 + v1.2.cows + */ + if len(chunks) != numericsFound { + return []int64{}, errors.New("version string contains nonsensical character sequence: " + versionString) + } + return numerics, nil + +} + +func (a *agentMetaData) addMetaData(conf *config.Config) error { + /* + simple initial (hence currently useless error return) helper that allows all non-version meta to be added + now add our basic stats here - can be anything else too high-level for inputs to have visibility of + */ + a.fields["number_inputs"] = len(conf.Inputs) + a.fields["number_outputs"] = len(conf.Outputs) + return nil +} + +func (a *agentMetaData) addVersion(conf *config.Config) error { + version := conf.Agent.Version + // handle empty version string + if version == "" { + version = "none" + } + a.versionString = version + a.fields["version_string"] = version + + // now that we have set the string version, we need to extract the numerics - and error if this fails + numericVersionChunks, err := getNumericVersion(version) + if err != nil { + if !conf.Agent.IgnoreInvalidVersion { + return err + } + } else { + // if here, we know that we have the required 3 numeric segments + a.fields["major_version"] = int64(numericVersionChunks[0]) + a.fields["minor_version"] = int64(numericVersionChunks[1]) + a.fields["patch_version"] = int64(numericVersionChunks[2]) + } + return nil +} + +func newagentMetaData(conf *config.Config) (*agentMetaData, error) { + a := new(agentMetaData) + a.fields = make(map[string]interface{}) + a.tags = conf.Tags + // add basic metadata + err := a.addMetaData(conf) + if err != nil { + return a, err + } + // now add the version + err = a.addVersion(conf) + return a, err +} + +type agentMonitor struct { + name string + config *config.Config + ctx context.Context + metaData *agentMetaData + outgoing chan telegraf.Metric + signals chan os.Signal + jitter time.Duration + interval time.Duration +} + +/* + next two functions are to fulfill the MetricMaker interface required by the Accumulator +*/ +func (a *agentMonitor) Name() string { + return a.name +} + +func (a *agentMonitor) MakeMetric(metric telegraf.Metric) telegraf.Metric { + return metric +} + +// NewAgentMonitor returns a new AgentMonitor, and an error, if one occured during instantiation of agentMetaData (which handles the version stuff) +func NewAgentMonitor(ctx context.Context, config *config.Config, singals chan os.Signal, outgoing chan telegraf.Metric) (*agentMonitor, error) { + a := new(agentMonitor) + a.name = "agent_monitor" + a.config = config + a.ctx = ctx + meta, err := newagentMetaData(config) + if err != nil { + return a, err + } + a.metaData = meta + a.outgoing = outgoing + a.signals = singals + // the next allows for easier testing as decouples the agent from config so Run() is testable with custom values + a.jitter = a.config.Agent.CollectionJitter.Duration + a.interval = a.config.Agent.MetaCollectionInterval.Duration + + return a, nil + +} + +func (a *agentMonitor) shouldRunCollection() bool { + // return whatever is set within the config + return a.config.Agent.EnableMeta +} + +func (a *agentMonitor) shouldRunSignals() bool { + // return whatever is set within the config + return a.config.Agent.EnableStateChange +} + +// Run starts and runs the AgentMonitor IF it has been enabled, and until the context is done. +func (a *agentMonitor) Run() { + /* + the agent emits two types of metric: + 1. scheduled periodic measurements. things like agent version + 2. notifications. things such as state changes as and when they occur (in this case, signals received by the agent) + + possible that Run() is called when not needed - simply log and exit gracefully. + */ + + if !a.shouldRunSignals() && !a.shouldRunCollection() { + log.Printf("D! [agent monitor] disabled in config, exiting") + return + } + acc := NewAccumulator(a, a.outgoing) + acc.SetPrecision(time.Second) + wg := new(sync.WaitGroup) + if a.shouldRunSignals() { + log.Printf("D! [agent monitor] starting signals") + // we need to send a state change on startup - as there is no Signal for this + fields := make(map[string]interface{}) + fields["state"] = "started" + acc.AddFields("agent_statechange", fields, a.metaData.tags, time.Now()) + wg.Add(1) + // now, start listening for signals from the parent as these take 1st priority + go func() { + defer wg.Done() + for { + select { + case data := <-a.signals: + signalReceived := data.String() + fields := make(map[string]interface{}) + fields["state"] = signalReceived + acc.AddFields("agent_statechange", fields, a.metaData.tags, time.Now()) + case <-a.ctx.Done(): + return + } + } + }() + } + if a.shouldRunCollection() { + log.Printf("D! [agent monitor] starting collector") + // now initiate the periodic collector + ticker := NewTicker(a.interval, a.jitter) + wg.Add(1) + go func() { + defer wg.Done() + defer ticker.Stop() + for { + acc.AddFields("agent_meta_data", a.metaData.fields, a.metaData.tags, time.Now()) + select { + case <-ticker.C: + //nothing to do - just allow next iteration + case <-a.ctx.Done(): + return + } + + } + }() + } + wg.Wait() + log.Printf("D! [agent monitor] exiting") +} diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index 4545833a71eff..1667856416732 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -88,11 +88,15 @@ func reloadLoop( ctx, cancel := context.WithCancel(context.Background()) signals := make(chan os.Signal) + signalsMonitorAgent := make(chan os.Signal, 10) // allows us to bubble the signal through to the monitor agent, add buffer to prevent blocking + signal.Notify(signals, os.Interrupt, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT) go func() { select { case sig := <-signals: + // push signal down to the monitor agent + signalsMonitorAgent <- sig if sig == syscall.SIGHUP { log.Printf("I! Reloading Telegraf config") <-reload @@ -104,7 +108,7 @@ func reloadLoop( } }() - err := runAgent(ctx, inputFilters, outputFilters) + err := runAgent(ctx, inputFilters, outputFilters, signalsMonitorAgent) if err != nil && err != context.Canceled { log.Fatalf("E! [telegraf] Error running agent: %v", err) } @@ -114,6 +118,7 @@ func reloadLoop( func runAgent(ctx context.Context, inputFilters []string, outputFilters []string, + signalsMonitorAgent chan os.Signal, ) error { // Setup default logging. This may need to change after reading the config // file, but we can configure it to use our logger implementation now. @@ -151,6 +156,8 @@ func runAgent(ctx context.Context, return fmt.Errorf("Agent flush_interval must be positive; found %s", c.Agent.Interval.Duration) } + // set version here + c.Agent.Version = version ag, err := agent.NewAgent(c) if err != nil { @@ -171,7 +178,7 @@ func runAgent(ctx context.Context, if *fTest || *fTestWait != 0 { testWaitDuration := time.Duration(*fTestWait) * time.Second - return ag.Test(ctx, testWaitDuration) + return ag.Test(ctx, testWaitDuration, signalsMonitorAgent) } log.Printf("I! Loaded inputs: %s", strings.Join(c.InputNames(), " ")) @@ -198,7 +205,7 @@ func runAgent(ctx context.Context, } } - return ag.Run(ctx) + return ag.Run(ctx, signalsMonitorAgent) } func usageExit(rc int) { @@ -217,6 +224,7 @@ func (p *program) Start(s service.Service) error { go p.run() return nil } + func (p *program) run() { stop = make(chan struct{}) reloadLoop( diff --git a/internal/config/config.go b/internal/config/config.go index a5315b9b6b5bc..075a544cbbb11 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -68,6 +68,7 @@ type Config struct { Processors models.RunningProcessors } +// NewConfig returns a Config object, with defaults initialized func NewConfig() *Config { c := &Config{ // Agent defaults: @@ -76,6 +77,10 @@ func NewConfig() *Config { RoundInterval: true, FlushInterval: internal.Duration{Duration: 10 * time.Second}, LogfileRotationMaxArchives: 5, + EnableMeta: true, + MetaCollectionInterval: internal.Duration{Duration: 3600 * time.Second}, + EnableStateChange: true, + IgnoreInvalidVersion: false, }, Tags: make(map[string]string), @@ -161,11 +166,17 @@ type AgentConfig struct { // If set to -1, no archives are removed. LogfileRotationMaxArchives int `toml:"logfile_rotation_max_archives"` - Hostname string - OmitHostname bool + Hostname string + OmitHostname bool + Version string + EnableMeta bool `toml:"report_meta_data"` + MetaCollectionInterval internal.Duration `toml:"meta_reporting_interval"` + EnableStateChange bool `toml:"report_state_change"` + IgnoreInvalidVersion bool `toml:"ignore_invalid_version"` + signal chan os.Signal } -// Inputs returns a list of strings of the configured inputs. +// InputNames returns a list of strings of the configured inputs. func (c *Config) InputNames() []string { var name []string for _, input := range c.Inputs { @@ -305,6 +316,24 @@ var agentConfig = ` ## If set to true, do no set the "host" tag in the telegraf agent. omit_hostname = false + ## Enable reporting of version + ## defaults to true + # report_meta_data = true + + ## Ignore invalid version string + ## this can happen if testing builds etc, and not using a semantic version schema. defaults to false + ## which means that the agent monitor will NOT send the version unless valid + # ignore_invalid_version = false + + ## Meta data reporting interval + ## only applies if "report_meta_data" is true, and defaults to every 60m + # meta_reporting_interval = "60m" + + ## Enable state-change reporting + ## when agent changes run state, it will, if enabled (the default), report + ## the change, when it takes place + # report_state_change = true + ` var outputHeader = `