1package main 2 3import ( 4 "context" 5 "flag" 6 "fmt" 7 "net/http" 8 "os" 9 "os/signal" 10 "syscall" 11 "time" 12 13 stdprometheus "github.com/prometheus/client_golang/prometheus" 14 "github.com/prometheus/client_golang/prometheus/promhttp" 15 16 "github.com/go-kit/kit/log" 17 kitprometheus "github.com/go-kit/kit/metrics/prometheus" 18 19 "github.com/go-kit/kit/examples/shipping/booking" 20 "github.com/go-kit/kit/examples/shipping/cargo" 21 "github.com/go-kit/kit/examples/shipping/handling" 22 "github.com/go-kit/kit/examples/shipping/inmem" 23 "github.com/go-kit/kit/examples/shipping/inspection" 24 "github.com/go-kit/kit/examples/shipping/location" 25 "github.com/go-kit/kit/examples/shipping/routing" 26 "github.com/go-kit/kit/examples/shipping/tracking" 27) 28 29const ( 30 defaultPort = "8080" 31 defaultRoutingServiceURL = "http://localhost:7878" 32) 33 34func main() { 35 var ( 36 addr = envString("PORT", defaultPort) 37 rsurl = envString("ROUTINGSERVICE_URL", defaultRoutingServiceURL) 38 39 httpAddr = flag.String("http.addr", ":"+addr, "HTTP listen address") 40 routingServiceURL = flag.String("service.routing", rsurl, "routing service URL") 41 42 ctx = context.Background() 43 ) 44 45 flag.Parse() 46 47 var logger log.Logger 48 logger = log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)) 49 logger = log.With(logger, "ts", log.DefaultTimestampUTC) 50 51 var ( 52 cargos = inmem.NewCargoRepository() 53 locations = inmem.NewLocationRepository() 54 voyages = inmem.NewVoyageRepository() 55 handlingEvents = inmem.NewHandlingEventRepository() 56 ) 57 58 // Configure some questionable dependencies. 59 var ( 60 handlingEventFactory = cargo.HandlingEventFactory{ 61 CargoRepository: cargos, 62 VoyageRepository: voyages, 63 LocationRepository: locations, 64 } 65 handlingEventHandler = handling.NewEventHandler( 66 inspection.NewService(cargos, handlingEvents, nil), 67 ) 68 ) 69 70 // Facilitate testing by adding some cargos. 71 storeTestData(cargos) 72 73 fieldKeys := []string{"method"} 74 75 var rs routing.Service 76 rs = routing.NewProxyingMiddleware(ctx, *routingServiceURL)(rs) 77 78 var bs booking.Service 79 bs = booking.NewService(cargos, locations, handlingEvents, rs) 80 bs = booking.NewLoggingService(log.With(logger, "component", "booking"), bs) 81 bs = booking.NewInstrumentingService( 82 kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ 83 Namespace: "api", 84 Subsystem: "booking_service", 85 Name: "request_count", 86 Help: "Number of requests received.", 87 }, fieldKeys), 88 kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 89 Namespace: "api", 90 Subsystem: "booking_service", 91 Name: "request_latency_microseconds", 92 Help: "Total duration of requests in microseconds.", 93 }, fieldKeys), 94 bs, 95 ) 96 97 var ts tracking.Service 98 ts = tracking.NewService(cargos, handlingEvents) 99 ts = tracking.NewLoggingService(log.With(logger, "component", "tracking"), ts) 100 ts = tracking.NewInstrumentingService( 101 kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ 102 Namespace: "api", 103 Subsystem: "tracking_service", 104 Name: "request_count", 105 Help: "Number of requests received.", 106 }, fieldKeys), 107 kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 108 Namespace: "api", 109 Subsystem: "tracking_service", 110 Name: "request_latency_microseconds", 111 Help: "Total duration of requests in microseconds.", 112 }, fieldKeys), 113 ts, 114 ) 115 116 var hs handling.Service 117 hs = handling.NewService(handlingEvents, handlingEventFactory, handlingEventHandler) 118 hs = handling.NewLoggingService(log.With(logger, "component", "handling"), hs) 119 hs = handling.NewInstrumentingService( 120 kitprometheus.NewCounterFrom(stdprometheus.CounterOpts{ 121 Namespace: "api", 122 Subsystem: "handling_service", 123 Name: "request_count", 124 Help: "Number of requests received.", 125 }, fieldKeys), 126 kitprometheus.NewSummaryFrom(stdprometheus.SummaryOpts{ 127 Namespace: "api", 128 Subsystem: "handling_service", 129 Name: "request_latency_microseconds", 130 Help: "Total duration of requests in microseconds.", 131 }, fieldKeys), 132 hs, 133 ) 134 135 httpLogger := log.With(logger, "component", "http") 136 137 mux := http.NewServeMux() 138 139 mux.Handle("/booking/v1/", booking.MakeHandler(bs, httpLogger)) 140 mux.Handle("/tracking/v1/", tracking.MakeHandler(ts, httpLogger)) 141 mux.Handle("/handling/v1/", handling.MakeHandler(hs, httpLogger)) 142 143 http.Handle("/", accessControl(mux)) 144 http.Handle("/metrics", promhttp.Handler()) 145 146 errs := make(chan error, 2) 147 go func() { 148 logger.Log("transport", "http", "address", *httpAddr, "msg", "listening") 149 errs <- http.ListenAndServe(*httpAddr, nil) 150 }() 151 go func() { 152 c := make(chan os.Signal) 153 signal.Notify(c, syscall.SIGINT) 154 errs <- fmt.Errorf("%s", <-c) 155 }() 156 157 logger.Log("terminated", <-errs) 158} 159 160func accessControl(h http.Handler) http.Handler { 161 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 162 w.Header().Set("Access-Control-Allow-Origin", "*") 163 w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") 164 w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type") 165 166 if r.Method == "OPTIONS" { 167 return 168 } 169 170 h.ServeHTTP(w, r) 171 }) 172} 173 174func envString(env, fallback string) string { 175 e := os.Getenv(env) 176 if e == "" { 177 return fallback 178 } 179 return e 180} 181 182func storeTestData(r cargo.Repository) { 183 test1 := cargo.New("FTL456", cargo.RouteSpecification{ 184 Origin: location.AUMEL, 185 Destination: location.SESTO, 186 ArrivalDeadline: time.Now().AddDate(0, 0, 7), 187 }) 188 if err := r.Store(test1); err != nil { 189 panic(err) 190 } 191 192 test2 := cargo.New("ABC123", cargo.RouteSpecification{ 193 Origin: location.SESTO, 194 Destination: location.CNHKG, 195 ArrivalDeadline: time.Now().AddDate(0, 0, 14), 196 }) 197 if err := r.Store(test2); err != nil { 198 panic(err) 199 } 200} 201