github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/aagent/watchers/nagioswatcher/nagios_test.go (about) 1 // Copyright (c) 2020-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package nagioswatcher 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "os" 11 "path/filepath" 12 "testing" 13 "time" 14 15 "github.com/choria-io/go-choria/aagent/model" 16 "github.com/golang/mock/gomock" 17 . "github.com/onsi/ginkgo/v2" 18 . "github.com/onsi/gomega" 19 20 "github.com/choria-io/go-choria/aagent/util" 21 "github.com/choria-io/go-choria/statistics" 22 ) 23 24 func Test(t *testing.T) { 25 RegisterFailHandler(Fail) 26 RunSpecs(t, "AAgent/Watchers/NagiosWatcher") 27 } 28 29 var _ = Describe("NagiosWatcher", func() { 30 var ( 31 mockctl *gomock.Controller 32 mockMachine *model.MockMachine 33 watch *Watcher 34 now time.Time 35 err error 36 td string 37 ) 38 39 BeforeEach(func() { 40 mockctl = gomock.NewController(GinkgoT()) 41 mockMachine = model.NewMockMachine(mockctl) 42 43 td, err = os.MkdirTemp("", "") 44 Expect(err).ToNot(HaveOccurred()) 45 46 now = time.Unix(1606924953, 0) 47 mockMachine.EXPECT().Name().Return("nagios").AnyTimes() 48 mockMachine.EXPECT().Identity().Return("ginkgo").AnyTimes() 49 mockMachine.EXPECT().InstanceID().Return("1234567890").AnyTimes() 50 mockMachine.EXPECT().Version().Return("1.0.0").AnyTimes() 51 mockMachine.EXPECT().TimeStampSeconds().Return(now.Unix()).AnyTimes() 52 mockMachine.EXPECT().TextFileDirectory().Return(td).AnyTimes() 53 54 wi, err := New(mockMachine, "ginkgo", []string{"always"}, "fail", "success", "1s", time.Second, map[string]any{ 55 "plugin": "/bin/sh", 56 }) 57 Expect(err).ToNot(HaveOccurred()) 58 59 watch = wi.(*Watcher) 60 watch.previousCheck = now 61 watch.previousOutput = "OK: ginkgo" 62 watch.previousPerfData = []util.PerfData{} 63 watch.previousRunTime = 500 * time.Millisecond 64 watch.previous = OK 65 watch.previousPlugin = "/bin/sh" 66 }) 67 68 AfterEach(func() { 69 mockctl.Finish() 70 os.RemoveAll(td) 71 }) 72 73 Describe("setProperties", func() { 74 It("Should parse valid properties", func() { 75 watch.properties = nil 76 err = watch.setProperties(map[string]any{ 77 "annotations": map[string]string{ 78 "a1": "v1", 79 "a2": "v2", 80 }, 81 "plugin": "cmd", 82 "timeout": "5s", 83 }) 84 Expect(err).ToNot(HaveOccurred()) 85 Expect(watch.properties.Annotations).To(Equal(map[string]string{ 86 "a1": "v1", 87 "a2": "v2", 88 })) 89 Expect(watch.properties.Plugin).To(Equal("cmd")) 90 Expect(watch.properties.Timeout).To(Equal(5 * time.Second)) 91 Expect(watch.properties.Builtin).To(BeEmpty()) 92 Expect(watch.properties.Gossfile).To(BeEmpty()) 93 }) 94 95 It("Should handle errors", func() { 96 watch.properties = nil 97 err = watch.setProperties(map[string]any{}) 98 Expect(err).To(MatchError("plugin or builtin is required")) 99 100 watch.properties = nil 101 err = watch.setProperties(map[string]any{ 102 "plugin": "cmd", 103 "builtin": "goss", 104 }) 105 Expect(err).To(MatchError("cannot set plugin and builtin")) 106 107 watch.properties = nil 108 err = watch.setProperties(map[string]any{ 109 "builtin": "goss", 110 }) 111 Expect(err).To(MatchError("gossfile property is required for the goss builtin check")) 112 113 watch.properties = nil 114 err = watch.setProperties(map[string]any{ 115 "builtin": "choria_status", 116 }) 117 Expect(err).To(MatchError("last_message property is required for the choria_status builtin check")) 118 }) 119 120 It("Should handle valid goss setups", func() { 121 watch.properties = nil 122 err = watch.setProperties(map[string]any{ 123 "builtin": "goss", 124 "gossFile": "/x", 125 }) 126 Expect(err).ToNot(HaveOccurred()) 127 Expect(watch.properties.Builtin).To(Equal("goss")) 128 Expect(watch.properties.Gossfile).To(Equal("/x")) 129 }) 130 131 It("Should handle valid choria_status setups", func() { 132 watch.properties = nil 133 err = watch.setProperties(map[string]any{ 134 "builtin": "choria_status", 135 "last_message": "1h", 136 }) 137 Expect(err).ToNot(HaveOccurred()) 138 Expect(watch.properties.Builtin).To(Equal("choria_status")) 139 Expect(watch.properties.LastMessage).To(Equal(time.Hour)) 140 141 sf := filepath.Join(td, "status.json") 142 mockMachine.EXPECT().ChoriaStatusFile().Return(sf, 60*60).AnyTimes() 143 state, _, err := watch.watchUsingChoria() 144 Expect(state).To(Equal(CRITICAL)) 145 Expect(err).ToNot(HaveOccurred()) 146 147 now := time.Now().UTC() 148 status := statistics.InstanceStatus{ 149 Identity: "ginkgo.example.net", 150 Uptime: 1000, 151 ConnectedServer: "broker.example.net", 152 LastMessage: now.Unix(), 153 Provisioning: false, 154 Stats: &statistics.ServerStats{ 155 Total: 4, 156 Valid: 1, 157 Invalid: 1, 158 Passed: 1, 159 Filtered: 1, 160 Replies: 2, 161 TTLExpired: 1, 162 Events: 10, 163 }, 164 CertificateExpires: now.Add(365 * 24 * time.Hour), 165 TokenExpires: now.Add(30 * 24 * time.Hour), 166 FileName: sf, 167 ModTime: now, 168 } 169 sj, _ := json.Marshal(status) 170 os.WriteFile(sf, sj, 0644) 171 172 state, output, err := watch.watchUsingChoria() 173 Expect(output).To(Or( 174 Equal(fmt.Sprintf("OK: %s|uptime=1000;; filtered_msgs=1;; invalid_msgs=1;; passed_msgs=1;; replies_msgs=2;; total_msgs=4;; ttlexpired_msgs=1;; last_msg=%d;; cert_expire_seconds=31536000;; token_expire_seconds=2592000;; events=10;;", sf, now.Unix())), 175 Equal(fmt.Sprintf("OK: %s|uptime=1000;; filtered_msgs=1;; invalid_msgs=1;; passed_msgs=1;; replies_msgs=2;; total_msgs=4;; ttlexpired_msgs=1;; last_msg=%d;; cert_expire_seconds=31535999;; token_expire_seconds=2591999;; events=10;;", sf, now.Unix())), 176 )) 177 Expect(state).To(Equal(OK)) 178 Expect(err).ToNot(HaveOccurred()) 179 180 lm := now.Add(-1 * 70 * 70 * time.Second) 181 status.LastMessage = lm.Unix() 182 sj, _ = json.Marshal(status) 183 os.WriteFile(sf, sj, 0644) 184 185 state, output, err = watch.watchUsingChoria() 186 Expect(state).To(Equal(CRITICAL)) 187 Expect(output).To(Or( 188 Equal(fmt.Sprintf("CRITICAL: last message at %s|uptime=1000;; filtered_msgs=1;; invalid_msgs=1;; passed_msgs=1;; replies_msgs=2;; total_msgs=4;; ttlexpired_msgs=1;; last_msg=%d;; cert_expire_seconds=31535999;; token_expire_seconds=2591999;; events=10;;", time.Unix(status.LastMessage, 0).UTC(), status.LastMessage)), 189 Equal(fmt.Sprintf("CRITICAL: last message at %s|uptime=1000;; filtered_msgs=1;; invalid_msgs=1;; passed_msgs=1;; replies_msgs=2;; total_msgs=4;; ttlexpired_msgs=1;; last_msg=%d;; cert_expire_seconds=31536000;; token_expire_seconds=2592000;; events=10;;", time.Unix(status.LastMessage, 0).UTC(), status.LastMessage)), 190 )) 191 Expect(err).ToNot(HaveOccurred()) 192 193 watch.properties.CertExpiry = 366 * 24 * time.Hour 194 state, output, err = watch.watchUsingChoria() 195 Expect(err).ToNot(HaveOccurred()) 196 Expect(state).To(Equal(CRITICAL)) 197 Expect(output).To(Or( 198 Equal(fmt.Sprintf("CRITICAL: certificate expires %s (8760h0m0s)|uptime=1000;; filtered_msgs=1;; invalid_msgs=1;; passed_msgs=1;; replies_msgs=2;; total_msgs=4;; ttlexpired_msgs=1;; last_msg=%d;; cert_expire_seconds=31535999;; token_expire_seconds=2591999;; events=10;;", status.CertificateExpires, status.LastMessage)), 199 Equal(fmt.Sprintf("CRITICAL: certificate expires %s (8760h0m0s)|uptime=1000;; filtered_msgs=1;; invalid_msgs=1;; passed_msgs=1;; replies_msgs=2;; total_msgs=4;; ttlexpired_msgs=1;; last_msg=%d;; cert_expire_seconds=31536000;; token_expire_seconds=2592000;; events=10;;", status.CertificateExpires, status.LastMessage)), 200 )) 201 202 watch.properties.CertExpiry = 24 * time.Hour 203 watch.properties.TokenExpiry = 365 * 24 * time.Hour 204 state, output, err = watch.watchUsingChoria() 205 Expect(err).ToNot(HaveOccurred()) 206 Expect(state).To(Equal(CRITICAL)) 207 Expect(output).To(Or( 208 Equal(fmt.Sprintf("CRITICAL: token expires %s (720h0m0s)|uptime=1000;; filtered_msgs=1;; invalid_msgs=1;; passed_msgs=1;; replies_msgs=2;; total_msgs=4;; ttlexpired_msgs=1;; last_msg=%d;; cert_expire_seconds=31535999;; token_expire_seconds=2591999;; events=10;;", status.TokenExpires, status.LastMessage)), 209 Equal(fmt.Sprintf("CRITICAL: token expires %s (720h0m0s)|uptime=1000;; filtered_msgs=1;; invalid_msgs=1;; passed_msgs=1;; replies_msgs=2;; total_msgs=4;; ttlexpired_msgs=1;; last_msg=%d;; cert_expire_seconds=31536000;; token_expire_seconds=2592000;; events=10;;", status.TokenExpires, status.LastMessage)), 210 )) 211 212 Expect(err).ToNot(HaveOccurred()) 213 }) 214 }) 215 216 Describe("CurrentState", func() { 217 It("Should be a valid state", func() { 218 cs := watch.CurrentState() 219 csj, err := cs.(*StateNotification).JSON() 220 Expect(err).ToNot(HaveOccurred()) 221 222 event := map[string]any{} 223 err = json.Unmarshal(csj, &event) 224 Expect(err).ToNot(HaveOccurred()) 225 delete(event, "id") 226 227 Expect(event).To(Equal(map[string]any{ 228 "time": "2020-12-02T16:02:33Z", 229 "type": "io.choria.machine.watcher.nagios.v1.state", 230 "subject": "ginkgo", 231 "specversion": "1.0", 232 "source": "io.choria.machine", 233 "datacontenttype": "application/json", 234 "data": map[string]any{ 235 "id": "1234567890", 236 "identity": "ginkgo", 237 "machine": "nagios", 238 "name": "ginkgo", 239 "protocol": "io.choria.machine.watcher.nagios.v1.state", 240 "type": "nagios", 241 "version": "1.0.0", 242 "timestamp": float64(now.Unix()), 243 "status_code": float64(0), 244 "runtime": 0.5, 245 "check_time": float64(now.Unix()), 246 "annotations": map[string]any{}, 247 "perfdata": []any{}, 248 "history": []any{}, 249 "status": "OK", 250 "output": "OK: ginkgo", 251 "plugin": "/bin/sh", 252 }, 253 })) 254 }) 255 }) 256 })