Skip to content
This repository has been archived by the owner on May 11, 2022. It is now read-only.

Commit

Permalink
fix #136 and #138: define config structs
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilsk committed Jul 20, 2018
1 parent ca8b753 commit fba4568
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 21 deletions.
2 changes: 1 addition & 1 deletion cmd/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var completionCmd = &cobra.Command{
Short: "Print Bash or Zsh completion",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("one argument is required, received %d arg(s)", len(args))
return fmt.Errorf("please provide only %q or %q as an argument", bashFormat, zshFormat)
}
if args[0] != bashFormat && args[0] != zshFormat {
return fmt.Errorf("only %q and %q formats are supported, received %q", bashFormat, zshFormat, args[0])
Expand Down
1 change: 1 addition & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package cmd
59 changes: 39 additions & 20 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

pb "github.com/kamilsk/form-api/pkg/server/grpc"

"github.com/kamilsk/form-api/pkg/config"
"github.com/kamilsk/form-api/pkg/dao"
"github.com/kamilsk/form-api/pkg/server"
"github.com/kamilsk/form-api/pkg/server/router/chi"
Expand All @@ -26,22 +27,28 @@ var runCmd = &cobra.Command{
Short: "Start HTTP server",
RunE: func(cmd *cobra.Command, args []string) error {
runtime.GOMAXPROCS(asInt(cmd.Flag("cpus").Value))
addr := cmd.Flag("bind").Value.String() + ":" + cmd.Flag("port").Value.String()

if err := startGRPC(":8092"); err != nil {
if err := startGRPCServer(config.GRPCConfig{Interface: ":8092"}); err != nil {
return err
}
if asBool(cmd.Flag("with-monitoring").Value) {
if err := startMonitoring(":8091"); err != nil {
if err := startMonitoring(config.MonitoringConfig{Interface: ":8091"}); err != nil {
return err
}
}
if asBool(cmd.Flag("with-profiler").Value) {
if err := startProfiler(":8090"); err != nil {
if err := startProfiler(config.ProfilerConfig{Interface: ":8090"}); err != nil {
return err
}
}

cnf := config.ServerConfig{
Interface: cmd.Flag("bind").Value.String() + ":" + cmd.Flag("port").Value.String(),
ReadTimeout: asDuration(cmd.Flag("read-timeout").Value),
ReadHeaderTimeout: asDuration(cmd.Flag("read-header-timeout").Value),
WriteTimeout: asDuration(cmd.Flag("write-timeout").Value),
IdleTimeout: asDuration(cmd.Flag("idle-timeout").Value),
}
handler := chi.NewRouter(
server.New(
cmd.Flag("base-url").Value.String(),
Expand All @@ -51,13 +58,7 @@ var runCmd = &cobra.Command{
),
),
)
srv := &http.Server{Addr: addr, Handler: handler,
ReadTimeout: asDuration(cmd.Flag("read-timeout").Value),
ReadHeaderTimeout: asDuration(cmd.Flag("read-header-timeout").Value),
WriteTimeout: asDuration(cmd.Flag("write-timeout").Value),
IdleTimeout: asDuration(cmd.Flag("idle-timeout").Value)}
log.Println("starting server at", addr)
return srv.ListenAndServe()
return startHTTPServer(cnf, handler)
},
}

Expand Down Expand Up @@ -101,9 +102,9 @@ func init() {
runCmd.Flags().Duration("idle-timeout", v.GetDuration("idle_timeout"),
"maximum amount of time to wait for the next request when keep-alive is enabled")
runCmd.Flags().Bool("with-profiler", false,
"enable pprof on /pprof")
"enable pprof on /pprof/* and /debug/pprof/")
runCmd.Flags().Bool("with-monitoring", false,
"enable expvar on /vars")
"enable prometheus on /monitoring and expvar on /vars")
runCmd.Flags().String("base-url", v.GetString("base_url"),
"hostname (and path) to the root")
runCmd.Flags().String("tpl-dir", v.GetString("template_dir"),
Expand All @@ -112,8 +113,23 @@ func init() {
db(runCmd)
}

func startGRPC(address string) error {
listener, err := net.Listen("tcp", address)
func startHTTPServer(cnf config.ServerConfig, handler http.Handler) error {
listener, err := net.Listen("tcp", cnf.Interface)
if err != nil {
return err
}
srv := &http.Server{Addr: cnf.Interface, Handler: handler,
ReadTimeout: cnf.ReadTimeout,
ReadHeaderTimeout: cnf.ReadHeaderTimeout,
WriteTimeout: cnf.WriteTimeout,
IdleTimeout: cnf.IdleTimeout,
}
log.Println("start HTTP server at", listener.Addr())
return srv.Serve(listener)
}

func startGRPCServer(cnf config.GRPCConfig) error {
listener, err := net.Listen("tcp", cnf.Interface)
if err != nil {
return err
}
Expand All @@ -123,14 +139,15 @@ func startGRPC(address string) error {
pb.RegisterTemplateServer(srv, pb.NewTemplateServer())
pb.RegisterInputServer(srv, pb.NewInputServer())
pb.RegisterLogServer(srv, pb.NewLogServer())
log.Println("start gRPC at", listener.Addr())
log.Println("start gRPC server at", listener.Addr())
_ = srv.Serve(listener) // TODO issue#139
listener.Close()
}()
return nil
}

func startMonitoring(address string) error {
listener, err := net.Listen("tcp", address)
func startMonitoring(cnf config.MonitoringConfig) error {
listener, err := net.Listen("tcp", cnf.Interface)
if err != nil {
return err
}
Expand All @@ -140,12 +157,13 @@ func startMonitoring(address string) error {
mux.Handle("/vars", expvar.Handler())
log.Println("start monitor at", listener.Addr())
_ = http.Serve(listener, mux) // TODO issue#139
listener.Close()
}()
return nil
}

func startProfiler(address string) error {
listener, err := net.Listen("tcp", address)
func startProfiler(cnf config.ProfilerConfig) error {
listener, err := net.Listen("tcp", cnf.Interface)
if err != nil {
return err
}
Expand All @@ -158,6 +176,7 @@ func startProfiler(address string) error {
mux.HandleFunc("/debug/pprof/", pprof.Index) // net/http/pprof.handler.ServeHTTP specificity
log.Println("start profiler at", listener.Addr())
_ = http.Serve(listener, mux) // TODO issue#139
listener.Close()
}()
return nil
}
71 changes: 71 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package config

import (
"net/url"
"time"
)

// ApplicationConfig holds all configurations of the application.
type ApplicationConfig struct {
Union struct {
DBConfig `yaml:"db"`
GRPCConfig `yaml:"grpc"`
MigrationConfig `yaml:"migration"`
MonitoringConfig `yaml:"monitoring"`
ProfilerConfig `yaml:"profiler"`
ServerConfig `yaml:"server"`
} `yaml:"config"`
}

// DBConfig contains configuration related to database.
type DBConfig struct {
DSN string `yaml:"dsn"`

dsn *url.URL
}

// DriverName returns database driver name.
func (cnf *DBConfig) DriverName() string {
if cnf.dsn == nil {
cnf.dsn, _ = url.Parse(cnf.DSN)
}
return cnf.dsn.Scheme
}

// GRPCConfig contains configuration related to gRPC server.
type GRPCConfig struct {
Interface string `yaml:"interface"`
}

// MigrationConfig contains configuration related to migrations.
type MigrationConfig struct {
Table string `yaml:"table"`
Schema string `yaml:"schema"`
Limit uint `yaml:"limit"`
DryRun bool `yaml:"dry-run"`
WithDemo bool `yaml:"with-demo"`
}

// MonitoringConfig contains configuration related to monitoring.
type MonitoringConfig struct {
Enabled bool `yaml:"enabled"`
Interface string `yaml:"interface"`
}

// ProfilerConfig contains configuration related to profiler.
type ProfilerConfig struct {
Enabled bool `yaml:"enabled"`
Interface string `yaml:"interface"`
}

// ServerConfig contains configuration related to the Forma server.
type ServerConfig struct {
Interface string `yaml:"interface"`
CPUCount uint `yaml:"cpus"`
ReadTimeout time.Duration `yaml:"read-timeout"`
ReadHeaderTimeout time.Duration `yaml:"read-header-timeout"`
WriteTimeout time.Duration `yaml:"write-timeout"`
IdleTimeout time.Duration `yaml:"idle-timeout"`
BaseURL string `yaml:"base-url"`
TemplateDir string `yaml:"tpl-dir"`
}
47 changes: 47 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package config_test

import (
"flag"
"io/ioutil"
"os"
"testing"

"github.com/kamilsk/form-api/pkg/config"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v2"
)

var update = flag.Bool("update", false, "update .golden files")

func TestYAMLSerialization(t *testing.T) {
testCases := []struct {
name string
in string
out string
}{
{"simple configuration", "fixtures/simple.yml", "fixtures/simple.yml.golden"},
}

for _, test := range testCases {
tc := test
t.Run(test.name, func(t *testing.T) {
raw, err := ioutil.ReadFile(tc.in)
assert.NoError(t, err)

var cnf config.ApplicationConfig
err = yaml.UnmarshalStrict(raw, &cnf)
assert.NoError(t, err)

actual, err := yaml.Marshal(cnf)
assert.NoError(t, err)

if *update {
err = ioutil.WriteFile(tc.out, actual, os.ModePerm)
assert.NoError(t, err)
}
expected, err := ioutil.ReadFile(tc.out)
assert.NoError(t, err)
assert.Equal(t, expected, actual)
})
}
}
33 changes: 33 additions & 0 deletions pkg/config/fixtures/simple.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
config:

db:
dsn: postgres://user:pass@localhost:5432/postgres?connect_timeout=10&sslmode=disable

grpc:
interface: 127.0.0.1:8092

migration:
table: migration
schema: public
limit: 0
dry-run: false
with-demo: false

monitoring:
enabled: true
interface: 127.0.0.1:8091

profiler:
enabled: true
interface: 127.0.0.1:8090

server:
interface: 127.0.0.1:8080
cpus: 1
read-timeout: 1s
read-header-timeout: 1s
write-timeout: 1s
idle-timeout: 1s
base-url: http://localhost/
tpl-dir: ~
26 changes: 26 additions & 0 deletions pkg/config/fixtures/simple.yml.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
config:
db:
dsn: postgres://user:pass@localhost:5432/postgres?connect_timeout=10&sslmode=disable
grpc:
interface: 127.0.0.1:8092
migration:
table: migration
schema: public
limit: 0
dry-run: false
with-demo: false
monitoring:
enabled: true
interface: 127.0.0.1:8091
profiler:
enabled: true
interface: 127.0.0.1:8090
server:
interface: 127.0.0.1:8080
cpus: 1
read-timeout: 1s
read-header-timeout: 1s
write-timeout: 1s
idle-timeout: 1s
base-url: http://localhost/
tpl-dir: ""

0 comments on commit fba4568

Please sign in to comment.