From 83272356dfd464262d7725ae6d94248fb5f2cb10 Mon Sep 17 00:00:00 2001 From: the-ericwang35 Date: Thu, 6 Aug 2020 16:50:10 +0000 Subject: [PATCH] Add time series to summary --- cmd/mock_server.go | 8 ++- internal/validation/mock_metric_validation.go | 61 ++++++++++++++++--- server/metric/mock_metric.go | 28 +++++---- static/css/main.css | 7 +++ static/summary_template.html | 48 ++++++++++++++- 5 files changed, 124 insertions(+), 28 deletions(-) diff --git a/cmd/mock_server.go b/cmd/mock_server.go index fbfe1c0..7977c99 100644 --- a/cmd/mock_server.go +++ b/cmd/mock_server.go @@ -48,6 +48,7 @@ var ( type summaryTable struct { Spans []*cloudtrace.Span MetricDescriptors []*validation.DescriptorStatus + TimeSeries []*validation.TimeSeriesStatus } func main() { @@ -87,7 +88,8 @@ func startStandaloneServer() { <-sig grpcServer.GracefulStop() if *summary { - summaryTable := createSummaryTable(mockTraceServer.SpansSummary(), mockMetricServer.MetricDescriptorSummary()) + summaryTable := createSummaryTable(mockTraceServer.SpansSummary(), + mockMetricServer.MetricDescriptorSummary(), mockMetricServer.TimeSeriesSummary()) writeSummaryPage(summaryTable) } finish <- true @@ -99,10 +101,12 @@ func startStandaloneServer() { <-finish } -func createSummaryTable(spans []*cloudtrace.Span, descriptors []*validation.DescriptorStatus) summaryTable { +func createSummaryTable(spans []*cloudtrace.Span, descriptors []*validation.DescriptorStatus, + timeSeries []*validation.TimeSeriesStatus) summaryTable { return summaryTable{ Spans: spans, MetricDescriptors: descriptors, + TimeSeries: timeSeries, } } diff --git a/internal/validation/mock_metric_validation.go b/internal/validation/mock_metric_validation.go index 904d9cc..61c65d6 100644 --- a/internal/validation/mock_metric_validation.go +++ b/internal/validation/mock_metric_validation.go @@ -70,6 +70,21 @@ type PreviousPoint struct { Time time.Time } +// TimeSeriesStruct wraps a TimeSeries with the status of its creation. +// Used in the TimeSeries summary table. +type TimeSeriesStatus struct { + TimeSeries *monitoring.TimeSeries + Status string +} + +// TimeSeriesData is a wrapper struct for all the data that the server +// keeps with respect to time series. +type TimeSeriesData struct { + UploadedPoints map[string]*PreviousPoint + TimeSeriesSummary []*TimeSeriesStatus + TimeSeriesLock sync.Mutex +} + // ValidateRequiredFields verifies that the given request contains the required fields. func ValidateRequiredFields(req interface{}) error { reqReflect := reflect.ValueOf(req) @@ -261,45 +276,71 @@ func ValidateRateLimit(timeSeries []*monitoring.TimeSeries, uploadedPoints map[s } // ValidateCreateTimeSeries checks that the given TimeSeries conform to the API requirements. -func ValidateCreateTimeSeries(timeSeries []*monitoring.TimeSeries, descriptors map[string]*metric.MetricDescriptor, - uploadedPoints map[string]*PreviousPoint) error { +func ValidateCreateTimeSeries(timeSeries []*monitoring.TimeSeries, timeSeriesData *TimeSeriesData, descriptors map[string]*metric.MetricDescriptor) error { if len(timeSeries) > maxTimeSeriesPerRequest { return statusTooManyTimeSeries } + var overallErr error for _, ts := range timeSeries { + var currentErr error + // Check that required fields for time series are present. if ts.Metric == nil || len(ts.Points) != 1 || ts.Resource == nil { - return statusInvalidTimeSeries + addTimeSeriesToSummary(&timeSeriesData.TimeSeriesSummary, ts, statusInvalidTimeSeries.Error()) + currentErr = statusInvalidTimeSeries } // Check that the metric labels follow the constraints. for k, v := range ts.Metric.Labels { if len(k) > maxTimeSeriesLabelKeyBytes { - return statusInvalidTimeSeriesLabelKey + addTimeSeriesToSummary(&timeSeriesData.TimeSeriesSummary, ts, statusInvalidTimeSeriesLabelKey.Error()) + currentErr = statusInvalidTimeSeriesLabelKey } if len(v) > maxTimeSeriesLabelValueBytes { - return statusInvalidTimeSeriesLabelValue + addTimeSeriesToSummary(&timeSeriesData.TimeSeriesSummary, ts, statusInvalidTimeSeriesLabelValue.Error()) + currentErr = statusInvalidTimeSeriesLabelValue } } if err := validateMetricKind(ts, descriptors); err != nil { - return err + addTimeSeriesToSummary(&timeSeriesData.TimeSeriesSummary, ts, err.Error()) + currentErr = err } - if err := validateValueType(ts.ValueType, ts.Points[0]); err != nil { - return err + if len(ts.Points) == 1 { + if err := validateValueType(ts.ValueType, ts.Points[0]); err != nil { + addTimeSeriesToSummary(&timeSeriesData.TimeSeriesSummary, ts, err.Error()) + currentErr = err + } + + if err := validatePoint(ts, timeSeriesData.UploadedPoints); err != nil { + addTimeSeriesToSummary(&timeSeriesData.TimeSeriesSummary, ts, err.Error()) + currentErr = err + } } - if err := validatePoint(ts, uploadedPoints); err != nil { - return err + if currentErr == nil { + addTimeSeriesToSummary(&timeSeriesData.TimeSeriesSummary, ts, "OK") + } else { + overallErr = currentErr } } + if overallErr != nil { + return overallErr + } return nil } +func addTimeSeriesToSummary(summary *[]*TimeSeriesStatus, ts *monitoring.TimeSeries, err string) { + *summary = append(*summary, &TimeSeriesStatus{ + TimeSeries: ts, + Status: err, + }) +} + // validateMetricKind check that if metric_kind is present, // it is the same as the metricKind of the associated metric. func validateMetricKind(timeSeries *monitoring.TimeSeries, descriptors map[string]*metric.MetricDescriptor) error { diff --git a/server/metric/mock_metric.go b/server/metric/mock_metric.go index 7d4d33a..5d9f271 100644 --- a/server/metric/mock_metric.go +++ b/server/metric/mock_metric.go @@ -15,8 +15,6 @@ package metric import ( - "sync" - "github.com/golang/protobuf/ptypes/empty" "github.com/googleinterns/cloud-operations-api-mock/internal/validation" "golang.org/x/net/context" @@ -32,8 +30,7 @@ import ( type MockMetricServer struct { monitoring.UnimplementedMetricServiceServer metricDescriptorData *validation.MetricDescriptorData - uploadedPoints map[string]*validation.PreviousPoint - uploadedPointsLock sync.Mutex + timeSeriesData *validation.TimeSeriesData } // NewMockMetricServer creates a new MockMetricServer and returns a pointer to it. @@ -43,9 +40,12 @@ func NewMockMetricServer() *MockMetricServer { UploadedMetricDescriptors: uploadedMetricDescriptors, } uploadedPoints := make(map[string]*validation.PreviousPoint) + timeSeriesData := &validation.TimeSeriesData{ + UploadedPoints: uploadedPoints, + } return &MockMetricServer{ metricDescriptorData: metricDescriptorData, - uploadedPoints: uploadedPoints, + timeSeriesData: timeSeriesData, } } @@ -152,24 +152,21 @@ func (s *MockMetricServer) ListMetricDescriptors(ctx context.Context, req *monit // If it already exists, an error is returned. func (s *MockMetricServer) CreateTimeSeries(ctx context.Context, req *monitoring.CreateTimeSeriesRequest, ) (*empty.Empty, error) { - s.uploadedPointsLock.Lock() - if err := validation.ValidateRateLimit(req.TimeSeries, s.uploadedPoints); err != nil { - s.uploadedPointsLock.Unlock() + s.timeSeriesData.TimeSeriesLock.Lock() + defer s.timeSeriesData.TimeSeriesLock.Unlock() + if err := validation.ValidateRateLimit(req.TimeSeries, s.timeSeriesData.UploadedPoints); err != nil { return nil, err } - s.uploadedPointsLock.Unlock() if err := validation.ValidateRequiredFields(req); err != nil { return nil, err } if err := validation.ValidateProjectName(req.Name); err != nil { return nil, err } - if err := validation.ValidateCreateTimeSeries(req.TimeSeries, s.metricDescriptorData.UploadedMetricDescriptors, s.uploadedPoints); err != nil { + if err := validation.ValidateCreateTimeSeries(req.TimeSeries, s.timeSeriesData, s.metricDescriptorData.UploadedMetricDescriptors); err != nil { return nil, err } - s.uploadedPointsLock.Lock() - defer s.uploadedPointsLock.Unlock() - validation.AddPoint(req.TimeSeries, s.uploadedPoints) + validation.AddPoint(req.TimeSeries, s.timeSeriesData.UploadedPoints) return &empty.Empty{}, nil } @@ -199,3 +196,8 @@ func addMetricDescriptorToSummary(summary *[]*validation.DescriptorStatus, metri func (s *MockMetricServer) MetricDescriptorSummary() []*validation.DescriptorStatus { return s.metricDescriptorData.MetricDescriptorSummary } + +// TimeSeriesSummary returns the time series data to display in the summary page. +func (s *MockMetricServer) TimeSeriesSummary() []*validation.TimeSeriesStatus { + return s.timeSeriesData.TimeSeriesSummary +} diff --git a/static/css/main.css b/static/css/main.css index 20347df..92b849a 100755 --- a/static/css/main.css +++ b/static/css/main.css @@ -92,3 +92,10 @@ th, td { color: #808080; line-height: 1.4; } + +.title { + color: #6c7ae0; + text-decoration: underline; + padding-top: 50px; + padding-bottom: 50px; +} \ No newline at end of file diff --git a/static/summary_template.html b/static/summary_template.html index b1f5d7b..75bc4c1 100755 --- a/static/summary_template.html +++ b/static/summary_template.html @@ -10,7 +10,7 @@
-

Trace Summary

+

Trace

@@ -43,7 +43,7 @@

Trace Summary

-

Metric Descriptors Summary

+

Metric Descriptors

@@ -56,7 +56,6 @@

Metric Descriptors Summa

-
@@ -75,6 +74,49 @@

Metric Descriptors Summa

+ + +

Time Series

+
+
+ + + + + + + + + + + +
Metric TypeMetric LabelsMonitored Resource TypeMonitored Resource LabelsPoint IntervalStatus
+
+
+ + + {{ range $index, $ts := .TimeSeries }} + {{ if eq $ts.Status "OK" }} + + {{ else }} + + {{ end }} + + + + + {{ if $ts.TimeSeries.Points }} + + {{ else }} + + {{ end }} + + + {{ end }} + +
{{ $ts.TimeSeries.Metric.Type }}{{ $ts.TimeSeries.Metric.Labels }}{{ $ts.TimeSeries.Resource.Type }}{{ $ts.TimeSeries.Resource.Labels }}{{ (index $ts.TimeSeries.Points 0).Interval }}Missing Point{{ $ts.Status }}
+
+