dn42regsrv.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //////////////////////////////////////////////////////////////////////////
  2. // DN42 Registry API Server
  3. //////////////////////////////////////////////////////////////////////////
  4. package main
  5. //////////////////////////////////////////////////////////////////////////
  6. import (
  7. "context"
  8. "encoding/json"
  9. "github.com/gorilla/mux"
  10. log "github.com/sirupsen/logrus"
  11. flag "github.com/spf13/pflag"
  12. "net/http"
  13. "os"
  14. "os/signal"
  15. "time"
  16. )
  17. //////////////////////////////////////////////////////////////////////////
  18. // simple event bus
  19. type NotifyFunc func(...interface{})
  20. type SimpleEventBus map[string][]NotifyFunc
  21. var EventBus = make(SimpleEventBus)
  22. // add a listener to an event
  23. func (bus SimpleEventBus) Listen(event string, nfunc NotifyFunc) {
  24. bus[event] = append(bus[event], nfunc)
  25. }
  26. // fire notifications for an event
  27. func (bus SimpleEventBus) Fire(event string, params ...interface{}) {
  28. funcs := bus[event]
  29. if funcs != nil {
  30. for _, nfunc := range funcs {
  31. nfunc(params...)
  32. }
  33. }
  34. }
  35. //////////////////////////////////////////////////////////////////////////
  36. // utility func for returning JSON from an API endpoint
  37. func ResponseJSON(w http.ResponseWriter, v interface{}) {
  38. // for response time testing
  39. //time.Sleep(time.Second)
  40. // marshal the JSON string
  41. data, err := json.Marshal(v)
  42. if err != nil {
  43. log.WithFields(log.Fields{
  44. "error": err,
  45. }).Error("Failed to marshal JSON")
  46. }
  47. // write back to http handler
  48. w.Header().Set("Content-Type", "application/json")
  49. w.Header().Set("Access-Control-Allow-Origin", "*")
  50. w.Write(data)
  51. }
  52. //////////////////////////////////////////////////////////////////////////
  53. // utility function to set the log level
  54. func setLogLevel(levelStr string) {
  55. if level, err := log.ParseLevel(levelStr); err != nil {
  56. // failed to set the level
  57. // set a sensible default and, of course, log the error
  58. log.SetLevel(log.InfoLevel)
  59. log.WithFields(log.Fields{
  60. "loglevel": levelStr,
  61. "error": err,
  62. }).Error("Failed to set requested log level")
  63. } else {
  64. // set the requested level
  65. log.SetLevel(level)
  66. }
  67. }
  68. //////////////////////////////////////////////////////////////////////////
  69. // http request logger
  70. func requestLogger(next http.Handler) http.Handler {
  71. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  72. log.WithFields(log.Fields{
  73. "method": r.Method,
  74. "URL": r.URL.String(),
  75. "Remote": r.RemoteAddr,
  76. }).Debug("HTTP Request")
  77. next.ServeHTTP(w, r)
  78. })
  79. }
  80. //////////////////////////////////////////////////////////////////////////
  81. // everything starts here
  82. func main() {
  83. // set a default log level, so that logging can be used immediately
  84. // the level will be overidden later on once the command line
  85. // options are loaded
  86. log.SetLevel(log.InfoLevel)
  87. log.Info("DN42 Registry API Server Starting")
  88. // declare cmd line options
  89. var (
  90. logLevel = flag.StringP("LogLevel", "l", "Info", "Log level")
  91. regDir = flag.StringP("RegDir", "d", "registry", "Registry data directory")
  92. bindAddress = flag.StringP("BindAddress", "b", "[::]:8042", "Server bind address")
  93. staticRoot = flag.StringP("StaticRoot", "s", "StaticRoot", "Static page directory")
  94. refreshInterval = flag.StringP("Refresh", "i", "60m", "Refresh interval")
  95. gitPath = flag.StringP("GitPath", "g", "/usr/bin/git", "Path to git executable")
  96. autoPull = flag.BoolP("AutoPull", "a", true, "Automatically pull the registry")
  97. branch = flag.StringP("Branch", "p", "master", "git branch to pull")
  98. )
  99. flag.Parse()
  100. // now initialise logging properly based on the cmd line options
  101. setLogLevel(*logLevel)
  102. // parse the refreshInterval and start data collection
  103. interval, err := time.ParseDuration(*refreshInterval)
  104. if err != nil {
  105. log.WithFields(log.Fields{
  106. "error": err,
  107. "interval": *refreshInterval,
  108. }).Fatal("Unable to parse registry refresh interval")
  109. }
  110. InitialiseRegistryData(*regDir, interval,
  111. *gitPath, *autoPull, *branch)
  112. // initialise router
  113. router := mux.NewRouter()
  114. // log all access
  115. router.Use(requestLogger)
  116. // add API routes
  117. subr := router.PathPrefix("/api").Subrouter()
  118. EventBus.Fire("APIEndpoint", subr)
  119. // initialise static routes
  120. InstallStaticRoutes(router, *staticRoot)
  121. // initialise http server
  122. server := &http.Server{
  123. Addr: *bindAddress,
  124. WriteTimeout: time.Second * 15,
  125. ReadTimeout: time.Second * 15,
  126. IdleTimeout: time.Second * 60,
  127. Handler: router,
  128. }
  129. // run the server in a non-blocking goroutine
  130. log.WithFields(log.Fields{
  131. "BindAddress": *bindAddress,
  132. }).Info("Starting server")
  133. go func() {
  134. if err := server.ListenAndServe(); err != nil {
  135. log.WithFields(log.Fields{
  136. "error": err,
  137. "BindAddress": *bindAddress,
  138. }).Fatal("Unable to start server")
  139. }
  140. }()
  141. // graceful shutdown via SIGINT (^C)
  142. csig := make(chan os.Signal, 1)
  143. signal.Notify(csig, os.Interrupt)
  144. // and block
  145. <-csig
  146. log.Info("Server shutting down")
  147. // deadline for server to shutdown
  148. ctx, cancel := context.WithTimeout(context.Background(), 10)
  149. defer cancel()
  150. // shutdown the server
  151. server.Shutdown(ctx)
  152. // nothing left to do
  153. log.Info("Shutdown complete, all done")
  154. os.Exit(0)
  155. }
  156. //////////////////////////////////////////////////////////////////////////
  157. // end of code