container_images/concourse-metrics/pkg/requests/requests.go (174 lines of code) (raw):
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package requests
import (
"fmt"
"strings"
"time"
"github.com/golang/protobuf/ptypes/timestamp"
metricpb "google.golang.org/genproto/googleapis/api/metric"
"google.golang.org/genproto/googleapis/api/monitoredres"
monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3"
)
// JobResultArgs defines the required and optional arguments for building a new
// job result request (in the form of a cloud monitoring time series request).
type JobResultArgs struct {
EndTimestamp *int64 // Optional - will default to now in ms.
Job string
MetricPath string
Pipeline string
ProjectID string
ResultState string
StartTimestamp int64 // Epoch Milliseconds.
Task string
Zone string
}
func validateJobResultRequestInput(input *JobResultArgs) error {
if strings.TrimSpace(input.ProjectID) == "" {
return fmt.Errorf("empty project-id value")
}
if strings.TrimSpace(input.Zone) == "" {
return fmt.Errorf("empty zone value")
}
if strings.TrimSpace(input.Pipeline) == "" {
return fmt.Errorf("empty pipeline value")
}
if strings.TrimSpace(input.Job) == "" {
return fmt.Errorf("empty job value")
}
if strings.TrimSpace(input.Task) == "" {
return fmt.Errorf("empty task value")
}
if strings.TrimSpace(input.MetricPath) == "" {
return fmt.Errorf("empty metric-path value")
}
state := strings.TrimSpace(input.ResultState)
if state != "success" && state != "failure" {
return fmt.Errorf("invalid state value")
}
// Don't let the end timestamp occur before the start timestamp.
if input.EndTimestamp != nil && *input.EndTimestamp < input.StartTimestamp {
return fmt.Errorf("end-timestamp cannot occur before start-timestamp")
}
return nil
}
// BuildJobResultRequest builds a new job result request object to submit to gcp cloud monitoring.
func BuildJobResultRequest(input JobResultArgs) (*monitoringpb.CreateTimeSeriesRequest, error) {
// Provide a default for the endTimestamp if one was not provided.
var endTimestamp int64
if input.EndTimestamp == nil {
endTimestamp = time.Now().UnixNano() / 1000000
} else {
endTimestamp = *input.EndTimestamp
}
if e := validateJobResultRequestInput(&input); e != nil {
return nil, e
}
return &monitoringpb.CreateTimeSeriesRequest{
Name: "projects/" + input.ProjectID,
TimeSeries: []*monitoringpb.TimeSeries{{
Metric: &metricpb.Metric{
Type: "custom.googleapis.com/" + input.MetricPath,
Labels: map[string]string{
"result_state": input.ResultState,
},
},
Resource: &monitoredres.MonitoredResource{
Type: "generic_task",
Labels: map[string]string{
"project_id": input.ProjectID,
"location": input.Zone,
"namespace": input.Pipeline,
"job": input.Job,
"task_id": input.Task,
},
},
Points: []*monitoringpb.Point{{
Interval: &monitoringpb.TimeInterval{
EndTime: ×tamp.Timestamp{
Seconds: endTimestamp / 1000,
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_Int64Value{
// Int64 value here is the duration (measured in ms).
Int64Value: endTimestamp - input.StartTimestamp,
},
},
}},
}},
}, nil
}
// CoverageArgs defines the required and optional arguments for building a new
// coverage request (in the form of a cloud monitoring time series request).
type CoverageArgs struct {
EndTimestamp *int64 // Optional - will default to now in ms.
MetricPath string
PackageName string
CoveragePercent int64
ProjectID string
}
// BuildCoverageRequest builds a new job result request object to submit to gcp cloud monitoring.
func BuildCoverageRequest(input CoverageArgs) (*monitoringpb.CreateTimeSeriesRequest, error) {
// Provide a default for the endTimestamp if one was not provided.
var endTimestamp int64
if input.EndTimestamp == nil {
endTimestamp = time.Now().UnixNano() / 1000000
} else {
endTimestamp = *input.EndTimestamp
}
return &monitoringpb.CreateTimeSeriesRequest{
Name: "projects/" + input.ProjectID,
TimeSeries: []*monitoringpb.TimeSeries{{
Metric: &metricpb.Metric{
Type: "custom.googleapis.com/" + input.MetricPath,
},
Resource: &monitoredres.MonitoredResource{
Type: "generic_task",
Labels: map[string]string{
"package_name": input.PackageName,
},
},
Points: []*monitoringpb.Point{{
Interval: &monitoringpb.TimeInterval{
EndTime: ×tamp.Timestamp{
Seconds: endTimestamp / 1000,
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_Int64Value{
// Int64 value here is the coverage (in percent of statements).
Int64Value: input.CoveragePercent,
},
},
}},
}},
}, nil
}
// LastPublishedArgs defines the required and optional arguments for building a new
// last published request (in the form of a cloud monitoring time series request).
type LastPublishedArgs struct {
MetricPath string
ProjectID string
PackageName string
LastPublished string
}
// BuildLastPublishedRequest builds a new job result request object to submit to gcp cloud monitoring.
func BuildLastPublishedRequest(input LastPublishedArgs) (*monitoringpb.CreateTimeSeriesRequest, error) {
now := time.Now().Unix()
lastPublished, err := time.Parse("20060102", input.LastPublished)
if err != nil {
return nil, err
}
lastPublishedSecs := lastPublished.Unix()
return &monitoringpb.CreateTimeSeriesRequest{
Name: "projects/" + input.ProjectID,
TimeSeries: []*monitoringpb.TimeSeries{{
Metric: &metricpb.Metric{
Type: "custom.googleapis.com/" + input.MetricPath,
},
Resource: &monitoredres.MonitoredResource{
Type: "generic_task",
Labels: map[string]string{
"package_name": input.PackageName,
},
},
Points: []*monitoringpb.Point{{
Interval: &monitoringpb.TimeInterval{
EndTime: ×tamp.Timestamp{
Seconds: now,
},
},
Value: &monitoringpb.TypedValue{
Value: &monitoringpb.TypedValue_Int64Value{
// Int64 value here is the time since last release (measured in seconds).
Int64Value: now - lastPublishedSecs,
},
},
}},
}},
}, nil
}