Live: array for Processor, Outputter and Subscriber in channel rule top level (#39677)
This commit is contained in:
+126
-20
@@ -192,6 +192,22 @@ func ProvideService(plugCtxProvider *plugincontext.Provider, cfg *setting.Cfg, r
|
||||
}
|
||||
}
|
||||
channelRuleGetter := pipeline.NewCacheSegmentedTree(builder)
|
||||
|
||||
// Pre-build/validate channel rules for all organizations on start.
|
||||
// This can be unreasonable to have in production scenario with many
|
||||
// organizations.
|
||||
query := &models.SearchOrgsQuery{}
|
||||
err := sqlstore.SearchOrgs(query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get org list: %w", err)
|
||||
}
|
||||
for _, org := range query.Result {
|
||||
_, _, err := channelRuleGetter.Get(org.Id, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building channel rules for org %d: %w", org.Id, err)
|
||||
}
|
||||
}
|
||||
|
||||
g.Pipeline, err = pipeline.New(channelRuleGetter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -537,28 +553,47 @@ func (g *GrafanaLive) handleOnSubscribe(client *centrifuge.Client, e centrifuge.
|
||||
|
||||
var reply models.SubscribeReply
|
||||
var status backend.SubscribeStreamStatus
|
||||
var ruleFound bool
|
||||
|
||||
var subscribeRuleFound bool
|
||||
if g.Pipeline != nil {
|
||||
rule, ok, err := g.Pipeline.Get(user.OrgId, channel)
|
||||
if err != nil {
|
||||
logger.Error("Error getting channel rule", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err)
|
||||
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal
|
||||
}
|
||||
if ok && rule.Subscriber != nil {
|
||||
subscribeRuleFound = true
|
||||
var err error
|
||||
reply, status, err = rule.Subscriber.Subscribe(client.Context(), pipeline.Vars{
|
||||
OrgID: orgID,
|
||||
Channel: channel,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("Error channel rule subscribe", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err)
|
||||
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal
|
||||
ruleFound = ok
|
||||
if ok {
|
||||
if rule.SubscribeAuth != nil {
|
||||
ok, err := rule.SubscribeAuth.CanSubscribe(client.Context(), user)
|
||||
if err != nil {
|
||||
logger.Error("Error checking subscribe permissions", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err)
|
||||
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal
|
||||
}
|
||||
if !ok {
|
||||
// using HTTP error codes for WS errors too.
|
||||
code, text := subscribeStatusToHTTPError(backend.SubscribeStreamStatusPermissionDenied)
|
||||
return centrifuge.SubscribeReply{}, ¢rifuge.Error{Code: uint32(code), Message: text}
|
||||
}
|
||||
}
|
||||
if len(rule.Subscribers) > 0 {
|
||||
var err error
|
||||
for _, sub := range rule.Subscribers {
|
||||
reply, status, err = sub.Subscribe(client.Context(), pipeline.Vars{
|
||||
OrgID: orgID,
|
||||
Channel: channel,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("Error channel rule subscribe", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err)
|
||||
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal
|
||||
}
|
||||
if status != backend.SubscribeStreamStatusOK {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !subscribeRuleFound {
|
||||
if !ruleFound {
|
||||
handler, addr, err := g.GetChannelHandler(user, channel)
|
||||
if err != nil {
|
||||
if errors.Is(err, live.ErrInvalidChannelID) {
|
||||
@@ -615,6 +650,42 @@ func (g *GrafanaLive) handleOnPublish(client *centrifuge.Client, e centrifuge.Pu
|
||||
return centrifuge.PublishReply{}, centrifuge.ErrorPermissionDenied
|
||||
}
|
||||
|
||||
if g.Pipeline != nil {
|
||||
rule, ok, err := g.Pipeline.Get(user.OrgId, channel)
|
||||
if err != nil {
|
||||
logger.Error("Error getting channel rule", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err)
|
||||
return centrifuge.PublishReply{}, centrifuge.ErrorInternal
|
||||
}
|
||||
if ok {
|
||||
if rule.PublishAuth != nil {
|
||||
ok, err := rule.PublishAuth.CanPublish(client.Context(), user)
|
||||
if err != nil {
|
||||
logger.Error("Error checking publish permissions", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err)
|
||||
return centrifuge.PublishReply{}, centrifuge.ErrorInternal
|
||||
}
|
||||
if !ok {
|
||||
// using HTTP error codes for WS errors too.
|
||||
code, text := publishStatusToHTTPError(backend.PublishStreamStatusPermissionDenied)
|
||||
return centrifuge.PublishReply{}, ¢rifuge.Error{Code: uint32(code), Message: text}
|
||||
}
|
||||
} else {
|
||||
if !user.HasRole(models.ROLE_ADMIN) {
|
||||
// using HTTP error codes for WS errors too.
|
||||
code, text := publishStatusToHTTPError(backend.PublishStreamStatusPermissionDenied)
|
||||
return centrifuge.PublishReply{}, ¢rifuge.Error{Code: uint32(code), Message: text}
|
||||
}
|
||||
}
|
||||
_, err := g.Pipeline.ProcessInput(client.Context(), user.OrgId, channel, e.Data)
|
||||
if err != nil {
|
||||
logger.Error("Error processing input", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err)
|
||||
return centrifuge.PublishReply{}, centrifuge.ErrorInternal
|
||||
}
|
||||
return centrifuge.PublishReply{
|
||||
Result: ¢rifuge.PublishResult{},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
handler, addr, err := g.GetChannelHandler(user, channel)
|
||||
if err != nil {
|
||||
if errors.Is(err, live.ErrInvalidChannelID) {
|
||||
@@ -633,6 +704,7 @@ func (g *GrafanaLive) handleOnPublish(client *centrifuge.Client, e centrifuge.Pu
|
||||
logger.Error("Error calling channel handler publish", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err)
|
||||
return centrifuge.PublishReply{}, centrifuge.ErrorInternal
|
||||
}
|
||||
|
||||
if status != backend.PublishStreamStatusOK {
|
||||
// using HTTP error codes for WS errors too.
|
||||
code, text := publishStatusToHTTPError(status)
|
||||
@@ -812,6 +884,38 @@ func (g *GrafanaLive) HandleHTTPPublish(ctx *models.ReqContext, cmd dtos.LivePub
|
||||
}
|
||||
|
||||
logger.Debug("Publish API cmd", "user", ctx.SignedInUser.UserId, "channel", cmd.Channel)
|
||||
user := ctx.SignedInUser
|
||||
channel := cmd.Channel
|
||||
|
||||
if g.Pipeline != nil {
|
||||
rule, ok, err := g.Pipeline.Get(user.OrgId, channel)
|
||||
if err != nil {
|
||||
logger.Error("Error getting channel rule", "user", user, "channel", channel, "error", err)
|
||||
return response.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), nil)
|
||||
}
|
||||
if ok {
|
||||
if rule.PublishAuth != nil {
|
||||
ok, err := rule.PublishAuth.CanPublish(ctx.Req.Context(), user)
|
||||
if err != nil {
|
||||
logger.Error("Error checking publish permissions", "user", user, "channel", channel, "error", err)
|
||||
return response.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), nil)
|
||||
}
|
||||
if !ok {
|
||||
return response.Error(http.StatusForbidden, http.StatusText(http.StatusForbidden), nil)
|
||||
}
|
||||
} else {
|
||||
if !user.HasRole(models.ROLE_ADMIN) {
|
||||
return response.Error(http.StatusForbidden, http.StatusText(http.StatusForbidden), nil)
|
||||
}
|
||||
}
|
||||
_, err := g.Pipeline.ProcessInput(ctx.Req.Context(), user.OrgId, channel, cmd.Data)
|
||||
if err != nil {
|
||||
logger.Error("Error processing input", "user", user, "channel", channel, "error", err)
|
||||
return response.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), nil)
|
||||
}
|
||||
return response.JSON(http.StatusOK, dtos.LivePublishResponse{})
|
||||
}
|
||||
}
|
||||
|
||||
channelHandler, addr, err := g.GetChannelHandler(ctx.SignedInUser, cmd.Channel)
|
||||
if err != nil {
|
||||
@@ -824,6 +928,7 @@ func (g *GrafanaLive) HandleHTTPPublish(ctx *models.ReqContext, cmd dtos.LivePub
|
||||
logger.Error("Error calling OnPublish", "error", err, "channel", cmd.Channel)
|
||||
return response.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), nil)
|
||||
}
|
||||
|
||||
if status != backend.PublishStreamStatusOK {
|
||||
code, text := publishStatusToHTTPError(status)
|
||||
return response.Error(code, text, nil)
|
||||
@@ -952,13 +1057,13 @@ func (g *GrafanaLive) HandlePipelineConvertTestHTTP(c *models.ReqContext) respon
|
||||
if !ok {
|
||||
return response.Error(http.StatusNotFound, "No rule found", nil)
|
||||
}
|
||||
channelFrames, ok, err := pipe.DataToChannelFrames(c.Req.Context(), *rule, c.OrgId, req.Channel, []byte(req.Data))
|
||||
if rule.Converter == nil {
|
||||
return response.Error(http.StatusNotFound, "No converter found", nil)
|
||||
}
|
||||
channelFrames, err := pipe.DataToChannelFrames(c.Req.Context(), *rule, c.OrgId, req.Channel, []byte(req.Data))
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Error converting data", err)
|
||||
}
|
||||
if !ok {
|
||||
return response.Error(http.StatusNotFound, "No converter found", nil)
|
||||
}
|
||||
return response.JSON(http.StatusOK, ConvertDryRunResponse{
|
||||
ChannelFrames: channelFrames,
|
||||
})
|
||||
@@ -1031,10 +1136,11 @@ func (g *GrafanaLive) HandleChannelRulesDeleteHTTP(c *models.ReqContext) respons
|
||||
// HandlePipelineEntitiesListHTTP ...
|
||||
func (g *GrafanaLive) HandlePipelineEntitiesListHTTP(_ *models.ReqContext) response.Response {
|
||||
return response.JSON(http.StatusOK, util.DynMap{
|
||||
"subscribers": pipeline.SubscribersRegistry,
|
||||
"outputs": pipeline.OutputsRegistry,
|
||||
"converters": pipeline.ConvertersRegistry,
|
||||
"processors": pipeline.ProcessorsRegistry,
|
||||
"subscribers": pipeline.SubscribersRegistry,
|
||||
"dataOutputs": pipeline.DataOutputsRegistry,
|
||||
"converters": pipeline.ConvertersRegistry,
|
||||
"frameProcessors": pipeline.FrameProcessorsRegistry,
|
||||
"frameOutputs": pipeline.FrameOutputsRegistry,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user