Skip to content
This repository was archived by the owner on Jan 15, 2024. It is now read-only.

Commit 257686c

Browse files
committed
use temp Grafana service accounts instead of API keys when managing Grafana Stack
1 parent 618bf97 commit 257686c

3 files changed

+82
-59
lines changed

client.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type Client struct {
2626

2727
// Config contains client configuration.
2828
type Config struct {
29-
// APIKey is an optional API key.
29+
// APIKey is an optional API key or service account token.
3030
APIKey string
3131
// BasicAuth is optional basic auth credentials.
3232
BasicAuth *url.Userinfo
@@ -36,7 +36,7 @@ type Config struct {
3636
Client *http.Client
3737
// OrgID provides an optional organization ID
3838
// with BasicAuth, it defaults to last used org
39-
// with APIKey, it is disallowed because API keys are scoped to a single org
39+
// with APIKey, it is disallowed because service account tokens are scoped to a single org
4040
OrgID int64
4141
// NumRetries contains the number of attempted retries
4242
NumRetries int

cloud_grafana_api_key.go

-57
This file was deleted.

cloud_grafana_service_account.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package gapi
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"time"
9+
)
10+
11+
// This function creates a service account inside the Grafana instance running in stack `stack`. It's used in order
12+
// to provision service accounts inside Grafana while just having access to a Grafana Cloud API key.
13+
func (c *Client) CreateGrafanaServiceAccountFromCloud(stack string, input *CreateServiceAccountRequest) (*ServiceAccountDTO, error) {
14+
data, err := json.Marshal(input)
15+
if err != nil {
16+
return nil, err
17+
}
18+
19+
resp := &ServiceAccountDTO{}
20+
err = c.request(http.MethodPost, fmt.Sprintf("/api/instances/%s/api/serviceaccounts", stack), nil, bytes.NewBuffer(data), resp)
21+
return resp, err
22+
}
23+
24+
// This function creates a service account token inside the Grafana instance running in stack `stack`. It's used in order
25+
// to provision service accounts inside Grafana while just having access to a Grafana Cloud API key.
26+
func (c *Client) CreateGrafanaServiceAccountTokenFromCloud(stack string, input *CreateServiceAccountTokenRequest) (*CreateServiceAccountTokenResponse, error) {
27+
data, err := json.Marshal(input)
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
resp := &CreateServiceAccountTokenResponse{}
33+
err = c.request(http.MethodPost, fmt.Sprintf("/api/instances/%s/api/serviceaccounts/%d/tokens", stack, input.ServiceAccountID), nil, bytes.NewBuffer(data), resp)
34+
return resp, err
35+
}
36+
37+
// The Grafana Cloud API is disconnected from the Grafana API on the stacks unfortunately. That's why we can't use
38+
// the Grafana Cloud API key to fully manage service accounts on the Grafana API. The only thing we can do is to create
39+
// a temporary Admin service account, and create a Grafana API client with that.
40+
func (c *Client) CreateTemporaryStackGrafanaClient(stackSlug, tempSaPrefix string, tempKeyDuration time.Duration) (tempClient *Client, cleanup func() error, err error) {
41+
stack, err := c.StackBySlug(stackSlug)
42+
if err != nil {
43+
return nil, nil, err
44+
}
45+
46+
name := fmt.Sprintf("%s%d", tempSaPrefix, time.Now().UnixNano())
47+
48+
req := &CreateServiceAccountRequest{
49+
Name: name,
50+
Role: "Admin",
51+
}
52+
53+
sa, err := c.CreateGrafanaServiceAccountFromCloud(stackSlug, req)
54+
if err != nil {
55+
return nil, nil, err
56+
}
57+
58+
tokenRequest := &CreateServiceAccountTokenRequest{
59+
Name: name,
60+
ServiceAccountID: sa.ID,
61+
SecondsToLive: int64(tempKeyDuration.Seconds()),
62+
}
63+
64+
token, err := c.CreateGrafanaServiceAccountTokenFromCloud(stackSlug, tokenRequest)
65+
if err != nil {
66+
return nil, nil, err
67+
}
68+
69+
client, err := New(stack.URL, Config{APIKey: token.Key})
70+
if err != nil {
71+
return nil, nil, err
72+
}
73+
74+
cleanup = func() error {
75+
_, err = client.DeleteServiceAccount(sa.ID)
76+
return err
77+
}
78+
79+
return client, cleanup, nil
80+
}

0 commit comments

Comments
 (0)