forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Heartbeat monitor code generator (elastic#5792)
- add support for `make create-monitor` - code that needs to be adapted by user is marked with `IMPLEMENT_ME`
- Loading branch information
Showing
10 changed files
with
597 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"flag" | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"reflect" | ||
"strings" | ||
"text/template" | ||
) | ||
|
||
func main() { | ||
var ( | ||
monitor string | ||
generatorHome string | ||
path string | ||
) | ||
flag.StringVar(&monitor, "monitor", "", "Monitor name") | ||
flag.StringVar(&generatorHome, "home", "./scripts/generator/{{monitor}}", "Generator home path") | ||
flag.StringVar(&path, "path", "./monitors/active", "monitor output directory") | ||
flag.Parse() | ||
|
||
if monitor == "" { | ||
if err := prompt("Monitor name [example]: ", &monitor); err != nil { | ||
fatal(err) | ||
} | ||
|
||
if monitor == "" { | ||
monitor = "example" | ||
} | ||
} | ||
|
||
env := map[string]interface{}{ | ||
// variables | ||
"monitor": noDot(monitor), | ||
|
||
// functions | ||
"upper": strings.ToUpper, | ||
"lower": strings.ToLower, | ||
"title": strings.Title, | ||
} | ||
if err := generate(generatorHome, filepath.Join(path, monitor), env); err != nil { | ||
fatal(err) | ||
} | ||
} | ||
|
||
func prompt(msg string, to interface{}) error { | ||
fmt.Print(msg) | ||
_, err := fmt.Scanln(to) | ||
return err | ||
} | ||
|
||
// create a template function, such that the variable can be accessed without | ||
// the leading `.`. | ||
func noDot(v string) interface{} { | ||
return func() string { return v } | ||
} | ||
|
||
func fatal(err error) { | ||
fmt.Fprintln(os.Stderr, err) | ||
os.Exit(1) | ||
} | ||
|
||
func generate(generatorPath string, outPath string, env map[string]interface{}) error { | ||
root, err := filepath.Abs(generatorPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
outPath, err = filepath.Abs(outPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err := os.MkdirAll(outPath, os.ModeDir|os.ModePerm); err != nil { | ||
return err | ||
} | ||
|
||
const tmplExt = ".tmpl" | ||
|
||
return filepath.Walk(root, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if path == root { | ||
return nil | ||
} | ||
|
||
name, err := execNameTemplate(path[len(root)+1:], env) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if info.IsDir() { | ||
dir := filepath.Join(outPath, name) | ||
return os.MkdirAll(dir, os.ModeDir|os.ModePerm) | ||
} | ||
|
||
if filepath.Ext(name) != tmplExt { | ||
return copyFile(filepath.Join(outPath, name), path) | ||
} | ||
|
||
// template file. | ||
name = name[:len(name)-len(tmplExt)] | ||
return copyTemplate(filepath.Join(outPath, name), path, env) | ||
}) | ||
} | ||
|
||
func execNameTemplate(in string, env map[string]interface{}) (string, error) { | ||
var err error | ||
tmpl := withFunc(template.New("name"), env) | ||
tmpl, err = tmpl.Parse(in) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
var buf bytes.Buffer | ||
err = tmpl.Execute(&buf, withEnv(env)) | ||
return buf.String(), err | ||
} | ||
|
||
func copyTemplate(to, tmplPath string, env map[string]interface{}) error { | ||
var err error | ||
tmpl := withFunc(template.New(filepath.Base(tmplPath)), env) | ||
tmpl, err = tmpl.ParseFiles(tmplPath) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
out, err := os.Create(to) | ||
if err != nil { | ||
return err | ||
} | ||
defer out.Close() | ||
|
||
tmpl = tmpl.Funcs(env) | ||
return tmpl.Execute(out, env) | ||
} | ||
|
||
func copyFile(to, from string) error { | ||
in, err := os.Open(from) | ||
if err != nil { | ||
return err | ||
} | ||
defer in.Close() | ||
|
||
out, err := os.Create(to) | ||
if err != nil { | ||
return err | ||
} | ||
defer out.Close() | ||
|
||
_, err = io.Copy(out, in) | ||
return err | ||
} | ||
|
||
func withFunc(tmpl *template.Template, env map[string]interface{}) *template.Template { | ||
return tmpl.Funcs(filterEnv(func(v interface{}) bool { | ||
return reflect.TypeOf(v).Kind() == reflect.Func | ||
}, env)) | ||
} | ||
|
||
func withEnv(env map[string]interface{}) map[string]interface{} { | ||
return filterEnv(func(v interface{}) bool { | ||
return reflect.TypeOf(v).Kind() != reflect.Func | ||
}, env) | ||
} | ||
|
||
func filterEnv(pred func(interface{}) bool, env map[string]interface{}) map[string]interface{} { | ||
tmp := map[string]interface{}{} | ||
for k, v := range env { | ||
if pred(v) { | ||
tmp[k] = v | ||
} | ||
} | ||
|
||
if len(tmp) == 0 { | ||
return nil | ||
} | ||
return tmp | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
## Readme | ||
|
||
Code generator for Heartbeat monitors. | ||
|
||
In order to add a new monitor type to Heartbeat, run `make create-monitor | ||
MONITOR=<name>` from the Heartbeat directory. | ||
|
||
``` | ||
$ cd $GOPATH/src/github.com/elastic/beats/heartbeat | ||
$ make create-monitor MONITOR=<name> | ||
$ make update | ||
$ make # build heartbeat | ||
``` | ||
|
||
`make update` is required to update the import list, such that the new monitor | ||
is compiled into Heartbeat. | ||
|
||
The new monitor will be added to the `monitors/active/<name>` sub directory. | ||
|
||
Monitor structure: | ||
- `config.go`: The monitor configuration options and configuration validation. | ||
- `check.go`: The monitor validation support. | ||
- `job.go`: Implements the ping function for connecting and validating an | ||
endpoint. This file generates the monitor specific fields to an event. | ||
- `<name>.go`: The monitor entrypoint registering the factory for setting up | ||
monitoring jobs. | ||
- `_meta/fields.yml`: Document the monitors event field. | ||
- `_meta/config.yml`: Minimal sample configuration file | ||
- `_meta/config.reference.yml`: Reference configuration file. All availalbe | ||
settings are documented in the reference file. | ||
|
||
Code comments tagged with `IMPLEMENT_ME` in the go and meta files give details | ||
on changes required to implement a new monitor type. |
62 changes: 62 additions & 0 deletions
62
heartbeat/scripts/generator/{{monitor}}/_meta/config.reference.yml.tmpl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
- type: {{monitor}} | ||
|
||
# Monitor name used for job name and document type | ||
#name: {{monitor}} | ||
|
||
# Enable/Disable monitor | ||
#enabled: true | ||
|
||
# Configure task schedule | ||
schedule: '@every 30s' | ||
|
||
# list of hosts to monitor | ||
hosts: ["localhost"] | ||
|
||
# list of ports to ping (used if host is given without portname) | ||
ports: [12345] | ||
|
||
# SOCKS5 proxy url | ||
#proxy_url: '' | ||
|
||
# Resolve hostnames locally instead on SOCKS5 server: | ||
#proxy_use_local_resolver: false | ||
|
||
# Configure file json file to be watched for changes to the monitor: | ||
#watch.poll_file: | ||
# Path to check for updates. | ||
#path: | ||
|
||
# Interval between file file changed checks. | ||
#interval: 5s | ||
|
||
|
||
# Configure IP protocol types to ping on if hostnames are configured. Ping | ||
# all resolvable IPs if `mode` is `all`, or only one IP if `mode` is `any`. | ||
ipv4: true | ||
ipv6: true | ||
mode: any | ||
|
||
# Configure file json file to be watched for changes to the monitor: | ||
#watch.poll_file: | ||
# Path to check for updates. | ||
#path: | ||
|
||
# Interval between file file changed checks. | ||
#interval: 5s | ||
|
||
# Total test connection and data exchange timeout | ||
#timeout: 16s | ||
|
||
|
||
# TLS/SSL connection settings: | ||
#ssl: | ||
# Certificate Authorities | ||
#certificate_authorities: [''] | ||
|
||
# Required TLS protocols | ||
#supported_protocols: ["TLSv1.0", "TLSv1.1", "TLSv1.2"] | ||
|
||
# IMPLEMENT_ME: document check/validation settings | ||
#check: | ||
#request: | ||
#response: |
9 changes: 9 additions & 0 deletions
9
heartbeat/scripts/generator/{{monitor}}/_meta/config.yml.tmpl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
- type: {{monitor}} | ||
# list of hosts to monitor | ||
hosts: ["localhost:12345"] | ||
|
||
# Configure task schedule | ||
schedule: '@every 30s' | ||
|
||
# Total test connection and data exchange timeout | ||
#timeout: 16s |
22 changes: 22 additions & 0 deletions
22
heartbeat/scripts/generator/{{monitor}}/_meta/fields.yml.tmpl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
- key: {{monitor}} | ||
title: "{{monitor|upper}} monitor" | ||
description: | ||
fields: | ||
- name: {{monitor}} | ||
type: group | ||
description: > | ||
{{monitor|upper}} related fields. | ||
fields: | ||
- name: rtt | ||
type: group | ||
description: > | ||
{{monitor|upper}} layer round trip times. | ||
fields: | ||
- name: validate | ||
type: group | ||
description: > | ||
Duration of validation step based on existing {{monitor|upper}} connection. | ||
fields: | ||
- name: us | ||
type: long | ||
description: Duration in microseconds |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package {{monitor}} | ||
|
||
import "net" | ||
|
||
// IMPLEMENT_ME: implement validator | ||
type validator struct { | ||
} | ||
|
||
func makeValidator(config *config) (*validator, error) { | ||
return &validator{}, nil | ||
} | ||
|
||
func (v *validator) Check(conn net.Conn) error { | ||
return nil | ||
} |
Oops, something went wrong.