Skip to content

Commit

Permalink
Merge branch 'master' into build-k
Browse files Browse the repository at this point in the history
  • Loading branch information
Songmu committed Dec 10, 2015
2 parents 0d823e5 + 98f657c commit b207ff3
Show file tree
Hide file tree
Showing 16 changed files with 494 additions and 176 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Expand Up @@ -21,8 +21,8 @@ deploy:
api_key:
secure: NtpNjquqjnwpeVQQM1GTHTTU7YOo8fEIyoBtMf3Vf1ayZjuWVZxwNfM77E596TG52a8pnZtpapXyHT0M4e1zms7F5KVCrOEfOB0OrA4IDzoATelVqdONnN3lbRJeVJVdSmK8/FNKwjI24tQZTaTQcIOioNqh7ZRcrEYlatGCuAw=
file:
- /home/travis/rpmbuild/RPMS/noarch/mackerel-agent-0.25.1-1.noarch.rpm
- packaging/mackerel-agent_0.25.1-1_all.deb
- /home/travis/rpmbuild/RPMS/noarch/mackerel-agent-0.26.2-1.noarch.rpm
- packaging/mackerel-agent_0.26.2-1_all.deb
- snapshot/mackerel-agent_darwin_386.zip
- snapshot/mackerel-agent_darwin_amd64.zip
- snapshot/mackerel-agent_freebsd_386.zip
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,25 @@
# Changelog

## 0.26.2 (2015-12-10)

* output success message to stderr when configtest succeed #178 (Songmu)


## 0.26.1 (2015-12-09)

* fix deprecate message #176 (Songmu)


## 0.26.0 (2015-12-08)

* Make HostID storage replacable #167 (motemen)
* Publicize command.Context's fields #168 (motemen)
* Configtest #169 (fujiwara)
* Refactor config loading and check if Apikey exists in configtest #171 (Songmu)
* fix exit status of debian init script. #172 (fujiwara)
* Deprecate version and once option #173 (Songmu)


## 0.25.1 (2015-11-25)

* Go 1.5.1 #164 (Songmu)
Expand Down
16 changes: 3 additions & 13 deletions Makefile
Expand Up @@ -22,25 +22,15 @@ run: build

deps:
go get -d -v -t ./...
go get github.com/golang/lint/golint
go get golang.org/x/tools/cmd/vet
go get golang.org/x/tools/cmd/cover
go get github.com/golang/lint/golint
go get github.com/pierrre/gotestcover
go get github.com/laher/goxc
go get github.com/mattn/goveralls

LINT_RET = .golint.txt
lint: deps
go vet ./...
rm -f $(LINT_RET)
for os in "$(BUILD_OS_TARGETS)"; do \
if [ $$os != "windows" ]; then \
GOOS=$$os golint ./... | grep -v '_string.go:' | tee -a $(LINT_RET); \
else \
GOOS=$$os golint --min_confidence=0.9 ./... | grep -v '_string.go:' | tee -a $(LINT_RET); \
fi \
done
test ! -s $(LINT_RET)
go tool vet -all .
tool/go-linter $(BUILD_OS_TARGETS)

