diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5645f24..879f29e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,13 +8,6 @@ jobs: - uses: actions/setup-go@v2 with: go-version: '^1.16.2' - - run: go test -v ./tsplot/... - tscli: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v2 - with: - go-version: '^1.16.2' - - run: go test -v ./tscli/... - + - run: make test + - run: make build + - run: make codegendiff diff --git a/Makefile b/Makefile index e6e95fe..3664c77 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,36 @@ +BINDIR ?= ./bin +SCRIPTDIR := ./scripts +SHELL := /bin/bash +GOBIN := $(shell which go 2> /dev/null) +ifeq ($(GOBIN),) +GOBIN := /usr/local/go/bin/go +endif + .PHONY: test test: - go test -v ./... + $(GOBIN) test -v ./... .PHONY: build -build: +build: $(BINDIR)/codegen make -C tscli +$(BINDIR)/codegen: $(SCRIPTDIR)/codegen.go + @mkdir -p $(BINDIR) + GOOS=linux GOARCH=amd64 $(GOBIN) build -o $(BINDIR)/codegen $< + .PHONY: install install: build - cp ./bin/tscli /usr/local/bin/ + cp $(BINDIR)/tscli /usr/local/bin/ .PHONY: clean clean: - rm -rf ./bin + rm -rf $(BINDIR) + +CODEGENFILE="set_aggregation_opts.go" +.PHONY: codegen +codegen: + $(BINDIR)/codegen -output ./tsplot/$(CODEGENFILE) + +.PHONY: codegendiff +codegendiff: + diff ./tsplot/$(CODEGENFILE) <($(BINDIR)/codegen -stdout) diff --git a/scripts/codegen.go b/scripts/codegen.go new file mode 100644 index 0000000..2afd13d --- /dev/null +++ b/scripts/codegen.go @@ -0,0 +1,67 @@ +package main +/* +codegen.go creates Set_* API methods for Google Cloud Monitoring aggregation alignment and reduction options. +Leverages the exported variables in the Google monitoring/v3 package (common.pb.go) to ensure that all +alignment and reduction options are covered. +*/ + +import ( + "flag" + "log" + "os" + "text/template" + + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" +) + +var rawTpl = `/* +DO NOT EDIT +Generated by codegen.go +https://github.com/googleapis/go-genproto/blob/0135a39c27378c1b903c75204eff61a060be5eb7/googleapis/monitoring/v3/common.pb.go +*/ +package tsplot +{{range $name, $value := .Aligners}} +func (mq *MetricQuery) Set_{{$name}}() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner({{$value}})) +} +{{end -}} +{{range $name, $value := .Reducers}} +func (mq *MetricQuery) Set_{{$name}}() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer({{$value}})) +} +{{end}} +` + +func main() { + outFileName := flag.String("output", "./tsplot/set_aggregation_opts.go", "Output path of generated file.") + toStdout := flag.Bool("stdout", false, "Toggle output to STDOUT.") + flag.Parse() + + var outFile *os.File + var fileErr error + + if *toStdout { + outFile = os.Stdout + } else { + outFile, fileErr = os.OpenFile(*outFileName, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644) + if fileErr != nil { + log.Print(fileErr) + } + defer outFile.Close() + } + + values := struct { + Aligners map[string]int32 + Reducers map[string]int32 + }{ + Aligners: monitoringpb.Aggregation_Aligner_value, + Reducers: monitoringpb.Aggregation_Reducer_value, + } + + t := template.New("codegen") + pt := template.Must(t.Parse(rawTpl)) + err := pt.Execute(outFile, values) + if err != nil { + log.Fatal(err) + } +} diff --git a/tscli/Makefile b/tscli/Makefile index f81515a..37fcc60 100644 --- a/tscli/Makefile +++ b/tscli/Makefile @@ -1,8 +1,13 @@ BINDIR ?= ../bin +SHELL := /bin/bash +GOBIN := $(shell which go 2> /dev/null) +ifeq ($(GOBIN),) +GOBIN := /usr/local/go/bin/go +endif default: build .PHONY: build build: @mkdir -p $(BINDIR) - GOOS=linux GOARCH=amd64 /usr/local/go/bin/go build -o $(BINDIR)/tscli + GOOS=linux GOARCH=amd64 $(GOBIN) build -o $(BINDIR)/tscli diff --git a/tscli/main.go b/tscli/main.go index 6024527..d35c577 100644 --- a/tscli/main.go +++ b/tscli/main.go @@ -114,18 +114,20 @@ func executeQuery(cmd *cobra.Command, args []string) error { st := parseTime(startTime) et := parseTime(endTime) - query := tsplot.MetricQuery{ - Project: project, - MetricDescriptor: fmt.Sprintf("custom.googleapis.com/opencensus/%s/%s/%s", app, service, metric), - StartTime: &st, - EndTime: &et, - } + query := tsplot.NewMetricQuery( + project, + fmt.Sprintf("custom.googleapis.com/opencensus/%s/%s/%s", app, service, metric), + &st, + &et, + ) if queryOverride != "" { query.SetQueryFilter(queryOverride) } - query.SetReduce(reduce) + if !reduce { + query.Set_REDUCE_NONE() + } tsi, err := query.PerformWithClient(GoogleCloudMonitoringClient) if err != nil { diff --git a/tsplot/options.go b/tsplot/options.go index e6162aa..eb85322 100644 --- a/tsplot/options.go +++ b/tsplot/options.go @@ -1,9 +1,13 @@ package tsplot import ( + "image/color" + "time" + "gonum.org/v1/plot" "gonum.org/v1/plot/plotter" - "image/color" + monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3" + "google.golang.org/protobuf/types/known/durationpb" ) // PlotOption defines the type used to configure the underlying *plot.Plot. @@ -91,3 +95,28 @@ func ApplyDefaultHighContrast(p *plot.Plot) { opt(p) } } + +// aggregationOption defines the type used to configure the underlying *monitoringpb.Aggregation. +// A function that returns aggregationOption can be used to set options on the *monitoringpb.Aggregation. +type aggregationOption func(agg *monitoringpb.Aggregation) + +// withAlignmentPeriod sets the duration of the aggregation's alignment period. +func withAlignmentPeriod(d time.Duration) aggregationOption { + return func(agg *monitoringpb.Aggregation) { + agg.AlignmentPeriod = durationpb.New(d) + } +} + +// withPerSeriesAligner sets the alignment method used for the time series. +func withPerSeriesAligner(aligner monitoringpb.Aggregation_Aligner) aggregationOption { + return func(agg *monitoringpb.Aggregation) { + agg.PerSeriesAligner = aligner + } +} + +// withCrossSeriesReducer sets the reduction method used for the time series. +func withCrossSeriesReducer(reducer monitoringpb.Aggregation_Reducer) aggregationOption { + return func(agg *monitoringpb.Aggregation) { + agg.CrossSeriesReducer = reducer + } +} diff --git a/tsplot/query.go b/tsplot/query.go index b65dd58..cc5ae41 100644 --- a/tsplot/query.go +++ b/tsplot/query.go @@ -29,7 +29,18 @@ type MetricQuery struct { EndTime *time.Time queryFilter string - reduce bool + aggregation *[]aggregationOption +} + +// NewMetricQuery creates a new MetricQuery type with the aggregation opts initialized. +func NewMetricQuery(project, metric string, startTime, endTime *time.Time) *MetricQuery { + return &MetricQuery{ + Project: project, + MetricDescriptor: metric, + StartTime: startTime, + EndTime: endTime, + aggregation: &[]aggregationOption{}, + } } // SetQueryFilter provides a hook to modify the metric query filter. @@ -37,10 +48,9 @@ func (mq *MetricQuery) SetQueryFilter(queryFilter string) { mq.queryFilter = queryFilter } -// SetReduce sets the boolean reduce value on *MetricQuery structure. This controls whether or not cross series reduction is -// performed on multiple time series. -func (mq *MetricQuery) SetReduce(b bool) { - mq.reduce = b +// SetAlignmentPeriod sets the alignment duration. +func (mq *MetricQuery) SetAlignmentPeriod(d time.Duration) { + *mq.aggregation = append(*mq.aggregation, withAlignmentPeriod(d)) } // request builds and returns a *monitoringpb.ListTimeSeriesRequest. @@ -88,8 +98,8 @@ func (mq *MetricQuery) request() (*monitoringpb.ListTimeSeriesRequest, error) { View: monitoringpb.ListTimeSeriesRequest_FULL, } - if !mq.reduce { - tsreq.Aggregation.CrossSeriesReducer = 0 + for _, opt := range *mq.aggregation { + opt(tsreq.Aggregation) } return &tsreq, nil diff --git a/tsplot/set_aggregation_opts.go b/tsplot/set_aggregation_opts.go new file mode 100644 index 0000000..5d54eff --- /dev/null +++ b/tsplot/set_aggregation_opts.go @@ -0,0 +1,139 @@ +/* +DO NOT EDIT +Generated by codegen.go +https://github.com/googleapis/go-genproto/blob/0135a39c27378c1b903c75204eff61a060be5eb7/googleapis/monitoring/v3/common.pb.go +*/ +package tsplot + +func (mq *MetricQuery) Set_ALIGN_COUNT() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(13)) +} + +func (mq *MetricQuery) Set_ALIGN_COUNT_FALSE() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(24)) +} + +func (mq *MetricQuery) Set_ALIGN_COUNT_TRUE() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(16)) +} + +func (mq *MetricQuery) Set_ALIGN_DELTA() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(1)) +} + +func (mq *MetricQuery) Set_ALIGN_FRACTION_TRUE() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(17)) +} + +func (mq *MetricQuery) Set_ALIGN_INTERPOLATE() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(3)) +} + +func (mq *MetricQuery) Set_ALIGN_MAX() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(11)) +} + +func (mq *MetricQuery) Set_ALIGN_MEAN() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(12)) +} + +func (mq *MetricQuery) Set_ALIGN_MIN() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(10)) +} + +func (mq *MetricQuery) Set_ALIGN_NEXT_OLDER() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(4)) +} + +func (mq *MetricQuery) Set_ALIGN_NONE() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(0)) +} + +func (mq *MetricQuery) Set_ALIGN_PERCENTILE_05() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(21)) +} + +func (mq *MetricQuery) Set_ALIGN_PERCENTILE_50() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(20)) +} + +func (mq *MetricQuery) Set_ALIGN_PERCENTILE_95() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(19)) +} + +func (mq *MetricQuery) Set_ALIGN_PERCENTILE_99() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(18)) +} + +func (mq *MetricQuery) Set_ALIGN_PERCENT_CHANGE() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(23)) +} + +func (mq *MetricQuery) Set_ALIGN_RATE() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(2)) +} + +func (mq *MetricQuery) Set_ALIGN_STDDEV() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(15)) +} + +func (mq *MetricQuery) Set_ALIGN_SUM() { + *mq.aggregation = append(*mq.aggregation, withPerSeriesAligner(14)) +} + +func (mq *MetricQuery) Set_REDUCE_COUNT() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(6)) +} + +func (mq *MetricQuery) Set_REDUCE_COUNT_FALSE() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(15)) +} + +func (mq *MetricQuery) Set_REDUCE_COUNT_TRUE() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(7)) +} + +func (mq *MetricQuery) Set_REDUCE_FRACTION_TRUE() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(8)) +} + +func (mq *MetricQuery) Set_REDUCE_MAX() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(3)) +} + +func (mq *MetricQuery) Set_REDUCE_MEAN() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(1)) +} + +func (mq *MetricQuery) Set_REDUCE_MIN() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(2)) +} + +func (mq *MetricQuery) Set_REDUCE_NONE() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(0)) +} + +func (mq *MetricQuery) Set_REDUCE_PERCENTILE_05() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(12)) +} + +func (mq *MetricQuery) Set_REDUCE_PERCENTILE_50() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(11)) +} + +func (mq *MetricQuery) Set_REDUCE_PERCENTILE_95() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(10)) +} + +func (mq *MetricQuery) Set_REDUCE_PERCENTILE_99() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(9)) +} + +func (mq *MetricQuery) Set_REDUCE_STDDEV() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(5)) +} + +func (mq *MetricQuery) Set_REDUCE_SUM() { + *mq.aggregation = append(*mq.aggregation, withCrossSeriesReducer(4)) +} +