Skip to content

Commit

Permalink
Merge pull request #22 from Anmol1696/anmol/exposer
Browse files Browse the repository at this point in the history
Anmol/exposer
  • Loading branch information
Anmol1696 authored Nov 28, 2022
2 parents f11e3d2 + e507e80 commit 17ea807
Show file tree
Hide file tree
Showing 15 changed files with 559 additions and 106 deletions.
2 changes: 1 addition & 1 deletion charts/devnet/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.6
version: 0.1.7

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
Expand Down
8 changes: 7 additions & 1 deletion charts/devnet/templates/chain/genesis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,19 @@ spec:
imagePullPolicy: Always
env:
{{- include "devnet.genesisVars" $dataExposer | indent 12}}
- name: GENESIS_FILE
- name: EXPOSER_ADDR
value: ":8081"
- name: EXPOSER_GENESIS_FILE
value: {{ $defaultChain.home }}/config/genesis.json
- name: EXPOSER_MNEMONIC_FILE
value: /configs/keys.json
command: [ "exposer" ]
resources: {{- toYaml $.Values.exposer.resources | nindent 12 }}
volumeMounts:
- mountPath: {{ $defaultChain.home }}
name: node
- mountPath: /configs
name: addresses
volumes:
- name: node
emptyDir: { }
Expand Down
8 changes: 7 additions & 1 deletion charts/devnet/templates/chain/validator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,19 @@ spec:
{{- include "devnet.defaultEvnVars" $defaultChain | indent 12 }}
{{- include "devnet.evnVars" $chain | indent 12 }}
{{- include "devnet.genesisVars" $dataExposer | indent 12}}
- name: GENESIS_FILE
- name: EXPOSER_ADDR
value: ":8081"
- name: EXPOSER_GENESIS_FILE
value: {{ $defaultChain.home }}/config/genesis.json
- name: EXPOSER_MNEMONIC_FILE
value: /configs/keys.json
command: [ "exposer" ]
resources: {{- toYaml $.Values.exposer.resources | nindent 12 }}
volumeMounts:
- mountPath: {{ $defaultChain.home }}
name: node
- mountPath: /configs
name: addresses
volumes:
- name: node
emptyDir: { }
Expand Down
2 changes: 1 addition & 1 deletion examples/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
EXAMPLE = upgrade-testing
EXAMPLE = mesh-security
FILE = custom-values.yaml

HELM_REPO = shuttle
Expand Down
4 changes: 2 additions & 2 deletions exposer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM golang:1.18-alpine AS build-env

# Set up dependencies
ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3
ENV PACKAGES curl make git libc-dev bash gcc linux-headers

# Set working directory for the build
WORKDIR /usr/local/share/app
Expand All @@ -10,7 +10,7 @@ WORKDIR /usr/local/share/app
COPY . .

# Install minimum necessary dependencies, build Cosmos SDK, remove packages
RUN apk add --no-cache $PACKAGES && GO111MODULE=off go build -o exposer ./...
RUN apk add --no-cache $PACKAGES && go build -mod readonly -o exposer ./...

# Final image
FROM alpine:3.16
Expand Down
125 changes: 125 additions & 0 deletions exposer/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package main

import (
"fmt"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/render"
"go.uber.org/zap"
"net/http"
"time"
)

type AppServer struct {
config *Config
logger *zap.Logger
server *http.Server
router http.Handler
}

func NewAppServer(config *Config) (*AppServer, error) {
log, err := NewLogger(config)
if err != nil {
return nil, err
}
log.Info(
"Starting the service",
zap.String("prog", Prog),
zap.String("version", Version),
zap.Any("config", config),
)

app := &AppServer{
config: config,
logger: log,
}

// Setup routes
router, err := app.Router()
if err != nil {
log.Error("Error setting up routes", zap.Error(err))
return nil, err
}
app.router = router

return app, err
}

func (a *AppServer) Router() (*chi.Mux, error) {
router := chi.NewRouter()
router.MethodNotAllowed(MethodNotAllowed)
router.NotFound(NotFound)

// Set middleware
router.Use(a.panicRecovery)
router.Use(render.SetContentType(render.ContentTypeJSON))

// Setup routes
router.Get("/node_id", a.GetNodeID)
router.Get("/pub_key", a.GetPubKey)
router.Get("/genesis", a.GetGenesisFile)
router.Get("/keys", a.GetKeysFile)

return router, nil
}

func (a *AppServer) loggingMiddleware(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
start := time.Now()
defer func() {
a.logger.Info("client request",
zap.Duration("latency", time.Since(start)),
zap.Int("status", ww.Status()),
zap.Int("bytes", ww.BytesWritten()),
zap.String("client_ip", r.RemoteAddr),
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("request-id", middleware.GetReqID(r.Context())))
}()

next.ServeHTTP(ww, r)
}
return http.HandlerFunc(fn)
}

