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