refactor(kit/feature): remove target logic; expose bykey (#17949)
* refactor(kit/feature): remove target logic; expose bykey * refactor(kit/feature): un-variadic-ify bykey argpull/17974/head^2
parent
4243bca075
commit
8af9b3c2d1
|
@ -906,6 +906,7 @@ func (m *Launcher) run(ctx context.Context) (err error) {
|
||||||
WriteEventRecorder: infprom.NewEventRecorder("write"),
|
WriteEventRecorder: infprom.NewEventRecorder("write"),
|
||||||
QueryEventRecorder: infprom.NewEventRecorder("query"),
|
QueryEventRecorder: infprom.NewEventRecorder("query"),
|
||||||
Flagger: flagger,
|
Flagger: flagger,
|
||||||
|
FlagsHandler: feature.NewFlagsHandler(kithttp.ErrorHandler(0), feature.ByKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
m.reg.MustRegister(m.apibackend.PrometheusCollectors()...)
|
m.reg.MustRegister(m.apibackend.PrometheusCollectors()...)
|
||||||
|
|
|
@ -84,6 +84,7 @@ type APIBackend struct {
|
||||||
NotificationRuleStore influxdb.NotificationRuleStore
|
NotificationRuleStore influxdb.NotificationRuleStore
|
||||||
NotificationEndpointService influxdb.NotificationEndpointService
|
NotificationEndpointService influxdb.NotificationEndpointService
|
||||||
Flagger feature.Flagger
|
Flagger feature.Flagger
|
||||||
|
FlagsHandler http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrometheusCollectors exposes the prometheus collectors associated with an APIBackend.
|
// PrometheusCollectors exposes the prometheus collectors associated with an APIBackend.
|
||||||
|
@ -205,7 +206,7 @@ func NewAPIHandler(b *APIBackend, opts ...APIHandlerOptFn) *APIHandler {
|
||||||
userHandler := NewUserHandler(b.Logger, userBackend)
|
userHandler := NewUserHandler(b.Logger, userBackend)
|
||||||
h.Mount(prefixMe, userHandler)
|
h.Mount(prefixMe, userHandler)
|
||||||
h.Mount(prefixUsers, userHandler)
|
h.Mount(prefixUsers, userHandler)
|
||||||
h.Mount("/api/v2/flags", serveFlagsHandler(b.HTTPErrorHandler))
|
h.Mount("/api/v2/flags", b.FlagsHandler)
|
||||||
|
|
||||||
variableBackend := NewVariableBackend(b.Logger.With(zap.String("handler", "variable")), b)
|
variableBackend := NewVariableBackend(b.Logger.With(zap.String("handler", "variable")), b)
|
||||||
variableBackend.VariableService = authorizer.NewVariableService(b.VariableService)
|
variableBackend.VariableService = authorizer.NewVariableService(b.VariableService)
|
||||||
|
@ -281,16 +282,3 @@ func serveLinksHandler(errorHandler influxdb.HTTPErrorHandler) http.Handler {
|
||||||
}
|
}
|
||||||
return http.HandlerFunc(fn)
|
return http.HandlerFunc(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func serveFlagsHandler(errorHandler influxdb.HTTPErrorHandler) http.Handler {
|
|
||||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var (
|
|
||||||
ctx = r.Context()
|
|
||||||
flags = feature.ExposedFlagsFromContext(ctx)
|
|
||||||
)
|
|
||||||
if err := encodeResponse(ctx, w, http.StatusOK, flags); err != nil {
|
|
||||||
errorHandler.HandleHTTPError(ctx, err, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return http.HandlerFunc(fn)
|
|
||||||
}
|
|
||||||
|
|
|
@ -51,18 +51,19 @@ func FlagsFromContext(ctx context.Context) map[string]interface{} {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ByKeyFn func(string) (Flag, bool)
|
||||||
|
|
||||||
// ExposedFlagsFromContext returns the filtered map of exposed flags attached
|
// ExposedFlagsFromContext returns the filtered map of exposed flags attached
|
||||||
// to the context by Annotate, or nil if none is found.
|
// to the context by Annotate, or nil if none is found.
|
||||||
func ExposedFlagsFromContext(ctx context.Context) map[string]interface{} {
|
func ExposedFlagsFromContext(ctx context.Context, byKey ByKeyFn) map[string]interface{} {
|
||||||
m := FlagsFromContext(ctx)
|
m := FlagsFromContext(ctx)
|
||||||
|
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
filtered := make(map[string]interface{})
|
filtered := make(map[string]interface{})
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
if flag := byKey[k]; flag != nil && flag.Expose() {
|
if flag, found := byKey(k); found && flag.Expose() {
|
||||||
filtered[k] = v
|
filtered[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,3 +132,9 @@ func (*defaultFlagger) Flags(_ context.Context, flags ...Flag) (map[string]inter
|
||||||
func Flags() []Flag {
|
func Flags() []Flag {
|
||||||
return all
|
return all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ByKey returns the Flag corresponding to the given key.
|
||||||
|
func ByKey(k string) (Flag, bool) {
|
||||||
|
v, found := byKey[k]
|
||||||
|
return v, found
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package feature
|
package feature
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/influxdata/influxdb/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,3 +43,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
h.next.ServeHTTP(w, r)
|
h.next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewFlagsHandler returns a handler that returns the map of computed feature flags on the request context.
|
||||||
|
func NewFlagsHandler(errorHandler influxdb.HTTPErrorHandler, byKey ByKeyFn) http.Handler {
|
||||||
|
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ctx = r.Context()
|
||||||
|
flags = ExposedFlagsFromContext(ctx, byKey)
|
||||||
|
)
|
||||||
|
if err := json.NewEncoder(w).Encode(flags); err != nil {
|
||||||
|
errorHandler.HandleHTTPError(ctx, err, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http.HandlerFunc(fn)
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ func Make(m map[string]string) (Flagger, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags returns a map of default values. It never returns an error.
|
// Flags returns a map of default values with overrides applied. It never returns an error.
|
||||||
func (f Flagger) Flags(_ context.Context, flags ...feature.Flag) (map[string]interface{}, error) {
|
func (f Flagger) Flags(_ context.Context, flags ...feature.Flag) (map[string]interface{}, error) {
|
||||||
if len(flags) == 0 {
|
if len(flags) == 0 {
|
||||||
flags = feature.Flags()
|
flags = feature.Flags()
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
package feature
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/influxdata/influxdb/v2"
|
|
||||||
icontext "github.com/influxdata/influxdb/v2/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrMissingTargetInfo = errors.New("unable to determine any user or org IDs from authorizer on context")
|
|
||||||
|
|
||||||
// Target against which to match a feature flag rule.
|
|
||||||
type Target struct {
|
|
||||||
// UserID to Target.
|
|
||||||
UserID influxdb.ID
|
|
||||||
// OrgIDs to Target.
|
|
||||||
OrgIDs []influxdb.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeTarget returns a populated feature flag Target for the given environment,
|
|
||||||
// including user and org information from the provided context, if available.
|
|
||||||
//
|
|
||||||
// If the authorizer on the context provides a user ID, it is used to fetch associated org IDs.
|
|
||||||
// If a user ID is not provided, an org ID is taken directly off the authorizer if possible.
|
|
||||||
// If no user or org information can be determined, a sentinel error is returned.
|
|
||||||
func MakeTarget(ctx context.Context, urms influxdb.UserResourceMappingService) (Target, error) {
|
|
||||||
auth, err := icontext.GetAuthorizer(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return Target{}, ErrMissingTargetInfo
|
|
||||||
}
|
|
||||||
userID := auth.GetUserID()
|
|
||||||
|
|
||||||
var orgIDs []influxdb.ID
|
|
||||||
if userID.Valid() {
|
|
||||||
orgIDs, err = fromURMs(ctx, userID, urms)
|
|
||||||
if err != nil {
|
|
||||||
return Target{}, err
|
|
||||||
}
|
|
||||||
} else if a, ok := auth.(*influxdb.Authorization); ok {
|
|
||||||
orgIDs = []influxdb.ID{a.OrgID}
|
|
||||||
} else {
|
|
||||||
return Target{}, ErrMissingTargetInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
return Target{
|
|
||||||
UserID: userID,
|
|
||||||
OrgIDs: orgIDs,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fromURMs(ctx context.Context, userID influxdb.ID, urms influxdb.UserResourceMappingService) ([]influxdb.ID, error) {
|
|
||||||
m, _, err := urms.FindUserResourceMappings(ctx, influxdb.UserResourceMappingFilter{
|
|
||||||
UserID: userID,
|
|
||||||
ResourceType: influxdb.OrgsResourceType,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("finding organization mappings for user %s: %v", userID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
orgIDs := make([]influxdb.ID, 0, len(m))
|
|
||||||
for _, o := range m {
|
|
||||||
orgIDs = append(orgIDs, o.ResourceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return orgIDs, nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue