github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/metrics/collect/handler_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package collect_test 5 6 import ( 7 "net" 8 "os" 9 "path/filepath" 10 "strings" 11 "time" 12 13 "github.com/juju/names" 14 "github.com/juju/testing" 15 jc "github.com/juju/testing/checkers" 16 gc "gopkg.in/check.v1" 17 corecharm "gopkg.in/juju/charm.v6-unstable" 18 19 coretesting "github.com/juju/juju/testing" 20 "github.com/juju/juju/worker/dependency" 21 dt "github.com/juju/juju/worker/dependency/testing" 22 "github.com/juju/juju/worker/metrics/collect" 23 "github.com/juju/juju/worker/metrics/spool" 24 "github.com/juju/juju/worker/uniter/runner/context" 25 ) 26 27 type handlerSuite struct { 28 coretesting.BaseSuite 29 30 manifoldConfig collect.ManifoldConfig 31 manifold dependency.Manifold 32 dataDir string 33 resources dt.StubResources 34 recorder *dummyRecorder 35 listener *mockListener 36 } 37 38 var _ = gc.Suite(&handlerSuite{}) 39 40 func (s *handlerSuite) SetUpTest(c *gc.C) { 41 s.BaseSuite.SetUpTest(c) 42 s.manifoldConfig = collect.ManifoldConfig{ 43 AgentName: "agent-name", 44 MetricSpoolName: "metric-spool-name", 45 CharmDirName: "charmdir-name", 46 } 47 s.manifold = collect.Manifold(s.manifoldConfig) 48 s.dataDir = c.MkDir() 49 50 // create unit agent base dir so that hooks can run. 51 err := os.MkdirAll(filepath.Join(s.dataDir, "agents", "unit-u-0"), 0777) 52 c.Assert(err, jc.ErrorIsNil) 53 54 s.recorder = &dummyRecorder{ 55 charmURL: "local:trusty/metered-1", 56 unitTag: "metered/0", 57 metrics: map[string]corecharm.Metric{ 58 "pings": corecharm.Metric{ 59 Description: "test metric", 60 Type: corecharm.MetricTypeAbsolute, 61 }, 62 "juju-units": corecharm.Metric{}, 63 }, 64 } 65 66 s.resources = dt.StubResources{ 67 "agent-name": dt.StubResource{Output: &dummyAgent{dataDir: s.dataDir}}, 68 "metric-spool-name": dt.StubResource{Output: &mockMetricFactory{recorder: s.recorder}}, 69 "charmdir-name": dt.StubResource{Output: &dummyCharmdir{aborted: false}}, 70 } 71 72 s.PatchValue(collect.NewRecorder, 73 func(_ names.UnitTag, _ context.Paths, _ spool.MetricFactory) (spool.MetricRecorder, error) { 74 // Return a dummyRecorder here, because otherwise a real one 75 // *might* get instantiated and error out, if the periodic worker 76 // happens to fire before the worker shuts down (as seen in 77 // LP:#1497355). 78 return &dummyRecorder{ 79 charmURL: "local:trusty/metered-1", 80 unitTag: "metered/0", 81 metrics: map[string]corecharm.Metric{ 82 "pings": corecharm.Metric{ 83 Description: "test metric", 84 Type: corecharm.MetricTypeAbsolute, 85 }, 86 "juju-units": corecharm.Metric{}, 87 }, 88 }, nil 89 }, 90 ) 91 s.PatchValue(collect.ReadCharm, 92 func(_ names.UnitTag, _ context.Paths) (*corecharm.URL, map[string]corecharm.Metric, error) { 93 return corecharm.MustParseURL("local:trusty/metered-1"), 94 map[string]corecharm.Metric{ 95 "pings": corecharm.Metric{Description: "test metric", Type: corecharm.MetricTypeAbsolute}, 96 "juju-units": corecharm.Metric{}, 97 }, nil 98 }, 99 ) 100 s.listener = &mockListener{} 101 s.PatchValue(collect.NewSocketListener, collect.NewSocketListenerFnc(s.listener)) 102 } 103 104 func (s *handlerSuite) TestListenerStart(c *gc.C) { 105 worker, err := s.manifold.Start(s.resources.Context()) 106 c.Assert(err, jc.ErrorIsNil) 107 c.Assert(worker, gc.NotNil) 108 c.Assert(s.listener.Calls(), gc.HasLen, 0) 109 worker.Kill() 110 err = worker.Wait() 111 c.Assert(err, jc.ErrorIsNil) 112 s.listener.CheckCall(c, 0, "Stop") 113 } 114 115 func (s *handlerSuite) TestJujuUnitsBuiltinMetric(c *gc.C) { 116 worker, err := s.manifold.Start(s.resources.Context()) 117 c.Assert(err, jc.ErrorIsNil) 118 c.Assert(worker, gc.NotNil) 119 c.Assert(s.listener.Calls(), gc.HasLen, 0) 120 121 conn, err := s.listener.trigger() 122 c.Assert(err, jc.ErrorIsNil) 123 c.Assert(conn.Calls(), gc.HasLen, 3) 124 conn.CheckCall(c, 2, "Close") 125 126 responseString := strings.Trim(string(conn.data), " \n\t") 127 c.Assert(responseString, gc.Equals, "ok") 128 c.Assert(s.recorder.batches, gc.HasLen, 1) 129 130 worker.Kill() 131 err = worker.Wait() 132 c.Assert(err, jc.ErrorIsNil) 133 s.listener.CheckCall(c, 0, "Stop") 134 } 135 136 func (s *handlerSuite) TestHandlerError(c *gc.C) { 137 worker, err := s.manifold.Start(s.resources.Context()) 138 c.Assert(err, jc.ErrorIsNil) 139 c.Assert(worker, gc.NotNil) 140 c.Assert(s.listener.Calls(), gc.HasLen, 0) 141 142 s.recorder.err = "well, this is embarassing" 143 144 conn, err := s.listener.trigger() 145 c.Assert(err, gc.ErrorMatches, "failed to collect metrics: error adding 'juju-units' metric: well, this is embarassing") 146 c.Assert(conn.Calls(), gc.HasLen, 3) 147 conn.CheckCall(c, 2, "Close") 148 149 responseString := strings.Trim(string(conn.data), " \n\t") 150 c.Assert(responseString, gc.Matches, ".*well, this is embarassing") 151 c.Assert(s.recorder.batches, gc.HasLen, 0) 152 153 worker.Kill() 154 err = worker.Wait() 155 c.Assert(err, jc.ErrorIsNil) 156 s.listener.CheckCall(c, 0, "Stop") 157 } 158 159 type mockListener struct { 160 testing.Stub 161 handler spool.ConnectionHandler 162 } 163 164 func (l *mockListener) trigger() (*mockConnection, error) { 165 conn := &mockConnection{} 166 err := l.handler.Handle(conn) 167 if err != nil { 168 return conn, err 169 } 170 return conn, nil 171 } 172 173 // Stop implements the stopper interface. 174 func (l *mockListener) Stop() { 175 l.AddCall("Stop") 176 } 177 178 func (l *mockListener) SetHandler(handler spool.ConnectionHandler) { 179 l.handler = handler 180 } 181 182 type mockConnection struct { 183 net.Conn 184 testing.Stub 185 data []byte 186 } 187 188 // SetDeadline implements the net.Conn interface. 189 func (c *mockConnection) SetDeadline(t time.Time) error { 190 c.AddCall("SetDeadline", t) 191 return nil 192 } 193 194 // Write implements the net.Conn interface. 195 func (c *mockConnection) Write(data []byte) (int, error) { 196 c.AddCall("Write", data) 197 c.data = data 198 return len(data), nil 199 } 200 201 // Close implements the net.Conn interface. 202 func (c *mockConnection) Close() error { 203 c.AddCall("Close") 204 return nil 205 } 206 207 type mockMetricFactory struct { 208 spool.MetricFactory 209 recorder *dummyRecorder 210 } 211 212 // Recorder implements the spool.MetricFactory interface. 213 func (f *mockMetricFactory) Recorder(metrics map[string]corecharm.Metric, charmURL, unitTag string) (spool.MetricRecorder, error) { 214 return f.recorder, nil 215 }