Skip to content

Commit

Permalink
Initial commit 鈱笍 馃敂
Browse files Browse the repository at this point in the history
  • Loading branch information
rbeuque74 committed Apr 16, 2018
0 parents commit ef2d44d
Show file tree
Hide file tree
Showing 25 changed files with 1,509 additions and 0 deletions.
17 changes: 17 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Binaries for programs and plugins
*.exe
*.dll
*.so
*.dylib

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/

# vim
*.swp
10 changes: 10 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
build:
binary: jagozzi
goos:
- linux
goarch:
- amd64
archive:
format: binary
snapshot:
name_template: "{{.Commit}}"
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
language: go

go:
- "1.10.x"

after_script:
- go get -v github.com/alecthomas/gometalinter/...
- gometalinter --install
- gometalinter

after_success:
- test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 Romain Beuque

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
all:
go build -o jagozzi main.go utils.go instance.go
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
jagozzi [![Build Status](https://travis-ci.org/rbeuque74/jagozzi.png?branch=master)](https://travis-ci.org/rbeuque74/jagozzi) [![GitHub release](https://img.shields.io/github/release/rbeuque74/jagozzi.svg)](https://github.com/rbeuque74/jagozzi/releases)
==============================

jagozzi is a light monitoring daemon for severals service in order to report results checks to a remote NSCA server.

This program is a Golang clone of [sauna](https://github.com/NicolasLM/sauna) that will parse the same configuration file format.

Services included
-----------------

- Supervisor
- Command
- Processes
- HTTP
- Marathon

Installation
------------

jagozzi can be installed using this command:

```
go install github.com/rbeuque74/jagozzi
```

License
-------

MIT, see [LICENSE](LICENSE)
22 changes: 22 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package config

import (
"io/ioutil"

"github.com/ghodss/yaml"
)

// Load is loading configuration from file and returns a jagozzi configuration
func Load(configurationFile string) (*Configuration, error) {
stream, err := ioutil.ReadFile(configurationFile)
if err != nil {
return nil, err
}

cfg := &Configuration{}
if err = yaml.Unmarshal(stream, cfg); err != nil {
return nil, err
}

return cfg, nil
}
72 changes: 72 additions & 0 deletions config/struct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package config

import (
"time"
)

// Configuration is the jagozzi main configuration structure
type Configuration struct {
rawConfiguration
// Periodicity is time span between two iterations of a check
Periodicity time.Duration `json:"-"`
}

type rawConfiguration struct {
RawPeriodicity int64 `json:"periodicity"`
Hostname string `json:"hostname"`
Consumers []ConsumerConfiguration `json:"consumers"`
Plugins []PluginConfiguration `json:"plugins"`
}

// UnmarshalJSON explicits some variables from configuration file to proper Golang type
func (cfg *Configuration) UnmarshalJSON(b []byte) error {
raw := &rawConfiguration{}

if err := UnmarshalConfig(b, raw); err != nil {
return err
}

cfg.rawConfiguration = *raw
cfg.Periodicity = time.Duration(raw.RawPeriodicity) * time.Second

return nil
}

// ConsumerConfiguration is the configuration of a consumer
type ConsumerConfiguration struct {
rawConsumerConfiguration
Timeout time.Duration `json:"-"`
}

type rawConsumerConfiguration struct {
Type string `json:"type"`
Server string `json:"server"`
Port int64 `json:"port"`
RawTimeout int64 `json:"timeout"`
Encryption int64 `json:"encryption"`
Key string `json:"key"`
}

// UnmarshalJSON explicits some variables from configuration file to proper Golang type
func (cfg *ConsumerConfiguration) UnmarshalJSON(b []byte) error {
raw := &rawConsumerConfiguration{}

if err := UnmarshalConfig(b, raw); err != nil {
return err
}

cfg.rawConsumerConfiguration = *raw
cfg.Timeout = time.Duration(raw.RawTimeout) * time.Millisecond

return nil
}

// PluginConfiguration represents the configuration of a plugin
type PluginConfiguration struct {
// Type is the name of the plugin that will run
Type string `json:"type"`
// Config is the custom configuration of the plugin
Config interface{} `json:"config,omitempty"`
// Checks is the list of all checks that plugin will run
Checks []interface{} `json:"checks"`
}
28 changes: 28 additions & 0 deletions config/unmarshal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package config

import (
"bytes"
"encoding/json"
"fmt"
"io"
)

// UnmarshalConfig will decode a JSON structure while disallowing unknown fields, to prevent configuration file errors
func UnmarshalConfig(b []byte, obj interface{}) error {
if ok := json.Valid(b); !ok {
return fmt.Errorf("json: not valid configuration")
}

reader := bytes.NewReader(b)
dec := json.NewDecoder(reader)
dec.DisallowUnknownFields()

if err := dec.Decode(obj); err != nil && err != io.EOF {
return err
}

if more := dec.More(); more {
return fmt.Errorf("json: not valid configuration")
}
return nil
}
62 changes: 62 additions & 0 deletions consumers/nsca/nsca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package nsca

import (
"context"

"github.com/rbeuque74/jagozzi/config"
"github.com/rbeuque74/jagozzi/plugins"
log "github.com/sirupsen/logrus"
"github.com/syncbak-git/nsca"
)

// Consumer is the representation of a NSCA consumer
type Consumer struct {
cfg config.ConsumerConfiguration
messageChannel chan *nsca.Message
exitChannel chan interface{}
}

// New generates a new NSCA Consumer instance
func New(messageChannel chan *nsca.Message, exitChannel chan interface{}) Consumer {
return Consumer{
messageChannel: messageChannel,
exitChannel: exitChannel,
}
}

// Send will produce a message to the specified consumer
func (consumer Consumer) Send(ctx context.Context, result plugins.Result, hostname string, errorChannel chan error) {
if consumer.messageChannel == nil {
log.Warnf("consumer: message channel is empty")
return
}

msg := nsca.Message{
State: int16(result.Status),
Host: hostname,
Service: result.Checker.ServiceName(),
Message: result.Message,
Status: errorChannel,
}

log.Debugf("consumer: send message %+v", msg)

ch := consumer.messageChannel
select {
case ch <- &msg:
return
case <-ctx.Done():
log.Warnf("consumer: timeout to push message to consumer message channel: %s", ctx.Err())
return
}
}

// Unload cleans all current operation/goroutine of consumer
func (consumer Consumer) Unload() {
if consumer.exitChannel == nil {
return
}

log.Debugf("consumer: sent 'quit' information to receiver")
consumer.exitChannel <- true
}
91 changes: 91 additions & 0 deletions instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"context"
"fmt"

"github.com/rbeuque74/jagozzi/config"
"github.com/rbeuque74/jagozzi/consumers/nsca"
"github.com/rbeuque74/jagozzi/plugins"
log "github.com/sirupsen/logrus"
nscalib "github.com/syncbak-git/nsca"
)

// Jagozzi is an instance of jagozzi checker
type Jagozzi struct {
cfg config.Configuration
checkers []plugins.Checker
consumers []nsca.Consumer
consumerErrorChannel chan error
}

// Load is loading configuration from file and returns a jagozzi configuration
func Load(cfg config.Configuration) (*Jagozzi, error) {
y := Jagozzi{
cfg: cfg,
consumerErrorChannel: make(chan error),
}

// Consumers initialisation
for _, consumer := range y.cfg.Consumers {
if consumer.Type != "NSCA" {
log.Warnf("config: found an unknown consumer type %q", consumer.Type)
continue
}
if consumer.Port == 0 {
// default nsca port
consumer.Port = 5667
}
serv := nscalib.ServerInfo{
Host: consumer.Server,
Port: fmt.Sprintf("%d", consumer.Port),
EncryptionMethod: int(consumer.Encryption),
Password: consumer.Key,
Timeout: consumer.Timeout,
}

exitChannel := make(chan interface{})
messagesChannel := make(chan *nscalib.Message)

log.Infof("consumer: starting NSCA server to %s:%d", consumer.Server, consumer.Port)
go nscalib.RunEndpoint(serv, exitChannel, messagesChannel)

consumerInstance := nsca.New(messagesChannel, exitChannel)
y.consumers = append(y.consumers, consumerInstance)
}

// Pluggins initialisation
for _, plugin := range y.cfg.Plugins {
for _, check := range plugin.Checks {
checker, err := plugins.CreateChecker(plugin.Type, check, plugin.Config)
if err != nil && err == plugins.UnknownCheckerTypeErr {
log.WithField("type", plugin.Type).Warn(err)
continue
} else if err != nil {
return nil, err
}
y.checkers = append(y.checkers, checker)
}
}

return &y, nil
}

// Unload cleans all current operation/goroutine loaded by configuration and configuration childs
func (y Jagozzi) Unload() {
for _, consumer := range y.consumers {
consumer.Unload()
}
}

// SendConsumers will send a NSCA message to all consumers
func (y Jagozzi) SendConsumers(ctx context.Context, result plugins.Result) {
for _, consumer := range y.consumers {
consumer.Send(ctx, result, y.cfg.Hostname, y.consumerErrorChannel)
}
}

// Checkers returns the list of checkers
func (y Jagozzi) Checkers() []plugins.Checker {
return y.checkers
}
Loading

0 comments on commit ef2d44d

Please sign in to comment.