cmd/stateth: initial commit for stateth app

This commit is contained in:
Anton Evangelatov 2018-05-17 13:10:06 +02:00
parent 92a633a7cb
commit 84b99c0e19
2 changed files with 2288 additions and 0 deletions

2033
cmd/stateth/json.go Normal file

File diff suppressed because it is too large Load diff

255
cmd/stateth/stateth.go Normal file
View file

@ -0,0 +1,255 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// puppeth is a command to assemble and maintain private networks.
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
"time"
"github.com/ethereum/go-ethereum/log"
gapi "github.com/teemupo/go-grafana-api"
"gopkg.in/urfave/cli.v1"
)
var (
dockerPrefix string // unique prefix used for the created docker resources
grafanaPort int // expose port for the Grafana HTTP interface
influxdbPort int // expose port for the InfluxDB HTTP interface
)
func main() {
app := cli.NewApp()
app.Name = "stateth"
app.Usage = "run a local grafana/influxdb setup for local Geth node stats visualization"
app.Version = "0.0.1"
app.Flags = []cli.Flag{
cli.IntFlag{
Name: "loglevel",
Value: 3,
Usage: "log level to emit to the screen",
},
cli.IntFlag{
Name: "influxdb-http-port",
Value: 8086,
Usage: "default influxdb http port",
},
cli.IntFlag{
Name: "grafana-http-port",
Value: 3000,
Usage: "default grafana http port",
},
cli.StringFlag{
Name: "docker-prefix",
Value: "stateth",
Usage: "prefix to be used for docker network and containers. must be unique.",
},
}
app.Action = func(c *cli.Context) error {
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
dockerPrefix = c.String("docker-prefix")
grafanaPort = c.Int("grafana-http-port")
influxdbPort = c.Int("influxdb-http-port")
if err := runNetwork(c); err != nil {
return err
}
if err := runInfluxDB(c); err != nil {
return err
}
if err := runGrafana(c); err != nil {
return err
}
log.Info("waiting for grafana to boot up...")
time.Sleep(7 * time.Second) // give time to Grafana to boot up
if err := importGrafanaDatasource(c); err != nil {
return err
}
if err := importGrafanaDashboard(c); err != nil {
return err
}
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
sig := <-sigs
fmt.Println()
fmt.Println(sig)
done <- true
}()
fmt.Println(fmt.Sprintf("grafana listening on http://localhost:%d", grafanaPort))
fmt.Println("username: admin")
fmt.Println("password: admin")
fmt.Println()
fmt.Println("waiting for SIGINT or SIGTERM (CTRL^C) to stop service and remove containers...")
<-done
if err := cleanupContainers(c); err != nil {
return err
}
return nil
}
app.Run(os.Args)
}
func runNetwork(c *cli.Context) error {
log.Info("creating docker network", "network", dockerPrefix)
command := strings.Split(fmt.Sprintf("docker network create %s", dockerPrefix), " ")
r, err := exec.Command(command[0], command[1:]...).CombinedOutput()
if err != nil {
log.Error(string(r))
return err
}
return nil
}
func runInfluxDB(c *cli.Context) error {
log.Info("pulling influxdb:1.5.2 docker image")
command := strings.Split("docker pull influxdb:1.5.2", " ")
r, err := exec.Command(command[0], command[1:]...).CombinedOutput()
if err != nil {
log.Error(err.Error())
return err
}
log.Info("running influxdb docker container", "container", fmt.Sprintf("%s_influxdb", dockerPrefix))
command = strings.Split(fmt.Sprintf("docker run --network %s --name %s_influxdb -e INFLUXDB_DB=metrics -e INFLUXDB_ADMIN_USER=admin -e INFLUXDB_ADMIN_PASSWORD=admin -p %d:8086 -d influxdb:1.5.2", dockerPrefix, dockerPrefix, influxdbPort), " ")
r, err = exec.Command(command[0], command[1:]...).CombinedOutput()
if err != nil {
log.Error(string(r))
return err
}
return nil
}
func runGrafana(c *cli.Context) error {
log.Info("pulling grafana/grafana:5.1.3 docker image")
command := strings.Split("docker pull grafana/grafana:5.1.3", " ")
r, err := exec.Command(command[0], command[1:]...).CombinedOutput()
if err != nil {
log.Error(string(r))
return err
}
log.Info("running grafana docker container", "container", fmt.Sprintf("%s_grafana", dockerPrefix))
command = strings.Split(fmt.Sprintf("docker run --network %s --name=%s_grafana -p %d:3000 -d grafana/grafana:5.1.3", dockerPrefix, dockerPrefix, grafanaPort), " ")
r, err = exec.Command(command[0], command[1:]...).CombinedOutput()
if err != nil {
log.Error(string(r))
return err
}
return nil
}
func cleanupContainers(c *cli.Context) error {
log.Info("removing influxdb container")
command := strings.Split(fmt.Sprintf("docker rm -f %s_influxdb", dockerPrefix), " ")
r, err := exec.Command(command[0], command[1:]...).CombinedOutput()
if err != nil {
log.Warn(string(r))
}
log.Info("removing grafana container")
command = strings.Split(fmt.Sprintf("docker rm -f %s_grafana", dockerPrefix), " ")
r, err = exec.Command(command[0], command[1:]...).CombinedOutput()
if err != nil {
log.Warn(string(r))
}
log.Info("removing network")
command = strings.Split(fmt.Sprintf("docker network rm %s", dockerPrefix), " ")
r, err = exec.Command(command[0], command[1:]...).CombinedOutput()
if err != nil {
log.Warn(string(r))
}
return nil
}
func importGrafanaDatasource(c *cli.Context) error {
log.Info("importing grafana datasource")
gclient, err := gapi.New("admin:admin", fmt.Sprintf("http://localhost:%d", grafanaPort))
if err != nil {
log.Warn(err.Error())
return nil
}
dataSource := &gapi.DataSource{
Name: "metrics",
Type: "influxdb",
URL: "http://stateth_influxdb:8086",
Access: "proxy",
Database: "metrics",
User: "admin",
Password: "admin",
IsDefault: true,
BasicAuth: false,
}
_, err = gclient.NewDataSource(dataSource)
if err != nil {
log.Warn(err.Error())
return err
}
return nil
}
func importGrafanaDashboard(c *cli.Context) error {
log.Info("importing grafana dashboards")
gclient, err := gapi.New("admin:admin", fmt.Sprintf("http://localhost:%d", grafanaPort))
if err != nil {
log.Warn(err.Error())
return nil
}
model := prepareDashboardModel(jsonDashboard)
_, err = gclient.SaveDashboard(model, false)
if err != nil {
log.Warn(err.Error())
return err
}
return nil
}
func prepareDashboardModel(configJSON string) map[string]interface{} {
configMap := map[string]interface{}{}
err := json.Unmarshal([]byte(configJSON), &configMap)
if err != nil {
panic("invalid JSON got into prepare func")
}
delete(configMap, "id")
// Only exists in 5.0+
delete(configMap, "uid")
configMap["version"] = 0
return configMap
}