Merge remote-tracking branch 'origin' into attempt_at_short_url_updates
This commit is contained in:
@@ -1226,5 +1226,13 @@
|
||||
"addToProject": {
|
||||
"url": "https://github.com/orgs/grafana/projects/69"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "label",
|
||||
"name": "area/suggestions",
|
||||
"action": "addToProject",
|
||||
"addToProject": {
|
||||
"url": "https://github.com/orgs/grafana/projects/56"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -469,5 +469,15 @@
|
||||
"addToProject": {
|
||||
"url": "https://github.com/orgs/grafana/projects/190"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "changedfiles",
|
||||
"matches": [
|
||||
"public/app/features/panel/suggestions/**/*",
|
||||
"public/app/plugins/panel/**/suggestions.ts",
|
||||
"packages/grafana-data/src/types/suggestions*"
|
||||
],
|
||||
"action": "updateLabel",
|
||||
"addLabel": "area/suggestions"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -85,6 +85,7 @@ area/scenes
|
||||
area/search
|
||||
area/security
|
||||
area/streaming
|
||||
area/suggestions
|
||||
area/templating/repeating
|
||||
area/tooltip
|
||||
area/transformations
|
||||
|
||||
+2
-2
@@ -853,8 +853,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.284.0 h1:1bK7eWsnPBLUWDcWJWe218Ik5ad
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.284.0/go.mod h1:lHPniaSxq3SL5MxDIPy04TYB1jnTp/ivkYO+xn5Rz3E=
|
||||
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b h1:6Bo65etvjQ4tStkaA5+N3A3ENbO4UAWj53TxF6g2Hdk=
|
||||
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b/go.mod h1:6+wASOCN8LWt6FJ8dc0oODUBIEY5XHaE6ABi8g0mR+k=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209171149-4b999cd94388 h1:ZGYKVzeRlBWSf1e17Q8ME2WT1WmEKP8g0tMFfaBjgfI=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209171149-4b999cd94388/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 h1:rDPMdshj3QMvpXn+wK4T8awF9n2sd8i4YRiGqX2xTvg=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s=
|
||||
|
||||
+2
-3
@@ -1747,9 +1747,8 @@ enabled = true
|
||||
|
||||
#################################### Short Links #############################
|
||||
[short_links]
|
||||
# Short links that are never accessed will be deleted as cleanup. Time is set up in days. The default is 7 days. Maximum value is 365.
|
||||
# 0 means they will be deleted approximately every 10 minutes. A negative value (such as -1) will disable expiration.
|
||||
expire_time = 7
|
||||
# Short links that are never accessed will be deleted as cleanup. Time is set up in days. The default is -1 (never expire). Maximum value is 365.
|
||||
expire_time = -1
|
||||
|
||||
#################################### Internal Grafana Metrics ############
|
||||
# Metrics available at HTTP URL /metrics and /metrics/plugins/:pluginId
|
||||
|
||||
+2
-2
@@ -1689,8 +1689,8 @@ default_datasource_uid =
|
||||
|
||||
#################################### Short Links #############################
|
||||
[short_links]
|
||||
# Short links which are never accessed will be deleted as cleanup. Time is in days. Default is 7 days. Max is 365. 0 means they will be deleted approximately every 10 minutes.
|
||||
;expire_time = 7
|
||||
# Short links that are never accessed will be deleted as cleanup. Time is set up in days. The default is -1 (never expire). Maximum value is 365.
|
||||
;expire_time = -1
|
||||
|
||||
#################################### Internal Grafana Metrics ##########################
|
||||
# Metrics available at HTTP URL /metrics and /metrics/plugins/:pluginId
|
||||
|
||||
@@ -2142,17 +2142,13 @@ Configures settings around the short link feature.
|
||||
|
||||
#### `expire_time`
|
||||
|
||||
Short links that are never accessed are considered expired or stale and are deleted as cleanup.
|
||||
Short links that are never accessed are considered expired or stale and can be deleted as cleanup.
|
||||
Set the expiration time in days.
|
||||
The default is `7` days.
|
||||
The default is `-1` days (never expire).
|
||||
The maximum is `365` days.
|
||||
A setting above the maximum uses the value `365` instead.
|
||||
Setting `0` means the short links are cleaned up approximately every 10 minutes.
|
||||
A negative value such as `-1` disables expiry.
|
||||
|
||||
{{< admonition type="caution" >}}
|
||||
Short links without an expiration increase the size of the database and can't be deleted. Grafana recommends setting a duration based on your specific use case
|
||||
{{< /admonition >}}
|
||||
A setting above the maximum uses the value `365` instead.
|
||||
A negative value such as `-1` disables expiry.
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
@@ -250,6 +250,7 @@ require (
|
||||
github.com/grafana/grafana/apps/plugins v0.0.0 // @grafana/plugins-platform-backend
|
||||
github.com/grafana/grafana/apps/preferences v0.0.0 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/grafana/apps/provisioning v0.0.0 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 // @grafana/grafana-search-and-storage
|
||||
github.com/grafana/grafana/apps/scope v0.0.0 // @grafana/grafana-operator-experience-squad
|
||||
github.com/grafana/grafana/apps/secret v0.0.0 // @grafana/grafana-operator-experience-squad
|
||||
github.com/grafana/grafana/apps/shorturl v0.0.0 // @grafana/sharing-squad
|
||||
|
||||
@@ -1653,8 +1653,8 @@ github.com/grafana/grafana-plugin-sdk-go v0.284.0 h1:1bK7eWsnPBLUWDcWJWe218Ik5ad
|
||||
github.com/grafana/grafana-plugin-sdk-go v0.284.0/go.mod h1:lHPniaSxq3SL5MxDIPy04TYB1jnTp/ivkYO+xn5Rz3E=
|
||||
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b h1:6Bo65etvjQ4tStkaA5+N3A3ENbO4UAWj53TxF6g2Hdk=
|
||||
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b/go.mod h1:6+wASOCN8LWt6FJ8dc0oODUBIEY5XHaE6ABi8g0mR+k=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209171149-4b999cd94388 h1:ZGYKVzeRlBWSf1e17Q8ME2WT1WmEKP8g0tMFfaBjgfI=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209171149-4b999cd94388/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2 h1:rDPMdshj3QMvpXn+wK4T8awF9n2sd8i4YRiGqX2xTvg=
|
||||
github.com/grafana/grafana/apps/quotas v0.0.0-20251209183543-1013d74f13f2/go.mod h1:M7bV60iRB61y0ISPG1HX/oNLZtlh0ZF22rUYwNkAKjo=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8 h1:VUWsqttdf0wMI4j9OX9oNrykguQpZcruudDAFpJJVw0=
|
||||
github.com/grafana/grafana/pkg/promlib v0.0.8/go.mod h1:U1ezG/MGaEPoThqsr3lymMPN5yIPdVTJnDZ+wcXT+ao=
|
||||
github.com/grafana/grafana/pkg/semconv v0.0.0-20250804150913-990f1c69ecc2 h1:A65jWgLk4Re28gIuZcpC0aTh71JZ0ey89hKGE9h543s=
|
||||
|
||||
@@ -90,11 +90,11 @@ func RegisterAPIService(
|
||||
|
||||
builder := &IdentityAccessManagementAPIBuilder{
|
||||
store: store,
|
||||
userLegacyStore: user.NewLegacyStore(store, accessClient, enableAuthnMutation),
|
||||
saLegacyStore: serviceaccount.NewLegacyStore(store, accessClient, enableAuthnMutation),
|
||||
legacyTeamStore: team.NewLegacyStore(store, legacyAccessClient, enableAuthnMutation),
|
||||
teamBindingLegacyStore: teambinding.NewLegacyBindingStore(store, enableAuthnMutation),
|
||||
ssoLegacyStore: sso.NewLegacyStore(ssoService),
|
||||
userLegacyStore: user.NewLegacyStore(store, accessClient, enableAuthnMutation, tracing),
|
||||
saLegacyStore: serviceaccount.NewLegacyStore(store, accessClient, enableAuthnMutation, tracing),
|
||||
legacyTeamStore: team.NewLegacyStore(store, legacyAccessClient, enableAuthnMutation, tracing),
|
||||
teamBindingLegacyStore: teambinding.NewLegacyBindingStore(store, enableAuthnMutation, tracing),
|
||||
ssoLegacyStore: sso.NewLegacyStore(ssoService, tracing),
|
||||
coreRolesStorage: coreRolesStorage,
|
||||
rolesStorage: rolesStorage,
|
||||
resourcePermissionsStorage: resourcepermission.ProvideStorageBackend(dbProvider),
|
||||
@@ -114,7 +114,7 @@ func RegisterAPIService(
|
||||
dual: dual,
|
||||
unified: unified,
|
||||
userSearchClient: resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), iamv0.UserResourceInfo.GroupResource(),
|
||||
unified, user.NewUserLegacySearchClient(userService), features),
|
||||
unified, user.NewUserLegacySearchClient(userService, tracing), features),
|
||||
teamSearch: NewTeamSearchHandler(tracing, dual, team.NewLegacyTeamSearchClient(teamService), unified, features),
|
||||
}
|
||||
apiregistration.RegisterAPI(builder)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -35,14 +36,15 @@ var (
|
||||
|
||||
var resource = iamv0alpha1.ServiceAccountResourceInfo
|
||||
|
||||
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool) *LegacyStore {
|
||||
return &LegacyStore{store, ac, enableAuthnMutation}
|
||||
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool, tracer trace.Tracer) *LegacyStore {
|
||||
return &LegacyStore{store, ac, enableAuthnMutation, tracer}
|
||||
}
|
||||
|
||||
type LegacyStore struct {
|
||||
store legacy.LegacyIdentityStore
|
||||
ac claims.AccessClient
|
||||
enableAuthnMutation bool
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
// DeleteCollection implements rest.CollectionDeleter.
|
||||
@@ -52,6 +54,9 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
|
||||
|
||||
// Delete implements rest.GracefulDeleter.
|
||||
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "serviceaccount.Delete")
|
||||
defer span.End()
|
||||
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
|
||||
}
|
||||
@@ -95,6 +100,9 @@ func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.Upda
|
||||
|
||||
// Create implements rest.Creater.
|
||||
func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "serviceaccount.Create")
|
||||
defer span.End()
|
||||
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
|
||||
}
|
||||
@@ -165,6 +173,9 @@ func (s *LegacyStore) ConvertToTable(ctx context.Context, object runtime.Object,
|
||||
}
|
||||
|
||||
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "serviceaccount.List")
|
||||
defer span.End()
|
||||
|
||||
res, err := common.List(
|
||||
ctx, resource, s.ac, common.PaginationFromListOptions(options),
|
||||
func(ctx context.Context, ns claims.NamespaceInfo, p common.Pagination) (*common.ListResponse[iamv0alpha1.ServiceAccount], error) {
|
||||
@@ -228,6 +239,9 @@ func extractPluginNameFromTitle(title string) string {
|
||||
}
|
||||
|
||||
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "serviceaccount.Get")
|
||||
defer span.End()
|
||||
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
commonv1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -32,12 +33,13 @@ var (
|
||||
|
||||
var resource = iamv0.SSOSettingResourceInfo
|
||||
|
||||
func NewLegacyStore(service ssosettings.Service) *LegacyStore {
|
||||
return &LegacyStore{service}
|
||||
func NewLegacyStore(service ssosettings.Service, tracer trace.Tracer) *LegacyStore {
|
||||
return &LegacyStore{service, tracer}
|
||||
}
|
||||
|
||||
type LegacyStore struct {
|
||||
service ssosettings.Service
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
// Destroy implements rest.Storage.
|
||||
@@ -71,6 +73,9 @@ func (s *LegacyStore) NewList() runtime.Object {
|
||||
|
||||
// List implements rest.Lister.
|
||||
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "sso.List")
|
||||
defer span.End()
|
||||
|
||||
ns, _ := request.NamespaceInfoFrom(ctx, false)
|
||||
|
||||
settings, err := s.service.List(ctx)
|
||||
@@ -88,6 +93,9 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
|
||||
|
||||
// Get implements rest.Getter.
|
||||
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "sso.Get")
|
||||
defer span.End()
|
||||
|
||||
ns, _ := request.NamespaceInfoFrom(ctx, false)
|
||||
|
||||
setting, err := s.service.GetForProviderWithRedactedSecrets(ctx, name)
|
||||
@@ -112,6 +120,9 @@ func (s *LegacyStore) Update(
|
||||
_ bool,
|
||||
_ *metav1.UpdateOptions,
|
||||
) (runtime.Object, bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "sso.Update")
|
||||
defer span.End()
|
||||
|
||||
const created = false
|
||||
ident, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
@@ -148,6 +159,9 @@ func (s *LegacyStore) Delete(
|
||||
_ rest.ValidateObjectFunc,
|
||||
options *metav1.DeleteOptions,
|
||||
) (runtime.Object, bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "sso.Delete")
|
||||
defer span.End()
|
||||
|
||||
obj, err := s.Get(ctx, name, nil)
|
||||
if err != nil {
|
||||
return obj, false, err
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -35,14 +36,15 @@ var (
|
||||
|
||||
var resource = iamv0alpha1.TeamResourceInfo
|
||||
|
||||
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool) *LegacyStore {
|
||||
return &LegacyStore{store, ac, enableAuthnMutation}
|
||||
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool, tracer trace.Tracer) *LegacyStore {
|
||||
return &LegacyStore{store, ac, enableAuthnMutation, tracer}
|
||||
}
|
||||
|
||||
type LegacyStore struct {
|
||||
store legacy.LegacyIdentityStore
|
||||
ac claims.AccessClient
|
||||
enableAuthnMutation bool
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
func (s *LegacyStore) New() runtime.Object {
|
||||
@@ -74,6 +76,9 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
|
||||
|
||||
// Delete implements rest.GracefulDeleter.
|
||||
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "team.Delete")
|
||||
defer span.End()
|
||||
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
|
||||
}
|
||||
@@ -112,6 +117,9 @@ func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation
|
||||
|
||||
// Update implements rest.Updater.
|
||||
func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "team.Update")
|
||||
defer span.End()
|
||||
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "update")
|
||||
}
|
||||
@@ -161,6 +169,9 @@ func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.Upda
|
||||
}
|
||||
|
||||
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "team.List")
|
||||
defer span.End()
|
||||
|
||||
res, err := common.List(
|
||||
ctx, resource, s.ac, common.PaginationFromListOptions(options),
|
||||
func(ctx context.Context, ns claims.NamespaceInfo, p common.Pagination) (*common.ListResponse[iamv0alpha1.Team], error) {
|
||||
@@ -197,6 +208,9 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
|
||||
}
|
||||
|
||||
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "team.Get")
|
||||
defer span.End()
|
||||
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -219,6 +233,9 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO
|
||||
}
|
||||
|
||||
func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "team.Create")
|
||||
defer span.End()
|
||||
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -35,13 +36,14 @@ var (
|
||||
_ rest.CollectionDeleter = (*LegacyBindingStore)(nil)
|
||||
)
|
||||
|
||||
func NewLegacyBindingStore(store legacy.LegacyIdentityStore, enableAuthnMutation bool) *LegacyBindingStore {
|
||||
return &LegacyBindingStore{store, enableAuthnMutation}
|
||||
func NewLegacyBindingStore(store legacy.LegacyIdentityStore, enableAuthnMutation bool, tracer trace.Tracer) *LegacyBindingStore {
|
||||
return &LegacyBindingStore{store, enableAuthnMutation, tracer}
|
||||
}
|
||||
|
||||
type LegacyBindingStore struct {
|
||||
store legacy.LegacyIdentityStore
|
||||
enableAuthnMutation bool
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
// Destroy implements rest.Storage.
|
||||
@@ -73,6 +75,9 @@ func (l *LegacyBindingStore) ConvertToTable(ctx context.Context, object runtime.
|
||||
}
|
||||
|
||||
func (l *LegacyBindingStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
ctx, span := l.tracer.Start(ctx, "teambinding.Update")
|
||||
defer span.End()
|
||||
|
||||
if !l.enableAuthnMutation {
|
||||
return nil, false, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "update")
|
||||
}
|
||||
@@ -125,6 +130,9 @@ func (l *LegacyBindingStore) Update(ctx context.Context, name string, objInfo re
|
||||
}
|
||||
|
||||
func (l *LegacyBindingStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
ctx, span := l.tracer.Start(ctx, "teambinding.Delete")
|
||||
defer span.End()
|
||||
|
||||
if !l.enableAuthnMutation {
|
||||
return nil, false, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "delete")
|
||||
}
|
||||
@@ -160,6 +168,9 @@ func (l *LegacyBindingStore) DeleteCollection(ctx context.Context, deleteValidat
|
||||
}
|
||||
|
||||
func (l *LegacyBindingStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
ctx, span := l.tracer.Start(ctx, "teambinding.Create")
|
||||
defer span.End()
|
||||
|
||||
if !l.enableAuthnMutation {
|
||||
return nil, apierrors.NewMethodNotSupported(bindingResource.GroupResource(), "create")
|
||||
}
|
||||
@@ -230,6 +241,9 @@ func (l *LegacyBindingStore) Create(ctx context.Context, obj runtime.Object, cre
|
||||
|
||||
// Get implements rest.Getter.
|
||||
func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ctx, span := l.tracer.Start(ctx, "teambinding.Get")
|
||||
defer span.End()
|
||||
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -254,6 +268,9 @@ func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *meta
|
||||
|
||||
// List implements rest.Lister.
|
||||
func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ctx, span := l.tracer.Start(ctx, "teambinding.List")
|
||||
defer span.End()
|
||||
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"log/slog"
|
||||
"math"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
@@ -27,13 +28,15 @@ type UserLegacySearchClient struct {
|
||||
resourcepb.ResourceIndexClient
|
||||
userService user.Service
|
||||
log *slog.Logger
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
// NewUserLegacySearchClient creates a new UserLegacySearchClient.
|
||||
func NewUserLegacySearchClient(userService user.Service) *UserLegacySearchClient {
|
||||
func NewUserLegacySearchClient(userService user.Service, tracer trace.Tracer) *UserLegacySearchClient {
|
||||
return &UserLegacySearchClient{
|
||||
userService: userService,
|
||||
log: slog.Default().With("logger", "legacy-user-search-client"),
|
||||
tracer: tracer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +44,9 @@ func NewUserLegacySearchClient(userService user.Service) *UserLegacySearchClient
|
||||
// It only supports exact matching for title, login, or email.
|
||||
// FIXME: This implementation only supports a single field query and will be extended in the future.
|
||||
func (c *UserLegacySearchClient) Search(ctx context.Context, req *resourcepb.ResourceSearchRequest, _ ...grpc.CallOption) (*resourcepb.ResourceSearchResponse, error) {
|
||||
ctx, span := c.tracer.Start(ctx, "user.Search")
|
||||
defer span.End()
|
||||
|
||||
signedInUser, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/usertest"
|
||||
res "github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
@@ -17,7 +18,7 @@ import (
|
||||
func TestUserLegacySearchClient_Search(t *testing.T) {
|
||||
t.Run("should return error if no query fields are provided", func(t *testing.T) {
|
||||
mockUserService := usertest.NewMockService(t)
|
||||
client := NewUserLegacySearchClient(mockUserService)
|
||||
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
|
||||
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
|
||||
req := &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
@@ -66,7 +67,7 @@ func TestUserLegacySearchClient_Search(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockUserService := usertest.NewMockService(t)
|
||||
client := NewUserLegacySearchClient(mockUserService)
|
||||
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
|
||||
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
|
||||
req := &resourcepb.ResourceSearchRequest{
|
||||
Limit: 10,
|
||||
@@ -125,7 +126,7 @@ func TestUserLegacySearchClient_Search(t *testing.T) {
|
||||
|
||||
t.Run("title should have precedence over login and email", func(t *testing.T) {
|
||||
mockUserService := usertest.NewMockService(t)
|
||||
client := NewUserLegacySearchClient(mockUserService)
|
||||
client := NewUserLegacySearchClient(mockUserService, tracing.NewNoopTracerService())
|
||||
ctx := identity.WithRequester(context.Background(), &user.SignedInUser{OrgID: 1, UserID: 1})
|
||||
req := &resourcepb.ResourceSearchRequest{
|
||||
Options: &resourcepb.ListOptions{
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -36,18 +37,22 @@ var (
|
||||
|
||||
var resource = iamv0alpha1.UserResourceInfo
|
||||
|
||||
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool) *LegacyStore {
|
||||
return &LegacyStore{store, ac, enableAuthnMutation}
|
||||
func NewLegacyStore(store legacy.LegacyIdentityStore, ac claims.AccessClient, enableAuthnMutation bool, tracer trace.Tracer) *LegacyStore {
|
||||
return &LegacyStore{store, ac, enableAuthnMutation, tracer}
|
||||
}
|
||||
|
||||
type LegacyStore struct {
|
||||
store legacy.LegacyIdentityStore
|
||||
ac claims.AccessClient
|
||||
enableAuthnMutation bool
|
||||
tracer trace.Tracer
|
||||
}
|
||||
|
||||
// Update implements rest.Updater.
|
||||
func (s *LegacyStore) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "user.Update")
|
||||
defer span.End()
|
||||
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "update")
|
||||
}
|
||||
@@ -105,6 +110,9 @@ func (s *LegacyStore) DeleteCollection(ctx context.Context, deleteValidation res
|
||||
|
||||
// Delete implements rest.GracefulDeleter.
|
||||
func (s *LegacyStore) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "user.Delete")
|
||||
defer span.End()
|
||||
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, false, apierrors.NewMethodNotSupported(resource.GroupResource(), "delete")
|
||||
}
|
||||
@@ -171,6 +179,9 @@ func (s *LegacyStore) ConvertToTable(ctx context.Context, object runtime.Object,
|
||||
}
|
||||
|
||||
func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "user.List")
|
||||
defer span.End()
|
||||
|
||||
res, err := common.List(
|
||||
ctx, resource, s.ac, common.PaginationFromListOptions(options),
|
||||
func(ctx context.Context, ns claims.NamespaceInfo, p common.Pagination) (*common.ListResponse[iamv0alpha1.User], error) {
|
||||
@@ -206,6 +217,9 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
|
||||
}
|
||||
|
||||
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "user.Get")
|
||||
defer span.End()
|
||||
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -229,6 +243,9 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO
|
||||
|
||||
// Create implements rest.Creater.
|
||||
func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
ctx, span := s.tracer.Start(ctx, "user.Create")
|
||||
defer span.End()
|
||||
|
||||
if !s.enableAuthnMutation {
|
||||
return nil, apierrors.NewMethodNotSupported(resource.GroupResource(), "create")
|
||||
}
|
||||
|
||||
@@ -1330,7 +1330,7 @@ func (cfg *Cfg) parseINIFile(iniFile *ini.File) error {
|
||||
cfg.QueryHistoryEnabled = queryHistory.Key("enabled").MustBool(true)
|
||||
|
||||
shortLinks := iniFile.Section("short_links")
|
||||
cfg.ShortLinkExpiration = shortLinks.Key("expire_time").MustInt(7)
|
||||
cfg.ShortLinkExpiration = shortLinks.Key("expire_time").MustInt(-1)
|
||||
|
||||
if cfg.ShortLinkExpiration > 365 {
|
||||
cfg.Logger.Warn("short_links expire_time must be less than 366 days. Setting to 365 days")
|
||||
|
||||
@@ -1618,6 +1618,7 @@ func (s *server) checkQuota(ctx context.Context, nsr NamespacedResource) {
|
||||
))
|
||||
|
||||
if s.overridesService == nil {
|
||||
s.log.FromContext(ctx).Debug("overrides service not configured, skipping quota check", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1632,6 +1633,12 @@ func (s *server) checkQuota(ctx context.Context, nsr NamespacedResource) {
|
||||
s.log.FromContext(ctx).Error("failed to get resource stats for quota checking", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource, "error", err)
|
||||
return
|
||||
}
|
||||
if len(stats) > 0 {
|
||||
s.log.FromContext(ctx).Debug("stats found", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource, "count", stats[0].Count)
|
||||
} else {
|
||||
s.log.FromContext(ctx).Debug("no stats found for resource", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource)
|
||||
}
|
||||
|
||||
if len(stats) > 0 && stats[0].Count >= int64(quota.Limit) {
|
||||
s.log.FromContext(ctx).Info("Quota exceeded on create", "namespace", nsr.Namespace, "group", nsr.Group, "resource", nsr.Resource, "quota", quota.Limit, "count", stats[0].Count, "stats_resource", stats[0].Resource)
|
||||
}
|
||||
|
||||
@@ -236,6 +236,10 @@ export class KeybindingSrv {
|
||||
appEvents.publish(new ZoomOutEvent({ scale: 0.5, updateUrl }));
|
||||
});
|
||||
|
||||
this.bind('t =', () => {
|
||||
appEvents.publish(new ZoomOutEvent({ scale: 0.5, updateUrl }));
|
||||
});
|
||||
|
||||
this.bind('t -', () => {
|
||||
appEvents.publish(new ZoomOutEvent({ scale: 2, updateUrl }));
|
||||
});
|
||||
|
||||
@@ -269,6 +269,13 @@ describe('setupKeyboardShortcuts', () => {
|
||||
expect(tPlusBinding).toBeDefined();
|
||||
});
|
||||
|
||||
it('should setup t = zoom in shortcut', () => {
|
||||
setupKeyboardShortcuts(mockScene);
|
||||
|
||||
const tEqualsBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't =');
|
||||
expect(tEqualsBinding).toBeDefined();
|
||||
});
|
||||
|
||||
it('should setup t - zoom out shortcut with keypress type', () => {
|
||||
setupKeyboardShortcuts(mockScene);
|
||||
|
||||
@@ -302,9 +309,11 @@ describe('setupKeyboardShortcuts', () => {
|
||||
setupKeyboardShortcuts(mockScene);
|
||||
|
||||
const tPlusBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't +');
|
||||
const tEqualsBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't =');
|
||||
const tMinusBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't -');
|
||||
|
||||
expect(tPlusBinding).toBeUndefined();
|
||||
expect(tEqualsBinding).toBeUndefined();
|
||||
expect(tMinusBinding).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -364,6 +373,28 @@ describe('setupKeyboardShortcuts', () => {
|
||||
expect(newSpan).toBe(3 * 60 * 60 * 1000); // 3 hours in milliseconds
|
||||
});
|
||||
|
||||
it('should zoom in (scale 0.5) when t = is pressed', () => {
|
||||
setupKeyboardShortcuts(mockScene);
|
||||
|
||||
const tEqualsBinding = mockKeybindingSet.addBinding.mock.calls.find((call) => call[0].key === 't =');
|
||||
const handler = tEqualsBinding![0].onTrigger;
|
||||
|
||||
handler();
|
||||
|
||||
// Scale 0.5 should result in 3 hour span (half of 6)
|
||||
expect(mockTimeRange.onTimeRangeChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
from: expect.any(Object),
|
||||
to: expect.any(Object),
|
||||
raw: expect.any(Object),
|
||||
})
|
||||
);
|
||||
|
||||
const call = mockTimeRange.onTimeRangeChange.mock.calls[0][0];
|
||||
const newSpan = call.to.valueOf() - call.from.valueOf();
|
||||
expect(newSpan).toBe(3 * 60 * 60 * 1000); // 3 hours in milliseconds
|
||||
});
|
||||
|
||||
it('should keep center point when zooming in', () => {
|
||||
setupKeyboardShortcuts(mockScene);
|
||||
|
||||
|
||||
@@ -138,6 +138,13 @@ export function setupKeyboardShortcuts(scene: DashboardScene) {
|
||||
},
|
||||
});
|
||||
|
||||
keybindings.addBinding({
|
||||
key: 't =',
|
||||
onTrigger: () => {
|
||||
handleZoom(scene, 0.5);
|
||||
},
|
||||
});
|
||||
|
||||
keybindings.addBinding({
|
||||
key: 't -',
|
||||
type: 'keypress', // NOTE: Because some browsers/OS identify minus symbol differently.
|
||||
|
||||
Reference in New Issue
Block a user