func (a *AppServer) panicRecovery(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
defer func() {
if rc := recover(); rc != nil {
err, ok := rc.(error)
if !ok {
err = fmt.Errorf("panic: %v", rc)
}
a.logger.Error("panic error",
zap.String("request-id", middleware.GetReqID(r.Context())),
zap.Error(err))

render.Render(w, r, ErrInternalServer)
return
}
}()
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}

func (a *AppServer) Run() error {
a.logger.Info("App starting", zap.Any("Config", a.config))

// Setup server
server := &http.Server{
Addr: a.config.Addr,
Handler: a.router,
}
a.server = server

// Start http server as long-running go routine
go func() {
if err := server.ListenAndServe(); err != nil {
a.logger.Error("failed to start the App HTTP server", zap.Error(err))
}
}()

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

import (
"fmt"
"reflect"

"github.com/urfave/cli"
"go.uber.org/zap"
)

func NewDefaultConfig() *Config {
return &Config{
Addr: ":8081",
GenesisFile: "",
MnemonicFile: "",
StatusURL: "http://0.0.0.0:26657/status",
}
}

type Config struct {
// Addr is the interface and port to bind the HTTP service on
Addr string `name:"addr" json:"addr" env:"ADDR" usage:"IP address and port to listen on"`
// GenesisFile is full path to the genesis file
GenesisFile string `name:"genesis-file" json:"genesis_file" env:"GENESIS_FILE" usage:"Path of genesis file"`
// MnemonicFile is full path to the keys file
MnemonicFile string `name:"mnemonic-file" json:"mnemonic_file" env:"MNEMONIC_FILE" usage:"Path of mnemonic file"`
// StatusURL is used to fetch status info from blockchain node
StatusURL string `name:"status-url" json:"status_url" env:"STATUS_URL" usage:"URL to fetch chain status"`
// Verbose switches on debug logging
Verbose bool `name:"verbose" json:"verbose" usage:"switch on debug / verbose logging"`
// OnlyFatalLog set log level as fatal to ignore logs
OnlyFatalLog bool `name:"only-fatal-log" json:"only-fatal-log" usage:"used while running test"`
}

func GetCommandLineOptions() []cli.Flag {
defaults := NewDefaultConfig()
var flags []cli.Flag
count := reflect.TypeOf(Config{}).NumField()
for i := 0; i < count; i++ {
field := reflect.TypeOf(Config{}).Field(i)
usage, found := field.Tag.Lookup("usage")
if !found {
continue
}
envName := field.Tag.Get("env")
if envName != "" {
envName = envPrefix + envName
}
optName := field.Tag.Get("name")

switch t := field.Type; t.Kind() {
case reflect.Bool:
dv := reflect.ValueOf(defaults).Elem().FieldByName(field.Name).Bool()
msg := fmt.Sprintf("%s (default: %t)", usage, dv)
flags = append(flags, cli.BoolTFlag{
Name: optName,
Usage: msg,
EnvVar: envName,
})
case reflect.String:
defaultValue := reflect.ValueOf(defaults).Elem().FieldByName(field.Name).String()
flags = append(flags, cli.StringFlag{
Name: optName,
Usage: usage,
EnvVar: envName,
Value: defaultValue,
})
}
}

return flags
}

func ParseCLIOptions(cx *cli.Context, config *Config) (err error) {
// iterate the Config and grab command line options via reflection
count := reflect.TypeOf(config).Elem().NumField()
for i := 0; i < count; i++ {
field := reflect.TypeOf(config).Elem().Field(i)
name := field.Tag.Get("name")

if cx.IsSet(name) {
switch field.Type.Kind() {
case reflect.Bool:
reflect.ValueOf(config).Elem().FieldByName(field.Name).SetBool(cx.Bool(name))
case reflect.String:
reflect.ValueOf(config).Elem().FieldByName(field.Name).SetString(cx.String(name))
}
}
}
return nil
}

func NewLogger(config *Config) (*zap.Logger, error) {
c := zap.NewProductionConfig()
c.DisableCaller = true
// c.Encoding = "console"

if config.Verbose {
c.DisableCaller = false
c.Development = true
c.DisableStacktrace = true // Disable stack trace for development
c.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
}

if config.OnlyFatalLog {
c.Level = zap.NewAtomicLevelAt(zap.FatalLevel)
}

log, err := c.Build()
if err != nil {
return nil, err
}
zap.ReplaceGlobals(log) // Set zap global logger
return log, err
}
21 changes: 21 additions & 0 deletions exposer/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

var (
Version = "v0"
RequestIdCtxKey = &contextKey{"RequestId"}
)

const (
Prog = "exposer"
Description = "is a sidecar for running cosmos chain nodes for debugging"
envPrefix = "EXPOSER_"
)

// copied and modified from net/http/http.go
// contextKey is a value for use with context.WithValue. It's used as
// a pointer, so it fits in an interface{} without allocation.
type contextKey struct {
name string
}

func (k *contextKey) String() string { return Prog + " context value " + k.name }
Loading

0 comments on commit 17ea807

Please sign in to comment.