crossbuild: deps
cp mackerel-agent.sample.conf mackerel-agent.conf
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Expand Up @@ -26,7 +26,7 @@ test_script:
- FOR /F "usebackq" %%w IN (`git rev-parse --show-cdup`) DO SET CDUP=%%w
- cd %CDUP%
- go get -d -v -t ./...
- go vet ./...
- go tool vet -all .
- go test -short ./...
notifications:
- provider: Slack
Expand Down
113 changes: 37 additions & 76 deletions command/command.go
Expand Up @@ -4,11 +4,8 @@ import (
"crypto/sha1"
"encoding/json"
"fmt"
"io/ioutil"
"math"
"os"
"path/filepath"
"strings"
"time"

"github.com/Songmu/retry"
Expand All @@ -25,52 +22,12 @@ import (
var logger = logging.GetLogger("command")
var metricsInterval = 60 * time.Second

const idFileName = "id"

func idFilePath(root string) string {
return filepath.Join(root, idFileName)
}

// LoadHostID loads hostID
func LoadHostID(root string) (string, error) {
content, err := ioutil.ReadFile(idFilePath(root))
if err != nil {
return "", err
}
return strings.TrimRight(string(content), "\r\n"), nil
}

// RemoveIDFile removes idfile
func RemoveIDFile(root string) error {
return os.Remove(idFilePath(root))
}

func saveHostID(root string, id string) error {
err := os.MkdirAll(root, 0755)
if err != nil {
return err
}

file, err := os.Create(idFilePath(root))
if err != nil {
return err
}
defer file.Close()

_, err = file.Write([]byte(id))
if err != nil {
return err
}

return nil
}

var retryNum uint = 20
var retryInterval = 3 * time.Second

// prepareHost collects specs of the host and sends them to Mackerel server.
// A unique host-id is returned by the server if one is not specified.
func prepareHost(root string, api *mackerel.API, roleFullnames []string, checks []string, displayName string, hostSt string) (*mackerel.Host, error) {
func prepareHost(conf *config.Config, api *mackerel.API) (*mackerel.Host, error) {
// XXX this configuration should be moved to under spec/linux
os.Setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin:"+os.Getenv("PATH"))
os.Setenv("LANG", "C") // prevent changing outputs of some command, e.g. ifconfig.
Expand All @@ -96,11 +53,11 @@ func prepareHost(root string, api *mackerel.API, roleFullnames []string, checks
}

var result *mackerel.Host
if hostID, err := LoadHostID(root); err != nil { // create
if hostID, err := conf.LoadHostID(); err != nil { // create
logger.Debugf("Registering new host on mackerel...")

doRetry(func() error {
hostID, lastErr = api.CreateHost(hostname, meta, interfaces, roleFullnames, displayName)
hostID, lastErr = api.CreateHost(hostname, meta, interfaces, conf.Roles, conf.DisplayName)
return filterErrorForRetry(lastErr)
})

Expand All @@ -121,10 +78,14 @@ func prepareHost(root string, api *mackerel.API, roleFullnames []string, checks
return filterErrorForRetry(lastErr)
})
if lastErr != nil {
return nil, fmt.Errorf("Failed to find this host on mackerel (You may want to delete file \"%s\" to register this host to an another organization): %s", idFilePath(root), lastErr.Error())
if fsStorage, ok := conf.HostIDStorage.(*config.FileSystemHostIDStorage); ok {
return nil, fmt.Errorf("Failed to find this host on mackerel (You may want to delete file \"%s\" to register this host to an another organization): %s", fsStorage.HostIDFile(), lastErr.Error())
}
return nil, fmt.Errorf("Failed to find this host on mackerel: %s", lastErr.Error())
}
}

hostSt := conf.HostStatus.OnStart
if hostSt != "" && hostSt != result.Status {
doRetry(func() error {
lastErr = api.UpdateHostStatus(result.ID, hostSt)
Expand All @@ -135,7 +96,7 @@ func prepareHost(root string, api *mackerel.API, roleFullnames []string, checks
}
}

lastErr = saveHostID(root, result.ID)
lastErr = conf.SaveHostID(result.ID)
if lastErr != nil {
return nil, fmt.Errorf("Failed to save host ID: %s", lastErr.Error())
}
Expand All @@ -153,10 +114,10 @@ func delayByHost(host *mackerel.Host) int {

// Context context object
type Context struct {
ag *agent.Agent
conf *config.Config
host *mackerel.Host
api *mackerel.API
Agent *agent.Agent
Config *config.Config
Host *mackerel.Host
API *mackerel.API
}

type postValue struct {
Expand Down Expand Up @@ -185,17 +146,17 @@ func loop(c *Context, termCh chan struct{}) int {
// Periodically update host specs.
go updateHostSpecsLoop(c, quit)

postQueue := make(chan *postValue, c.conf.Connection.PostMetricsBufferSize)
postQueue := make(chan *postValue, c.Config.Connection.PostMetricsBufferSize)
go enqueueLoop(c, postQueue, quit)

postDelaySeconds := delayByHost(c.host)
postDelaySeconds := delayByHost(c.Host)
initialDelay := postDelaySeconds / 2
logger.Debugf("wait %d seconds before initial posting.", initialDelay)
select {
case <-termCh:
return 0
case <-time.After(time.Duration(initialDelay) * time.Second):
c.ag.InitPluginGenerators(c.api)
c.Agent.InitPluginGenerators(c.API)
}

termCheckerCh := make(chan struct{})
Expand Down Expand Up @@ -236,10 +197,10 @@ func loop(c *Context, termCh chan struct{}) int {
case loopStateFirst: // request immediately to create graph defs of host
// nop
case loopStateQueued:
delaySeconds = c.conf.Connection.PostMetricsDequeueDelaySeconds
delaySeconds = c.Config.Connection.PostMetricsDequeueDelaySeconds
case loopStateHadError:
// TODO: better interval calculation. exponential backoff or so.
delaySeconds = c.conf.Connection.PostMetricsRetryDelaySeconds
delaySeconds = c.Config.Connection.PostMetricsRetryDelaySeconds
case loopStateTerminating:
// dequeue and post every one second when terminating.
delaySeconds = 1
Expand Down Expand Up @@ -278,7 +239,7 @@ func loop(c *Context, termCh chan struct{}) int {
for _, v := range origPostValues {
postValues = append(postValues, v.values...)
}
err := c.api.PostMetricsValues(postValues)
err := c.API.PostMetricsValues(postValues)
if err != nil {
logger.Errorf("Failed to post metrics value (will retry): %s", err.Error())
if lState != loopStateTerminating {
Expand All @@ -289,7 +250,7 @@ func loop(c *Context, termCh chan struct{}) int {
v.retryCnt++
// It is difficult to distinguish the error is server error or data error.
// So, if retryCnt exceeded the configured limit, postValue is considered invalid and abandoned.
if v.retryCnt > c.conf.Connection.PostMetricsRetryMax {
if v.retryCnt > c.Config.Connection.PostMetricsRetryMax {
json, err := json.Marshal(v.values)
if err != nil {
logger.Errorf("Something wrong with post values. marshaling failed.")
Expand Down Expand Up @@ -325,7 +286,7 @@ func updateHostSpecsLoop(c *Context, quit chan struct{}) {
}

func enqueueLoop(c *Context, postQueue chan *postValue, quit chan struct{}) {
metricsResult := c.ag.Watch()
metricsResult := c.Agent.Watch()
for {
select {
case <-quit:
Expand All @@ -335,14 +296,14 @@ func enqueueLoop(c *Context, postQueue chan *postValue, quit chan struct{}) {
creatingValues := [](*mackerel.CreatingMetricsValue){}
for name, value := range (map[string]float64)(result.Values) {
if math.IsNaN(value) || math.IsInf(value, 0) {
logger.Warningf("Invalid value: hostID = %s, name = %s, value = %f\n is not sent.", c.host.ID, name, value)
logger.Warningf("Invalid value: hostID = %s, name = %s, value = %f\n is not sent.", c.Host.ID, name, value)
continue
}

creatingValues = append(
creatingValues,
&mackerel.CreatingMetricsValue{
HostID: c.host.ID,
HostID: c.Host.ID,
Name: name,
Time: created,
Value: value,
Expand All @@ -363,7 +324,7 @@ func runCheckersLoop(c *Context, termCheckerCh <-chan struct{}, quit <-chan stru
checkReportCh chan *checks.Report
reportCheckImmediateCh chan struct{}
)
for _, checker := range c.ag.Checkers {
for _, checker := range c.Agent.Checkers {
if checkReportCh == nil {
checkReportCh = make(chan *checks.Report)
reportCheckImmediateCh = make(chan struct{})
Expand Down Expand Up @@ -439,7 +400,7 @@ func runCheckersLoop(c *Context, termCheckerCh <-chan struct{}, quit <-chan stru
continue
}

err := c.api.ReportCheckMonitors(c.host.ID, reports)
err := c.API.ReportCheckMonitors(c.Host.ID, reports)
if err != nil {
logger.Errorf("ReportCheckMonitors: %s", err)

Expand Down Expand Up @@ -496,13 +457,13 @@ func (c *Context) UpdateHostSpecs() {
return
}

err = c.api.UpdateHost(c.host.ID, mackerel.HostSpec{
err = c.API.UpdateHost(c.Host.ID, mackerel.HostSpec{
Name: hostname,
Meta: meta,
Interfaces: interfaces,
RoleFullnames: c.conf.Roles,
Checks: c.conf.CheckNames(),
DisplayName: c.conf.DisplayName,
RoleFullnames: c.Config.Roles,
Checks: c.Config.CheckNames(),
DisplayName: c.Config.DisplayName,
})

if err != nil {
Expand All @@ -520,16 +481,16 @@ func Prepare(conf *config.Config) (*Context, error) {
return nil, fmt.Errorf("Failed to prepare an api: %s", err.Error())
}

host, err := prepareHost(conf.Root, api, conf.Roles, conf.CheckNames(), conf.DisplayName, conf.HostStatus.OnStart)
host, err := prepareHost(conf, api)
if err != nil {
return nil, fmt.Errorf("Failed to prepare host: %s", err.Error())
}

return &Context{
ag: NewAgent(conf),
conf: conf,
host: host,
api: api,
Agent: NewAgent(conf),
Config: conf,
Host: host,
API: api,
}, nil
}

Expand Down Expand Up @@ -588,12 +549,12 @@ func NewAgent(conf *config.Config) *agent.Agent {

// Run starts the main metric collecting logic and this function will never return.
func Run(c *Context, termCh chan struct{}) int {
logger.Infof("Start: apibase = %s, hostName = %s, hostID = %s", c.conf.Apibase, c.host.Name, c.host.ID)
logger.Infof("Start: apibase = %s, hostName = %s, hostID = %s", c.Config.Apibase, c.Host.Name, c.Host.ID)

exitCode := loop(c, termCh)
if exitCode == 0 && c.conf.HostStatus.OnStop != "" {
if exitCode == 0 && c.Config.HostStatus.OnStop != "" {
// TOOD error handling. supoprt retire(?)
err := c.api.UpdateHostStatus(c.host.ID, c.conf.HostStatus.OnStop)
err := c.API.UpdateHostStatus(c.Host.ID, c.Config.HostStatus.OnStop)
if err != nil {
logger.Errorf("Failed update host status on stop: %s", err)
}
Expand Down
18 changes: 9 additions & 9 deletions command/command_test.go
Expand Up @@ -107,8 +107,8 @@ func TestPrepareWithCreate(t *testing.T) {
}

c, _ := Prepare(&conf)
api := c.api
host := c.host
api := c.API
host := c.Host

if api.BaseURL.String() != ts.URL {
t.Errorf("Apibase mismatch: %s != %s", api.BaseURL, ts.URL)
Expand Down Expand Up @@ -151,7 +151,7 @@ func TestPrepareWithUpdate(t *testing.T) {
defer ts.Close()
tempDir, _ := ioutil.TempDir("", "")
conf.Root = tempDir
saveHostID(tempDir, "xxx12345678901")
conf.SaveHostID("xxx12345678901")

mockHandlers["PUT /api/v0/hosts/xxx12345678901"] = func(req *http.Request) (int, jsonObject) {
return 200, jsonObject{
Expand All @@ -171,8 +171,8 @@ func TestPrepareWithUpdate(t *testing.T) {
}

c, _ := Prepare(&conf)
api := c.api
host := c.host
api := c.API
host := c.Host

if api.BaseURL.String() != ts.URL {
t.Errorf("Apibase mismatch: %s != %s", api.BaseURL, ts.URL)
Expand Down Expand Up @@ -311,10 +311,10 @@ func TestLoop(t *testing.T) {
termCh := make(chan struct{})
exitCh := make(chan int)
c := &Context{
ag: ag,
conf: &conf,
api: api,
host: host,
Agent: ag,
Config: &conf,
API: api,
Host: host,
}
// Start looping!
go func() {
Expand Down

0 comments on commit b207ff3

Please sign in to comment.