2017-03-31 18:01:58 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"sync"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
2017-05-18 18:54:23 +02:00
|
|
|
"golang.org/x/net/context"
|
2017-03-31 18:01:58 +02:00
|
|
|
|
|
|
|
"github.com/go-kit/kit/log"
|
|
|
|
kitprometheus "github.com/go-kit/kit/metrics/prometheus"
|
|
|
|
|
|
|
|
"github.com/go-kit/kit/examples/shipping/booking"
|
|
|
|
"github.com/go-kit/kit/examples/shipping/cargo"
|
|
|
|
"github.com/go-kit/kit/examples/shipping/handling"
|
|
|
|
"github.com/go-kit/kit/examples/shipping/inmem"
|
|
|
|
"github.com/go-kit/kit/examples/shipping/inspection"
|
|
|
|
"github.com/go-kit/kit/examples/shipping/location"
|
|
|
|
"github.com/go-kit/kit/examples/shipping/routing"
|
|
|
|
"github.com/go-kit/kit/examples/shipping/tracking"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
defaultPort = "8080"
|
|
|
|
defaultRoutingServiceURL = "http://localhost:7878"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var (
|
|
|
|
addr = envString("PORT", defaultPort)
|
|
|
|
rsurl = envString("ROUTINGSERVICE_URL", defaultRoutingServiceURL)
|
|
|
|
|
|
|
|
httpAddr = flag.String("http.addr", ":"+addr, "HTTP listen address")
|
|
|
|
routingServiceURL = flag.String("service.routing", rsurl, "routing service URL")
|
|
|
|
|
|
|
|
ctx = context.Background()
|
|
|
|
)
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
var logger log.Logger
|
|
|
|
logger = log.NewLogfmtLogger(os.Stderr)
|
|
|
|
logger = &serializedLogger{Logger: logger}
|
2017-05-18 18:54:23 +02:00
|
|
|
logger = log.NewContext(logger).With("ts", log.DefaultTimestampUTC)
|
2017-03-31 18:01:58 +02:00
|
|
|
|
|
|
|
var (
|
|
|
|
cargos = inmem.NewCargoRepository()
|
|
|
|
locations = inmem.NewLocationRepository()
|
|
|
|
voyages = inmem.NewVoyageRepository()
|
|
|
|
handlingEvents = inmem.NewHandlingEventRepository()
|
|
|
|
)
|
|
|
|
|
|
|
|
// Configure some questionable dependencies.
|
|
|
|
var (
|
|
|
|
handlingEventFactory = cargo.HandlingEventFactory{
|
|
|
|
CargoRepository: cargos,
|
|
|
|
VoyageRepository: voyages,
|
|
|
|
LocationRepository: locations,
|
|
|
|
}
|
|
|
|
handlingEventHandler = handling.NewEventHandler(
|
|
|
|
inspection.NewService(cargos, handlingEvents, nil),
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
// Facilitate testing by adding some cargos.
|
|
|
|
storeTestData(cargos)
|
|
|
|
|
|
|
|
fieldKeys := []string{"method"}
|
|
|
|
|
|
|
|
var rs routing.Service
|
|
|
|
rs = routing.NewProxyingMiddleware(ctx, *routingServiceURL)(rs)
|
|
|
|
|
|
|
|
var bs booking.Service
|
|
|
|
bs = booking.NewService(cargos, locations, handlingEvents, rs)
|
2017-05-18 18:54:23 +02:00
|
|
|
bs = booking.NewLoggingService(log.NewContext(logger).With("component", "booking"), bs)
|
2017-03-31 18:01:58 +02:00
|
|
|
bs = booking.NewInstrumentingService(
|
|
|
|
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
|
|
|
Namespace: "api",
|
|
|
|
Subsystem: "booking_service",
|
|
|
|
Name: "request_count",
|
|
|
|
Help: "Number of requests received.",
|
|
|
|
}, fieldKeys),
|
|
|
|
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
|
|
|
Namespace: "api",
|
|
|
|
Subsystem: "booking_service",
|
|
|
|
Name: "request_latency_microseconds",
|
|
|
|
Help: "Total duration of requests in microseconds.",
|
|
|
|
}, fieldKeys),
|
|
|
|
bs,
|
|
|
|
)
|
|
|
|
|
|
|
|
var ts tracking.Service
|
|
|
|
ts = tracking.NewService(cargos, handlingEvents)
|
2017-05-18 18:54:23 +02:00
|
|
|
ts = tracking.NewLoggingService(log.NewContext(logger).With("component", "tracking"), ts)
|
2017-03-31 18:01:58 +02:00
|
|
|
ts = tracking.NewInstrumentingService(
|
|
|
|
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
|
|
|
Namespace: "api",
|
|
|
|
Subsystem: "tracking_service",
|
|
|
|
Name: "request_count",
|
|
|
|
Help: "Number of requests received.",
|
|
|
|
}, fieldKeys),
|
|
|
|
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
|
|
|
Namespace: "api",
|
|
|
|
Subsystem: "tracking_service",
|
|
|
|
Name: "request_latency_microseconds",
|
|
|
|
Help: "Total duration of requests in microseconds.",
|
|
|
|
}, fieldKeys),
|
|
|
|
ts,
|
|
|
|
)
|
|
|
|
|
|
|
|
var hs handling.Service
|
|
|
|
hs = handling.NewService(handlingEvents, handlingEventFactory, handlingEventHandler)
|
2017-05-18 18:54:23 +02:00
|
|
|
hs = handling.NewLoggingService(log.NewContext(logger).With("component", "handling"), hs)
|
2017-03-31 18:01:58 +02:00
|
|
|
hs = handling.NewInstrumentingService(
|
|
|
|
kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
|
|
|
Namespace: "api",
|
|
|
|
Subsystem: "handling_service",
|
|
|
|
Name: "request_count",
|
|
|
|
Help: "Number of requests received.",
|
|
|
|
}, fieldKeys),
|
|
|
|
kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
|
|
|
|
Namespace: "api",
|
|
|
|
Subsystem: "handling_service",
|
|
|
|
Name: "request_latency_microseconds",
|
|
|
|
Help: "Total duration of requests in microseconds.",
|
|
|
|
}, fieldKeys),
|
|
|
|
hs,
|
|
|
|
)
|
|
|
|
|
2017-05-18 18:54:23 +02:00
|
|
|
httpLogger := log.NewContext(logger).With("component", "http")
|
2017-03-31 18:01:58 +02:00
|
|
|
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
2017-05-18 18:54:23 +02:00
|
|
|
mux.Handle("/booking/v1/", booking.MakeHandler(ctx, bs, httpLogger))
|
|
|
|
mux.Handle("/tracking/v1/", tracking.MakeHandler(ctx, ts, httpLogger))
|
|
|
|
mux.Handle("/handling/v1/", handling.MakeHandler(ctx, hs, httpLogger))
|
2017-03-31 18:01:58 +02:00
|
|
|
|
|
|
|
http.Handle("/", accessControl(mux))
|
|
|
|
http.Handle("/metrics", stdprometheus.Handler())
|
|
|
|
|
|
|
|
errs := make(chan error, 2)
|
|
|
|
go func() {
|
|
|
|
logger.Log("transport", "http", "address", *httpAddr, "msg", "listening")
|
|
|
|
errs <- http.ListenAndServe(*httpAddr, nil)
|
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
c := make(chan os.Signal)
|
|
|
|
signal.Notify(c, syscall.SIGINT)
|
|
|
|
errs <- fmt.Errorf("%s", <-c)
|
|
|
|
}()
|
|
|
|
|
|
|
|
logger.Log("terminated", <-errs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func accessControl(h http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
|
|
|
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
|
|
|
w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")
|
|
|
|
|
|
|
|
if r.Method == "OPTIONS" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
h.ServeHTTP(w, r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func envString(env, fallback string) string {
|
|
|
|
e := os.Getenv(env)
|
|
|
|
if e == "" {
|
|
|
|
return fallback
|
|
|
|
}
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func storeTestData(r cargo.Repository) {
|
|
|
|
test1 := cargo.New("FTL456", cargo.RouteSpecification{
|
|
|
|
Origin: location.AUMEL,
|
|
|
|
Destination: location.SESTO,
|
|
|
|
ArrivalDeadline: time.Now().AddDate(0, 0, 7),
|
|
|
|
})
|
|
|
|
if err := r.Store(test1); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
test2 := cargo.New("ABC123", cargo.RouteSpecification{
|
|
|
|
Origin: location.SESTO,
|
|
|
|
Destination: location.CNHKG,
|
|
|
|
ArrivalDeadline: time.Now().AddDate(0, 0, 14),
|
|
|
|
})
|
|
|
|
if err := r.Store(test2); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type serializedLogger struct {
|
|
|
|
mtx sync.Mutex
|
|
|
|
log.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *serializedLogger) Log(keyvals ...interface{}) error {
|
|
|
|
l.mtx.Lock()
|
|
|
|
defer l.mtx.Unlock()
|
|
|
|
return l.Logger.Log(keyvals...)
|
|
|
|
}
|