Skip to content

Commit 17ea807

Browse files
authored
Merge pull request #22 from Anmol1696/anmol/exposer
Anmol/exposer
2 parents f11e3d2 + e507e80 commit 17ea807

File tree

15 files changed

+559
-106
lines changed

15 files changed

+559
-106
lines changed

charts/devnet/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ type: application
1515
# This is the chart version. This version number should be incremented each time you make changes
1616
# to the chart and its templates, including the app version.
1717
# Versions are expected to follow Semantic Versioning (https://semver.org/)
18-
version: 0.1.6
18+
version: 0.1.7
1919

2020
# This is the version number of the application being deployed. This version number should be
2121
# incremented each time you make changes to the application. Versions are not expected to

charts/devnet/templates/chain/genesis.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,13 +129,19 @@ spec:
129129
imagePullPolicy: Always
130130
env:
131131
{{- include "devnet.genesisVars" $dataExposer | indent 12}}
132-
- name: GENESIS_FILE
132+
- name: EXPOSER_ADDR
133+
value: ":8081"
134+
- name: EXPOSER_GENESIS_FILE
133135
value: {{ $defaultChain.home }}/config/genesis.json
136+
- name: EXPOSER_MNEMONIC_FILE
137+
value: /configs/keys.json
134138
command: [ "exposer" ]
135139
resources: {{- toYaml $.Values.exposer.resources | nindent 12 }}
136140
volumeMounts:
137141
- mountPath: {{ $defaultChain.home }}
138142
name: node
143+
- mountPath: /configs
144+
name: addresses
139145
volumes:
140146
- name: node
141147
emptyDir: { }

charts/devnet/templates/chain/validator.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,19 @@ spec:
173173
{{- include "devnet.defaultEvnVars" $defaultChain | indent 12 }}
174174
{{- include "devnet.evnVars" $chain | indent 12 }}
175175
{{- include "devnet.genesisVars" $dataExposer | indent 12}}
176-
- name: GENESIS_FILE
176+
- name: EXPOSER_ADDR
177+
value: ":8081"
178+
- name: EXPOSER_GENESIS_FILE
177179
value: {{ $defaultChain.home }}/config/genesis.json
180+
- name: EXPOSER_MNEMONIC_FILE
181+
value: /configs/keys.json
178182
command: [ "exposer" ]
179183
resources: {{- toYaml $.Values.exposer.resources | nindent 12 }}
180184
volumeMounts:
181185
- mountPath: {{ $defaultChain.home }}
182186
name: node
187+
- mountPath: /configs
188+
name: addresses
183189
volumes:
184190
- name: node
185191
emptyDir: { }

examples/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
EXAMPLE = upgrade-testing
1+
EXAMPLE = mesh-security
22
FILE = custom-values.yaml
33

44
HELM_REPO = shuttle

exposer/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM golang:1.18-alpine AS build-env
22

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

66
# Set working directory for the build
77
WORKDIR /usr/local/share/app
@@ -10,7 +10,7 @@ WORKDIR /usr/local/share/app
1010
COPY . .
1111

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

1515
# Final image
1616
FROM alpine:3.16

