github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/metricsender/sender_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package metricsender_test 5 6 import ( 7 "encoding/json" 8 "net/http" 9 "net/http/httptest" 10 "time" 11 12 "github.com/juju/clock" 13 "github.com/juju/clock/testclock" 14 wireformat "github.com/juju/romulus/wireformat/metrics" 15 jc "github.com/juju/testing/checkers" 16 "github.com/juju/utils" 17 gc "gopkg.in/check.v1" 18 19 "github.com/juju/juju/apiserver/facades/agent/metricsender" 20 jujujutesting "github.com/juju/juju/juju/testing" 21 "github.com/juju/juju/state" 22 "github.com/juju/juju/testing/factory" 23 ) 24 25 type SenderSuite struct { 26 jujujutesting.JujuConnSuite 27 unit *state.Unit 28 meteredService *state.Application 29 clock clock.Clock 30 } 31 32 var _ = gc.Suite(&SenderSuite{}) 33 34 func (s *SenderSuite) SetUpTest(c *gc.C) { 35 s.JujuConnSuite.SetUpTest(c) 36 meteredCharm := s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"}) 37 s.meteredService = s.Factory.MakeApplication(c, &factory.ApplicationParams{Charm: meteredCharm}) 38 s.unit = s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.meteredService, SetCharmURL: true}) 39 s.clock = testclock.NewClock(time.Now()) 40 } 41 42 // startServer starts a test HTTP server, returning a function that should be 43 // run at the end of the test to clean up. 44 func (s *SenderSuite) startServer(c *gc.C, handler http.Handler) func() { 45 ts := httptest.NewServer(handler) 46 cleanup := metricsender.PatchHost(ts.URL) 47 return func() { 48 ts.Close() 49 cleanup() 50 } 51 } 52 53 var _ metricsender.MetricSender = (*metricsender.HTTPSender)(nil) 54 55 // TestHTTPSender checks that if the default sender 56 // is in use metrics get sent 57 func (s *SenderSuite) TestHTTPSender(c *gc.C) { 58 metricCount := 3 59 expectedCharmURL, _ := s.unit.CharmURL() 60 61 receiverChan := make(chan wireformat.MetricBatch, metricCount) 62 cleanup := s.startServer(c, testHandler(c, receiverChan, nil, 0)) 63 defer cleanup() 64 65 now := time.Now() 66 metrics := make([]*state.MetricBatch, metricCount) 67 for i := range metrics { 68 metrics[i] = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now}) 69 } 70 sender := metricsender.DefaultSenderFactory()("http://example.com") 71 err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 10, true) 72 c.Assert(err, jc.ErrorIsNil) 73 74 c.Assert(receiverChan, gc.HasLen, metricCount) 75 close(receiverChan) 76 for batch := range receiverChan { 77 c.Assert(batch.CharmUrl, gc.Equals, expectedCharmURL.String()) 78 } 79 80 for _, metric := range metrics { 81 m, err := s.State.MetricBatch(metric.UUID()) 82 c.Assert(err, jc.ErrorIsNil) 83 c.Assert(m.Sent(), jc.IsTrue) 84 } 85 } 86 87 // StatusMap defines a type for a function that returns the status and information for a specified unit. 88 type StatusMap func(unitName string) (unit string, status string, info string) 89 90 func errorHandler(c *gc.C, errorCode int) http.HandlerFunc { 91 return func(w http.ResponseWriter, r *http.Request) { 92 w.WriteHeader(errorCode) 93 } 94 } 95 96 func testHandler(c *gc.C, batches chan<- wireformat.MetricBatch, statusMap StatusMap, gracePeriod time.Duration) http.HandlerFunc { 97 return func(w http.ResponseWriter, r *http.Request) { 98 c.Assert(r.Method, gc.Equals, "POST") 99 dec := json.NewDecoder(r.Body) 100 enc := json.NewEncoder(w) 101 var incoming []wireformat.MetricBatch 102 err := dec.Decode(&incoming) 103 c.Assert(err, jc.ErrorIsNil) 104 105 var resp = make(wireformat.EnvironmentResponses) 106 for _, batch := range incoming { 107 c.Logf("received metrics batch: %+v", batch) 108 109 resp.Ack(batch.ModelUUID, batch.UUID) 110 111 if statusMap != nil { 112 unitName, status, info := statusMap(batch.UnitName) 113 resp.SetUnitStatus(batch.ModelUUID, unitName, status, info) 114 } 115 116 select { 117 case batches <- batch: 118 default: 119 } 120 } 121 uuid, err := utils.NewUUID() 122 c.Assert(err, jc.ErrorIsNil) 123 err = enc.Encode(wireformat.Response{ 124 UUID: uuid.String(), 125 EnvResponses: resp, 126 NewGracePeriod: gracePeriod, 127 }) 128 c.Assert(err, jc.ErrorIsNil) 129 } 130 } 131 132 // TestErrorCodes checks that for a set of error codes SendMetrics returns an 133 // error and metrics are marked as not being sent 134 func (s *SenderSuite) TestErrorCodes(c *gc.C) { 135 tests := []struct { 136 errorCode int 137 expectedErr string 138 }{ 139 {http.StatusBadRequest, "failed to send metrics http 400"}, 140 {http.StatusServiceUnavailable, "failed to send metrics http 503"}, 141 } 142 143 for _, test := range tests { 144 killServer := s.startServer(c, errorHandler(c, test.errorCode)) 145 146 now := time.Now() 147 batches := make([]*state.MetricBatch, 3) 148 for i := range batches { 149 batches[i] = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false, Time: &now}) 150 } 151 sender := metricsender.DefaultSenderFactory()("http://example.com") 152 err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 10, true) 153 c.Assert(err, gc.ErrorMatches, test.expectedErr) 154 for _, batch := range batches { 155 m, err := s.State.MetricBatch(batch.UUID()) 156 c.Assert(err, jc.ErrorIsNil) 157 c.Assert(m.Sent(), jc.IsFalse) 158 } 159 killServer() 160 } 161 } 162 163 // TestMeterStatus checks that the meter status information returned 164 // by the collector service is propagated to the unit. 165 // is in use metrics get sent 166 func (s *SenderSuite) TestMeterStatus(c *gc.C) { 167 statusFunc := func(unitName string) (string, string, string) { 168 return unitName, "GREEN", "" 169 } 170 171 cleanup := s.startServer(c, testHandler(c, nil, statusFunc, 0)) 172 defer cleanup() 173 174 _ = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false}) 175 176 status, err := s.unit.GetMeterStatus() 177 c.Assert(err, jc.ErrorIsNil) 178 c.Assert(status.Code, gc.Equals, state.MeterNotSet) 179 180 sender := metricsender.DefaultSenderFactory()("http://example.com") 181 err = metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 10, true) 182 c.Assert(err, jc.ErrorIsNil) 183 184 status, err = s.unit.GetMeterStatus() 185 c.Assert(err, jc.ErrorIsNil) 186 c.Assert(status.Code, gc.Equals, state.MeterGreen) 187 } 188 189 // TestMeterStatusInvalid checks that the metric sender deals with invalid 190 // meter status data properly. 191 func (s *SenderSuite) TestMeterStatusInvalid(c *gc.C) { 192 unit1 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.meteredService, SetCharmURL: true}) 193 unit2 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.meteredService, SetCharmURL: true}) 194 unit3 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.meteredService, SetCharmURL: true}) 195 196 statusFunc := func(unitName string) (string, string, string) { 197 switch unitName { 198 case unit1.Name(): 199 // valid meter status 200 return unitName, "GREEN", "" 201 case unit2.Name(): 202 // invalid meter status 203 return unitName, "blah", "" 204 case unit3.Name(): 205 // invalid unit name 206 return "no-such-unit", "GREEN", "" 207 default: 208 return unitName, "GREEN", "" 209 } 210 } 211 212 cleanup := s.startServer(c, testHandler(c, nil, statusFunc, 0)) 213 defer cleanup() 214 215 _ = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: unit1, Sent: false}) 216 _ = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: unit2, Sent: false}) 217 _ = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: unit3, Sent: false}) 218 219 for _, unit := range []*state.Unit{unit1, unit2, unit3} { 220 status, err := unit.GetMeterStatus() 221 c.Assert(err, jc.ErrorIsNil) 222 c.Assert(status.Code, gc.Equals, state.MeterNotSet) 223 } 224 225 sender := metricsender.DefaultSenderFactory()("http://example.com") 226 err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 10, true) 227 c.Assert(err, jc.ErrorIsNil) 228 229 status, err := unit1.GetMeterStatus() 230 c.Assert(err, jc.ErrorIsNil) 231 c.Assert(status.Code, gc.Equals, state.MeterGreen) 232 233 status, err = unit2.GetMeterStatus() 234 c.Assert(err, jc.ErrorIsNil) 235 c.Assert(status.Code, gc.Equals, state.MeterNotSet) 236 237 status, err = unit3.GetMeterStatus() 238 c.Assert(err, jc.ErrorIsNil) 239 c.Assert(status.Code, gc.Equals, state.MeterNotSet) 240 241 } 242 243 func (s *SenderSuite) TestGracePeriodResponse(c *gc.C) { 244 _ = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false}) 245 cleanup := s.startServer(c, testHandler(c, nil, nil, 47*time.Hour)) 246 defer cleanup() 247 sender := metricsender.DefaultSenderFactory()("http://example.com") 248 err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 10, true) 249 c.Assert(err, jc.ErrorIsNil) 250 mm, err := s.State.MetricsManager() 251 c.Assert(err, jc.ErrorIsNil) 252 c.Assert(mm.GracePeriod(), gc.Equals, 47*time.Hour) 253 } 254 255 func (s *SenderSuite) TestNegativeGracePeriodResponse(c *gc.C) { 256 _ = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false}) 257 258 cleanup := s.startServer(c, testHandler(c, nil, nil, -47*time.Hour)) 259 defer cleanup() 260 sender := metricsender.DefaultSenderFactory()("http://example.com") 261 err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 10, true) 262 c.Assert(err, jc.ErrorIsNil) 263 mm, err := s.State.MetricsManager() 264 c.Assert(err, jc.ErrorIsNil) 265 c.Assert(mm.GracePeriod(), gc.Equals, 24*time.Hour*7) //Default (unchanged) 266 } 267 268 func (s *SenderSuite) TestZeroGracePeriodResponse(c *gc.C) { 269 _ = s.Factory.MakeMetric(c, &factory.MetricParams{Unit: s.unit, Sent: false}) 270 271 cleanup := s.startServer(c, testHandler(c, nil, nil, 0)) 272 defer cleanup() 273 sender := metricsender.DefaultSenderFactory()("http://example.com") 274 err := metricsender.SendMetrics(TestSenderBackend{s.State, s.Model}, sender, s.clock, 10, true) 275 c.Assert(err, jc.ErrorIsNil) 276 mm, err := s.State.MetricsManager() 277 c.Assert(err, jc.ErrorIsNil) 278 c.Assert(mm.GracePeriod(), gc.Equals, 24*time.Hour*7) //Default (unchanged) 279 }