From f41ee615ba7f1f932dc3cbb2060029e063b973cd Mon Sep 17 00:00:00 2001 From: Andres Martinez Gotor Date: Wed, 3 Jul 2024 09:19:54 +0200 Subject: [PATCH] Chore: Add basic admission handlers for registered API services (#89824) --- pkg/promlib/admission_handler.go | 77 ++++++++++++++++++++++++ pkg/registry/apis/datasource/register.go | 2 + pkg/tsdb/graphite/admission_handler.go | 77 ++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 pkg/promlib/admission_handler.go create mode 100644 pkg/tsdb/graphite/admission_handler.go diff --git a/pkg/promlib/admission_handler.go b/pkg/promlib/admission_handler.go new file mode 100644 index 00000000000..4cfa68b1d53 --- /dev/null +++ b/pkg/promlib/admission_handler.go @@ -0,0 +1,77 @@ +package promlib + +import ( + "context" + "fmt" + "net/http" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var ( + _ backend.AdmissionHandler = (*Service)(nil) +) + +// ValidateAdmission implements backend.AdmissionHandler. +func (s *Service) ValidateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.ValidationResponse, error) { + rsp, err := s.MutateAdmission(ctx, req) + if rsp != nil { + return &backend.ValidationResponse{ + Allowed: rsp.Allowed, + Result: rsp.Result, + Warnings: rsp.Warnings, + }, err + } + return nil, err +} + +// MutateAdmission implements backend.AdmissionHandler. +func (s *Service) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) { + expected := (&backend.DataSourceInstanceSettings{}).GVK() + if req.Kind.Kind != expected.Kind && req.Kind.Group != expected.Group { + return getBadRequest("expected DataSourceInstanceSettings protobuf payload"), nil + } + + // Convert the payload from protobuf to an SDK struct + settings, err := backend.DataSourceInstanceSettingsFromProto(req.ObjectBytes, "") + if err != nil { + return nil, err + } + if settings == nil { + return getBadRequest("missing datasource settings"), nil + } + + switch settings.APIVersion { + case "", "v0alpha1": + // OK! + default: + return getBadRequest(fmt.Sprintf("expected apiVersion: v0alpha1, found: %s", settings.APIVersion)), nil + } + if settings.URL != "" { + return getBadRequest("unsupported URL value"), nil + } + + pb, err := backend.DataSourceInstanceSettingsToProtoBytes(settings) + return &backend.MutationResponse{ + Allowed: true, + ObjectBytes: pb, + }, err +} + +// ConvertObject implements backend.AdmissionHandler. +func (s *Service) ConvertObject(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) { + return nil, fmt.Errorf("not implemented") +} + +func getBadRequest(msg string) *backend.MutationResponse { + return &backend.MutationResponse{ + Allowed: false, + Result: &backend.StatusResult{ + Status: "Failure", + Message: msg, + Reason: string(metav1.StatusReasonBadRequest), + Code: http.StatusBadRequest, + }, + } +} diff --git a/pkg/registry/apis/datasource/register.go b/pkg/registry/apis/datasource/register.go index 9ee6741720c..a938631a2ee 100644 --- a/pkg/registry/apis/datasource/register.go +++ b/pkg/registry/apis/datasource/register.go @@ -67,6 +67,8 @@ func RegisterAPIService( var err error var builder *DataSourceAPIBuilder all := pluginStore.Plugins(context.Background(), plugins.TypeDataSource) + // ATTENTION: Adding a datasource here requires the plugin to implement + // an AdmissionHandler to validate the datasource settings. ids := []string{ "grafana-testdata-datasource", "prometheus", diff --git a/pkg/tsdb/graphite/admission_handler.go b/pkg/tsdb/graphite/admission_handler.go new file mode 100644 index 00000000000..829324d844b --- /dev/null +++ b/pkg/tsdb/graphite/admission_handler.go @@ -0,0 +1,77 @@ +package graphite + +import ( + "context" + "fmt" + "net/http" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var ( + _ backend.AdmissionHandler = (*Service)(nil) +) + +// ValidateAdmission implements backend.AdmissionHandler. +func (s *Service) ValidateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.ValidationResponse, error) { + rsp, err := s.MutateAdmission(ctx, req) + if rsp != nil { + return &backend.ValidationResponse{ + Allowed: rsp.Allowed, + Result: rsp.Result, + Warnings: rsp.Warnings, + }, err + } + return nil, err +} + +// MutateAdmission implements backend.AdmissionHandler. +func (s *Service) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) { + expected := (&backend.DataSourceInstanceSettings{}).GVK() + if req.Kind.Kind != expected.Kind && req.Kind.Group != expected.Group { + return getBadRequest("expected DataSourceInstanceSettings protobuf payload"), nil + } + + // Convert the payload from protobuf to an SDK struct + settings, err := backend.DataSourceInstanceSettingsFromProto(req.ObjectBytes, "") + if err != nil { + return nil, err + } + if settings == nil { + return getBadRequest("missing datasource settings"), nil + } + + switch settings.APIVersion { + case "", "v0alpha1": + // OK! + default: + return getBadRequest(fmt.Sprintf("expected apiVersion: v0alpha1, found: %s", settings.APIVersion)), nil + } + if settings.URL != "" { + return getBadRequest("unsupported URL value"), nil + } + + pb, err := backend.DataSourceInstanceSettingsToProtoBytes(settings) + return &backend.MutationResponse{ + Allowed: true, + ObjectBytes: pb, + }, err +} + +// ConvertObject implements backend.AdmissionHandler. +func (s *Service) ConvertObject(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) { + return nil, fmt.Errorf("not implemented") +} + +func getBadRequest(msg string) *backend.MutationResponse { + return &backend.MutationResponse{ + Allowed: false, + Result: &backend.StatusResult{ + Status: "Failure", + Message: msg, + Reason: string(metav1.StatusReasonBadRequest), + Code: http.StatusBadRequest, + }, + } +}