diff --git a/integration_tests/notifier/notifier_test.go b/integration_tests/notifier/notifier_test.go index cb69629a5..79d7e1746 100644 --- a/integration_tests/notifier/notifier_test.go +++ b/integration_tests/notifier/notifier_test.go @@ -17,6 +17,7 @@ import ( "github.com/moira-alert/moira/notifier" "github.com/moira-alert/moira/notifier/events" "github.com/moira-alert/moira/notifier/notifications" + . "github.com/smartystreets/goconvey/convey" ) var senderSettings = map[string]string{ @@ -78,41 +79,51 @@ var event = moira.NotificationEvent{ func TestNotifier(t *testing.T) { mockCtrl = gomock.NewController(t) defer mockCtrl.Finish() - database := redis.NewTestDatabase(logger) - metricsSourceProvider := metricSource.CreateMetricSourceProvider(local.Create(database), nil) - database.SaveContact(&contact) //nolint - database.SaveSubscription(&subscription) //nolint - database.SaveTrigger(trigger.ID, &trigger) //nolint - database.PushNotificationEvent(&event, true) //nolint - notifier2 := notifier.NewNotifier(database, logger, notifierConfig, notifierMetrics, metricsSourceProvider, map[string]moira.ImageStore{}) - sender := mock_moira_alert.NewMockSender(mockCtrl) - sender.EXPECT().Init(senderSettings, logger, location, dateTimeFormat).Return(nil) - notifier2.RegisterSender(senderSettings, sender) //nolint - sender.EXPECT().SendEvents(gomock.Any(), contact, triggerData, gomock.Any(), false).Return(nil).Do(func(arg0, arg1, arg2, arg3, arg4 interface{}) { - fmt.Print("SendEvents called. End test") - close(shutdown) - }) - fetchEventsWorker := events.FetchEventsWorker{ - Database: database, - Logger: logger, - Metrics: notifierMetrics, - Scheduler: notifier.NewScheduler(database, logger, notifierMetrics), - } - - fetchNotificationsWorker := notifications.FetchNotificationsWorker{ - Database: database, - Logger: logger, - Notifier: notifier2, - } - - fetchEventsWorker.Start() - fetchNotificationsWorker.Start() - - waitTestEnd() - - fetchEventsWorker.Stop() //nolint - fetchNotificationsWorker.Stop() //nolint + Convey("TestNotifier", t, func() { + database := redis.NewTestDatabase(logger) + metricsSourceProvider := metricSource.CreateMetricSourceProvider(local.Create(database), nil) + err := database.SaveContact(&contact) + So(err, ShouldBeNil) + err = database.SaveSubscription(&subscription) + So(err, ShouldBeNil) + err = database.SaveTrigger(trigger.ID, &trigger) + So(err, ShouldBeNil) + err = database.PushNotificationEvent(&event, true) + So(err, ShouldBeNil) + + notifier2 := notifier.NewNotifier(database, logger, notifierConfig, notifierMetrics, metricsSourceProvider, map[string]moira.ImageStore{}) + sender := mock_moira_alert.NewMockSender(mockCtrl) + err = notifier2.RegisterSender(senderSettings, sender) + So(err, ShouldBeNil) + sender.EXPECT().SendEvents(gomock.Any(), contact, triggerData, gomock.Any(), false).Return(nil).Do(func(arg0, arg1, arg2, arg3, arg4 interface{}) { + fmt.Print("SendEvents called. End test") + close(shutdown) + }) + + fetchEventsWorker := events.FetchEventsWorker{ + Database: database, + Logger: logger, + Metrics: notifierMetrics, + Scheduler: notifier.NewScheduler(database, logger, notifierMetrics), + } + + fetchNotificationsWorker := notifications.FetchNotificationsWorker{ + Database: database, + Logger: logger, + Notifier: notifier2, + } + + fetchEventsWorker.Start() + fetchNotificationsWorker.Start() + + waitTestEnd() + + err = fetchEventsWorker.Stop() + So(err, ShouldBeNil) + err = fetchNotificationsWorker.Stop() + So(err, ShouldBeNil) + }) } func waitTestEnd() { diff --git a/interfaces.go b/interfaces.go index 74bd2ed12..c0386175d 100644 --- a/interfaces.go +++ b/interfaces.go @@ -193,7 +193,6 @@ type Logger interface { // Sender interface for implementing specified contact type sender type Sender interface { SendEvents(events NotificationEvents, contact ContactData, trigger TriggerData, plot [][]byte, throttled bool) error - Init(senderSettings map[string]string, logger Logger, location *time.Location, dateTimeFormat string) error } // ImageStore is the interface for image storage providers diff --git a/mock/moira-alert/sender.go b/mock/moira-alert/sender.go index f74004a44..cf8371b9a 100644 --- a/mock/moira-alert/sender.go +++ b/mock/moira-alert/sender.go @@ -6,7 +6,6 @@ package mock_moira_alert import ( reflect "reflect" - time "time" gomock "github.com/golang/mock/gomock" moira "github.com/moira-alert/moira" @@ -35,20 +34,6 @@ func (m *MockSender) EXPECT() *MockSenderMockRecorder { return m.recorder } -// Init mocks base method. -func (m *MockSender) Init(arg0 map[string]string, arg1 moira.Logger, arg2 *time.Location, arg3 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Init", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// Init indicates an expected call of Init. -func (mr *MockSenderMockRecorder) Init(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockSender)(nil).Init), arg0, arg1, arg2, arg3) -} - // SendEvents mocks base method. func (m *MockSender) SendEvents(arg0 moira.NotificationEvents, arg1 moira.ContactData, arg2 moira.TriggerData, arg3 [][]byte, arg4 bool) error { m.ctrl.T.Helper() diff --git a/notifier/notifier_test.go b/notifier/notifier_test.go index f7cbb3434..cc50eac96 100644 --- a/notifier/notifier_test.go +++ b/notifier/notifier_test.go @@ -219,11 +219,10 @@ func configureNotifier(t *testing.T) { "type": "test", } - sender.EXPECT().Init(senderSettings, logger, location, "15:04 02.01.2006").Return(nil) - - notif.RegisterSender(senderSettings, sender) //nolint - Convey("Should return one sender", t, func() { + err := notif.RegisterSender(senderSettings, sender) + So(err, ShouldBeNil) + So(notif.GetSenders(), ShouldResemble, map[string]bool{"test": true}) }) } diff --git a/notifier/registrator.go b/notifier/registrator.go index 634c69308..53061a51a 100644 --- a/notifier/registrator.go +++ b/notifier/registrator.go @@ -38,50 +38,58 @@ const ( msTeamsSender = "msteams" ) -// RegisterSenders watch on senders config and register all configured senders +// RegisterSenders creates all senders and registers them. func (notifier *StandardNotifier) RegisterSenders(connector moira.Database) error { //nolint var err error for _, senderSettings := range notifier.config.Senders { senderSettings["front_uri"] = notifier.config.FrontURL + + var sender moira.Sender + switch senderSettings["type"] { case mailSender: - err = notifier.RegisterSender(senderSettings, &mail.Sender{}) + sender, err = mail.NewSender(senderSettings, notifier.logger, notifier.config.Location, notifier.config.DateTimeFormat) case pushoverSender: - err = notifier.RegisterSender(senderSettings, &pushover.Sender{}) + sender, err = pushover.NewSender(senderSettings, notifier.logger, notifier.config.Location) case scriptSender: - err = notifier.RegisterSender(senderSettings, &script.Sender{}) + sender, err = script.NewSender(senderSettings, notifier.logger) case discordSender: - err = notifier.RegisterSender(senderSettings, &discord.Sender{DataBase: connector}) + sender, err = discord.NewSender(senderSettings, notifier.logger, notifier.config.Location, connector) case slackSender: - err = notifier.RegisterSender(senderSettings, &slack.Sender{}) + sender, err = slack.NewSender(senderSettings, notifier.logger, notifier.config.Location) case telegramSender: - err = notifier.RegisterSender(senderSettings, &telegram.Sender{DataBase: connector}) + sender, err = telegram.NewSender(senderSettings, notifier.logger, notifier.config.Location, connector) case msTeamsSender: - err = notifier.RegisterSender(senderSettings, &msteams.Sender{}) + sender, err = msteams.NewSender(senderSettings, notifier.logger, notifier.config.Location) case pagerdutySender: - err = notifier.RegisterSender(senderSettings, &pagerduty.Sender{ImageStores: notifier.imageStores}) + sender = pagerduty.NewSender(senderSettings, notifier.logger, notifier.config.Location, notifier.imageStores) case twilioSmsSender, twilioVoiceSender: - err = notifier.RegisterSender(senderSettings, &twilio.Sender{}) + sender, err = twilio.NewSender(senderSettings, notifier.logger, notifier.config.Location) case webhookSender: - err = notifier.RegisterSender(senderSettings, &webhook.Sender{}) + sender, err = webhook.NewSender(senderSettings, notifier.logger) case opsgenieSender: - err = notifier.RegisterSender(senderSettings, &opsgenie.Sender{ImageStores: notifier.imageStores}) + sender, err = opsgenie.NewSender(senderSettings, notifier.logger, notifier.config.Location, notifier.imageStores) case victoropsSender: - err = notifier.RegisterSender(senderSettings, &victorops.Sender{ImageStores: notifier.imageStores}) + sender, err = victorops.NewSender(senderSettings, notifier.logger, notifier.config.Location, notifier.imageStores) // case "email": - // err = notifier.RegisterSender(senderSettings, &kontur.MailSender{}) + // sender = kontur.NewMailSender(senderSettings, notifier.logger, notifier.config.Location, notifier.config.DateTimeFormat) // case "phone": - // err = notifier.RegisterSender(senderSettings, &kontur.SmsSender{}) + // sender = kontur.NewSmsSender(senderSettings, notifier.logger, notifier.config.Location) default: return fmt.Errorf("unknown sender type [%s]", senderSettings["type"]) } if err != nil { - return err + return fmt.Errorf("failed to initialize sender [%s], err [%s]", senderSettings["type"], err.Error()) + } + err = notifier.RegisterSender(senderSettings, sender) + if err != nil { + return fmt.Errorf("failed to register sender [%s], err [%s]", senderSettings["type"], err.Error()) } } if notifier.config.SelfStateEnabled { selfStateSettings := map[string]string{"type": selfStateSender} - if err = notifier.RegisterSender(selfStateSettings, &selfstate.Sender{Database: connector}); err != nil { + sender := selfstate.NewSender(notifier.logger, connector) + if err = notifier.RegisterSender(selfStateSettings, sender); err != nil { notifier.logger.Warningf("failed to register selfstate sender: %s", err.Error()) } } @@ -97,10 +105,7 @@ func (notifier *StandardNotifier) RegisterSender(senderSettings map[string]strin default: senderIdent = senderSettings["type"] } - err := sender.Init(senderSettings, notifier.logger, notifier.config.Location, notifier.config.DateTimeFormat) - if err != nil { - return fmt.Errorf("failed to initialize sender [%s], err [%s]", senderIdent, err.Error()) - } + eventsChannel := make(chan NotificationPackage) notifier.senders[senderIdent] = eventsChannel notifier.metrics.SendersOkMetrics.RegisterMeter(senderIdent, getGraphiteSenderIdent(senderIdent), "sends_ok") diff --git a/senders/discord/init.go b/senders/discord/init.go index d857ada69..82ecdeed1 100644 --- a/senders/discord/init.go +++ b/senders/discord/init.go @@ -16,7 +16,8 @@ const ( workerName = "DiscordBot" ) -// Sender implements moira sender interface for discord +// Sender implements moira sender interface for Discord. +// Use NewSender to create instance. type Sender struct { DataBase moira.Database logger moira.Logger @@ -26,16 +27,20 @@ type Sender struct { botUserID string } -// Init reads the yaml config -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location, db moira.Database) (*Sender, error) { + sender := &Sender{ + DataBase: db, + } + var err error token := senderSettings["token"] if token == "" { - return fmt.Errorf("cannot read the discord token from the config") + return nil, fmt.Errorf("cannot read the discord token from the config") } sender.session, err = discordgo.New("Bot " + token) if err != nil { - return fmt.Errorf("error creating discord session: %s", err) + return nil, fmt.Errorf("error creating discord session: %s", err) } sender.logger = logger sender.frontURI = senderSettings["front_uri"] @@ -56,7 +61,8 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger sender.session.AddHandler(handleMsg) go sender.runBot() - return nil + + return sender, nil } func (sender *Sender) runBot() { diff --git a/senders/discord/init_test.go b/senders/discord/init_test.go index a00315d13..04abfd116 100644 --- a/senders/discord/init_test.go +++ b/senders/discord/init_test.go @@ -18,22 +18,21 @@ type MockLock struct { moira.Lock } -func (lock *MockLock) Acquire(stop <-chan struct{}) (lost <-chan struct{}, error error) { +func (lock *MockLock) Acquire(<-chan struct{}) (lost <-chan struct{}, error error) { return lost, nil } -func (db *MockDB) NewLock(name string, ttl time.Duration) moira.Lock { +func (db *MockDB) NewLock(string, time.Duration) moira.Lock { return &MockLock{} } -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) location, _ := time.LoadLocation("UTC") Convey("Init tests", t, func() { - sender := Sender{DataBase: &MockDB{}} Convey("Empty map", func() { - err := sender.Init(map[string]string{}, logger, nil, "") + sender, err := NewSender(map[string]string{}, logger, nil, &MockDB{}) So(err, ShouldResemble, fmt.Errorf("cannot read the discord token from the config")) - So(sender, ShouldResemble, Sender{DataBase: &MockDB{}}) + So(sender, ShouldBeNil) }) Convey("Has settings", func() { @@ -41,7 +40,8 @@ func TestInit(t *testing.T) { "token": "123", "front_uri": "http://moira.uri", } - sender.Init(senderSettings, logger, location, "15:04") //nolint + sender, err := NewSender(senderSettings, logger, location, &MockDB{}) + So(err, ShouldBeNil) So(sender.frontURI, ShouldResemble, "http://moira.uri") So(sender.session.Token, ShouldResemble, "Bot 123") So(sender.logger, ShouldResemble, logger) diff --git a/senders/mail/mail.go b/senders/mail/mail.go index 55955507e..e206147fe 100644 --- a/senders/mail/mail.go +++ b/senders/mail/mail.go @@ -12,7 +12,8 @@ import ( "github.com/moira-alert/moira" ) -// Sender implements moira sender interface via pushover +// Sender implements moira sender interface via Email. +// Use NewSender to create instance. type Sender struct { From string SMTPHello string @@ -30,18 +31,24 @@ type Sender struct { dateTimeFormat string } -// Init read yaml config -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) (*Sender, error) { + sender := &Sender{} + err := sender.fillSettings(senderSettings, logger, location, dateTimeFormat) if err != nil { - return err + return nil, err } sender.TemplateName, sender.Template, err = parseTemplate(sender.TemplateFile) if err != nil { - return err + return nil, err } err = sender.tryDial() - return err + if err != nil { + return nil, err + } + + return sender, nil } func (sender *Sender) fillSettings(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { diff --git a/senders/msteams/msteams.go b/senders/msteams/msteams.go index 2f522e382..40c135c9b 100644 --- a/senders/msteams/msteams.go +++ b/senders/msteams/msteams.go @@ -35,7 +35,8 @@ var headers = map[string]string{ "Content-Type": "application/json", } -// Sender implements moira sender interface via MS Teams +// Sender implements moira sender interface via MS Teams. +// Use NewSender to create instance. type Sender struct { frontURI string maxEvents int @@ -44,24 +45,27 @@ type Sender struct { client *http.Client } -// Init initialises settings required for full functionality -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location) (*Sender, error) { + sender := &Sender{} + sender.logger = logger sender.location = location sender.frontURI = senderSettings["front_uri"] maxEvents, err := strconv.Atoi(senderSettings["max_events"]) if err != nil { - return fmt.Errorf("max_events should be an integer: %w", err) + return nil, fmt.Errorf("max_events should be an integer: %w", err) } sender.maxEvents = maxEvents sender.client = &http.Client{ Timeout: time.Duration(30) * time.Second, //nolint } - return nil + + return sender, nil } // SendEvents implements Sender interface Send -func (sender *Sender) SendEvents(events moira.NotificationEvents, contact moira.ContactData, trigger moira.TriggerData, plots [][]byte, throttled bool) error { +func (sender *Sender) SendEvents(events moira.NotificationEvents, contact moira.ContactData, trigger moira.TriggerData, _ [][]byte, throttled bool) error { err := sender.isValidWebhookURL(contact.Value) if err != nil { return err diff --git a/senders/msteams/msteams_test.go b/senders/msteams/msteams_test.go index 15b5beef3..8761f48c6 100644 --- a/senders/msteams/msteams_test.go +++ b/senders/msteams/msteams_test.go @@ -11,20 +11,20 @@ import ( "gopkg.in/h2non/gock.v1" ) -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) Convey("Init tests", t, func() { - sender := Sender{} senderSettings := map[string]string{ "max_events": "-1", } Convey("Empty map should fail", func() { - err := sender.Init(map[string]string{}, logger, nil, "") - So(err, ShouldNotResemble, nil) + sender, err := NewSender(map[string]string{}, logger, nil) + So(sender, ShouldBeNil) + So(err, ShouldNotBeNil) }) Convey("Minimal settings", func() { - err := sender.Init(senderSettings, logger, nil, "") - So(err, ShouldResemble, nil) + sender, err := NewSender(senderSettings, logger, nil) + So(err, ShouldBeNil) So(sender, ShouldNotResemble, Sender{}) So(sender.maxEvents, ShouldResemble, -1) }) @@ -32,12 +32,11 @@ func TestInit(t *testing.T) { } func TestMSTeamsHttpResponse(t *testing.T) { - sender := Sender{} logger, _ := logging.ConfigureLog("stdout", "info", "test", true) location, _ := time.LoadLocation("UTC") - _ = sender.Init(map[string]string{ + senderSettings := map[string]string{ "max_events": "-1", - }, logger, location, "") + } event := moira.NotificationEvent{ TriggerID: "TriggerID", Values: map[string]float64{"t1": 123}, @@ -59,6 +58,9 @@ some other text _italic text_`, } Convey("When HTTP Response", t, func() { + sender, err := NewSender(senderSettings, logger, location) + So(err, ShouldBeNil) + Convey("is 200 and body is '1' there should be no error", func() { defer gock.Off() gock.New("https://outlook.office.com/webhook/foo"). diff --git a/senders/opsgenie/init.go b/senders/opsgenie/init.go index b64fc2d8b..3e81a8cd2 100644 --- a/senders/opsgenie/init.go +++ b/senders/opsgenie/init.go @@ -10,7 +10,8 @@ import ( "github.com/opsgenie/opsgenie-go-sdk-v2/client" ) -// Sender implements the Sender interface for opsgenie +// Sender implements the Sender interface for Opsgenie. +// Use NewSender to create instance. type Sender struct { apiKey string client *alert.Client @@ -23,12 +24,16 @@ type Sender struct { frontURI string } -// Init initializes the opsgenie sender -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location, imageStores map[string]moira.ImageStore) (*Sender, error) { + sender := &Sender{ + ImageStores: imageStores, + } + var ok bool if sender.apiKey, ok = senderSettings["api_key"]; !ok { - return fmt.Errorf("cannot read the api_key from the sender settings") + return nil, fmt.Errorf("cannot read the api_key from the sender settings") } sender.imageStoreID, sender.imageStore, sender.imageStoreConfigured = @@ -39,11 +44,12 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger ApiKey: sender.apiKey, }) if err != nil { - return fmt.Errorf("error while creating opsgenie client: %s", err) + return nil, fmt.Errorf("error while creating opsgenie client: %s", err) } sender.frontURI = senderSettings["front_uri"] sender.logger = logger sender.location = location - return nil + + return sender, nil } diff --git a/senders/opsgenie/init_test.go b/senders/opsgenie/init_test.go index 744390b6f..94aa4375f 100644 --- a/senders/opsgenie/init_test.go +++ b/senders/opsgenie/init_test.go @@ -13,35 +13,36 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) location, _ := time.LoadLocation("UTC") mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) Convey("Init tests", t, func() { - sender := Sender{ImageStores: map[string]moira.ImageStore{ - "s3": imageStore, - }} - Convey("Empty map", func() { - err := sender.Init(map[string]string{}, logger, nil, "") + imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } + sender, err := NewSender(map[string]string{}, logger, nil, imageStores) So(err, ShouldResemble, fmt.Errorf("cannot read the api_key from the sender settings")) - So(sender, ShouldResemble, Sender{ - ImageStores: map[string]moira.ImageStore{ - "s3": imageStore, - }}) + So(sender, ShouldBeNil) }) Convey("Has settings", func() { + imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) imageStore.EXPECT().IsEnabled().Return(true) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } senderSettings := map[string]string{ "api_key": "testkey", "front_uri": "http://moira.uri", "image_store": "s3", } - sender.Init(senderSettings, logger, location, "15:04") //nolint + sender, err := NewSender(senderSettings, logger, location, imageStores) + So(err, ShouldBeNil) So(sender.apiKey, ShouldResemble, "testkey") So(sender.frontURI, ShouldResemble, "http://moira.uri") So(sender.logger, ShouldResemble, logger) @@ -49,27 +50,34 @@ func TestInit(t *testing.T) { }) Convey("Wrong image_store name", func() { + imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } senderSettings := map[string]string{ "front_uri": "http://moira.uri", "api_key": "testkey", "image_store": "s4", } - sender.Init(senderSettings, logger, location, "15:04") //nolint + sender, err := NewSender(senderSettings, logger, location, imageStores) + So(err, ShouldBeNil) So(sender.imageStoreConfigured, ShouldResemble, false) So(sender.imageStore, ShouldResemble, nil) }) Convey("image store not configured", func() { + imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } imageStore.EXPECT().IsEnabled().Return(false) senderSettings := map[string]string{ "api_key": "testkey", "front_uri": "http://moira.uri", "image_store": "s3", } - sender := Sender{ImageStores: map[string]moira.ImageStore{ - "s3": imageStore, - }} - sender.Init(senderSettings, logger, location, "15:04") //nolint + sender, err := NewSender(senderSettings, logger, location, imageStores) + So(err, ShouldBeNil) So(sender.imageStoreConfigured, ShouldResemble, false) So(sender.imageStore, ShouldResemble, nil) }) diff --git a/senders/pagerduty/init.go b/senders/pagerduty/init.go index 3bccc5411..21819fb80 100644 --- a/senders/pagerduty/init.go +++ b/senders/pagerduty/init.go @@ -7,7 +7,8 @@ import ( "github.com/moira-alert/moira/senders" ) -// Sender implements moira sender interface for pagerduty +// Sender implements moira sender interface for PagerDuty. +// Use NewSender to create instance. type Sender struct { ImageStores map[string]moira.ImageStore imageStoreID string @@ -18,8 +19,12 @@ type Sender struct { location *time.Location } -// Init loads yaml config, configures the pagerduty client -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location, imageStores map[string]moira.ImageStore) *Sender { + sender := &Sender{ + ImageStores: imageStores, + } + sender.frontURI = senderSettings["front_uri"] sender.imageStoreID, sender.imageStore, sender.imageStoreConfigured = @@ -27,5 +32,6 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger sender.logger = logger sender.location = location - return nil + + return sender } diff --git a/senders/pagerduty/init_test.go b/senders/pagerduty/init_test.go index 59c8195c8..e87aaec33 100644 --- a/senders/pagerduty/init_test.go +++ b/senders/pagerduty/init_test.go @@ -12,7 +12,7 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) location, _ := time.LoadLocation("UTC") mockCtrl := gomock.NewController(t) @@ -20,17 +20,16 @@ func TestInit(t *testing.T) { imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) Convey("Init tests", t, func() { - sender := Sender{ImageStores: map[string]moira.ImageStore{ - "s3": imageStore, - }} - Convey("Has settings", func() { imageStore.EXPECT().IsEnabled().Return(true) senderSettings := map[string]string{ "front_uri": "http://moira.uri", "image_store": "s3", } - sender.Init(senderSettings, logger, location, "15:04") //nolint + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } + sender := NewSender(senderSettings, logger, location, imageStores) So(sender.frontURI, ShouldResemble, "http://moira.uri") So(sender.logger, ShouldResemble, logger) So(sender.location, ShouldResemble, location) @@ -42,9 +41,13 @@ func TestInit(t *testing.T) { "front_uri": "http://moira.uri", "image_store": "s4", } - sender.Init(senderSettings, logger, location, "15:04") //nolint - So(sender.imageStoreConfigured, ShouldResemble, false) - So(sender.imageStore, ShouldResemble, nil) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } + sender := NewSender(senderSettings, logger, location, imageStores) + So(sender, ShouldNotBeNil) + So(sender.imageStoreConfigured, ShouldEqual, false) + So(sender.imageStore, ShouldBeNil) }) Convey("image store not configured", func() { imageStore.EXPECT().IsEnabled().Return(false) @@ -52,12 +55,13 @@ func TestInit(t *testing.T) { "front_uri": "http://moira.uri", "image_store": "s3", } - sender := Sender{ImageStores: map[string]moira.ImageStore{ + imageStores := map[string]moira.ImageStore{ "s3": imageStore, - }} - sender.Init(senderSettings, logger, location, "15:04") //nolint - So(sender.imageStoreConfigured, ShouldResemble, false) - So(sender.imageStore, ShouldResemble, nil) + } + sender := NewSender(senderSettings, logger, location, imageStores) + So(sender, ShouldNotBeNil) + So(sender.imageStoreConfigured, ShouldEqual, false) + So(sender.imageStore, ShouldBeNil) }) }) } diff --git a/senders/pushover/pushover.go b/senders/pushover/pushover.go index b8b241128..7c5ffde71 100644 --- a/senders/pushover/pushover.go +++ b/senders/pushover/pushover.go @@ -14,7 +14,8 @@ const printEventsCount int = 5 const titleLimit = 250 const urlLimit = 512 -// Sender implements moira sender interface via pushover +// Sender implements moira sender interface via Pushover. +// Use NewSender to create instance. type Sender struct { logger moira.Logger location *time.Location @@ -24,17 +25,20 @@ type Sender struct { frontURI string } -// Init read yaml config -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location) (*Sender, error) { + sender := &Sender{} + sender.apiToken = senderSettings["api_token"] if sender.apiToken == "" { - return fmt.Errorf("can not read pushover api_token from config") + return nil, fmt.Errorf("can not read pushover api_token from config") } sender.client = pushover.New(sender.apiToken) sender.logger = logger sender.frontURI = senderSettings["front_uri"] sender.location = location - return nil + + return sender, nil } // SendEvents implements pushover build and send message functionality diff --git a/senders/pushover/pushover_test.go b/senders/pushover/pushover_test.go index 8b34d8ab1..b5e492e55 100644 --- a/senders/pushover/pushover_test.go +++ b/senders/pushover/pushover_test.go @@ -12,28 +12,25 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestSender_Init(t *testing.T) { +func TestSender_NewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) Convey("Empty map", t, func() { - sender := Sender{} - err := sender.Init(map[string]string{}, logger, nil, "") + sender, err := NewSender(map[string]string{}, logger, nil) So(err, ShouldResemble, fmt.Errorf("can not read pushover api_token from config")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) Convey("Settings has api_token", t, func() { - sender := Sender{} - err := sender.Init(map[string]string{"api_token": "123"}, logger, nil, "") + sender, err := NewSender(map[string]string{"api_token": "123"}, logger, nil) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{apiToken: "123", client: pushover.New("123"), logger: logger}) + So(sender, ShouldResemble, &Sender{apiToken: "123", client: pushover.New("123"), logger: logger}) }) Convey("Settings has all data", t, func() { - sender := Sender{} location, _ := time.LoadLocation("UTC") - err := sender.Init(map[string]string{"api_token": "123", "front_uri": "321"}, logger, location, "") + sender, err := NewSender(map[string]string{"api_token": "123", "front_uri": "321"}, logger, location) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{apiToken: "123", client: pushover.New("123"), frontURI: "321", logger: logger, location: location}) + So(sender, ShouldResemble, &Sender{apiToken: "123", client: pushover.New("123"), frontURI: "321", logger: logger, location: location}) }) } @@ -176,7 +173,8 @@ func TestMakePushoverMessage(t *testing.T) { Title: "ERROR TriggerName [tag1][tag2] (1)", Message: "02:40: Metric = 123 (OK to ERROR)\n", } - expected.AddAttachment(bytes.NewReader([]byte{1, 0, 1})) //nolint - So(sender.makePushoverMessage(event, trigger, [][]byte{[]byte{1, 0, 1}}, false), ShouldResemble, expected) + err := expected.AddAttachment(bytes.NewReader([]byte{1, 0, 1})) + So(err, ShouldBeNil) + So(sender.makePushoverMessage(event, trigger, [][]byte{{1, 0, 1}}, false), ShouldResemble, expected) }) } diff --git a/senders/script/script.go b/senders/script/script.go index 9065fb656..a0bd4eb6b 100644 --- a/senders/script/script.go +++ b/senders/script/script.go @@ -7,41 +7,44 @@ import ( "os" "os/exec" "strings" - "time" "github.com/moira-alert/moira" ) -// Sender implements moira sender interface via script execution +// Sender implements moira sender interface via script execution. +// Use NewSender to create instance. type Sender struct { exec string logger moira.Logger } -type scriptNotification struct { - Events []moira.NotificationEvent `json:"events"` - Trigger moira.TriggerData `json:"trigger"` - Contact moira.ContactData `json:"contact"` - Throttled bool `json:"throttled"` - Timestamp int64 `json:"timestamp"` -} +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger) (*Sender, error) { + sender := &Sender{} -// Init read yaml config -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { if senderSettings["name"] == "" { - return fmt.Errorf("required name for sender type script") + return nil, fmt.Errorf("required name for sender type script") } _, _, err := parseExec(senderSettings["exec"]) if err != nil { - return err + return nil, err } sender.exec = senderSettings["exec"] sender.logger = logger - return nil + + return sender, nil +} + +type scriptNotification struct { + Events []moira.NotificationEvent `json:"events"` + Trigger moira.TriggerData `json:"trigger"` + Contact moira.ContactData `json:"contact"` + Throttled bool `json:"throttled"` + Timestamp int64 `json:"timestamp"` } // SendEvents implements Sender interface Send -func (sender *Sender) SendEvents(events moira.NotificationEvents, contact moira.ContactData, trigger moira.TriggerData, plots [][]byte, throttled bool) error { +func (sender *Sender) SendEvents(events moira.NotificationEvents, contact moira.ContactData, trigger moira.TriggerData, _ [][]byte, throttled bool) error { scriptFile, args, scriptBody, err := sender.buildCommandData(events, contact, trigger, throttled) if err != nil { return err diff --git a/senders/script/script_test.go b/senders/script/script_test.go index 100897e9a..578deae38 100644 --- a/senders/script/script_test.go +++ b/senders/script/script_test.go @@ -44,36 +44,35 @@ var execStringTestCases = []execStringTestCase{ }, } -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) Convey("Init tests", t, func() { - sender := Sender{} settings := map[string]string{} Convey("Empty map", func() { - err := sender.Init(settings, logger, nil, "") + sender, err := NewSender(settings, logger) So(err, ShouldResemble, fmt.Errorf("required name for sender type script")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) settings["name"] = "script_name" Convey("Empty exec", func() { - err := sender.Init(settings, logger, nil, "") + sender, err := NewSender(settings, logger) So(err, ShouldResemble, fmt.Errorf("file not found")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) Convey("Exec with not exists file", func() { settings["exec"] = "./test_file1" - err := sender.Init(settings, logger, nil, "") + sender, err := NewSender(settings, logger) So(err, ShouldResemble, fmt.Errorf("file ./test_file1 not found")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) Convey("Exec with exists file", func() { settings["exec"] = "script.go" - err := sender.Init(settings, logger, nil, "") + sender, err := NewSender(settings, logger) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{exec: "script.go", logger: logger}) + So(sender, ShouldResemble, &Sender{exec: "script.go", logger: logger}) }) }) } diff --git a/senders/selfstate/selfstate.go b/senders/selfstate/selfstate.go index 79c72fbc8..495af2e3a 100644 --- a/senders/selfstate/selfstate.go +++ b/senders/selfstate/selfstate.go @@ -2,25 +2,30 @@ package selfstate import ( "fmt" - "time" "github.com/moira-alert/moira" ) -// Sender implements moira sender interface via selfstate +// Sender implements moira sender interface via selfstate. +// Use NewSender to create instance. type Sender struct { Database moira.Database logger moira.Logger } -// Init read yaml config -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(logger moira.Logger, db moira.Database) *Sender { + sender := &Sender{ + Database: db, + } + sender.logger = logger - return nil + + return sender } // SendEvents implements Sender interface Send -func (sender *Sender) SendEvents(events moira.NotificationEvents, contact moira.ContactData, trigger moira.TriggerData, plots [][]byte, throttled bool) error { +func (sender *Sender) SendEvents(events moira.NotificationEvents, _ moira.ContactData, trigger moira.TriggerData, _ [][]byte, _ bool) error { selfState, err := sender.Database.GetNotifierState() if err != nil { return fmt.Errorf("failed to get notifier state: %s", err.Error()) diff --git a/senders/slack/slack.go b/senders/slack/slack.go index 9940b2e46..d270dca55 100644 --- a/senders/slack/slack.go +++ b/senders/slack/slack.go @@ -40,7 +40,8 @@ var stateEmoji = map[moira.State]string{ moira.StateTEST: testEmoji, } -// Sender implements moira sender interface via slack +// Sender implements moira sender interface via Slack. +// Use NewSender to create instance. type Sender struct { frontURI string useEmoji bool @@ -49,18 +50,21 @@ type Sender struct { client *slack.Client } -// Init read yaml config -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location) (*Sender, error) { + sender := &Sender{} + apiToken := senderSettings["api_token"] if apiToken == "" { - return fmt.Errorf("can not read slack api_token from config") + return nil, fmt.Errorf("can not read slack api_token from config") } sender.useEmoji, _ = strconv.ParseBool(senderSettings["use_emoji"]) sender.logger = logger sender.frontURI = senderSettings["front_uri"] sender.location = location sender.client = slack.New(apiToken) - return nil + + return sender, nil } // SendEvents implements Sender interface Send diff --git a/senders/slack/slack_test.go b/senders/slack/slack_test.go index e8b48672f..181c921ae 100644 --- a/senders/slack/slack_test.go +++ b/senders/slack/slack_test.go @@ -12,15 +12,14 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) Convey("Init tests", t, func() { - sender := Sender{} senderSettings := map[string]string{} Convey("Empty map", func() { - err := sender.Init(senderSettings, logger, nil, "") + sender, err := NewSender(senderSettings, logger, nil) So(err, ShouldResemble, fmt.Errorf("can not read slack api_token from config")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) Convey("has api_token", func() { @@ -28,30 +27,30 @@ func TestInit(t *testing.T) { client := slack.New("123") Convey("use_emoji not set", func() { - err := sender.Init(senderSettings, logger, nil, "") + sender, err := NewSender(senderSettings, logger, nil) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{logger: logger, client: client}) + So(sender, ShouldResemble, &Sender{logger: logger, client: client}) }) Convey("use_emoji set to false", func() { senderSettings["use_emoji"] = "false" - err := sender.Init(senderSettings, logger, nil, "") + sender, err := NewSender(senderSettings, logger, nil) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{logger: logger, client: client}) + So(sender, ShouldResemble, &Sender{logger: logger, client: client}) }) Convey("use_emoji set to true", func() { senderSettings["use_emoji"] = "true" - err := sender.Init(senderSettings, logger, nil, "") + sender, err := NewSender(senderSettings, logger, nil) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{logger: logger, useEmoji: true, client: client}) + So(sender, ShouldResemble, &Sender{logger: logger, useEmoji: true, client: client}) }) Convey("use_emoji set to something wrong", func() { senderSettings["use_emoji"] = "123" - err := sender.Init(senderSettings, logger, nil, "") + sender, err := NewSender(senderSettings, logger, nil) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{logger: logger, useEmoji: false, client: client}) + So(sender, ShouldResemble, &Sender{logger: logger, useEmoji: false, client: client}) }) }) }) diff --git a/senders/telegram/init.go b/senders/telegram/init.go index a2f6fb9e3..9dd437b0c 100644 --- a/senders/telegram/init.go +++ b/senders/telegram/init.go @@ -37,11 +37,15 @@ type Sender struct { location *time.Location } -// Init loads yaml config, configures and starts telegram bot -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance, configures and starts Telegram bot. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location, db moira.Database) (*Sender, error) { + sender := &Sender{ + DataBase: db, + } + apiToken := senderSettings["api_token"] if apiToken == "" { - return fmt.Errorf("can not read telegram api_token from config") + return nil, fmt.Errorf("can not read telegram api_token from config") } sender.apiToken = apiToken @@ -54,7 +58,7 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger Poller: &telebot.LongPoller{Timeout: pollerTimeout}, }) if err != nil { - return err + return nil, err } sender.bot.Handle(telebot.OnText, func(message *telebot.Message) { @@ -63,7 +67,8 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger } }) go sender.runTelebot() - return nil + + return sender, nil } // runTelebot starts telegram bot and manages bot subscriptions diff --git a/senders/telegram/init_test.go b/senders/telegram/init_test.go index 24c8aac3f..ae645ce62 100644 --- a/senders/telegram/init_test.go +++ b/senders/telegram/init_test.go @@ -9,27 +9,24 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) location, _ := time.LoadLocation("UTC") Convey("Init tests", t, func() { - sender := Sender{} Convey("Empty map", func() { - err := sender.Init(map[string]string{}, logger, nil, "") + sender, err := NewSender(map[string]string{}, logger, nil, nil) So(err, ShouldResemble, fmt.Errorf("can not read telegram api_token from config")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) - Convey("Has settings", func() { + Convey("Incorrect api token", func() { senderSettings := map[string]string{ "api_token": "123", "front_uri": "http://moira.uri", } - sender.Init(senderSettings, logger, location, "15:04") //nolint - So(sender.apiToken, ShouldResemble, "123") - So(sender.frontURI, ShouldResemble, "http://moira.uri") - So(sender.logger, ShouldResemble, logger) - So(sender.location, ShouldResemble, location) + sender, err := NewSender(senderSettings, logger, location, nil) + So(sender, ShouldBeNil) + So(err, ShouldNotBeNil) }) }) } diff --git a/senders/twilio/twilio.go b/senders/twilio/twilio.go index bfff2ab38..b624dc3af 100644 --- a/senders/twilio/twilio.go +++ b/senders/twilio/twilio.go @@ -8,39 +8,31 @@ import ( "github.com/moira-alert/moira" ) -// Sender implements moira sender interface via twilio +// Sender implements moira sender interface via Twilio. +// Use NewSender to create instance. type Sender struct { sender sendEventsTwilio } -type sendEventsTwilio interface { - SendEvents(events moira.NotificationEvents, contact moira.ContactData, trigger moira.TriggerData, plots [][]byte, throttled bool) error -} +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location) (*Sender, error) { + sender := &Sender{} -type twilioSender struct { - client *twilio.TwilioClient - APIFromPhone string - logger moira.Logger - location *time.Location -} - -// Init read yaml config -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { apiType := senderSettings["type"] apiASID := senderSettings["api_asid"] if apiASID == "" { - return fmt.Errorf("can not read [%s] api_sid param from config", apiType) + return nil, fmt.Errorf("can not read [%s] api_sid param from config", apiType) } apiAuthToken := senderSettings["api_authtoken"] if apiAuthToken == "" { - return fmt.Errorf("can not read [%s] api_authtoken param from config", apiType) + return nil, fmt.Errorf("can not read [%s] api_authtoken param from config", apiType) } apiFromPhone := senderSettings["api_fromphone"] if apiFromPhone == "" { - return fmt.Errorf("can not read [%s] api_fromphone param from config", apiType) + return nil, fmt.Errorf("can not read [%s] api_fromphone param from config", apiType) } twilioClient := twilio.NewClient(apiASID, apiAuthToken) @@ -61,7 +53,7 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger voiceURL := senderSettings["voiceurl"] if voiceURL == "" && !twimletsEcho { - return fmt.Errorf("can not read [%s] voiceurl param from config", apiType) + return nil, fmt.Errorf("can not read [%s] voiceurl param from config", apiType) } sender.sender = &twilioSenderVoice{ @@ -72,10 +64,21 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger } default: - return fmt.Errorf("wrong twilio type: %s", apiType) + return nil, fmt.Errorf("wrong twilio type: %s", apiType) } - return nil + return sender, nil +} + +type sendEventsTwilio interface { + SendEvents(events moira.NotificationEvents, contact moira.ContactData, trigger moira.TriggerData, plots [][]byte, throttled bool) error +} + +type twilioSender struct { + client *twilio.TwilioClient + APIFromPhone string + logger moira.Logger + location *time.Location } // SendEvents implements Sender interface Send diff --git a/senders/twilio/twilio_test.go b/senders/twilio/twilio_test.go index 8d886a788..517ef41fa 100644 --- a/senders/twilio/twilio_test.go +++ b/senders/twilio/twilio_test.go @@ -10,47 +10,46 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { Convey("Tests init twilio sender", t, func() { - sender := Sender{} logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) location, _ := time.LoadLocation("UTC") settings := map[string]string{} Convey("no api asid", func() { - err := sender.Init(settings, logger, nil, "15:04") + sender, err := NewSender(settings, logger, nil) So(err, ShouldResemble, fmt.Errorf("can not read [%s] api_sid param from config", "")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) settings["api_asid"] = "123" Convey("no api authtoken", func() { - err := sender.Init(settings, logger, nil, "15:04") + sender, err := NewSender(settings, logger, nil) So(err, ShouldResemble, fmt.Errorf("can not read [%s] api_authtoken param from config", "")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) settings["api_authtoken"] = "321" Convey("no api fromphone", func() { - err := sender.Init(settings, logger, nil, "15:04") + sender, err := NewSender(settings, logger, nil) So(err, ShouldResemble, fmt.Errorf("can not read [%s] api_fromphone param from config", "")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) settings["api_fromphone"] = "12345678989" Convey("no api type", func() { - err := sender.Init(settings, logger, nil, "15:04") + sender, err := NewSender(settings, logger, nil) So(err, ShouldResemble, fmt.Errorf("wrong twilio type: %s", "")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) Convey("config sms", func() { settings["type"] = "twilio sms" - err := sender.Init(settings, logger, location, "15:04") + sender, err := NewSender(settings, logger, location) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{sender: &twilioSenderSms{ + So(sender, ShouldResemble, &Sender{sender: &twilioSenderSms{ twilioSender{ client: twilio.NewClient("123", "321"), APIFromPhone: "12345678989", @@ -63,18 +62,18 @@ func TestInit(t *testing.T) { Convey("config voice", func() { settings["type"] = "twilio voice" Convey("no voice url", func() { - err := sender.Init(settings, logger, location, "15:04") + sender, err := NewSender(settings, logger, nil) So(err, ShouldResemble, fmt.Errorf("can not read [%s] voiceurl param from config", "twilio voice")) - So(sender, ShouldResemble, Sender{}) + So(sender, ShouldBeNil) }) Convey("has voice url", func() { settings["voiceurl"] = "url here" Convey("append_message == true", func() { settings["append_message"] = "true" - err := sender.Init(settings, logger, location, "15:04") + sender, err := NewSender(settings, logger, location) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{sender: &twilioSenderVoice{ + So(sender, ShouldResemble, &Sender{sender: &twilioSenderVoice{ twilioSender: twilioSender{ client: twilio.NewClient("123", "321"), APIFromPhone: "12345678989", @@ -88,9 +87,9 @@ func TestInit(t *testing.T) { Convey("append_message is something another string", func() { settings["append_message"] = "something another string" - err := sender.Init(settings, logger, location, "15:04") + sender, err := NewSender(settings, logger, location) So(err, ShouldBeNil) - So(sender, ShouldResemble, Sender{sender: &twilioSenderVoice{ + So(sender, ShouldResemble, &Sender{sender: &twilioSenderVoice{ twilioSender: twilioSender{ client: twilio.NewClient("123", "321"), APIFromPhone: "12345678989", diff --git a/senders/victorops/init.go b/senders/victorops/init.go index 7c7b7c2eb..101856c14 100644 --- a/senders/victorops/init.go +++ b/senders/victorops/init.go @@ -9,7 +9,8 @@ import ( "github.com/moira-alert/moira" ) -// Sender implements moira sender interface for victorops +// Sender implements moira sender interface for VictorOps. +// Use NewSender to create instance. type Sender struct { DataBase moira.Database ImageStores map[string]moira.ImageStore @@ -24,12 +25,16 @@ type Sender struct { client *api.Client } -// Init loads yaml config, configures the victorops sender -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger, location *time.Location, imageStores map[string]moira.ImageStore) (*Sender, error) { + sender := &Sender{ + ImageStores: imageStores, + } + var ok bool sender.routingURL, ok = senderSettings["routing_url"] if !ok { - return fmt.Errorf("cannot read the routing url from the yaml config") + return nil, fmt.Errorf("cannot read the routing url from the yaml config") } sender.imageStoreID, ok = senderSettings["image_store"] @@ -50,5 +55,5 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger sender.logger = logger sender.location = location - return nil + return sender, nil } diff --git a/senders/victorops/init_test.go b/senders/victorops/init_test.go index b3f0d5b3c..6a0b7de79 100644 --- a/senders/victorops/init_test.go +++ b/senders/victorops/init_test.go @@ -14,61 +14,72 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestInit(t *testing.T) { +func TestNewSender(t *testing.T) { logger, _ := logging.ConfigureLog("stdout", "debug", "test", true) location, _ := time.LoadLocation("UTC") mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() - imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) Convey("Init tests", t, func() { - sender := Sender{ImageStores: map[string]moira.ImageStore{ - "s3": imageStore, - }} Convey("Empty map", func() { - err := sender.Init(map[string]string{}, logger, nil, "") + imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } + sender, err := NewSender(map[string]string{}, logger, nil, imageStores) So(err, ShouldResemble, fmt.Errorf("cannot read the routing url from the yaml config")) - So(sender, ShouldResemble, Sender{ - ImageStores: map[string]moira.ImageStore{ - "s3": imageStore, - }}) + So(sender, ShouldBeNil) }) Convey("Has settings", func() { + imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) imageStore.EXPECT().IsEnabled().Return(true) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } senderSettings := map[string]string{ "routing_url": "https://testurl.com", "front_uri": "http://moira.uri", "image_store": "s3", } - sender.Init(senderSettings, logger, location, "15:04") //nolint + sender, err := NewSender(senderSettings, logger, location, imageStores) + So(err, ShouldBeNil) So(sender.routingURL, ShouldResemble, "https://testurl.com") So(sender.frontURI, ShouldResemble, "http://moira.uri") So(sender.logger, ShouldResemble, logger) So(sender.location, ShouldResemble, location) So(sender.client, ShouldResemble, api.NewClient("https://testurl.com", nil)) }) + Convey("Wrong image_store name", func() { + imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } senderSettings := map[string]string{ "front_uri": "http://moira.uri", "routing_url": "https://testurl.com", "image_store": "s4", } - sender.Init(senderSettings, logger, location, "15:04") //nolint + sender, err := NewSender(senderSettings, logger, location, imageStores) + So(err, ShouldBeNil) So(sender.imageStoreConfigured, ShouldResemble, false) So(sender.imageStore, ShouldResemble, nil) }) + Convey("image store not configured", func() { + imageStore := mock_moira_alert.NewMockImageStore(mockCtrl) imageStore.EXPECT().IsEnabled().Return(false) + imageStores := map[string]moira.ImageStore{ + "s3": imageStore, + } senderSettings := map[string]string{ "front_uri": "http://moira.uri", "routing_url": "https://testurl.com", "image_store": "s3", } - sender := Sender{ImageStores: map[string]moira.ImageStore{ - "s3": imageStore, - }} - sender.Init(senderSettings, logger, location, "15:04") //nolint + sender, err := NewSender(senderSettings, logger, location, imageStores) + So(err, ShouldBeNil) So(sender.imageStoreConfigured, ShouldResemble, false) So(sender.imageStore, ShouldResemble, nil) }) diff --git a/senders/webhook/webhook.go b/senders/webhook/webhook.go index dc60ce817..9c010744f 100644 --- a/senders/webhook/webhook.go +++ b/senders/webhook/webhook.go @@ -10,7 +10,8 @@ import ( "github.com/moira-alert/moira" ) -// Sender implements moira sender interface via webhook +// Sender implements moira sender interface via Webhook. +// Use NewSender to create instance. type Sender struct { url string user string @@ -20,15 +21,17 @@ type Sender struct { log moira.Logger } -// Init read yaml config -func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger, location *time.Location, dateTimeFormat string) error { +// NewSender creates Sender instance. +func NewSender(senderSettings map[string]string, logger moira.Logger) (*Sender, error) { + sender := &Sender{} + if senderSettings["name"] == "" { - return fmt.Errorf("required name for sender type webhook") + return nil, fmt.Errorf("required name for sender type webhook") } sender.url = senderSettings["url"] if sender.url == "" { - return fmt.Errorf("can not read url from config") + return nil, fmt.Errorf("can not read url from config") } sender.user, sender.password = senderSettings["user"], senderSettings["password"] @@ -43,7 +46,7 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger var err error timeout, err = strconv.Atoi(timeoutRaw) if err != nil { - return fmt.Errorf("can not read timeout from config: %s", err.Error()) + return nil, fmt.Errorf("can not read timeout from config: %s", err.Error()) } } @@ -52,7 +55,8 @@ func (sender *Sender) Init(senderSettings map[string]string, logger moira.Logger Timeout: time.Duration(timeout) * time.Second, Transport: &http.Transport{DisableKeepAlives: true}, } - return nil + + return sender, nil } // SendEvents implements Sender interface Send diff --git a/senders/webhook/webhook_test.go b/senders/webhook/webhook_test.go index 9918711e9..3e6c2a7d0 100644 --- a/senders/webhook/webhook_test.go +++ b/senders/webhook/webhook_test.go @@ -10,10 +10,8 @@ import ( "net/url" "strings" "testing" - "time" "github.com/moira-alert/moira" - logging "github.com/moira-alert/moira/logging/zerolog_adapter" . "github.com/smartystreets/goconvey/convey" ) @@ -33,16 +31,19 @@ func TestSender_SendEvents(t *testing.T) { status, err := testRequestURL(r) if err != nil { w.WriteHeader(status) - w.Write([]byte(err.Error())) //nolint + _, err = w.Write([]byte(err.Error())) + So(err, ShouldBeNil) } status, err = testRequestHeaders(r) if err != nil { w.WriteHeader(status) - w.Write([]byte(err.Error())) //nolint + _, err = w.Write([]byte(err.Error())) + So(err, ShouldBeNil) } status, err = testRequestBody(r) if err != nil { - w.Write([]byte(err.Error())) //nolint + _, err = w.Write([]byte(err.Error())) //nolint + So(err, ShouldBeNil) w.WriteHeader(status) } w.WriteHeader(status) @@ -57,8 +58,8 @@ func TestSender_SendEvents(t *testing.T) { "user": testUser, "password": testPass, } - sender := Sender{} - err := sender.Init(senderSettings, logger, time.UTC, "") + + sender, err := NewSender(senderSettings, logger) So(err, ShouldBeNil) err = sender.SendEvents(testEvents, testContact, testTrigger, testPlot, false)