The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/cmd/grafana-cli/services/api_client.go

136 lines
3.4 KiB

package services
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"path"
"runtime"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
)
func GetPluginInfoFromRepo(pluginId, repoUrl string) (models.Plugin, error) {
logger.Debugf("getting plugin metadata from: %v pluginId: %v \n", repoUrl, pluginId)
body, err := sendRequestGetBytes(HttpClient, repoUrl, "repo", pluginId)
if err != nil {
if errors.Is(err, ErrNotFoundError) {
return models.Plugin{}, fmt.Errorf("%v: %w",
fmt.Sprintf("Failed to find requested plugin, check if the plugin_id (%s) is correct", pluginId), err)
}
return models.Plugin{}, fmt.Errorf("%v: %w", "Failed to send request", err)
}
var data models.Plugin
err = json.Unmarshal(body, &data)
if err != nil {
logger.Info("Failed to unmarshal plugin repo response error:", err)
return models.Plugin{}, err
}
return data, nil
}
func ListAllPlugins(repoUrl string) (models.PluginRepo, error) {
body, err := sendRequestGetBytes(HttpClient, repoUrl, "repo")
if err != nil {
logger.Info("Failed to send request", "error", err)
return models.PluginRepo{}, fmt.Errorf("%v: %w", "Failed to send request", err)
}
var data models.PluginRepo
err = json.Unmarshal(body, &data)
if err != nil {
logger.Info("Failed to unmarshal plugin repo response error:", err)
return models.PluginRepo{}, err
}
return data, nil
}
func sendRequestGetBytes(client http.Client, repoUrl string, subPaths ...string) ([]byte, error) {
bodyReader, err := sendRequest(client, repoUrl, subPaths...)
if err != nil {
return []byte{}, err
}
defer func() {
if err := bodyReader.Close(); err != nil {
logger.Warn("Failed to close stream", "err", err)
}
}()
return io.ReadAll(bodyReader)
}
func sendRequest(client http.Client, repoUrl string, subPaths ...string) (io.ReadCloser, error) {
req, err := createRequest(repoUrl, subPaths...)
if err != nil {
return nil, err
}
res, err := client.Do(req)
if err != nil {
return nil, err
}
return handleResponse(res)
}
func createRequest(repoUrl string, subPaths ...string) (*http.Request, error) {
u, err := url.Parse(repoUrl)
if err != nil {
return nil, err
}
for _, v := range subPaths {
u.Path = path.Join(u.Path, v)
}
req, err := http.NewRequest(http.MethodGet, u.String(), nil)
if err != nil {
return nil, err
}
req.Header.Set("grafana-version", GrafanaVersion)
req.Header.Set("grafana-os", runtime.GOOS)
req.Header.Set("grafana-arch", runtime.GOARCH)
req.Header.Set("User-Agent", "grafana "+GrafanaVersion)
return req, err
}
func handleResponse(res *http.Response) (io.ReadCloser, error) {
if res.StatusCode == 404 {
return nil, ErrNotFoundError
}
if res.StatusCode/100 != 2 && res.StatusCode/100 != 4 {
return nil, fmt.Errorf("API returned invalid status: %s", res.Status)
}
if res.StatusCode/100 == 4 {
body, err := io.ReadAll(res.Body)
defer func() {
if err := res.Body.Close(); err != nil {
logger.Warn("Failed to close response body", "err", err)
}
}()
if err != nil || len(body) == 0 {
return nil, &BadRequestError{Status: res.Status}
}
var message string
var jsonBody map[string]string
err = json.Unmarshal(body, &jsonBody)
if err != nil || len(jsonBody["message"]) == 0 {
message = string(body)
} else {
message = jsonBody["message"]
}
return nil, &BadRequestError{Status: res.Status, Message: message}
}
return res.Body, nil
}