-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tests: file channel to ease channel testing
First, a new channel was added, printing each received notification event JSON-encoded line-wise into a file or something file-like. This file channel was then used in the newly written TestNotificationRoundTrip integration test to get back events from Icinga 2 over a FIFO between the host running tests and the icinga-notifications Docker container.
- Loading branch information
Showing
7 changed files
with
205 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/icinga/icinga-notifications/internal" | ||
"github.com/icinga/icinga-notifications/pkg/plugin" | ||
"os" | ||
) | ||
|
||
// File resource to line-wise append each plugin.NotificationRequest as its JSON representation. | ||
// | ||
// The File.Path will be created, if not existing. If something file-like - a file, a fifo(7), .. - exists, it will be | ||
// appended to. | ||
type File struct { | ||
Path string `json:"path"` | ||
|
||
encoder *json.Encoder `json:"-"` | ||
} | ||
|
||
func main() { | ||
plugin.RunPlugin(&File{}) | ||
} | ||
|
||
func (ch *File) SendNotification(req *plugin.NotificationRequest) error { | ||
return ch.encoder.Encode(req) | ||
} | ||
|
||
func (ch *File) SetConfig(jsonStr json.RawMessage) error { | ||
err := json.Unmarshal(jsonStr, ch) | ||
if err != nil { | ||
return fmt.Errorf("failed to load config: %s %w", jsonStr, err) | ||
} | ||
|
||
if ch.Path == "" { | ||
return fmt.Errorf("the path attribute must be set") | ||
} | ||
|
||
if ch.Path == "" { | ||
return fmt.Errorf("file path is empty") | ||
} | ||
|
||
f, err := os.OpenFile(ch.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) | ||
if err != nil { | ||
return fmt.Errorf("cannot open file path %s: %w", ch.Path, err) | ||
} | ||
|
||
ch.encoder = json.NewEncoder(f) | ||
ch.encoder.SetEscapeHTML(false) | ||
|
||
return nil | ||
} | ||
|
||
func (ch *File) GetInfo() *plugin.Info { | ||
elements := []*plugin.ConfigOption{ | ||
{ | ||
Name: "path", | ||
Type: "string", | ||
Label: map[string]string{ | ||
"en_US": "File Path", | ||
"de_DE": "Dateipfad", | ||
}, | ||
}, | ||
} | ||
|
||
configAttrs, err := json.Marshal(elements) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return &plugin.Info{ | ||
Name: "File", | ||
Version: internal.Version.Version, | ||
Author: "Icinga GmbH", | ||
ConfigAttributes: configAttrs, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package notifications_test | ||
|
||
import ( | ||
"encoding/json" | ||
"github.com/icinga/icinga-notifications/pkg/plugin" | ||
"github.com/icinga/icinga-testing/utils/eventually" | ||
"github.com/jmoiron/sqlx" | ||
"github.com/stretchr/testify/require" | ||
"golang.org/x/sys/unix" | ||
"os" | ||
"testing" | ||
"time" | ||
) | ||
|
||
// TestNotificationRoundTrip instructs an Icinga 2 node to send a notification back for further inspection. | ||
func TestNotificationRoundTrip(t *testing.T) { | ||
fileChFifo := "./tmp/file-fifo" | ||
require.NoError(t, unix.Mkfifo(fileChFifo, 0666), "mkfifo") | ||
require.NoError(t, os.Chmod(fileChFifo, 0666), "chmod") // mkfifo includes umask | ||
|
||
rdb := getDatabase(t) | ||
notifications := it.IcingaNotificationsInstanceT(t, rdb) | ||
icinga := it.Icinga2NodeT(t, "master") | ||
icinga.EnableIcingaNotifications(notifications) | ||
require.NoError(t, icinga.Reload(), "icinga.Reload()") | ||
|
||
db, err := sqlx.Open(rdb.Driver(), rdb.DSN()) | ||
require.NoError(t, err, "SQL database open") | ||
defer func() { require.NoError(t, db.Close(), "db.Close") }() | ||
|
||
t.Run("configure channel in database", func(t *testing.T) { | ||
eventually.Require(t, func(t require.TestingT) { | ||
var channelCount int | ||
err := db.QueryRow(`SELECT COUNT(*) FROM available_channel_type WHERE type = 'file'`).Scan(&channelCount) | ||
require.NoError(t, err, "SQL SELECT FROM available_channel_type query") | ||
require.Equal(t, channelCount, 1, "file type missing from available_channel_type") | ||
}, 10*time.Second, time.Second) | ||
|
||
_, err := db.Exec(` | ||
INSERT INTO channel (id, name, type, config) | ||
VALUES (1, 'file-fifo', 'file', '{"path":"\/shared\/file-fifo"}'); | ||
INSERT INTO contact (id, full_name, username, default_channel_id, color) | ||
VALUES (1, 'icingaadmin', 'icingaadmin', 1, '#000000'); | ||
INSERT INTO rule (id, name, is_active) VALUES (1, 'file-fifo', 'y'); | ||
INSERT INTO rule_escalation (id, rule_id, position) VALUES (1, 1, 1); | ||
INSERT INTO rule_escalation_recipient (id, rule_escalation_id, contact_id, channel_id) VALUES (1, 1, 1, 1);`) | ||
require.NoError(t, err, "populating tables failed") | ||
}) | ||
|
||
t.Run("create icinga objects", func(t *testing.T) { | ||
client := icinga.ApiClient() | ||
client.CreateObject(t, "checkcommands", "failure-check", map[string]any{ | ||
"templates": []any{"plugin-check-command"}, | ||
"attrs": map[string]any{"command": []string{"/bin/false"}}, | ||
}) | ||
client.CreateHost(t, "test-host", map[string]any{ | ||
"attrs": map[string]any{"check_command": "failure-check"}, | ||
}) | ||
client.CreateService(t, "test-host", "test-service", map[string]any{ | ||
"attrs": map[string]any{"check_command": "failure-check"}, | ||
}) | ||
}) | ||
|
||
t.Run("read notification back from channel", func(t *testing.T) { | ||
f, err := os.Open(fileChFifo) | ||
require.NoErrorf(t, err, "opening %s for reading", fileChFifo) | ||
defer f.Close() | ||
|
||
notificationReqCh := make(chan *plugin.NotificationRequest) | ||
go func() { | ||
var req plugin.NotificationRequest | ||
require.NoError(t, json.NewDecoder(f).Decode(&req), "JSON decoding") | ||
notificationReqCh <- &req | ||
}() | ||
|
||
select { | ||
case req := <-notificationReqCh: | ||
require.Contains(t, req.Object.Name, "test-", "name must contain test prefix") | ||
|
||
case <-time.After(5 * time.Minute): | ||
require.Fail(t, "no notification was received") | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters