diff --git a/pkg/services/live/pipeline/pipeline.go b/pkg/services/live/pipeline/pipeline.go index 3b7df5fb3e5..f2ae2185f0f 100644 --- a/pkg/services/live/pipeline/pipeline.go +++ b/pkg/services/live/pipeline/pipeline.go @@ -120,17 +120,46 @@ type SubscribeAuthChecker interface { CanSubscribe(ctx context.Context, u *models.SignedInUser) (bool, error) } -// LiveChannelRule is an in-memory representation of each specific rule, with Converter, FrameProcessor -// and FrameOutputter to be executed by Pipeline. +// LiveChannelRule is an in-memory representation of each specific rule to be executed by Pipeline. type LiveChannelRule struct { - OrgId int64 - Pattern string - PublishAuth PublishAuthChecker - SubscribeAuth SubscribeAuthChecker - Subscribers []Subscriber - DataOutputters []DataOutputter - Converter Converter + // OrgId this rule belongs to. + OrgId int64 + // Pattern is a pattern for a channel which when matched results in the rule execution + // during Subscribe or Publish operations. This is very similar to HTTP router functionality but + // adapted for Grafana Live channels. + // We use a modified version of github.com/julienschmidt/httprouter for pattern matching logic + // (see tree package's README for more information). + Pattern string + + // SubscribeAuth allows providing authorization logic for subscribing to a channel. + // If SubscribeAuth is not set then all authenticated users can subscribe to a channel. + SubscribeAuth SubscribeAuthChecker + // Subscribers allow modifying subscription properties and optionally call additional logic + // like opening a single stream to a plugin to consume channel events. If not set then + // subscription will have all options disabled, no initial data. + Subscribers []Subscriber + + // PublishAuth allows providing authorization logic for publishing into a channel. + // If PublishAuth is not set then ROLE_ADMIN is required to publish. + PublishAuth PublishAuthChecker + // DataOutputters if set allows doing something useful with raw input data. If not set then + // we step further to the converter. Each DataOutputter can optionally return a slice + // of ChannelData to pass the control to a rule defined by ChannelData.Channel - i.e. + // DataOutputters for the returned ChannelData.Channel will be executed. Note that in + // this case input processing will skip PublishAuth of ChannelData.Channel. I.e. authorization + // rules defined by the first channel in a pipeline chain. + DataOutputters []DataOutputter + // Converter allows transforming raw input data to frames. The Converter can split raw data to + // slice of ChannelFrame. Each ChannelFrame is then processed according to ChannelFrame.Channel + // rules - i.e. FrameProcessors for the returned ChannelFrame.Channel will be executed. + // If ChannelFrame.Channel is empty then we proceed with the current rule towards + // applying its FrameProcessors. + Converter Converter + // FrameProcessors can have logic to modify data.Frame before applying FrameOutputters. FrameProcessors []FrameProcessor + // FrameOutputters if set allow doing something useful with data.Frame. Each FrameOutputter + // can optionally return a slice of ChannelFrame to pass the control to a rule defined + // by ChannelFrame.Channel. FrameOutputters []FrameOutputter } diff --git a/pkg/services/live/pipeline/tree/readme.md b/pkg/services/live/pipeline/tree/readme.md index 2066594e9c6..a393a4fd0f1 100644 --- a/pkg/services/live/pipeline/tree/readme.md +++ b/pkg/services/live/pipeline/tree/readme.md @@ -1,4 +1,4 @@ -This is a tree code from https://github.com/julienschmidt/httprouter with an important fixes made inside Gin web framework. +This is a tree code from https://github.com/julienschmidt/httprouter with an important fixes/improvements made inside [Gin](https://github.com/gin-gonic/gin) web framework. See: * https://github.com/julienschmidt/httprouter/pull/329 @@ -7,3 +7,25 @@ See: See also https://github.com/julienschmidt/httprouter/issues/235 – that's the reason why we can't use a custom branch patched with fixes. Original LICENSE and copyright left unchanged here. + +## How channel pattern matching works + +As stated in [httprouter readme](https://github.com/julienschmidt/httprouter#features) about route matching behavior: + +> Only explicit matches: With other routers, like http.ServeMux, a requested URL path could match multiple patterns. Therefore they have some awkward pattern priority rules, like longest match or first registered, first matched. By design of this router, a request can only match exactly one or no route. As a result, there are also no unintended matches + +This copy of the httprouter follows this semantics. But it also contains several improvements where original httprouter was too restrictive. + +One possible way is to start with `tree_test.go` file for routing examples. + +What's possible: + +* Possible to define explicit patterns without any parameters: `stream/metrics/cpu`, `stream/metrics/mem`, `stream/metrics/cpu/spikes` +* Possible to have named parameter to match multiple channels: `stream/metrics/:metric`, `stream/metrics/:metric/spikes` +* Possible to have `stream/metrics/:metric` and `stream/metrics/cpu` defined together so a version without named parameter is preferred when matching +* Possible to use catch-all parameters like `stream/metrics/*rest` which will match multiple segments + +What's impossible: + +* Having 2 patterns like this: `stream/:scope/cpu` and `stream/metrics/:metric` since it will cause conflict in the underlying tree +* Having pattern like `stream/metrics/:metric` while you already have `stream/*rest` – again this is a conflict. So use catch-all parameters only in cases where you know for sure there won't be any sub-patterns in the future for which you need to override the behavior.