From 94941f40cff93df3aeb9d87044becac723fc3013 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 24 May 2024 12:58:01 +0300 Subject: [PATCH] API: Add new AdmissionControl service (experimental for now) (#983) Co-authored-by: Andres Martinez Gotor Co-authored-by: Marcus Efraimsson --- backend/admission.go | 136 ++++ backend/admission_adapter.go | 48 ++ backend/app/manage.go | 6 +- backend/common.go | 16 + backend/convert_from_protobuf.go | 71 ++ backend/convert_from_protobuf_test.go | 128 ++++ backend/convert_to_protobuf.go | 110 +++ backend/convert_to_protobuf_test.go | 56 +- backend/datasource/manage.go | 6 +- backend/datasource/serve.go | 4 + backend/diagnostics.go | 2 +- backend/grpcplugin/grpc_admission.go | 74 ++ backend/grpcplugin/serve.go | 7 + backend/serve.go | 19 +- genproto/pluginv2/backend.pb.go | 1024 ++++++++++++++++++++++--- genproto/pluginv2/backend_grpc.pb.go | 168 ++++ proto/backend.proto | 143 +++- 17 files changed, 1888 insertions(+), 130 deletions(-) create mode 100644 backend/admission.go create mode 100644 backend/admission_adapter.go create mode 100644 backend/grpcplugin/grpc_admission.go diff --git a/backend/admission.go b/backend/admission.go new file mode 100644 index 000000000..e9f81e0c1 --- /dev/null +++ b/backend/admission.go @@ -0,0 +1,136 @@ +package backend + +import ( + "context" + + "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" +) + +// AdmissionHandler is an EXPERIMENTAL service that allows checking objects before they are saved +// This is modeled after the kubernetes model for admission controllers +// Since grafana 11.1, this feature is under active development and will continue to evolve in 2024 +// This may also be replaced with a more native kubernetes solution that does not work with existing tooling +type AdmissionHandler interface { + // ValidateAdmission is a simple yes|no check if an object can be saved + ValidateAdmission(context.Context, *AdmissionRequest) (*ValidationResponse, error) + // MutateAdmission converts the input into an object that can be saved, or rejects the request + MutateAdmission(context.Context, *AdmissionRequest) (*MutationResponse, error) + // ConvertObject is called to covert objects between different versions + ConvertObject(context.Context, *ConversionRequest) (*ConversionResponse, error) +} + +type ValidateAdmissionFunc func(context.Context, *AdmissionRequest) (*ValidationResponse, error) +type MutateAdmissionFunc func(context.Context, *AdmissionRequest) (*MutationResponse, error) +type ConvertObjectFunc func(context.Context, *ConversionRequest) (*ConversionResponse, error) + +// Operation is the type of resource operation being checked for admission control +// https://github.com/kubernetes/kubernetes/blob/v1.30.0/pkg/apis/admission/types.go#L158 +type AdmissionRequestOperation int32 + +const ( + AdmissionRequestCreate AdmissionRequestOperation = 0 + AdmissionRequestUpdate AdmissionRequestOperation = 1 + AdmissionRequestDelete AdmissionRequestOperation = 2 +) + +// String textual representation of the operation. +func (o AdmissionRequestOperation) String() string { + return pluginv2.AdmissionRequest_Operation(o).String() +} + +// Identify the Object properties +type GroupVersionKind struct { + Group string `json:"group,omitempty"` + Version string `json:"version,omitempty"` + Kind string `json:"kind,omitempty"` +} + +type AdmissionRequest struct { + // NOTE: this may not include populated instance settings depending on the request + PluginContext PluginContext `json:"pluginContext,omitempty"` + // The requested operation + Operation AdmissionRequestOperation `json:"operation,omitempty"` + // The object kind + Kind GroupVersionKind `json:"kind,omitempty"` + // Object is the object in the request. This includes the full metadata envelope. + ObjectBytes []byte `json:"object_bytes,omitempty"` + // OldObject is the object as it currently exists in storage. This includes the full metadata envelope. + OldObjectBytes []byte `json:"old_object_bytes,omitempty"` +} + +// ConversionRequest supports converting an object from on version to another +type ConversionRequest struct { + // NOTE: this may not include app or datasource instance settings depending on the request + PluginContext PluginContext `json:"pluginContext,omitempty"` + // The object kind + Kind GroupVersionKind `json:"kind,omitempty"` + // Object is the object in the request. This includes the full metadata envelope. + ObjectBytes []byte `json:"object_bytes,omitempty"` + // Target converted version + TargetVersion string `json:"target_version,omitempty"` +} + +// Basic request to say if the validation was successful or not +type ValidationResponse struct { + // Allowed indicates whether or not the admission request was permitted. + Allowed bool `json:"allowed,omitempty"` + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + Result *StatusResult `json:"result,omitempty"` + // warnings is a list of warning messages to return to the requesting API client. + // Warning messages describe a problem the client making the API request should correct or be aware of. + // Limit warnings to 120 characters if possible. + // Warnings over 256 characters and large numbers of warnings may be truncated. + // +optional + Warnings []string `json:"warnings,omitempty"` +} + +type MutationResponse struct { + // Allowed indicates whether or not the admission request was permitted. + Allowed bool `json:"allowed,omitempty"` + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + Result *StatusResult `json:"result,omitempty"` + // warnings is a list of warning messages to return to the requesting API client. + // Warning messages describe a problem the client making the API request should correct or be aware of. + // Limit warnings to 120 characters if possible. + // Warnings over 256 characters and large numbers of warnings may be truncated. + // +optional + Warnings []string `json:"warnings,omitempty"` + // Mutated object bytes (when requested) + // +optional + ObjectBytes []byte `json:"object_bytes,omitempty"` +} + +type ConversionResponse struct { + // Allowed indicates whether or not the admission request was permitted. + Allowed bool `json:"allowed,omitempty"` + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + Result *StatusResult `json:"result,omitempty"` + // Converted object bytes + ObjectBytes []byte `json:"object_bytes,omitempty"` +} + +type StatusResult struct { + // Status of the operation. + // One of: "Success" or "Failure". + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + // +optional + Status string `json:"status,omitempty"` + // A human-readable description of the status of this operation. + // +optional + Message string `json:"message,omitempty"` + // A machine-readable description of why this operation is in the + // "Failure" status. If this value is empty there + // is no information available. A Reason clarifies an HTTP status + // code but does not override it. + // +optional + Reason string `json:"reason,omitempty"` + // Suggested HTTP return code for this status, 0 if not set. + // +optional + Code int32 `json:"code,omitempty"` +} diff --git a/backend/admission_adapter.go b/backend/admission_adapter.go new file mode 100644 index 000000000..3e2fe776d --- /dev/null +++ b/backend/admission_adapter.go @@ -0,0 +1,48 @@ +package backend + +import ( + "context" + + "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" +) + +// admissionSDKAdapter adapter between low level plugin protocol and SDK interfaces. +type admissionSDKAdapter struct { + handler AdmissionHandler +} + +func newAdmissionSDKAdapter(handler AdmissionHandler) *admissionSDKAdapter { + return &admissionSDKAdapter{ + handler: handler, + } +} + +func (a *admissionSDKAdapter) ValidateAdmission(ctx context.Context, req *pluginv2.AdmissionRequest) (*pluginv2.ValidationResponse, error) { + ctx = propagateTenantIDIfPresent(ctx) + parsedReq := FromProto().AdmissionRequest(req) + resp, err := a.handler.ValidateAdmission(ctx, parsedReq) + if err != nil { + return nil, err + } + return ToProto().ValidationResponse(resp), nil +} + +func (a *admissionSDKAdapter) MutateAdmission(ctx context.Context, req *pluginv2.AdmissionRequest) (*pluginv2.MutationResponse, error) { + ctx = propagateTenantIDIfPresent(ctx) + parsedReq := FromProto().AdmissionRequest(req) + resp, err := a.handler.MutateAdmission(ctx, parsedReq) + if err != nil { + return nil, err + } + return ToProto().MutationResponse(resp), nil +} + +func (a *admissionSDKAdapter) ConvertObject(ctx context.Context, req *pluginv2.ConversionRequest) (*pluginv2.ConversionResponse, error) { + ctx = propagateTenantIDIfPresent(ctx) + parsedReq := FromProto().ConversionRequest(req) + resp, err := a.handler.ConvertObject(ctx, parsedReq) + if err != nil { + return nil, err + } + return ToProto().ConversionResponse(resp), nil +} diff --git a/backend/app/manage.go b/backend/app/manage.go index c995e2c8a..012bb811d 100644 --- a/backend/app/manage.go +++ b/backend/app/manage.go @@ -11,13 +11,16 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/internal/buildinfo" ) -// ManageOpts can modify Manage behaviour. +// ManageOpts can modify Manage behavior. type ManageOpts struct { // GRPCSettings settings for gPRC. GRPCSettings backend.GRPCSettings // TracingOpts contains settings for tracing setup. TracingOpts tracing.Opts + + // Stateless admission handler + AdmissionHandler backend.AdmissionHandler } // Manage starts serving the app over gPRC with automatic instance management. @@ -43,6 +46,7 @@ func Manage(pluginID string, instanceFactory InstanceFactoryFunc, opts ManageOpt CallResourceHandler: handler, QueryDataHandler: handler, StreamHandler: handler, + AdmissionHandler: opts.AdmissionHandler, GRPCSettings: opts.GRPCSettings, }) } diff --git a/backend/common.go b/backend/common.go index 01d2bf982..659a87abe 100644 --- a/backend/common.go +++ b/backend/common.go @@ -56,6 +56,14 @@ func (s *AppInstanceSettings) HTTPClientOptions(_ context.Context) (httpclient.O return opts, nil } +func (s *AppInstanceSettings) GVK() GroupVersionKind { + return GroupVersionKind{ + Group: "grafana-plugin-sdk-go", // raw protobuf + Version: s.APIVersion, + Kind: "AppInstanceSettings", + } +} + // DataSourceInstanceSettings represents settings for a data source instance. // // In Grafana a data source instance is a data source plugin of certain @@ -145,6 +153,14 @@ func (s *DataSourceInstanceSettings) HTTPClientOptions(ctx context.Context) (htt return opts, nil } +func (s *DataSourceInstanceSettings) GVK() GroupVersionKind { + return GroupVersionKind{ + Group: "grafana-plugin-sdk-go", // raw protobuf + Version: s.APIVersion, + Kind: "DataSourceInstanceSettings", + } +} + // PluginContext holds contextual information about a plugin request, such as // Grafana organization, user and plugin instance settings. type PluginContext struct { diff --git a/backend/convert_from_protobuf.go b/backend/convert_from_protobuf.go index a9352d261..105f93887 100644 --- a/backend/convert_from_protobuf.go +++ b/backend/convert_from_protobuf.go @@ -297,6 +297,77 @@ func (f ConvertFromProtobuf) StreamPacket(protoReq *pluginv2.StreamPacket) *Stre } } +// StatusResult ... +func (f ConvertFromProtobuf) StatusResult(s *pluginv2.StatusResult) *StatusResult { + if s == nil { + return nil + } + return &StatusResult{ + Status: s.Status, + Message: s.Message, + Reason: s.Reason, + Code: s.Code, + } +} + +// GroupVersionKind ... +func (f ConvertFromProtobuf) GroupVersionKind(req *pluginv2.GroupVersionKind) GroupVersionKind { + return GroupVersionKind{ + Group: req.Group, + Version: req.Version, + Kind: req.Kind, + } +} + +// AdmissionRequest ... +func (f ConvertFromProtobuf) AdmissionRequest(req *pluginv2.AdmissionRequest) *AdmissionRequest { + return &AdmissionRequest{ + PluginContext: f.PluginContext(req.PluginContext), + Operation: AdmissionRequestOperation(req.Operation), + Kind: f.GroupVersionKind(req.Kind), + ObjectBytes: req.ObjectBytes, + OldObjectBytes: req.OldObjectBytes, + } +} + +// ConversionRequest ... +func (f ConvertFromProtobuf) ConversionRequest(req *pluginv2.ConversionRequest) *ConversionRequest { + return &ConversionRequest{ + PluginContext: f.PluginContext(req.PluginContext), + Kind: f.GroupVersionKind(req.Kind), + ObjectBytes: req.ObjectBytes, + TargetVersion: req.TargetVersion, + } +} + +// MutationResponse ... +func (f ConvertFromProtobuf) MutationResponse(rsp *pluginv2.MutationResponse) *MutationResponse { + return &MutationResponse{ + Allowed: rsp.Allowed, + Result: f.StatusResult(rsp.Result), + Warnings: rsp.Warnings, + ObjectBytes: rsp.ObjectBytes, + } +} + +// ValidationResponse ... +func (f ConvertFromProtobuf) ValidationResponse(rsp *pluginv2.ValidationResponse) *ValidationResponse { + return &ValidationResponse{ + Allowed: rsp.Allowed, + Result: f.StatusResult(rsp.Result), + Warnings: rsp.Warnings, + } +} + +// ConversionResponse ... +func (f ConvertFromProtobuf) ConversionResponse(rsp *pluginv2.ConversionResponse) *ConversionResponse { + return &ConversionResponse{ + Allowed: rsp.Allowed, + Result: f.StatusResult(rsp.Result), + ObjectBytes: rsp.ObjectBytes, + } +} + func (f ConvertFromProtobuf) GrafanaConfig(cfg map[string]string) *GrafanaCfg { return NewGrafanaCfg(cfg) } diff --git a/backend/convert_from_protobuf_test.go b/backend/convert_from_protobuf_test.go index 70a5ecc6a..a9e15e68e 100644 --- a/backend/convert_from_protobuf_test.go +++ b/backend/convert_from_protobuf_test.go @@ -533,3 +533,131 @@ func datasourceInstanceProtoFieldCountDelta() int64 { // returning 1 to account for the Type field in the SDK that is not in the protobuf return int64(1) } + +func TestConvertFromProtobufAdmissionRequest(t *testing.T) { + protoAR := &pluginv2.AdmissionRequest{ + PluginContext: protoPluginContext, + Operation: pluginv2.AdmissionRequest_UPDATE, + Kind: &pluginv2.GroupVersionKind{ + Group: "g", + Version: "v", + Kind: "k", + }, + ObjectBytes: []byte(`{"hello": "world"}`), + OldObjectBytes: []byte(`{"x": "y"}`), + } + + protoWalker := &walker{} + err := reflectwalk.Walk(protoAR, protoWalker) + require.NoError(t, err) + + if protoWalker.HasZeroFields() { + t.Fatalf(unsetErrFmt, + "proto", "AdmissionRequest", protoWalker.ZeroValueFieldCount, protoWalker.FieldCount) + } + + sdkAR := f.AdmissionRequest(protoAR) + + sdkWalker := &walker{} + err = reflectwalk.Walk(sdkAR, sdkWalker) + require.NoError(t, err) + + if sdkWalker.HasZeroFields() { + t.Fatalf(unsetErrFmt, "sdk", "AdmissionRequest", sdkWalker.ZeroValueFieldCount, sdkWalker.FieldCount) + } + + require.Equal(t, protoWalker.FieldCount+datasourceInstanceProtoFieldCountDelta(), sdkWalker.FieldCount) + + requireCounter := &requireCounter{} + + // PluginContext + requireCounter.Equal(t, protoAR.PluginContext.OrgId, sdkAR.PluginContext.OrgID) + requireCounter.Equal(t, protoAR.PluginContext.PluginId, sdkAR.PluginContext.PluginID) + requireCounter.Equal(t, protoAR.PluginContext.ApiVersion, sdkAR.PluginContext.APIVersion) + // User + requireCounter.Equal(t, protoAR.PluginContext.User.Login, sdkAR.PluginContext.User.Login) + requireCounter.Equal(t, protoAR.PluginContext.User.Name, sdkAR.PluginContext.User.Name) + requireCounter.Equal(t, protoAR.PluginContext.User.Email, sdkAR.PluginContext.User.Email) + requireCounter.Equal(t, protoAR.PluginContext.User.Role, sdkAR.PluginContext.User.Role) + + // App Instance Settings + requireCounter.Equal(t, json.RawMessage(protoAR.PluginContext.AppInstanceSettings.JsonData), sdkAR.PluginContext.AppInstanceSettings.JSONData) + requireCounter.Equal(t, map[string]string{"secret": "quiet"}, sdkAR.PluginContext.AppInstanceSettings.DecryptedSecureJSONData) + requireCounter.Equal(t, time.Unix(0, 86400*2*1e9), sdkAR.PluginContext.AppInstanceSettings.Updated) + requireCounter.Equal(t, protoAR.PluginContext.AppInstanceSettings.ApiVersion, sdkAR.PluginContext.AppInstanceSettings.APIVersion) + + // Datasource Instance Settings + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.Name, sdkAR.PluginContext.DataSourceInstanceSettings.Name) + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.Id, sdkAR.PluginContext.DataSourceInstanceSettings.ID) + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.Uid, sdkAR.PluginContext.DataSourceInstanceSettings.UID) + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.ApiVersion, sdkAR.PluginContext.DataSourceInstanceSettings.APIVersion) + requireCounter.Equal(t, protoAR.PluginContext.PluginId, sdkAR.PluginContext.DataSourceInstanceSettings.Type) + requireCounter.Equal(t, protoAR.PluginContext.PluginVersion, sdkAR.PluginContext.PluginVersion) + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.Url, sdkAR.PluginContext.DataSourceInstanceSettings.URL) + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.User, sdkAR.PluginContext.DataSourceInstanceSettings.User) + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.Database, sdkAR.PluginContext.DataSourceInstanceSettings.Database) + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.BasicAuthEnabled, sdkAR.PluginContext.DataSourceInstanceSettings.BasicAuthEnabled) + requireCounter.Equal(t, protoAR.PluginContext.DataSourceInstanceSettings.BasicAuthUser, sdkAR.PluginContext.DataSourceInstanceSettings.BasicAuthUser) + requireCounter.Equal(t, json.RawMessage(protoAR.PluginContext.DataSourceInstanceSettings.JsonData), sdkAR.PluginContext.DataSourceInstanceSettings.JSONData) + requireCounter.Equal(t, map[string]string{"secret": "quiet"}, sdkAR.PluginContext.DataSourceInstanceSettings.DecryptedSecureJSONData) + requireCounter.Equal(t, time.Unix(0, 86400*2*1e9), sdkAR.PluginContext.DataSourceInstanceSettings.Updated) + requireCounter.Equal(t, protoAR.PluginContext.UserAgent, sdkAR.PluginContext.UserAgent.String()) + + // The actual request values + requireCounter.Equal(t, protoAR.Kind.Group, sdkAR.Kind.Group) + requireCounter.Equal(t, protoAR.Kind.Version, sdkAR.Kind.Version) + requireCounter.Equal(t, protoAR.Kind.Kind, sdkAR.Kind.Kind) + requireCounter.Equal(t, int(protoAR.Operation), int(sdkAR.Operation)) + requireCounter.Equal(t, protoAR.ObjectBytes, sdkAR.ObjectBytes) + requireCounter.Equal(t, protoAR.OldObjectBytes, sdkAR.OldObjectBytes) + + require.Equal(t, requireCounter.Count, sdkWalker.FieldCount-6, "untested fields in conversion") // -6 Struct Fields +} + +func TestConvertFromProtobufMutationResponse(t *testing.T) { + protoRSP := &pluginv2.MutationResponse{ + Allowed: true, + Result: &pluginv2.StatusResult{ + Status: "A", + Message: "M", + Reason: "bad", + Code: 500, + }, + Warnings: []string{"hello"}, + ObjectBytes: []byte(`{"hello": "world"}`), + } + + protoWalker := &walker{} + err := reflectwalk.Walk(protoRSP, protoWalker) + require.NoError(t, err) + + if protoWalker.HasZeroFields() { + t.Fatalf(unsetErrFmt, + "proto", "MutationResponse", protoWalker.ZeroValueFieldCount, protoWalker.FieldCount) + } + + sdkRSP := f.MutationResponse(protoRSP) + + sdkWalker := &walker{} + err = reflectwalk.Walk(sdkRSP, sdkWalker) + require.NoError(t, err) + + if sdkWalker.HasZeroFields() { + t.Fatalf(unsetErrFmt, "sdk", "MutationResponse", sdkWalker.ZeroValueFieldCount, sdkWalker.FieldCount) + } + + require.Equal(t, protoWalker.FieldCount, sdkWalker.FieldCount) + + requireCounter := &requireCounter{} + + // PluginContext + requireCounter.Equal(t, protoRSP.Allowed, sdkRSP.Allowed) + requireCounter.Equal(t, protoRSP.ObjectBytes, sdkRSP.ObjectBytes) + requireCounter.Equal(t, protoRSP.Warnings, sdkRSP.Warnings) + requireCounter.Equal(t, protoRSP.Result.Code, sdkRSP.Result.Code) + requireCounter.Equal(t, protoRSP.Result.Message, sdkRSP.Result.Message) + requireCounter.Equal(t, protoRSP.Result.Reason, sdkRSP.Result.Reason) + requireCounter.Equal(t, protoRSP.Result.Status, sdkRSP.Result.Status) + + require.Equal(t, sdkWalker.FieldCount-1, requireCounter.Count, "untested fields in conversion") +} diff --git a/backend/convert_to_protobuf.go b/backend/convert_to_protobuf.go index 36f0cb43f..a2f2a6ec7 100644 --- a/backend/convert_to_protobuf.go +++ b/backend/convert_to_protobuf.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/useragent" "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" + "google.golang.org/protobuf/proto" ) // ConvertToProtobuf has a collection of methods for converting the autogenerated @@ -49,6 +50,44 @@ func (t ConvertToProtobuf) AppInstanceSettings(s *AppInstanceSettings) *pluginv2 } } +func AppInstanceSettingsToProtoBytes(s *AppInstanceSettings) ([]byte, error) { + if s == nil { + return nil, nil + } + return proto.Marshal(ConvertToProtobuf{}.AppInstanceSettings(s)) +} + +func AppInstanceSettingsFromProto(body []byte) (*AppInstanceSettings, error) { + if len(body) == 0 { + return nil, nil + } + tmp := &pluginv2.AppInstanceSettings{} + err := proto.Unmarshal(body, tmp) + if err != nil { + return nil, err + } + return ConvertFromProtobuf{}.AppInstanceSettings(tmp), nil +} + +func DataSourceInstanceSettingsToProtoBytes(s *DataSourceInstanceSettings) ([]byte, error) { + if s == nil { + return nil, nil + } + return proto.Marshal(ConvertToProtobuf{}.DataSourceInstanceSettings(s)) +} + +func DataSourceInstanceSettingsFromProto(body []byte, pluginID string) (*DataSourceInstanceSettings, error) { + if len(body) == 0 { + return nil, nil + } + tmp := &pluginv2.DataSourceInstanceSettings{} + err := proto.Unmarshal(body, tmp) + if err != nil { + return nil, err + } + return ConvertFromProtobuf{}.DataSourceInstanceSettings(tmp, pluginID), nil +} + // DataSourceInstanceSettings converts the SDK version of a DataSourceInstanceSettings to the protobuf version. func (t ConvertToProtobuf) DataSourceInstanceSettings(s *DataSourceInstanceSettings) *pluginv2.DataSourceInstanceSettings { if s == nil { @@ -298,6 +337,77 @@ func (t ConvertToProtobuf) CollectMetricsResult(res *CollectMetricsResult) *plug } } +// StatusResult converts the SDK version of a StatusResult to the protobuf version. +func (t ConvertToProtobuf) StatusResult(s *StatusResult) *pluginv2.StatusResult { + if s == nil { + return nil + } + return &pluginv2.StatusResult{ + Status: s.Status, + Message: s.Message, + Reason: s.Reason, + Code: s.Code, + } +} + +// GroupVersionKind converts the SDK version of a GroupVersionKind to the protobuf version. +func (t ConvertToProtobuf) GroupVersionKind(req *GroupVersionKind) *pluginv2.GroupVersionKind { + return &pluginv2.GroupVersionKind{ + Group: req.Group, + Version: req.Version, + Kind: req.Kind, + } +} + +// AdmissionRequest converts the SDK version of a AdmissionRequest to the protobuf version. +func (t ConvertToProtobuf) AdmissionRequest(req *AdmissionRequest) *pluginv2.AdmissionRequest { + return &pluginv2.AdmissionRequest{ + PluginContext: t.PluginContext(req.PluginContext), + Operation: pluginv2.AdmissionRequest_Operation(req.Operation), + Kind: t.GroupVersionKind(&req.Kind), + ObjectBytes: req.ObjectBytes, + OldObjectBytes: req.OldObjectBytes, + } +} + +// ConversionRequest converts the SDK version of a ConversionRequest to the protobuf version. +func (t ConvertToProtobuf) ConversionRequest(req *ConversionRequest) *pluginv2.ConversionRequest { + return &pluginv2.ConversionRequest{ + PluginContext: t.PluginContext(req.PluginContext), + Kind: t.GroupVersionKind(&req.Kind), + ObjectBytes: req.ObjectBytes, + TargetVersion: req.TargetVersion, + } +} + +// MutationResponse converts the SDK version of a MutationResponse to the protobuf version. +func (t ConvertToProtobuf) MutationResponse(rsp *MutationResponse) *pluginv2.MutationResponse { + return &pluginv2.MutationResponse{ + Allowed: rsp.Allowed, + Result: t.StatusResult(rsp.Result), + Warnings: rsp.Warnings, + ObjectBytes: rsp.ObjectBytes, + } +} + +// ValidationResponse converts the SDK version of a ValidationResponse to the protobuf version. +func (t ConvertToProtobuf) ValidationResponse(rsp *ValidationResponse) *pluginv2.ValidationResponse { + return &pluginv2.ValidationResponse{ + Allowed: rsp.Allowed, + Result: t.StatusResult(rsp.Result), + Warnings: rsp.Warnings, + } +} + +// ConversionResponse converts the SDK version of a ConversionResponse to the protobuf version. +func (t ConvertToProtobuf) ConversionResponse(rsp *ConversionResponse) *pluginv2.ConversionResponse { + return &pluginv2.ConversionResponse{ + Allowed: rsp.Allowed, + Result: t.StatusResult(rsp.Result), + ObjectBytes: rsp.ObjectBytes, + } +} + // GrafanaConfig converts the SDK version of a GrafanaCfg to the protobuf version. func (t ConvertToProtobuf) GrafanaConfig(cfg *GrafanaCfg) map[string]string { if cfg == nil { diff --git a/backend/convert_to_protobuf_test.go b/backend/convert_to_protobuf_test.go index 8e8c916b3..996024cc6 100644 --- a/backend/convert_to_protobuf_test.go +++ b/backend/convert_to_protobuf_test.go @@ -8,10 +8,10 @@ import ( "os" "syscall" "testing" - - "github.com/stretchr/testify/require" + "time" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/stretchr/testify/require" ) func TestConvertToProtobufQueryDataResponse(t *testing.T) { @@ -91,3 +91,55 @@ func TestConvertToProtobufQueryDataResponse(t *testing.T) { }) } } + +func TestConvertToProtobufStatus(t *testing.T) { + ar := ToProto().StatusResult(&StatusResult{ + Status: "a", + Message: "b", + Reason: "c", + Code: 234, + }) + require.NotNil(t, ar) + require.Equal(t, "a", ar.Status) + require.Equal(t, "b", ar.Message) + require.Equal(t, "c", ar.Reason) + require.Equal(t, int32(234), ar.Code) +} + +func TestInstanceSettingsAdmissionConversions(t *testing.T) { + t.Run("DataSource", func(t *testing.T) { + before := &DataSourceInstanceSettings{ + URL: "http://something", + Updated: time.Now(), + User: "u", + JSONData: []byte(`{"hello": "world"}`), + DecryptedSecureJSONData: map[string]string{ + "A": "B", + }, + } + wire, err := DataSourceInstanceSettingsToProtoBytes(before) + require.NoError(t, err) + after, err := DataSourceInstanceSettingsFromProto(wire, "") + require.NoError(t, err) + require.Equal(t, before.URL, after.URL) + require.Equal(t, before.User, after.User) + require.Equal(t, before.JSONData, after.JSONData) + require.Equal(t, before.DecryptedSecureJSONData, after.DecryptedSecureJSONData) + }) + + t.Run("App", func(t *testing.T) { + before := &AppInstanceSettings{ + Updated: time.Now(), + JSONData: []byte(`{"hello": "world"}`), + DecryptedSecureJSONData: map[string]string{ + "A": "B", + }, + } + wire, err := AppInstanceSettingsToProtoBytes(before) + require.NoError(t, err) + after, err := AppInstanceSettingsFromProto(wire) + require.NoError(t, err) + require.Equal(t, before.JSONData, after.JSONData) + require.Equal(t, before.DecryptedSecureJSONData, after.DecryptedSecureJSONData) + }) +} diff --git a/backend/datasource/manage.go b/backend/datasource/manage.go index ad298384d..019afcb7b 100644 --- a/backend/datasource/manage.go +++ b/backend/datasource/manage.go @@ -11,13 +11,16 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/internal/buildinfo" ) -// ManageOpts can modify Manage behaviour. +// ManageOpts can modify Manage behavior. type ManageOpts struct { // GRPCSettings settings for gPRC. GRPCSettings backend.GRPCSettings // TracingOpts contains settings for tracing setup. TracingOpts tracing.Opts + + // Stateless admission handler + AdmissionHandler backend.AdmissionHandler } // Manage starts serving the data source over gPRC with automatic instance management. @@ -43,6 +46,7 @@ func Manage(pluginID string, instanceFactory InstanceFactoryFunc, opts ManageOpt CallResourceHandler: handler, QueryDataHandler: handler, StreamHandler: handler, + AdmissionHandler: opts.AdmissionHandler, GRPCSettings: opts.GRPCSettings, }) } diff --git a/backend/datasource/serve.go b/backend/datasource/serve.go index d7db75873..a9794d4a5 100644 --- a/backend/datasource/serve.go +++ b/backend/datasource/serve.go @@ -21,6 +21,9 @@ type ServeOpts struct { // StreamHandler for streaming queries. backend.StreamHandler + // AdmissionHandler for processing storage requests + backend.AdmissionHandler + // GRPCSettings settings for gPRC. GRPCSettings backend.GRPCSettings } @@ -32,6 +35,7 @@ func Serve(opts ServeOpts) error { CallResourceHandler: opts.CallResourceHandler, QueryDataHandler: opts.QueryDataHandler, StreamHandler: opts.StreamHandler, + AdmissionHandler: opts.AdmissionHandler, GRPCSettings: opts.GRPCSettings, }) } diff --git a/backend/diagnostics.go b/backend/diagnostics.go index 49d48fe20..fcd8e83d9 100644 --- a/backend/diagnostics.go +++ b/backend/diagnostics.go @@ -43,7 +43,7 @@ var healthStatusNames = map[int]string{ 2: "ERROR", } -// String textual represntation of the status. +// String textual representation of the status. func (hs HealthStatus) String() string { s, exists := healthStatusNames[int(hs)] if exists { diff --git a/backend/grpcplugin/grpc_admission.go b/backend/grpcplugin/grpc_admission.go new file mode 100644 index 000000000..d5fc57824 --- /dev/null +++ b/backend/grpcplugin/grpc_admission.go @@ -0,0 +1,74 @@ +package grpcplugin + +import ( + "context" + + plugin "github.com/hashicorp/go-plugin" + "google.golang.org/grpc" + + "github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2" +) + +// AdmissionServer represents an admission control server. +type AdmissionServer interface { + pluginv2.AdmissionControlServer +} + +// AdmissionClient represents an admission control client. +type AdmissionClient interface { + pluginv2.AdmissionControlClient +} + +// AdmissionGRPCPlugin implements the GRPCPlugin interface from github.com/hashicorp/go-plugin. +type AdmissionGRPCPlugin struct { + plugin.NetRPCUnsupportedPlugin + plugin.GRPCPlugin + AdmissionServer AdmissionServer +} + +// GRPCServer registers p as an admission control gRPC server. +func (p *AdmissionGRPCPlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error { + pluginv2.RegisterAdmissionControlServer(s, &admissionGRPCServer{ + server: p.AdmissionServer, + }) + return nil +} + +func (p *AdmissionGRPCPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &admissionGRPCClient{client: pluginv2.NewAdmissionControlClient(c)}, nil +} + +type admissionGRPCServer struct { + server AdmissionServer +} + +func (s *admissionGRPCServer) ValidateAdmission(ctx context.Context, req *pluginv2.AdmissionRequest) (*pluginv2.ValidationResponse, error) { + return s.server.ValidateAdmission(ctx, req) +} + +func (s *admissionGRPCServer) MutateAdmission(ctx context.Context, req *pluginv2.AdmissionRequest) (*pluginv2.MutationResponse, error) { + return s.server.MutateAdmission(ctx, req) +} + +func (s *admissionGRPCServer) ConvertObject(ctx context.Context, req *pluginv2.ConversionRequest) (*pluginv2.ConversionResponse, error) { + return s.server.ConvertObject(ctx, req) +} + +type admissionGRPCClient struct { + client pluginv2.AdmissionControlClient +} + +func (s *admissionGRPCClient) ValidateAdmission(ctx context.Context, req *pluginv2.AdmissionRequest, opts ...grpc.CallOption) (*pluginv2.ValidationResponse, error) { + return s.client.ValidateAdmission(ctx, req, opts...) +} + +func (s *admissionGRPCClient) MutateAdmission(ctx context.Context, req *pluginv2.AdmissionRequest, opts ...grpc.CallOption) (*pluginv2.MutationResponse, error) { + return s.client.MutateAdmission(ctx, req, opts...) +} + +func (s *admissionGRPCClient) ConvertObject(ctx context.Context, req *pluginv2.ConversionRequest, opts ...grpc.CallOption) (*pluginv2.ConversionResponse, error) { + return s.client.ConvertObject(ctx, req, opts...) +} + +var _ AdmissionServer = &admissionGRPCServer{} +var _ AdmissionClient = &admissionGRPCClient{} diff --git a/backend/grpcplugin/serve.go b/backend/grpcplugin/serve.go index 8658024ac..81be687e2 100644 --- a/backend/grpcplugin/serve.go +++ b/backend/grpcplugin/serve.go @@ -12,6 +12,7 @@ type ServeOpts struct { ResourceServer ResourceServer DataServer DataServer StreamServer StreamServer + AdmissionServer AdmissionServer // GRPCServer factory method for creating GRPC server. // If nil, the default one will be used. @@ -47,6 +48,12 @@ func Serve(opts ServeOpts) error { } } + if opts.AdmissionServer != nil { + pSet["admission"] = &AdmissionGRPCPlugin{ + AdmissionServer: opts.AdmissionServer, + } + } + versionedPlugins[ProtocolVersion] = pSet if opts.GRPCServer == nil { diff --git a/backend/serve.go b/backend/serve.go index 00bbf240f..5c56f68ff 100644 --- a/backend/serve.go +++ b/backend/serve.go @@ -51,9 +51,12 @@ type ServeOpts struct { QueryDataHandler QueryDataHandler // StreamHandler handler for streaming queries. - // This is EXPERIMENTAL and is a subject to change till Grafana 8. StreamHandler StreamHandler + // AdmissionHandler validates resource storage + // This is EXPERIMENTAL and is a subject to change till Grafana 12 + AdmissionHandler AdmissionHandler + // GRPCSettings settings for gPRC. GRPCSettings GRPCSettings } @@ -74,6 +77,10 @@ func GRPCServeOpts(opts ServeOpts) grpcplugin.ServeOpts { if opts.StreamHandler != nil { pluginOpts.StreamServer = newStreamSDKAdapter(opts.StreamHandler) } + + if opts.AdmissionHandler != nil { + pluginOpts.AdmissionServer = newAdmissionSDKAdapter(opts.AdmissionHandler) + } return pluginOpts } @@ -191,6 +198,11 @@ func GracefulStandaloneServe(dsopts ServeOpts, info standalone.ServerSettings) e plugKeys = append(plugKeys, "stream") } + if pluginOpts.AdmissionServer != nil { + pluginv2.RegisterAdmissionControlServer(server, pluginOpts.AdmissionServer) + plugKeys = append(plugKeys, "admission") + } + // Start the GRPC server and handle graceful shutdown to ensure we execute deferred functions correctly log.DefaultLogger.Debug("Standalone plugin server", "capabilities", plugKeys) listener, err := net.Listen("tcp", info.Address) @@ -293,6 +305,11 @@ func TestStandaloneServe(opts ServeOpts, address string) (*grpc.Server, error) { plugKeys = append(plugKeys, "stream") } + if pluginOpts.AdmissionServer != nil { + pluginv2.RegisterAdmissionControlServer(server, pluginOpts.AdmissionServer) + plugKeys = append(plugKeys, "admission") + } + // Start the GRPC server and handle graceful shutdown to ensure we execute deferred functions correctly log.DefaultLogger.Info("Standalone plugin server", "capabilities", plugKeys) listener, err := net.Listen("tcp", address) diff --git a/genproto/pluginv2/backend.pb.go b/genproto/pluginv2/backend.pb.go index 7cffab6bd..f3ea3a5ed 100644 --- a/genproto/pluginv2/backend.pb.go +++ b/genproto/pluginv2/backend.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.0 +// protoc-gen-go v1.34.1 // protoc (unknown) // source: backend.proto @@ -167,6 +167,57 @@ func (PublishStreamResponse_Status) EnumDescriptor() ([]byte, []int) { return file_backend_proto_rawDescGZIP(), []int{19, 0} } +// Operation is the type of resource operation being checked for admission control +// https://github.com/kubernetes/kubernetes/blob/v1.30.0/pkg/apis/admission/types.go#L158 +type AdmissionRequest_Operation int32 + +const ( + AdmissionRequest_CREATE AdmissionRequest_Operation = 0 + AdmissionRequest_UPDATE AdmissionRequest_Operation = 1 + AdmissionRequest_DELETE AdmissionRequest_Operation = 2 +) + +// Enum value maps for AdmissionRequest_Operation. +var ( + AdmissionRequest_Operation_name = map[int32]string{ + 0: "CREATE", + 1: "UPDATE", + 2: "DELETE", + } + AdmissionRequest_Operation_value = map[string]int32{ + "CREATE": 0, + "UPDATE": 1, + "DELETE": 2, + } +) + +func (x AdmissionRequest_Operation) Enum() *AdmissionRequest_Operation { + p := new(AdmissionRequest_Operation) + *p = x + return p +} + +func (x AdmissionRequest_Operation) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (AdmissionRequest_Operation) Descriptor() protoreflect.EnumDescriptor { + return file_backend_proto_enumTypes[3].Descriptor() +} + +func (AdmissionRequest_Operation) Type() protoreflect.EnumType { + return &file_backend_proto_enumTypes[3] +} + +func (x AdmissionRequest_Operation) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use AdmissionRequest_Operation.Descriptor instead. +func (AdmissionRequest_Operation) EnumDescriptor() ([]byte, []int) { + return file_backend_proto_rawDescGZIP(), []int{23, 0} +} + type AppInstanceSettings struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1703,6 +1754,540 @@ func (x *StreamPacket) GetData() []byte { return nil } +// Identify the Object properties +type GroupVersionKind struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Group string `protobuf:"bytes,1,opt,name=group,proto3" json:"group,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Kind string `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` +} + +func (x *GroupVersionKind) Reset() { + *x = GroupVersionKind{} + if protoimpl.UnsafeEnabled { + mi := &file_backend_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GroupVersionKind) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GroupVersionKind) ProtoMessage() {} + +func (x *GroupVersionKind) ProtoReflect() protoreflect.Message { + mi := &file_backend_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GroupVersionKind.ProtoReflect.Descriptor instead. +func (*GroupVersionKind) Descriptor() ([]byte, []int) { + return file_backend_proto_rawDescGZIP(), []int{22} +} + +func (x *GroupVersionKind) GetGroup() string { + if x != nil { + return x.Group + } + return "" +} + +func (x *GroupVersionKind) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *GroupVersionKind) GetKind() string { + if x != nil { + return x.Kind + } + return "" +} + +// AdmissionRequest contains information from a kubernetes Admission request and decoded object(s). +type AdmissionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // NOTE: this may not include app or datasource instance settings depending on the request + PluginContext *PluginContext `protobuf:"bytes,1,opt,name=pluginContext,proto3" json:"pluginContext,omitempty"` + // The requested operation + Operation AdmissionRequest_Operation `protobuf:"varint,2,opt,name=operation,proto3,enum=pluginv2.AdmissionRequest_Operation" json:"operation,omitempty"` + // The object kind + Kind *GroupVersionKind `protobuf:"bytes,3,opt,name=kind,proto3" json:"kind,omitempty"` + // Object is the object in the request. This includes the full metadata envelope. + ObjectBytes []byte `protobuf:"bytes,4,opt,name=object_bytes,json=objectBytes,proto3" json:"object_bytes,omitempty"` + // OldObject is the object as it currently exists in storage. This includes the full metadata envelope. + OldObjectBytes []byte `protobuf:"bytes,5,opt,name=old_object_bytes,json=oldObjectBytes,proto3" json:"old_object_bytes,omitempty"` +} + +func (x *AdmissionRequest) Reset() { + *x = AdmissionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_backend_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdmissionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdmissionRequest) ProtoMessage() {} + +func (x *AdmissionRequest) ProtoReflect() protoreflect.Message { + mi := &file_backend_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AdmissionRequest.ProtoReflect.Descriptor instead. +func (*AdmissionRequest) Descriptor() ([]byte, []int) { + return file_backend_proto_rawDescGZIP(), []int{23} +} + +func (x *AdmissionRequest) GetPluginContext() *PluginContext { + if x != nil { + return x.PluginContext + } + return nil +} + +func (x *AdmissionRequest) GetOperation() AdmissionRequest_Operation { + if x != nil { + return x.Operation + } + return AdmissionRequest_CREATE +} + +func (x *AdmissionRequest) GetKind() *GroupVersionKind { + if x != nil { + return x.Kind + } + return nil +} + +func (x *AdmissionRequest) GetObjectBytes() []byte { + if x != nil { + return x.ObjectBytes + } + return nil +} + +func (x *AdmissionRequest) GetOldObjectBytes() []byte { + if x != nil { + return x.OldObjectBytes + } + return nil +} + +// ConversionRequest supports converting an object from one version to another +type ConversionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // NOTE: this may not include app or datasource instance settings depending on the request + PluginContext *PluginContext `protobuf:"bytes,1,opt,name=pluginContext,proto3" json:"pluginContext,omitempty"` + // The object kind + Kind *GroupVersionKind `protobuf:"bytes,2,opt,name=kind,proto3" json:"kind,omitempty"` + // Object is the object in the request. This includes the full metadata envelope. + ObjectBytes []byte `protobuf:"bytes,3,opt,name=object_bytes,json=objectBytes,proto3" json:"object_bytes,omitempty"` + // Target converted version + TargetVersion string `protobuf:"bytes,4,opt,name=target_version,json=targetVersion,proto3" json:"target_version,omitempty"` +} + +func (x *ConversionRequest) Reset() { + *x = ConversionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_backend_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConversionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConversionRequest) ProtoMessage() {} + +func (x *ConversionRequest) ProtoReflect() protoreflect.Message { + mi := &file_backend_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConversionRequest.ProtoReflect.Descriptor instead. +func (*ConversionRequest) Descriptor() ([]byte, []int) { + return file_backend_proto_rawDescGZIP(), []int{24} +} + +func (x *ConversionRequest) GetPluginContext() *PluginContext { + if x != nil { + return x.PluginContext + } + return nil +} + +func (x *ConversionRequest) GetKind() *GroupVersionKind { + if x != nil { + return x.Kind + } + return nil +} + +func (x *ConversionRequest) GetObjectBytes() []byte { + if x != nil { + return x.ObjectBytes + } + return nil +} + +func (x *ConversionRequest) GetTargetVersion() string { + if x != nil { + return x.TargetVersion + } + return "" +} + +// Check if an object can be admitted +type ValidationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Allowed indicates whether or not the admission request was permitted. + Allowed bool `protobuf:"varint,1,opt,name=allowed,proto3" json:"allowed,omitempty"` + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + Result *StatusResult `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` + // warnings is a list of warning messages to return to the requesting API client. + // Warning messages describe a problem the client making the API request should correct or be aware of. + // Limit warnings to 120 characters if possible. + // Warnings over 256 characters and large numbers of warnings may be truncated. + // +optional + Warnings []string `protobuf:"bytes,3,rep,name=warnings,proto3" json:"warnings,omitempty"` +} + +func (x *ValidationResponse) Reset() { + *x = ValidationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_backend_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidationResponse) ProtoMessage() {} + +func (x *ValidationResponse) ProtoReflect() protoreflect.Message { + mi := &file_backend_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ValidationResponse.ProtoReflect.Descriptor instead. +func (*ValidationResponse) Descriptor() ([]byte, []int) { + return file_backend_proto_rawDescGZIP(), []int{25} +} + +func (x *ValidationResponse) GetAllowed() bool { + if x != nil { + return x.Allowed + } + return false +} + +func (x *ValidationResponse) GetResult() *StatusResult { + if x != nil { + return x.Result + } + return nil +} + +func (x *ValidationResponse) GetWarnings() []string { + if x != nil { + return x.Warnings + } + return nil +} + +// Return a mutated copy of the object in a form that can be admitted +type MutationResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Allowed indicates whether or not the admission request was permitted. + Allowed bool `protobuf:"varint,1,opt,name=allowed,proto3" json:"allowed,omitempty"` + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + Result *StatusResult `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` + // warnings is a list of warning messages to return to the requesting API client. + // Warning messages describe a problem the client making the API request should correct or be aware of. + // Limit warnings to 120 characters if possible. + // Warnings over 256 characters and large numbers of warnings may be truncated. + // +optional + Warnings []string `protobuf:"bytes,3,rep,name=warnings,proto3" json:"warnings,omitempty"` + // Mutated object bytes + ObjectBytes []byte `protobuf:"bytes,4,opt,name=object_bytes,json=objectBytes,proto3" json:"object_bytes,omitempty"` +} + +func (x *MutationResponse) Reset() { + *x = MutationResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_backend_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MutationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MutationResponse) ProtoMessage() {} + +func (x *MutationResponse) ProtoReflect() protoreflect.Message { + mi := &file_backend_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MutationResponse.ProtoReflect.Descriptor instead. +func (*MutationResponse) Descriptor() ([]byte, []int) { + return file_backend_proto_rawDescGZIP(), []int{26} +} + +func (x *MutationResponse) GetAllowed() bool { + if x != nil { + return x.Allowed + } + return false +} + +func (x *MutationResponse) GetResult() *StatusResult { + if x != nil { + return x.Result + } + return nil +} + +func (x *MutationResponse) GetWarnings() []string { + if x != nil { + return x.Warnings + } + return nil +} + +func (x *MutationResponse) GetObjectBytes() []byte { + if x != nil { + return x.ObjectBytes + } + return nil +} + +type ConversionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Allowed indicates whether or not the admission request was permitted. + Allowed bool `protobuf:"varint,1,opt,name=allowed,proto3" json:"allowed,omitempty"` + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + Result *StatusResult `protobuf:"bytes,2,opt,name=result,proto3" json:"result,omitempty"` + // Converted object bytes + ObjectBytes []byte `protobuf:"bytes,3,opt,name=object_bytes,json=objectBytes,proto3" json:"object_bytes,omitempty"` +} + +func (x *ConversionResponse) Reset() { + *x = ConversionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_backend_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConversionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConversionResponse) ProtoMessage() {} + +func (x *ConversionResponse) ProtoReflect() protoreflect.Message { + mi := &file_backend_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConversionResponse.ProtoReflect.Descriptor instead. +func (*ConversionResponse) Descriptor() ([]byte, []int) { + return file_backend_proto_rawDescGZIP(), []int{27} +} + +func (x *ConversionResponse) GetAllowed() bool { + if x != nil { + return x.Allowed + } + return false +} + +func (x *ConversionResponse) GetResult() *StatusResult { + if x != nil { + return x.Result + } + return nil +} + +func (x *ConversionResponse) GetObjectBytes() []byte { + if x != nil { + return x.ObjectBytes + } + return nil +} + +// Status structure is copied from: +// https://github.com/kubernetes/apimachinery/blob/v0.30.1/pkg/apis/meta/v1/generated.proto#L979 +type StatusResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Status of the operation. + // One of: "Success" or "Failure". + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + // +optional + Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + // A human-readable description of the status of this operation. + // +optional + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + // A machine-readable description of why this operation is in the + // "Failure" status. If this value is empty there + // is no information available. A Reason clarifies an HTTP status + // code but does not override it. + // +optional + Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` + // Suggested HTTP return code for this status, 0 if not set. + // +optional + Code int32 `protobuf:"varint,4,opt,name=code,proto3" json:"code,omitempty"` +} + +func (x *StatusResult) Reset() { + *x = StatusResult{} + if protoimpl.UnsafeEnabled { + mi := &file_backend_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StatusResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatusResult) ProtoMessage() {} + +func (x *StatusResult) ProtoReflect() protoreflect.Message { + mi := &file_backend_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatusResult.ProtoReflect.Descriptor instead. +func (*StatusResult) Descriptor() ([]byte, []int) { + return file_backend_proto_rawDescGZIP(), []int{28} +} + +func (x *StatusResult) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *StatusResult) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *StatusResult) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +func (x *StatusResult) GetCode() int32 { + if x != nil { + return x.Code + } + return 0 +} + type CollectMetricsResponse_Payload struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1714,7 +2299,7 @@ type CollectMetricsResponse_Payload struct { func (x *CollectMetricsResponse_Payload) Reset() { *x = CollectMetricsResponse_Payload{} if protoimpl.UnsafeEnabled { - mi := &file_backend_proto_msgTypes[29] + mi := &file_backend_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1727,7 +2312,7 @@ func (x *CollectMetricsResponse_Payload) String() string { func (*CollectMetricsResponse_Payload) ProtoMessage() {} func (x *CollectMetricsResponse_Payload) ProtoReflect() protoreflect.Message { - mi := &file_backend_proto_msgTypes[29] + mi := &file_backend_proto_msgTypes[36] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2024,46 +2609,133 @@ var file_backend_proto_rawDesc = []byte{ 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x22, 0x0a, 0x0c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x5b, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x12, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x61, - 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, - 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x30, 0x01, 0x32, 0x4c, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a, 0x09, 0x51, - 0x75, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, - 0x6e, 0x76, 0x32, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x32, 0xae, 0x01, 0x0a, 0x0b, 0x44, 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, - 0x73, 0x12, 0x4a, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, - 0x12, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, - 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x48, - 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, - 0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, - 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, - 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x32, 0xf5, 0x01, 0x0a, 0x06, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x56, 0x0a, - 0x0f, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x73, - 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x09, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x52, 0x75, - 0x6e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, - 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x30, 0x01, 0x12, 0x50, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x73, 0x68, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, - 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, - 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x56, 0x0a, 0x10, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, + 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, + 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6b, 0x69, + 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x22, 0xc3, + 0x02, 0x0a, 0x10, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0d, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x52, 0x0d, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x12, 0x42, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, + 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, + 0x47, 0x72, 0x6f, 0x75, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, 0x69, 0x6e, 0x64, + 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x6c, 0x64, + 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6f, 0x6c, 0x64, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, + 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x45, 0x4c, 0x45, + 0x54, 0x45, 0x10, 0x02, 0x22, 0xcc, 0x01, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0d, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x50, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x52, 0x0d, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6b, 0x69, 0x6e, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x76, 0x32, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x4b, + 0x69, 0x6e, 0x64, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, + 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x22, 0x7a, 0x0a, 0x12, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x65, 0x64, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x22, + 0x9b, 0x01, 0x0a, 0x10, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x2e, + 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x08, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x81, 0x01, + 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x12, 0x2e, + 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x21, + 0x0a, 0x0c, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x22, 0x6c, 0x0a, 0x0c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x63, + 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x32, + 0x5b, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x43, + 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1d, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x32, 0x4c, 0x0a, 0x04, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x44, 0x0a, 0x09, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xae, 0x01, 0x0a, 0x0b, 0x44, + 0x69, 0x61, 0x67, 0x6e, 0x6f, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x4a, 0x0a, 0x0b, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x76, 0x32, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, 0x69, + 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xf5, 0x01, 0x0a, 0x06, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x56, 0x0a, 0x0f, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x20, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, + 0x0a, 0x09, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1a, 0x2e, 0x70, 0x6c, + 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, + 0x76, 0x32, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x30, + 0x01, 0x12, 0x50, 0x0a, 0x0d, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x32, 0xf8, 0x01, 0x0a, 0x10, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x12, 0x4d, 0x0a, 0x11, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, + 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0f, 0x4d, 0x75, 0x74, 0x61, 0x74, + 0x65, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x2e, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, + 0x32, 0x2e, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x12, 0x1b, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, + 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1c, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6e, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x0d, + 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x76, 0x32, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2078,91 +2750,113 @@ func file_backend_proto_rawDescGZIP() []byte { return file_backend_proto_rawDescData } -var file_backend_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 31) +var file_backend_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_backend_proto_msgTypes = make([]protoimpl.MessageInfo, 38) var file_backend_proto_goTypes = []interface{}{ (CheckHealthResponse_HealthStatus)(0), // 0: pluginv2.CheckHealthResponse.HealthStatus (SubscribeStreamResponse_Status)(0), // 1: pluginv2.SubscribeStreamResponse.Status (PublishStreamResponse_Status)(0), // 2: pluginv2.PublishStreamResponse.Status - (*AppInstanceSettings)(nil), // 3: pluginv2.AppInstanceSettings - (*DataSourceInstanceSettings)(nil), // 4: pluginv2.DataSourceInstanceSettings - (*User)(nil), // 5: pluginv2.User - (*PluginContext)(nil), // 6: pluginv2.PluginContext - (*StringList)(nil), // 7: pluginv2.StringList - (*CallResourceRequest)(nil), // 8: pluginv2.CallResourceRequest - (*CallResourceResponse)(nil), // 9: pluginv2.CallResourceResponse - (*TimeRange)(nil), // 10: pluginv2.TimeRange - (*DataQuery)(nil), // 11: pluginv2.DataQuery - (*QueryDataRequest)(nil), // 12: pluginv2.QueryDataRequest - (*QueryDataResponse)(nil), // 13: pluginv2.QueryDataResponse - (*DataResponse)(nil), // 14: pluginv2.DataResponse - (*CollectMetricsRequest)(nil), // 15: pluginv2.CollectMetricsRequest - (*CollectMetricsResponse)(nil), // 16: pluginv2.CollectMetricsResponse - (*CheckHealthRequest)(nil), // 17: pluginv2.CheckHealthRequest - (*CheckHealthResponse)(nil), // 18: pluginv2.CheckHealthResponse - (*SubscribeStreamRequest)(nil), // 19: pluginv2.SubscribeStreamRequest - (*SubscribeStreamResponse)(nil), // 20: pluginv2.SubscribeStreamResponse - (*PublishStreamRequest)(nil), // 21: pluginv2.PublishStreamRequest - (*PublishStreamResponse)(nil), // 22: pluginv2.PublishStreamResponse - (*RunStreamRequest)(nil), // 23: pluginv2.RunStreamRequest - (*StreamPacket)(nil), // 24: pluginv2.StreamPacket - nil, // 25: pluginv2.AppInstanceSettings.DecryptedSecureJsonDataEntry - nil, // 26: pluginv2.DataSourceInstanceSettings.DecryptedSecureJsonDataEntry - nil, // 27: pluginv2.PluginContext.GrafanaConfigEntry - nil, // 28: pluginv2.CallResourceRequest.HeadersEntry - nil, // 29: pluginv2.CallResourceResponse.HeadersEntry - nil, // 30: pluginv2.QueryDataRequest.HeadersEntry - nil, // 31: pluginv2.QueryDataResponse.ResponsesEntry - (*CollectMetricsResponse_Payload)(nil), // 32: pluginv2.CollectMetricsResponse.Payload - nil, // 33: pluginv2.CheckHealthRequest.HeadersEntry + (AdmissionRequest_Operation)(0), // 3: pluginv2.AdmissionRequest.Operation + (*AppInstanceSettings)(nil), // 4: pluginv2.AppInstanceSettings + (*DataSourceInstanceSettings)(nil), // 5: pluginv2.DataSourceInstanceSettings + (*User)(nil), // 6: pluginv2.User + (*PluginContext)(nil), // 7: pluginv2.PluginContext + (*StringList)(nil), // 8: pluginv2.StringList + (*CallResourceRequest)(nil), // 9: pluginv2.CallResourceRequest + (*CallResourceResponse)(nil), // 10: pluginv2.CallResourceResponse + (*TimeRange)(nil), // 11: pluginv2.TimeRange + (*DataQuery)(nil), // 12: pluginv2.DataQuery + (*QueryDataRequest)(nil), // 13: pluginv2.QueryDataRequest + (*QueryDataResponse)(nil), // 14: pluginv2.QueryDataResponse + (*DataResponse)(nil), // 15: pluginv2.DataResponse + (*CollectMetricsRequest)(nil), // 16: pluginv2.CollectMetricsRequest + (*CollectMetricsResponse)(nil), // 17: pluginv2.CollectMetricsResponse + (*CheckHealthRequest)(nil), // 18: pluginv2.CheckHealthRequest + (*CheckHealthResponse)(nil), // 19: pluginv2.CheckHealthResponse + (*SubscribeStreamRequest)(nil), // 20: pluginv2.SubscribeStreamRequest + (*SubscribeStreamResponse)(nil), // 21: pluginv2.SubscribeStreamResponse + (*PublishStreamRequest)(nil), // 22: pluginv2.PublishStreamRequest + (*PublishStreamResponse)(nil), // 23: pluginv2.PublishStreamResponse + (*RunStreamRequest)(nil), // 24: pluginv2.RunStreamRequest + (*StreamPacket)(nil), // 25: pluginv2.StreamPacket + (*GroupVersionKind)(nil), // 26: pluginv2.GroupVersionKind + (*AdmissionRequest)(nil), // 27: pluginv2.AdmissionRequest + (*ConversionRequest)(nil), // 28: pluginv2.ConversionRequest + (*ValidationResponse)(nil), // 29: pluginv2.ValidationResponse + (*MutationResponse)(nil), // 30: pluginv2.MutationResponse + (*ConversionResponse)(nil), // 31: pluginv2.ConversionResponse + (*StatusResult)(nil), // 32: pluginv2.StatusResult + nil, // 33: pluginv2.AppInstanceSettings.DecryptedSecureJsonDataEntry + nil, // 34: pluginv2.DataSourceInstanceSettings.DecryptedSecureJsonDataEntry + nil, // 35: pluginv2.PluginContext.GrafanaConfigEntry + nil, // 36: pluginv2.CallResourceRequest.HeadersEntry + nil, // 37: pluginv2.CallResourceResponse.HeadersEntry + nil, // 38: pluginv2.QueryDataRequest.HeadersEntry + nil, // 39: pluginv2.QueryDataResponse.ResponsesEntry + (*CollectMetricsResponse_Payload)(nil), // 40: pluginv2.CollectMetricsResponse.Payload + nil, // 41: pluginv2.CheckHealthRequest.HeadersEntry } var file_backend_proto_depIdxs = []int32{ - 25, // 0: pluginv2.AppInstanceSettings.decryptedSecureJsonData:type_name -> pluginv2.AppInstanceSettings.DecryptedSecureJsonDataEntry - 26, // 1: pluginv2.DataSourceInstanceSettings.decryptedSecureJsonData:type_name -> pluginv2.DataSourceInstanceSettings.DecryptedSecureJsonDataEntry - 5, // 2: pluginv2.PluginContext.user:type_name -> pluginv2.User - 3, // 3: pluginv2.PluginContext.appInstanceSettings:type_name -> pluginv2.AppInstanceSettings - 4, // 4: pluginv2.PluginContext.dataSourceInstanceSettings:type_name -> pluginv2.DataSourceInstanceSettings - 27, // 5: pluginv2.PluginContext.grafanaConfig:type_name -> pluginv2.PluginContext.GrafanaConfigEntry - 6, // 6: pluginv2.CallResourceRequest.pluginContext:type_name -> pluginv2.PluginContext - 28, // 7: pluginv2.CallResourceRequest.headers:type_name -> pluginv2.CallResourceRequest.HeadersEntry - 29, // 8: pluginv2.CallResourceResponse.headers:type_name -> pluginv2.CallResourceResponse.HeadersEntry - 10, // 9: pluginv2.DataQuery.timeRange:type_name -> pluginv2.TimeRange - 6, // 10: pluginv2.QueryDataRequest.pluginContext:type_name -> pluginv2.PluginContext - 30, // 11: pluginv2.QueryDataRequest.headers:type_name -> pluginv2.QueryDataRequest.HeadersEntry - 11, // 12: pluginv2.QueryDataRequest.queries:type_name -> pluginv2.DataQuery - 31, // 13: pluginv2.QueryDataResponse.responses:type_name -> pluginv2.QueryDataResponse.ResponsesEntry - 6, // 14: pluginv2.CollectMetricsRequest.pluginContext:type_name -> pluginv2.PluginContext - 32, // 15: pluginv2.CollectMetricsResponse.metrics:type_name -> pluginv2.CollectMetricsResponse.Payload - 6, // 16: pluginv2.CheckHealthRequest.pluginContext:type_name -> pluginv2.PluginContext - 33, // 17: pluginv2.CheckHealthRequest.headers:type_name -> pluginv2.CheckHealthRequest.HeadersEntry + 33, // 0: pluginv2.AppInstanceSettings.decryptedSecureJsonData:type_name -> pluginv2.AppInstanceSettings.DecryptedSecureJsonDataEntry + 34, // 1: pluginv2.DataSourceInstanceSettings.decryptedSecureJsonData:type_name -> pluginv2.DataSourceInstanceSettings.DecryptedSecureJsonDataEntry + 6, // 2: pluginv2.PluginContext.user:type_name -> pluginv2.User + 4, // 3: pluginv2.PluginContext.appInstanceSettings:type_name -> pluginv2.AppInstanceSettings + 5, // 4: pluginv2.PluginContext.dataSourceInstanceSettings:type_name -> pluginv2.DataSourceInstanceSettings + 35, // 5: pluginv2.PluginContext.grafanaConfig:type_name -> pluginv2.PluginContext.GrafanaConfigEntry + 7, // 6: pluginv2.CallResourceRequest.pluginContext:type_name -> pluginv2.PluginContext + 36, // 7: pluginv2.CallResourceRequest.headers:type_name -> pluginv2.CallResourceRequest.HeadersEntry + 37, // 8: pluginv2.CallResourceResponse.headers:type_name -> pluginv2.CallResourceResponse.HeadersEntry + 11, // 9: pluginv2.DataQuery.timeRange:type_name -> pluginv2.TimeRange + 7, // 10: pluginv2.QueryDataRequest.pluginContext:type_name -> pluginv2.PluginContext + 38, // 11: pluginv2.QueryDataRequest.headers:type_name -> pluginv2.QueryDataRequest.HeadersEntry + 12, // 12: pluginv2.QueryDataRequest.queries:type_name -> pluginv2.DataQuery + 39, // 13: pluginv2.QueryDataResponse.responses:type_name -> pluginv2.QueryDataResponse.ResponsesEntry + 7, // 14: pluginv2.CollectMetricsRequest.pluginContext:type_name -> pluginv2.PluginContext + 40, // 15: pluginv2.CollectMetricsResponse.metrics:type_name -> pluginv2.CollectMetricsResponse.Payload + 7, // 16: pluginv2.CheckHealthRequest.pluginContext:type_name -> pluginv2.PluginContext + 41, // 17: pluginv2.CheckHealthRequest.headers:type_name -> pluginv2.CheckHealthRequest.HeadersEntry 0, // 18: pluginv2.CheckHealthResponse.status:type_name -> pluginv2.CheckHealthResponse.HealthStatus - 6, // 19: pluginv2.SubscribeStreamRequest.pluginContext:type_name -> pluginv2.PluginContext + 7, // 19: pluginv2.SubscribeStreamRequest.pluginContext:type_name -> pluginv2.PluginContext 1, // 20: pluginv2.SubscribeStreamResponse.status:type_name -> pluginv2.SubscribeStreamResponse.Status - 6, // 21: pluginv2.PublishStreamRequest.pluginContext:type_name -> pluginv2.PluginContext + 7, // 21: pluginv2.PublishStreamRequest.pluginContext:type_name -> pluginv2.PluginContext 2, // 22: pluginv2.PublishStreamResponse.status:type_name -> pluginv2.PublishStreamResponse.Status - 6, // 23: pluginv2.RunStreamRequest.pluginContext:type_name -> pluginv2.PluginContext - 7, // 24: pluginv2.CallResourceRequest.HeadersEntry.value:type_name -> pluginv2.StringList - 7, // 25: pluginv2.CallResourceResponse.HeadersEntry.value:type_name -> pluginv2.StringList - 14, // 26: pluginv2.QueryDataResponse.ResponsesEntry.value:type_name -> pluginv2.DataResponse - 8, // 27: pluginv2.Resource.CallResource:input_type -> pluginv2.CallResourceRequest - 12, // 28: pluginv2.Data.QueryData:input_type -> pluginv2.QueryDataRequest - 17, // 29: pluginv2.Diagnostics.CheckHealth:input_type -> pluginv2.CheckHealthRequest - 15, // 30: pluginv2.Diagnostics.CollectMetrics:input_type -> pluginv2.CollectMetricsRequest - 19, // 31: pluginv2.Stream.SubscribeStream:input_type -> pluginv2.SubscribeStreamRequest - 23, // 32: pluginv2.Stream.RunStream:input_type -> pluginv2.RunStreamRequest - 21, // 33: pluginv2.Stream.PublishStream:input_type -> pluginv2.PublishStreamRequest - 9, // 34: pluginv2.Resource.CallResource:output_type -> pluginv2.CallResourceResponse - 13, // 35: pluginv2.Data.QueryData:output_type -> pluginv2.QueryDataResponse - 18, // 36: pluginv2.Diagnostics.CheckHealth:output_type -> pluginv2.CheckHealthResponse - 16, // 37: pluginv2.Diagnostics.CollectMetrics:output_type -> pluginv2.CollectMetricsResponse - 20, // 38: pluginv2.Stream.SubscribeStream:output_type -> pluginv2.SubscribeStreamResponse - 24, // 39: pluginv2.Stream.RunStream:output_type -> pluginv2.StreamPacket - 22, // 40: pluginv2.Stream.PublishStream:output_type -> pluginv2.PublishStreamResponse - 34, // [34:41] is the sub-list for method output_type - 27, // [27:34] is the sub-list for method input_type - 27, // [27:27] is the sub-list for extension type_name - 27, // [27:27] is the sub-list for extension extendee - 0, // [0:27] is the sub-list for field type_name + 7, // 23: pluginv2.RunStreamRequest.pluginContext:type_name -> pluginv2.PluginContext + 7, // 24: pluginv2.AdmissionRequest.pluginContext:type_name -> pluginv2.PluginContext + 3, // 25: pluginv2.AdmissionRequest.operation:type_name -> pluginv2.AdmissionRequest.Operation + 26, // 26: pluginv2.AdmissionRequest.kind:type_name -> pluginv2.GroupVersionKind + 7, // 27: pluginv2.ConversionRequest.pluginContext:type_name -> pluginv2.PluginContext + 26, // 28: pluginv2.ConversionRequest.kind:type_name -> pluginv2.GroupVersionKind + 32, // 29: pluginv2.ValidationResponse.result:type_name -> pluginv2.StatusResult + 32, // 30: pluginv2.MutationResponse.result:type_name -> pluginv2.StatusResult + 32, // 31: pluginv2.ConversionResponse.result:type_name -> pluginv2.StatusResult + 8, // 32: pluginv2.CallResourceRequest.HeadersEntry.value:type_name -> pluginv2.StringList + 8, // 33: pluginv2.CallResourceResponse.HeadersEntry.value:type_name -> pluginv2.StringList + 15, // 34: pluginv2.QueryDataResponse.ResponsesEntry.value:type_name -> pluginv2.DataResponse + 9, // 35: pluginv2.Resource.CallResource:input_type -> pluginv2.CallResourceRequest + 13, // 36: pluginv2.Data.QueryData:input_type -> pluginv2.QueryDataRequest + 18, // 37: pluginv2.Diagnostics.CheckHealth:input_type -> pluginv2.CheckHealthRequest + 16, // 38: pluginv2.Diagnostics.CollectMetrics:input_type -> pluginv2.CollectMetricsRequest + 20, // 39: pluginv2.Stream.SubscribeStream:input_type -> pluginv2.SubscribeStreamRequest + 24, // 40: pluginv2.Stream.RunStream:input_type -> pluginv2.RunStreamRequest + 22, // 41: pluginv2.Stream.PublishStream:input_type -> pluginv2.PublishStreamRequest + 27, // 42: pluginv2.AdmissionControl.ValidateAdmission:input_type -> pluginv2.AdmissionRequest + 27, // 43: pluginv2.AdmissionControl.MutateAdmission:input_type -> pluginv2.AdmissionRequest + 28, // 44: pluginv2.AdmissionControl.ConvertObject:input_type -> pluginv2.ConversionRequest + 10, // 45: pluginv2.Resource.CallResource:output_type -> pluginv2.CallResourceResponse + 14, // 46: pluginv2.Data.QueryData:output_type -> pluginv2.QueryDataResponse + 19, // 47: pluginv2.Diagnostics.CheckHealth:output_type -> pluginv2.CheckHealthResponse + 17, // 48: pluginv2.Diagnostics.CollectMetrics:output_type -> pluginv2.CollectMetricsResponse + 21, // 49: pluginv2.Stream.SubscribeStream:output_type -> pluginv2.SubscribeStreamResponse + 25, // 50: pluginv2.Stream.RunStream:output_type -> pluginv2.StreamPacket + 23, // 51: pluginv2.Stream.PublishStream:output_type -> pluginv2.PublishStreamResponse + 29, // 52: pluginv2.AdmissionControl.ValidateAdmission:output_type -> pluginv2.ValidationResponse + 30, // 53: pluginv2.AdmissionControl.MutateAdmission:output_type -> pluginv2.MutationResponse + 31, // 54: pluginv2.AdmissionControl.ConvertObject:output_type -> pluginv2.ConversionResponse + 45, // [45:55] is the sub-list for method output_type + 35, // [35:45] is the sub-list for method input_type + 35, // [35:35] is the sub-list for extension type_name + 35, // [35:35] is the sub-list for extension extendee + 0, // [0:35] is the sub-list for field type_name } func init() { file_backend_proto_init() } @@ -2435,7 +3129,91 @@ func file_backend_proto_init() { return nil } } - file_backend_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_backend_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GroupVersionKind); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backend_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AdmissionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backend_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConversionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backend_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backend_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MutationResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backend_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConversionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backend_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StatusResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_backend_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CollectMetricsResponse_Payload); i { case 0: return &v.state @@ -2453,10 +3231,10 @@ func file_backend_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_backend_proto_rawDesc, - NumEnums: 3, - NumMessages: 31, + NumEnums: 4, + NumMessages: 38, NumExtensions: 0, - NumServices: 4, + NumServices: 5, }, GoTypes: file_backend_proto_goTypes, DependencyIndexes: file_backend_proto_depIdxs, diff --git a/genproto/pluginv2/backend_grpc.pb.go b/genproto/pluginv2/backend_grpc.pb.go index 7e280ca72..553664ef6 100644 --- a/genproto/pluginv2/backend_grpc.pb.go +++ b/genproto/pluginv2/backend_grpc.pb.go @@ -559,3 +559,171 @@ var Stream_ServiceDesc = grpc.ServiceDesc{ }, Metadata: "backend.proto", } + +const ( + AdmissionControl_ValidateAdmission_FullMethodName = "/pluginv2.AdmissionControl/ValidateAdmission" + AdmissionControl_MutateAdmission_FullMethodName = "/pluginv2.AdmissionControl/MutateAdmission" + AdmissionControl_ConvertObject_FullMethodName = "/pluginv2.AdmissionControl/ConvertObject" +) + +// AdmissionControlClient is the client API for AdmissionControl service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type AdmissionControlClient interface { + // Validate a resource -- the response is a simple yes/no + ValidateAdmission(ctx context.Context, in *AdmissionRequest, opts ...grpc.CallOption) (*ValidationResponse, error) + // Return a modified copy of the request that can be saved or a descriptive error + MutateAdmission(ctx context.Context, in *AdmissionRequest, opts ...grpc.CallOption) (*MutationResponse, error) + // Convert a resource to a new version + ConvertObject(ctx context.Context, in *ConversionRequest, opts ...grpc.CallOption) (*ConversionResponse, error) +} + +type admissionControlClient struct { + cc grpc.ClientConnInterface +} + +func NewAdmissionControlClient(cc grpc.ClientConnInterface) AdmissionControlClient { + return &admissionControlClient{cc} +} + +func (c *admissionControlClient) ValidateAdmission(ctx context.Context, in *AdmissionRequest, opts ...grpc.CallOption) (*ValidationResponse, error) { + out := new(ValidationResponse) + err := c.cc.Invoke(ctx, AdmissionControl_ValidateAdmission_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *admissionControlClient) MutateAdmission(ctx context.Context, in *AdmissionRequest, opts ...grpc.CallOption) (*MutationResponse, error) { + out := new(MutationResponse) + err := c.cc.Invoke(ctx, AdmissionControl_MutateAdmission_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *admissionControlClient) ConvertObject(ctx context.Context, in *ConversionRequest, opts ...grpc.CallOption) (*ConversionResponse, error) { + out := new(ConversionResponse) + err := c.cc.Invoke(ctx, AdmissionControl_ConvertObject_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdmissionControlServer is the server API for AdmissionControl service. +// All implementations should embed UnimplementedAdmissionControlServer +// for forward compatibility +type AdmissionControlServer interface { + // Validate a resource -- the response is a simple yes/no + ValidateAdmission(context.Context, *AdmissionRequest) (*ValidationResponse, error) + // Return a modified copy of the request that can be saved or a descriptive error + MutateAdmission(context.Context, *AdmissionRequest) (*MutationResponse, error) + // Convert a resource to a new version + ConvertObject(context.Context, *ConversionRequest) (*ConversionResponse, error) +} + +// UnimplementedAdmissionControlServer should be embedded to have forward compatible implementations. +type UnimplementedAdmissionControlServer struct { +} + +func (UnimplementedAdmissionControlServer) ValidateAdmission(context.Context, *AdmissionRequest) (*ValidationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidateAdmission not implemented") +} +func (UnimplementedAdmissionControlServer) MutateAdmission(context.Context, *AdmissionRequest) (*MutationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MutateAdmission not implemented") +} +func (UnimplementedAdmissionControlServer) ConvertObject(context.Context, *ConversionRequest) (*ConversionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ConvertObject not implemented") +} + +// UnsafeAdmissionControlServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AdmissionControlServer will +// result in compilation errors. +type UnsafeAdmissionControlServer interface { + mustEmbedUnimplementedAdmissionControlServer() +} + +func RegisterAdmissionControlServer(s grpc.ServiceRegistrar, srv AdmissionControlServer) { + s.RegisterService(&AdmissionControl_ServiceDesc, srv) +} + +func _AdmissionControl_ValidateAdmission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AdmissionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdmissionControlServer).ValidateAdmission(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdmissionControl_ValidateAdmission_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdmissionControlServer).ValidateAdmission(ctx, req.(*AdmissionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdmissionControl_MutateAdmission_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AdmissionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdmissionControlServer).MutateAdmission(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdmissionControl_MutateAdmission_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdmissionControlServer).MutateAdmission(ctx, req.(*AdmissionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdmissionControl_ConvertObject_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ConversionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdmissionControlServer).ConvertObject(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AdmissionControl_ConvertObject_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdmissionControlServer).ConvertObject(ctx, req.(*ConversionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AdmissionControl_ServiceDesc is the grpc.ServiceDesc for AdmissionControl service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AdmissionControl_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "pluginv2.AdmissionControl", + HandlerType: (*AdmissionControlServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "ValidateAdmission", + Handler: _AdmissionControl_ValidateAdmission_Handler, + }, + { + MethodName: "MutateAdmission", + Handler: _AdmissionControl_MutateAdmission_Handler, + }, + { + MethodName: "ConvertObject", + Handler: _AdmissionControl_ConvertObject_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "backend.proto", +} diff --git a/proto/backend.proto b/proto/backend.proto index 95204d2a3..e2f8da8d3 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -217,7 +217,7 @@ message CheckHealthResponse { } //----------------------------------------------------------------- -// Stream -- EXPERIMENTAL and is subject to change until 8.0 +// Stream //----------------------------------------------------------------- service Stream { @@ -306,3 +306,144 @@ message StreamPacket { // JSON-encoded data to publish into a channel. bytes data = 1; } + +//----------------------------------------------------------------- +// AdmissionControl -- EXPERIMENTAL and is subject to change until 12.?? +//----------------------------------------------------------------- + +// Admission control is a service based on the kubernetes admission webhook patterns. +// This service can be used to verify if objects are valid and convert between versions +// See: https://github.com/kubernetes/kubernetes/blob/v1.30.0/pkg/apis/admission/types.go#L41 +// And: https://github.com/grafana/grafana-app-sdk/blob/main/resource/admission.go#L14 +service AdmissionControl { + // Validate a resource -- the response is a simple yes/no + rpc ValidateAdmission(AdmissionRequest) returns (ValidationResponse); + + // Return a modified copy of the request that can be saved or a descriptive error + rpc MutateAdmission(AdmissionRequest) returns (MutationResponse); + + // Convert a resource to a new version + rpc ConvertObject(ConversionRequest) returns (ConversionResponse); +} + +// Identify the Object properties +message GroupVersionKind { + string group = 1; + string version = 2; + string kind = 3; +} + +// AdmissionRequest contains information from a kubernetes Admission request and decoded object(s). +message AdmissionRequest { + // Operation is the type of resource operation being checked for admission control + // https://github.com/kubernetes/kubernetes/blob/v1.30.0/pkg/apis/admission/types.go#L158 + enum Operation { + CREATE = 0; + UPDATE = 1; + DELETE = 2; + } + + // NOTE: this may not include app or datasource instance settings depending on the request + PluginContext pluginContext = 1; + + // The requested operation + Operation operation = 2; + + // The object kind + GroupVersionKind kind = 3; + + // Object is the object in the request. This includes the full metadata envelope. + bytes object_bytes = 4; + + // OldObject is the object as it currently exists in storage. This includes the full metadata envelope. + bytes old_object_bytes = 5; +} + +// ConversionRequest supports converting an object from one version to another +message ConversionRequest { + // NOTE: this may not include app or datasource instance settings depending on the request + PluginContext pluginContext = 1; + + // The object kind + GroupVersionKind kind = 2; + + // Object is the object in the request. This includes the full metadata envelope. + bytes object_bytes = 3; + + // Target converted version + string target_version = 4; +} + +// Check if an object can be admitted +message ValidationResponse { + // Allowed indicates whether or not the admission request was permitted. + bool allowed = 1; + + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + StatusResult result = 2; + + // warnings is a list of warning messages to return to the requesting API client. + // Warning messages describe a problem the client making the API request should correct or be aware of. + // Limit warnings to 120 characters if possible. + // Warnings over 256 characters and large numbers of warnings may be truncated. + // +optional + repeated string warnings = 3; +} + +// Return a mutated copy of the object in a form that can be admitted +message MutationResponse { + // Allowed indicates whether or not the admission request was permitted. + bool allowed = 1; + + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + StatusResult result = 2; + + // warnings is a list of warning messages to return to the requesting API client. + // Warning messages describe a problem the client making the API request should correct or be aware of. + // Limit warnings to 120 characters if possible. + // Warnings over 256 characters and large numbers of warnings may be truncated. + // +optional + repeated string warnings = 3; + + // Mutated object bytes + bytes object_bytes = 4; +} + +message ConversionResponse { + // Allowed indicates whether or not the admission request was permitted. + bool allowed = 1; + + // Result contains extra details into why an admission request was denied. + // This field IS NOT consulted in any way if "Allowed" is "true". + // +optional + StatusResult result = 2; + + // Converted object bytes + bytes object_bytes = 3; +} + +// Status structure is copied from: +// https://github.com/kubernetes/apimachinery/blob/v0.30.1/pkg/apis/meta/v1/generated.proto#L979 +message StatusResult { + // Status of the operation. + // One of: "Success" or "Failure". + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + // +optional + string status = 1; + // A human-readable description of the status of this operation. + // +optional + string message = 2; + // A machine-readable description of why this operation is in the + // "Failure" status. If this value is empty there + // is no information available. A Reason clarifies an HTTP status + // code but does not override it. + // +optional + string reason = 3; + // Suggested HTTP return code for this status, 0 if not set. + // +optional + int32 code = 4; +} \ No newline at end of file