Skip to content

Commit 66d105e

Browse files
committed
tests: Simulate vanishing Comments and Downtimes
Ensure that removed Comment and Downtime objects attached to Hosts, vanishing after a config pack redeployment, are correctly closed by Icinga DB.
1 parent 0029aa7 commit 66d105e

1 file changed

Lines changed: 184 additions & 0 deletions

File tree

tests/history_test.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
_ "embed"
77
"encoding/json"
88
"fmt"
9+
"github.com/icinga/icinga-go-library/types"
910
"github.com/icinga/icinga-testing/services"
1011
"github.com/icinga/icinga-testing/utils"
1112
"github.com/icinga/icinga-testing/utils/eventually"
@@ -637,6 +638,189 @@ func testHistory(t *testing.T, numNodes int) {
637638

638639
testConsistency(t, stream)
639640
})
641+
642+
t.Run("UndeployedComment", func(t *testing.T) {
643+
configPack := utils.RandomString(8)
644+
client.CreateConfigPackage(t, configPack)
645+
646+
hostname := utils.UniqueName(t, "host")
647+
client.CreateConfigPackageStage(t, configPack, map[string]string{
648+
"conf.d/h.conf": fmt.Sprintf(`object Host %q { check_command = "dummy" }`, hostname),
649+
})
650+
defer client.DeleteConfigPackage(t, configPack)
651+
652+
// Wait for the new config package stage to be applied.
653+
eventually.Require(t, func(t require.TestingT) {
654+
response, err := client.GetJson("/v1/objects/hosts/" + hostname)
655+
require.NoErrorf(t, err, "fetching host %q should not fail", hostname)
656+
require.Equalf(t, 200, response.StatusCode, "fetching host %q should not fail", hostname)
657+
}, 15*time.Second, 200*time.Millisecond)
658+
659+
author := utils.RandomString(8)
660+
comment := utils.RandomString(8)
661+
req, err := json.Marshal(ActionsAddCommentRequest{
662+
Type: "Host",
663+
Filter: fmt.Sprintf(`host.name==%q`, hostname),
664+
Author: author,
665+
Comment: comment,
666+
})
667+
require.NoError(t, err, "marshal request")
668+
response, err := client.PostJson("/v1/actions/add-comment", bytes.NewBuffer(req))
669+
require.NoError(t, err, "add-comment")
670+
require.Equal(t, 200, response.StatusCode, "add-comment")
671+
672+
var commentId types.Binary
673+
eventually.Require(t, func(t require.TestingT) {
674+
err := db.Get(&commentId, db.Rebind(`
675+
SELECT comment.id
676+
FROM comment
677+
JOIN host ON comment.host_id = host.id
678+
WHERE host.name = ?`),
679+
hostname)
680+
require.NoError(t, err, "select comment_id")
681+
}, 15*time.Second, 200*time.Millisecond)
682+
683+
// Undeploy config pack, let the host vanish.
684+
client.CreateConfigPackageStage(t, configPack, map[string]string{})
685+
686+
eventually.Require(t, func(t require.TestingT) {
687+
// Check for updated comment_history row with has_been_removed = 'y'.
688+
var commentHistoryEvents int
689+
err := db.Get(&commentHistoryEvents, db.Rebind(`
690+
SELECT COUNT(*)
691+
FROM comment_history
692+
WHERE
693+
comment_id = ? AND
694+
has_been_removed = 'y' AND
695+
author = ? AND
696+
comment = ?`),
697+
commentId,
698+
author,
699+
comment)
700+
require.NoError(t, err, "select count(*) comment_history")
701+
assert.Equal(t, 1, commentHistoryEvents, "not exactly one comment_history entry")
702+
703+
// Check if new history row was created.
704+
var historyEvents int
705+
err = db.Get(&historyEvents, db.Rebind(`
706+
SELECT COUNT(*)
707+
FROM history
708+
WHERE comment_history_id = ?`),
709+
commentId)
710+
require.NoError(t, err, "select count(*) history")
711+
assert.Equal(t, 2, historyEvents, "not exactly two history entries")
712+
}, 15*time.Second, 200*time.Millisecond)
713+
})
714+
715+
t.Run("UndeployedDowntime", func(t *testing.T) {
716+
// Compared to the previous UndeployedComment test case, this test let's a configurable amount of
717+
// hosts with assigned downtimes vanish.
718+
const hosts = 1000
719+
720+
configPack := utils.RandomString(8)
721+
client.CreateConfigPackage(t, configPack)
722+
723+
hostnamePrefix := utils.RandomString(4) + "."
724+
hostnames := make([]string, 0, hosts)
725+
hostObjectConf := make(map[string]string)
726+
for range hosts {
727+
hostname := hostnamePrefix + utils.RandomString(8)
728+
hostnames = append(hostnames, hostname)
729+
hostObjectConf[fmt.Sprintf("conf.d/%s.conf", hostname)] = fmt.Sprintf(
730+
`object Host %q { check_command = "dummy" }`, hostname)
731+
}
732+
client.CreateConfigPackageStage(t, configPack, hostObjectConf)
733+
defer client.DeleteConfigPackage(t, configPack)
734+
735+
// Wait for the new config package stage to be applied.
736+
eventually.Require(t, func(t require.TestingT) {
737+
for _, hostname := range hostnames {
738+
response, err := client.GetJson("/v1/objects/hosts/" + hostname)
739+
require.NoErrorf(t, err, "fetching host %q should not fail", hostname)
740+
require.Equalf(t, 200, response.StatusCode, "fetching host %q should not fail", hostname)
741+
}
742+
}, 15*time.Second, 200*time.Millisecond)
743+
744+
downtimeStart := time.Now()
745+
author := utils.RandomString(8)
746+
comment := utils.RandomString(8)
747+
req, err := json.Marshal(ActionsScheduleDowntimeRequest{
748+
Type: "Host",
749+
Filter: fmt.Sprintf(`match("%s*", host.name)`, hostnamePrefix),
750+
StartTime: downtimeStart.Unix(),
751+
EndTime: downtimeStart.Add(time.Hour).Unix(),
752+
Fixed: true,
753+
Author: author,
754+
Comment: comment,
755+
})
756+
require.NoError(t, err, "marshal request")
757+
response, err := client.PostJson("/v1/actions/schedule-downtime", bytes.NewBuffer(req))
758+
require.NoError(t, err, "schedule-downtime")
759+
require.Equal(t, 200, response.StatusCode, "schedule-downtime")
760+
761+
var downtimeIds []types.Binary
762+
eventually.Require(t, func(t require.TestingT) {
763+
downtimeIds = make([]types.Binary, 0, len(hostnames))
764+
for _, hostname := range hostnames {
765+
var downtimeId types.Binary
766+
err := db.Get(&downtimeId, db.Rebind(`
767+
SELECT downtime.id
768+
FROM downtime
769+
JOIN host ON downtime.host_id = host.id
770+
WHERE host.name = ?`),
771+
hostname)
772+
require.NoError(t, err, "select downtime_id")
773+
downtimeIds = append(downtimeIds, downtimeId)
774+
}
775+
}, 15*time.Second, 200*time.Millisecond)
776+
777+
// Undeploy config pack, let the host vanish.
778+
client.CreateConfigPackageStage(t, configPack, map[string]string{})
779+
780+
eventually.Require(t, func(t require.TestingT) {
781+
for _, downtimeId := range downtimeIds {
782+
// Check for updated downtime_history row with has_been_cancelled = 'y'.
783+
var downtimeHistoryEvents int
784+
err := db.Get(&downtimeHistoryEvents, db.Rebind(`
785+
SELECT COUNT(*)
786+
FROM downtime_history
787+
WHERE
788+
downtime_id = ? AND
789+
has_been_cancelled = 'y' AND
790+
author = ? AND
791+
comment = ?`),
792+
downtimeId,
793+
author,
794+
comment)
795+
require.NoError(t, err, "select count(*) downtime_history")
796+
assert.Equal(t, 1, downtimeHistoryEvents, "not exactly one downtime_history entry")
797+
798+
// Check for updated sla_history_downtime row with new downtime_end time.
799+
var slaHistoryDowntimes int
800+
err = db.Get(&slaHistoryDowntimes, db.Rebind(`
801+
SELECT COUNT(*)
802+
FROM sla_history_downtime
803+
WHERE
804+
downtime_id = ? AND
805+
? <= downtime_end AND downtime_end <= ?`),
806+
downtimeId,
807+
time.Now().Add(-time.Minute).UnixMilli(),
808+
time.Now().Add(time.Minute).UnixMilli())
809+
require.NoError(t, err, "select count(*) sla_history_downtime")
810+
assert.Equal(t, 1, slaHistoryDowntimes, "not exactly one sla_history_downtime entry")
811+
812+
// Check if new history row was created.
813+
var historyEvents int
814+
err = db.Get(&historyEvents, db.Rebind(`
815+
SELECT COUNT(*)
816+
FROM history
817+
WHERE downtime_history_id = ?`),
818+
downtimeId)
819+
require.NoError(t, err, "select count(*) history")
820+
assert.Equal(t, 2, historyEvents, "not exactly two history entries")
821+
}
822+
}, 15*time.Second, 200*time.Millisecond)
823+
})
640824
}
641825

642826
func assertEventuallyDrained(t testing.TB, redis *redis.Client, stream string) {

0 commit comments

Comments
 (0)