Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into mackerel-agent_safe
Browse files Browse the repository at this point in the history
  • Loading branch information
Songmu committed Feb 21, 2017
2 parents a225f87 + ee0b056 commit 6305c9f
Show file tree
Hide file tree
Showing 20 changed files with 696 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
!.goxc.json
!.travis.yml
/build
/build-linux-*/
/id
/pid
/wix/mackerel-agent.wxs
Expand Down
2 changes: 1 addition & 1 deletion .goxc.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"TasksExclude": [
"go-test"
],
"MainDirsExclude": "wix,testdata,checks/testdata",
"MainDirsExclude": "wix,testdata,checks/testdata,metadata/testdata",
"ResourcesInclude": "README*,mackerel-agent.conf"
}
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 0.39.4 (2017-02-08)

* prepare windows eventlog #319 (daiksy)
* Refactor plugin configurations #322 (itchyny)
* Execute less `go build`s on deploy #323 (astj)
* treat xmlns #324 (mattn)
* Fix xmlns #326 (mattn)


## 0.39.3 (2017-01-25)

* Fix segfault when loading a bad config file #316 (hanazuki)
Expand Down
8 changes: 5 additions & 3 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import (
"github.com/mackerelio/mackerel-agent/checks"
"github.com/mackerelio/mackerel-agent/config"
"github.com/mackerelio/mackerel-agent/mackerel"
"github.com/mackerelio/mackerel-agent/metadata"
"github.com/mackerelio/mackerel-agent/metrics"
)

// Agent is the root of metrics collectors
type Agent struct {
MetricsGenerators []metrics.Generator
PluginGenerators []metrics.PluginGenerator
Checkers []*checks.Checker
MetricsGenerators []metrics.Generator
PluginGenerators []metrics.PluginGenerator
Checkers []*checks.Checker
MetadataGenerators []*metadata.Generator
}

// MetricsResult XXX
Expand Down
24 changes: 20 additions & 4 deletions command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,24 +192,39 @@ func loop(c *Context, termCh chan struct{}) error {

termMetricsCh := make(chan struct{})
var termCheckerCh chan struct{}
var termMetadataCh chan struct{}

hasChecks := len(c.Agent.Checkers) > 0
if hasChecks {
termCheckerCh = make(chan struct{})
}

hasMetadataPlugins := len(c.Agent.MetadataGenerators) > 0
if hasMetadataPlugins {
termMetadataCh = make(chan struct{})
}

// fan-out termCh
go func() {
for range termCh {
termMetricsCh <- struct{}{}
if termCheckerCh != nil {
termCheckerCh <- struct{}{}
}
if termMetadataCh != nil {
termMetadataCh <- struct{}{}
}
}
}()

if hasChecks {
go runCheckersLoop(c, termCheckerCh, quit)
}

if hasMetadataPlugins {
go runMetadataLoop(c, termMetadataCh, quit)
}

lState := loopStateFirst
for {
select {
Expand Down Expand Up @@ -420,7 +435,7 @@ func runCheckersLoop(c *Context, termCheckerCh <-chan struct{}, quit <-chan stru
select {
case <-time.After(1 * time.Minute):
case <-termCheckerCh:
logger.Debugf("received 'term' chan")
logger.Debugf("received 'term' chan for checkers loop")
exit = true
case <-reportImmediateCh:
logger.Debugf("received 'immediate' chan")
Expand Down Expand Up @@ -586,9 +601,10 @@ func runOncePayload(conf *config.Config) ([]mackerel.CreateGraphDefsPayload, *ma
// NewAgent creates a new instance of agent.Agent from its configuration conf.
func NewAgent(conf *config.Config) *agent.Agent {
return &agent.Agent{
MetricsGenerators: prepareGenerators(conf),
PluginGenerators: pluginGenerators(conf),
Checkers: createCheckers(conf),
MetricsGenerators: prepareGenerators(conf),
PluginGenerators: pluginGenerators(conf),
Checkers: createCheckers(conf),
MetadataGenerators: metadataGenerators(conf),
}
}

Expand Down
142 changes: 142 additions & 0 deletions command/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package command

import (
"os"
"path/filepath"
"time"

"github.com/mackerelio/mackerel-agent/config"
"github.com/mackerelio/mackerel-agent/metadata"
)

func metadataGenerators(conf *config.Config) []*metadata.Generator {
generators := make([]*metadata.Generator, 0, len(conf.MetadataPlugins))

workdir := os.Getenv("MACKEREL_PLUGIN_WORKDIR")
if workdir == "" {
workdir = os.TempDir()
}

for name, pluginConfig := range conf.MetadataPlugins {
generator := &metadata.Generator{
Name: name,
Config: pluginConfig,
Cachefile: filepath.Join(workdir, "mackerel-metadata", name),
}
logger.Debugf("Metadata plugin generator created: %#v %#v", generator, generator.Config)
generators = append(generators, generator)
}

return generators
}

type metadataResult struct {
namespace string
metadata interface{}
createdAt time.Time
}

func runMetadataLoop(c *Context, termMetadataCh <-chan struct{}, quit <-chan struct{}) {
resultCh := make(chan *metadataResult)
for _, g := range c.Agent.MetadataGenerators {
go runEachMetadataLoop(g, resultCh, quit)
}

exit := false
for !exit {
select {
case <-time.After(1 * time.Minute):
case <-termMetadataCh:
logger.Debugf("received 'term' chan for metadata loop")
exit = true
}

results := make(map[string]*metadataResult)
ConsumeResults:
for {
select {
case result := <-resultCh:
// prefer new result to avoid infinite number of retries
if prev, ok := results[result.namespace]; ok {
if result.createdAt.After(prev.createdAt) {
results[result.namespace] = result
}
} else {
results[result.namespace] = result
}
default:
break ConsumeResults
}
}

for _, result := range results {
resp, err := c.API.PutMetadata(c.Host.ID, result.namespace, result.metadata)
// retry on 5XX errors
if resp != nil && resp.StatusCode >= 500 {
logger.Errorf("put metadata %q failed: status %s", result.namespace, resp.Status)
go func() {
resultCh <- result
}()
continue
}
if err != nil {
logger.Errorf("put metadata %q failed: %v", result.namespace, err)
clearMetadataCache(c.Agent.MetadataGenerators, result.namespace)
continue
}
}
results = nil
}
}

func clearMetadataCache(generators []*metadata.Generator, namespace string) {
for _, g := range generators {
if g.Name == namespace {
g.Clear()
return
}
}
}

func runEachMetadataLoop(g *metadata.Generator, resultCh chan<- *metadataResult, quit <-chan struct{}) {
interval := g.Interval()
nextInterval := 10 * time.Second
nextTime := time.Now()

for {
select {
case <-time.After(nextInterval):
metadata, err := g.Fetch()

// case for laptop sleep mode (now >> nextTime + interval)
now := time.Now()
nextInterval = interval - (now.Sub(nextTime) % interval)
nextTime = now.Add(nextInterval)

if err != nil {
logger.Warningf("metadata plugin %q: %s", g.Name, err.Error())
continue
}

if !g.IsChanged(metadata) {
logger.Debugf("metadata plugin %q: metadata does not change", g.Name)
continue
}

if err := g.Save(metadata); err != nil {
logger.Warningf("metadata plugin %q: %s", g.Name, err.Error())
continue
}

logger.Debugf("metadata plugin %q: generated metadata (and saved cache to file: %s)", g.Name, g.Cachefile)
resultCh <- &metadataResult{
namespace: g.Name,
metadata: metadata,
createdAt: time.Now(),
}

case <-quit:
return
}
}
}
60 changes: 51 additions & 9 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ type Config struct {
Include string

// Cannot exist in configuration files
HostIDStorage HostIDStorage
MetricPlugins map[string]*MetricPlugin
CheckPlugins map[string]*CheckPlugin
HostIDStorage HostIDStorage
MetricPlugins map[string]*MetricPlugin
CheckPlugins map[string]*CheckPlugin
MetadataPlugins map[string]*MetadataPlugin
}

// PluginConfig represents a plugin configuration.
Expand All @@ -73,6 +74,7 @@ type PluginConfig struct {
User string
NotificationInterval *int32 `toml:"notification_interval"`
CheckInterval *int32 `toml:"check_interval"`
ExecutionInterval *int32 `toml:"execution_interval"`
MaxCheckAttempts *int32 `toml:"max_check_attempts"`
CustomIdentifier *string `toml:"custom_identifier"`
}
Expand Down Expand Up @@ -100,7 +102,7 @@ func (pconf *PluginConfig) buildMetricPlugin() (*MetricPlugin, error) {
}

// Run the metric plugin.
func (pconf *MetricPlugin) Run() (string, string, int, error) {
func (pconf *MetricPlugin) Run() (stdout, stderr string, exitCode int, err error) {
if len(pconf.CommandArgs) > 0 {
return util.RunCommandArgs(pconf.CommandArgs, pconf.User)
}
Expand Down Expand Up @@ -142,7 +144,37 @@ func (pconf *PluginConfig) buildCheckPlugin() (*CheckPlugin, error) {
}

// Run the check plugin.
func (pconf *CheckPlugin) Run() (string, string, int, error) {
func (pconf *CheckPlugin) Run() (stdout, stderr string, exitCode int, err error) {
if len(pconf.CommandArgs) > 0 {
return util.RunCommandArgs(pconf.CommandArgs, pconf.User)
}
return util.RunCommand(pconf.Command, pconf.User)
}

// MetadataPlugin represents the configuration of a metadata plugin
// The User option is ignored on Windows
type MetadataPlugin struct {
Command string
CommandArgs []string
User string
ExecutionInterval *int32
}

func (pconf *PluginConfig) buildMetadataPlugin() (*MetadataPlugin, error) {
err := pconf.prepareCommand()
if err != nil {
return nil, err
}
return &MetadataPlugin{
Command: pconf.Command,
CommandArgs: pconf.CommandArgs,
User: pconf.User,
ExecutionInterval: pconf.ExecutionInterval,
}, nil
}

// Run the metadata plugin.
func (pconf *MetadataPlugin) Run() (stdout, stderr string, exitCode int, err error) {
if len(pconf.CommandArgs) > 0 {
return util.RunCommandArgs(pconf.CommandArgs, pconf.User)
}
Expand Down Expand Up @@ -289,7 +321,7 @@ func LoadConfig(conffile string) (*Config, error) {
return config, err
}

func (conf *Config) setMetricPluginsAndCheckPlugins() error {
func (conf *Config) setEachPlugins() error {
if pconfs, ok := conf.Plugin["metrics"]; ok {
var err error
for name, pconf := range pconfs {
Expand All @@ -308,8 +340,17 @@ func (conf *Config) setMetricPluginsAndCheckPlugins() error {
}
}
}
if pconfs, ok := conf.Plugin["metadata"]; ok {
var err error
for name, pconf := range pconfs {
conf.MetadataPlugins[name], err = pconf.buildMetadataPlugin()
if err != nil {
return err
}
}
}
// Make Plugins empty because we should not use this later.
// Use MetricPlugins and CheckPlugins.
// Use MetricPlugins, CheckPlugins and MetadataPlugins.
conf.Plugin = nil
return nil
}
Expand All @@ -322,7 +363,8 @@ func loadConfigFile(file string) (*Config, error) {

config.MetricPlugins = make(map[string]*MetricPlugin)
config.CheckPlugins = make(map[string]*CheckPlugin)
if err := config.setMetricPluginsAndCheckPlugins(); err != nil {
config.MetadataPlugins = make(map[string]*MetadataPlugin)
if err := config.setEachPlugins(); err != nil {
return nil, err
}

Expand Down Expand Up @@ -360,7 +402,7 @@ func includeConfigFile(config *Config, include string) error {
}

// Add new plugin or overwrite a plugin with the same plugin name.
if err := config.setMetricPluginsAndCheckPlugins(); err != nil {
if err := config.setEachPlugins(); err != nil {
return err
}
}
Expand Down

0 comments on commit 6305c9f

Please sign in to comment.