Skip to content

Commit 10600bc

Browse files
solidDoWantmoolen
andauthored
Improve Grafana generator integration with in-cluster Grafana (external-secrets#4519)
* Improve Grafana generator integration with in-cluster Grafana Signed-off-by: solidDoWant <[email protected]> * Switch to URL parsing Signed-off-by: solidDoWant <[email protected]> * rm unnecessary type conversion Signed-off-by: solidDoWant <[email protected]> * `omitEmpty` -> `omitempty` Signed-off-by: solidDoWant <[email protected]> --------- Signed-off-by: solidDoWant <[email protected]> Co-authored-by: Moritz Johner <[email protected]>
1 parent 63740fc commit 10600bc

File tree

7 files changed

+219
-37
lines changed

7 files changed

+219
-37
lines changed

apis/generators/v1alpha1/types_grafana.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,21 @@ type GrafanaAuth struct {
4444
// Note: you need a token which has elevated permissions to create service accounts.
4545
// See here for the documentation on basic roles offered by Grafana:
4646
// https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/
47-
Token SecretKeySelector `json:"token"`
47+
// +optional
48+
Token *SecretKeySelector `json:"token,omitempty"`
49+
// Basic auth credentials used to authenticate against the Grafana instance.
50+
// Note: you need a token which has elevated permissions to create service accounts.
51+
// See here for the documentation on basic roles offered by Grafana:
52+
// https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/
53+
// +optional
54+
Basic *GrafanaBasicAuth `json:"basic,omitempty"`
55+
}
56+
57+
type GrafanaBasicAuth struct {
58+
// A basic auth username used to authenticate against the Grafana instance.
59+
Username string `json:"username"`
60+
// A basic auth password used to authenticate against the Grafana instance.
61+
Password SecretKeySelector `json:"password"`
4862
}
4963

5064
// GrafanaServiceAccountTokenState is the state type produced by the Grafana generator.

apis/generators/v1alpha1/zz_generated.deepcopy.go

Lines changed: 29 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crds/bases/generators.external-secrets.io_clustergenerators.yaml

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,39 @@ spec:
537537
Auth is the authentication configuration to authenticate
538538
against the Grafana instance.
539539
properties:
540+
basic:
541+
description: |-
542+
Basic auth credentials used to authenticate against the Grafana instance.
543+
Note: you need a token which has elevated permissions to create service accounts.
544+
See here for the documentation on basic roles offered by Grafana:
545+
https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/
546+
properties:
547+
password:
548+
description: A basic auth password used to authenticate
549+
against the Grafana instance.
550+
properties:
551+
key:
552+
description: The key where the token is found.
553+
maxLength: 253
554+
minLength: 1
555+
pattern: ^[-._a-zA-Z0-9]+$
556+
type: string
557+
name:
558+
description: The name of the Secret resource being
559+
referred to.
560+
maxLength: 253
561+
minLength: 1
562+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
563+
type: string
564+
type: object
565+
username:
566+
description: A basic auth username used to authenticate
567+
against the Grafana instance.
568+
type: string
569+
required:
570+
- password
571+
- username
572+
type: object
540573
token:
541574
description: |-
542575
A service account token used to authenticate against the Grafana instance.
@@ -558,8 +591,6 @@ spec:
558591
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
559592
type: string
560593
type: object
561-
required:
562-
- token
563594
type: object
564595
serviceAccount:
565596
description: |-

config/crds/bases/generators.external-secrets.io_grafanas.yaml

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,39 @@ spec:
4747
Auth is the authentication configuration to authenticate
4848
against the Grafana instance.
4949
properties:
50+
basic:
51+
description: |-
52+
Basic auth credentials used to authenticate against the Grafana instance.
53+
Note: you need a token which has elevated permissions to create service accounts.
54+
See here for the documentation on basic roles offered by Grafana:
55+
https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/
56+
properties:
57+
password:
58+
description: A basic auth password used to authenticate against
59+
the Grafana instance.
60+
properties:
61+
key:
62+
description: The key where the token is found.
63+
maxLength: 253
64+
minLength: 1
65+
pattern: ^[-._a-zA-Z0-9]+$
66+
type: string
67+
name:
68+
description: The name of the Secret resource being referred
69+
to.
70+
maxLength: 253
71+
minLength: 1
72+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
73+
type: string
74+
type: object
75+
username:
76+
description: A basic auth username used to authenticate against
77+
the Grafana instance.
78+
type: string
79+
required:
80+
- password
81+
- username
82+
type: object
5083
token:
5184
description: |-
5285
A service account token used to authenticate against the Grafana instance.
@@ -68,8 +101,6 @@ spec:
68101
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
69102
type: string
70103
type: object
71-
required:
72-
- token
73104
type: object
74105
serviceAccount:
75106
description: |-

deploy/crds/bundle.yaml

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14525,6 +14525,36 @@ spec:
1452514525
Auth is the authentication configuration to authenticate
1452614526
against the Grafana instance.
1452714527
properties:
14528+
basic:
14529+
description: |-
14530+
Basic auth credentials used to authenticate against the Grafana instance.
14531+
Note: you need a token which has elevated permissions to create service accounts.
14532+
See here for the documentation on basic roles offered by Grafana:
14533+
https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/
14534+
properties:
14535+
password:
14536+
description: A basic auth password used to authenticate against the Grafana instance.
14537+
properties:
14538+
key:
14539+
description: The key where the token is found.
14540+
maxLength: 253
14541+
minLength: 1
14542+
pattern: ^[-._a-zA-Z0-9]+$
14543+
type: string
14544+
name:
14545+
description: The name of the Secret resource being referred to.
14546+
maxLength: 253
14547+
minLength: 1
14548+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
14549+
type: string
14550+
type: object
14551+
username:
14552+
description: A basic auth username used to authenticate against the Grafana instance.
14553+
type: string
14554+
required:
14555+
- password
14556+
- username
14557+
type: object
1452814558
token:
1452914559
description: |-
1453014560
A service account token used to authenticate against the Grafana instance.
@@ -14545,8 +14575,6 @@ spec:
1454514575
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
1454614576
type: string
1454714577
type: object
14548-
required:
14549-
- token
1455014578
type: object
1455114579
serviceAccount:
1455214580
description: |-
@@ -16464,6 +16492,36 @@ spec:
1646416492
Auth is the authentication configuration to authenticate
1646516493
against the Grafana instance.
1646616494
properties:
16495+
basic:
16496+
description: |-
16497+
Basic auth credentials used to authenticate against the Grafana instance.
16498+
Note: you need a token which has elevated permissions to create service accounts.
16499+
See here for the documentation on basic roles offered by Grafana:
16500+
https://grafana.com/docs/grafana/latest/administration/roles-and-permissions/access-control/rbac-fixed-basic-role-definitions/
16501+
properties:
16502+
password:
16503+
description: A basic auth password used to authenticate against the Grafana instance.
16504+
properties:
16505+
key:
16506+
description: The key where the token is found.
16507+
maxLength: 253
16508+
minLength: 1
16509+
pattern: ^[-._a-zA-Z0-9]+$
16510+
type: string
16511+
name:
16512+
description: The name of the Secret resource being referred to.
16513+
maxLength: 253
16514+
minLength: 1
16515+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
16516+
type: string
16517+
type: object
16518+
username:
16519+
description: A basic auth username used to authenticate against the Grafana instance.
16520+
type: string
16521+
required:
16522+
- password
16523+
- username
16524+
type: object
1646716525
token:
1646816526
description: |-
1646916527
A service account token used to authenticate against the Grafana instance.
@@ -16484,8 +16542,6 @@ spec:
1648416542
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
1648516543
type: string
1648616544
type: object
16487-
required:
16488-
- token
1648916545
type: object
1649016546
serviceAccount:
1649116547
description: |-

e2e/suites/generator/grafana.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ var _ = Describe("grafana generator", Label("grafana"), func() {
9898
Role: "Viewer",
9999
},
100100
Auth: genv1alpha1.GrafanaAuth{
101-
Token: genv1alpha1.SecretKeySelector{
101+
Token: &genv1alpha1.SecretKeySelector{
102102
Name: grafanaCredsSecretName,
103103
Key: "grafana-token",
104104
},

pkg/generator/grafana/grafana.go

Lines changed: 48 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"encoding/json"
2020
"fmt"
21+
"net/url"
2122
"strings"
2223

2324
"github.com/google/uuid"
@@ -40,23 +41,12 @@ func (w *Grafana) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kc
4041
if err != nil {
4142
return nil, nil, err
4243
}
43-
secret, err := resolvers.SecretKeyRef(ctx, kclient, resolvers.EmptyStoreKind, ns, &esmeta.SecretKeySelector{
44-
Namespace: &ns,
45-
Name: gen.Spec.Auth.Token.Name,
46-
Key: gen.Spec.Auth.Token.Key,
47-
})
44+
45+
cl, err := newClient(ctx, gen, kclient, ns)
4846
if err != nil {
4947
return nil, nil, err
5048
}
51-
url := strings.TrimPrefix(gen.Spec.URL, "https://")
52-
cfg := &grafanaclient.TransportConfig{
53-
Host: url,
54-
BasePath: "/api",
55-
Schemes: []string{"https"},
56-
APIKey: secret,
57-
}
5849

59-
cl := grafanaclient.NewHTTPClientWithConfig(nil, cfg)
6050
state, err := createOrGetServiceAccount(cl, gen)
6151
if err != nil {
6252
return nil, nil, err
@@ -100,24 +90,59 @@ func (w *Grafana) Cleanup(ctx context.Context, jsonSpec *apiextensions.JSON, pre
10090
}
10191

10292
func newClient(ctx context.Context, gen *genv1alpha1.Grafana, kclient client.Client, ns string) (*grafanaclient.GrafanaHTTPAPI, error) {
103-
secret, err := resolvers.SecretKeyRef(ctx, kclient, resolvers.EmptyStoreKind, ns, &esmeta.SecretKeySelector{
104-
Namespace: &ns,
105-
Name: gen.Spec.Auth.Token.Name,
106-
Key: gen.Spec.Auth.Token.Key,
107-
})
93+
parsedURL, err := url.Parse(gen.Spec.URL)
10894
if err != nil {
10995
return nil, err
11096
}
111-
url := strings.TrimPrefix(gen.Spec.URL, "https://")
97+
11298
cfg := &grafanaclient.TransportConfig{
113-
Host: url,
114-
BasePath: "/api",
115-
Schemes: []string{"https"},
116-
APIKey: secret,
99+
Host: parsedURL.Host,
100+
BasePath: parsedURL.JoinPath("/api").Path,
101+
Schemes: []string{parsedURL.Scheme},
117102
}
103+
104+
if err := setGrafanaClientCredentials(ctx, gen, kclient, ns, cfg); err != nil {
105+
return nil, err
106+
}
107+
118108
return grafanaclient.NewHTTPClientWithConfig(nil, cfg), nil
119109
}
120110

111+
func setGrafanaClientCredentials(ctx context.Context, gen *genv1alpha1.Grafana, kclient client.Client, ns string, cfg *grafanaclient.TransportConfig) error {
112+
// First try to use service account auth
113+
if gen.Spec.Auth.Token != nil {
114+
serviceAccountAPIKey, err := resolvers.SecretKeyRef(ctx, kclient, resolvers.EmptyStoreKind, ns, &esmeta.SecretKeySelector{
115+
Namespace: &ns,
116+
Name: gen.Spec.Auth.Token.Name,
117+
Key: gen.Spec.Auth.Token.Key,
118+
})
119+
if err != nil {
120+
return err
121+
}
122+
123+
cfg.APIKey = serviceAccountAPIKey
124+
return nil
125+
}
126+
127+
// Next try to use basic auth
128+
if gen.Spec.Auth.Basic != nil {
129+
basicAuthPassword, err := resolvers.SecretKeyRef(ctx, kclient, resolvers.EmptyStoreKind, ns, &esmeta.SecretKeySelector{
130+
Namespace: &ns,
131+
Name: gen.Spec.Auth.Basic.Password.Name,
132+
Key: gen.Spec.Auth.Basic.Password.Key,
133+
})
134+
if err != nil {
135+
return err
136+
}
137+
138+
cfg.BasicAuth = url.UserPassword(gen.Spec.Auth.Basic.Username, basicAuthPassword)
139+
return nil
140+
}
141+
142+
// No auth found, fail
143+
return fmt.Errorf("no auth configuration found")
144+
}
145+
121146
func createOrGetServiceAccount(cl *grafanaclient.GrafanaHTTPAPI, gen *genv1alpha1.Grafana) (*genv1alpha1.GrafanaServiceAccountTokenState, error) {
122147
saList, err := cl.ServiceAccounts.SearchOrgServiceAccountsWithPaging(&grafanasa.SearchOrgServiceAccountsWithPagingParams{
123148
Query: ptr.To(gen.Spec.ServiceAccount.Name),

0 commit comments

Comments
 (0)