mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 02:12:23 +00:00
cmd, dashboard, stateth: integrate stateth into geth dashboard
This commit is contained in:
parent
7820faf26e
commit
b99e1a441c
12 changed files with 645 additions and 217 deletions
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/stateth"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
"github.com/naoina/toml"
|
"github.com/naoina/toml"
|
||||||
)
|
)
|
||||||
|
|
@ -80,6 +81,7 @@ type gethConfig struct {
|
||||||
Node node.Config
|
Node node.Config
|
||||||
Ethstats ethstatsConfig
|
Ethstats ethstatsConfig
|
||||||
Dashboard dashboard.Config
|
Dashboard dashboard.Config
|
||||||
|
Stateth stateth.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadConfig(file string, cfg *gethConfig) error {
|
func loadConfig(file string, cfg *gethConfig) error {
|
||||||
|
|
@ -114,6 +116,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
Shh: whisper.DefaultConfig,
|
Shh: whisper.DefaultConfig,
|
||||||
Node: defaultNodeConfig(),
|
Node: defaultNodeConfig(),
|
||||||
Dashboard: dashboard.DefaultConfig,
|
Dashboard: dashboard.DefaultConfig,
|
||||||
|
Stateth: stateth.DefaultConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load config file.
|
// Load config file.
|
||||||
|
|
@ -135,6 +138,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.SetShhConfig(ctx, stack, &cfg.Shh)
|
utils.SetShhConfig(ctx, stack, &cfg.Shh)
|
||||||
|
utils.SetStatethConfig(ctx, &cfg.Stateth)
|
||||||
utils.SetDashboardConfig(ctx, &cfg.Dashboard)
|
utils.SetDashboardConfig(ctx, &cfg.Dashboard)
|
||||||
|
|
||||||
return stack, cfg
|
return stack, cfg
|
||||||
|
|
@ -156,6 +160,7 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
utils.RegisterEthService(stack, &cfg.Eth)
|
utils.RegisterEthService(stack, &cfg.Eth)
|
||||||
|
|
||||||
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
|
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
|
||||||
|
utils.RegisterStatethService(stack, &cfg.Stateth, ctx)
|
||||||
utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit)
|
utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit)
|
||||||
}
|
}
|
||||||
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,11 @@ var (
|
||||||
utils.DashboardAddrFlag,
|
utils.DashboardAddrFlag,
|
||||||
utils.DashboardPortFlag,
|
utils.DashboardPortFlag,
|
||||||
utils.DashboardRefreshFlag,
|
utils.DashboardRefreshFlag,
|
||||||
|
utils.StatethDockerPrefixFlag,
|
||||||
|
utils.StatethDashboardsFolderFlag,
|
||||||
|
utils.StatethGrafanaPortFlag,
|
||||||
|
utils.StatethInfluxdbPortFlag,
|
||||||
|
utils.StatethRmFlag,
|
||||||
utils.EthashCacheDirFlag,
|
utils.EthashCacheDirFlag,
|
||||||
utils.EthashCachesInMemoryFlag,
|
utils.EthashCachesInMemoryFlag,
|
||||||
utils.EthashCachesOnDiskFlag,
|
utils.EthashCachesOnDiskFlag,
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,16 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
// utils.DashboardAssetsFlag,
|
// utils.DashboardAssetsFlag,
|
||||||
// },
|
// },
|
||||||
//},
|
//},
|
||||||
|
//{
|
||||||
|
// Name: "STATETH",
|
||||||
|
// Flags: []cli.Flag{
|
||||||
|
// utils.StatethDockerPrefixFlag,
|
||||||
|
// utils.StatethDashboardsFolderFlag,
|
||||||
|
// utils.StatethGrafanaPortFlag,
|
||||||
|
// utils.StatethInfluxdbPortFlag,
|
||||||
|
// utils.StatethRmFlag,
|
||||||
|
// },
|
||||||
|
//},
|
||||||
{
|
{
|
||||||
Name: "TRANSACTION POOL",
|
Name: "TRANSACTION POOL",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
|
@ -284,7 +294,7 @@ func init() {
|
||||||
uncategorized := []cli.Flag{}
|
uncategorized := []cli.Flag{}
|
||||||
for _, flag := range data.(*cli.App).Flags {
|
for _, flag := range data.(*cli.App).Flags {
|
||||||
if _, ok := categorized[flag.String()]; !ok {
|
if _, ok := categorized[flag.String()]; !ok {
|
||||||
if strings.HasPrefix(flag.GetName(), "dashboard") {
|
if strings.HasPrefix(flag.GetName(), "dashboard") || strings.HasPrefix(flag.GetName(), "stateth") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
uncategorized = append(uncategorized, flag)
|
uncategorized = append(uncategorized, flag)
|
||||||
|
|
|
||||||
|
|
@ -18,33 +18,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"os/signal"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
gapi "github.com/teemupo/go-grafana-api"
|
"github.com/ethereum/go-ethereum/stateth"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
"os"
|
||||||
|
"os/signal"
|
||||||
var (
|
"syscall"
|
||||||
dockerPrefix string // unique prefix used for the created docker resources
|
|
||||||
dashboardsFolder string // folder containing all dashboards to be imported in Grafana
|
|
||||||
grafanaPort int // expose port for the Grafana HTTP interface
|
|
||||||
influxdbPort int // expose port for the InfluxDB HTTP interface
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
influxdbAdminUser = "test" // admin username for InfluxDB
|
|
||||||
influxdbAdminPass = "test" // admin password for InfluxDB
|
|
||||||
grafanaUser = "admin" // default Grafana username - should not be changed here without first updating the docker image
|
|
||||||
grafanaPass = "admin" // default Grafana password - should not be changed here without first udpating the docker image
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -78,30 +58,25 @@ func main() {
|
||||||
Value: "stateth",
|
Value: "stateth",
|
||||||
Usage: "prefix to be used for docker network and containers. must be unique.",
|
Usage: "prefix to be used for docker network and containers. must be unique.",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "rm",
|
||||||
|
Usage: "Remove existing stateth network and stateth containers upon startup. make sure that the start up works every time, even if the service wasn't shut down gracefully",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
app.Action = func(c *cli.Context) error {
|
app.Action = func(ctx *cli.Context) error {
|
||||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(ctx.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
||||||
|
|
||||||
dockerPrefix = c.String("docker-prefix")
|
se, err := stateth.New(ctx, &stateth.Config{
|
||||||
grafanaPort = c.Int("grafana-http-port")
|
DockerPrefix: ctx.String("docker-prefix"),
|
||||||
influxdbPort = c.Int("influxdb-http-port")
|
GrafanaPort: ctx.Int("grafana-http-port"),
|
||||||
dashboardsFolder = c.String("grafana-dashboards-folder")
|
InfluxDBPort: ctx.Int("influxdb-http-port"),
|
||||||
|
DashboardsFolder: ctx.String("grafana-dashboards-folder"),
|
||||||
if err := runNetwork(c); err != nil {
|
Rm: ctx.Bool("rm"),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := runInfluxDB(c); err != nil {
|
if err = se.StartExternal(); 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 := importGrafanaDashboards(c); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,172 +91,11 @@ func main() {
|
||||||
done <- true
|
done <- true
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fmt.Println(fmt.Sprintf("grafana listening on http://localhost:%d", grafanaPort))
|
|
||||||
fmt.Println(fmt.Sprintf("username: %s", grafanaUser))
|
|
||||||
fmt.Println(fmt.Sprintf("password: %s", grafanaPass))
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Println("waiting for SIGINT or SIGTERM (CTRL^C) to stop service and remove containers...")
|
fmt.Println("waiting for SIGINT or SIGTERM (CTRL^C) to stop service and remove containers...")
|
||||||
<-done
|
<-done
|
||||||
|
|
||||||
return cleanupContainers(c)
|
return se.StopExternal()
|
||||||
}
|
}
|
||||||
|
|
||||||
app.Run(os.Args)
|
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=%s -e INFLUXDB_ADMIN_PASSWORD=%s -p %d:8086 -d influxdb:1.5.2", dockerPrefix, dockerPrefix, influxdbAdminUser, influxdbAdminPass, 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(fmt.Sprintf("%s:%s", grafanaUser, grafanaPass), fmt.Sprintf("http://localhost:%d", grafanaPort))
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
dataSource := &gapi.DataSource{
|
|
||||||
Name: "metrics",
|
|
||||||
Type: "influxdb",
|
|
||||||
URL: fmt.Sprintf("http://%s_influxdb:%d", dockerPrefix, influxdbPort),
|
|
||||||
Access: "proxy",
|
|
||||||
Database: "metrics",
|
|
||||||
User: influxdbAdminUser,
|
|
||||||
Password: influxdbAdminPass,
|
|
||||||
IsDefault: true,
|
|
||||||
BasicAuth: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = gclient.NewDataSource(dataSource)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err.Error())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func importGrafanaDashboards(c *cli.Context) error {
|
|
||||||
log.Info("importing grafana dashboards")
|
|
||||||
gclient, err := gapi.New(fmt.Sprintf("%s:%s", grafanaUser, grafanaPass), fmt.Sprintf("http://localhost:%d", grafanaPort))
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := ioutil.ReadDir(dashboardsFolder)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range files {
|
|
||||||
name := f.Name()
|
|
||||||
if strings.Contains(name, "json") {
|
|
||||||
log.Info("importing dashboard", "dashboard", name)
|
|
||||||
|
|
||||||
blob, err := ioutil.ReadFile(dashboardsFolder + "/" + name)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
model := prepareDashboardModel(string(blob))
|
|
||||||
|
|
||||||
_, err = gclient.SaveDashboard(model, false)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn(err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/stateth"
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
@ -193,7 +194,7 @@ var (
|
||||||
}
|
}
|
||||||
// Dashboard settings
|
// Dashboard settings
|
||||||
DashboardEnabledFlag = cli.BoolFlag{
|
DashboardEnabledFlag = cli.BoolFlag{
|
||||||
Name: "dashboard",
|
Name: metrics.DashboardEnabledFlag,
|
||||||
Usage: "Enable the dashboard",
|
Usage: "Enable the dashboard",
|
||||||
}
|
}
|
||||||
DashboardAddrFlag = cli.StringFlag{
|
DashboardAddrFlag = cli.StringFlag{
|
||||||
|
|
@ -211,6 +212,31 @@ var (
|
||||||
Usage: "Dashboard metrics collection refresh rate",
|
Usage: "Dashboard metrics collection refresh rate",
|
||||||
Value: dashboard.DefaultConfig.Refresh,
|
Value: dashboard.DefaultConfig.Refresh,
|
||||||
}
|
}
|
||||||
|
// Stateth settings
|
||||||
|
StatethDockerPrefixFlag = cli.StringFlag{
|
||||||
|
Name: "stateth.dockerprefix",
|
||||||
|
Usage: "Prefix to be used for docker network and containers. Must be unique.",
|
||||||
|
Value: stateth.DefaultConfig.DockerPrefix,
|
||||||
|
}
|
||||||
|
StatethDashboardsFolderFlag = cli.StringFlag{
|
||||||
|
Name: "stateth.dashboardsfolder",
|
||||||
|
Usage: "Grafana dashboards folder",
|
||||||
|
Value: stateth.DefaultConfig.DashboardsFolder,
|
||||||
|
}
|
||||||
|
StatethGrafanaPortFlag = cli.IntFlag{
|
||||||
|
Name: "stateth.grafanaport",
|
||||||
|
Usage: "Grafana http port",
|
||||||
|
Value: stateth.DefaultConfig.GrafanaPort,
|
||||||
|
}
|
||||||
|
StatethInfluxdbPortFlag = cli.IntFlag{
|
||||||
|
Name: "stateth.influxdbport",
|
||||||
|
Usage: "Influxdb http port",
|
||||||
|
Value: stateth.DefaultConfig.InfluxDBPort,
|
||||||
|
}
|
||||||
|
StatethRmFlag = cli.BoolFlag{
|
||||||
|
Name: "stateth.rm",
|
||||||
|
Usage: "Remove existing stateth network and stateth containers upon startup. Make sure that the start up works every time, even if the service wasn't shut down gracefully.",
|
||||||
|
}
|
||||||
// Ethash settings
|
// Ethash settings
|
||||||
EthashCacheDirFlag = DirectoryFlag{
|
EthashCacheDirFlag = DirectoryFlag{
|
||||||
Name: "ethash.cachedir",
|
Name: "ethash.cachedir",
|
||||||
|
|
@ -1157,6 +1183,14 @@ func SetDashboardConfig(ctx *cli.Context, cfg *dashboard.Config) {
|
||||||
cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name)
|
cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetStatethConfig(ctx *cli.Context, cfg *stateth.Config) {
|
||||||
|
cfg.DockerPrefix = ctx.GlobalString(StatethDockerPrefixFlag.Name)
|
||||||
|
cfg.DashboardsFolder = ctx.GlobalString(StatethDashboardsFolderFlag.Name)
|
||||||
|
cfg.GrafanaPort = ctx.GlobalInt(StatethGrafanaPortFlag.Name)
|
||||||
|
cfg.InfluxDBPort = ctx.GlobalInt(StatethInfluxdbPortFlag.Name)
|
||||||
|
cfg.Rm = ctx.GlobalBool(StatethRmFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterEthService adds an Ethereum client to the stack.
|
// RegisterEthService adds an Ethereum client to the stack.
|
||||||
func RegisterEthService(stack *node.Node, cfg *eth.Config) {
|
func RegisterEthService(stack *node.Node, cfg *eth.Config) {
|
||||||
var err error
|
var err error
|
||||||
|
|
@ -1193,6 +1227,12 @@ func RegisterDashboardService(stack *node.Node, cfg *dashboard.Config, commit st
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RegisterStatethService(stack *node.Node, cfg *stateth.Config, cliCtx *cli.Context) {
|
||||||
|
stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||||
|
return stateth.New(cliCtx, cfg)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterShhService configures Whisper and adds it to the given node.
|
// RegisterShhService configures Whisper and adds it to the given node.
|
||||||
func RegisterShhService(stack *node.Node, cfg *whisper.Config) {
|
func RegisterShhService(stack *node.Node, cfg *whisper.Config) {
|
||||||
if err := stack.Register(func(n *node.ServiceContext) (node.Service, error) {
|
if err := stack.Register(func(n *node.ServiceContext) (node.Service, error) {
|
||||||
|
|
|
||||||
|
|
@ -29943,7 +29943,19 @@ var _bundleJs = []byte((((((((((`!function(modules) {
|
||||||
color: "inherit",
|
color: "inherit",
|
||||||
noWrap: !0,
|
noWrap: !0,
|
||||||
className: classes.title
|
className: classes.title
|
||||||
}, "Go Ethereum Dashboard")));
|
}, "Go Ethereum Dashboard"), _react2.default.createElement(_Typography2.default, {
|
||||||
|
type: "title",
|
||||||
|
color: "inherit",
|
||||||
|
noWrap: !0,
|
||||||
|
className: classes.title
|
||||||
|
}, _react2.default.createElement("a", {
|
||||||
|
href: "http://localhost:3000",
|
||||||
|
target: "_blank",
|
||||||
|
style: {
|
||||||
|
color: "inherit",
|
||||||
|
textDecoration: "none"
|
||||||
|
}
|
||||||
|
}, "Grafana"))));
|
||||||
}
|
}
|
||||||
} ]), Header;
|
} ]), Header;
|
||||||
}(_react.Component);
|
}(_react.Component);
|
||||||
|
|
@ -40572,7 +40584,7 @@ func bundleJs() (*asset, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "bundle.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
info := bindataFileInfo{name: "bundle.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x88, 0x4d, 0x2e, 0x77, 0x25, 0xbe, 0x43, 0xdf, 0x86, 0xed, 0x5b, 0x2c, 0xa8, 0xdd, 0xa2, 0xd5, 0x38, 0xf3, 0x82, 0xbe, 0x29, 0x9d, 0x52, 0x3, 0xd4, 0x4c, 0xd3, 0x7e, 0xaa, 0x3e, 0xf6, 0x8c}}
|
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x49, 0x2b, 0x2a, 0x45, 0xb3, 0x61, 0xc, 0xc3, 0xa3, 0x8a, 0x9e, 0x66, 0x30, 0x3e, 0x98, 0xe7, 0xde, 0x48, 0x25, 0xf3, 0x77, 0x8f, 0xbe, 0x86, 0x48, 0x3, 0x1, 0x3a, 0x4a, 0xab, 0xac, 0x38}}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,9 @@ class Header extends Component<Props> {
|
||||||
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
||||||
Go Ethereum Dashboard
|
Go Ethereum Dashboard
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
||||||
|
<a href={'http://localhost:3000'} target='_blank' style={{color: 'inherit', textDecoration: 'none'}} >Grafana</a>
|
||||||
|
</Typography>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,7 @@ func New(config *Config, commit string, ethServ *eth.Ethereum, lesServ *les.Ligh
|
||||||
},
|
},
|
||||||
ethServ: ethServ,
|
ethServ: ethServ,
|
||||||
lesServ: lesServ,
|
lesServ: lesServ,
|
||||||
logdir: logdir,
|
logdir: logdir,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,13 +126,14 @@ func emptyChartEntries(t time.Time, limit int, refresh time.Duration) ChartEntri
|
||||||
return ce
|
return ce
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protocols is a meaningless implementation of node.Service.
|
// Protocols implements the node.Service interface.
|
||||||
func (db *Dashboard) Protocols() []p2p.Protocol { return nil }
|
func (db *Dashboard) Protocols() []p2p.Protocol { return nil }
|
||||||
|
|
||||||
// APIs is a meaningless implementation of node.Service.
|
// APIs implements the node.Service interface.
|
||||||
func (db *Dashboard) APIs() []rpc.API { return nil }
|
func (db *Dashboard) APIs() []rpc.API { return nil }
|
||||||
|
|
||||||
// Start implements node.Service, starting the data collection thread and the listening server of the dashboard.
|
// Start starts the data collection thread and the listening server of the dashboard.
|
||||||
|
// Implements the node.Service interface.
|
||||||
func (db *Dashboard) Start(server *p2p.Server) error {
|
func (db *Dashboard) Start(server *p2p.Server) error {
|
||||||
log.Info("Starting dashboard")
|
log.Info("Starting dashboard")
|
||||||
|
|
||||||
|
|
@ -154,7 +155,8 @@ func (db *Dashboard) Start(server *p2p.Server) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop implements node.Service, stopping the data collection thread and the connection listener of the dashboard.
|
// Stop stops the data collection thread and the connection listener of the dashboard.
|
||||||
|
// Implements the node.Service interface.
|
||||||
func (db *Dashboard) Stop() error {
|
func (db *Dashboard) Stop() error {
|
||||||
// Close the connection listener.
|
// Close the connection listener.
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,8 @@ type SystemMessage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogsMessage struct {
|
type LogsMessage struct {
|
||||||
Old *LogFile `json:"old,omitempty"` // Attributes of the log file.
|
Old *LogFile `json:"old,omitempty"` // Attributes of the log file.
|
||||||
Chunk json.RawMessage `json:"chunk"` // Contains log records.
|
Chunk json.RawMessage `json:"chunk"` // Contains log records.
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogFile struct {
|
type LogFile struct {
|
||||||
|
|
|
||||||
49
stateth/config.go
Normal file
49
stateth/config.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package stateth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultConfig contains default settings for the stateth.
|
||||||
|
var DefaultConfig = Config{
|
||||||
|
DockerPrefix: "stateth",
|
||||||
|
DashboardsFolder: os.Getenv("GOPATH") + "/src/github.com/ethereum/go-ethereum/stateth/grafana_dashboards",
|
||||||
|
GrafanaPort: 3000,
|
||||||
|
InfluxDBPort: 8086,
|
||||||
|
Rm: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config contains the configuration parameters of the dashboard.
|
||||||
|
type Config struct {
|
||||||
|
// DockerPrefix is a unique prefix used for the created docker resources.
|
||||||
|
DockerPrefix string `toml:",omitempty"`
|
||||||
|
|
||||||
|
// DashboardsFolder is a folder containing all dashboards to be imported in Grafana.
|
||||||
|
DashboardsFolder string `toml:",omitempty"`
|
||||||
|
|
||||||
|
// GrafanaPort is the expose port for the Grafana HTTP interface.
|
||||||
|
GrafanaPort int `toml:",omitempty"`
|
||||||
|
|
||||||
|
// InfluxDBPort is the expose port for the InfluxDB HTTP interface.
|
||||||
|
InfluxDBPort int `toml:",omitempty"`
|
||||||
|
|
||||||
|
// Rm removes existing stateth network and stateth containers upon startup if true.
|
||||||
|
// Makes sure that the start up works every time, even if the service wasn't shut down gracefully.
|
||||||
|
Rm bool `toml:",omitempty"`
|
||||||
|
}
|
||||||
231
stateth/grafana_dashboards/geth.json
Normal file
231
stateth/grafana_dashboards/geth.json
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:452",
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"gnetId": null,
|
||||||
|
"graphTooltip": 1,
|
||||||
|
"id": 1,
|
||||||
|
"iteration": 1526677592883,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 51,
|
||||||
|
"panels": [],
|
||||||
|
"title": "Geth",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cacheTimeout": null,
|
||||||
|
"colorBackground": false,
|
||||||
|
"colorValue": false,
|
||||||
|
"colors": [
|
||||||
|
"#299c46",
|
||||||
|
"rgba(237, 129, 40, 0.89)",
|
||||||
|
"#d44a3a"
|
||||||
|
],
|
||||||
|
"datasource": null,
|
||||||
|
"format": "none",
|
||||||
|
"gauge": {
|
||||||
|
"maxValue": 100,
|
||||||
|
"minValue": 0,
|
||||||
|
"show": false,
|
||||||
|
"thresholdLabels": false,
|
||||||
|
"thresholdMarkers": true
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 4,
|
||||||
|
"w": 5,
|
||||||
|
"x": 0,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"id": 53,
|
||||||
|
"interval": null,
|
||||||
|
"links": [],
|
||||||
|
"mappingType": 1,
|
||||||
|
"mappingTypes": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:731",
|
||||||
|
"name": "value to text",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:732",
|
||||||
|
"name": "range to text",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maxDataPoints": 100,
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"nullText": null,
|
||||||
|
"postfix": "",
|
||||||
|
"postfixFontSize": "50%",
|
||||||
|
"prefix": "",
|
||||||
|
"prefixFontSize": "50%",
|
||||||
|
"rangeMaps": [
|
||||||
|
{
|
||||||
|
"from": "null",
|
||||||
|
"text": "N/A",
|
||||||
|
"to": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"sparkline": {
|
||||||
|
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||||
|
"full": true,
|
||||||
|
"lineColor": "rgb(31, 120, 193)",
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
"tableColumn": "",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:668",
|
||||||
|
"groupBy": [
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"$__interval"
|
||||||
|
],
|
||||||
|
"type": "time"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"null"
|
||||||
|
],
|
||||||
|
"type": "fill"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"measurement": "geth.currentBlock.gauge",
|
||||||
|
"orderByTime": "ASC",
|
||||||
|
"policy": "default",
|
||||||
|
"refId": "A",
|
||||||
|
"resultFormat": "time_series",
|
||||||
|
"select": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"params": [
|
||||||
|
"value"
|
||||||
|
],
|
||||||
|
"type": "field"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"params": [],
|
||||||
|
"type": "last"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": "",
|
||||||
|
"title": "Current block number",
|
||||||
|
"type": "singlestat",
|
||||||
|
"valueFontSize": "100%",
|
||||||
|
"valueMaps": [
|
||||||
|
{
|
||||||
|
"$$hashKey": "object:734",
|
||||||
|
"op": "=",
|
||||||
|
"text": "N/A",
|
||||||
|
"value": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"valueName": "avg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": "5s",
|
||||||
|
"schemaVersion": 16,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [],
|
||||||
|
"templating": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"auto": false,
|
||||||
|
"auto_count": 30,
|
||||||
|
"auto_min": "10s",
|
||||||
|
"current": {
|
||||||
|
"text": "10s",
|
||||||
|
"value": "10s"
|
||||||
|
},
|
||||||
|
"hide": 0,
|
||||||
|
"label": "resolution",
|
||||||
|
"name": "myinterval",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "5s",
|
||||||
|
"value": "5s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": true,
|
||||||
|
"text": "10s",
|
||||||
|
"value": "10s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "30s",
|
||||||
|
"value": "30s"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "100s",
|
||||||
|
"value": "100s"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "5s,10s,30s,100s",
|
||||||
|
"refresh": 2,
|
||||||
|
"type": "interval"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-15m",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {
|
||||||
|
"refresh_intervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
],
|
||||||
|
"time_options": [
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"1h",
|
||||||
|
"6h",
|
||||||
|
"12h",
|
||||||
|
"24h",
|
||||||
|
"2d",
|
||||||
|
"7d",
|
||||||
|
"30d"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Geth",
|
||||||
|
"uid": "dUpKvj7mz",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
257
stateth/stateth.go
Normal file
257
stateth/stateth.go
Normal file
|
|
@ -0,0 +1,257 @@
|
||||||
|
// Copyright 2018 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package stateth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
gapi "github.com/teemupo/go-grafana-api"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
"io/ioutil"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
influxDBAdminUser = "test" // admin username for InfluxDB.
|
||||||
|
influxDBAdminPass = "test" // admin password for InfluxDB.
|
||||||
|
grafanaUser = "admin" // default Grafana username - should not be changed here without first updating the docker image.
|
||||||
|
grafanaPass = "admin" // default Grafana password - should not be changed here without first udpating the docker image.
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stateth contains the stateth internals.
|
||||||
|
type Stateth struct {
|
||||||
|
ctx *cli.Context
|
||||||
|
config *Config
|
||||||
|
gclient *gapi.Client // Grafana client.
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new stateth instance with the given configuration.
|
||||||
|
func New(ctx *cli.Context, config *Config) (*Stateth, error) {
|
||||||
|
return &Stateth{
|
||||||
|
ctx: ctx,
|
||||||
|
config: config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocols implements the node.Service interface.
|
||||||
|
func (se *Stateth) Protocols() []p2p.Protocol { return nil }
|
||||||
|
|
||||||
|
// APIs implements the node.Service interface.
|
||||||
|
func (se *Stateth) APIs() []rpc.API { return nil }
|
||||||
|
|
||||||
|
// Start starts InfluxDB and Grafana.
|
||||||
|
// Implements the node.Service interface.
|
||||||
|
func (se *Stateth) Start(server *p2p.Server) error {
|
||||||
|
return se.StartExternal()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop cleans up the containers.
|
||||||
|
// Implements the node.Service interface.
|
||||||
|
func (se *Stateth) Stop() error { return se.StopExternal() }
|
||||||
|
|
||||||
|
func (se *Stateth) StartExternal() error {
|
||||||
|
var err error
|
||||||
|
if se.config.Rm {
|
||||||
|
if err = se.cleanupContainers(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = se.runNetwork(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = se.runInfluxDB(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = se.runGrafana(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Info("waiting for grafana to boot up...")
|
||||||
|
time.Sleep(7 * time.Second) // give time to Grafana to boot up
|
||||||
|
|
||||||
|
se.gclient, err = gapi.New(fmt.Sprintf("%s:%s", grafanaUser, grafanaPass), fmt.Sprintf("http://localhost:%d", se.config.GrafanaPort))
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err = se.importGrafanaDatasource(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = se.importGrafanaDashboards(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(fmt.Sprintf("grafana listening on http://localhost:%d", se.config.GrafanaPort))
|
||||||
|
fmt.Println(fmt.Sprintf("username: %s", grafanaUser))
|
||||||
|
fmt.Println(fmt.Sprintf("password: %s", grafanaPass))
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *Stateth) StopExternal() error { return se.cleanupContainers() }
|
||||||
|
|
||||||
|
func (se *Stateth) runNetwork() error {
|
||||||
|
log.Info("creating docker network", "network", se.config.DockerPrefix)
|
||||||
|
command := strings.Split(fmt.Sprintf("docker network create %s", se.config.DockerPrefix), " ")
|
||||||
|
r, err := exec.Command(command[0], command[1:]...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(string(r))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *Stateth) runInfluxDB() 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", se.config.DockerPrefix))
|
||||||
|
command = strings.Split(fmt.Sprintf("docker run --network %s --name %s_influxdb -e INFLUXDB_DB=metrics -e INFLUXDB_ADMIN_USER=%s -e INFLUXDB_ADMIN_PASSWORD=%s -p %d:8086 -d influxdb:1.5.2", se.config.DockerPrefix, se.config.DockerPrefix, influxDBAdminUser, influxDBAdminPass, se.config.InfluxDBPort), " ")
|
||||||
|
r, err = exec.Command(command[0], command[1:]...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(string(r))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *Stateth) runGrafana() 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", se.config.DockerPrefix))
|
||||||
|
//command = strings.Split(fmt.Sprintf("docker run --network %s --name=%s_grafana -p %d:3000 -d grafana/grafana:5.1.3", se.config.DockerPrefix, se.config.DockerPrefix, se.config.GrafanaPort), " ")
|
||||||
|
command = strings.Split(fmt.Sprintf("docker run --network %s --name=%s_grafana -e GF_AUTH_ANONYMOUS_ENABLED=true -p %d:3000 -d grafana/grafana:5.1.3", se.config.DockerPrefix, se.config.DockerPrefix, se.config.GrafanaPort), " ")
|
||||||
|
r, err = exec.Command(command[0], command[1:]...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(string(r))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *Stateth) cleanupContainers() error {
|
||||||
|
log.Info("removing influxdb container")
|
||||||
|
command := strings.Split(fmt.Sprintf("docker rm -f %s_influxdb", se.config.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", se.config.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", se.config.DockerPrefix), " ")
|
||||||
|
r, err = exec.Command(command[0], command[1:]...).CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(string(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *Stateth) importGrafanaDatasource() error {
|
||||||
|
log.Info("importing grafana datasource")
|
||||||
|
|
||||||
|
dataSource := &gapi.DataSource{
|
||||||
|
Name: "metrics",
|
||||||
|
Type: "influxdb",
|
||||||
|
URL: fmt.Sprintf("http://%s_influxdb:%d", se.config.DockerPrefix, se.config.InfluxDBPort),
|
||||||
|
Access: "proxy",
|
||||||
|
Database: "metrics",
|
||||||
|
User: influxDBAdminUser,
|
||||||
|
Password: influxDBAdminPass,
|
||||||
|
IsDefault: true,
|
||||||
|
BasicAuth: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := se.gclient.NewDataSource(dataSource)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *Stateth) importGrafanaDashboards() error {
|
||||||
|
log.Info("importing grafana dashboards")
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(se.config.DashboardsFolder)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
name := f.Name()
|
||||||
|
if strings.Contains(name, "json") {
|
||||||
|
log.Info("importing dashboard", "dashboard", name)
|
||||||
|
|
||||||
|
blob, err := ioutil.ReadFile(filepath.Join(se.config.DashboardsFolder, name))
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
model := se.prepareDashboardModel(string(blob))
|
||||||
|
|
||||||
|
_, err = se.gclient.SaveDashboard(model, false)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err.Error())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (se *Stateth) 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
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue