mirror of https://github.com/grafana/grafana
K8s: Add subresource to the example apiserver (#78030)
parent
a2a6f9a6d8
commit
1be1432926
@ -0,0 +1,19 @@ |
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by defaulter-gen. DO NOT EDIT.
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
runtime "k8s.io/apimachinery/pkg/runtime" |
||||
) |
||||
|
||||
// RegisterDefaults adds defaulters functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error { |
||||
return nil |
||||
} |
||||
@ -1,89 +0,0 @@ |
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
common "k8s.io/kube-openapi/pkg/common" |
||||
spec "k8s.io/kube-openapi/pkg/validation/spec" |
||||
) |
||||
|
||||
// NOTE: this must match the golang fully qualified name!
|
||||
const kindKey = "github.com/grafana/grafana/pkg/apis/example/v0alpha1.RuntimeInfo" |
||||
|
||||
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { |
||||
return map[string]common.OpenAPIDefinition{ |
||||
kindKey: schema_pkg_apis_example_v0alpha1_RuntimeInfo(ref), |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_example_v0alpha1_RuntimeInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"kind": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"apiVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"startupTime": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"integer"}, |
||||
Format: "int64", |
||||
}, |
||||
}, |
||||
"buildVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"buildCommit": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"enterpriseBuildCommit": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"buildBranch": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"buildStamp": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"integer"}, |
||||
Format: "int64", |
||||
}, |
||||
}, |
||||
"enterprise": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"boolean"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"packaging": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
} |
||||
@ -0,0 +1,220 @@ |
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by openapi-gen. DO NOT EDIT.
|
||||
|
||||
// This file was autogenerated by openapi-gen. Do not edit it manually!
|
||||
|
||||
package v0alpha1 |
||||
|
||||
import ( |
||||
common "k8s.io/kube-openapi/pkg/common" |
||||
spec "k8s.io/kube-openapi/pkg/validation/spec" |
||||
) |
||||
|
||||
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { |
||||
return map[string]common.OpenAPIDefinition{ |
||||
"github.com/grafana/grafana/pkg/apis/example/v0alpha1.DummyResource": schema_pkg_apis_example_v0alpha1_DummyResource(ref), |
||||
"github.com/grafana/grafana/pkg/apis/example/v0alpha1.DummyResourceList": schema_pkg_apis_example_v0alpha1_DummyResourceList(ref), |
||||
"github.com/grafana/grafana/pkg/apis/example/v0alpha1.DummySubresource": schema_pkg_apis_example_v0alpha1_DummySubresource(ref), |
||||
"github.com/grafana/grafana/pkg/apis/example/v0alpha1.RuntimeInfo": schema_pkg_apis_example_v0alpha1_RuntimeInfo(ref), |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_example_v0alpha1_DummyResource(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"kind": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"apiVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"metadata": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"), |
||||
}, |
||||
}, |
||||
"spec": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_example_v0alpha1_DummyResourceList(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"kind": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"apiVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"metadata": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"), |
||||
}, |
||||
}, |
||||
"items": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"array"}, |
||||
Items: &spec.SchemaOrArray{ |
||||
Schema: &spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Default: map[string]interface{}{}, |
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/example/v0alpha1.DummyResource"), |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
Dependencies: []string{ |
||||
"github.com/grafana/grafana/pkg/apis/example/v0alpha1.DummyResource", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_example_v0alpha1_DummySubresource(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"kind": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"apiVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"info": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "add subresource info here", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func schema_pkg_apis_example_v0alpha1_RuntimeInfo(ref common.ReferenceCallback) common.OpenAPIDefinition { |
||||
return common.OpenAPIDefinition{ |
||||
Schema: spec.Schema{ |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Mirrors the info exposed in \"github.com/grafana/grafana/pkg/setting\"", |
||||
Type: []string{"object"}, |
||||
Properties: map[string]spec.Schema{ |
||||
"kind": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"apiVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"startupTime": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Description: "Unix timestamp when the process started", |
||||
Type: []string{"integer"}, |
||||
Format: "int64", |
||||
}, |
||||
}, |
||||
"buildVersion": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"buildCommit": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"enterpriseBuildCommit": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"buildBranch": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"buildStamp": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"integer"}, |
||||
Format: "int64", |
||||
}, |
||||
}, |
||||
"enterprise": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"boolean"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
"packaging": { |
||||
SchemaProps: spec.SchemaProps{ |
||||
Type: []string{"string"}, |
||||
Format: "", |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
}, |
||||
} |
||||
} |
||||
@ -0,0 +1,118 @@ |
||||
package example |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"slices" |
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion" |
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
||||
"k8s.io/apimachinery/pkg/runtime" |
||||
"k8s.io/apimachinery/pkg/runtime/schema" |
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" |
||||
"k8s.io/apiserver/pkg/registry/rest" |
||||
|
||||
example "github.com/grafana/grafana/pkg/apis/example/v0alpha1" |
||||
grafanarequest "github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request" |
||||
grafanaregistry "github.com/grafana/grafana/pkg/services/grafana-apiserver/registry/generic" |
||||
) |
||||
|
||||
var ( |
||||
_ rest.Storage = (*dummyStorage)(nil) |
||||
_ rest.Scoper = (*dummyStorage)(nil) |
||||
_ rest.SingularNameProvider = (*dummyStorage)(nil) |
||||
_ rest.Getter = (*dummyStorage)(nil) |
||||
_ rest.Lister = (*dummyStorage)(nil) |
||||
) |
||||
|
||||
type dummyStorage struct { |
||||
store *genericregistry.Store |
||||
names []string |
||||
creationTimestamp metav1.Time |
||||
} |
||||
|
||||
func newDummyStorage(gv schema.GroupVersion, scheme *runtime.Scheme, names ...string) *dummyStorage { |
||||
strategy := grafanaregistry.NewStrategy(scheme) |
||||
store := &genericregistry.Store{ |
||||
NewFunc: func() runtime.Object { return &example.DummyResource{} }, // getter not supported
|
||||
NewListFunc: func() runtime.Object { return &example.DummyResourceList{} }, // both list and get return the same thing
|
||||
PredicateFunc: grafanaregistry.Matcher, |
||||
DefaultQualifiedResource: gv.WithResource("dummy").GroupResource(), |
||||
SingularQualifiedResource: gv.WithResource("dummy").GroupResource(), |
||||
CreateStrategy: strategy, |
||||
UpdateStrategy: strategy, |
||||
DeleteStrategy: strategy, |
||||
} |
||||
store.TableConvertor = rest.NewDefaultTableConvertor(store.DefaultQualifiedResource) |
||||
|
||||
return &dummyStorage{ |
||||
store: store, |
||||
names: names, |
||||
creationTimestamp: metav1.Now(), |
||||
} |
||||
} |
||||
|
||||
func (s *dummyStorage) New() runtime.Object { |
||||
return s.store.New() |
||||
} |
||||
|
||||
func (s *dummyStorage) Destroy() {} |
||||
|
||||
func (s *dummyStorage) NamespaceScoped() bool { |
||||
return true |
||||
} |
||||
|
||||
func (s *dummyStorage) GetSingularName() string { |
||||
return "dummy" |
||||
} |
||||
|
||||
func (s *dummyStorage) NewList() runtime.Object { |
||||
return s.store.NewListFunc() |
||||
} |
||||
|
||||
func (s *dummyStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { |
||||
return s.store.TableConvertor.ConvertToTable(ctx, object, tableOptions) |
||||
} |
||||
|
||||
func (s *dummyStorage) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) { |
||||
info, err := grafanarequest.NamespaceInfoFrom(ctx, true) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
idx := slices.Index(s.names, name) |
||||
if idx < 0 { |
||||
return nil, fmt.Errorf("dummy not found") |
||||
} |
||||
|
||||
return &example.DummyResource{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: name, |
||||
Namespace: info.Value, |
||||
CreationTimestamp: s.creationTimestamp, |
||||
ResourceVersion: "1", |
||||
}, |
||||
Spec: fmt.Sprintf("dummy: %s", name), |
||||
}, nil |
||||
} |
||||
|
||||
func (s *dummyStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { |
||||
info, err := grafanarequest.NamespaceInfoFrom(ctx, true) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
res := &example.DummyResourceList{} |
||||
for _, name := range s.names { |
||||
res.Items = append(res.Items, example.DummyResource{ |
||||
ObjectMeta: metav1.ObjectMeta{ |
||||
Name: name, |
||||
Namespace: info.Value, |
||||
CreationTimestamp: s.creationTimestamp, |
||||
ResourceVersion: "1", |
||||
}, |
||||
Spec: fmt.Sprintf("dummy: %s", name), |
||||
}) |
||||
} |
||||
return res, nil |
||||
} |
||||
@ -0,0 +1,54 @@ |
||||
package example |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"net/http" |
||||
|
||||
"k8s.io/apimachinery/pkg/runtime" |
||||
"k8s.io/apiserver/pkg/registry/rest" |
||||
|
||||
example "github.com/grafana/grafana/pkg/apis/example/v0alpha1" |
||||
"github.com/grafana/grafana/pkg/infra/appcontext" |
||||
"github.com/grafana/grafana/pkg/services/grafana-apiserver/endpoints/request" |
||||
) |
||||
|
||||
type dummySubresourceREST struct{} |
||||
|
||||
var _ = rest.Connecter(&dummySubresourceREST{}) |
||||
|
||||
func (r *dummySubresourceREST) New() runtime.Object { |
||||
return &example.DummySubresource{} |
||||
} |
||||
|
||||
func (r *dummySubresourceREST) Destroy() { |
||||
} |
||||
|
||||
func (r *dummySubresourceREST) ConnectMethods() []string { |
||||
return []string{"GET"} |
||||
} |
||||
|
||||
func (r *dummySubresourceREST) NewConnectOptions() (runtime.Object, bool, string) { |
||||
return nil, false, "" // true means you can use the trailing path as a variable
|
||||
} |
||||
|
||||
func (r *dummySubresourceREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) { |
||||
info, err := request.NamespaceInfoFrom(ctx, true) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
user, err := appcontext.User(ctx) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
// This response object format is negotiated by k8s
|
||||
dummy := &example.DummySubresource{ |
||||
Info: fmt.Sprintf("%s/%s", info.Value, user.Login), |
||||
} |
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |
||||
responder.Object(http.StatusOK, dummy) |
||||
}), nil |
||||
} |
||||
@ -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)) |
||||
}) |
||||
} |
||||
@ -1,5 +1,5 @@ |
||||
import config from 'app/core/config'; |
||||
|
||||
export function getThemeColor(dark: string, light: string): string { |
||||
return config.bootData.user.lightTheme ? light : dark; |
||||
return config.bootData?.user?.lightTheme ? light : dark; |
||||
} |
||||
|
||||
Loading…
Reference in new issue