diff --git a/cmd/mock_server.go b/cmd/mock_server.go index fb84e62..fbfe1c0 100644 --- a/cmd/mock_server.go +++ b/cmd/mock_server.go @@ -24,7 +24,7 @@ import ( "syscall" mocktrace "github.com/googleinterns/cloud-operations-api-mock/api" - + "github.com/googleinterns/cloud-operations-api-mock/internal/validation" "github.com/googleinterns/cloud-operations-api-mock/server/metric" "github.com/googleinterns/cloud-operations-api-mock/server/trace" "google.golang.org/genproto/googleapis/devtools/cloudtrace/v2" @@ -43,6 +43,13 @@ var ( "If flag is set, a summary page HTML file will be generated") ) +// summaryTable wraps the summaries for both trace and metrics, +// and is used to pass data to the HTML template. +type summaryTable struct { + Spans []*cloudtrace.Span + MetricDescriptors []*validation.DescriptorStatus +} + func main() { flag.Parse() startStandaloneServer() @@ -60,10 +67,13 @@ func startStandaloneServer() { } grpcServer := grpc.NewServer() + mockTraceServer := trace.NewMockTraceServer() cloudtrace.RegisterTraceServiceServer(grpcServer, mockTraceServer) mocktrace.RegisterMockTraceServiceServer(grpcServer, mockTraceServer) - monitoring.RegisterMetricServiceServer(grpcServer, metric.NewMockMetricServer()) + + mockMetricServer := metric.NewMockMetricServer() + monitoring.RegisterMetricServiceServer(grpcServer, mockMetricServer) log.Printf("Listening on %s\n", lis.Addr().String()) @@ -77,7 +87,8 @@ func startStandaloneServer() { <-sig grpcServer.GracefulStop() if *summary { - writeSummaryPage(mockTraceServer.ResultTable()) + summaryTable := createSummaryTable(mockTraceServer.SpansSummary(), mockMetricServer.MetricDescriptorSummary()) + writeSummaryPage(summaryTable) } finish <- true }() @@ -88,14 +99,21 @@ func startStandaloneServer() { <-finish } +func createSummaryTable(spans []*cloudtrace.Span, descriptors []*validation.DescriptorStatus) summaryTable { + return summaryTable{ + Spans: spans, + MetricDescriptors: descriptors, + } +} + // writeSummaryPage creates summary.html from the results and the template HTML. -func writeSummaryPage(results []*cloudtrace.Span) { +func writeSummaryPage(table summaryTable) { outputFile, err := os.Create("../static/summary.html") if err != nil { panic(err) } t := template.Must(template.ParseFiles("../static/summary_template.html")) - err = t.Execute(outputFile, results) + err = t.Execute(outputFile, table) if err != nil { panic(err) } diff --git a/internal/validation/mock_metric_validation.go b/internal/validation/mock_metric_validation.go index b3d824f..904d9cc 100644 --- a/internal/validation/mock_metric_validation.go +++ b/internal/validation/mock_metric_validation.go @@ -19,6 +19,7 @@ import ( "reflect" "regexp" "strings" + "sync" "time" "github.com/golang/protobuf/ptypes" @@ -42,11 +43,26 @@ const ( // Service name regex sourced from https://github.com/asaskevich/govalidator/blob/master/patterns.go#L33 var ( labelKeyRegex = regexp.MustCompile("[a-z][a-zA0-9_-]*") - serviceNameRegex = regexp.MustCompile(`^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`) + serviceNameRegex = regexp.MustCompile(`^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[._]?$`) relativeMetricNameRegex = regexp.MustCompile("[A-Za-z0-9_/]{1,100}") - diplayNameRegex = regexp.MustCompile(`opentelemetry\/.*`) + displayNameRegex = regexp.MustCompile(`opentelemetry/.*`) ) +// MetricDescriptorStatus wraps a MetricDescriptor with the status of its creation. +// Used in the MetricDescriptor summary table. +type DescriptorStatus struct { + MetricDescriptor *metric.MetricDescriptor + Status string +} + +// MetricDescriptorData is a wrapper struct for all the data that the server +// keeps with respect to metric descriptors. +type MetricDescriptorData struct { + UploadedMetricDescriptors map[string]*metric.MetricDescriptor + MetricDescriptorSummary []*DescriptorStatus + MetricDescriptorsLock sync.Mutex +} + // PreviousPoint contains information about the most recently uploaded // point for a time series. type PreviousPoint struct { @@ -142,7 +158,7 @@ func validateLabels(labels []*lbl.LabelDescriptor) error { } func validateMetricDisplayName(displayName string) error { - if !diplayNameRegex.MatchString(displayName) { + if !displayNameRegex.MatchString(displayName) { return statusInvalidDisplayName } diff --git a/server/metric/mock_metric.go b/server/metric/mock_metric.go index 4714b17..7d4d33a 100644 --- a/server/metric/mock_metric.go +++ b/server/metric/mock_metric.go @@ -31,19 +31,21 @@ import ( // uploaded data. type MockMetricServer struct { monitoring.UnimplementedMetricServiceServer - uploadedMetricDescriptors map[string]*metric.MetricDescriptor - uploadedMetricDescriptorsLock sync.Mutex - uploadedPoints map[string]*validation.PreviousPoint - uploadedPointsLock sync.Mutex + metricDescriptorData *validation.MetricDescriptorData + uploadedPoints map[string]*validation.PreviousPoint + uploadedPointsLock sync.Mutex } // NewMockMetricServer creates a new MockMetricServer and returns a pointer to it. func NewMockMetricServer() *MockMetricServer { uploadedMetricDescriptors := make(map[string]*metric.MetricDescriptor) + metricDescriptorData := &validation.MetricDescriptorData{ + UploadedMetricDescriptors: uploadedMetricDescriptors, + } uploadedPoints := make(map[string]*validation.PreviousPoint) return &MockMetricServer{ - uploadedMetricDescriptors: uploadedMetricDescriptors, - uploadedPoints: uploadedPoints, + metricDescriptorData: metricDescriptorData, + uploadedPoints: uploadedPoints, } } @@ -77,9 +79,9 @@ func (s *MockMetricServer) GetMetricDescriptor(ctx context.Context, req *monitor return nil, err } - s.uploadedMetricDescriptorsLock.Lock() - defer s.uploadedMetricDescriptorsLock.Unlock() - metricDescriptor, err := validation.AccessMetricDescriptor(s.uploadedMetricDescriptors, req.Name) + s.metricDescriptorData.MetricDescriptorsLock.Lock() + defer s.metricDescriptorData.MetricDescriptorsLock.Unlock() + metricDescriptor, err := validation.AccessMetricDescriptor(s.metricDescriptorData.UploadedMetricDescriptors, req.Name) if err != nil { return nil, err } @@ -92,23 +94,28 @@ func (s *MockMetricServer) GetMetricDescriptor(ctx context.Context, req *monitor func (s *MockMetricServer) CreateMetricDescriptor(ctx context.Context, req *monitoring.CreateMetricDescriptorRequest, ) (*metric.MetricDescriptor, error) { if err := validation.ValidateRequiredFields(req); err != nil { + addMetricDescriptorToSummary(&s.metricDescriptorData.MetricDescriptorSummary, req.MetricDescriptor, err.Error()) return nil, err } if err := validation.ValidateProjectName(req.Name); err != nil { + addMetricDescriptorToSummary(&s.metricDescriptorData.MetricDescriptorSummary, req.MetricDescriptor, err.Error()) return nil, err } if err := validation.ValidateCreateMetricDescriptor(req.MetricDescriptor); err != nil { + addMetricDescriptorToSummary(&s.metricDescriptorData.MetricDescriptorSummary, req.MetricDescriptor, err.Error()) return nil, err } - s.uploadedMetricDescriptorsLock.Lock() - defer s.uploadedMetricDescriptorsLock.Unlock() - if err := validation.AddMetricDescriptor(s.uploadedMetricDescriptors, req.MetricDescriptor.Type, req.MetricDescriptor); err != nil { + s.metricDescriptorData.MetricDescriptorsLock.Lock() + defer s.metricDescriptorData.MetricDescriptorsLock.Unlock() + if err := validation.AddMetricDescriptor(s.metricDescriptorData.UploadedMetricDescriptors, req.MetricDescriptor.Type, req.MetricDescriptor); err != nil { + addMetricDescriptorToSummary(&s.metricDescriptorData.MetricDescriptorSummary, req.MetricDescriptor, err.Error()) return nil, err } + addMetricDescriptorToSummary(&s.metricDescriptorData.MetricDescriptorSummary, req.MetricDescriptor, "OK") return req.MetricDescriptor, nil } @@ -120,9 +127,9 @@ func (s *MockMetricServer) DeleteMetricDescriptor(ctx context.Context, req *moni return nil, err } - s.uploadedMetricDescriptorsLock.Lock() - defer s.uploadedMetricDescriptorsLock.Unlock() - if err := validation.RemoveMetricDescriptor(s.uploadedMetricDescriptors, req.Name); err != nil { + s.metricDescriptorData.MetricDescriptorsLock.Lock() + defer s.metricDescriptorData.MetricDescriptorsLock.Unlock() + if err := validation.RemoveMetricDescriptor(s.metricDescriptorData.UploadedMetricDescriptors, req.Name); err != nil { return nil, err } @@ -157,7 +164,7 @@ func (s *MockMetricServer) CreateTimeSeries(ctx context.Context, req *monitoring if err := validation.ValidateProjectName(req.Name); err != nil { return nil, err } - if err := validation.ValidateCreateTimeSeries(req.TimeSeries, s.uploadedMetricDescriptors, s.uploadedPoints); err != nil { + if err := validation.ValidateCreateTimeSeries(req.TimeSeries, s.metricDescriptorData.UploadedMetricDescriptors, s.uploadedPoints); err != nil { return nil, err } s.uploadedPointsLock.Lock() @@ -178,3 +185,17 @@ func (s *MockMetricServer) ListTimeSeries(ctx context.Context, req *monitoring.L ExecutionErrors: []*status.Status{}, }, nil } + +// addMetricDescriptorToSummary adds the given metric descriptor and status to the summary. +func addMetricDescriptorToSummary(summary *[]*validation.DescriptorStatus, metricDescriptor *metric.MetricDescriptor, err string) { + metricDescriptorStatus := &validation.DescriptorStatus{ + MetricDescriptor: metricDescriptor, + Status: err, + } + *summary = append(*summary, metricDescriptorStatus) +} + +// MetricDescriptorSummary returns the metric descriptor data to display in the summary page. +func (s *MockMetricServer) MetricDescriptorSummary() []*validation.DescriptorStatus { + return s.metricDescriptorData.MetricDescriptorSummary +} diff --git a/server/trace/mock_trace.go b/server/trace/mock_trace.go index 2d7d62c..ecb508b 100644 --- a/server/trace/mock_trace.go +++ b/server/trace/mock_trace.go @@ -123,6 +123,6 @@ func (s *MockTraceServer) SetOnUpload(onUpload func(ctx context.Context, spans [ s.onUpload = onUpload } -func (s *MockTraceServer) ResultTable() []*cloudtrace.Span { +func (s *MockTraceServer) SpansSummary() []*cloudtrace.Span { return s.spanData.SpansSummary }