156 lines
3.1 KiB
Go
156 lines
3.1 KiB
Go
|
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
||
|
// Use of this source code is governed by the Drone Non-Commercial License
|
||
|
// that can be found in the LICENSE file.
|
||
|
|
||
|
// +build !oss
|
||
|
|
||
|
package sink
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"context"
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"net/http"
|
||
|
"time"
|
||
|
|
||
|
"github.com/drone/drone/core"
|
||
|
)
|
||
|
|
||
|
type payload struct {
|
||
|
Series []series `json:"series"`
|
||
|
}
|
||
|
|
||
|
type series struct {
|
||
|
Metric string `json:"metric"`
|
||
|
Points [][]int64 `json:"points"`
|
||
|
Host string `json:"host"`
|
||
|
Type string `json:"type"`
|
||
|
Tags map[string]string `json:"tags,omitempty"`
|
||
|
}
|
||
|
|
||
|
// Datadog defines a no-op sink to datadog.
|
||
|
type Datadog struct {
|
||
|
users core.UserStore
|
||
|
repos core.RepositoryStore
|
||
|
builds core.BuildStore
|
||
|
system core.System
|
||
|
config Config
|
||
|
client *http.Client
|
||
|
}
|
||
|
|
||
|
// New returns a Datadog sink.
|
||
|
func New(
|
||
|
users core.UserStore,
|
||
|
repos core.RepositoryStore,
|
||
|
builds core.BuildStore,
|
||
|
system core.System,
|
||
|
config Config,
|
||
|
) *Datadog {
|
||
|
return &Datadog{
|
||
|
users: users,
|
||
|
repos: repos,
|
||
|
builds: builds,
|
||
|
system: system,
|
||
|
config: config,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Start starts the sink.
|
||
|
func (d *Datadog) Start(ctx context.Context) error {
|
||
|
for {
|
||
|
diff := midnightDiff()
|
||
|
select {
|
||
|
case <-time.After(diff):
|
||
|
d.do(ctx, time.Now().Unix())
|
||
|
case <-ctx.Done():
|
||
|
return nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (d *Datadog) do(ctx context.Context, unix int64) error {
|
||
|
users, err := d.users.Count(ctx)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
repos, err := d.repos.Count(ctx)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
builds, err := d.builds.Count(ctx)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
data := new(payload)
|
||
|
data.Series = []series{
|
||
|
{
|
||
|
Metric: "drone.users",
|
||
|
Points: [][]int64{[]int64{unix, users}},
|
||
|
Type: "gauge",
|
||
|
Host: d.system.Host,
|
||
|
},
|
||
|
{
|
||
|
Metric: "drone.repos",
|
||
|
Points: [][]int64{[]int64{unix, repos}},
|
||
|
Type: "gauge",
|
||
|
Host: d.system.Host,
|
||
|
},
|
||
|
{
|
||
|
Metric: "drone.builds",
|
||
|
Points: [][]int64{[]int64{unix, builds}},
|
||
|
Type: "gauge",
|
||
|
Host: d.system.Host,
|
||
|
},
|
||
|
}
|
||
|
|
||
|
buf := new(bytes.Buffer)
|
||
|
err = json.NewEncoder(buf).Encode(data)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
endpoint := fmt.Sprintf("%s?api_key=%s", d.config.Endpoint, d.config.Token)
|
||
|
req, err := http.NewRequest("POST", endpoint, buf)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
req.Header.Add("Accept", "application/json")
|
||
|
req.Header.Add("Content-Type", "application/json")
|
||
|
|
||
|
res, err := httpClient.Do(req)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
res.Body.Close()
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Client returns the http client. If no custom
|
||
|
// client is provided, the default client is used.
|
||
|
func (d *Datadog) Client() *http.Client {
|
||
|
if d.client == nil {
|
||
|
return httpClient
|
||
|
}
|
||
|
return d.client
|
||
|
}
|
||
|
|
||
|
// calculate the differene between now and midnight.
|
||
|
func midnightDiff() time.Duration {
|
||
|
a := time.Now()
|
||
|
b := time.Date(a.Year(), a.Month(), a.Day()+1, 0, 0, 0, 0, a.Location())
|
||
|
return b.Sub(a)
|
||
|
}
|
||
|
|
||
|
// httpClient should be used for HTTP requests. It
|
||
|
// is configured with a timeout for reliability.
|
||
|
var httpClient = &http.Client{
|
||
|
Transport: &http.Transport{
|
||
|
Proxy: http.ProxyFromEnvironment,
|
||
|
TLSHandshakeTimeout: 30 * time.Second,
|
||
|
DisableKeepAlives: true,
|
||
|
},
|
||
|
Timeout: 1 * time.Minute,
|
||
|
}
|