Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setup App object for configuration #419

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 24 additions & 10 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/hlog"

"github.com/Shopify/toxiproxy/v2/app"
"github.com/Shopify/toxiproxy/v2/collectors"
"github.com/Shopify/toxiproxy/v2/toxics"
)

Expand All @@ -31,8 +33,9 @@ func timeoutMiddleware(next http.Handler) http.Handler {
}

type ApiServer struct {
app *app.App
Collection *ProxyCollection
Metrics *metricsContainer
Metrics *collectors.MetricsContainer
Logger *zerolog.Logger
http *http.Server
}
Expand All @@ -42,22 +45,29 @@ const (
read_timeout = 15 * time.Second
)

func NewServer(m *metricsContainer, logger zerolog.Logger) *ApiServer {
return &ApiServer{
func NewServer(app *app.App) *ApiServer {
server := ApiServer{
app: app,
Collection: NewProxyCollection(),
Metrics: m,
Logger: &logger,
Metrics: app.Metrics,
Logger: app.Logger,
}

if len(app.Config) > 0 {
server.PopulateConfig(app.Config)
}

return &server
}

func (server *ApiServer) Listen(addr string) error {
func (server *ApiServer) Listen() error {
server.Logger.
Info().
Str("address", addr).
Str("address", server.app.Addr).
Msg("Starting Toxiproxy HTTP server")

server.http = &http.Server{
Addr: addr,
Addr: server.app.Addr,
Handler: server.Routes(),
WriteTimeout: wait_timeout,
ReadTimeout: read_timeout,
Expand All @@ -73,6 +83,10 @@ func (server *ApiServer) Listen(addr string) error {
}

func (server *ApiServer) Shutdown() error {
server.Logger.
Info().
Msg("Shutdown started")

if server.http == nil {
return nil
}
Expand Down Expand Up @@ -136,8 +150,8 @@ func (server *ApiServer) Routes() *mux.Router {

r.HandleFunc("/version", server.Version).Methods("GET").Name("Version")

if server.Metrics.anyMetricsEnabled() {
r.Handle("/metrics", server.Metrics.handler()).Name("Metrics")
if server.Metrics.AnyMetricsEnabled() {
r.Handle("/metrics", server.Metrics.Handler()).Name("Metrics")
}

return r
Expand Down
25 changes: 14 additions & 11 deletions api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,35 @@ import (
"testing"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/rs/zerolog"

"github.com/Shopify/toxiproxy/v2"
"github.com/Shopify/toxiproxy/v2/app"
tclient "github.com/Shopify/toxiproxy/v2/client"
"github.com/Shopify/toxiproxy/v2/collectors"
)

var testServer *toxiproxy.ApiServer

var client = tclient.NewClient("http://127.0.0.1:8475")

func WithServer(t *testing.T, f func(string)) {
log := zerolog.Nop()
if flag.Lookup("test.v").DefValue == "true" {
log = zerolog.New(os.Stdout).With().Caller().Timestamp().Logger()
}

// Make sure only one server is running at a time. Apparently there's no clean
// way to shut it down between each test run.
if testServer == nil {
testServer = toxiproxy.NewServer(
toxiproxy.NewMetricsContainer(prometheus.NewRegistry()),
log,
)
log := zerolog.Nop()
if flag.Lookup("test.v").DefValue == "true" {
log = zerolog.New(os.Stdout).With().Caller().Timestamp().Logger()
}

a := app.App{
Addr: "localhost:8475",
Logger: &log,
Metrics: &collectors.MetricsContainer{},
}
testServer = toxiproxy.NewServer(&a)

go testServer.Listen("localhost:8475")
go testServer.Listen()

// Allow server to start. There's no clean way to know when it listens.
time.Sleep(50 * time.Millisecond)
Expand Down
69 changes: 69 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package app

import (
"fmt"
"math/rand"
"net"

"github.com/rs/zerolog"

"github.com/Shopify/toxiproxy/v2/collectors"
)

type ServerOptions struct {
Host string
Port string
Config string
Seed int64
PrintVersion bool
ProxyMetrics bool
RuntimeMetrics bool
}

// App is used for keep central location of configuration and resources.
type App struct {
Addr string
Logger *zerolog.Logger
Metrics *collectors.MetricsContainer
Config string
EnabledProxyMetrics bool
EnabledRuntimeMetrics bool
}

// NewApp initialize App instance.
func NewApp(options ServerOptions) (*App, error) {
rand.Seed(options.Seed)

app := &App{
Addr: net.JoinHostPort(options.Host, options.Port),
Config: options.Config,
EnabledProxyMetrics: options.ProxyMetrics,
EnabledRuntimeMetrics: options.RuntimeMetrics,
}

start([]unit{
{"Logger", app.setLogger},
{"Metrics", app.setMetrics},
})

return app, nil
}

// unit keeps initialization tasks.
// could be used later for graceful stop function per service if it is required.
type unit struct {
name string
start func() error
}

// start run initialized step for resource.
// could be wrapped with debug information and resource usage.
func start(units []unit) error {
for _, unit := range units {
err := unit.start()
if err != nil {
return fmt.Errorf("initialization %s failed: %w", unit.name, err)
}
}
return nil
}
49 changes: 49 additions & 0 deletions app/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package app

import (
"os"
"strconv"
"time"

"github.com/rs/zerolog"
)

func init() {
zerolog.TimestampFunc = func() time.Time {
return time.Now().UTC()
}

zerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string {
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
return file + ":" + strconv.Itoa(line)
}
}

func (a *App) setLogger() error {
logger := zerolog.New(os.Stdout).With().Caller().Timestamp().Logger()
defer func(a *App, logger zerolog.Logger) {
a.Logger = &logger
}(a, logger)

val, ok := os.LookupEnv("LOG_LEVEL")
if !ok {
return nil
}

lvl, err := zerolog.ParseLevel(val)
if err == nil {
logger = logger.Level(lvl)
} else {
l := &logger
l.Err(err).Msgf("unknown LOG_LEVEL value: \"%s\"", val)
}

return nil
}
17 changes: 17 additions & 0 deletions app/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package app

import (
"github.com/Shopify/toxiproxy/v2/collectors"
)

func (a *App) setMetrics() error {
metrics := collectors.NewMetricsContainer()
if a.EnabledProxyMetrics {
metrics.ProxyMetrics = collectors.NewProxyMetricCollectors()
}
if a.EnabledRuntimeMetrics {
metrics.RuntimeMetrics = collectors.NewRuntimeMetricCollectors()
}
a.Metrics = metrics
return nil
}
Loading