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  }