diff --git a/pkg/apis/example/v0alpha1/doc.go b/pkg/apis/example/v0alpha1/doc.go deleted file mode 100644 index 96849073b83..00000000000 --- a/pkg/apis/example/v0alpha1/doc.go +++ /dev/null @@ -1,11 +0,0 @@ -// +k8s:deepcopy-gen=package -// +k8s:openapi-gen=true -// +k8s:defaulter-gen=TypeMeta -// +groupName=example.grafana.app - -// The testing api is a dependency free service that we can use to experiment with -// api aggregation across multiple deployment models. Specifically: -// - standalone: running as part of the standard grafana build -// - aggregated: running as the target - -package v0alpha1 // import "github.com/grafana/grafana/pkg/apis/example/v0alpha1" diff --git a/pkg/apis/example/v0alpha1/register.go b/pkg/apis/example/v0alpha1/register.go deleted file mode 100644 index e254cad1d34..00000000000 --- a/pkg/apis/example/v0alpha1/register.go +++ /dev/null @@ -1,30 +0,0 @@ -package v0alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - - common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" -) - -const ( - GROUP = "example.grafana.app" - VERSION = "v0alpha1" - APIVERSION = GROUP + "/" + VERSION -) - -var RuntimeResourceInfo = common.NewResourceInfo(GROUP, VERSION, - "runtime", "runtime", "RuntimeInfo", - func() runtime.Object { return &RuntimeInfo{} }, - func() runtime.Object { return &RuntimeInfo{} }, -) -var DummyResourceInfo = common.NewResourceInfo(GROUP, VERSION, - "dummy", "dummy", "DummyResource", - func() runtime.Object { return &DummyResource{} }, - func() runtime.Object { return &DummyResourceList{} }, -) - -var ( - // SchemeGroupVersion is group version used to register these objects - SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION} -) diff --git a/pkg/apis/example/v0alpha1/types.go b/pkg/apis/example/v0alpha1/types.go deleted file mode 100644 index a8fef3ffe6f..00000000000 --- a/pkg/apis/example/v0alpha1/types.go +++ /dev/null @@ -1,48 +0,0 @@ -package v0alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" -) - -// Mirrors the info exposed in "github.com/grafana/grafana/pkg/setting" -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type RuntimeInfo struct { - metav1.TypeMeta `json:",inline"` - - // Unix timestamp when the process started - StartupTime int64 `json:"startupTime,omitempty"` - - BuildVersion string `json:"buildVersion,omitempty"` - BuildCommit string `json:"buildCommit,omitempty"` - EnterpriseBuildCommit string `json:"enterpriseBuildCommit,omitempty"` - BuildBranch string `json:"buildBranch,omitempty"` - BuildStamp int64 `json:"buildStamp,omitempty"` - IsEnterprise bool `json:"enterprise,omitempty"` - Packaging string `json:"packaging,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type DummyResource struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec common.Unstructured `json:"spec,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type DummyResourceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - - Items []DummyResource `json:"items,omitempty"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -type DummySubresource struct { - metav1.TypeMeta `json:",inline"` - - // add subresource info here - Info string `json:"info,omitempty"` -} diff --git a/pkg/apis/example/v0alpha1/zz_generated.deepcopy.go b/pkg/apis/example/v0alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 063ad395718..00000000000 --- a/pkg/apis/example/v0alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,122 +0,0 @@ -//go:build !ignore_autogenerated -// +build !ignore_autogenerated - -// SPDX-License-Identifier: AGPL-3.0-only - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v0alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DummyResource) DeepCopyInto(out *DummyResource) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DummyResource. -func (in *DummyResource) DeepCopy() *DummyResource { - if in == nil { - return nil - } - out := new(DummyResource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DummyResource) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DummyResourceList) DeepCopyInto(out *DummyResourceList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]DummyResource, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DummyResourceList. -func (in *DummyResourceList) DeepCopy() *DummyResourceList { - if in == nil { - return nil - } - out := new(DummyResourceList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DummyResourceList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DummySubresource) DeepCopyInto(out *DummySubresource) { - *out = *in - out.TypeMeta = in.TypeMeta - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DummySubresource. -func (in *DummySubresource) DeepCopy() *DummySubresource { - if in == nil { - return nil - } - out := new(DummySubresource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *DummySubresource) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RuntimeInfo) DeepCopyInto(out *RuntimeInfo) { - *out = *in - out.TypeMeta = in.TypeMeta - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuntimeInfo. -func (in *RuntimeInfo) DeepCopy() *RuntimeInfo { - if in == nil { - return nil - } - out := new(RuntimeInfo) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RuntimeInfo) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} diff --git a/pkg/apis/example/v0alpha1/zz_generated.defaults.go b/pkg/apis/example/v0alpha1/zz_generated.defaults.go deleted file mode 100644 index 238fc2f4edc..00000000000 --- a/pkg/apis/example/v0alpha1/zz_generated.defaults.go +++ /dev/null @@ -1,19 +0,0 @@ -//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 -} diff --git a/pkg/apis/example/v0alpha1/zz_generated.openapi.go b/pkg/apis/example/v0alpha1/zz_generated.openapi.go deleted file mode 100644 index 8c441c55064..00000000000 --- a/pkg/apis/example/v0alpha1/zz_generated.openapi.go +++ /dev/null @@ -1,219 +0,0 @@ -//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{ - Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"), - }, - }, - }, - }, - }, - Dependencies: []string{ - "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured", "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: "", - }, - }, - }, - }, - }, - } -} diff --git a/pkg/apis/example/v0alpha1/zz_generated.openapi_violation_exceptions.list b/pkg/apis/example/v0alpha1/zz_generated.openapi_violation_exceptions.list deleted file mode 100644 index 75c16b7b44e..00000000000 --- a/pkg/apis/example/v0alpha1/zz_generated.openapi_violation_exceptions.list +++ /dev/null @@ -1 +0,0 @@ -API rule violation: names_match,github.com/grafana/grafana/pkg/apis/example/v0alpha1,RuntimeInfo,IsEnterprise diff --git a/pkg/registry/apis/apis.go b/pkg/registry/apis/apis.go index 3ccfae82528..a44271582af 100644 --- a/pkg/registry/apis/apis.go +++ b/pkg/registry/apis/apis.go @@ -8,7 +8,6 @@ import ( "github.com/grafana/grafana/pkg/registry/apis/dashboard" "github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot" "github.com/grafana/grafana/pkg/registry/apis/datasource" - "github.com/grafana/grafana/pkg/registry/apis/example" "github.com/grafana/grafana/pkg/registry/apis/featuretoggle" "github.com/grafana/grafana/pkg/registry/apis/folders" "github.com/grafana/grafana/pkg/registry/apis/peakq" @@ -28,7 +27,6 @@ type Service struct{} func ProvideRegistryServiceSink( _ *dashboard.DashboardsAPIBuilder, _ *playlist.PlaylistAPIBuilder, - _ *example.TestingAPIBuilder, _ *dashboardsnapshot.SnapshotsAPIBuilder, _ *featuretoggle.FeatureFlagAPIBuilder, _ *datasource.DataSourceAPIBuilder, diff --git a/pkg/registry/apis/example/dummy_storage.go b/pkg/registry/apis/example/dummy_storage.go deleted file mode 100644 index 574a87dd527..00000000000 --- a/pkg/registry/apis/example/dummy_storage.go +++ /dev/null @@ -1,128 +0,0 @@ -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" - - common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" - example "github.com/grafana/grafana/pkg/apis/example/v0alpha1" - grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" - grafanarequest "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request" -) - -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 { - var resourceInfo = example.DummyResourceInfo - strategy := grafanaregistry.NewStrategy(scheme) - store := &genericregistry.Store{ - NewFunc: resourceInfo.NewFunc, - NewListFunc: resourceInfo.NewListFunc, - PredicateFunc: grafanaregistry.Matcher, - DefaultQualifiedResource: resourceInfo.GroupResource(), - SingularQualifiedResource: resourceInfo.SingularGroupResource(), - TableConvertor: rest.NewDefaultTableConvertor(resourceInfo.GroupResource()), - CreateStrategy: strategy, - UpdateStrategy: strategy, - DeleteStrategy: strategy, - } - - 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 example.DummyResourceInfo.GetSingularName() -} - -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: common.Unstructured{ - Object: map[string]any{ - "Dummy": 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: common.Unstructured{ - Object: map[string]any{ - "Dummy": name, - }, - }, - }) - } - return res, nil -} diff --git a/pkg/registry/apis/example/register.go b/pkg/registry/apis/example/register.go deleted file mode 100644 index 6dc5644bffa..00000000000 --- a/pkg/registry/apis/example/register.go +++ /dev/null @@ -1,226 +0,0 @@ -package example - -import ( - "context" - "fmt" - "net/http" - - "github.com/prometheus/client_golang/prometheus" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/apiserver/pkg/authorization/authorizer" - "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" - "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - "k8s.io/apiserver/pkg/registry/rest" - genericapiserver "k8s.io/apiserver/pkg/server" - common "k8s.io/kube-openapi/pkg/common" - "k8s.io/kube-openapi/pkg/spec3" - "k8s.io/kube-openapi/pkg/validation/spec" - - "github.com/grafana/grafana/pkg/apimachinery/identity" - example "github.com/grafana/grafana/pkg/apis/example/v0alpha1" - "github.com/grafana/grafana/pkg/apiserver/builder" - grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" - "github.com/grafana/grafana/pkg/services/featuremgmt" -) - -var _ builder.APIGroupBuilder = (*TestingAPIBuilder)(nil) - -// This is used just so wire has something unique to return -type TestingAPIBuilder struct { - codecs serializer.CodecFactory - gv schema.GroupVersion -} - -func NewTestingAPIBuilder() *TestingAPIBuilder { - return &TestingAPIBuilder{ - gv: schema.GroupVersion{Group: example.GROUP, Version: example.VERSION}, - } -} - -func RegisterAPIService(features featuremgmt.FeatureToggles, apiregistration builder.APIRegistrar, reg prometheus.Registerer) *TestingAPIBuilder { - if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) { - return nil // skip registration unless opting into experimental apis - } - builder := NewTestingAPIBuilder() - apiregistration.RegisterAPI(builder) - return builder -} - -func (b *TestingAPIBuilder) GetGroupVersion() schema.GroupVersion { - return b.gv -} - -func (b *TestingAPIBuilder) GetDesiredDualWriterMode(dualWrite bool, modeMap map[string]grafanarest.DualWriterMode) grafanarest.DualWriterMode { - // Add required configuration support in order to enable other modes. For an example, see pkg/registry/apis/playlist/register.go - return grafanarest.Mode0 -} - -func addKnownTypes(scheme *runtime.Scheme, gv schema.GroupVersion) { - scheme.AddKnownTypes(gv, - &example.RuntimeInfo{}, - &example.DummyResource{}, - &example.DummyResourceList{}, - &example.DummySubresource{}, - ) -} - -func (b *TestingAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { - addKnownTypes(scheme, b.gv) - - // 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: b.gv.Group, - Version: runtime.APIVersionInternal, - }) - - // If multiple versions exist, then register conversions from zz_generated.conversion.go - // if err := playlist.RegisterConversions(scheme); err != nil { - // return err - // } - metav1.AddToGroupVersion(scheme, b.gv) - return scheme.SetVersionPriority(b.gv) -} - -func (b *TestingAPIBuilder) GetAPIGroupInfo( - scheme *runtime.Scheme, - codecs serializer.CodecFactory, // pointer? - _ generic.RESTOptionsGetter, - _ grafanarest.DualWriterMode, - _ prometheus.Registerer, -) (*genericapiserver.APIGroupInfo, error) { - b.codecs = codecs - apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(b.gv.Group, scheme, metav1.ParameterCodec, codecs) - - storage := map[string]rest.Storage{} - storage[example.RuntimeResourceInfo.StoragePath()] = newDeploymentInfoStorage(b.gv, scheme) - storage[example.DummyResourceInfo.StoragePath()] = newDummyStorage(b.gv, scheme, "test1", "test2", "test3") - storage[example.DummyResourceInfo.StoragePath("sub")] = &dummySubresourceREST{} - apiGroupInfo.VersionedResourcesStorageMap[b.gv.Version] = storage - return &apiGroupInfo, nil -} - -func (b *TestingAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions { - return example.GetOpenAPIDefinitions -} - -// Register additional routes with the server -func (b *TestingAPIBuilder) GetAPIRoutes() *builder.APIRoutes { - return &builder.APIRoutes{ - Root: []builder.APIRouteHandler{ - { - Path: "aaa", - Spec: &spec3.PathProps{ - Summary: "an example at the root level", - Description: "longer description here?", - Get: &spec3.Operation{ - OperationProps: spec3.OperationProps{ - Parameters: []*spec3.Parameter{ - {ParameterProps: spec3.ParameterProps{ - Name: "a", - }}, - }, - Responses: &spec3.Responses{ - ResponsesProps: spec3.ResponsesProps{ - StatusCodeResponses: map[int]*spec3.Response{ - 200: { - ResponseProps: spec3.ResponseProps{ - Description: "OK", - Content: map[string]*spec3.MediaType{ - "text/plain": { - MediaTypeProps: spec3.MediaTypeProps{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - Handler: func(w http.ResponseWriter, r *http.Request) { - _, _ = w.Write([]byte("Root level handler (aaa)")) - }, - }, - { - Path: "bbb", - Spec: &spec3.PathProps{ - Summary: "an example at the root level", - Description: "longer description here?", - Get: &spec3.Operation{ - OperationProps: spec3.OperationProps{ - Parameters: []*spec3.Parameter{ - {ParameterProps: spec3.ParameterProps{ - Name: "b", - }}, - }, - }, - }, - }, - Handler: func(w http.ResponseWriter, r *http.Request) { - _, _ = w.Write([]byte("Root level handler (bbb)")) - }, - }, - }, - Namespace: []builder.APIRouteHandler{ - { - Path: "ccc", - Spec: &spec3.PathProps{ - Summary: "an example at the root level", - Description: "longer description here?", - Get: &spec3.Operation{ - OperationProps: spec3.OperationProps{ - Parameters: []*spec3.Parameter{ - {ParameterProps: spec3.ParameterProps{ - Name: "a", - }}, - }, - }, - }, - }, - Handler: func(w http.ResponseWriter, r *http.Request) { - info, ok := request.RequestInfoFrom(r.Context()) - if !ok { - responsewriters.ErrorNegotiated( - apierrors.NewInternalError(fmt.Errorf("no RequestInfo found in the context")), - b.codecs, schema.GroupVersion{}, w, r, - ) - return - } - - _, _ = w.Write([]byte("Custom namespace route ccc: " + info.Namespace)) - }, - }, - }, - } -} - -func (b *TestingAPIBuilder) GetAuthorizer() authorizer.Authorizer { - return authorizer.AuthorizerFunc( - func(ctx context.Context, attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) { - if !attr.IsResourceRequest() { - return authorizer.DecisionNoOpinion, "", nil - } - - // require a user - _, err = identity.GetRequester(ctx) - if err != nil { - return authorizer.DecisionDeny, "valid user is required", err - } - - return authorizer.DecisionNoOpinion, "", err // fallback to org/role logic - }) -} diff --git a/pkg/registry/apis/example/storage.go b/pkg/registry/apis/example/storage.go deleted file mode 100644 index 4e2c1858837..00000000000 --- a/pkg/registry/apis/example/storage.go +++ /dev/null @@ -1,86 +0,0 @@ -package example - -import ( - "context" - "time" - - "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" - grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" - "github.com/grafana/grafana/pkg/setting" -) - -var ( - _ rest.Storage = (*staticStorage)(nil) - _ rest.Scoper = (*staticStorage)(nil) - _ rest.SingularNameProvider = (*staticStorage)(nil) - _ rest.Lister = (*staticStorage)(nil) -) - -type staticStorage struct { - Store *genericregistry.Store - info example.RuntimeInfo -} - -func newDeploymentInfoStorage(gv schema.GroupVersion, scheme *runtime.Scheme) *staticStorage { - var resourceInfo = example.RuntimeResourceInfo - strategy := grafanaregistry.NewStrategy(scheme) - store := &genericregistry.Store{ - NewFunc: resourceInfo.NewFunc, - NewListFunc: resourceInfo.NewListFunc, - PredicateFunc: grafanaregistry.Matcher, - DefaultQualifiedResource: resourceInfo.GroupResource(), - SingularQualifiedResource: resourceInfo.SingularGroupResource(), - TableConvertor: rest.NewDefaultTableConvertor(resourceInfo.GroupResource()), - CreateStrategy: strategy, - UpdateStrategy: strategy, - DeleteStrategy: strategy, - } - - return &staticStorage{ - Store: store, - info: example.RuntimeInfo{ - TypeMeta: example.RuntimeResourceInfo.TypeMeta(), - BuildVersion: setting.BuildVersion, - BuildCommit: setting.BuildCommit, - BuildBranch: setting.BuildBranch, - EnterpriseBuildCommit: setting.EnterpriseBuildCommit, - BuildStamp: setting.BuildStamp, - IsEnterprise: setting.IsEnterprise, - Packaging: setting.Packaging, - StartupTime: time.Now().UnixMilli(), - }, - } -} - -func (s *staticStorage) New() runtime.Object { - return s.Store.New() -} - -func (s *staticStorage) Destroy() {} - -func (s *staticStorage) NamespaceScoped() bool { - return false -} - -func (s *staticStorage) GetSingularName() string { - return example.RuntimeResourceInfo.GetSingularName() -} - -func (s *staticStorage) NewList() runtime.Object { - return s.Store.NewListFunc() -} - -func (s *staticStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) { - return s.Store.TableConvertor.ConvertToTable(ctx, object, tableOptions) -} - -func (s *staticStorage) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) { - return &s.info, nil -} diff --git a/pkg/registry/apis/example/subresource.go b/pkg/registry/apis/example/subresource.go deleted file mode 100644 index 3fa5807a9e9..00000000000 --- a/pkg/registry/apis/example/subresource.go +++ /dev/null @@ -1,54 +0,0 @@ -package example - -import ( - "context" - "fmt" - "net/http" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/registry/rest" - - "github.com/grafana/grafana/pkg/apimachinery/identity" - example "github.com/grafana/grafana/pkg/apis/example/v0alpha1" - "github.com/grafana/grafana/pkg/services/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 := identity.GetRequester(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.GetLogin()), - } - - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - responder.Object(http.StatusOK, dummy) - }), nil -} diff --git a/pkg/registry/apis/wireset.go b/pkg/registry/apis/wireset.go index d9782c44697..2ce96e4af0d 100644 --- a/pkg/registry/apis/wireset.go +++ b/pkg/registry/apis/wireset.go @@ -7,7 +7,6 @@ import ( "github.com/grafana/grafana/pkg/registry/apis/dashboard" "github.com/grafana/grafana/pkg/registry/apis/dashboardsnapshot" "github.com/grafana/grafana/pkg/registry/apis/datasource" - "github.com/grafana/grafana/pkg/registry/apis/example" "github.com/grafana/grafana/pkg/registry/apis/featuretoggle" "github.com/grafana/grafana/pkg/registry/apis/folders" "github.com/grafana/grafana/pkg/registry/apis/peakq" @@ -29,7 +28,6 @@ var WireSet = wire.NewSet( // Each must be added here *and* in the ServiceSink above playlist.RegisterAPIService, dashboard.RegisterAPIService, - example.RegisterAPIService, dashboardsnapshot.RegisterAPIService, featuretoggle.RegisterAPIService, datasource.RegisterAPIService, diff --git a/pkg/services/apiserver/standalone/factory.go b/pkg/services/apiserver/standalone/factory.go index 34ae2316813..a2c413d69a9 100644 --- a/pkg/services/apiserver/standalone/factory.go +++ b/pkg/services/apiserver/standalone/factory.go @@ -15,7 +15,6 @@ import ( "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/registry/apis/datasource" - "github.com/grafana/grafana/pkg/registry/apis/example" "github.com/grafana/grafana/pkg/registry/apis/featuretoggle" "github.com/grafana/grafana/pkg/registry/apis/query" "github.com/grafana/grafana/pkg/registry/apis/query/client" @@ -75,9 +74,6 @@ func (p *DummyAPIFactory) MakeAPIServer(_ context.Context, tracer tracing.Tracer } switch gv.Group { - case "example.grafana.app": - return example.NewTestingAPIBuilder(), nil - // Only works with testdata case "query.grafana.app": return query.NewQueryAPIBuilder( diff --git a/pkg/tests/apis/example/example_test.go b/pkg/tests/apis/example/example_test.go deleted file mode 100644 index c702402837f..00000000000 --- a/pkg/tests/apis/example/example_test.go +++ /dev/null @@ -1,176 +0,0 @@ -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/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 TestIntegrationExampleApp(t *testing.T) { - if testing.Short() { - t.Skip("skipping integration test") - } - helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{ - AppModeProduction: false, // required for experimental APIs - DisableAnonymous: true, - EnableFeatureToggles: []string{ - 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.ResourceClient(t, 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.ResourceClient(t, 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) - - v, ok, err := unstructured.NestedString(rsp.Object, "spec", "Dummy") - require.NoError(t, err) - require.True(t, ok) - - require.Equal(t, "test2", v) - 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)) - }) -}