|
|
|
@ -24,6 +24,7 @@ import ( |
|
|
|
|
"github.com/grafana/grafana/pkg/services/live/features" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/live/livecontext" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/live/managedstream" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/live/orgchannel" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/live/pushws" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/live/runstream" |
|
|
|
|
"github.com/grafana/grafana/pkg/services/sqlstore" |
|
|
|
@ -285,18 +286,32 @@ func runConcurrentlyIfNeeded(ctx context.Context, semaphore chan struct{}, fn fu |
|
|
|
|
|
|
|
|
|
func (g *GrafanaLive) handleOnSubscribe(client *centrifuge.Client, e centrifuge.SubscribeEvent) (centrifuge.SubscribeReply, error) { |
|
|
|
|
logger.Debug("Client wants to subscribe", "user", client.UserID(), "client", client.ID(), "channel", e.Channel) |
|
|
|
|
|
|
|
|
|
user, ok := livecontext.GetContextSignedUser(client.Context()) |
|
|
|
|
if !ok { |
|
|
|
|
logger.Error("No user found in context", "user", client.UserID(), "client", client.ID(), "channel", e.Channel) |
|
|
|
|
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal |
|
|
|
|
} |
|
|
|
|
handler, addr, err := g.GetChannelHandler(user, e.Channel) |
|
|
|
|
|
|
|
|
|
// See a detailed comment for StripOrgID about orgID management in Live.
|
|
|
|
|
orgID, channel, err := orgchannel.StripOrgID(e.Channel) |
|
|
|
|
if err != nil { |
|
|
|
|
logger.Error("Error parsing channel", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err) |
|
|
|
|
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if user.OrgId != orgID { |
|
|
|
|
logger.Info("Error subscribing: wrong orgId", "user", client.UserID(), "client", client.ID(), "channel", e.Channel) |
|
|
|
|
return centrifuge.SubscribeReply{}, centrifuge.ErrorPermissionDenied |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
handler, addr, err := g.GetChannelHandler(user, channel) |
|
|
|
|
if err != nil { |
|
|
|
|
logger.Error("Error getting channel handler", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err) |
|
|
|
|
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal |
|
|
|
|
} |
|
|
|
|
reply, status, err := handler.OnSubscribe(client.Context(), user, models.SubscribeEvent{ |
|
|
|
|
Channel: e.Channel, |
|
|
|
|
Channel: channel, |
|
|
|
|
Path: addr.Path, |
|
|
|
|
}) |
|
|
|
|
if err != nil { |
|
|
|
@ -322,18 +337,32 @@ func (g *GrafanaLive) handleOnSubscribe(client *centrifuge.Client, e centrifuge. |
|
|
|
|
|
|
|
|
|
func (g *GrafanaLive) handleOnPublish(client *centrifuge.Client, e centrifuge.PublishEvent) (centrifuge.PublishReply, error) { |
|
|
|
|
logger.Debug("Client wants to publish", "user", client.UserID(), "client", client.ID(), "channel", e.Channel) |
|
|
|
|
|
|
|
|
|
user, ok := livecontext.GetContextSignedUser(client.Context()) |
|
|
|
|
if !ok { |
|
|
|
|
logger.Error("No user found in context", "user", client.UserID(), "client", client.ID(), "channel", e.Channel) |
|
|
|
|
return centrifuge.PublishReply{}, centrifuge.ErrorInternal |
|
|
|
|
} |
|
|
|
|
handler, addr, err := g.GetChannelHandler(user, e.Channel) |
|
|
|
|
|
|
|
|
|
// See a detailed comment for StripOrgID about orgID management in Live.
|
|
|
|
|
orgID, channel, err := orgchannel.StripOrgID(e.Channel) |
|
|
|
|
if err != nil { |
|
|
|
|
logger.Error("Error parsing channel", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err) |
|
|
|
|
return centrifuge.PublishReply{}, centrifuge.ErrorInternal |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if user.OrgId != orgID { |
|
|
|
|
logger.Info("Error subscribing: wrong orgId", "user", client.UserID(), "client", client.ID(), "channel", e.Channel) |
|
|
|
|
return centrifuge.PublishReply{}, centrifuge.ErrorPermissionDenied |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
handler, addr, err := g.GetChannelHandler(user, channel) |
|
|
|
|
if err != nil { |
|
|
|
|
logger.Error("Error getting channel handler", "user", client.UserID(), "client", client.ID(), "channel", e.Channel, "error", err) |
|
|
|
|
return centrifuge.PublishReply{}, centrifuge.ErrorInternal |
|
|
|
|
} |
|
|
|
|
reply, status, err := handler.OnPublish(client.Context(), user, models.PublishEvent{ |
|
|
|
|
Channel: e.Channel, |
|
|
|
|
Channel: channel, |
|
|
|
|
Path: addr.Path, |
|
|
|
|
Data: e.Data, |
|
|
|
|
}) |
|
|
|
@ -478,8 +507,8 @@ func (g *GrafanaLive) handlePluginScope(_ *models.SignedInUser, namespace string |
|
|
|
|
), nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *GrafanaLive) handleStreamScope(_ *models.SignedInUser, namespace string) (models.ChannelHandlerFactory, error) { |
|
|
|
|
return g.ManagedStreamRunner.GetOrCreateStream(namespace) |
|
|
|
|
func (g *GrafanaLive) handleStreamScope(u *models.SignedInUser, namespace string) (models.ChannelHandlerFactory, error) { |
|
|
|
|
return g.ManagedStreamRunner.GetOrCreateStream(u.OrgId, namespace) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (g *GrafanaLive) handlePushScope(_ *models.SignedInUser, namespace string) (models.ChannelHandlerFactory, error) { |
|
|
|
@ -512,14 +541,14 @@ func (g *GrafanaLive) handleDatasourceScope(user *models.SignedInUser, namespace |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Publish sends the data to the channel without checking permissions etc
|
|
|
|
|
func (g *GrafanaLive) Publish(channel string, data []byte) error { |
|
|
|
|
_, err := g.node.Publish(channel, data) |
|
|
|
|
func (g *GrafanaLive) Publish(orgID int64, channel string, data []byte) error { |
|
|
|
|
_, err := g.node.Publish(orgchannel.PrependOrgID(orgID, channel), data) |
|
|
|
|
return err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ClientCount returns the number of clients
|
|
|
|
|
func (g *GrafanaLive) ClientCount(channel string) (int, error) { |
|
|
|
|
p, err := g.node.Presence(channel) |
|
|
|
|
func (g *GrafanaLive) ClientCount(orgID int64, channel string) (int, error) { |
|
|
|
|
p, err := g.node.Presence(orgchannel.PrependOrgID(orgID, channel)) |
|
|
|
|
if err != nil { |
|
|
|
|
return 0, err |
|
|
|
|
} |
|
|
|
@ -561,11 +590,11 @@ func (g *GrafanaLive) HandleHTTPPublish(ctx *models.ReqContext, cmd dtos.LivePub |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// HandleListHTTP returns metadata so the UI can build a nice form
|
|
|
|
|
func (g *GrafanaLive) HandleListHTTP(_ *models.ReqContext) response.Response { |
|
|
|
|
func (g *GrafanaLive) HandleListHTTP(c *models.ReqContext) response.Response { |
|
|
|
|
info := util.DynMap{} |
|
|
|
|
channels := make([]util.DynMap, 0) |
|
|
|
|
for k, v := range g.ManagedStreamRunner.Streams() { |
|
|
|
|
channels = append(channels, v.ListChannels("stream/"+k+"/")...) |
|
|
|
|
for k, v := range g.ManagedStreamRunner.Streams(c.SignedInUser.OrgId) { |
|
|
|
|
channels = append(channels, v.ListChannels(c.SignedInUser.OrgId, "stream/"+k+"/")...) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Hardcode sample streams
|
|
|
|
@ -595,7 +624,7 @@ func (g *GrafanaLive) HandleInfoHTTP(ctx *models.ReqContext) response.Response { |
|
|
|
|
path := ctx.Params("*") |
|
|
|
|
if path == "grafana/dashboards/gitops" { |
|
|
|
|
return response.JSON(200, util.DynMap{ |
|
|
|
|
"active": g.GrafanaScope.Dashboards.HasGitOpsObserver(), |
|
|
|
|
"active": g.GrafanaScope.Dashboards.HasGitOpsObserver(ctx.SignedInUser.OrgId), |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
return response.JSONStreaming(404, util.DynMap{ |
|
|
|
|