From 98401be295855bbc7171fba3a4e47e9b5001cb3a Mon Sep 17 00:00:00 2001 From: "grafana-delivery-bot[bot]" <132647405+grafana-delivery-bot[bot]@users.noreply.github.com> Date: Tue, 16 Apr 2024 17:01:46 +0100 Subject: [PATCH] [v11.0.x] Scope: Add basic integration tests (#86367) Scopes: Add basic integration tests (#85351) Co-authored-by: Kyle Brandt (cherry picked from commit dd6c8732b37cf3e0bbea0cb04a6e7d2598ea5b07) Co-authored-by: Ryan McKinley --- pkg/apis/scope/v0alpha1/register.go | 14 +- pkg/registry/apis/scope/register.go | 18 +- pkg/tests/apis/scopes/scopes_test.go | 155 ++++++++++++++++++ .../example-scope-dashboard-binding-abc.yaml | 7 + .../example-scope-dashboard-binding-xyz.yaml | 7 + .../apis/scopes/testdata/example-scope.yaml | 16 ++ .../apis/scopes/testdata/example-scope2.yaml | 16 ++ 7 files changed, 217 insertions(+), 16 deletions(-) create mode 100644 pkg/tests/apis/scopes/scopes_test.go create mode 100644 pkg/tests/apis/scopes/testdata/example-scope-dashboard-binding-abc.yaml create mode 100644 pkg/tests/apis/scopes/testdata/example-scope-dashboard-binding-xyz.yaml create mode 100644 pkg/tests/apis/scopes/testdata/example-scope.yaml create mode 100644 pkg/tests/apis/scopes/testdata/example-scope2.yaml diff --git a/pkg/apis/scope/v0alpha1/register.go b/pkg/apis/scope/v0alpha1/register.go index 6581f5948d3..1503e9d9294 100644 --- a/pkg/apis/scope/v0alpha1/register.go +++ b/pkg/apis/scope/v0alpha1/register.go @@ -1,7 +1,6 @@ package v0alpha1 import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -28,7 +27,8 @@ var ScopeDashboardBindingResourceInfo = common.NewResourceInfo(GROUP, VERSION, var ( // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION} + SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION} + InternalGroupVersion = schema.GroupVersion{Group: GROUP, Version: runtime.APIVersionInternal} // SchemaBuilder is used by standard codegen SchemeBuilder runtime.SchemeBuilder @@ -37,18 +37,20 @@ var ( ) func init() { - localSchemeBuilder.Register(addKnownTypes) + localSchemeBuilder.Register(func(s *runtime.Scheme) error { + return AddKnownTypes(SchemeGroupVersion, s) + }) } // Adds the list of known types to the given scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, +func AddKnownTypes(gv schema.GroupVersion, scheme *runtime.Scheme) error { + scheme.AddKnownTypes(gv, &Scope{}, &ScopeList{}, &ScopeDashboardBinding{}, &ScopeDashboardBindingList{}, ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + //metav1.AddToGroupVersion(scheme, gv) return nil } diff --git a/pkg/registry/apis/scope/register.go b/pkg/registry/apis/scope/register.go index 54ac4b531b7..71699b5f9ef 100644 --- a/pkg/registry/apis/scope/register.go +++ b/pkg/registry/apis/scope/register.go @@ -45,7 +45,6 @@ func (b *ScopeAPIBuilder) GetGroupVersion() schema.GroupVersion { } func (b *ScopeAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { - gv := scope.SchemeGroupVersion err := scope.AddToScheme(scheme) if err != nil { return err @@ -83,15 +82,14 @@ func (b *ScopeAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { return err } - // Link this version to the internal representation. - // This is used for server-side-apply (PATCH), and avoids the error: - // "no kind is registered for the type" - // addKnownTypes(scheme, schema.GroupVersion{ - // Group: scope.GROUP, - // Version: runtime.APIVersionInternal, - // }) - metav1.AddToGroupVersion(scheme, gv) - return scheme.SetVersionPriority(gv) + // This is required for --server-side apply + err = scope.AddKnownTypes(scope.InternalGroupVersion, scheme) + if err != nil { + return err + } + + // Only one version right now + return scheme.SetVersionPriority(scope.SchemeGroupVersion) } func (b *ScopeAPIBuilder) GetAPIGroupInfo( diff --git a/pkg/tests/apis/scopes/scopes_test.go b/pkg/tests/apis/scopes/scopes_test.go new file mode 100644 index 00000000000..caaa02f89a3 --- /dev/null +++ b/pkg/tests/apis/scopes/scopes_test.go @@ -0,0 +1,155 @@ +package playlist + +import ( + "context" + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/tests/apis" + "github.com/grafana/grafana/pkg/tests/testinfra" + "github.com/grafana/grafana/pkg/tests/testsuite" +) + +func TestMain(m *testing.M) { + testsuite.Run(m) +} + +func TestIntegrationScopes(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + ctx := context.Background() + helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{ + AppModeProduction: false, // required for experimental APIs + EnableFeatureToggles: []string{ + featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // Required to start the example service + }, + }) + + t.Run("Check discovery client", func(t *testing.T) { + disco := helper.NewDiscoveryClient() + resources, err := disco.ServerResourcesForGroupVersion("scope.grafana.app/v0alpha1") + require.NoError(t, err) + + v1Disco, err := json.MarshalIndent(resources, "", " ") + require.NoError(t, err) + + require.JSONEq(t, `{ + "kind": "APIResourceList", + "apiVersion": "v1", + "groupVersion": "scope.grafana.app/v0alpha1", + "resources": [ + { + "name": "scopedashboardbindings", + "singularName": "scopedashboardbinding", + "namespaced": true, + "kind": "ScopeDashboardBinding", + "verbs": [ + "create", + "delete", + "deletecollection", + "get", + "list", + "patch", + "update", + "watch" + ] + }, + { + "name": "scopes", + "singularName": "scope", + "namespaced": true, + "kind": "Scope", + "verbs": [ + "create", + "delete", + "deletecollection", + "get", + "list", + "patch", + "update", + "watch" + ] + } + ] + }`, string(v1Disco)) + }) + + t.Run("Check create and list", func(t *testing.T) { + // Scope create+get + scopeClient := helper.GetResourceClient(apis.ResourceClientArgs{ + User: helper.Org1.Admin, + Namespace: "default", // actually org1 + GVR: schema.GroupVersionResource{ + Group: "scope.grafana.app", Version: "v0alpha1", Resource: "scopes", + }, + }) + createOptions := metav1.CreateOptions{FieldValidation: "Strict"} + + s0, err := scopeClient.Resource.Create(ctx, + helper.LoadYAMLOrJSONFile("testdata/example-scope.yaml"), + createOptions, + ) + + require.NoError(t, err) + require.Equal(t, "example", s0.GetName()) + s1, err := scopeClient.Resource.Get(ctx, "example", metav1.GetOptions{}) + require.NoError(t, err) + require.Equal(t, + mustNestedString(s0.Object, "spec", "title"), + mustNestedString(s1.Object, "spec", "title"), + ) + + _, err = scopeClient.Resource.Create(ctx, + helper.LoadYAMLOrJSONFile("testdata/example-scope2.yaml"), + createOptions, + ) + require.NoError(t, err) + + // Field Selector test + found, err := scopeClient.Resource.List(ctx, metav1.ListOptions{ + FieldSelector: "spec.category=fun", + }) + require.NoError(t, err) + require.Len(t, found.Items, 1) + require.Equal(t, + "example2", + mustNestedString(found.Items[0].Object, "metadata", "name"), + ) + + // Create bindings + scopeDashboardBindingClient := helper.GetResourceClient(apis.ResourceClientArgs{ + User: helper.Org1.Admin, + Namespace: "default", // actually org1 + GVR: schema.GroupVersionResource{ + Group: "scope.grafana.app", Version: "v0alpha1", Resource: "scopedashboardbindings", + }, + }) + _, err = scopeDashboardBindingClient.Resource.Create(ctx, + helper.LoadYAMLOrJSONFile("testdata/example-scope-dashboard-binding-abc.yaml"), + createOptions, + ) + require.NoError(t, err) + _, err = scopeDashboardBindingClient.Resource.Create(ctx, + helper.LoadYAMLOrJSONFile("testdata/example-scope-dashboard-binding-xyz.yaml"), + createOptions, + ) + require.NoError(t, err) + + found, err = scopeDashboardBindingClient.Resource.List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, found.Items, 2) + }) +} + +func mustNestedString(obj map[string]interface{}, fields ...string) string { + v, _, _ := unstructured.NestedString(obj, fields...) + return v +} diff --git a/pkg/tests/apis/scopes/testdata/example-scope-dashboard-binding-abc.yaml b/pkg/tests/apis/scopes/testdata/example-scope-dashboard-binding-abc.yaml new file mode 100644 index 00000000000..be175b5613b --- /dev/null +++ b/pkg/tests/apis/scopes/testdata/example-scope-dashboard-binding-abc.yaml @@ -0,0 +1,7 @@ +apiVersion: scope.grafana.app/v0alpha1 +kind: ScopeDashboardBinding +metadata: + name: example_abc +spec: + scope: example + dashboard: abc \ No newline at end of file diff --git a/pkg/tests/apis/scopes/testdata/example-scope-dashboard-binding-xyz.yaml b/pkg/tests/apis/scopes/testdata/example-scope-dashboard-binding-xyz.yaml new file mode 100644 index 00000000000..98cfa8ed93f --- /dev/null +++ b/pkg/tests/apis/scopes/testdata/example-scope-dashboard-binding-xyz.yaml @@ -0,0 +1,7 @@ +apiVersion: scope.grafana.app/v0alpha1 +kind: ScopeDashboardBinding +metadata: + name: example_xyz +spec: + scope: example + dashboard: xyz \ No newline at end of file diff --git a/pkg/tests/apis/scopes/testdata/example-scope.yaml b/pkg/tests/apis/scopes/testdata/example-scope.yaml new file mode 100644 index 00000000000..4adaf8fde71 --- /dev/null +++ b/pkg/tests/apis/scopes/testdata/example-scope.yaml @@ -0,0 +1,16 @@ +apiVersion: scope.grafana.app/v0alpha1 +kind: Scope +metadata: + name: example +spec: + title: Example scope + description: Longer description for a scope + category: should this be an enum or an external reference? + type: should this be an enum or an external reference? + filters: + - key: aaa + operator: equals + value: bbb + - key: ccc + operator: not-equals + value: ddd diff --git a/pkg/tests/apis/scopes/testdata/example-scope2.yaml b/pkg/tests/apis/scopes/testdata/example-scope2.yaml new file mode 100644 index 00000000000..951bf889c93 --- /dev/null +++ b/pkg/tests/apis/scopes/testdata/example-scope2.yaml @@ -0,0 +1,16 @@ +apiVersion: scope.grafana.app/v0alpha1 +kind: Scope +metadata: + name: example2 +spec: + title: Example scope 2 + description: Longer description for a scope + category: fun + type: should this be an enum or an external reference? + filters: + - key: aaa + operator: equals + value: zzz + - key: ccc + operator: not-equals + value: yyy