Skip to content

Commit

Permalink
Convert configuration and status to YAML.
Browse files Browse the repository at this point in the history
A --check flag is added to the main entrypoint.

Other flags are moved to the config file.

The pool can now provide and interruptable, blocking Wait option.

Connections can be forced away to other backends after a timeout.

Close #4
  • Loading branch information
bobvawter committed Oct 23, 2020
1 parent f68706a commit db2744f
Show file tree
Hide file tree
Showing 27 changed files with 540 additions and 434 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ lint: generate
test: generate
go vet ./...
go test -v -race -coverprofile=coverage.txt ./...
find configs -name *.yaml -print0 | xargs -0 -L1 -t go run github.com/bobvawter/iaido/cmd/iaido --check -c

release: fmt lint test build

84 changes: 51 additions & 33 deletions cmd/iaido/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ import (
"github.com/bobvawter/iaido/pkg/frontend"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"gopkg.in/yaml.v3"
)

func main() {
fs := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
cfgPath := fs.StringP("config", "c", "iaido.json", "the configuration file to load")
cfgReloadPeriod := fs.Duration("reloadPeriod", 30*time.Second, "how often to check the configuration file for changes")
diagAddr := fs.String("diagAddr", "", "an IP:Port pair to bind a diagnostic HTTP server to (e.g. 127.0.0.1:6060)")
gracePeriod := fs.Duration("gracePeriod", 30*time.Second, "how long to wait before forcefully terminating")
cfgPath := fs.StringP("config", "c", "iaido.yaml", "the configuration file to load")
cfgReloadPeriod := fs.Duration("reloadPeriod", time.Second, "how often to check the configuration file for changes")
checkOnly := fs.Bool("check", false, "if true, check and print the configuration file and exit")
version := fs.Bool("version", false, "print the version and exit")
if err := fs.Parse(os.Args[1:]); err != nil {
if err != pflag.ErrHelp {
Expand All @@ -49,40 +49,72 @@ func main() {
os.Exit(0)
}

cfg := &config.Config{}
var mTime time.Time
loadConfig := func() (bool, error) {
info, err := os.Stat(*cfgPath)
if err != nil {
return false, errors.Wrapf(err, "unable to stat config file: %s", *cfgPath)
}
if info.ModTime() == mTime {
return false, nil
}
mTime = info.ModTime()

err = loadConfig(*cfgPath, cfg)
if err != nil {
return false, err
}
return true, nil
}

if _, err := loadConfig(); err != nil {
log.Fatal(err)
}

if *checkOnly {
bytes, err := yaml.Marshal(cfg)
if err != nil {
log.Fatal(err)
}
print(string(bytes))
os.Exit(0)
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Set up a signal handler to gratefully, then forcefully terminate.
{
interrupted := make(chan os.Signal, 1)
signal.Notify(interrupted, os.Interrupt)
signal.Notify(interrupted, syscall.SIGTERM, syscall.SIGINT)
go func() {
<-interrupted
log.Printf("signal received, draining for %s", *gracePeriod)
log.Printf("signal received, draining for %s", cfg.GracePeriod)
cancel()
select {
case <-interrupted:
log.Print("second interrupt received")
case <-time.After(*gracePeriod):
case <-time.After(cfg.GracePeriod):
log.Print("grace period expired")
}
os.Exit(1)
}()
}

fe := &frontend.Frontend{}
if *diagAddr != "" {

if cfg.DiagAddr != "" {
go func() {
elts := map[string]interface{}{
"/configz": cfg,
"/statusz": fe,
"/healthz": "OK",
}
log.Print(diagnostics.ListenAndServe(*diagAddr, elts))
log.Print(diagnostics.ListenAndServe(cfg.DiagAddr, elts))
}()
}

var mTime time.Time

hupped := make(chan os.Signal, 1)
signal.Notify(hupped, syscall.SIGHUP)

Expand All @@ -91,45 +123,31 @@ func main() {

refreshLoop:
for {
if info, err := os.Stat(*cfgPath); err == nil {
if info.ModTime().After(mTime) {
mTime = info.ModTime()

cfg, err := loadConfig(*cfgPath)
if err != nil {
log.Print(errors.Wrap(err, "configuration load failed"))
continue
}

if err := fe.Ensure(ctx, cfg); err != nil {
log.Print(errors.Wrapf(err, "unable to refresh backends"))
continue
}
log.Print("configuration updated")
}
} else {
log.Printf("unable to stat config file: %v", err)
if err := fe.Ensure(ctx, cfg); err != nil {
log.Printf("unable to refresh frontend: %v", err)
}

select {
case <-ctx.Done():
break refreshLoop
case <-ticker.C:
case <-hupped:
}
if _, err := loadConfig(); err != nil {
log.Printf("unable to reload configuration: %v", err)
}
}

fe.Wait()
log.Print("drained, goodbye!")
os.Exit(0)
}

func loadConfig(path string) (*config.Config, error) {
func loadConfig(path string, cfg *config.Config) error {
cfgFile, err := os.Open(path)
if err != nil {
return nil, errors.Wrapf(err, "could not open configuration file %s", path)
return errors.Wrapf(err, "could not open configuration file %s", path)
}
defer cfgFile.Close()

return config.ParseConfig(cfgFile)
return config.DecodeConfig(cfgFile, cfg)
}
2 changes: 1 addition & 1 deletion configs/crdb-local/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ cluster and configure Iaido to act as a proxy for both the SQL and Admin
UI endpoints.

* Execute the `start-cockroach.sh` to initialize a 3-node cluster
* Run `iaido --config iaido.json --diagAddr 127.0.0.1:6060`
* Run `iaido --config iaido.yaml --diagAddr 127.0.0.1:6060`
* Navigate to http://127.0.0.1:6060/statusz to see the Iaido status
* Execute `cockroach workload init tpcc --warehouses 10 --drop`
* Navigate to http://127.0.0.1:8080/#/metrics/sql/cluster to see the SQL
Expand Down
73 changes: 0 additions & 73 deletions configs/crdb-local/iaido.json

This file was deleted.

40 changes: 40 additions & 0 deletions configs/crdb-local/iaido.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# This is a trivial configuration that presents a local CockroachDB
# cluster as a unified endpoint.
diagAddr: 127.0.0.1:6060
frontends:
- backendPool:
tiers:
- dialFailureTimeout: 30s
maxBackendConnections: 32
targets:
- hosts:
- 127.0.0.1
port: 26258
- hosts:
- 127.0.0.1
port: 26259
- hosts:
- 127.0.0.1
port: 26260
bindAddress: 127.0.0.1:26257
idleDuration: 1h
- backendPool:
tiers:
- dialFailureTimeout: 30s
maxBackendConnections: 0
targets:
- hosts:
- 127.0.0.1
port: 8081
- hosts:
- 127.0.0.1
port: 8082
- dialFailureTimeout: 30s
maxBackendConnections: 0
targets:
- hosts:
- 127.0.0.1
port: 8083
bindAddress: 127.0.0.1:8080
idleDuration: 1h
gracePeriod: 30s
92 changes: 0 additions & 92 deletions configs/crdb-local/prod.json

This file was deleted.

Loading

0 comments on commit db2744f

Please sign in to comment.