github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/statemetrics/statemetrics_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENSE file for details. 3 4 package statemetrics_test 5 6 import ( 7 "errors" 8 "reflect" 9 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/prometheus/client_golang/prometheus/testutil" 14 dto "github.com/prometheus/client_model/go" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/names.v2" 17 18 "github.com/juju/juju/core/status" 19 "github.com/juju/juju/permission" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/state/statemetrics" 22 ) 23 24 type collectorSuite struct { 25 testing.IsolationSuite 26 pool *mockStatePool 27 collector *statemetrics.Collector 28 } 29 30 var _ = gc.Suite(&collectorSuite{}) 31 32 func (s *collectorSuite) SetUpTest(c *gc.C) { 33 s.IsolationSuite.SetUpTest(c) 34 35 users := []*mockUser{{ 36 tag: names.NewUserTag("alice"), 37 controllerAccess: permission.NoAccess, 38 }, { 39 tag: names.NewUserTag("bob"), 40 controllerAccess: permission.NoAccess, 41 }, { 42 tag: names.NewUserTag("cayley@cambridge"), 43 deleted: true, 44 controllerAccess: permission.AddModelAccess, 45 }, { 46 tag: names.NewUserTag("dominique"), 47 disabled: true, 48 controllerAccess: permission.ReadAccess, 49 }} 50 51 s.pool = &mockStatePool{ 52 models: []*mockModel{{ 53 tag: names.NewModelTag("b266dff7-eee8-4297-b03a-4692796ec193"), 54 life: state.Alive, 55 status: status.StatusInfo{Status: status.Available}, 56 machines: []*mockMachine{{ 57 life: state.Alive, 58 agentStatus: status.StatusInfo{Status: status.Started}, 59 instanceStatus: status.StatusInfo{Status: status.Running}, 60 }}, 61 }, { 62 tag: names.NewModelTag("1ab5799e-e72d-4de7-b70d-499edfab0e5c"), 63 life: state.Dying, 64 status: status.StatusInfo{Status: status.Destroying}, 65 machines: []*mockMachine{{ 66 life: state.Alive, 67 agentStatus: status.StatusInfo{Status: status.Error}, 68 instanceStatus: status.StatusInfo{Status: status.ProvisioningError}, 69 }}, 70 }}, 71 } 72 s.pool.system = &mockState{ 73 users: users, 74 modelUUIDs: s.pool.modelUUIDs(), 75 } 76 s.collector = statemetrics.New(s.pool) 77 } 78 79 func (s *collectorSuite) TestDescribe(c *gc.C) { 80 ch := make(chan *prometheus.Desc) 81 go func() { 82 defer close(ch) 83 s.collector.Describe(ch) 84 }() 85 var descStrings []string 86 for desc := range ch { 87 descStrings = append(descStrings, desc.String()) 88 } 89 expect := []string{ 90 `.*fqName: "juju_state_machines".*`, 91 `.*fqName: "juju_state_models".*`, 92 `.*fqName: "juju_state_users".*`, 93 `.*fqName: "juju_state_scrape_errors".*`, 94 `.*fqName: "juju_state_scrape_duration_seconds".*`, 95 } 96 c.Assert(descStrings, gc.HasLen, len(expect)) 97 for i, expect := range expect { 98 c.Assert(descStrings[i], gc.Matches, expect) 99 } 100 } 101 102 func (s *collectorSuite) collect(c *gc.C) ([]prometheus.Metric, []dto.Metric) { 103 ch := make(chan prometheus.Metric) 104 go func() { 105 defer close(ch) 106 s.collector.Collect(ch) 107 }() 108 var metrics []prometheus.Metric 109 for metric := range ch { 110 metrics = append(metrics, metric) 111 } 112 dtoMetrics := make([]dto.Metric, len(metrics)) 113 for i, metric := range metrics { 114 err := metric.Write(&dtoMetrics[i]) 115 c.Assert(err, jc.ErrorIsNil) 116 } 117 return metrics, dtoMetrics 118 } 119 120 func (s *collectorSuite) checkExpected(c *gc.C, actual, expected []dto.Metric) { 121 c.Assert(actual, gc.HasLen, len(expected)) 122 for i, dm := range actual { 123 var found bool 124 for i, m := range expected { 125 if !reflect.DeepEqual(dm, m) { 126 continue 127 } 128 expected = append(expected[:i], expected[i+1:]...) 129 found = true 130 break 131 } 132 if !found { 133 c.Errorf("metric #%d %+v not expected", i, dm) 134 } 135 } 136 } 137 138 func float64ptr(v float64) *float64 { 139 return &v 140 } 141 142 func (s *collectorSuite) TestCollect(c *gc.C) { 143 _, dtoMetrics := s.collect(c) 144 145 // The scrape time metric has a non-deterministic value, 146 // so we just check that it is non-zero. 147 c.Assert(dtoMetrics, gc.Not(gc.HasLen), 0) 148 scrapeDurationMetric := dtoMetrics[len(dtoMetrics)-1] 149 c.Assert(scrapeDurationMetric.Gauge.GetValue(), gc.Not(gc.Equals), 0) 150 151 labelpair := func(n, v string) *dto.LabelPair { 152 return &dto.LabelPair{Name: &n, Value: &v} 153 } 154 155 s.checkExpected(c, dtoMetrics, []dto.Metric{ 156 // juju_state_machines 157 { 158 Gauge: &dto.Gauge{Value: float64ptr(1)}, 159 Label: []*dto.LabelPair{ 160 labelpair("agent_status", "started"), 161 labelpair("life", "alive"), 162 labelpair("machine_status", "running"), 163 }, 164 }, 165 { 166 Gauge: &dto.Gauge{Value: float64ptr(1)}, 167 Label: []*dto.LabelPair{ 168 labelpair("agent_status", "error"), 169 labelpair("life", "alive"), 170 labelpair("machine_status", "provisioning error"), 171 }, 172 }, 173 174 // juju_state_models 175 { 176 Gauge: &dto.Gauge{Value: float64ptr(1)}, 177 Label: []*dto.LabelPair{ 178 labelpair("life", "alive"), 179 labelpair("status", "available"), 180 }, 181 }, 182 { 183 Gauge: &dto.Gauge{Value: float64ptr(1)}, 184 Label: []*dto.LabelPair{ 185 labelpair("life", "dying"), 186 labelpair("status", "destroying"), 187 }, 188 }, 189 190 // juju_state_users 191 { 192 Gauge: &dto.Gauge{Value: float64ptr(1)}, 193 Label: []*dto.LabelPair{ 194 labelpair("controller_access", "add-model"), 195 labelpair("deleted", "true"), 196 labelpair("disabled", ""), 197 labelpair("domain", "cambridge"), 198 }, 199 }, 200 { 201 Gauge: &dto.Gauge{Value: float64ptr(1)}, 202 Label: []*dto.LabelPair{ 203 labelpair("controller_access", "read"), 204 labelpair("deleted", ""), 205 labelpair("disabled", "true"), 206 labelpair("domain", ""), 207 }, 208 }, 209 { 210 Gauge: &dto.Gauge{Value: float64ptr(2)}, 211 Label: []*dto.LabelPair{ 212 labelpair("controller_access", ""), 213 labelpair("deleted", ""), 214 labelpair("disabled", ""), 215 labelpair("domain", ""), 216 }, 217 }, 218 219 // juju_state_scrape_errors 220 { 221 Gauge: &dto.Gauge{Value: float64ptr(0)}, 222 Label: []*dto.LabelPair{}, 223 }, 224 225 // juju_state_scrape_interval_seconds 226 { 227 Gauge: &dto.Gauge{Value: scrapeDurationMetric.Gauge.Value}, 228 Label: []*dto.LabelPair{}, 229 }, 230 }) 231 } 232 233 func (s *collectorSuite) TestCollectErrors(c *gc.C) { 234 s.pool.system.SetErrors( 235 errors.New("no models for you"), 236 errors.New("no users for you"), 237 ) 238 s.collect(c) 239 c.Check(testutil.ToFloat64(s.collector.ScrapeDurationGauge()), jc.GreaterThan, float64(0)) 240 c.Check(testutil.ToFloat64(s.collector.ScrapeErrorsGauge()), gc.Equals, float64(2)) 241 }