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