Skip to content
This repository has been archived by the owner on Oct 26, 2022. It is now read-only.

Commit

Permalink
Add time series to summary (#54)
Browse files Browse the repository at this point in the history
  • Loading branch information
the-ericwang35 authored Aug 11, 2020
1 parent db0a0ed commit e8bcc34
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 29 deletions.
8 changes: 6 additions & 2 deletions cmd/mock_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var (
type summaryTable struct {
Spans []*cloudtrace.Span
MetricDescriptors []*validation.DescriptorStatus
TimeSeries []*validation.TimeSeriesStatus
}

func main() {
Expand Down Expand Up @@ -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
Expand All @@ -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,
}
}

Expand Down
61 changes: 51 additions & 10 deletions internal/validation/mock_metric_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
28 changes: 15 additions & 13 deletions server/metric/mock_metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand All @@ -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,
}
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}
9 changes: 8 additions & 1 deletion static/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ body, html {
align-items: center;
justify-content: center;
flex-wrap: wrap;
padding: 33px 30px;
padding: 0 60px 60px;
}

.summary {
Expand Down Expand Up @@ -92,3 +92,10 @@ th, td {
color: #808080;
line-height: 1.4;
}

.title {
color: #6c7ae0;
text-decoration: underline;
padding-top: 50px;
padding-bottom: 50px;
}
48 changes: 45 additions & 3 deletions static/summary_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div class="container-summary">

<!-- Trace table -->
<h1 style="color: #6c7ae0; text-decoration: underline;">Trace Summary</h1>
<h1 class="title">Trace</h1>
<div class="summary">
<div class="summary-head">
<table>
Expand Down Expand Up @@ -43,7 +43,7 @@ <h1 style="color: #6c7ae0; text-decoration: underline;">Trace Summary</h1>
</div>

<!-- Metric Descriptor table -->
<h1 style="color: #6c7ae0; text-decoration: underline;">Metric Descriptors Summary</h1>
<h1 class="title">Metric Descriptors</h1>
<div class="summary">
<div class="summary-head">
<table>
Expand All @@ -56,7 +56,6 @@ <h1 style="color: #6c7ae0; text-decoration: underline;">Metric Descriptors Summa
</thead>
</table>
</div>

<div class="summary-body">
<table>
<tbody>
Expand All @@ -75,6 +74,49 @@ <h1 style="color: #6c7ae0; text-decoration: underline;">Metric Descriptors Summa
</table>
</div>
</div>

<!-- Time Series table -->
<h1 class="title">Time Series</h1>
<div class="summary">
<div class="summary-head">
<table>
<thead>
<tr class="head">
<th class="cell">Metric Type</th>
<th class="cell">Metric Labels</th>
<th class="cell">Monitored Resource Type</th>
<th class="cell">Monitored Resource Labels</th>
<th class="cell">Point Interval</th>
<th class="cell">Status</th>
</tr>
</thead>
</table>
</div>
<div class="summary-body">
<table>
<tbody>
{{ range $index, $ts := .TimeSeries }}
{{ if eq $ts.Status "OK" }}
<tr class="body">
{{ else }}
<tr class="body" style="border: solid 2px red;">
{{ end }}
<td class="cell">{{ $ts.TimeSeries.Metric.Type }}</td>
<td class="cell">{{ $ts.TimeSeries.Metric.Labels }}</td>
<td class="cell">{{ $ts.TimeSeries.Resource.Type }}</td>
<td class="cell">{{ $ts.TimeSeries.Resource.Labels }}</td>
{{ if $ts.TimeSeries.Points }}
<td class="cell">{{ (index $ts.TimeSeries.Points 0).Interval }}</td>
{{ else }}
<td class="cell">Missing Point</td>
{{ end }}
<td class="cell">{{ $ts.Status }}</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>

0 comments on commit e8bcc34

Please sign in to comment.