Skip to content

Commit 440e6a4

Browse files
committed
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.
1 parent 0b60bc7 commit 440e6a4

File tree

7 files changed

+199
-54
lines changed

7 files changed

+199
-54
lines changed

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ FROM docker.io/library/alpine
2020
COPY --from=build /src/icinga-notifications/bin/icinga-notifications-daemon /usr/bin/icinga-notifications-daemon
2121
COPY --from=build /src/icinga-notifications/bin/channel /usr/libexec/icinga-notifications/channel
2222

23+
RUN mkdir /etc/icinga-notifications/
24+
COPY config.example.yml /etc/icinga-notifications/config.yml
25+
2326
RUN apk add tzdata
2427

2528
ARG username=notifications

cmd/channel/file/main.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"github.com/icinga/icinga-notifications/internal"
7+
"github.com/icinga/icinga-notifications/pkg/plugin"
8+
"os"
9+
)
10+
11+
// File resource to line-wise append each plugin.NotificationRequest as its JSON representation.
12+
//
13+
// The File.Path will be created, if not existing. If something file-like - a file, a fifo(7), .. - exists, it will be
14+
// appended to.
15+
type File struct {
16+
Path string `json:"path"`
17+
18+
encoder *json.Encoder `json:"-"`
19+
}
20+
21+
func main() {
22+
plugin.RunPlugin(&File{})
23+
}
24+
25+
func (ch *File) SendNotification(req *plugin.NotificationRequest) error {
26+
return ch.encoder.Encode(req)
27+
}
28+
29+
func (ch *File) SetConfig(jsonStr json.RawMessage) error {
30+
err := json.Unmarshal(jsonStr, ch)
31+
if err != nil {
32+
return fmt.Errorf("failed to load config: %s %w", jsonStr, err)
33+
}
34+
35+
if ch.Path == "" {
36+
return fmt.Errorf("the path attribute must be set")
37+
}
38+
39+
if ch.Path == "" {
40+
return fmt.Errorf("file path is empty")
41+
}
42+
43+
f, err := os.OpenFile(ch.Path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
44+
if err != nil {
45+
return fmt.Errorf("cannot open file path %s: %w", ch.Path, err)
46+
}
47+
48+
ch.encoder = json.NewEncoder(f)
49+
ch.encoder.SetEscapeHTML(false)
50+
51+
return nil
52+
}
53+
54+
func (ch *File) GetInfo() *plugin.Info {
55+
elements := []*plugin.ConfigOption{
56+
{
57+
Name: "path",
58+
Type: "string",
59+
Label: map[string]string{
60+
"en_US": "File Path",
61+
"de_DE": "Dateipfad",
62+
},
63+
},
64+
}
65+
66+
configAttrs, err := json.Marshal(elements)
67+
if err != nil {
68+
panic(err)
69+
}
70+
71+
return &plugin.Info{
72+
Name: "File",
73+
Version: internal.Version.Version,
74+
Author: "Icinga GmbH",
75+
ConfigAttributes: configAttrs,
76+
}
77+
}

tests/go.mod

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ module github.com/icinga/icinga-notifications/tests
22

33
go 1.21
44

5-
replace github.com/icinga/icinga-testing => github.com/oxzi/icinga-testing v0.0.0-20231220141937-686d5c9faef9
5+
replace (
6+
github.com/icinga/icinga-notifications => ../
7+
github.com/icinga/icinga-testing => github.com/oxzi/icinga-testing v0.0.0-20240103162208-091308cd619c
8+
)
69

710
require (
11+
github.com/icinga/icinga-notifications v0.0.0-20240102102116-0d6f7271c116
812
github.com/icinga/icinga-testing v0.0.0-20220516144008-9600081b7a69
913
github.com/jmoiron/sqlx v1.3.5
1014
github.com/stretchr/testify v1.8.4
15+
golang.org/x/sys v0.15.0
1116
)
1217

1318
require (
@@ -24,17 +29,19 @@ require (
2429
github.com/go-redis/redis/v8 v8.11.5 // indirect
2530
github.com/go-sql-driver/mysql v1.7.1 // indirect
2631
github.com/gogo/protobuf v1.3.2 // indirect
32+
github.com/google/uuid v1.3.0 // indirect
33+
github.com/icinga/icingadb v1.1.1-0.20230418113126-7c4b947aad3a // indirect
2734
github.com/lib/pq v1.10.9 // indirect
2835
github.com/opencontainers/go-digest v1.0.0 // indirect
2936
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
3037
github.com/pkg/errors v0.9.1 // indirect
3138
github.com/pmezard/go-difflib v1.0.0 // indirect
3239
go.uber.org/multierr v1.11.0 // indirect
3340
go.uber.org/zap v1.26.0 // indirect
41+
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d // indirect
3442
golang.org/x/mod v0.14.0 // indirect
3543
golang.org/x/net v0.19.0 // indirect
3644
golang.org/x/sync v0.5.0 // indirect
37-
golang.org/x/sys v0.15.0 // indirect
3845
golang.org/x/tools v0.16.1 // indirect
3946
gopkg.in/yaml.v3 v3.0.1 // indirect
4047
)

tests/go.sum

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,12 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
6060
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
6161
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
6262
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
63+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
64+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
6365
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
6466
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
67+
github.com/icinga/icingadb v1.1.1-0.20230418113126-7c4b947aad3a h1:NfVdBKa4dhPk7IU8u0MOF6ywi0LDpMkQMGs1j803+3c=
68+
github.com/icinga/icingadb v1.1.1-0.20230418113126-7c4b947aad3a/go.mod h1:zamCKaKn4JJQinctcUyewTSNNXDfpLc0HSbqb+9lTYs=
6569
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
6670
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
6771
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -79,8 +83,9 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
7983
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
8084
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
8185
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
82-
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
8386
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
87+
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
88+
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
8489
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae h1:O4SWKdcHVCvYqyDV+9CJA1fcDN2L11Bule0iFy3YlAI=
8590
github.com/moby/term v0.0.0-20220808134915-39b0c02b01ae/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=
8691
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -117,8 +122,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
117122
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
118123
github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
119124
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
120-
github.com/oxzi/icinga-testing v0.0.0-20231220141937-686d5c9faef9 h1:+XijgwcZ3MwRxuJ1v/fK/qILzvNKK2GWlcjo4uTPerg=
121-
github.com/oxzi/icinga-testing v0.0.0-20231220141937-686d5c9faef9/go.mod h1:4xtpVSat274WBBuOF7gDkFhE4OtsrHpmJpL1qMU1vsw=
125+
github.com/oxzi/icinga-testing v0.0.0-20240103162208-091308cd619c h1:+RwoI1rFT11O1fiMqnPlIOHXLZDOO2bu7ZSPpwNPik0=
126+
github.com/oxzi/icinga-testing v0.0.0-20240103162208-091308cd619c/go.mod h1:4xtpVSat274WBBuOF7gDkFhE4OtsrHpmJpL1qMU1vsw=
122127
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
123128
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
124129
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -166,6 +171,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
166171
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
167172
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
168173
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
174+
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d h1:vtUKgx8dahOomfFzLREU8nSv25YHnTgLBn4rDnWZdU0=
175+
golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
169176
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
170177
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
171178
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=

tests/main_test.go

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
package icingadb_test
1+
package notifications_test
22

33
import (
44
"github.com/icinga/icinga-testing"
55
"github.com/icinga/icinga-testing/services"
66
"github.com/jmoiron/sqlx"
77
"github.com/stretchr/testify/require"
88
"testing"
9-
"time"
109
)
1110

1211
var it *icingatesting.IT
@@ -24,14 +23,12 @@ func getDatabase(t testing.TB) services.RelationalDatabase {
2423

2524
db, err := sqlx.Open(rdb.Driver(), rdb.DSN())
2625
require.NoError(t, err, "SQL database open")
27-
defer db.Close()
26+
defer func() { _ = db.Close() }()
2827

29-
rows, err := db.Query(`
28+
_, err = db.Exec(`
3029
INSERT INTO source (id, type, name, listener_password_hash)
31-
VALUES (1, 'icinga2', 'Icinga 2', '$2y$10$QU8bJ7cpW1SmoVQ/RndX5O2J5L1PJF7NZ2dlIW7Rv3zUEcbUFg3z2')
32-
`)
33-
require.NoError(t, err, "SQL population query")
34-
_ = rows.Close()
30+
VALUES (1, 'icinga2', 'Icinga 2', '$2y$10$QU8bJ7cpW1SmoVQ/RndX5O2J5L1PJF7NZ2dlIW7Rv3zUEcbUFg3z2')`)
31+
require.NoError(t, err, "populating source table failed")
3532

3633
return rdb
3734
}
@@ -40,28 +37,3 @@ func getEmptyDatabase(t testing.TB) services.RelationalDatabase {
4037
// Currently, PostgreSQL is the only supported database backend.
4138
return it.PostgresqlDatabaseT(t)
4239
}
43-
44-
func TestBasicFunctionality(t *testing.T) {
45-
rdb := getDatabase(t)
46-
notifications := it.IcingaNotificationsInstanceT(t, rdb)
47-
icinga := it.Icinga2NodeT(t, "master")
48-
icinga.EnableIcingaNotifications(notifications)
49-
icinga.Reload()
50-
51-
t.Run("available_channel_type populated", func(t *testing.T) {
52-
db, err := sqlx.Open(rdb.Driver(), rdb.DSN())
53-
require.NoError(t, err, "SQL database open")
54-
defer db.Close()
55-
56-
for i := 0; i < 5; i++ {
57-
var rows int
58-
err := db.QueryRow(`SELECT COUNT(*) FROM available_channel_type`).Scan(&rows)
59-
require.NoError(t, err, "SQL SELECT FROM available_channel_type query")
60-
if rows > 0 {
61-
return
62-
}
63-
time.Sleep(3 * time.Second)
64-
}
65-
require.Fail(t, "available_channel_type table is still empty")
66-
})
67-
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package notifications_test
2+
3+
import (
4+
"encoding/json"
5+
"github.com/icinga/icinga-notifications/pkg/plugin"
6+
"github.com/icinga/icinga-testing/utils/eventually"
7+
"github.com/jmoiron/sqlx"
8+
"github.com/stretchr/testify/require"
9+
"golang.org/x/sys/unix"
10+
"os"
11+
"testing"
12+
"time"
13+
)
14+
15+
// TestNotificationRoundTrip instructs an Icinga 2 node to send a notification back for further inspection.
16+
func TestNotificationRoundTrip(t *testing.T) {
17+
fileChFifo := "./tmp/file-fifo"
18+
require.NoError(t, unix.Mkfifo(fileChFifo, 0666), "mkfifo(3)")
19+
20+
rdb := getDatabase(t)
21+
notifications := it.IcingaNotificationsInstanceT(t, rdb)
22+
icinga := it.Icinga2NodeT(t, "master")
23+
icinga.EnableIcingaNotifications(notifications)
24+
require.NoError(t, icinga.Reload(), "icinga.Reload()")
25+
26+
db, err := sqlx.Open(rdb.Driver(), rdb.DSN())
27+
require.NoError(t, err, "SQL database open")
28+
defer func() { require.NoError(t, db.Close(), "db.Close") }()
29+
30+
t.Run("configure channel in database", func(t *testing.T) {
31+
eventually.Require(t, func(t require.TestingT) {
32+
var channelCount int
33+
err := db.QueryRow(`SELECT COUNT(*) FROM available_channel_type WHERE type = 'file'`).Scan(&channelCount)
34+
require.NoError(t, err, "SQL SELECT FROM available_channel_type query")
35+
require.Equal(t, channelCount, 1, "file type missing from available_channel_type")
36+
}, 10*time.Second, time.Second)
37+
38+
_, err := db.Exec(`
39+
INSERT INTO channel (id, name, type, config)
40+
VALUES (1, 'file-fifo', 'file', '{"path":"\/shared\/file-fifo"}');
41+
42+
INSERT INTO contact (id, full_name, username, default_channel_id, color)
43+
VALUES (1, 'icingaadmin', 'icingaadmin', 1, '#000000');
44+
45+
INSERT INTO rule (id, name, is_active) VALUES (1, 'file-fifo', 'y');
46+
INSERT INTO rule_escalation (id, rule_id, position) VALUES (1, 1, 1);
47+
INSERT INTO rule_escalation_recipient (id, rule_escalation_id, contact_id, channel_id) VALUES (1, 1, 1, 1);`)
48+
require.NoError(t, err, "populating tables failed")
49+
})
50+
51+
t.Run("create icinga objects", func(t *testing.T) {
52+
client := icinga.ApiClient()
53+
client.CreateObject(t, "checkcommands", "failure-check", map[string]any{
54+
"templates": []any{"plugin-check-command"},
55+
"attrs": map[string]any{"command": []string{"/bin/false"}},
56+
})
57+
client.CreateHost(t, "test-host", map[string]any{
58+
"attrs": map[string]any{"check_command": "failure-check"},
59+
})
60+
client.CreateService(t, "test-host", "test-service", map[string]any{
61+
"attrs": map[string]any{"check_command": "failure-check"},
62+
})
63+
})
64+
65+
t.Run("read notification back from channel", func(t *testing.T) {
66+
f, err := os.Open(fileChFifo)
67+
require.NoErrorf(t, err, "opening %s for reading", fileChFifo)
68+
defer f.Close()
69+
70+
notificationReqCh := make(chan *plugin.NotificationRequest)
71+
go func() {
72+
var req plugin.NotificationRequest
73+
require.NoError(t, json.NewDecoder(f).Decode(&req), "JSON decoding")
74+
notificationReqCh <- &req
75+
}()
76+
77+
select {
78+
case req := <-notificationReqCh:
79+
require.Contains(t, req.Object.Name, "test-", "name must contain test prefix")
80+
81+
case <-time.After(5 * time.Minute):
82+
require.Fail(t, "no notification was received")
83+
}
84+
})
85+
}

tests/test.sh

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,17 @@
22

33
set -eux
44

5-
test -d ./tmp && rm -r ./tmp
6-
mkdir -p ./tmp/channel
7-
8-
cd ..
9-
export CGO_ENABLED=0
10-
go build -o ./tests/tmp/icinga-notifications-daemon ./cmd/icinga-notifications-daemon
11-
go build -o ./tests/tmp/channel ./cmd/channel/...
12-
unset CGO_ENABLED
13-
cd tests
14-
15-
go test -o ./tmp/icinga-notifications-test -c .
16-
17-
ICINGA_TESTING_ICINGA_NOTIFICATIONS_BINARY="$(realpath tmp/icinga-notifications-daemon)"
18-
ICINGA_TESTING_ICINGA_NOTIFICATIONS_CHANNEL_DIR="$(realpath tmp/channel)"
5+
ICINGA_TESTING_NOTIFICATIONS_IMAGE="icinga-notifications:latest"
6+
ICINGA_TESTING_ICINGA_NOTIFICATIONS_SHARED_DIR="$(realpath tmp)"
197
ICINGA_TESTING_ICINGA_NOTIFICATIONS_SCHEMA_PGSQL="$(realpath ../schema/pgsql/schema.sql)"
20-
export ICINGA_TESTING_ICINGA_NOTIFICATIONS_BINARY
21-
export ICINGA_TESTING_ICINGA_NOTIFICATIONS_CHANNEL_DIR
8+
export ICINGA_TESTING_NOTIFICATIONS_IMAGE
9+
export ICINGA_TESTING_ICINGA_NOTIFICATIONS_SHARED_DIR
2210
export ICINGA_TESTING_ICINGA_NOTIFICATIONS_SCHEMA_PGSQL
2311

12+
docker build -t "$ICINGA_TESTING_NOTIFICATIONS_IMAGE" ..
13+
14+
test -d ./tmp && rm -r ./tmp
15+
mkdir ./tmp
16+
17+
go test -o ./tmp/icinga-notifications-test -c .
2418
exec ./tmp/icinga-notifications-test -icingatesting.debuglog ./tmp/debug.log -test.v

0 commit comments

Comments
 (0)