mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
K8s: Add subresource to the example apiserver (#78030)
This commit is contained in:
167
pkg/tests/apis/example/example_test.go
Normal file
167
pkg/tests/apis/example/example_test.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package playlist
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestExampleApp(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true, // do not start extra port 6443
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagGrafanaAPIServer,
|
||||
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // Required to start the example service
|
||||
},
|
||||
})
|
||||
|
||||
t.Run("Check runtime info resource", func(t *testing.T) {
|
||||
// Resource is not namespaced!
|
||||
client := helper.Org1.Admin.Client.Resource(schema.GroupVersionResource{
|
||||
Group: "example.grafana.app",
|
||||
Version: "v0alpha1",
|
||||
Resource: "runtime",
|
||||
})
|
||||
rsp, err := client.List(context.Background(), metav1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
v, ok := rsp.Object["startupTime"].(int64)
|
||||
require.True(t, ok)
|
||||
require.Greater(t, v, time.Now().Add(-1*time.Hour).UnixMilli()) // should be within the last hour
|
||||
})
|
||||
|
||||
t.Run("Check discovery client", func(t *testing.T) {
|
||||
disco := helper.NewDiscoveryClient()
|
||||
resources, err := disco.ServerResourcesForGroupVersion("example.grafana.app/v0alpha1")
|
||||
require.NoError(t, err)
|
||||
|
||||
v1Disco, err := json.MarshalIndent(resources, "", " ")
|
||||
require.NoError(t, err)
|
||||
// fmt.Printf("%s", string(v1Disco))
|
||||
|
||||
require.JSONEq(t, `{
|
||||
"kind": "APIResourceList",
|
||||
"apiVersion": "v1",
|
||||
"groupVersion": "example.grafana.app/v0alpha1",
|
||||
"resources": [
|
||||
{
|
||||
"name": "dummy",
|
||||
"singularName": "dummy",
|
||||
"namespaced": true,
|
||||
"kind": "DummyResource",
|
||||
"verbs": [
|
||||
"get",
|
||||
"list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dummy/sub",
|
||||
"singularName": "",
|
||||
"namespaced": true,
|
||||
"kind": "DummySubresource",
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "runtime",
|
||||
"singularName": "runtime",
|
||||
"namespaced": false,
|
||||
"kind": "RuntimeInfo",
|
||||
"verbs": [
|
||||
"list"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`, string(v1Disco))
|
||||
|
||||
//fmt.Printf("%s", string(v1Disco))
|
||||
require.JSONEq(t, `[
|
||||
{
|
||||
"version": "v0alpha1",
|
||||
"freshness": "Current",
|
||||
"resources": [
|
||||
{
|
||||
"resource": "dummy",
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "DummyResource",
|
||||
"version": ""
|
||||
},
|
||||
"scope": "Namespaced",
|
||||
"singularResource": "dummy",
|
||||
"subresources": [
|
||||
{
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "DummySubresource",
|
||||
"version": ""
|
||||
},
|
||||
"subresource": "sub",
|
||||
"verbs": [
|
||||
"get"
|
||||
]
|
||||
}
|
||||
],
|
||||
"verbs": [
|
||||
"get",
|
||||
"list"
|
||||
]
|
||||
},
|
||||
{
|
||||
"resource": "runtime",
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "RuntimeInfo",
|
||||
"version": ""
|
||||
},
|
||||
"scope": "Cluster",
|
||||
"singularResource": "runtime",
|
||||
"verbs": [
|
||||
"list"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]`, helper.GetGroupVersionInfoJSON("example.grafana.app"))
|
||||
})
|
||||
|
||||
t.Run("Check dummy with subresource", func(t *testing.T) {
|
||||
client := helper.Org1.Viewer.Client.Resource(schema.GroupVersionResource{
|
||||
Group: "example.grafana.app",
|
||||
Version: "v0alpha1",
|
||||
Resource: "dummy",
|
||||
}).Namespace("default")
|
||||
rsp, err := client.Get(context.Background(), "test2", metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "dummy: test2", rsp.Object["spec"])
|
||||
require.Equal(t, "DummyResource", rsp.GetObjectKind().GroupVersionKind().Kind)
|
||||
|
||||
// Now a sub-resource
|
||||
rsp, err = client.Get(context.Background(), "test2", metav1.GetOptions{}, "sub")
|
||||
require.NoError(t, err)
|
||||
|
||||
raw, err := json.MarshalIndent(rsp, "", " ")
|
||||
require.NoError(t, err)
|
||||
//fmt.Printf("%s", string(raw))
|
||||
require.JSONEq(t, `{
|
||||
"apiVersion": "example.grafana.app/v0alpha1",
|
||||
"kind": "DummySubresource",
|
||||
"info": "default/viewer-1"
|
||||
}`, string(raw))
|
||||
})
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
||||
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
|
||||
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
@@ -397,7 +398,60 @@ func (c K8sTestHelper) createTestUsers(orgName string) OrgUsers {
|
||||
}
|
||||
}
|
||||
|
||||
func (c K8sTestHelper) CreateDS(cmd *datasources.AddDataSourceCommand) *datasources.DataSource {
|
||||
func (c *K8sTestHelper) NewDiscoveryClient() *discovery.DiscoveryClient {
|
||||
c.t.Helper()
|
||||
|
||||
baseUrl := fmt.Sprintf("http://%s", c.env.Server.HTTPServer.Listener.Addr())
|
||||
conf := &rest.Config{
|
||||
Host: baseUrl,
|
||||
Username: c.Org1.Admin.Identity.GetLogin(),
|
||||
Password: c.Org1.Admin.password,
|
||||
}
|
||||
client, err := discovery.NewDiscoveryClientForConfig(conf)
|
||||
require.NoError(c.t, err)
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *K8sTestHelper) GetGroupVersionInfoJSON(group string) string {
|
||||
c.t.Helper()
|
||||
|
||||
disco := c.NewDiscoveryClient()
|
||||
req := disco.RESTClient().Get().
|
||||
Prefix("apis").
|
||||
SetHeader("Accept", "application/json;g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList,application/json")
|
||||
|
||||
result := req.Do(context.Background())
|
||||
require.NoError(c.t, result.Error())
|
||||
|
||||
type DiscoItem struct {
|
||||
Metadata struct {
|
||||
Name string `json:"name"`
|
||||
} `json:"metadata"`
|
||||
Versions []any `json:"versions,omitempty"`
|
||||
}
|
||||
type DiscoList struct {
|
||||
Items []DiscoItem `json:"items"`
|
||||
}
|
||||
|
||||
raw, err := result.Raw()
|
||||
require.NoError(c.t, err)
|
||||
all := &DiscoList{}
|
||||
err = json.Unmarshal(raw, all)
|
||||
require.NoError(c.t, err)
|
||||
|
||||
for _, item := range all.Items {
|
||||
if item.Metadata.Name == group {
|
||||
v, err := json.MarshalIndent(item.Versions, "", " ")
|
||||
require.NoError(c.t, err)
|
||||
return string(v)
|
||||
}
|
||||
}
|
||||
|
||||
require.Fail(c.t, "could not find discovery info for: ", group)
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *K8sTestHelper) CreateDS(cmd *datasources.AddDataSourceCommand) *datasources.DataSource {
|
||||
c.t.Helper()
|
||||
|
||||
dataSource, err := c.env.Server.HTTPServer.DataSourcesService.AddDataSource(context.Background(), cmd)
|
||||
|
||||
@@ -26,13 +26,43 @@ func TestPlaylist(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("default setup", func(t *testing.T) {
|
||||
doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
h := doPlaylistTests(t, apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true, // do not start extra port 6443
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagGrafanaAPIServer,
|
||||
},
|
||||
}))
|
||||
|
||||
// The accepted verbs will change when dual write is enabled
|
||||
disco := h.GetGroupVersionInfoJSON("playlist.grafana.app")
|
||||
// fmt.Printf("%s", string(disco))
|
||||
require.JSONEq(t, `[
|
||||
{
|
||||
"version": "v0alpha1",
|
||||
"freshness": "Current",
|
||||
"resources": [
|
||||
{
|
||||
"resource": "playlists",
|
||||
"responseKind": {
|
||||
"group": "",
|
||||
"kind": "Playlist",
|
||||
"version": ""
|
||||
},
|
||||
"scope": "Namespaced",
|
||||
"singularResource": "playlist",
|
||||
"verbs": [
|
||||
"create",
|
||||
"delete",
|
||||
"get",
|
||||
"list",
|
||||
"patch",
|
||||
"update"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]`, disco)
|
||||
})
|
||||
|
||||
t.Run("with k8s api flag", func(t *testing.T) {
|
||||
@@ -47,17 +77,13 @@ func TestPlaylist(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func doPlaylistTests(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
func doPlaylistTests(t *testing.T, helper *apis.K8sTestHelper) *apis.K8sTestHelper {
|
||||
gvr := schema.GroupVersionResource{
|
||||
Group: "playlist.grafana.app",
|
||||
Version: "v0alpha1",
|
||||
Resource: "playlists",
|
||||
}
|
||||
|
||||
defer func() {
|
||||
helper.Shutdown()
|
||||
}()
|
||||
|
||||
t.Run("Check direct List permissions from different org users", func(t *testing.T) {
|
||||
// Check view permissions
|
||||
rsp := helper.List(helper.Org1.Viewer, "default", gvr)
|
||||
@@ -332,6 +358,8 @@ func doPlaylistTests(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, list.Items)
|
||||
})
|
||||
|
||||
return helper
|
||||
}
|
||||
|
||||
// typescript style map function
|
||||
|
||||
Reference in New Issue
Block a user