Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/client/client.go
4094 views
1
// Package client provides a client interface to the Agent HTTP
2
// API.
3
package client
4
5
import (
6
"bytes"
7
"context"
8
"encoding/json"
9
"fmt"
10
"io"
11
"net/http"
12
"strings"
13
14
"github.com/grafana/agent/pkg/metrics/cluster/configapi"
15
"github.com/grafana/agent/pkg/metrics/instance"
16
"gopkg.in/yaml.v2"
17
)
18
19
// Client is a collection of all subsystem clients.
20
type Client struct {
21
PrometheusClient
22
}
23
24
// New creates a new Client.
25
func New(addr string) *Client {
26
return &Client{
27
PrometheusClient: &prometheusClient{addr: addr},
28
}
29
}
30
31
// PrometheusClient is the client interface to the API exposed by the
32
// Prometheus subsystem of the Grafana Agent.
33
type PrometheusClient interface {
34
// Instances runs the list of currently running instances.
35
Instances(ctx context.Context) ([]string, error)
36
37
// The following methods are for the scraping service mode
38
// only and will fail when not enabled on the Agent.
39
40
// ListConfigs runs the list of instance configs stored in the config
41
// management KV store.
42
ListConfigs(ctx context.Context) (*configapi.ListConfigurationsResponse, error)
43
44
// GetConfiguration returns a named configuration from the config
45
// management KV store.
46
GetConfiguration(ctx context.Context, name string) (*instance.Config, error)
47
48
// PutConfiguration adds or updates a named configuration into the
49
// config management KV store.
50
PutConfiguration(ctx context.Context, name string, cfg *instance.Config) error
51
52
// DeleteConfiguration removes a named configuration from the config
53
// management KV store.
54
DeleteConfiguration(ctx context.Context, name string) error
55
}
56
57
type prometheusClient struct {
58
addr string
59
}
60
61
func (c *prometheusClient) Instances(ctx context.Context) ([]string, error) {
62
url := fmt.Sprintf("%s/agent/api/v1/metrics/instances", c.addr)
63
64
resp, err := c.doRequest(ctx, "GET", url, nil)
65
if err != nil {
66
return nil, err
67
}
68
69
var data []string
70
err = unmarshalPrometheusAPIResponse(resp.Body, &data)
71
return data, err
72
}
73
74
func (c *prometheusClient) ListConfigs(ctx context.Context) (*configapi.ListConfigurationsResponse, error) {
75
url := fmt.Sprintf("%s/agent/api/v1/configs", c.addr)
76
77
resp, err := c.doRequest(ctx, "GET", url, nil)
78
if err != nil {
79
return nil, err
80
}
81
82
var data configapi.ListConfigurationsResponse
83
err = unmarshalPrometheusAPIResponse(resp.Body, &data)
84
return &data, err
85
}
86
87
func (c *prometheusClient) GetConfiguration(ctx context.Context, name string) (*instance.Config, error) {
88
url := fmt.Sprintf("%s/agent/api/v1/configs/%s", c.addr, name)
89
90
resp, err := c.doRequest(ctx, "GET", url, nil)
91
if err != nil {
92
return nil, err
93
}
94
95
var data configapi.GetConfigurationResponse
96
if err := unmarshalPrometheusAPIResponse(resp.Body, &data); err != nil {
97
return nil, err
98
}
99
100
var config instance.Config
101
err = yaml.NewDecoder(strings.NewReader(data.Value)).Decode(&config)
102
return &config, err
103
}
104
105
func (c *prometheusClient) PutConfiguration(ctx context.Context, name string, cfg *instance.Config) error {
106
url := fmt.Sprintf("%s/agent/api/v1/config/%s", c.addr, name)
107
108
bb, err := instance.MarshalConfig(cfg, false)
109
if err != nil {
110
return err
111
}
112
113
resp, err := c.doRequest(ctx, "POST", url, bytes.NewReader(bb))
114
if err != nil {
115
return err
116
}
117
118
return unmarshalPrometheusAPIResponse(resp.Body, nil)
119
}
120
121
func (c *prometheusClient) DeleteConfiguration(ctx context.Context, name string) error {
122
url := fmt.Sprintf("%s/agent/api/v1/config/%s", c.addr, name)
123
124
resp, err := c.doRequest(ctx, "DELETE", url, nil)
125
if err != nil {
126
return err
127
}
128
129
return unmarshalPrometheusAPIResponse(resp.Body, nil)
130
}
131
132
func (c *prometheusClient) doRequest(ctx context.Context, method string, url string, body io.Reader) (*http.Response, error) {
133
req, err := http.NewRequestWithContext(ctx, method, url, body)
134
if err != nil {
135
return nil, err
136
}
137
return http.DefaultClient.Do(req)
138
}
139
140
// unmarshalPrometheusAPIResponse will unmarshal a response from the Prometheus
141
// subsystem API.
142
//
143
// r will be closed after this method is called.
144
func unmarshalPrometheusAPIResponse(r io.ReadCloser, v interface{}) error {
145
defer func() {
146
_ = r.Close()
147
}()
148
149
resp := struct {
150
Status string `json:"status"`
151
Data json.RawMessage `json:"data"`
152
}{}
153
154
err := json.NewDecoder(r).Decode(&resp)
155
if err != nil {
156
return fmt.Errorf("could not read response: %w", err)
157
}
158
159
if v != nil && resp.Status == "success" {
160
err := json.Unmarshal(resp.Data, v)
161
if err != nil {
162
return fmt.Errorf("unmarshaling response: %w", err)
163
}
164
} else if resp.Status == "error" {
165
var errResp configapi.ErrorResponse
166
err := json.Unmarshal(resp.Data, &errResp)
167
if err != nil {
168
return fmt.Errorf("unmarshaling error: %w", err)
169
}
170
171
return fmt.Errorf("%s", errResp.Error)
172
}
173
174
if resp.Status != "success" && resp.Status != "error" {
175
return fmt.Errorf("unknown API response status: %s", resp.Status)
176
}
177
178
return nil
179
}
180
181