mirror of https://github.com/grafana/grafana
parent
0118c7666a
commit
df148ca9ea
@ -0,0 +1,30 @@ |
|||||||
|
package resource |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/fullstorydev/grpchan" |
||||||
|
"github.com/fullstorydev/grpchan/inprocgrpc" |
||||||
|
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth" |
||||||
|
"google.golang.org/grpc" |
||||||
|
|
||||||
|
grpcUtils "github.com/grafana/grafana/pkg/services/store/entity/grpc" |
||||||
|
) |
||||||
|
|
||||||
|
func NewResourceStoreClientLocal(server ResourceStoreServer) ResourceStoreClient { |
||||||
|
channel := &inprocgrpc.Channel{} |
||||||
|
|
||||||
|
auth := &grpcUtils.Authenticator{} |
||||||
|
|
||||||
|
channel.RegisterService( |
||||||
|
grpchan.InterceptServer( |
||||||
|
&ResourceStore_ServiceDesc, |
||||||
|
grpcAuth.UnaryServerInterceptor(auth.Authenticate), |
||||||
|
grpcAuth.StreamServerInterceptor(auth.Authenticate), |
||||||
|
), |
||||||
|
server, |
||||||
|
) |
||||||
|
return NewResourceStoreClient(grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor)) |
||||||
|
} |
||||||
|
|
||||||
|
func NewEntityStoreClientGRPC(channel *grpc.ClientConn) ResourceStoreClient { |
||||||
|
return NewResourceStoreClient(grpchan.InterceptClientConn(channel, grpcUtils.UnaryClientInterceptor, grpcUtils.StreamClientInterceptor)) |
||||||
|
} |
||||||
@ -0,0 +1,105 @@ |
|||||||
|
package resource_server_tests |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/tracing" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/components/satokengen" |
||||||
|
"github.com/grafana/grafana/pkg/infra/appcontext" |
||||||
|
"github.com/grafana/grafana/pkg/server" |
||||||
|
"github.com/grafana/grafana/pkg/services/featuremgmt" |
||||||
|
"github.com/grafana/grafana/pkg/services/org" |
||||||
|
saAPI "github.com/grafana/grafana/pkg/services/serviceaccounts/api" |
||||||
|
saTests "github.com/grafana/grafana/pkg/services/serviceaccounts/tests" |
||||||
|
"github.com/grafana/grafana/pkg/services/store/entity/db/dbimpl" |
||||||
|
"github.com/grafana/grafana/pkg/services/store/resource" |
||||||
|
"github.com/grafana/grafana/pkg/services/store/resource/sqlstash" |
||||||
|
"github.com/grafana/grafana/pkg/services/user" |
||||||
|
"github.com/grafana/grafana/pkg/tests/testinfra" |
||||||
|
"github.com/grafana/grafana/pkg/tests/testsuite" |
||||||
|
) |
||||||
|
|
||||||
|
func TestMain(m *testing.M) { |
||||||
|
testsuite.Run(m) |
||||||
|
} |
||||||
|
|
||||||
|
func createServiceAccountAdminToken(t *testing.T, env *server.TestEnv) (string, *user.SignedInUser) { |
||||||
|
t.Helper() |
||||||
|
|
||||||
|
account := saTests.SetupUserServiceAccount(t, env.SQLStore, env.Cfg, saTests.TestUser{ |
||||||
|
Name: "grpc-server-sa", |
||||||
|
Role: string(org.RoleAdmin), |
||||||
|
Login: "grpc-server-sa", |
||||||
|
IsServiceAccount: true, |
||||||
|
}) |
||||||
|
|
||||||
|
keyGen, err := satokengen.New(saAPI.ServiceID) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
_ = saTests.SetupApiKey(t, env.SQLStore, env.Cfg, saTests.TestApiKey{ |
||||||
|
Name: "grpc-server-test", |
||||||
|
Role: org.RoleAdmin, |
||||||
|
OrgId: account.OrgID, |
||||||
|
Key: keyGen.HashedKey, |
||||||
|
ServiceAccountID: &account.ID, |
||||||
|
}) |
||||||
|
|
||||||
|
return keyGen.ClientSecret, &user.SignedInUser{ |
||||||
|
UserID: account.ID, |
||||||
|
Email: account.Email, |
||||||
|
Name: account.Name, |
||||||
|
Login: account.Login, |
||||||
|
OrgID: account.OrgID, |
||||||
|
IsServiceAccount: account.IsServiceAccount, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type testContext struct { |
||||||
|
authToken string |
||||||
|
client resource.ResourceStoreClient |
||||||
|
user *user.SignedInUser |
||||||
|
ctx context.Context |
||||||
|
} |
||||||
|
|
||||||
|
func createTestContext(t *testing.T) testContext { |
||||||
|
t.Helper() |
||||||
|
|
||||||
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ |
||||||
|
EnableFeatureToggles: []string{ |
||||||
|
featuremgmt.FlagGrpcServer, |
||||||
|
featuremgmt.FlagUnifiedStorage, |
||||||
|
}, |
||||||
|
AppModeProduction: false, // required for migrations to run
|
||||||
|
GRPCServerAddress: "127.0.0.1:0", // :0 for choosing the port automatically
|
||||||
|
}) |
||||||
|
|
||||||
|
_, env := testinfra.StartGrafanaEnv(t, dir, path) |
||||||
|
|
||||||
|
authToken, serviceAccountUser := createServiceAccountAdminToken(t, env) |
||||||
|
|
||||||
|
eDB, err := dbimpl.ProvideEntityDB(env.SQLStore, env.Cfg, env.FeatureToggles, nil) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
err = eDB.Init() |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
traceConfig, err := tracing.ParseTracingConfig(env.Cfg) |
||||||
|
require.NoError(t, err) |
||||||
|
tracer, err := tracing.ProvideService(traceConfig) |
||||||
|
require.NoError(t, err) |
||||||
|
store, err := sqlstash.ProvideSQLResourceServer(eDB, tracer) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
client := resource.NewResourceStoreClientLocal(store) |
||||||
|
|
||||||
|
return testContext{ |
||||||
|
authToken: authToken, |
||||||
|
client: client, |
||||||
|
user: serviceAccountUser, |
||||||
|
ctx: appcontext.WithUser(context.Background(), serviceAccountUser), |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,84 @@ |
|||||||
|
package resource_server_tests |
||||||
|
|
||||||
|
import ( |
||||||
|
_ "embed" |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"net/http" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/stretchr/testify/require" |
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||||
|
"k8s.io/apimachinery/pkg/types" |
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/apis/playlist/v0alpha1" |
||||||
|
"github.com/grafana/grafana/pkg/infra/appcontext" |
||||||
|
"github.com/grafana/grafana/pkg/services/store/resource" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
//go:embed testdata/dashboard-with-tags-b-g.json
|
||||||
|
dashboardWithTagsBlueGreen string |
||||||
|
//go:embed testdata/dashboard-with-tags-r-g.json
|
||||||
|
dashboardWithTagsRedGreen string |
||||||
|
) |
||||||
|
|
||||||
|
func TestIntegrationEntityServer(t *testing.T) { |
||||||
|
if testing.Short() { |
||||||
|
t.Skip("skipping integration test") |
||||||
|
} |
||||||
|
|
||||||
|
testCtx := createTestContext(t) |
||||||
|
ctx := appcontext.WithUser(testCtx.ctx, testCtx.user) |
||||||
|
|
||||||
|
t.Run("should not retrieve non-existent objects", func(t *testing.T) { |
||||||
|
resp, err := testCtx.client.GetResource(ctx, &resource.GetResourceRequest{ |
||||||
|
Key: &resource.Key{ |
||||||
|
Group: "X", |
||||||
|
Namespace: "X", |
||||||
|
}, |
||||||
|
}) |
||||||
|
require.NoError(t, err) |
||||||
|
require.NotNil(t, resp) |
||||||
|
require.NotNil(t, resp.Status) |
||||||
|
require.Nil(t, resp.Value) |
||||||
|
require.Equal(t, int32(http.StatusBadRequest), resp.Status.Code) |
||||||
|
}) |
||||||
|
|
||||||
|
t.Run("insert an object", func(t *testing.T) { |
||||||
|
var err error |
||||||
|
key := &resource.Key{ |
||||||
|
Namespace: "default", |
||||||
|
Group: "playlists.grafana.app", |
||||||
|
Resource: "Playlist", |
||||||
|
Name: "x123", |
||||||
|
} |
||||||
|
sample := v0alpha1.Playlist{ |
||||||
|
ObjectMeta: metav1.ObjectMeta{ |
||||||
|
Name: key.Name, |
||||||
|
Namespace: key.Namespace, |
||||||
|
UID: types.UID("xyz"), |
||||||
|
}, |
||||||
|
TypeMeta: metav1.TypeMeta{ |
||||||
|
Kind: key.Resource, |
||||||
|
APIVersion: key.Group + "/v0alpha1", |
||||||
|
}, |
||||||
|
Spec: v0alpha1.Spec{ |
||||||
|
Title: "hello", |
||||||
|
}, |
||||||
|
} |
||||||
|
req := &resource.CreateRequest{ |
||||||
|
Key: key, |
||||||
|
} |
||||||
|
req.Value, err = json.Marshal(sample) |
||||||
|
require.NoError(t, err) |
||||||
|
|
||||||
|
fmt.Printf("%s", string(req.Value)) |
||||||
|
|
||||||
|
resp, err := testCtx.client.Create(ctx, req) |
||||||
|
require.NoError(t, err) |
||||||
|
require.NotNil(t, resp) |
||||||
|
require.Nil(t, resp.Status) |
||||||
|
require.True(t, resp.ResourceVersion > 0) // that it has a positive resource version
|
||||||
|
}) |
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
{ |
||||||
|
"tags": [ |
||||||
|
"blue", |
||||||
|
"green" |
||||||
|
], |
||||||
|
"editable": true, |
||||||
|
"fiscalYearStartMonth": 0, |
||||||
|
"graphTooltip": 0, |
||||||
|
"id": 221, |
||||||
|
"links": [], |
||||||
|
"liveNow": false, |
||||||
|
"panels": [ |
||||||
|
{ |
||||||
|
"gridPos": { |
||||||
|
"h": 1, |
||||||
|
"w": 24, |
||||||
|
"x": 0, |
||||||
|
"y": 8 |
||||||
|
}, |
||||||
|
"id": 8, |
||||||
|
"title": "Row title", |
||||||
|
"type": "row" |
||||||
|
} |
||||||
|
], |
||||||
|
"schemaVersion": 36, |
||||||
|
"templating": { |
||||||
|
"list": [] |
||||||
|
}, |
||||||
|
"time": { |
||||||
|
"from": "now-6h", |
||||||
|
"to": "now" |
||||||
|
}, |
||||||
|
"timepicker": {}, |
||||||
|
"timezone": "", |
||||||
|
"title": "special ds", |
||||||
|
"uid": "mocpwtR4k", |
||||||
|
"version": 1, |
||||||
|
"weekStart": "" |
||||||
|
} |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
{ |
||||||
|
"tags": [ |
||||||
|
"red", |
||||||
|
"green" |
||||||
|
], |
||||||
|
"editable": true, |
||||||
|
"fiscalYearStartMonth": 0, |
||||||
|
"graphTooltip": 0, |
||||||
|
"id": 221, |
||||||
|
"links": [], |
||||||
|
"liveNow": false, |
||||||
|
"panels": [ |
||||||
|
{ |
||||||
|
"gridPos": { |
||||||
|
"h": 1, |
||||||
|
"w": 24, |
||||||
|
"x": 0, |
||||||
|
"y": 8 |
||||||
|
}, |
||||||
|
"id": 8, |
||||||
|
"title": "Row title", |
||||||
|
"type": "row" |
||||||
|
} |
||||||
|
], |
||||||
|
"schemaVersion": 36, |
||||||
|
"templating": { |
||||||
|
"list": [] |
||||||
|
}, |
||||||
|
"time": { |
||||||
|
"from": "now-6h", |
||||||
|
"to": "now" |
||||||
|
}, |
||||||
|
"timepicker": {}, |
||||||
|
"timezone": "", |
||||||
|
"title": "special ds", |
||||||
|
"uid": "mocpwtR4k", |
||||||
|
"version": 1, |
||||||
|
"weekStart": "" |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
package resource |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"net/http" |
||||||
|
) |
||||||
|
|
||||||
|
func badRequest(format string, a ...any) *StatusResult { |
||||||
|
return &StatusResult{ |
||||||
|
Status: "Failure", |
||||||
|
Message: fmt.Sprintf(format, a...), |
||||||
|
Code: http.StatusBadRequest, |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue