Live: api to show available pipeline entities (#39469)
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
co-authored by
Ryan McKinley
parent
a680162792
commit
0bf70b14fd
@@ -438,6 +438,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
// POST Live data to be processed according to channel rules.
|
||||
liveRoute.Post("/push/:streamId/:path", hs.LivePushGateway.HandlePath)
|
||||
liveRoute.Get("/channel-rules", routing.Wrap(hs.Live.HandleChannelRulesListHTTP), reqOrgAdmin)
|
||||
liveRoute.Get("/pipeline-entities", routing.Wrap(hs.Live.HandlePipelineEntitiesListHTTP), reqOrgAdmin)
|
||||
liveRoute.Get("/remote-write-backends", routing.Wrap(hs.Live.HandleRemoteWriteBackendsListHTTP), reqOrgAdmin)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -868,6 +868,94 @@ func (g *GrafanaLive) HandleChannelRulesListHTTP(c *models.ReqContext) response.
|
||||
})
|
||||
}
|
||||
|
||||
type configInfo struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
Example interface{} `json:"example,omitempty"`
|
||||
}
|
||||
|
||||
// HandlePipelineEntitiesListHTTP ...
|
||||
func (g *GrafanaLive) HandlePipelineEntitiesListHTTP(_ *models.ReqContext) response.Response {
|
||||
return response.JSON(http.StatusOK, util.DynMap{
|
||||
"subscribers": []configInfo{
|
||||
{
|
||||
Type: pipeline.SubscriberTypeBuiltin,
|
||||
Description: "list the fields that should be removed",
|
||||
// Example: pipeline.Bu{},
|
||||
},
|
||||
{
|
||||
Type: pipeline.SubscriberTypeManagedStream,
|
||||
Description: "list the fields that should be removed",
|
||||
},
|
||||
{
|
||||
Type: pipeline.SubscriberTypeMultiple,
|
||||
},
|
||||
},
|
||||
"outputs": []configInfo{
|
||||
{
|
||||
Type: pipeline.OutputTypeManagedStream,
|
||||
Description: "Only send schema when structure changes. Note this also requires a matching subscriber",
|
||||
Example: pipeline.ManagedStreamOutputConfig{},
|
||||
},
|
||||
{
|
||||
Type: pipeline.OutputTypeMultiple,
|
||||
Description: "Send the output to multiple destinations",
|
||||
Example: pipeline.MultipleOutputterConfig{},
|
||||
},
|
||||
{
|
||||
Type: pipeline.OutputTypeConditional,
|
||||
Description: "send to an output depending on frame values",
|
||||
Example: pipeline.ConditionalOutputConfig{},
|
||||
},
|
||||
{
|
||||
Type: pipeline.OutputTypeRedirect,
|
||||
},
|
||||
{
|
||||
Type: pipeline.OutputTypeThreshold,
|
||||
},
|
||||
{
|
||||
Type: pipeline.OutputTypeChangeLog,
|
||||
},
|
||||
{
|
||||
Type: pipeline.OutputTypeRemoteWrite,
|
||||
},
|
||||
},
|
||||
"converters": []configInfo{
|
||||
{
|
||||
Type: pipeline.ConverterTypeJsonAuto,
|
||||
},
|
||||
{
|
||||
Type: pipeline.ConverterTypeJsonExact,
|
||||
},
|
||||
{
|
||||
Type: pipeline.ConverterTypeInfluxAuto,
|
||||
Description: "accept influx line protocol",
|
||||
Example: pipeline.AutoInfluxConverterConfig{},
|
||||
},
|
||||
{
|
||||
Type: pipeline.ConverterTypeJsonFrame,
|
||||
},
|
||||
},
|
||||
"processors": []configInfo{
|
||||
{
|
||||
Type: pipeline.ProcessorTypeKeepFields,
|
||||
Description: "list the fields that should stay",
|
||||
Example: pipeline.KeepFieldsProcessorConfig{},
|
||||
},
|
||||
{
|
||||
Type: pipeline.ProcessorTypeDropFields,
|
||||
Description: "list the fields that should be removed",
|
||||
Example: pipeline.DropFieldsProcessorConfig{},
|
||||
},
|
||||
{
|
||||
Type: pipeline.ProcessorTypeMultiple,
|
||||
Description: "apply multiplie processors",
|
||||
Example: pipeline.MultipleProcessorConfig{},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// HandleRemoteWriteBackendsListHTTP ...
|
||||
func (g *GrafanaLive) HandleRemoteWriteBackendsListHTTP(c *models.ReqContext) response.Response {
|
||||
result, err := g.channelRuleStorage.ListRemoteWriteBackends(c.Req.Context(), c.OrgId)
|
||||
|
||||
@@ -8,5 +8,6 @@ import (
|
||||
|
||||
// ConditionChecker checks conditions in context of data.Frame being processed.
|
||||
type ConditionChecker interface {
|
||||
Type() string
|
||||
CheckCondition(ctx context.Context, frame *data.Frame) (bool, error)
|
||||
}
|
||||
|
||||
@@ -16,24 +16,30 @@ const (
|
||||
|
||||
// MultipleConditionChecker can check multiple conditions according to ConditionType.
|
||||
type MultipleConditionChecker struct {
|
||||
Type ConditionType
|
||||
Conditions []ConditionChecker
|
||||
ConditionType ConditionType
|
||||
Conditions []ConditionChecker
|
||||
}
|
||||
|
||||
func (m MultipleConditionChecker) CheckCondition(ctx context.Context, frame *data.Frame) (bool, error) {
|
||||
for _, c := range m.Conditions {
|
||||
ok, err := c.CheckCondition(ctx, frame)
|
||||
const ConditionCheckerTypeMultiple = "multiple"
|
||||
|
||||
func (c *MultipleConditionChecker) Type() string {
|
||||
return ConditionCheckerTypeMultiple
|
||||
}
|
||||
|
||||
func (c *MultipleConditionChecker) CheckCondition(ctx context.Context, frame *data.Frame) (bool, error) {
|
||||
for _, cond := range c.Conditions {
|
||||
ok, err := cond.CheckCondition(ctx, frame)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if ok && m.Type == ConditionAny {
|
||||
if ok && c.ConditionType == ConditionAny {
|
||||
return true, nil
|
||||
}
|
||||
if !ok && m.Type == ConditionAll {
|
||||
if !ok && c.ConditionType == ConditionAll {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
if m.Type == ConditionAny {
|
||||
if c.ConditionType == ConditionAny {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
@@ -41,5 +47,5 @@ func (m MultipleConditionChecker) CheckCondition(ctx context.Context, frame *dat
|
||||
|
||||
// NewMultipleConditionChecker creates new MultipleConditionChecker.
|
||||
func NewMultipleConditionChecker(conditionType ConditionType, conditions ...ConditionChecker) *MultipleConditionChecker {
|
||||
return &MultipleConditionChecker{Type: conditionType, Conditions: conditions}
|
||||
return &MultipleConditionChecker{ConditionType: conditionType, Conditions: conditions}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,16 @@ const (
|
||||
NumberCompareOpNe NumberCompareOp = "ne"
|
||||
)
|
||||
|
||||
func (f NumberCompareCondition) CheckCondition(_ context.Context, frame *data.Frame) (bool, error) {
|
||||
const ConditionCheckerTypeNumberCompare = "numberCompare"
|
||||
|
||||
func (c *NumberCompareCondition) Type() string {
|
||||
return ConditionCheckerTypeNumberCompare
|
||||
}
|
||||
|
||||
func (c *NumberCompareCondition) CheckCondition(_ context.Context, frame *data.Frame) (bool, error) {
|
||||
for _, field := range frame.Fields {
|
||||
// TODO: support other numeric types.
|
||||
if field.Name == f.FieldName && (field.Type() == data.FieldTypeNullableFloat64) {
|
||||
if field.Name == c.FieldName && (field.Type() == data.FieldTypeNullableFloat64) {
|
||||
value, ok := field.At(0).(*float64)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("unexpected value type: %T", field.At(0))
|
||||
@@ -38,21 +44,21 @@ func (f NumberCompareCondition) CheckCondition(_ context.Context, frame *data.Fr
|
||||
if value == nil {
|
||||
return false, nil
|
||||
}
|
||||
switch f.Op {
|
||||
switch c.Op {
|
||||
case NumberCompareOpGt:
|
||||
return *value > f.Value, nil
|
||||
return *value > c.Value, nil
|
||||
case NumberCompareOpGte:
|
||||
return *value >= f.Value, nil
|
||||
return *value >= c.Value, nil
|
||||
case NumberCompareOpLte:
|
||||
return *value <= f.Value, nil
|
||||
return *value <= c.Value, nil
|
||||
case NumberCompareOpLt:
|
||||
return *value < f.Value, nil
|
||||
return *value < c.Value, nil
|
||||
case NumberCompareOpEq:
|
||||
return *value == f.Value, nil
|
||||
return *value == c.Value, nil
|
||||
case NumberCompareOpNe:
|
||||
return *value != f.Value, nil
|
||||
return *value != c.Value, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unknown comparison operator: %s", f.Op)
|
||||
return false, fmt.Errorf("unknown comparison operator: %s", c.Op)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,11 +128,11 @@ func (f *StorageRuleBuilder) extractSubscriber(config *SubscriberConfig) (Subscr
|
||||
}
|
||||
missingConfiguration := fmt.Errorf("missing configuration for %s", config.Type)
|
||||
switch config.Type {
|
||||
case "builtin":
|
||||
case SubscriberTypeBuiltin:
|
||||
return NewBuiltinSubscriber(f.ChannelHandlerGetter), nil
|
||||
case "managedStream":
|
||||
case SubscriberTypeManagedStream:
|
||||
return NewManagedStreamSubscriber(f.ManagedStream), nil
|
||||
case "multiple":
|
||||
case SubscriberTypeMultiple:
|
||||
if config.MultipleSubscriberConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
@@ -157,22 +157,22 @@ func (f *StorageRuleBuilder) extractConverter(config *ConverterConfig) (Converte
|
||||
}
|
||||
missingConfiguration := fmt.Errorf("missing configuration for %s", config.Type)
|
||||
switch config.Type {
|
||||
case "jsonAuto":
|
||||
case ConverterTypeJsonAuto:
|
||||
if config.AutoJsonConverterConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
return NewAutoJsonConverter(*config.AutoJsonConverterConfig), nil
|
||||
case "jsonExact":
|
||||
case ConverterTypeJsonExact:
|
||||
if config.ExactJsonConverterConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
return NewExactJsonConverter(*config.ExactJsonConverterConfig), nil
|
||||
case "jsonFrame":
|
||||
case ConverterTypeJsonFrame:
|
||||
if config.JsonFrameConverterConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
return NewJsonFrameConverter(*config.JsonFrameConverterConfig), nil
|
||||
case "influxAuto":
|
||||
case ConverterTypeInfluxAuto:
|
||||
if config.AutoInfluxConverterConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
@@ -188,17 +188,17 @@ func (f *StorageRuleBuilder) extractProcessor(config *ProcessorConfig) (Processo
|
||||
}
|
||||
missingConfiguration := fmt.Errorf("missing configuration for %s", config.Type)
|
||||
switch config.Type {
|
||||
case "dropFields":
|
||||
case ProcessorTypeDropFields:
|
||||
if config.DropFieldsProcessorConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
return NewDropFieldsProcessor(*config.DropFieldsProcessorConfig), nil
|
||||
case "keepFields":
|
||||
case ProcessorTypeKeepFields:
|
||||
if config.KeepFieldsProcessorConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
return NewKeepFieldsProcessor(*config.KeepFieldsProcessorConfig), nil
|
||||
case "multiple":
|
||||
case ProcessorTypeMultiple:
|
||||
if config.MultipleProcessorConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
@@ -223,13 +223,13 @@ func (f *StorageRuleBuilder) extractConditionChecker(config *ConditionCheckerCon
|
||||
}
|
||||
missingConfiguration := fmt.Errorf("missing configuration for %s", config.Type)
|
||||
switch config.Type {
|
||||
case "numberCompare":
|
||||
case ConditionCheckerTypeNumberCompare:
|
||||
if config.NumberCompareConditionConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
c := *config.NumberCompareConditionConfig
|
||||
return NewNumberCompareCondition(c.FieldName, c.Op, c.Value), nil
|
||||
case "multiple":
|
||||
case ConditionCheckerTypeMultiple:
|
||||
var conditions []ConditionChecker
|
||||
if config.MultipleConditionCheckerConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
@@ -254,12 +254,12 @@ func (f *StorageRuleBuilder) extractOutputter(config *OutputterConfig, remoteWri
|
||||
}
|
||||
missingConfiguration := fmt.Errorf("missing configuration for %s", config.Type)
|
||||
switch config.Type {
|
||||
case "redirect":
|
||||
case OutputTypeRedirect:
|
||||
if config.RedirectOutputConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
return NewRedirectOutput(*config.RedirectOutputConfig), nil
|
||||
case "multiple":
|
||||
case OutputTypeMultiple:
|
||||
if config.MultipleOutputterConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
@@ -273,11 +273,11 @@ func (f *StorageRuleBuilder) extractOutputter(config *OutputterConfig, remoteWri
|
||||
outputters = append(outputters, outputter)
|
||||
}
|
||||
return NewMultipleOutput(outputters...), nil
|
||||
case "managedStream":
|
||||
case OutputTypeManagedStream:
|
||||
return NewManagedStreamOutput(f.ManagedStream), nil
|
||||
case "localSubscribers":
|
||||
case OutputTypeLocalSubscribers:
|
||||
return NewLocalSubscribersOutput(f.Node), nil
|
||||
case "conditional":
|
||||
case OutputTypeConditional:
|
||||
if config.ConditionalOutputConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
@@ -290,12 +290,12 @@ func (f *StorageRuleBuilder) extractOutputter(config *OutputterConfig, remoteWri
|
||||
return nil, err
|
||||
}
|
||||
return NewConditionalOutput(condition, outputter), nil
|
||||
case "threshold":
|
||||
case OutputTypeThreshold:
|
||||
if config.ThresholdOutputConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
return NewThresholdOutput(f.FrameStorage, *config.ThresholdOutputConfig), nil
|
||||
case "remoteWrite":
|
||||
case OutputTypeRemoteWrite:
|
||||
if config.RemoteWriteOutputConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
@@ -304,7 +304,7 @@ func (f *StorageRuleBuilder) extractOutputter(config *OutputterConfig, remoteWri
|
||||
return nil, fmt.Errorf("unknown remote write backend uid: %s", config.RemoteWriteOutputConfig.UID)
|
||||
}
|
||||
return NewRemoteWriteOutput(*remoteWriteConfig), nil
|
||||
case "changeLog":
|
||||
case OutputTypeChangeLog:
|
||||
if config.ChangeLogOutputConfig == nil {
|
||||
return nil, missingConfiguration
|
||||
}
|
||||
|
||||
@@ -22,8 +22,14 @@ func NewAutoInfluxConverter(config AutoInfluxConverterConfig) *AutoInfluxConvert
|
||||
return &AutoInfluxConverter{config: config, converter: convert.NewConverter()}
|
||||
}
|
||||
|
||||
func (i AutoInfluxConverter) Convert(_ context.Context, vars Vars, body []byte) ([]*ChannelFrame, error) {
|
||||
frameWrappers, err := i.converter.Convert(body, i.config.FrameFormat)
|
||||
const ConverterTypeInfluxAuto = "influxAuto"
|
||||
|
||||
func (c *AutoInfluxConverter) Type() string {
|
||||
return ConverterTypeInfluxAuto
|
||||
}
|
||||
|
||||
func (c *AutoInfluxConverter) Convert(_ context.Context, vars Vars, body []byte) ([]*ChannelFrame, error) {
|
||||
frameWrappers, err := c.converter.Convert(body, c.config.FrameFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@ func NewAutoJsonConverter(c AutoJsonConverterConfig) *AutoJsonConverter {
|
||||
return &AutoJsonConverter{config: c}
|
||||
}
|
||||
|
||||
const ConverterTypeJsonAuto = "jsonAuto"
|
||||
|
||||
func (c *AutoJsonConverter) Type() string {
|
||||
return ConverterTypeJsonAuto
|
||||
}
|
||||
|
||||
// Automatic conversion works this way:
|
||||
// * Time added automatically
|
||||
// * Nulls dropped
|
||||
|
||||
@@ -27,6 +27,12 @@ func NewExactJsonConverter(c ExactJsonConverterConfig) *ExactJsonConverter {
|
||||
return &ExactJsonConverter{config: c}
|
||||
}
|
||||
|
||||
const ConverterTypeJsonExact = "jsonExact"
|
||||
|
||||
func (c *ExactJsonConverter) Type() string {
|
||||
return ConverterTypeJsonExact
|
||||
}
|
||||
|
||||
func (c *ExactJsonConverter) Convert(_ context.Context, vars Vars, body []byte) ([]*ChannelFrame, error) {
|
||||
//obj, err := oj.Parse(body)
|
||||
//if err != nil {
|
||||
|
||||
@@ -20,6 +20,12 @@ func NewJsonFrameConverter(c JsonFrameConverterConfig) *JsonFrameConverter {
|
||||
}
|
||||
}
|
||||
|
||||
const ConverterTypeJsonFrame = "jsonFrame"
|
||||
|
||||
func (c *JsonFrameConverter) Type() string {
|
||||
return ConverterTypeJsonFrame
|
||||
}
|
||||
|
||||
func (c *JsonFrameConverter) Convert(_ context.Context, _ Vars, body []byte) ([]*ChannelFrame, error) {
|
||||
var frame data.Frame
|
||||
err := json.Unmarshal(body, &frame)
|
||||
|
||||
@@ -24,13 +24,19 @@ func NewChangeLogOutput(frameStorage FrameGetSetter, config ChangeLogOutputConfi
|
||||
return &ChangeLogOutput{frameStorage: frameStorage, config: config}
|
||||
}
|
||||
|
||||
func (l ChangeLogOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
previousFrame, previousFrameOK, err := l.frameStorage.Get(vars.OrgID, l.config.Channel)
|
||||
const OutputTypeChangeLog = "changeLog"
|
||||
|
||||
func (out *ChangeLogOutput) Type() string {
|
||||
return OutputTypeChangeLog
|
||||
}
|
||||
|
||||
func (out *ChangeLogOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
previousFrame, previousFrameOK, err := out.frameStorage.Get(vars.OrgID, out.config.Channel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fieldName := l.config.FieldName
|
||||
fieldName := out.config.FieldName
|
||||
|
||||
previousFrameFieldIndex := -1
|
||||
if previousFrameOK {
|
||||
@@ -78,15 +84,15 @@ func (l ChangeLogOutput) Output(_ context.Context, vars OutputVars, frame *data.
|
||||
|
||||
if fTime.Len() > 0 {
|
||||
changeFrame := data.NewFrame("change", fTime, f1, f2)
|
||||
err := l.frameStorage.Set(vars.OrgID, l.config.Channel, frame)
|
||||
err := out.frameStorage.Set(vars.OrgID, out.config.Channel, frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*ChannelFrame{{
|
||||
Channel: l.config.Channel,
|
||||
Channel: out.config.Channel,
|
||||
Frame: changeFrame,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
return nil, l.frameStorage.Set(vars.OrgID, l.config.Channel, frame)
|
||||
return nil, out.frameStorage.Set(vars.OrgID, out.config.Channel, frame)
|
||||
}
|
||||
|
||||
@@ -15,13 +15,19 @@ func NewConditionalOutput(condition ConditionChecker, outputter Outputter) *Cond
|
||||
return &ConditionalOutput{Condition: condition, Outputter: outputter}
|
||||
}
|
||||
|
||||
func (l ConditionalOutput) Output(ctx context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
ok, err := l.Condition.CheckCondition(ctx, frame)
|
||||
const OutputTypeConditional = "conditional"
|
||||
|
||||
func (out *ConditionalOutput) Type() string {
|
||||
return OutputTypeConditional
|
||||
}
|
||||
|
||||
func (out ConditionalOutput) Output(ctx context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
ok, err := out.Condition.CheckCondition(ctx, frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return l.Outputter.Output(ctx, vars, frame)
|
||||
return out.Outputter.Output(ctx, vars, frame)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,13 @@ func NewLocalSubscribersOutput(node *centrifuge.Node) *LocalSubscribersOutput {
|
||||
return &LocalSubscribersOutput{node: node}
|
||||
}
|
||||
|
||||
func (l *LocalSubscribersOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
const OutputTypeLocalSubscribers = "localSubscribers"
|
||||
|
||||
func (out *LocalSubscribersOutput) Type() string {
|
||||
return OutputTypeLocalSubscribers
|
||||
}
|
||||
|
||||
func (out *LocalSubscribersOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
channelID := vars.Channel
|
||||
channel := orgchannel.PrependOrgID(vars.OrgID, channelID)
|
||||
frameJSON, err := json.Marshal(frame)
|
||||
@@ -30,7 +36,7 @@ func (l *LocalSubscribersOutput) Output(_ context.Context, vars OutputVars, fram
|
||||
pub := ¢rifuge.Publication{
|
||||
Data: frameJSON,
|
||||
}
|
||||
err = l.node.Hub().BroadcastPublication(channel, pub, centrifuge.StreamPosition{})
|
||||
err = out.node.Hub().BroadcastPublication(channel, pub, centrifuge.StreamPosition{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error publishing %s: %w", string(frameJSON), err)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,14 @@ func NewManagedStreamOutput(managedStream *managedstream.Runner) *ManagedStreamO
|
||||
return &ManagedStreamOutput{managedStream: managedStream}
|
||||
}
|
||||
|
||||
func (l *ManagedStreamOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
stream, err := l.managedStream.GetOrCreateStream(vars.OrgID, vars.Scope, vars.Namespace)
|
||||
const OutputTypeManagedStream = "managedStream"
|
||||
|
||||
func (out *ManagedStreamOutput) Type() string {
|
||||
return OutputTypeManagedStream
|
||||
}
|
||||
|
||||
func (out *ManagedStreamOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
stream, err := out.managedStream.GetOrCreateStream(vars.OrgID, vars.Scope, vars.Namespace)
|
||||
if err != nil {
|
||||
logger.Error("Error getting stream", "error", err)
|
||||
return nil, err
|
||||
|
||||
@@ -12,9 +12,15 @@ type MultipleOutput struct {
|
||||
Outputters []Outputter
|
||||
}
|
||||
|
||||
func (m MultipleOutput) Output(ctx context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
const OutputTypeMultiple = "multiple"
|
||||
|
||||
func (out *MultipleOutput) Type() string {
|
||||
return OutputTypeMultiple
|
||||
}
|
||||
|
||||
func (out MultipleOutput) Output(ctx context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
var frames []*ChannelFrame
|
||||
for _, out := range m.Outputters {
|
||||
for _, out := range out.Outputters {
|
||||
f, err := out.Output(ctx, vars, frame)
|
||||
if err != nil {
|
||||
logger.Error("Error outputting frame", "error", err)
|
||||
|
||||
@@ -22,12 +22,18 @@ func NewRedirectOutput(config RedirectOutputConfig) *RedirectOutput {
|
||||
return &RedirectOutput{config: config}
|
||||
}
|
||||
|
||||
func (l *RedirectOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
if vars.Channel == l.config.Channel {
|
||||
return nil, fmt.Errorf("redirect to the same channel: %s", l.config.Channel)
|
||||
const OutputTypeRedirect = "redirect"
|
||||
|
||||
func (out *RedirectOutput) Type() string {
|
||||
return OutputTypeRedirect
|
||||
}
|
||||
|
||||
func (out *RedirectOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
if vars.Channel == out.config.Channel {
|
||||
return nil, fmt.Errorf("redirect to the same channel: %s", out.config.Channel)
|
||||
}
|
||||
return []*ChannelFrame{{
|
||||
Channel: l.config.Channel,
|
||||
Channel: out.config.Channel,
|
||||
Frame: frame,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
@@ -32,8 +32,14 @@ func NewRemoteWriteOutput(config RemoteWriteConfig) *RemoteWriteOutput {
|
||||
}
|
||||
}
|
||||
|
||||
func (r RemoteWriteOutput) Output(_ context.Context, _ OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
if r.config.Endpoint == "" {
|
||||
const OutputTypeRemoteWrite = "remoteWrite"
|
||||
|
||||
func (out *RemoteWriteOutput) Type() string {
|
||||
return OutputTypeRemoteWrite
|
||||
}
|
||||
|
||||
func (out *RemoteWriteOutput) Output(_ context.Context, _ OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
if out.config.Endpoint == "" {
|
||||
logger.Debug("Skip sending to remote write: no url")
|
||||
return nil, nil
|
||||
}
|
||||
@@ -45,8 +51,8 @@ func (r RemoteWriteOutput) Output(_ context.Context, _ OutputVars, frame *data.F
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Debug("Sending to remote write endpoint", "url", r.config.Endpoint, "bodyLength", len(remoteWriteData))
|
||||
req, err := http.NewRequest(http.MethodPost, r.config.Endpoint, bytes.NewReader(remoteWriteData))
|
||||
logger.Debug("Sending to remote write endpoint", "url", out.config.Endpoint, "bodyLength", len(remoteWriteData))
|
||||
req, err := http.NewRequest(http.MethodPost, out.config.Endpoint, bytes.NewReader(remoteWriteData))
|
||||
if err != nil {
|
||||
logger.Error("Error constructing remote write request", "error", err)
|
||||
return nil, err
|
||||
@@ -54,10 +60,10 @@ func (r RemoteWriteOutput) Output(_ context.Context, _ OutputVars, frame *data.F
|
||||
req.Header.Set("Content-Type", "application/x-protobuf")
|
||||
req.Header.Set("Content-Encoding", "snappy")
|
||||
req.Header.Set("X-Prometheus-Remote-Write-Version", "0.1.0")
|
||||
req.SetBasicAuth(r.config.User, r.config.Password)
|
||||
req.SetBasicAuth(out.config.User, out.config.Password)
|
||||
|
||||
started := time.Now()
|
||||
resp, err := r.httpClient.Do(req)
|
||||
resp, err := out.httpClient.Do(req)
|
||||
if err != nil {
|
||||
logger.Error("Error sending remote write request", "error", err)
|
||||
return nil, err
|
||||
@@ -67,6 +73,6 @@ func (r RemoteWriteOutput) Output(_ context.Context, _ OutputVars, frame *data.F
|
||||
logger.Error("Unexpected response code from remote write endpoint", "code", resp.StatusCode)
|
||||
return nil, errors.New("unexpected response code from remote write endpoint")
|
||||
}
|
||||
logger.Debug("Successfully sent to remote write endpoint", "url", r.config.Endpoint, "elapsed", time.Since(started))
|
||||
logger.Debug("Successfully sent to remote write endpoint", "url", out.config.Endpoint, "elapsed", time.Since(started))
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -31,15 +31,21 @@ func NewThresholdOutput(frameStorage FrameGetSetter, config ThresholdOutputConfi
|
||||
return &ThresholdOutput{frameStorage: frameStorage, config: config}
|
||||
}
|
||||
|
||||
func (l *ThresholdOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
const OutputTypeThreshold = "threshold"
|
||||
|
||||
func (out *ThresholdOutput) Type() string {
|
||||
return OutputTypeThreshold
|
||||
}
|
||||
|
||||
func (out *ThresholdOutput) Output(_ context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
if frame == nil {
|
||||
return nil, nil
|
||||
}
|
||||
previousFrame, previousFrameOk, err := l.frameStorage.Get(vars.OrgID, l.config.Channel)
|
||||
previousFrame, previousFrameOk, err := out.frameStorage.Get(vars.OrgID, out.config.Channel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fieldName := l.config.FieldName
|
||||
fieldName := out.config.FieldName
|
||||
|
||||
currentFrameFieldIndex := -1
|
||||
for i, f := range frame.Fields {
|
||||
@@ -136,15 +142,15 @@ func (l *ThresholdOutput) Output(_ context.Context, vars OutputVars, frame *data
|
||||
|
||||
if fTime.Len() > 0 {
|
||||
stateFrame := data.NewFrame("state", fTime, f1, f2, f3)
|
||||
err := l.frameStorage.Set(vars.OrgID, l.config.Channel, frame)
|
||||
err := out.frameStorage.Set(vars.OrgID, out.config.Channel, frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []*ChannelFrame{{
|
||||
Channel: l.config.Channel,
|
||||
Channel: out.config.Channel,
|
||||
Frame: stateFrame,
|
||||
}}, nil
|
||||
}
|
||||
|
||||
return nil, l.frameStorage.Set(vars.OrgID, l.config.Channel, frame)
|
||||
return nil, out.frameStorage.Set(vars.OrgID, out.config.Channel, frame)
|
||||
}
|
||||
|
||||
@@ -47,22 +47,26 @@ type OutputVars struct {
|
||||
// of resulting slice will be then individually processed and outputted
|
||||
// according configured channel rules.
|
||||
type Converter interface {
|
||||
Type() string
|
||||
Convert(ctx context.Context, vars Vars, body []byte) ([]*ChannelFrame, error)
|
||||
}
|
||||
|
||||
// Processor can modify data.Frame in a custom way before it will be outputted.
|
||||
type Processor interface {
|
||||
Type() string
|
||||
Process(ctx context.Context, vars ProcessorVars, frame *data.Frame) (*data.Frame, error)
|
||||
}
|
||||
|
||||
// Outputter outputs data.Frame to a custom destination. Or simply
|
||||
// do nothing if some conditions not met.
|
||||
type Outputter interface {
|
||||
Type() string
|
||||
Output(ctx context.Context, vars OutputVars, frame *data.Frame) ([]*ChannelFrame, error)
|
||||
}
|
||||
|
||||
// Subscriber can handle channel subscribe events.
|
||||
type Subscriber interface {
|
||||
Type() string
|
||||
Subscribe(ctx context.Context, vars Vars) (models.SubscribeReply, backend.SubscribeStreamStatus, error)
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ type testRuleGetter struct {
|
||||
rules map[string]*LiveChannelRule
|
||||
}
|
||||
|
||||
func (t *testRuleGetter) Get(orgID int64, channel string) (*LiveChannelRule, bool, error) {
|
||||
func (t *testRuleGetter) Get(_ int64, channel string) (*LiveChannelRule, bool, error) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
rule, ok := t.rules[channel]
|
||||
@@ -48,12 +48,20 @@ type testConverter struct {
|
||||
frame *data.Frame
|
||||
}
|
||||
|
||||
func (t *testConverter) Type() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
func (t *testConverter) Convert(_ context.Context, _ Vars, _ []byte) ([]*ChannelFrame, error) {
|
||||
return []*ChannelFrame{{Channel: t.channel, Frame: t.frame}}, nil
|
||||
}
|
||||
|
||||
type testProcessor struct{}
|
||||
|
||||
func (t *testProcessor) Type() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
func (t *testProcessor) Process(_ context.Context, _ ProcessorVars, frame *data.Frame) (*data.Frame, error) {
|
||||
return frame, nil
|
||||
}
|
||||
@@ -63,6 +71,10 @@ type testOutputter struct {
|
||||
frame *data.Frame
|
||||
}
|
||||
|
||||
func (t *testOutputter) Type() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
func (t *testOutputter) Output(_ context.Context, _ OutputVars, frame *data.Frame) ([]*ChannelFrame, error) {
|
||||
if t.err != nil {
|
||||
return nil, t.err
|
||||
|
||||
@@ -23,8 +23,14 @@ func NewDropFieldsProcessor(config DropFieldsProcessorConfig) *DropFieldsProcess
|
||||
return &DropFieldsProcessor{config: config}
|
||||
}
|
||||
|
||||
func (d DropFieldsProcessor) Process(_ context.Context, _ ProcessorVars, frame *data.Frame) (*data.Frame, error) {
|
||||
for _, f := range d.config.FieldNames {
|
||||
const ProcessorTypeDropFields = "dropFields"
|
||||
|
||||
func (p *DropFieldsProcessor) Type() string {
|
||||
return ProcessorTypeDropFields
|
||||
}
|
||||
|
||||
func (p *DropFieldsProcessor) Process(_ context.Context, _ ProcessorVars, frame *data.Frame) (*data.Frame, error) {
|
||||
for _, f := range p.config.FieldNames {
|
||||
inner:
|
||||
for i, field := range frame.Fields {
|
||||
if f == field.Name {
|
||||
|
||||
@@ -28,10 +28,16 @@ func stringInSlice(str string, slice []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (d KeepFieldsProcessor) Process(_ context.Context, _ ProcessorVars, frame *data.Frame) (*data.Frame, error) {
|
||||
const ProcessorTypeKeepFields = "keepFields"
|
||||
|
||||
func (p *KeepFieldsProcessor) Type() string {
|
||||
return ProcessorTypeKeepFields
|
||||
}
|
||||
|
||||
func (p *KeepFieldsProcessor) Process(_ context.Context, _ ProcessorVars, frame *data.Frame) (*data.Frame, error) {
|
||||
var fieldsToKeep []*data.Field
|
||||
for _, field := range frame.Fields {
|
||||
if stringInSlice(field.Name, d.config.FieldNames) {
|
||||
if stringInSlice(field.Name, p.config.FieldNames) {
|
||||
fieldsToKeep = append(fieldsToKeep, field)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,14 @@ type MultipleProcessor struct {
|
||||
Processors []Processor
|
||||
}
|
||||
|
||||
func (m MultipleProcessor) Process(ctx context.Context, vars ProcessorVars, frame *data.Frame) (*data.Frame, error) {
|
||||
for _, p := range m.Processors {
|
||||
const ProcessorTypeMultiple = "multiple"
|
||||
|
||||
func (p *MultipleProcessor) Type() string {
|
||||
return ProcessorTypeMultiple
|
||||
}
|
||||
|
||||
func (p *MultipleProcessor) Process(ctx context.Context, vars ProcessorVars, frame *data.Frame) (*data.Frame, error) {
|
||||
for _, p := range p.Processors {
|
||||
var err error
|
||||
frame, err = p.Process(ctx, vars, frame)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,16 +18,22 @@ type ChannelHandlerGetter interface {
|
||||
GetChannelHandler(user *models.SignedInUser, channel string) (models.ChannelHandler, live.Channel, error)
|
||||
}
|
||||
|
||||
const SubscriberTypeBuiltin = "builtin"
|
||||
|
||||
func NewBuiltinSubscriber(channelHandlerGetter ChannelHandlerGetter) *BuiltinSubscriber {
|
||||
return &BuiltinSubscriber{channelHandlerGetter: channelHandlerGetter}
|
||||
}
|
||||
|
||||
func (m *BuiltinSubscriber) Subscribe(ctx context.Context, vars Vars) (models.SubscribeReply, backend.SubscribeStreamStatus, error) {
|
||||
func (s *BuiltinSubscriber) Type() string {
|
||||
return SubscriberTypeBuiltin
|
||||
}
|
||||
|
||||
func (s *BuiltinSubscriber) Subscribe(ctx context.Context, vars Vars) (models.SubscribeReply, backend.SubscribeStreamStatus, error) {
|
||||
u, ok := livecontext.GetContextSignedUser(ctx)
|
||||
if !ok {
|
||||
return models.SubscribeReply{}, backend.SubscribeStreamStatusPermissionDenied, nil
|
||||
}
|
||||
handler, _, err := m.channelHandlerGetter.GetChannelHandler(u, vars.Channel)
|
||||
handler, _, err := s.channelHandlerGetter.GetChannelHandler(u, vars.Channel)
|
||||
if err != nil {
|
||||
return models.SubscribeReply{}, 0, err
|
||||
}
|
||||
|
||||
@@ -14,12 +14,18 @@ type ManagedStreamSubscriber struct {
|
||||
managedStream *managedstream.Runner
|
||||
}
|
||||
|
||||
const SubscriberTypeManagedStream = "managedStream"
|
||||
|
||||
func NewManagedStreamSubscriber(managedStream *managedstream.Runner) *ManagedStreamSubscriber {
|
||||
return &ManagedStreamSubscriber{managedStream: managedStream}
|
||||
}
|
||||
|
||||
func (m *ManagedStreamSubscriber) Subscribe(ctx context.Context, vars Vars) (models.SubscribeReply, backend.SubscribeStreamStatus, error) {
|
||||
stream, err := m.managedStream.GetOrCreateStream(vars.OrgID, vars.Scope, vars.Namespace)
|
||||
func (s *ManagedStreamSubscriber) Type() string {
|
||||
return SubscriberTypeManagedStream
|
||||
}
|
||||
|
||||
func (s *ManagedStreamSubscriber) Subscribe(ctx context.Context, vars Vars) (models.SubscribeReply, backend.SubscribeStreamStatus, error) {
|
||||
stream, err := s.managedStream.GetOrCreateStream(vars.OrgID, vars.Scope, vars.Namespace)
|
||||
if err != nil {
|
||||
logger.Error("Error getting managed stream", "error", err)
|
||||
return models.SubscribeReply{}, 0, err
|
||||
|
||||
@@ -15,10 +15,16 @@ func NewMultipleSubscriber(subscribers ...Subscriber) *MultipleSubscriber {
|
||||
return &MultipleSubscriber{Subscribers: subscribers}
|
||||
}
|
||||
|
||||
func (m *MultipleSubscriber) Subscribe(ctx context.Context, vars Vars) (models.SubscribeReply, backend.SubscribeStreamStatus, error) {
|
||||
const SubscriberTypeMultiple = "multiple"
|
||||
|
||||
func (s *MultipleSubscriber) Type() string {
|
||||
return SubscriberTypeMultiple
|
||||
}
|
||||
|
||||
func (s *MultipleSubscriber) Subscribe(ctx context.Context, vars Vars) (models.SubscribeReply, backend.SubscribeStreamStatus, error) {
|
||||
finalReply := models.SubscribeReply{}
|
||||
|
||||
for _, s := range m.Subscribers {
|
||||
for _, s := range s.Subscribers {
|
||||
reply, status, err := s.Subscribe(ctx, vars)
|
||||
if err != nil {
|
||||
return models.SubscribeReply{}, 0, err
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type PermissionDeniedSubscriber struct{}
|
||||
|
||||
func NewPermissionDeniedSubscriber() *PermissionDeniedSubscriber {
|
||||
return &PermissionDeniedSubscriber{}
|
||||
}
|
||||
|
||||
func (m *PermissionDeniedSubscriber) Subscribe(ctx context.Context, vars Vars) (models.SubscribeReply, backend.SubscribeStreamStatus, error) {
|
||||
return models.SubscribeReply{}, backend.SubscribeStreamStatusPermissionDenied, nil
|
||||
}
|
||||
Reference in New Issue
Block a user