2020-02-03 17:39:01 +00:00
|
|
|
package pkger
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-06-16 17:57:07 +00:00
|
|
|
"net/url"
|
2020-06-16 19:24:11 +00:00
|
|
|
"path"
|
2020-06-16 17:57:07 +00:00
|
|
|
"strings"
|
2020-02-03 17:39:01 +00:00
|
|
|
|
2020-04-03 17:39:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2"
|
|
|
|
"github.com/influxdata/influxdb/v2/kit/metric"
|
|
|
|
"github.com/influxdata/influxdb/v2/kit/prom"
|
2020-06-16 17:57:07 +00:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2020-02-03 17:39:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type mwMetrics struct {
|
|
|
|
// RED metrics
|
|
|
|
rec *metric.REDClient
|
|
|
|
|
|
|
|
next SVC
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ SVC = (*mwMetrics)(nil)
|
|
|
|
|
|
|
|
// MWMetrics is a metrics service middleware for the notification endpoint service.
|
|
|
|
func MWMetrics(reg *prom.Registry) SVCMiddleware {
|
|
|
|
return func(svc SVC) SVC {
|
|
|
|
return &mwMetrics{
|
2020-06-16 17:57:07 +00:00
|
|
|
rec: metric.New(reg, "pkger", metric.WithVec(templateVec())),
|
2020-02-03 17:39:01 +00:00
|
|
|
next: svc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-20 23:20:53 +00:00
|
|
|
func (s *mwMetrics) InitStack(ctx context.Context, userID influxdb.ID, newStack Stack) (Stack, error) {
|
2020-03-20 02:08:35 +00:00
|
|
|
rec := s.rec.Record("init_stack")
|
2020-03-20 23:20:53 +00:00
|
|
|
stack, err := s.next.InitStack(ctx, userID, newStack)
|
2020-03-20 02:08:35 +00:00
|
|
|
return stack, rec(err)
|
|
|
|
}
|
|
|
|
|
2020-05-01 21:40:32 +00:00
|
|
|
func (s *mwMetrics) DeleteStack(ctx context.Context, identifiers struct{ OrgID, UserID, StackID influxdb.ID }) error {
|
|
|
|
rec := s.rec.Record("delete_stack")
|
|
|
|
return rec(s.next.DeleteStack(ctx, identifiers))
|
|
|
|
}
|
|
|
|
|
2020-06-01 21:58:36 +00:00
|
|
|
func (s *mwMetrics) ExportStack(ctx context.Context, orgID, stackID influxdb.ID) (*Pkg, error) {
|
|
|
|
rec := s.rec.Record("export_stack")
|
|
|
|
pkg, err := s.next.ExportStack(ctx, orgID, stackID)
|
|
|
|
return pkg, rec(err)
|
|
|
|
}
|
|
|
|
|
2020-04-30 04:50:52 +00:00
|
|
|
func (s *mwMetrics) ListStacks(ctx context.Context, orgID influxdb.ID, f ListFilter) ([]Stack, error) {
|
|
|
|
rec := s.rec.Record("list_stacks")
|
|
|
|
stacks, err := s.next.ListStacks(ctx, orgID, f)
|
|
|
|
return stacks, rec(err)
|
|
|
|
}
|
|
|
|
|
2020-02-03 17:39:01 +00:00
|
|
|
func (s *mwMetrics) CreatePkg(ctx context.Context, setters ...CreatePkgSetFn) (*Pkg, error) {
|
|
|
|
rec := s.rec.Record("create_pkg")
|
|
|
|
pkg, err := s.next.CreatePkg(ctx, setters...)
|
|
|
|
return pkg, rec(err)
|
|
|
|
}
|
|
|
|
|
2020-06-15 21:13:38 +00:00
|
|
|
func (s *mwMetrics) DryRun(ctx context.Context, orgID, userID influxdb.ID, opts ...ApplyOptFn) (PkgImpactSummary, error) {
|
2020-02-03 17:39:01 +00:00
|
|
|
rec := s.rec.Record("dry_run")
|
2020-06-15 21:13:38 +00:00
|
|
|
impact, err := s.next.DryRun(ctx, orgID, userID, opts...)
|
2020-06-16 17:57:07 +00:00
|
|
|
return impact, rec(err, metric.RecordAdditional(map[string]interface{}{
|
|
|
|
"sources": impact.Sources,
|
|
|
|
}))
|
2020-02-03 17:39:01 +00:00
|
|
|
}
|
|
|
|
|
2020-06-15 21:13:38 +00:00
|
|
|
func (s *mwMetrics) Apply(ctx context.Context, orgID, userID influxdb.ID, opts ...ApplyOptFn) (PkgImpactSummary, error) {
|
2020-02-03 17:39:01 +00:00
|
|
|
rec := s.rec.Record("apply")
|
2020-06-15 21:13:38 +00:00
|
|
|
impact, err := s.next.Apply(ctx, orgID, userID, opts...)
|
2020-06-16 17:57:07 +00:00
|
|
|
return impact, rec(err, metric.RecordAdditional(map[string]interface{}{
|
|
|
|
"sources": impact.Sources,
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
func templateVec() metric.VecOpts {
|
|
|
|
return metric.VecOpts{
|
|
|
|
Name: "template_count",
|
|
|
|
Help: "Number of installations per template",
|
|
|
|
LabelNames: []string{"method", "source"},
|
|
|
|
CounterFn: func(vec *prometheus.CounterVec, o metric.CollectFnOpts) {
|
|
|
|
if o.Err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// safe to ignore the failed type assertion, a zero value
|
|
|
|
// provides a nil slice, so no worries.
|
|
|
|
sources, _ := o.AdditionalProps["sources"].([]string)
|
|
|
|
for _, source := range normalizeRemoteSources(sources) {
|
|
|
|
vec.
|
|
|
|
With(prometheus.Labels{
|
|
|
|
"method": o.Method,
|
|
|
|
"source": source.String(),
|
|
|
|
}).
|
|
|
|
Inc()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func normalizeRemoteSources(sources []string) []url.URL {
|
|
|
|
var out []url.URL
|
|
|
|
for _, source := range sources {
|
|
|
|
u, err := url.Parse(source)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !strings.HasPrefix(u.Scheme, "http") {
|
|
|
|
continue
|
|
|
|
}
|
2020-06-16 19:24:11 +00:00
|
|
|
if u.Host == githubRawContentHost {
|
|
|
|
u.Host = githubHost
|
2020-06-16 17:57:07 +00:00
|
|
|
u.Path = normalizeRawGithubPath(u.Path)
|
|
|
|
}
|
|
|
|
out = append(out, *u)
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
func normalizeRawGithubPath(rawPath string) string {
|
|
|
|
parts := strings.Split(rawPath, "/")
|
|
|
|
if len(parts) < 4 {
|
|
|
|
return rawPath
|
|
|
|
}
|
|
|
|
// keep /account/repo as base, then append the blob to it
|
|
|
|
tail := append([]string{"blob"}, parts[3:]...)
|
|
|
|
parts = append(parts[:3], tail...)
|
2020-06-16 19:24:11 +00:00
|
|
|
return path.Join(parts...)
|
2020-02-03 17:39:01 +00:00
|
|
|
}
|