func main() { var ( err error closeChan = make(chan bool) ) leapsConfig := LeapsConfig{ NumProcesses: runtime.NumCPU(), LoggerConfig: log.DefaultLoggerConfig(), StatsConfig: log.DefaultStatsConfig(), RiemannConfig: log.NewRiemannClientConfig(), StoreConfig: store.NewConfig(), AuthenticatorConfig: auth.NewConfig(), CuratorConfig: lib.DefaultCuratorConfig(), HTTPServerConfig: net.DefaultHTTPServerConfig(), InternalServerConfig: net.NewInternalServerConfig(), StatsServerConfig: log.DefaultStatsServerConfig(), } // A list of default config paths to check for if not explicitly defined defaultPaths := []string{} /* If we manage to get the path of our executable then we want to try and find config files * relative to that path, we always check from the parent folder since we assume leaps is * stored within the bin folder. */ if executablePath, err := path.BinaryPath(); err == nil { defaultPaths = append(defaultPaths, filepath.Join(executablePath, "..", "config.yaml")) defaultPaths = append(defaultPaths, filepath.Join(executablePath, "..", "config", "leaps.yaml")) defaultPaths = append(defaultPaths, filepath.Join(executablePath, "..", "config.json")) defaultPaths = append(defaultPaths, filepath.Join(executablePath, "..", "config", "leaps.json")) } defaultPaths = append(defaultPaths, []string{ filepath.Join(".", "leaps.yaml"), filepath.Join(".", "leaps.json"), "/etc/leaps.yaml", "/etc/leaps.json", "/etc/leaps/config.yaml", "/etc/leaps/config.json", }...) // Load configuration etc if !util.Bootstrap(&leapsConfig, defaultPaths...) { return } if len(*sharePathOverride) > 0 { leapsConfig.AuthenticatorConfig.FileConfig.SharePath = *sharePathOverride leapsConfig.StoreConfig.StoreDirectory = *sharePathOverride } runtime.GOMAXPROCS(leapsConfig.NumProcesses) // Logging and stats aggregation logger := log.NewLogger(os.Stdout, leapsConfig.LoggerConfig) stats := log.NewStats(leapsConfig.StatsConfig) if riemannClient, err := log.NewRiemannClient(leapsConfig.RiemannConfig); err == nil { logger.UseRiemann(riemannClient) stats.UseRiemann(riemannClient) defer riemannClient.Close() } else if err != log.ErrEmptyConfigAddress { fmt.Fprintln(os.Stderr, fmt.Sprintf("Riemann client error: %v\n", err)) return } defer stats.Close() fmt.Printf("Launching a leaps instance, use CTRL+C to close.\n\n") // Document storage engine documentStore, err := store.Factory(leapsConfig.StoreConfig) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Document store error: %v\n", err)) return } // Authenticator authenticator, err := auth.Factory(leapsConfig.AuthenticatorConfig, logger, stats) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Authenticator error: %v\n", err)) return } // Curator of documents curator, err := lib.NewCurator(leapsConfig.CuratorConfig, logger, stats, authenticator, documentStore) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Curator error: %v\n", err)) return } defer curator.Close() // HTTP API leapHTTP, err := net.CreateHTTPServer(curator, leapsConfig.HTTPServerConfig, logger, stats) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("HTTP error: %v\n", err)) return } defer leapHTTP.Stop() go func() { if httperr := leapHTTP.Listen(); httperr != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Http listen error: %v\n", httperr)) } closeChan <- true }() var adminRegister register.EndpointRegister // Internal admin HTTP API if 0 < len(leapsConfig.InternalServerConfig.Address) { adminHTTP, err := net.NewInternalServer(curator, leapsConfig.InternalServerConfig, logger, stats) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Admin HTTP error: %v\n", err)) return } adminRegister = adminHTTP go func() { if httperr := adminHTTP.Listen(); httperr != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Admin HTTP listen error: %v\n", httperr)) } closeChan <- true }() } // Register for allowing other components to set API endpoints. register := newEndpointsRegister(leapHTTP, adminRegister) if err = authenticator.RegisterHandlers(register); err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Register authentication endpoints failed: %v\n", err)) return } // Internal Statistics HTTP API statsServer, err := log.NewStatsServer(leapsConfig.StatsServerConfig, logger, stats) if err != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Stats error: %v\n", err)) return } go func() { if statserr := statsServer.Listen(); statserr != nil { fmt.Fprintln(os.Stderr, fmt.Sprintf("Stats server listen error: %v\n", statserr)) } }() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) // Wait for termination signal select { case <-sigChan: case <-closeChan: } }
func TestHttpServer(t *testing.T) { bytes, err := ioutil.ReadFile("../test/stories/binder_stories.js") if err != nil { t.Errorf("Read file error: %v", err) return } var scont binderStoriesContainer if err := json.Unmarshal(bytes, &scont); err != nil { t.Errorf("Story parse error: %v", err) return } httpServerConfig := DefaultHTTPServerConfig() httpServerConfig.Address = "localhost:8254" logger, stats := loggerAndStats() auth, storage := authAndStore(logger, stats) curator, err := lib.NewCurator(lib.DefaultCuratorConfig(), logger, stats, auth, storage) if err != nil { t.Errorf("Curator error: %v", err) } go func() { http, err := CreateHTTPServer(curator, httpServerConfig, logger, stats) if err != nil { t.Errorf("Create HTTP error: %v", err) return } if err = http.Listen(); err != nil { t.Errorf("Listen error: %v", err) } }() time.Sleep(50 * time.Millisecond) origin := "http://localhost/" url := "ws://localhost:8254/leaps/socket" for _, story := range scont.Stories { ws, err := websocket.Dial(url, "", origin) if err != nil { t.Errorf("client connect error: %v", err) return } websocket.JSON.Send(ws, LeapClientMessage{ Command: "create", Document: &store.Document{ Content: story.Content, }, }) var initResponse LeapServerMessage if err := websocket.JSON.Receive(ws, &initResponse); err != nil { t.Errorf("Init receive error: %v", err) return } switch initResponse.Type { case "document": case "error": t.Errorf("Server returned error: %v", initResponse.Error) return default: t.Errorf("unexpected message type from server init: %v", initResponse.Type) return } feeds := make(chan lib.OTransform) wg := sync.WaitGroup{} wg.Add(10) go senderClient(initResponse.Document.ID, feeds, t) for j := 0; j < 10; j++ { go goodStoryClient(initResponse.Document.ID, &story, &wg, t) } time.Sleep(50 * time.Millisecond) for j := 0; j < len(story.Transforms); j++ { feeds <- story.Transforms[j] } wg.Wait() } curator.Close() }