exposer/app.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"github.com/go-chi/chi"
6+
"github.com/go-chi/chi/middleware"
7+
"github.com/go-chi/render"
8+
"go.uber.org/zap"
9+
"net/http"
10+
"time"
11+
)
12+
13+
type AppServer struct {
14+
config *Config
15+
logger *zap.Logger
16+
server *http.Server
17+
router http.Handler
18+
}
19+
20+
func NewAppServer(config *Config) (*AppServer, error) {
21+
log, err := NewLogger(config)
22+
if err != nil {
23+
return nil, err
24+
}
25+
log.Info(
26+
"Starting the service",
27+
zap.String("prog", Prog),
28+
zap.String("version", Version),
29+
zap.Any("config", config),
30+
)
31+
32+
app := &AppServer{
33+
config: config,
34+
logger: log,
35+
}
36+
37+
// Setup routes
38+
router, err := app.Router()
39+
if err != nil {
40+
log.Error("Error setting up routes", zap.Error(err))
41+
return nil, err
42+
}
43+
app.router = router
44+
45+
return app, err
46+
}
47+
48+
func (a *AppServer) Router() (*chi.Mux, error) {
49+
router := chi.NewRouter()
50+
router.MethodNotAllowed(MethodNotAllowed)
51+
router.NotFound(NotFound)
52+
53+
// Set middleware
54+
router.Use(a.panicRecovery)
55+
router.Use(render.SetContentType(render.ContentTypeJSON))
56+
57+
// Setup routes
58+
router.Get("/node_id", a.GetNodeID)
59+
router.Get("/pub_key", a.GetPubKey)
60+
router.Get("/genesis", a.GetGenesisFile)
61+
router.Get("/keys", a.GetKeysFile)
62+
63+
return router, nil
64+
}
65+
66+
func (a *AppServer) loggingMiddleware(next http.Handler) http.Handler {
67+
fn := func(w http.ResponseWriter, r *http.Request) {
68+
ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
69+
start := time.Now()
70+
defer func() {
71+
a.logger.Info("client request",
72+
zap.Duration("latency", time.Since(start)),
73+
zap.Int("status", ww.Status()),
74+
zap.Int("bytes", ww.BytesWritten()),
75+
zap.String("client_ip", r.RemoteAddr),
76+
zap.String("method", r.Method),
77+
zap.String("path", r.URL.Path),
78+
zap.String("request-id", middleware.GetReqID(r.Context())))
79+
}()
80+
81+
next.ServeHTTP(ww, r)
82+
}
83+
return http.HandlerFunc(fn)
84+
}
85+
86+
func (a *AppServer) panicRecovery(next http.Handler) http.Handler {
87+
fn := func(w http.ResponseWriter, r *http.Request) {
88+
defer func() {
89+
if rc := recover(); rc != nil {
90+
err, ok := rc.(error)
91+
if !ok {
92+
err = fmt.Errorf("panic: %v", rc)
93+
}
94+
a.logger.Error("panic error",
95+
zap.String("request-id", middleware.GetReqID(r.Context())),
96+
zap.Error(err))
97+
98+
render.Render(w, r, ErrInternalServer)
99+
return
100+
}
101+
}()
102+
next.ServeHTTP(w, r)
103+
}
104+
return http.HandlerFunc(fn)
105+
}
106+
107+
func (a *AppServer) Run() error {
108+
a.logger.Info("App starting", zap.Any("Config", a.config))
109+
110+
// Setup server
111+
server := &http.Server{
112+
Addr: a.config.Addr,
113+
Handler: a.router,
114+
}
115+
a.server = server
116+
117+
// Start http server as long-running go routine
118+
go func() {
119+
if err := server.ListenAndServe(); err != nil {
120+
a.logger.Error("failed to start the App HTTP server", zap.Error(err))
121+
}
122+
}()
123+
124+
return nil
125+
}

