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