func main() { r := runner.New(Name) if err := r.Init(); err != nil { log.Fatal(err) } appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() segmentExporter := eventexporter.NewSegmentIOExporter(appConfig.Segment, QueueLength) datadogExporter := eventexporter.NewDatadogExporter(r.DogStatsD) // TODO // we are gonna add this line into the multiexporter // firstly, we need to make sure our json data satisfy druid's data specs // druidExporter := eventexporter.NewDruidExporter(appConfig.DruidHost) // exporter := eventexporter.NewMultiExporter(segmentExporter, datadogExporter, druidExporter) exporter := eventexporter.NewMultiExporter(segmentExporter, datadogExporter) constructor := emailsender.New(exporter, r.Log, appConfig) r.ShutdownHandler = constructor.Close r.SetContext(constructor) r.Register(emailsender.Mail{}).On(emailsender.SendEmailEventName).Handle((*emailsender.Controller).Process) r.Listen() r.Wait() }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { fmt.Println(err) return } // init mongo connection appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() r.SetContext(realtime.New(r.Bongo.Broker.MQ, r.Log)) r.Register(models.ChannelMessage{}).OnUpdate().Handle((*realtime.Controller).MessageUpdated) r.Register(models.MessageReply{}).OnCreate().Handle((*realtime.Controller).MessageReplySaved) r.Register(models.MessageReply{}).OnDelete().Handle((*realtime.Controller).MessageReplyDeleted) r.Register(models.ChannelMessageList{}).OnCreate().Handle((*realtime.Controller).MessageListSaved) r.Register(models.ChannelMessageList{}).OnUpdate().Handle((*realtime.Controller).ChannelMessageListUpdated) r.Register(models.ChannelMessageList{}).OnDelete().Handle((*realtime.Controller).MessageListDeleted) r.Register(models.ParticipantEvent{}).On(models.ChannelParticipant_Removed_From_Channel_Event).Handle((*realtime.Controller).ChannelParticipantRemoved) r.Register(models.ParticipantEvent{}).On(models.ChannelParticipant_Added_To_Channel_Event).Handle((*realtime.Controller).ChannelParticipantsAdded) r.Register(models.ChannelParticipant{}).OnUpdate().Handle((*realtime.Controller).ChannelParticipantUpdatedEvent) r.Register(models.Channel{}).OnDelete().Handle((*realtime.Controller).ChannelDeletedEvent) r.Register(models.Channel{}).OnUpdate().Handle((*realtime.Controller).ChannelUpdatedEvent) r.Listen() r.Wait() }
func TestChannelPrepareName(t *testing.T) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatalf("couldnt start bongo %s", err.Error()) } defer r.Close() appConfig := config.MustRead(r.Conf.Path) Convey("While creating PubNub channels", t, func() { Convey("Notification channel name format must be as 'notification'-[env]-[nickname]", func() { a := &models.Account{} a.Nick = "hello" nc := NewNotificationChannel(a) name := nc.PrepareName() expectedName := fmt.Sprintf("notification-%s-%s", appConfig.Environment, a.Nick) So(name, ShouldEqual, expectedName) }) Convey("Message update and channel name format must be as 'channel'-[token]", func() { c := Channel{} c.Token = "12345" pc := NewPrivateMessageChannel(c) expectedName := fmt.Sprintf("channel-%s", c.Token) So(pc.PrepareName(), ShouldEqual, expectedName) uim := UpdateInstanceMessage{} uim.ChannelToken = "12345" um := NewMessageUpdateChannel(uim) So(um.PrepareName(), ShouldEqual, expectedName) }) }) }
func withTestServer(t *testing.T, f func(url string)) { const workerName = "pingtest" r := runner.New(workerName) if err := r.Init(); err != nil { t.Fatal(err) } c := config.MustRead(r.Conf.Path) // init mongo connection modelhelper.Initialize(c.Mongo) defer modelhelper.Close() port := tests.GetFreePort() mc := mux.NewConfig(workerName, "localhost", port) mc.Debug = r.Conf.Debug m := mux.New(mc, r.Log, r.Metrics) AddHandlers(m) m.Listen() go r.Listen() f(fmt.Sprintf("http://localhost:%s", port)) if err := r.Close(); err != nil { t.Fatalf("server close errored: %s", err.Error()) } // shutdown server m.Close() }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { log.Fatal(err) } if r.Kite == nil { r.Log.Fatal("couldnt init kite") } // remove QOS, we want to consume all the messages from RMQ if err := r.Bongo.Broker.Sub.(*broker.Consumer).Consumer.QOS(0); err != nil { r.Log.Fatal("couldnt remove the QOS %# v", err) } appConfig := config.MustRead(r.Conf.Path) // init mongo connection modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() // init with defaults & ensure expireAt index mongoCache := cache.NewMongoCacheWithTTL(modelhelper.Mongo.Session, cache.StartGC(), cache.MustEnsureIndexExpireAt()) defer mongoCache.StopGC() handler := collaboration.New(r.Log, mongoCache, appConfig, r.Kite) r.SetContext(handler) // only listen and operate on collaboration ping messages that are fired by the handler r.Register(models.Ping{}).On(collaboration.FireEventName).Handle((*collaboration.Controller).Ping) r.Listen() r.Wait() }
func TestGetIdsFromMailboxHash(t *testing.T) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatalf("couldnt start bongo %s", err.Error()) } defer r.Close() // init mongo connection appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() Convey("while getting ids from mailboxhash", t, func() { Convey("returns error if 1.index of mailboxhash doesn't exist", func() { m := &Mail{ MailboxHash: "message.", } gid, err := m.getIdsFromMailboxHash() So(err, ShouldNotBeNil) So(gid, ShouldEqual, 0) }) Convey("returns error if 1.index doesn't exist", func() { m := &Mail{ MailboxHash: "message.1234", } gid, err := m.getIdsFromMailboxHash() So(err, ShouldBeNil) So(gid, ShouldEqual, 1234) }) }) }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { fmt.Println(err) return } appConfig := config.MustRead(r.Conf.Path) // create a realtime service provider instance. pubnub := models.NewPubNub(appConfig.GateKeeper.Pubnub, r.Log) defer pubnub.Close() // When we use the same RMQ connection for both, we received // 'Exception (504) Reason: "CHANNEL_ERROR - unexpected method in connection state running"' // error at some point. It needs debugging. rmqBroker, err := runner.NewRabbitMQ(r.Conf, r.Log).Connect() if err != nil { fmt.Println(err) return } defer rmqBroker.Conn().Close() broker := models.NewBroker(rmqBroker, r.Log) r.SetContext(dispatcher.NewController(r.Bongo.Broker.MQ, pubnub, broker)) r.ListenFor("dispatcher_channel_updated", (*dispatcher.Controller).UpdateChannel) r.ListenFor("dispatcher_message_updated", (*dispatcher.Controller).UpdateMessage) r.ListenFor("dispatcher_notify_user", (*dispatcher.Controller).NotifyUser) r.ListenFor("dispatcher_notify_group", (*dispatcher.Controller).NotifyGroup) r.ListenFor("event.channel_participant_removed_from_channel", (*dispatcher.Controller).RevokeChannelAccess) r.Listen() r.Wait() }
func TestValidate(t *testing.T) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatalf("couldnt start bongo %s", err.Error()) } defer r.Close() // init mongo connection appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() Convey("while testing Validate", t, func() { Convey("From field of Mail struct should not be empty, otherwise return err", func() { m := &Mail{} err := m.Validate() So(err, ShouldNotBeNil) So(err, ShouldEqual, ErrFromFieldIsNotSet) }) Convey("TextBody field of Mail struct should not be empty, otherwise return err", func() { m := &Mail{From: "*****@*****.**"} err := m.Validate() So(err, ShouldNotBeNil) So(err, ShouldEqual, ErrTextBodyIsNotSet) }) Convey("returns nil if Mail struct is set ", func() { m := &Mail{From: "*****@*****.**", TextBody: "Some text parapraph"} So(m.Validate(), ShouldBeNil) }) }) }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { fmt.Println(err) return } appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() // create a realtime service provider instance. pubnub := models.NewPubNub(appConfig.GateKeeper.Pubnub, r.Log) defer pubnub.Close() mc := mux.NewConfig(Name, appConfig.GateKeeper.Host, appConfig.GateKeeper.Port) m := mux.New(mc, r.Log, r.Metrics) h := api.NewHandler(pubnub, appConfig, r.Log) h.AddHandlers(m) // consume messages from RMQ // Gatekeeper is not using RMQ, but runner is creating a message queue for // each worker. We need to discard the messages in the queue, otherwise // all the messages are piled up go r.Listen() m.Listen() defer m.Close() r.Wait() }
func startRunner() *runner.Runner { r := runner.New("emailsender_test") if err := r.Init(); err != nil { panic(err) } return r }
func TestTeam(t *testing.T) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatalf("couldnt start bongo %s", err.Error()) } defer r.Close() SkipConvey("while testing team", t, func() { }) }
func TestCollaborationSesionEnd(t *testing.T) { r := runner.New("collaboration-tests") err := r.Init() if err != nil { panic(err) } defer r.Close() appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() Convey("while testing collaboration session end", t, func() { Convey("we should be able to create a doc on google drive", func() { Convey("we should be able to delete a created doc", func() { Convey("deleting an already deleted doc should not give error", func() { }) }) }) Convey("trying to delete a non-existing doc should not give error", func() { }) }) Convey("while pinging collaboration", t, func() { // owner owner, err := apimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) So(owner, ShouldNotBeNil) groupName := apimodels.RandomGroupName() apimodels.CreateTypedGroupedChannelWithTest( owner.Id, apimodels.Channel_TYPE_GROUP, groupName, ) ownerSession, err := modelhelper.FetchOrCreateSession(owner.Nick, groupName) So(err, ShouldBeNil) So(ownerSession, ShouldNotBeNil) Convey("reponse should be success", func() { p := &models.Ping{ AccountId: 1, FileId: "hello", } res, err := rest.CollaborationPing(p, ownerSession.ClientId) So(err, ShouldBeNil) So(res, ShouldNotBeNil) }) }) }
func WithRunner(t *testing.T, f func(*runner.Runner)) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatalf("couldnt start bongo %s", err.Error()) } defer r.Close() appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() f(r) }
func WithConfiguration(t *testing.T, f func(c *config.Config)) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatal(err.Error()) } defer r.Close() c := config.MustRead(r.Conf.Path) modelhelper.Initialize(c.Mongo) defer modelhelper.Close() f(c) }
func getTestHandler() (*runner.Runner, *Controller) { r := runner.New("AlogoliaConnector-Test") err := r.Init() if err != nil { panic(err) } appConfig := config.MustRead(r.Conf.Path) algolia := algoliasearch.NewClient(appConfig.Algolia.AppId, appConfig.Algolia.ApiSecretKey) // create message handler return r, New(r.Log, algolia, ".test") }
func main() { r := runner.New(name) if err := r.Init(); err != nil { log.Fatal(err.Error()) } appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() r.SetContext(presence.New(r.Log, appConfig)) r.Register(presence.Ping{}).On(presence.EventName).Handle((*presence.Controller).Ping) r.Listen() r.Wait() }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { log.Fatal(err) } // init mongo connection appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() if err := models.DeleteDiffedDBAccounts(); err != nil { fmt.Println("error while deleting account that non-existing in mongo", err) return } }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { fmt.Println(err) return } defer r.Close() // init mongo connection appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() algolia := algoliasearch.NewClient( appConfig.Algolia.AppId, appConfig.Algolia.ApiSecretKey, ) // create message handler handler := algoliaconnector.New(r.Log, algolia, appConfig.Algolia.IndexSuffix) counter := 0 for b := 0; ; b++ { var accounts []models.Account err := (&models.Account{}).Some(&accounts, &bongo.Query{ Pagination: *bongo.NewPagination(100, b*100), }) if err != nil { r.Log.Error(err.Error()) continue } for _, account := range accounts { counter++ r.Log.Info("[%d]: currently migrating: '%v'", counter, account.Nick) if err := handler.AccountUpdated(&account); err != nil { r.Log.Error(err.Error()) continue } } if len(accounts) < 100 { break } } }
func TestGetSocialIdFromEmail(t *testing.T) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatalf("couldnt start bongo %s", err.Error()) } defer r.Close() // init mongo connection appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() Convey("while getting account id in the mail", t, func() { Convey("From fields should be saved in db, otherwise return err", func() { m := &Mail{ From: "mailisnotexist@abo", } gid, err := m.getSocialIdFromEmail() So(err, ShouldNotBeNil) So(err, ShouldEqual, ErrEmailIsNotFetched) So(gid, ShouldEqual, 0) }) Convey("should not be any error if all is well", func() { acc, err := socialapimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) mongoUser, err := modelhelper.GetUser(acc.Nick) So(err, ShouldBeNil) m := &Mail{ From: mongoUser.Email, } gid, err := m.getSocialIdFromEmail() So(err, ShouldBeNil) So(gid, ShouldEqual, acc.Id) }) }) }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { log.Fatal(err) } appConfig := config.MustRead(r.Conf.Path) algolia := algoliasearch.NewClient(appConfig.Algolia.AppId, appConfig.Algolia.ApiSecretKey) // create message handler handler := algoliaconnector.New(r.Log, algolia, appConfig.Algolia.IndexSuffix) if err := handler.RemoveGuestAccounts(); err != nil { r.Log.Error("Could not remove guest accounts: %s", err) } }
func TestCollaborationOperationsDeleteDriveDoc(t *testing.T) { r := runner.New("collaboration-DeleteDriveDoc-tests") err := r.Init() if err != nil { panic(err) } defer r.Close() appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() // init with defaults mongoCache := cache.NewMongoCacheWithTTL(modelhelper.Mongo.Session) defer mongoCache.StopGC() handler := New(r.Log, mongoCache, appConfig, r.Kite) Convey("while testing DeleteDriveDoc", t, func() { req := &models.Ping{ AccountId: 1, FileId: fmt.Sprintf("%d", rand.Int63()), } Convey("should be able to create the file", func() { f, err := createTestFile(handler) So(err, ShouldBeNil) req.FileId = f.Id Convey("should be able to delete the created file", func() { err = handler.DeleteDriveDoc(req) So(err, ShouldBeNil) }) Convey("if file id is nil response should be nil", func() { req := req req.FileId = "" err = handler.DeleteDriveDoc(req) So(err, ShouldBeNil) }) }) }) }
func TestGetAccount(t *testing.T) { r := runner.New("test") if err := r.Init(); err != nil { t.Fatalf("couldnt start bongo %s", err.Error()) } defer r.Close() // init mongo connection appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() Convey("while testing get account", t, func() { Convey("returns empty if parameter is invalid", func() { acc, err := GetAccount("interestingEmail@somethinglikethat") So(err, ShouldNotBeNil) So(acc, ShouldBeNil) }) Convey("returns empty if parameter is empty", func() { acc, err := GetAccount("") So(err, ShouldNotBeNil) So(acc, ShouldBeNil) }) Convey("Should return blank if parameter is empty", func() { acc, err := socialapimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) mongoUser, err := modelhelper.GetUser(acc.Nick) So(err, ShouldBeNil) m := &Mail{ From: mongoUser.Email, } ga, err := GetAccount(m.From) So(err, ShouldBeNil) So(ga, ShouldNotBeNil) }) }) }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { fmt.Println(err) return } defer r.Close() appConfig := config.MustRead(r.Conf.Path) algolia := algoliasearch.NewClient( appConfig.Algolia.AppId, appConfig.Algolia.ApiSecretKey, ) // create message handler handler := algoliaconnector.New(r.Log, algolia, appConfig.Algolia.IndexSuffix) counter := 0 for b := 0; ; b++ { counter++ topics, err := (&models.Channel{}).List(&request.Query{ GroupName: "koding", Type: "topic", Limit: 100, Skip: b * 100, }) if err != nil { r.Log.Error(err.Error()) return } for _, topic := range topics { r.Log.Info("[%d] currently migrating: '%v'", counter, topic.Name) handler.ChannelCreated(&topic) } if len(topics) < 100 { break } } }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { log.Fatal(err) } appConfig := config.MustRead(r.Conf.Path) algolia := algoliasearch.NewClient(appConfig.Algolia.AppId, appConfig.Algolia.ApiSecretKey) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() // create message handler handler := algoliaconnector.New(r.Log, algolia, appConfig.Algolia.IndexSuffix) index, ok := indexes.(map[string]interface{}) items, ok := index["items"].([]interface{}) if !ok { return } for _, item := range items { it, ok := item.(map[string]interface{}) if !ok { continue } updatedAt := it["updatedAt"].(string) t, err := time.Parse(time.RFC3339, updatedAt) if err != nil { fmt.Println("parsing time:", err) return } if t.Before(GetLast60Days()) { if _, err := handler.InitAndDeleteIndex(it["name"].(string)); err != nil { fmt.Println(err) return } } } r.Wait() }
func TestMailParse(t *testing.T) { r := runner.New("test") err := r.Init() if err != nil { panic(err) } defer r.Close() appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() Convey("while sending mail", t, func() { Convey("reponse should be success", func() { acc, err := socialapimodels.CreateAccountInBothDbs() So(err, ShouldBeNil) c := socialapimodels.CreateChannelWithTest(acc.Id) socialapimodels.AddParticipantsWithTest(c.Id, acc.Id) cm := socialapimodels.CreateMessage(c.Id, acc.Id, socialapimodels.ChannelMessage_TYPE_POST) So(cm, ShouldNotBeNil) mongoUser, err := modelhelper.GetUser(acc.Nick) So(err, ShouldBeNil) p := &models.Mail{ From: mongoUser.Email, OriginalRecipient: fmt.Sprintf("*****@*****.**", c.Id), MailboxHash: fmt.Sprintf("messageid.%d", cm.Id), TextBody: "Its an example of text message", StrippedTextReply: "This one is reply message", } res, err := rest.MailParse(p) So(err, ShouldBeNil) So(res, ShouldNotBeNil) }) }) }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { log.Fatal(err) } // init mongo connection appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() r.SetContext(team.NewController(r.Log, appConfig)) r.Register(models.ChannelParticipant{}).OnCreate().Handle((*team.Controller).HandleParticipant) r.Register(models.ChannelParticipant{}).OnUpdate().Handle((*team.Controller).HandleParticipant) r.Register(models.ChannelParticipant{}).OnDelete().Handle((*team.Controller).HandleParticipant) r.Register(models.Channel{}).OnCreate().Handle((*team.Controller).HandleCreator) r.Register(models.Channel{}).OnDelete().Handle((*team.Controller).HandleChannel) r.Listen() r.Wait() }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { log.Fatal(err) } appConfig := config.MustRead(r.Conf.Path) algolia := algoliasearch.NewClient(appConfig.Algolia.AppId, appConfig.Algolia.ApiSecretKey) modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() // create message handler handler := algoliaconnector.New(r.Log, algolia, appConfig.Algolia.IndexSuffix) if err := handler.DeleteNicksWithQueryBrowseAll(""); err != nil { r.Log.Error("Could not remove guest accounts: %s", err) } r.Wait() }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { fmt.Println(err) return } defer r.Close() appConfig := config.MustRead(r.Conf.Path) algolia := algoliasearch.NewClient( appConfig.Algolia.AppId, appConfig.Algolia.ApiSecretKey, ) // create message handler handler := algoliaconnector.New(r.Log, algolia, appConfig.Algolia.IndexSuffix) if err := migrateChannels(r, handler); err != nil { panic(err) } }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { log.Fatal(err.Error()) } appConfig := config.MustRead(r.Conf.Path) // init mongo connection modelhelper.Initialize(appConfig.Mongo) defer modelhelper.Close() algolia := algoliasearch.NewClient(appConfig.Algolia.AppId, appConfig.Algolia.ApiSecretKey) // create message handler handler := algoliaconnector.New(r.Log, algolia, appConfig.Algolia.IndexSuffix) if err := handler.Init(); err != nil { // this is not a blocker for algoliaconnector worker, we can continue working r.Log.Error("Err while init: %s", err.Error()) } r.SetContext(handler) r.Register(models.Channel{}).OnCreate().Handle((*algoliaconnector.Controller).ChannelCreated) r.Register(models.Channel{}).OnUpdate().Handle((*algoliaconnector.Controller).ChannelUpdated) r.Register(models.Account{}).OnCreate().Handle((*algoliaconnector.Controller).AccountCreated) r.Register(models.Account{}).OnUpdate().Handle((*algoliaconnector.Controller).AccountUpdated) r.Register(models.ChannelMessageList{}).OnCreate().Handle((*algoliaconnector.Controller).MessageListSaved) r.Register(models.ChannelMessageList{}).OnDelete().Handle((*algoliaconnector.Controller).MessageListDeleted) r.Register(models.ChannelMessage{}).OnUpdate().Handle((*algoliaconnector.Controller).MessageUpdated) // participant related events r.Register(models.ChannelParticipant{}).OnCreate().Handle((*algoliaconnector.Controller).ParticipantCreated) r.Register(models.ChannelParticipant{}).OnUpdate().Handle((*algoliaconnector.Controller).ParticipantUpdated) r.Listen() r.Wait() }
func main() { r := runner.New(Name) if err := r.Init(); err != nil { fmt.Println(err) return } defer r.Close() appConfig := config.MustRead(r.Conf.Path) modelhelper.Initialize(appConfig.Mongo) pubnub := models.NewPubNub(appConfig.GateKeeper.Pubnub, r.Log) defer pubnub.Close() handler, err := controller.New(r.Log, pubnub) if err != nil { panic(err) } if *flagSchedule { r.ShutdownHandler = handler.Shutdown if err := handler.Schedule(); err != nil { panic(err) } r.Wait() return } if *flagPubNub { handler.GrantPublicAccess() return } handler.Start() }