exposer/config.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
7+
"github.com/urfave/cli"
8+
"go.uber.org/zap"
9+
)
10+
11+
func NewDefaultConfig() *Config {
12+
return &Config{
13+
Addr: ":8081",
14+
GenesisFile: "",
15+
MnemonicFile: "",
16+
StatusURL: "http://0.0.0.0:26657/status",
17+
}
18+
}
19+
20+
type Config struct {
21+
// Addr is the interface and port to bind the HTTP service on
22+
Addr string `name:"addr" json:"addr" env:"ADDR" usage:"IP address and port to listen on"`
23+
// GenesisFile is full path to the genesis file
24+
GenesisFile string `name:"genesis-file" json:"genesis_file" env:"GENESIS_FILE" usage:"Path of genesis file"`
25+
// MnemonicFile is full path to the keys file
26+
MnemonicFile string `name:"mnemonic-file" json:"mnemonic_file" env:"MNEMONIC_FILE" usage:"Path of mnemonic file"`
27+
// StatusURL is used to fetch status info from blockchain node
28+
StatusURL string `name:"status-url" json:"status_url" env:"STATUS_URL" usage:"URL to fetch chain status"`
29+
// Verbose switches on debug logging
30+
Verbose bool `name:"verbose" json:"verbose" usage:"switch on debug / verbose logging"`
31+
// OnlyFatalLog set log level as fatal to ignore logs
32+
OnlyFatalLog bool `name:"only-fatal-log" json:"only-fatal-log" usage:"used while running test"`
33+
}
34+
35+
func GetCommandLineOptions() []cli.Flag {
36+
defaults := NewDefaultConfig()
37+
var flags []cli.Flag
38+
count := reflect.TypeOf(Config{}).NumField()
39+
for i := 0; i < count; i++ {
40+
field := reflect.TypeOf(Config{}).Field(i)
41+
usage, found := field.Tag.Lookup("usage")
42+
if !found {
43+
continue
44+
}
45+
envName := field.Tag.Get("env")
46+
if envName != "" {
47+
envName = envPrefix + envName
48+
}
49+
optName := field.Tag.Get("name")
50+
51+
switch t := field.Type; t.Kind() {
52+
case reflect.Bool:
53+
dv := reflect.ValueOf(defaults).Elem().FieldByName(field.Name).Bool()
54+
msg := fmt.Sprintf("%s (default: %t)", usage, dv)
55+
flags = append(flags, cli.BoolTFlag{
56+
Name: optName,
57+
Usage: msg,
58+
EnvVar: envName,
59+
})
60+
case reflect.String:
61+
defaultValue := reflect.ValueOf(defaults).Elem().FieldByName(field.Name).String()
62+
flags = append(flags, cli.StringFlag{
63+
Name: optName,
64+
Usage: usage,
65+
EnvVar: envName,
66+
Value: defaultValue,
67+
})
68+
}
69+
}
70+
71+
return flags
72+
}
73+
74+
func ParseCLIOptions(cx *cli.Context, config *Config) (err error) {
75+
// iterate the Config and grab command line options via reflection
76+
count := reflect.TypeOf(config).Elem().NumField()
77+
for i := 0; i < count; i++ {
78+
field := reflect.TypeOf(config).Elem().Field(i)
79+
name := field.Tag.Get("name")
80+
81+
if cx.IsSet(name) {
82+
switch field.Type.Kind() {
83+
case reflect.Bool:
84+
reflect.ValueOf(config).Elem().FieldByName(field.Name).SetBool(cx.Bool(name))
85+
case reflect.String:
86+
reflect.ValueOf(config).Elem().FieldByName(field.Name).SetString(cx.String(name))
87+
}
88+
}
89+
}
90+
return nil
91+
}
92+
93+
func NewLogger(config *Config) (*zap.Logger, error) {
94+
c := zap.NewProductionConfig()
95+
c.DisableCaller = true
96+
// c.Encoding = "console"
97+
98+
if config.Verbose {
99+
c.DisableCaller = false
100+
c.Development = true
101+
c.DisableStacktrace = true // Disable stack trace for development
102+
c.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
103+
}
104+
105+
if config.OnlyFatalLog {
106+
c.Level = zap.NewAtomicLevelAt(zap.FatalLevel)
107+
}
108+
109+
log, err := c.Build()
110+
if err != nil {
111+
return nil, err
112+
}
113+
zap.ReplaceGlobals(log) // Set zap global logger
114+
return log, err
115+
}

exposer/const.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package main
2+
3+
var (
4+
Version = "v0"
5+
RequestIdCtxKey = &contextKey{"RequestId"}
6+
)
7+
8+
const (
9+
Prog = "exposer"
10+
Description = "is a sidecar for running cosmos chain nodes for debugging"
11+
envPrefix = "EXPOSER_"
12+
)
13+
14+
// copied and modified from net/http/http.go
15+
// contextKey is a value for use with context.WithValue. It's used as
16+
// a pointer, so it fits in an interface{} without allocation.
17+
type contextKey struct {
18+
name string
19+
}
20+
21+
func (k *contextKey) String() string { return Prog + " context value " + k.name }

0 commit comments

Comments
 (0)