Skip to content

Commit 7d582d5

Browse files
committed
wip
1 parent 163fb27 commit 7d582d5

File tree

2 files changed

+114
-45
lines changed

2 files changed

+114
-45
lines changed

controllers/model/grafana_resources.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package model
22

33
import (
4+
"crypto/sha1"
45
"fmt"
6+
"strconv"
7+
"strings"
8+
"time"
59

610
grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1"
711
routev1 "github.com/openshift/api/route/v1"
@@ -46,6 +50,77 @@ func GetGrafanaAdminSecret(cr *grafanav1beta1.Grafana, scheme *runtime.Scheme) *
4650
return secret
4751
}
4852

53+
func generateInternalServiceAccountTokenSecretName(grafanaName, serviceAccountSpecID, tokenName string) string {
54+
const maxSecretNameLength = 63
55+
56+
sanitizeK8sName := func(s string) string {
57+
s = strings.ToLower(s)
58+
s = strings.ReplaceAll(s, "_", "-")
59+
return s
60+
}
61+
62+
base := strings.Join([]string{grafanaName, serviceAccountSpecID, tokenName}, "-")
63+
if len(base) <= maxSecretNameLength {
64+
return sanitizeK8sName(base)
65+
}
66+
67+
prefixLen := maxSecretNameLength - 7
68+
if prefixLen < 1 {
69+
prefixLen = 1
70+
}
71+
prefix := base[:prefixLen]
72+
73+
sum := sha1.Sum([]byte(base)) // nolint:gosec
74+
shortHash := fmt.Sprintf("%x", sum[:3])
75+
76+
return sanitizeK8sName(fmt.Sprintf("%s-%s", prefix, shortHash))
77+
}
78+
79+
func GetInternalServiceAccountSecret(
80+
cr *grafanav1beta1.Grafana,
81+
saStatus grafanav1beta1.GrafanaServiceAccountStatus,
82+
tokenStatus grafanav1beta1.GrafanaServiceAccountTokenStatus,
83+
tokenKey []byte,
84+
scheme *runtime.Scheme,
85+
) *v1.Secret {
86+
secret := &v1.Secret{
87+
ObjectMeta: metav1.ObjectMeta{
88+
Name: generateInternalServiceAccountTokenSecretName(cr.Name, saStatus.SpecID, tokenStatus.Name),
89+
Namespace: cr.Namespace,
90+
Labels: MergeAnnotations(
91+
GetCommonLabels(),
92+
map[string]string{
93+
"app": "grafana-serviceaccount-token",
94+
"grafana.integreatly.org/instance": cr.Name,
95+
"grafana.integreatly.org/sa-spec-id": saStatus.SpecID,
96+
"grafana.integreatly.org/token-name": tokenStatus.Name,
97+
},
98+
),
99+
Annotations: map[string]string{
100+
"grafana.integreatly.org/token-id": strconv.FormatInt(tokenStatus.ID, 10),
101+
},
102+
},
103+
Type: v1.SecretTypeOpaque,
104+
Data: map[string][]byte{
105+
"token": tokenKey,
106+
},
107+
}
108+
if tokenStatus.Expires != nil {
109+
secret.Annotations = map[string]string{
110+
"grafana.integreatly.org/token-expiry": tokenStatus.Expires.Format(time.RFC3339),
111+
}
112+
}
113+
114+
if scheme != nil {
115+
controllerutil.SetControllerReference(cr, secret, scheme) //nolint:errcheck
116+
}
117+
return secret
118+
}
119+
120+
func GetInternalServiceAccountSpecIDFromSecret(secret v1.Secret) string {
121+
return secret.Labels["grafana.integreatly.org/sa-spec-id"]
122+
}
123+
49124
func GetGrafanaDataPVC(cr *grafanav1beta1.Grafana, scheme *runtime.Scheme) *v1.PersistentVolumeClaim {
50125
pvc := &v1.PersistentVolumeClaim{
51126
ObjectMeta: metav1.ObjectMeta{

controllers/reconcilers/grafana/grafana_service_account_reconciler.go

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package grafana
33
import (
44
"context"
55
"errors"
6-
"strconv"
76

87
// sha1 is used to generate a hash for the secret name.
98
"crypto/sha1" // nolint:gosec
@@ -17,14 +16,14 @@ import (
1716
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1817
"k8s.io/apimachinery/pkg/runtime"
1918
"sigs.k8s.io/controller-runtime/pkg/client"
20-
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2119
logf "sigs.k8s.io/controller-runtime/pkg/log"
2220

2321
genapi "github.com/grafana/grafana-openapi-client-go/client"
2422
"github.com/grafana/grafana-openapi-client-go/client/service_accounts"
2523
"github.com/grafana/grafana-openapi-client-go/models"
2624
v1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1"
2725
client2 "github.com/grafana/grafana-operator/v5/controllers/client"
26+
model2 "github.com/grafana/grafana-operator/v5/controllers/model"
2827
)
2928

3029
func isEqualExpirationTime(a, b *metav1.Time) bool {
@@ -85,6 +84,11 @@ func (r *GrafanaServiceAccountReconciler) reconcileAccounts(
8584
gClient *genapi.GrafanaHTTPAPI,
8685
scheme *runtime.Scheme,
8786
) error {
87+
allSecrets, err := r.listAllTokenSecrets(ctx, cr)
88+
if err != nil {
89+
return fmt.Errorf("listing all token secrets for instance %q: %w", cr.Name, err)
90+
}
91+
8892
existingSAs, err := listServiceAccounts(ctx, gClient)
8993
if err != nil {
9094
return fmt.Errorf("listing service accounts: %w", err)
@@ -129,7 +133,10 @@ func (r *GrafanaServiceAccountReconciler) reconcileAccounts(
129133
}
130134

131135
for i := range groups.toSync {
132-
err := r.reconcileAccount(ctx, cr, gClient, groups.toSync[i].spec, &groups.toSync[i].status, scheme)
136+
spec := groups.toSync[i].spec
137+
status := &groups.toSync[i].status
138+
existingSecrets := allSecrets[spec.ID]
139+
err := r.reconcileAccount(ctx, cr, gClient, spec, status, existingSecrets, scheme)
133140
if err != nil {
134141
groups.consolidateTo(cr)
135142
return fmt.Errorf("reconciling service account %q: %w", groups.toSync[i].status.SpecID, err)
@@ -172,6 +179,7 @@ func (r *GrafanaServiceAccountReconciler) reconcileAccount(
172179
gClient *genapi.GrafanaHTTPAPI,
173180
spec v1beta1.GrafanaServiceAccountSpec,
174181
status *v1beta1.GrafanaServiceAccountStatus,
182+
existingSecrets map[string]corev1.Secret,
175183
scheme *runtime.Scheme,
176184
) error {
177185
var hasDiscrepancy bool
@@ -208,7 +216,7 @@ func (r *GrafanaServiceAccountReconciler) reconcileAccount(
208216
status.Name = update.Payload.Serviceaccount.Name
209217
}
210218

211-
err := r.reconcileTokens(ctx, cr, gClient, spec, status, scheme)
219+
err := r.reconcileTokens(ctx, cr, gClient, spec, status, existingSecrets, scheme)
212220
if err != nil {
213221
return fmt.Errorf("reconciling tokens for service account %s: %w", status.Name, err)
214222
}
@@ -223,16 +231,13 @@ func (r *GrafanaServiceAccountReconciler) reconcileTokens(
223231
gClient *genapi.GrafanaHTTPAPI,
224232
spec v1beta1.GrafanaServiceAccountSpec,
225233
sa *v1beta1.GrafanaServiceAccountStatus,
234+
existingSecrets map[string]corev1.Secret,
226235
scheme *runtime.Scheme,
227236
) error {
228237
existingTokens, err := listServiceAccountTokens(ctx, gClient, sa.ServiceAccountID)
229238
if err != nil {
230239
return fmt.Errorf("listing tokens for service account: %w", err)
231240
}
232-
existingSecrets, err := r.listServiceAccountSecrets(ctx, cr, sa.SpecID)
233-
if err != nil {
234-
return fmt.Errorf("listing secrets for service account %q: %w", sa.SpecID, err)
235-
}
236241
groups := r.classifyTokens(cr, existingTokens, existingSecrets, spec, *sa)
237242

238243
for i, tokenStatus := range groups.toDelete {
@@ -327,33 +332,8 @@ func (r *GrafanaServiceAccountReconciler) createToken(
327332
}
328333

329334
// The token was created, let's create a secret for it.
330-
secret := &corev1.Secret{
331-
ObjectMeta: metav1.ObjectMeta{
332-
Name: generateTokenSecretName(cr.Name, sa.SpecID, status.Name),
333-
Namespace: cr.Namespace,
334-
Labels: map[string]string{
335-
"app": "grafana-serviceaccount-token",
336-
"grafana.integreatly.org/sa-spec-id": sa.SpecID,
337-
"grafana.integreatly.org/token-name": status.Name,
338-
},
339-
Annotations: map[string]string{
340-
"grafana.integreatly.org/token-id": strconv.FormatInt(status.ID, 10),
341-
},
342-
},
343-
Type: corev1.SecretTypeOpaque,
344-
Data: map[string][]byte{
345-
"token": []byte(createResp.Payload.Key),
346-
},
347-
}
348-
if spec.Expires != nil {
349-
secret.Annotations = map[string]string{
350-
"grafana.integreatly.org/token-expiry": spec.Expires.Format(time.RFC3339),
351-
}
352-
}
353-
err = controllerutil.SetControllerReference(cr, secret, scheme)
354-
if err != nil {
355-
return status, fmt.Errorf("setting owner reference on token secret %q: %w", secret.Name, err)
356-
}
335+
tokenKey := []byte(createResp.Payload.Key)
336+
secret := model2.GetInternalServiceAccountSecret(cr, sa, *status, tokenKey, scheme)
357337
err = r.client.Create(ctx, secret)
358338
if err != nil {
359339
return status, fmt.Errorf("creating token secret %q: %w", secret.Name, err)
@@ -671,19 +651,33 @@ func listServiceAccountTokens(
671651
return existingTokens, nil
672652
}
673653

674-
func (r *GrafanaServiceAccountReconciler) listServiceAccountSecrets(
654+
func (r *GrafanaServiceAccountReconciler) listAllTokenSecrets(
675655
ctx context.Context,
676656
cr *v1beta1.Grafana,
677-
serviceAccountSpecID string,
678-
) (map[string]corev1.Secret, error) {
679-
var secrets corev1.SecretList
680-
err := r.client.List(ctx, &secrets, client.InNamespace(cr.Namespace), client.MatchingLabels{"grafana.integreatly.org/sa-spec-id": serviceAccountSpecID})
657+
) (map[string]map[string]corev1.Secret, error) {
658+
var list corev1.SecretList
659+
err := r.client.List(ctx, &list,
660+
client.InNamespace(cr.Namespace),
661+
client.MatchingLabels{
662+
"app": "grafana-serviceaccount-token",
663+
"grafana.integreatly.org/instance": cr.Name,
664+
},
665+
)
681666
if err != nil {
682-
return nil, fmt.Errorf("listing secrets: %w", err)
667+
return nil, fmt.Errorf("listing token secrets: %w", err)
683668
}
684-
existingSecrets := map[string]corev1.Secret{}
685-
for _, secret := range secrets.Items {
686-
existingSecrets[secret.Name] = secret
669+
670+
res := map[string]map[string]corev1.Secret{}
671+
for _, s := range list.Items {
672+
specID := model2.GetInternalServiceAccountSpecIDFromSecret(s)
673+
if specID == "" {
674+
logf.FromContext(ctx).V(1).Info("secret doesn't have a spec ID, skipping", "secret", s.Name)
675+
continue
676+
}
677+
if res[specID] == nil {
678+
res[specID] = map[string]corev1.Secret{}
679+
}
680+
res[specID][s.Name] = s
687681
}
688-
return existingSecrets, nil
682+
return res, nil
689683
}

0 commit comments

Comments
 (0)