github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/metrics/collect/manifold_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package collect_test 5 6 import ( 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils" 14 gc "gopkg.in/check.v1" 15 corecharm "gopkg.in/juju/charm.v6-unstable" 16 "gopkg.in/juju/names.v2" 17 18 "github.com/juju/juju/agent" 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/fortress" 23 "github.com/juju/juju/worker/metrics/collect" 24 "github.com/juju/juju/worker/metrics/spool" 25 "github.com/juju/juju/worker/uniter/runner/context" 26 "github.com/juju/juju/worker/uniter/runner/jujuc" 27 ) 28 29 type ManifoldSuite struct { 30 coretesting.BaseSuite 31 32 dataDir string 33 oldLcAll string 34 35 manifoldConfig collect.ManifoldConfig 36 manifold dependency.Manifold 37 resources dt.StubResources 38 } 39 40 var _ = gc.Suite(&ManifoldSuite{}) 41 42 func (s *ManifoldSuite) 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.resources = dt.StubResources{ 57 "agent-name": dt.StubResource{Output: &dummyAgent{dataDir: s.dataDir}}, 58 "metric-spool-name": dt.StubResource{Output: &dummyMetricFactory{}}, 59 "charmdir-name": dt.StubResource{Output: &dummyCharmdir{aborted: false}}, 60 } 61 } 62 63 // TestInputs ensures the collect manifold has the expected defined inputs. 64 func (s *ManifoldSuite) TestInputs(c *gc.C) { 65 c.Check(s.manifold.Inputs, jc.DeepEquals, []string{ 66 "agent-name", "metric-spool-name", "charmdir-name", 67 }) 68 } 69 70 // TestStartMissingDeps ensures that the manifold correctly handles a missing 71 // resource dependency. 72 func (s *ManifoldSuite) TestStartMissingDeps(c *gc.C) { 73 for _, missingDep := range []string{ 74 "agent-name", "metric-spool-name", "charmdir-name", 75 } { 76 testResources := dt.StubResources{} 77 for k, v := range s.resources { 78 if k == missingDep { 79 testResources[k] = dt.StubResource{Error: dependency.ErrMissing} 80 } else { 81 testResources[k] = v 82 } 83 } 84 worker, err := s.manifold.Start(testResources.Context()) 85 c.Check(worker, gc.IsNil) 86 c.Check(err, gc.Equals, dependency.ErrMissing) 87 } 88 } 89 90 // TestCollectWorkerStarts ensures that the manifold correctly sets up the worker. 91 func (s *ManifoldSuite) TestCollectWorkerStarts(c *gc.C) { 92 s.PatchValue(collect.NewRecorder, 93 func(_ names.UnitTag, _ context.Paths, _ spool.MetricFactory) (spool.MetricRecorder, error) { 94 // Return a dummyRecorder here, because otherwise a real one 95 // *might* get instantiated and error out, if the periodic worker 96 // happens to fire before the worker shuts down (as seen in 97 // LP:#1497355). 98 return &dummyRecorder{ 99 charmURL: "cs:ubuntu-1", 100 unitTag: "ubuntu/0", 101 }, nil 102 }) 103 s.PatchValue(collect.ReadCharm, 104 func(_ names.UnitTag, _ context.Paths) (*corecharm.URL, map[string]corecharm.Metric, error) { 105 return corecharm.MustParseURL("cs:ubuntu-1"), map[string]corecharm.Metric{"pings": corecharm.Metric{Description: "test metric", Type: corecharm.MetricTypeAbsolute}}, nil 106 }) 107 worker, err := s.manifold.Start(s.resources.Context()) 108 c.Assert(err, jc.ErrorIsNil) 109 c.Assert(worker, gc.NotNil) 110 worker.Kill() 111 err = worker.Wait() 112 c.Assert(err, jc.ErrorIsNil) 113 } 114 115 // TestJujuUnitsBuiltinMetric tests that the juju-units built-in metric is collected 116 // with a mock implementation of newRecorder. 117 func (s *ManifoldSuite) TestJujuUnitsBuiltinMetric(c *gc.C) { 118 recorder := &dummyRecorder{ 119 charmURL: "cs:wordpress-37", 120 unitTag: "wp/0", 121 isDeclaredMetric: true, 122 } 123 s.PatchValue(collect.NewRecorder, 124 func(_ names.UnitTag, _ context.Paths, _ spool.MetricFactory) (spool.MetricRecorder, error) { 125 return recorder, nil 126 }) 127 s.PatchValue(collect.ReadCharm, 128 func(_ names.UnitTag, _ context.Paths) (*corecharm.URL, map[string]corecharm.Metric, error) { 129 return corecharm.MustParseURL("cs:wordpress-37"), map[string]corecharm.Metric{"pings": corecharm.Metric{Description: "test metric", Type: corecharm.MetricTypeAbsolute}}, nil 130 }) 131 collectEntity, err := collect.NewCollect(s.manifoldConfig, s.resources.Context()) 132 c.Assert(err, jc.ErrorIsNil) 133 err = collectEntity.Do(nil) 134 c.Assert(err, jc.ErrorIsNil) 135 c.Assert(recorder.closed, jc.IsTrue) 136 c.Assert(recorder.batches, gc.HasLen, 1) 137 c.Assert(recorder.batches[0].CharmURL, gc.Equals, "cs:wordpress-37") 138 c.Assert(recorder.batches[0].UnitTag, gc.Equals, "wp/0") 139 c.Assert(recorder.batches[0].Metrics, gc.HasLen, 1) 140 c.Assert(recorder.batches[0].Metrics[0].Key, gc.Equals, "juju-units") 141 c.Assert(recorder.batches[0].Metrics[0].Value, gc.Equals, "1") 142 } 143 144 // TestAvailability tests that the charmdir resource is properly checked. 145 func (s *ManifoldSuite) TestAvailability(c *gc.C) { 146 recorder := &dummyRecorder{ 147 charmURL: "cs:wordpress-37", 148 unitTag: "wp/0", 149 isDeclaredMetric: true, 150 } 151 s.PatchValue(collect.NewRecorder, 152 func(_ names.UnitTag, _ context.Paths, _ spool.MetricFactory) (spool.MetricRecorder, error) { 153 return recorder, nil 154 }) 155 s.PatchValue(collect.ReadCharm, 156 func(_ names.UnitTag, _ context.Paths) (*corecharm.URL, map[string]corecharm.Metric, error) { 157 return corecharm.MustParseURL("cs:wordpress-37"), map[string]corecharm.Metric{"pings": corecharm.Metric{Description: "test metric", Type: corecharm.MetricTypeAbsolute}}, nil 158 }) 159 charmdir := &dummyCharmdir{aborted: true} 160 s.resources["charmdir-name"] = dt.StubResource{Output: charmdir} 161 collectEntity, err := collect.NewCollect(s.manifoldConfig, s.resources.Context()) 162 c.Assert(err, jc.ErrorIsNil) 163 err = collectEntity.Do(nil) 164 c.Assert(err, jc.ErrorIsNil) 165 c.Assert(recorder.batches, gc.HasLen, 0) 166 167 charmdir = &dummyCharmdir{aborted: false} 168 s.resources["charmdir-name"] = dt.StubResource{Output: charmdir} 169 collectEntity, err = collect.NewCollect(s.manifoldConfig, s.resources.Context()) 170 c.Assert(err, jc.ErrorIsNil) 171 err = collectEntity.Do(nil) 172 c.Assert(err, jc.ErrorIsNil) 173 c.Assert(recorder.closed, jc.IsTrue) 174 c.Assert(recorder.batches, gc.HasLen, 1) 175 } 176 177 // TestNoMetricsDeclared tests that if metrics are not declared, none are 178 // collected, not even builtin. 179 func (s *ManifoldSuite) TestNoMetricsDeclared(c *gc.C) { 180 recorder := &dummyRecorder{ 181 charmURL: "cs:wordpress-37", 182 unitTag: "wp/0", 183 isDeclaredMetric: false, 184 } 185 s.PatchValue(collect.NewRecorder, 186 func(_ names.UnitTag, _ context.Paths, _ spool.MetricFactory) (spool.MetricRecorder, error) { 187 return recorder, nil 188 }) 189 s.PatchValue(collect.ReadCharm, 190 func(_ names.UnitTag, _ context.Paths) (*corecharm.URL, map[string]corecharm.Metric, error) { 191 return corecharm.MustParseURL("cs:wordpress-37"), map[string]corecharm.Metric{}, nil 192 }) 193 collectEntity, err := collect.NewCollect(s.manifoldConfig, s.resources.Context()) 194 c.Assert(err, jc.ErrorIsNil) 195 err = collectEntity.Do(nil) 196 c.Assert(err, jc.ErrorIsNil) 197 c.Assert(recorder.closed, jc.IsTrue) 198 c.Assert(recorder.batches, gc.HasLen, 0) 199 } 200 201 type dummyAgent struct { 202 agent.Agent 203 dataDir string 204 } 205 206 func (a dummyAgent) CurrentConfig() agent.Config { 207 return &dummyAgentConfig{dataDir: a.dataDir} 208 } 209 210 type dummyAgentConfig struct { 211 agent.Config 212 dataDir string 213 } 214 215 // Tag implements agent.AgentConfig. 216 func (ac dummyAgentConfig) Tag() names.Tag { 217 return names.NewUnitTag("u/0") 218 } 219 220 // DataDir implements agent.AgentConfig. 221 func (ac dummyAgentConfig) DataDir() string { 222 return ac.dataDir 223 } 224 225 type dummyCharmdir struct { 226 fortress.Guest 227 228 aborted bool 229 } 230 231 func (a *dummyCharmdir) Visit(visit fortress.Visit, _ fortress.Abort) error { 232 if a.aborted { 233 return fortress.ErrAborted 234 } 235 return visit() 236 } 237 238 type dummyMetricFactory struct { 239 spool.MetricFactory 240 } 241 242 type dummyRecorder struct { 243 spool.MetricRecorder 244 245 // inputs 246 charmURL, unitTag string 247 metrics map[string]corecharm.Metric 248 isDeclaredMetric bool 249 err string 250 251 // outputs 252 closed bool 253 batches []spool.MetricBatch 254 } 255 256 func (r *dummyRecorder) AddMetric(key, value string, created time.Time) error { 257 if r.err != "" { 258 return errors.New(r.err) 259 } 260 then := time.Date(2015, 8, 20, 15, 48, 0, 0, time.UTC) 261 r.batches = append(r.batches, spool.MetricBatch{ 262 CharmURL: r.charmURL, 263 UUID: utils.MustNewUUID().String(), 264 Created: then, 265 Metrics: []jujuc.Metric{{ 266 Key: key, 267 Value: value, 268 Time: then, 269 }}, 270 UnitTag: r.unitTag, 271 }) 272 return nil 273 } 274 275 func (r *dummyRecorder) IsDeclaredMetric(key string) bool { 276 if r.isDeclaredMetric { 277 return true 278 } 279 _, ok := r.metrics[key] 280 return ok 281 } 282 283 func (r *dummyRecorder) Close() error { 284 r.closed = true 285 return nil 286 }