Provisioning: Setup server and storage (#99757)

feat: setup server and storage

This simply sets up the API server and its storage for `provisioning.grafana.app`.
Features will be added eventually.
pull/99793/head
Mariell Hoversholm 4 months ago committed by GitHub
parent 3c0383f0d5
commit 0c120db84d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      pkg/apiserver/registry/generic/storage.go
  2. 5
      pkg/apiserver/registry/generic/store.go
  3. 160
      pkg/registry/apis/provisioning/register.go
  4. 2
      pkg/registry/apis/wireset.go

@ -29,3 +29,9 @@ func NewRegistryStore(scheme *runtime.Scheme, resourceInfo utils.ResourceInfo, o
}
return store, nil
}
func NewRegistryStatusStore(scheme *runtime.Scheme, specStore *registry.Store) *StatusREST {
gv := specStore.New().GetObjectKind().GroupVersionKind().GroupVersion()
strategy := NewStatusStrategy(scheme, gv)
return NewStatusREST(specStore, strategy)
}

@ -26,7 +26,10 @@ type StatusREST struct {
store *genericregistry.Store
}
var _ = rest.Patcher(&StatusREST{})
var (
_ rest.Patcher = (*StatusREST)(nil)
_ rest.Storage = (*StatusREST)(nil)
)
// New creates a new DataPlaneService object.
func (r *StatusREST) New() runtime.Object {

@ -0,0 +1,160 @@
package provisioning
import (
"context"
"fmt"
provisioning "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/featuremgmt"
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/util/validation/field"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/spec3"
)
var (
_ builder.APIGroupBuilder = (*APIBuilder)(nil)
)
type APIBuilder struct{}
// NewAPIBuilder creates an API builder.
// It avoids anything that is core to Grafana, such that it can be used in a multi-tenant service down the line.
// This means there are no hidden dependencies, and no use of e.g. *settings.Cfg.
func NewAPIBuilder() *APIBuilder {
return &APIBuilder{}
}
// RegisterAPIService returns an API builder, from [NewAPIBuilder]. It is called by Wire.
// This function happily uses services core to Grafana, and does not need to be multi-tenancy-compatible.
func RegisterAPIService(
features featuremgmt.FeatureToggles,
apiregistration builder.APIRegistrar,
) (*APIBuilder, error) {
if !features.IsEnabledGlobally(featuremgmt.FlagProvisioning) &&
!features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
return nil, nil // skip registration unless opting into experimental apis OR the feature specifically
}
builder := NewAPIBuilder()
apiregistration.RegisterAPI(builder)
return builder, nil
}
func (b *APIBuilder) GetAuthorizer() authorizer.Authorizer {
return authorizer.AuthorizerFunc(
func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
// TODO: Implement a webhook authoriser somehow.
// fallback to the standard authorizer
return authorizer.DecisionNoOpinion, "", nil
})
}
func (b *APIBuilder) GetGroupVersion() schema.GroupVersion {
return provisioning.SchemeGroupVersion
}
func (b *APIBuilder) InstallSchema(scheme *runtime.Scheme) error {
err := provisioning.AddToScheme(scheme)
if err != nil {
return err
}
// This is required for --server-side apply
err = provisioning.AddKnownTypes(provisioning.InternalGroupVersion, scheme)
if err != nil {
return err
}
metav1.AddToGroupVersion(scheme, provisioning.SchemeGroupVersion)
// Only 1 version (for now?)
return scheme.SetVersionPriority(provisioning.SchemeGroupVersion)
}
func (b *APIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
repositoryStorage, err := grafanaregistry.NewRegistryStore(opts.Scheme, provisioning.RepositoryResourceInfo, opts.OptsGetter)
if err != nil {
return fmt.Errorf("failed to create repository storage: %w", err)
}
repositoryStatusStorage := grafanaregistry.NewRegistryStatusStore(opts.Scheme, repositoryStorage)
storage := map[string]rest.Storage{}
storage[provisioning.RepositoryResourceInfo.StoragePath()] = repositoryStorage
storage[provisioning.RepositoryResourceInfo.StoragePath("status")] = repositoryStatusStorage
apiGroupInfo.VersionedResourcesStorageMap[provisioning.VERSION] = storage
return nil
}
func (b *APIBuilder) Mutate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
obj := a.GetObject()
if obj == nil || a.GetOperation() == admission.Connect {
return nil // This is normal for sub-resource
}
r, ok := obj.(*provisioning.Repository)
if !ok {
return fmt.Errorf("expected repository configuration")
}
// TODO: Do something based on the resource we got.
_ = r
return nil
}
func (b *APIBuilder) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) {
obj := a.GetObject()
if obj == nil || a.GetOperation() == admission.Connect {
return nil // This is normal for sub-resource
}
var list field.ErrorList
// TODO: Fill the list with validation errors.
if len(list) > 0 {
return apierrors.NewInvalid(
provisioning.RepositoryResourceInfo.GroupVersionKind().GroupKind(),
a.GetName(), list)
}
return nil
}
func (b *APIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
return provisioning.GetOpenAPIDefinitions
}
func (b *APIBuilder) GetPostStartHooks() (map[string]genericapiserver.PostStartHookFunc, error) {
postStartHooks := map[string]genericapiserver.PostStartHookFunc{
"grafana-provisioning": func(postStartHookCtx genericapiserver.PostStartHookContext) error {
// TODO: Set up a shared informer for a controller and a watcher with workers.
return nil
},
}
return postStartHooks, nil
}
func (b *APIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
oas.Info.Description = "Provisioning"
root := "/apis/" + b.GetGroupVersion().String() + "/"
// The root API discovery list
sub := oas.Paths.Paths[root]
if sub != nil && sub.Get != nil {
sub.Get.Tags = []string{"API Discovery"} // sorts first in the list
}
return oas, nil
}

@ -14,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/folders"
"github.com/grafana/grafana/pkg/registry/apis/iam"
"github.com/grafana/grafana/pkg/registry/apis/peakq"
"github.com/grafana/grafana/pkg/registry/apis/provisioning"
"github.com/grafana/grafana/pkg/registry/apis/query"
"github.com/grafana/grafana/pkg/registry/apis/scope"
"github.com/grafana/grafana/pkg/registry/apis/service"
@ -40,6 +41,7 @@ var WireSet = wire.NewSet(
folders.RegisterAPIService,
iam.RegisterAPIService,
peakq.RegisterAPIService,
provisioning.RegisterAPIService,
service.RegisterAPIService,
query.RegisterAPIService,
scope.RegisterAPIService,

Loading…
Cancel
Save