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"),
|
||||
QueryEventRecorder: infprom.NewEventRecorder("query"),
|
||||
Flagger: flagger,
|
||||
FlagsHandler: feature.NewFlagsHandler(kithttp.ErrorHandler(0), feature.ByKey),
|
||||
}
|
||||
|
||||
m.reg.MustRegister(m.apibackend.PrometheusCollectors()...)
|
||||
|
|
|
@ -84,6 +84,7 @@ type APIBackend struct {
|
|||
NotificationRuleStore influxdb.NotificationRuleStore
|
||||
NotificationEndpointService influxdb.NotificationEndpointService
|
||||
Flagger feature.Flagger
|
||||
FlagsHandler http.Handler
|
||||
}
|
||||
|
||||
// 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)
|
||||
h.Mount(prefixMe, 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.VariableService = authorizer.NewVariableService(b.VariableService)
|
||||
|
@ -281,16 +282,3 @@ func serveLinksHandler(errorHandler influxdb.HTTPErrorHandler) http.Handler {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
||||
type ByKeyFn func(string) (Flag, bool)
|
||||
|
||||
// ExposedFlagsFromContext returns the filtered map of exposed flags attached
|
||||
// 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)
|
||||
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
filtered := make(map[string]interface{})
|
||||
for k, v := range m {
|
||||
if flag := byKey[k]; flag != nil && flag.Expose() {
|
||||
if flag, found := byKey(k); found && flag.Expose() {
|
||||
filtered[k] = v
|
||||
}
|
||||
}
|
||||
|
@ -131,3 +132,9 @@ func (*defaultFlagger) Flags(_ context.Context, flags ...Flag) (map[string]inter
|
|||
func Flags() []Flag {
|
||||
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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/influxdata/influxdb/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -41,3 +43,20 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if len(flags) == 0 {
|
||||
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