Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API: Add new AdmissionControl service (experimental for now) #983

Merged
merged 39 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
366c6cd
add admission service
ryantxu May 10, 2024
5b1a3ad
tabs
ryantxu May 10, 2024
479ee40
use k8s names
ryantxu May 10, 2024
0a0e017
more adapters
ryantxu May 13, 2024
8debcd4
more adapters
ryantxu May 13, 2024
4f7ef3c
more adapters
ryantxu May 13, 2024
53b94e1
better tests
ryantxu May 13, 2024
20b1338
add plugin context
ryantxu May 13, 2024
80f737d
Merge remote-tracking branch 'origin/main' into validate-settings
ryantxu May 17, 2024
e42def7
add instance settings callback
ryantxu May 17, 2024
bb04333
add instance settings callback
ryantxu May 17, 2024
c0a3f78
add function for fake client
ryantxu May 17, 2024
f850b5f
dooh
ryantxu May 17, 2024
35a409b
Merge remote-tracking branch 'origin/main' into validate-settings
ryantxu May 20, 2024
fc6110a
create and update functions
ryantxu May 20, 2024
823af31
now with storage interface
ryantxu May 20, 2024
43b23be
now with storage interface
ryantxu May 20, 2024
1e20bd7
fix lint
ryantxu May 20, 2024
d793710
now as admission
ryantxu May 21, 2024
186c771
fix lint
ryantxu May 21, 2024
88f4b29
update command
ryantxu May 21, 2024
9aee73f
add converters
ryantxu May 21, 2024
61197ea
add converters
ryantxu May 21, 2024
66df1d5
fix lint
ryantxu May 21, 2024
9686768
storage >> admission
ryantxu May 21, 2024
0a5fbac
Use GVK not GVKR
ryantxu May 21, 2024
218525d
proto helpers
ryantxu May 21, 2024
68e4b33
cleanup
ryantxu May 21, 2024
416574f
Merge remote-tracking branch 'origin/main' into validate-settings
ryantxu May 22, 2024
a437721
lint
ryantxu May 22, 2024
be57ff5
Update backend/admission.go
ryantxu May 23, 2024
fcce0f6
use different response structs
ryantxu May 23, 2024
3662d46
more cleanup
ryantxu May 23, 2024
9f22d16
added better conversion tests
ryantxu May 23, 2024
a5f8757
test lint
ryantxu May 23, 2024
62dae98
Merge remote-tracking branch 'origin/main' into validate-settings
ryantxu May 23, 2024
fd2f15b
better comments
ryantxu May 23, 2024
9b8dfd5
add response status to validate method
ryantxu May 24, 2024
48f12a0
Update backend/admission.go
ryantxu May 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 6 additions & 5 deletions backend/app/manage.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ func Manage(pluginID string, instanceFactory InstanceFactoryFunc, opts ManageOpt
}
handler := automanagement.NewManager(NewInstanceManager(instanceFactory))
return backend.Manage(pluginID, backend.ServeOpts{
CheckHealthHandler: handler,
CallResourceHandler: handler,
QueryDataHandler: handler,
StreamHandler: handler,
GRPCSettings: opts.GRPCSettings,
CheckHealthHandler: handler,
CallResourceHandler: handler,
QueryDataHandler: handler,
StreamHandler: handler,
InstanceSettingsHandler: handler,
GRPCSettings: opts.GRPCSettings,
})
}
33 changes: 33 additions & 0 deletions backend/convert_from_protobuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,39 @@ 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,
}
}

// ProcessInstanceSettingsRequest ...
func (f ConvertFromProtobuf) ProcessInstanceSettingsRequest(req *pluginv2.ProcessInstanceSettingsRequest) *ProcessInstanceSettingsRequest {
return &ProcessInstanceSettingsRequest{
PluginContext: f.PluginContext(req.PluginContext),
Operation: InstanceSettingsOperation(req.Operation),
CheckHealth: req.CheckHealth,
}
}

// ProcessInstanceSettingsResponse ...
func (f ConvertFromProtobuf) ProcessInstanceSettingsResponse(rsp *pluginv2.ProcessInstanceSettingsResponse) *ProcessInstanceSettingsResponse {
return &ProcessInstanceSettingsResponse{
Allowed: rsp.Allowed,
Result: f.StatusResult(rsp.Result),
Warnings: rsp.Warnings,
AppInstanceSettings: f.AppInstanceSettings(rsp.AppInstanceSettings),
DataSourceInstanceSettings: f.DataSourceInstanceSettings(rsp.DataSourceInstanceSettings, ""),
}
}

func (f ConvertFromProtobuf) GrafanaConfig(cfg map[string]string) *GrafanaCfg {
return NewGrafanaCfg(cfg)
}
74 changes: 74 additions & 0 deletions backend/convert_from_protobuf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,3 +533,77 @@ func datasourceInstanceProtoFieldCountDelta() int64 {
// returning 1 to account for the Type field in the SDK that is not in the protobuf
return int64(1)
}

func TestConvertFromProtobufProcessInstanceSettingsRequest(t *testing.T) {
protoPIS := &pluginv2.ProcessInstanceSettingsRequest{
PluginContext: protoPluginContext,
Operation: pluginv2.ProcessInstanceSettingsRequest_UPDATE,
CheckHealth: true,
}

protoWalker := &walker{}
err := reflectwalk.Walk(protoPIS, protoWalker)
require.NoError(t, err)

if protoWalker.HasZeroFields() {
t.Fatalf(unsetErrFmt,
"proto", "QueryDataRequest", protoWalker.ZeroValueFieldCount, protoWalker.FieldCount)
}

sdkPIS := f.ProcessInstanceSettingsRequest(protoPIS)

sdkWalker := &walker{}
err = reflectwalk.Walk(sdkPIS, sdkWalker)
require.NoError(t, err)

if sdkWalker.HasZeroFields() {
t.Fatalf(unsetErrFmt, "sdk", "QueryDataRequest", sdkWalker.ZeroValueFieldCount, sdkWalker.FieldCount)
}

require.Equal(t, protoWalker.FieldCount+datasourceInstanceProtoFieldCountDelta(), sdkWalker.FieldCount)

requireCounter := &requireCounter{}

requireCounter.Equal(t, int32(protoPIS.Operation), int32(sdkPIS.Operation))
requireCounter.Equal(t, protoPIS.CheckHealth, sdkPIS.CheckHealth)

// PluginContext
requireCounter.Equal(t, protoPIS.PluginContext.OrgId, sdkPIS.PluginContext.OrgID)
requireCounter.Equal(t, protoPIS.PluginContext.PluginId, sdkPIS.PluginContext.PluginID)
requireCounter.Equal(t, protoPIS.PluginContext.ApiVersion, sdkPIS.PluginContext.APIVersion)
// User
requireCounter.Equal(t, protoPIS.PluginContext.User.Login, sdkPIS.PluginContext.User.Login)
requireCounter.Equal(t, protoPIS.PluginContext.User.Name, sdkPIS.PluginContext.User.Name)
requireCounter.Equal(t, protoPIS.PluginContext.User.Email, sdkPIS.PluginContext.User.Email)
requireCounter.Equal(t, protoPIS.PluginContext.User.Role, sdkPIS.PluginContext.User.Role)

// App Instance Settings
requireCounter.Equal(t, json.RawMessage(protoPIS.PluginContext.AppInstanceSettings.JsonData), sdkPIS.PluginContext.AppInstanceSettings.JSONData)
requireCounter.Equal(t, map[string]string{"secret": "quiet"}, sdkPIS.PluginContext.AppInstanceSettings.DecryptedSecureJSONData)
requireCounter.Equal(t, time.Unix(0, 86400*2*1e9), sdkPIS.PluginContext.AppInstanceSettings.Updated)
requireCounter.Equal(t, protoPIS.PluginContext.AppInstanceSettings.ApiVersion, sdkPIS.PluginContext.AppInstanceSettings.APIVersion)

// Datasource Instance Settings
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.Name, sdkPIS.PluginContext.DataSourceInstanceSettings.Name)
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.Id, sdkPIS.PluginContext.DataSourceInstanceSettings.ID)
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.Uid, sdkPIS.PluginContext.DataSourceInstanceSettings.UID)
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.ApiVersion, sdkPIS.PluginContext.DataSourceInstanceSettings.APIVersion)
requireCounter.Equal(t, protoPIS.PluginContext.PluginId, sdkPIS.PluginContext.DataSourceInstanceSettings.Type)
requireCounter.Equal(t, protoPIS.PluginContext.PluginVersion, sdkPIS.PluginContext.PluginVersion)
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.Url, sdkPIS.PluginContext.DataSourceInstanceSettings.URL)
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.User, sdkPIS.PluginContext.DataSourceInstanceSettings.User)
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.Database, sdkPIS.PluginContext.DataSourceInstanceSettings.Database)
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.BasicAuthEnabled, sdkPIS.PluginContext.DataSourceInstanceSettings.BasicAuthEnabled)
requireCounter.Equal(t, protoPIS.PluginContext.DataSourceInstanceSettings.BasicAuthUser, sdkPIS.PluginContext.DataSourceInstanceSettings.BasicAuthUser)
requireCounter.Equal(t, json.RawMessage(protoPIS.PluginContext.DataSourceInstanceSettings.JsonData), sdkPIS.PluginContext.DataSourceInstanceSettings.JSONData)
requireCounter.Equal(t, map[string]string{"secret": "quiet"}, sdkPIS.PluginContext.DataSourceInstanceSettings.DecryptedSecureJSONData)
requireCounter.Equal(t, time.Unix(0, 86400*2*1e9), sdkPIS.PluginContext.DataSourceInstanceSettings.Updated)
requireCounter.Equal(t, protoPIS.PluginContext.UserAgent, sdkPIS.PluginContext.UserAgent.String())

// -5 is:
// PluginContext, .User, .AppInstanceSettings, .DataSourceInstanceSettings
//
//
//
require.Equal(t, requireCounter.Count, sdkWalker.FieldCount-5, "untested fields in conversion") // -6 Struct Fields
}
33 changes: 33 additions & 0 deletions backend/convert_to_protobuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,39 @@ 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,
}
}

// ProcessInstanceSettingsRequest converts the SDK version of a ProcessInstanceSettingsRequest to the protobuf version.
func (t ConvertToProtobuf) ProcessInstanceSettingsRequest(req *ProcessInstanceSettingsRequest) *pluginv2.ProcessInstanceSettingsRequest {
return &pluginv2.ProcessInstanceSettingsRequest{
PluginContext: t.PluginContext(req.PluginContext),
Operation: pluginv2.ProcessInstanceSettingsRequest_Operation(req.Operation),
CheckHealth: req.CheckHealth,
}
}

// ProcessInstanceSettingsRequest converts the SDK version of a ProcessInstanceSettingsResponse to the protobuf version.
func (t ConvertToProtobuf) ProcessInstanceSettingsResponse(rsp *ProcessInstanceSettingsResponse) *pluginv2.ProcessInstanceSettingsResponse {
return &pluginv2.ProcessInstanceSettingsResponse{
Allowed: rsp.Allowed,
Result: t.StatusResult(rsp.Result),
Warnings: rsp.Warnings,
AppInstanceSettings: t.AppInstanceSettings(rsp.AppInstanceSettings),
DataSourceInstanceSettings: t.DataSourceInstanceSettings(rsp.DataSourceInstanceSettings),
}
}

// GrafanaConfig converts the SDK version of a GrafanaCfg to the protobuf version.
func (t ConvertToProtobuf) GrafanaConfig(cfg *GrafanaCfg) map[string]string {
if cfg == nil {
Expand Down
14 changes: 14 additions & 0 deletions backend/convert_to_protobuf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,17 @@ 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), int32(ar.Code))
}
11 changes: 6 additions & 5 deletions backend/datasource/manage.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ func Manage(pluginID string, instanceFactory InstanceFactoryFunc, opts ManageOpt
}
handler := automanagement.NewManager(NewInstanceManager(instanceFactory))
return backend.Manage(pluginID, backend.ServeOpts{
CheckHealthHandler: handler,
CallResourceHandler: handler,
QueryDataHandler: handler,
StreamHandler: handler,
GRPCSettings: opts.GRPCSettings,
CheckHealthHandler: handler,
CallResourceHandler: handler,
QueryDataHandler: handler,
StreamHandler: handler,
InstanceSettingsHandler: handler,
GRPCSettings: opts.GRPCSettings,
})
}
14 changes: 9 additions & 5 deletions backend/datasource/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,21 @@ type ServeOpts struct {
// StreamHandler for streaming queries.
backend.StreamHandler

// InstanceSettingsHandler for processing instance settings
backend.InstanceSettingsHandler

// GRPCSettings settings for gPRC.
GRPCSettings backend.GRPCSettings
}

// Serve starts serving the data source over gPRC.
func Serve(opts ServeOpts) error {
return backend.Serve(backend.ServeOpts{
CheckHealthHandler: opts.CheckHealthHandler,
CallResourceHandler: opts.CallResourceHandler,
QueryDataHandler: opts.QueryDataHandler,
StreamHandler: opts.StreamHandler,
GRPCSettings: opts.GRPCSettings,
CheckHealthHandler: opts.CheckHealthHandler,
CallResourceHandler: opts.CallResourceHandler,
QueryDataHandler: opts.QueryDataHandler,
StreamHandler: opts.StreamHandler,
InstanceSettingsHandler: opts.InstanceSettingsHandler,
GRPCSettings: opts.GRPCSettings,
})
}
59 changes: 59 additions & 0 deletions backend/grpcplugin/grpc_instance_settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package grpcplugin

import (
"context"

plugin "github.com/hashicorp/go-plugin"
"google.golang.org/grpc"

"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
)

// InstanceSettingsServer represents a data server.
type InstanceSettingsServer interface {
pluginv2.InstanceSettingsServer
}

// InstanceSettingsClient represents a data client.
type InstanceSettingsClient interface {
pluginv2.InstanceSettingsClient
}

// InstanceSettingsGRPCPlugin implements the GRPCPlugin interface from github.com/hashicorp/go-plugin.
type InstanceSettingsGRPCPlugin struct {
plugin.NetRPCUnsupportedPlugin
plugin.GRPCPlugin
InstanceSettingsServer InstanceSettingsServer
}

// GRPCServer registers p as a data gRPC server.
func (p *InstanceSettingsGRPCPlugin) GRPCServer(_ *plugin.GRPCBroker, s *grpc.Server) error {
pluginv2.RegisterInstanceSettingsServer(s, &instanceSettingsGRPCServer{
server: p.InstanceSettingsServer,
})
return nil
}

// GRPCClient returns c as a data gRPC client.
func (p *InstanceSettingsGRPCPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
return &instanceSettingsGRPCClient{client: pluginv2.NewInstanceSettingsClient(c)}, nil
}

type instanceSettingsGRPCServer struct {
server InstanceSettingsServer
}

func (s *instanceSettingsGRPCServer) ProcessInstanceSettings(ctx context.Context, req *pluginv2.ProcessInstanceSettingsRequest) (*pluginv2.ProcessInstanceSettingsResponse, error) {
return s.server.ProcessInstanceSettings(ctx, req)
}

type instanceSettingsGRPCClient struct {
client pluginv2.InstanceSettingsClient
}

func (c *instanceSettingsGRPCClient) ProcessInstanceSettings(ctx context.Context, req *pluginv2.ProcessInstanceSettingsRequest, opts ...grpc.CallOption) (*pluginv2.ProcessInstanceSettingsResponse, error) {
return c.client.ProcessInstanceSettings(ctx, req, opts...)
}

var _ InstanceSettingsServer = &instanceSettingsGRPCServer{}
var _ InstanceSettingsClient = &instanceSettingsGRPCClient{}
15 changes: 11 additions & 4 deletions backend/grpcplugin/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import (

// ServeOpts contains options for serving plugins.
type ServeOpts struct {
DiagnosticsServer DiagnosticsServer
ResourceServer ResourceServer
DataServer DataServer
StreamServer StreamServer
DiagnosticsServer DiagnosticsServer
ResourceServer ResourceServer
DataServer DataServer
StreamServer StreamServer
InstanceSettingsServer InstanceSettingsServer

// GRPCServer factory method for creating GRPC server.
// If nil, the default one will be used.
Expand Down Expand Up @@ -47,6 +48,12 @@ func Serve(opts ServeOpts) error {
}
}

if opts.InstanceSettingsServer != nil {
pSet["instanceSettings"] = &InstanceSettingsGRPCPlugin{
InstanceSettingsServer: opts.InstanceSettingsServer,
}
}

versionedPlugins[ProtocolVersion] = pSet

if opts.GRPCServer == nil {
Expand Down
9 changes: 8 additions & 1 deletion backend/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

// InstanceSettingsHandler validates resource changes
// This is EXPERIMENTAL and is a subject to change till Grafana 12
InstanceSettingsHandler InstanceSettingsHandler

// GRPCSettings settings for gPRC.
GRPCSettings GRPCSettings
}
Expand All @@ -74,6 +77,10 @@ func GRPCServeOpts(opts ServeOpts) grpcplugin.ServeOpts {
if opts.StreamHandler != nil {
pluginOpts.StreamServer = newStreamSDKAdapter(opts.StreamHandler)
}

if opts.InstanceSettingsHandler != nil {
pluginOpts.InstanceSettingsServer = newInstanceSettingsSDKAdapter(opts.InstanceSettingsHandler)
}
return pluginOpts
}

Expand Down