refactor(kit/feature): remove target logic; expose bykey (#17949)

* refactor(kit/feature): remove target logic; expose bykey

* refactor(kit/feature): un-variadic-ify bykey arg
pull/17974/head^2
Gavin Cabbage 2020-05-06 11:13:17 -04:00 committed by GitHub
parent 4243bca075
commit 8af9b3c2d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 33 additions and 86 deletions

View File

@ -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()...)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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()

View File

@ -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
}