Skip to content

Commit fc35937

Browse files
committed
wip
1 parent 2533d2c commit fc35937

32 files changed

+3125
-10
lines changed

api/v1beta1/grafana_types.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,16 @@ type GrafanaPreferences struct {
131131

132132
// GrafanaStatus defines the observed state of Grafana
133133
type GrafanaStatus struct {
134-
Stage OperatorStageName `json:"stage,omitempty"`
135-
StageStatus OperatorStageStatus `json:"stageStatus,omitempty"`
136-
LastMessage string `json:"lastMessage,omitempty"`
137-
AdminUrl string `json:"adminUrl,omitempty"`
138-
Dashboards NamespacedResourceList `json:"dashboards,omitempty"`
139-
Datasources NamespacedResourceList `json:"datasources,omitempty"`
140-
Folders NamespacedResourceList `json:"folders,omitempty"`
141-
LibraryPanels NamespacedResourceList `json:"libraryPanels,omitempty"`
142-
Version string `json:"version,omitempty"`
134+
Stage OperatorStageName `json:"stage,omitempty"`
135+
StageStatus OperatorStageStatus `json:"stageStatus,omitempty"`
136+
LastMessage string `json:"lastMessage,omitempty"`
137+
AdminUrl string `json:"adminUrl,omitempty"`
138+
Dashboards NamespacedResourceList `json:"dashboards,omitempty"`
139+
Datasources NamespacedResourceList `json:"datasources,omitempty"`
140+
ServiceAccounts NamespacedResourceList `json:"serviceaccounts,omitempty"`
141+
Folders NamespacedResourceList `json:"folders,omitempty"`
142+
LibraryPanels NamespacedResourceList `json:"libraryPanels,omitempty"`
143+
Version string `json:"version,omitempty"`
143144
}
144145

145146
// +kubebuilder:object:root=true
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
Copyright 2025.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1beta1
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
"k8s.io/apimachinery/pkg/types"
22+
)
23+
24+
// GrafanaServiceAccountToken defines a token to be created for a Grafana service account.
25+
type GrafanaServiceAccountToken struct {
26+
// Name is the name of the Kubernetes Secret in which this token will be stored.
27+
// +kubebuilder:validation:Required
28+
Name string `json:"name"`
29+
30+
// Expires specifies the expiration timestamp (TTL) for this token. If set, the operator
31+
// will rotate or replace the token once the specified expiration time is reached.
32+
// If not set, the token does not expire (defaults to never expire).
33+
// +kubebuilder:validation:Optional
34+
Expires *metav1.Time `json:"expires,omitempty"`
35+
}
36+
37+
// GrafanaServiceAccountTokenStatus describes the current state of a token that was created in Grafana.
38+
type GrafanaServiceAccountTokenStatus struct {
39+
// Name is the name of the token, matching the one specified in .spec.tokens or generated automatically.
40+
Name string `json:"name"`
41+
42+
// TokenID is the numeric identifier of the token as returned by Grafana upon creation.
43+
TokenID int64 `json:"tokenId"`
44+
45+
// SecretName is the name of the Kubernetes Secret that stores the actual token value (Key).
46+
SecretName string `json:"secretName"`
47+
}
48+
49+
// GrafanaServiceAccountPermission defines a permission grant for a user or team related to this service account.
50+
type GrafanaServiceAccountPermission struct {
51+
// User is the Grafana username (login/Email) for which to grant permission.
52+
// +kubebuilder:validation:Optional
53+
User string `json:"user"`
54+
55+
// Team is the Grafana team name for which to grant permission.
56+
// +kubebuilder:validation:Optional
57+
Team string `json:"team"`
58+
59+
// Permission is the level of access granted for this service account (e.g., "Viewer", "Editor", "Admin").
60+
// +kubebuilder:validation:Required
61+
// +kubebuilder:validation:Enum=Viewer;Editor;Admin
62+
Permission string `json:"permission"`
63+
}
64+
65+
// GrafanaServiceAccountSpec defines the desired state of a GrafanaServiceAccount.
66+
type GrafanaServiceAccountSpec struct {
67+
GrafanaCommonSpec `json:",inline"`
68+
69+
// Name is the name of the service account in Grafana.
70+
// +kubebuilder:validation:Required
71+
Name string `json:"name,omitempty"`
72+
73+
// Role is the service account role in Grafana (Viewer, Editor, Admin).
74+
// +kubebuilder:validation:Required
75+
// +kubebuilder:validation:Enum=Viewer;Editor;Admin
76+
Role string `json:"role,omitempty"`
77+
78+
// IsDisabled indicates whether the service account should be disabled in Grafana.
79+
// +kubebuilder:validation:Optional
80+
IsDisabled bool `json:"isDisabled,omitempty"`
81+
82+
// Tokens is the list of tokens to create for this service account. For each token in the list,
83+
// the operator generates a Grafana access token and stores it in a Kubernetes Secret with the specified name.
84+
// If no tokens are specified and GenerateTokenSecret is true, the operator creates a default token
85+
// in a Secret with a default name.
86+
// +kubebuilder:validation:Optional
87+
Tokens []GrafanaServiceAccountToken `json:"tokens,omitempty"`
88+
89+
// Permissions specifies additional access permissions for users or teams in Grafana
90+
// related to this service account. This aligns with the UI where you can grant specific
91+
// users or groups Edit/Admin permissions on the service account.
92+
// +kubebuilder:validation:Optional
93+
Permissions []GrafanaServiceAccountPermission `json:"permissions,omitempty"`
94+
95+
// GenerateTokenSecret indicates whether the operator should automatically create a Kubernetes Secret
96+
// to store a token for this service account. If true (default), at least one Secret with a token will be created.
97+
// If false, no token is generated unless explicitly defined in Tokens.
98+
// +kubebuilder:default=true
99+
GenerateTokenSecret bool `json:"generateTokenSecret,omitempty"`
100+
}
101+
102+
// GrafanaServiceAccountInstanceStatus holds the details about how this GSA is applied to a specific Grafana instance.
103+
type GrafanaServiceAccountInstanceStatus struct {
104+
// GrafanaUID uniquely identifies the specific Grafana object,/ so we know if it's the same object or a re-created one.
105+
GrafanaUID types.UID `json:"grafanaUID"`
106+
107+
// GrafanaRefNamespace and GrafanaRefName specify which Grafana resource this status record belongs to.
108+
GrafanaRefNamespace string `json:"grafanaRefNamespace"`
109+
GrafanaRefName string `json:"grafanaRefName"`
110+
111+
// ServiceAccountID is the numeric identifier of the service account in that specific Grafana.
112+
ServiceAccountID int64 `json:"serviceAccountID,omitempty"`
113+
114+
// Tokens is a list of token statuses in that specific Grafana instance.
115+
Tokens []GrafanaServiceAccountTokenStatus `json:"tokens,omitempty"`
116+
}
117+
118+
// GrafanaServiceAccountStatus defines the observed state of a GrafanaServiceAccount.
119+
type GrafanaServiceAccountStatus struct {
120+
GrafanaCommonStatus `json:",inline"`
121+
122+
// Instances is a list of all Grafana instances that currently match this GSA's instanceSelector,
123+
// along with per-instance details such as the service account ID and tokens in that specific Grafana.
124+
// +optional
125+
Instances []GrafanaServiceAccountInstanceStatus `json:"instances,omitempty"`
126+
}
127+
128+
//+kubebuilder:object:root=true
129+
//+kubebuilder:subresource:status
130+
131+
// GrafanaServiceAccount is the Schema for the grafanaserviceaccounts API.
132+
// +kubebuilder:printcolumn:name="Last resync",type="date",format="date-time",JSONPath=".status.lastResync",description=""
133+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
134+
// +kubebuilder:resource:categories={grafana-operator}
135+
type GrafanaServiceAccount struct {
136+
metav1.TypeMeta `json:",inline"`
137+
metav1.ObjectMeta `json:"metadata,omitempty"`
138+
139+
Spec GrafanaServiceAccountSpec `json:"spec,omitempty"`
140+
Status GrafanaServiceAccountStatus `json:"status,omitempty"`
141+
}
142+
143+
//+kubebuilder:object:root=true
144+
145+
// GrafanaServiceAccountList contains a list of GrafanaServiceAccount objects.
146+
type GrafanaServiceAccountList struct {
147+
metav1.TypeMeta `json:",inline"`
148+
metav1.ListMeta `json:"metadata,omitempty"`
149+
Items []GrafanaServiceAccount `json:"items"`
150+
}
151+
152+
// Find searches for a GrafanaServiceAccount by namespace/name in the list.
153+
func (in *GrafanaServiceAccountList) Find(namespace, name string) *GrafanaServiceAccount {
154+
for _, serviceAccount := range in.Items {
155+
if serviceAccount.Namespace == namespace && serviceAccount.Name == name {
156+
return &serviceAccount
157+
}
158+
}
159+
return nil
160+
}
161+
162+
// MatchLabels returns the LabelSelector (from GrafanaCommonSpec) to find matching Grafana instances.
163+
func (in *GrafanaServiceAccount) MatchLabels() *metav1.LabelSelector {
164+
return in.Spec.InstanceSelector
165+
}
166+
167+
// MatchNamespace returns the namespace where this service account is defined.
168+
func (in *GrafanaServiceAccount) MatchNamespace() string {
169+
return in.ObjectMeta.Namespace
170+
}
171+
172+
// AllowCrossNamespace indicates whether cross-namespace import is allowed for this resource.
173+
func (in *GrafanaServiceAccount) AllowCrossNamespace() bool {
174+
return in.Spec.AllowCrossNamespaceImport
175+
}
176+
177+
func init() {
178+
SchemeBuilder.Register(&GrafanaServiceAccount{}, &GrafanaServiceAccountList{})
179+
}

0 commit comments

Comments
 (0)