Skip to content

Commit 15284ee

Browse files
committed
feat(Grafana): Manage Grafana Service Accounts
1 parent 642b609 commit 15284ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2667
-32
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
# Binaries for programs and plugins
3+
__debug_bin*
34
*.exe
45
*.exe~
56
*.dll
@@ -26,6 +27,7 @@ vendor
2627
*~
2728

2829
.vscode/
30+
.mirrord/
2931
.DS_Store
3032

3133
# Audit lab

api/v1beta1/grafana_types.go

Lines changed: 110 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,54 @@ type OperatorReconcileVars struct {
5252
Plugins string
5353
}
5454

55+
// GrafanaServiceAccountTokenSpec describes a token to create.
56+
type GrafanaServiceAccountTokenSpec struct {
57+
// Name is the name of the Kubernetes Secret (and token identifier in Grafana). The secret will contain the token value.
58+
// +kubebuilder:validation:Required
59+
Name string `json:"name"`
60+
61+
// Expires is the optional expiration time for the token. After this time, the operator may rotate the token.
62+
// +kubebuilder:validation:Optional
63+
Expires *metav1.Time `json:"expires,omitempty"`
64+
}
65+
66+
// GrafanaServiceAccountSpec defines the desired state of a GrafanaServiceAccount.
67+
type GrafanaServiceAccountSpec struct {
68+
// ID is a kind of unique identifier to distinguish between service accounts if the name is changed.
69+
// +kubebuilder:validation:Required
70+
ID string `json:"id"`
71+
72+
// Name is the desired name of the service account in Grafana.
73+
// +kubebuilder:validation:Required
74+
Name string `json:"name"`
75+
76+
// Role is the Grafana role for the service account (Viewer, Editor, Admin).
77+
// +kubebuilder:validation:Required
78+
// +kubebuilder:validation:Enum=Viewer;Editor;Admin
79+
Role string `json:"role"`
80+
81+
// IsDisabled indicates if the service account should be disabled in Grafana.
82+
// +kubebuilder:validation:Optional
83+
IsDisabled bool `json:"isDisabled,omitempty"`
84+
85+
// Tokens defines API tokens to create for this service account. Each token will be stored in a Kubernetes Secret with the given name.
86+
// +kubebuilder:validation:Optional
87+
Tokens []GrafanaServiceAccountTokenSpec `json:"tokens,omitempty"`
88+
}
89+
90+
type GrafanaServiceAccounts struct {
91+
// Accounts lists Grafana service accounts to manage.
92+
// Each service account is uniquely identified by its ID.
93+
// +listType=map
94+
// +listMapKey=id
95+
Accounts []GrafanaServiceAccountSpec `json:"accounts,omitempty"`
96+
97+
// GenerateTokenSecret, if true, will create one default API token in a Secret if no Tokens are specified.
98+
// If false, no token is created unless explicitly listed in Tokens.
99+
// +kubebuilder:default=true
100+
GenerateTokenSecret bool `json:"generateTokenSecret,omitempty"`
101+
}
102+
55103
// GrafanaSpec defines the desired state of Grafana
56104
type GrafanaSpec struct {
57105
// +kubebuilder:pruning:PreserveUnknownFields
@@ -83,6 +131,8 @@ type GrafanaSpec struct {
83131
// DisableDefaultSecurityContext prevents the operator from populating securityContext on deployments
84132
// +kubebuilder:validation:Enum=Pod;Container;All
85133
DisableDefaultSecurityContext string `json:"disableDefaultSecurityContext,omitempty"`
134+
// Grafana Service Accounts
135+
GrafanaServiceAccounts *GrafanaServiceAccounts `json:"grafanaServiceAccounts,omitempty"`
86136
}
87137

88138
type External struct {
@@ -134,22 +184,68 @@ type GrafanaPreferences struct {
134184
HomeDashboardUID string `json:"homeDashboardUid,omitempty"`
135185
}
136186

187+
type GrafanaServiceAccountSecretStatus struct {
188+
Namespace string `json:"namespace,omitempty"`
189+
Name string `json:"name,omitempty"`
190+
}
191+
192+
// GrafanaServiceAccountTokenStatus describes a token created in Grafana.
193+
type GrafanaServiceAccountTokenStatus struct {
194+
// Name is the name of the Kubernetes Secret. The secret will contain the token value.
195+
Name string `json:"name"`
196+
197+
// Expires is the expiration time for the token.
198+
// N.B. There's possible discrepancy with the expiration time in spec.
199+
// It happens because Grafana API accepts TTL in seconds then calculates the expiration time against the current time.
200+
Expires *metav1.Time `json:"expires,omitempty"`
201+
202+
// ID is the Grafana-assigned ID of the token.
203+
ID int64 `json:"tokenId"`
204+
205+
// Secret is the Kubernetes Secret that stores the actual token value.
206+
// This may seem redundant if the Secret name usually matches the token's Name,
207+
// but it's stored explicitly in Status for clarity and future flexibility.
208+
Secret *GrafanaServiceAccountSecretStatus `json:"secret,omitempty"`
209+
}
210+
211+
// GrafanaServiceAccountStatus holds status for one Grafana instance.
212+
type GrafanaServiceAccountStatus struct {
213+
// SpecID is a kind of unique identifier to distinguish between service accounts if the name is changed.
214+
SpecID string `json:"specId"`
215+
216+
// Name is the name of the service account in Grafana.
217+
Name string `json:"name"`
218+
219+
// ServiceAccountID is the numeric ID of the service account in this Grafana.
220+
ServiceAccountID int64 `json:"serviceAccountId"`
221+
222+
// Role is the Grafana role for the service account (Viewer, Editor, Admin).
223+
Role string `json:"role"`
224+
225+
// IsDisabled indicates if the service account is disabled.
226+
IsDisabled bool `json:"isDisabled,omitempty"`
227+
228+
// Tokens is the status of tokens for this service account in Grafana.
229+
Tokens []GrafanaServiceAccountTokenStatus `json:"tokens,omitempty"`
230+
}
231+
137232
// GrafanaStatus defines the observed state of Grafana
138233
type GrafanaStatus struct {
139-
Stage OperatorStageName `json:"stage,omitempty"`
140-
StageStatus OperatorStageStatus `json:"stageStatus,omitempty"`
141-
LastMessage string `json:"lastMessage,omitempty"`
142-
AdminURL string `json:"adminUrl,omitempty"`
143-
AlertRuleGroups NamespacedResourceList `json:"alertRuleGroups,omitempty"`
144-
ContactPoints NamespacedResourceList `json:"contactPoints,omitempty"`
145-
Dashboards NamespacedResourceList `json:"dashboards,omitempty"`
146-
Datasources NamespacedResourceList `json:"datasources,omitempty"`
147-
Folders NamespacedResourceList `json:"folders,omitempty"`
148-
LibraryPanels NamespacedResourceList `json:"libraryPanels,omitempty"`
149-
MuteTimings NamespacedResourceList `json:"muteTimings,omitempty"`
150-
NotificationTemplates NamespacedResourceList `json:"notificationTemplates,omitempty"`
151-
Version string `json:"version,omitempty"`
152-
Conditions []metav1.Condition `json:"conditions,omitempty"`
234+
Stage OperatorStageName `json:"stage,omitempty"`
235+
StageStatus OperatorStageStatus `json:"stageStatus,omitempty"`
236+
LastMessage string `json:"lastMessage,omitempty"`
237+
AdminURL string `json:"adminUrl,omitempty"`
238+
AlertRuleGroups NamespacedResourceList `json:"alertRuleGroups,omitempty"`
239+
ContactPoints NamespacedResourceList `json:"contactPoints,omitempty"`
240+
Dashboards NamespacedResourceList `json:"dashboards,omitempty"`
241+
Datasources NamespacedResourceList `json:"datasources,omitempty"`
242+
Folders NamespacedResourceList `json:"folders,omitempty"`
243+
LibraryPanels NamespacedResourceList `json:"libraryPanels,omitempty"`
244+
MuteTimings NamespacedResourceList `json:"muteTimings,omitempty"`
245+
NotificationTemplates NamespacedResourceList `json:"notificationTemplates,omitempty"`
246+
Version string `json:"version,omitempty"`
247+
Conditions []metav1.Condition `json:"conditions,omitempty"`
248+
GrafanaServiceAccounts []GrafanaServiceAccountStatus `json:"serviceAccounts,omitempty"`
153249
}
154250

155251
// +kubebuilder:object:root=true

api/v1beta1/zz_generated.deepcopy.go

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

0 commit comments

Comments
 (0)