From 876238d68892b4a8988c1a709f63b6994f9e587f Mon Sep 17 00:00:00 2001 From: Alirie Gray <alirie.gray@gmail.com> Date: Thu, 28 May 2020 08:26:08 -0700 Subject: [PATCH] feat: add feature flag for new labels service (#18215) --- cmd/influxd/launcher/launcher.go | 29 +++++++++- flags.yml | 8 +++ http/api_handler.go | 3 - http/label_service.go | 4 ++ kit/feature/list.go | 16 ++++++ label/controller.go | 94 ++++++++++++++++++++++++++++++++ label/controller_test.go | 55 +++++++++++++++++++ label/middleware_auth.go | 27 +++++---- label/middleware_auth_test.go | 26 +++++---- label/service.go | 7 +++ 10 files changed, 243 insertions(+), 26 deletions(-) create mode 100644 label/controller.go create mode 100644 label/controller_test.go diff --git a/cmd/influxd/launcher/launcher.go b/cmd/influxd/launcher/launcher.go index afb81815a0..e90ead99d4 100644 --- a/cmd/influxd/launcher/launcher.go +++ b/cmd/influxd/launcher/launcher.go @@ -37,6 +37,7 @@ import ( "github.com/influxdata/influxdb/v2/kit/tracing" kithttp "github.com/influxdata/influxdb/v2/kit/transport/http" "github.com/influxdata/influxdb/v2/kv" + "github.com/influxdata/influxdb/v2/label" influxlogger "github.com/influxdata/influxdb/v2/logger" "github.com/influxdata/influxdb/v2/nats" "github.com/influxdata/influxdb/v2/pkger" @@ -592,7 +593,6 @@ func (m *Launcher) run(ctx context.Context) (err error) { orgLogSvc platform.OrganizationOperationLogService = m.kvService scraperTargetSvc platform.ScraperTargetStoreService = m.kvService telegrafSvc platform.TelegrafConfigStore = m.kvService - labelSvc platform.LabelService = m.kvService secretSvc platform.SecretService = m.kvService lookupSvc platform.LookupService = m.kvService notificationEndpointStore platform.NotificationEndpointService = m.kvService @@ -869,6 +869,17 @@ func (m *Launcher) run(ctx context.Context) (err error) { sessionSvc = session.NewServiceController(flagger, m.kvService, sessionSvc) } + var labelSvc platform.LabelService + { + labelsStore, err := label.NewStore(m.kvStore) + if err != nil { + m.log.Error("Failed creating new labels store", zap.Error(err)) + return err + } + ls := label.NewService(labelsStore, m.kvService) + labelSvc = label.NewLabelController(flagger, m.kvService, ls) + } + m.apibackend = &http.APIBackend{ AssetsPath: m.assetsPath, HTTPErrorHandler: kithttp.ErrorHandler(0), @@ -970,6 +981,21 @@ func (m *Launcher) run(ctx context.Context) (err error) { onboardHTTPServer = tenant.NewHTTPOnboardHandler(m.log, onboardSvc) } + // feature flagging for new labels service + var labelsHTTPServer *kithttp.FeatureHandler + { + b := m.apibackend + labelSvcWithOrg := authorizer.NewLabelServiceWithOrg(labelSvc, b.OrgLookupService) + oldHandler := http.NewLabelHandler(m.log.With(zap.String("handler", "labels")), labelSvcWithOrg, kithttp.ErrorHandler(0)) + + labelSvc = label.NewAuthedLabelService(labelSvc, b.OrgLookupService) + labelSvc = label.NewLabelLogger(m.log.With(zap.String("handler", "labels")), labelSvc) + labelSvc = label.NewLabelMetrics(m.reg, labelSvc) + newHandler := label.NewHTTPLabelHandler(m.log, labelSvc) + + labelsHTTPServer = kithttp.NewFeatureHandler(feature.NewLabelPackage(), flagger, oldHandler, newHandler, newHandler.Prefix()) + } + // feature flagging for new authorization service var authHTTPServer *kithttp.FeatureHandler { @@ -1006,6 +1032,7 @@ func (m *Launcher) run(ctx context.Context) (err error) { http.WithResourceHandler(pkgHTTPServer), http.WithResourceHandler(onboardHTTPServer), http.WithResourceHandler(authHTTPServer), + http.WithResourceHandler(labelsHTTPServer), http.WithResourceHandler(kithttp.NewFeatureHandler(feature.SessionService(), flagger, oldSessionHandler, sessionHTTPServer.SignInResourceHandler(), sessionHTTPServer.SignInResourceHandler().Prefix())), http.WithResourceHandler(kithttp.NewFeatureHandler(feature.SessionService(), flagger, oldSessionHandler, sessionHTTPServer.SignOutResourceHandler(), sessionHTTPServer.SignOutResourceHandler().Prefix())), http.WithResourceHandler(userHTTPServer.MeResourceHandler()), diff --git a/flags.yml b/flags.yml index d721460d59..9f2f8a8649 100644 --- a/flags.yml +++ b/flags.yml @@ -58,3 +58,11 @@ key: pushDownGroupAggregateCount default: false contact: Query Team + +- name: New Label Package + description: Enables the refactored labels api + key: newLabels + default: false + contact: Alirie Gray + lifetime: temporary + diff --git a/http/api_handler.go b/http/api_handler.go index 726bcb20d4..3eb5de10d7 100644 --- a/http/api_handler.go +++ b/http/api_handler.go @@ -126,7 +126,6 @@ func NewAPIHandler(b *APIBackend, opts ...APIHandlerOptFn) *APIHandler { noAuthUserResourceMappingService := b.UserResourceMappingService b.UserResourceMappingService = authorizer.NewURMService(b.OrgLookupService, b.UserResourceMappingService) - b.LabelService = authorizer.NewLabelServiceWithOrg(b.LabelService, b.OrgLookupService) h.Mount("/api/v2", serveLinksHandler(b.HTTPErrorHandler)) @@ -155,8 +154,6 @@ func NewAPIHandler(b *APIBackend, opts ...APIHandlerOptFn) *APIHandler { fluxBackend := NewFluxBackend(b.Logger.With(zap.String("handler", "query")), b) h.Mount(prefixQuery, NewFluxHandler(b.Logger, fluxBackend)) - h.Mount(prefixLabels, NewLabelHandler(b.Logger, b.LabelService, b.HTTPErrorHandler)) - notificationEndpointBackend := NewNotificationEndpointBackend(b.Logger.With(zap.String("handler", "notificationEndpoint")), b) notificationEndpointBackend.NotificationEndpointService = authorizer.NewNotificationEndpointService(b.NotificationEndpointService, b.UserResourceMappingService, b.OrganizationService) diff --git a/http/label_service.go b/http/label_service.go index 1f17c4457b..015823d971 100644 --- a/http/label_service.go +++ b/http/label_service.go @@ -47,6 +47,10 @@ func NewLabelHandler(log *zap.Logger, s influxdb.LabelService, he influxdb.HTTPE return h } +func (h *LabelHandler) Prefix() string { + return prefixLabels +} + // handlePostLabel is the HTTP handler for the POST /api/v2/labels route. func (h *LabelHandler) handlePostLabel(w http.ResponseWriter, r *http.Request) { ctx := r.Context() diff --git a/kit/feature/list.go b/kit/feature/list.go index a2bce7d183..8790a06aeb 100644 --- a/kit/feature/list.go +++ b/kit/feature/list.go @@ -100,6 +100,20 @@ func PushDownGroupAggregateCount() BoolFlag { return pushDownGroupAggregateCount } +var newLabels = MakeBoolFlag( + "New Label Package", + "newLabels", + "Alirie Gray", + false, + Temporary, + false, +) + +// NewLabelPackage - Enables the refactored labels api +func NewLabelPackage() BoolFlag { + return newLabels +} + var all = []Flag{ backendExample, frontendExample, @@ -108,6 +122,7 @@ var all = []Flag{ newAuth, sessionService, pushDownGroupAggregateCount, + newLabels, } var byKey = map[string]Flag{ @@ -118,4 +133,5 @@ var byKey = map[string]Flag{ "newAuth": newAuth, "sessionService": sessionService, "pushDownGroupAggregateCount": pushDownGroupAggregateCount, + "newLabels": newLabels, } diff --git a/label/controller.go b/label/controller.go new file mode 100644 index 0000000000..1d1a6d1b36 --- /dev/null +++ b/label/controller.go @@ -0,0 +1,94 @@ +package label + +import ( + "context" + + "github.com/influxdata/influxdb/v2" + "github.com/influxdata/influxdb/v2/kit/feature" +) + +var _ influxdb.LabelService = (*LabelController)(nil) + +// LabelController is a temporary system for switching the label backend between +// the old and refactored versions with a feature flag +type LabelController struct { + flagger feature.Flagger + oldLabelService influxdb.LabelService + newLabelService influxdb.LabelService +} + +func NewLabelController(flagger feature.Flagger, oldLabelService, newLabelService influxdb.LabelService) *LabelController { + return &LabelController{ + flagger: flagger, + oldLabelService: oldLabelService, + newLabelService: newLabelService, + } +} + +func (s *LabelController) useNew(ctx context.Context) bool { + return feature.NewLabelPackage().Enabled(ctx, s.flagger) +} + +func (s *LabelController) CreateLabel(ctx context.Context, l *influxdb.Label) error { + if s.useNew(ctx) { + return s.newLabelService.CreateLabel(ctx, l) + } + return s.oldLabelService.CreateLabel(ctx, l) + +} + +func (s *LabelController) FindLabelByID(ctx context.Context, id influxdb.ID) (*influxdb.Label, error) { + if s.useNew(ctx) { + return s.newLabelService.FindLabelByID(ctx, id) + } + return s.oldLabelService.FindLabelByID(ctx, id) + +} + +func (s *LabelController) FindLabels(ctx context.Context, filter influxdb.LabelFilter, opt ...influxdb.FindOptions) ([]*influxdb.Label, error) { + if s.useNew(ctx) { + return s.newLabelService.FindLabels(ctx, filter, opt...) + } + return s.oldLabelService.FindLabels(ctx, filter, opt...) + +} + +func (s *LabelController) FindResourceLabels(ctx context.Context, filter influxdb.LabelMappingFilter) ([]*influxdb.Label, error) { + if s.useNew(ctx) { + return s.newLabelService.FindResourceLabels(ctx, filter) + } + return s.oldLabelService.FindResourceLabels(ctx, filter) + +} + +func (s *LabelController) UpdateLabel(ctx context.Context, id influxdb.ID, upd influxdb.LabelUpdate) (*influxdb.Label, error) { + if s.useNew(ctx) { + return s.newLabelService.UpdateLabel(ctx, id, upd) + } + return s.oldLabelService.UpdateLabel(ctx, id, upd) + +} + +func (s *LabelController) DeleteLabel(ctx context.Context, id influxdb.ID) error { + if s.useNew(ctx) { + return s.newLabelService.DeleteLabel(ctx, id) + } + return s.oldLabelService.DeleteLabel(ctx, id) + +} + +func (s *LabelController) CreateLabelMapping(ctx context.Context, m *influxdb.LabelMapping) error { + if s.useNew(ctx) { + return s.newLabelService.CreateLabelMapping(ctx, m) + } + return s.oldLabelService.CreateLabelMapping(ctx, m) + +} + +func (s *LabelController) DeleteLabelMapping(ctx context.Context, m *influxdb.LabelMapping) error { + if s.useNew(ctx) { + return s.newLabelService.DeleteLabelMapping(ctx, m) + } + return s.oldLabelService.DeleteLabelMapping(ctx, m) + +} diff --git a/label/controller_test.go b/label/controller_test.go new file mode 100644 index 0000000000..be702f4765 --- /dev/null +++ b/label/controller_test.go @@ -0,0 +1,55 @@ +package label_test + +import ( + "context" + "testing" + + "github.com/influxdata/influxdb/v2" + "github.com/influxdata/influxdb/v2/kit/feature" + "github.com/influxdata/influxdb/v2/kv" + "github.com/influxdata/influxdb/v2/label" + influxdbtesting "github.com/influxdata/influxdb/v2/testing" + "go.uber.org/zap/zaptest" +) + +func TestLabelServiceController(t *testing.T) { + influxdbtesting.LabelService(initBoltLabelServiceController, t) +} + +func initBoltLabelServiceController(f influxdbtesting.LabelFields, t *testing.T) (influxdb.LabelService, string, func()) { + newSvc, s, closer := initBoltLabelService(f, t) + st, _, _ := NewTestBoltStore(t) + oldSvc, _, _ := initOldLabelService(st, f, t) + + flagger := feature.DefaultFlagger() + return label.NewLabelController(flagger, oldSvc, newSvc), s, closer +} + +func initOldLabelService(s kv.Store, f influxdbtesting.LabelFields, t *testing.T) (influxdb.LabelService, string, func()) { + svc := kv.NewService(zaptest.NewLogger(t), s) + svc.IDGenerator = f.IDGenerator + + ctx := context.Background() + if err := svc.Initialize(ctx); err != nil { + t.Fatalf("error initializing label service: %v", err) + } + for _, l := range f.Labels { + if err := svc.PutLabel(ctx, l); err != nil { + t.Fatalf("failed to populate labels: %v", err) + } + } + + for _, m := range f.Mappings { + if err := svc.PutLabelMapping(ctx, m); err != nil { + t.Fatalf("failed to populate label mappings: %v", err) + } + } + + return svc, kv.OpPrefix, func() { + for _, l := range f.Labels { + if err := svc.DeleteLabel(ctx, l.ID); err != nil { + t.Logf("failed to remove label: %v", err) + } + } + } +} diff --git a/label/middleware_auth.go b/label/middleware_auth.go index fbd0ef6c98..2c117aaa03 100644 --- a/label/middleware_auth.go +++ b/label/middleware_auth.go @@ -6,19 +6,20 @@ import ( "github.com/influxdata/influxdb/v2" "github.com/influxdata/influxdb/v2/authorizer" - kithttp "github.com/influxdata/influxdb/v2/kit/transport/http" ) var _ influxdb.LabelService = (*AuthedLabelService)(nil) type AuthedLabelService struct { - s influxdb.LabelService + s influxdb.LabelService + orgSvc authorizer.OrganizationService } // NewAuthedLabelService constructs an instance of an authorizing label serivce. -func NewAuthedLabelService(s influxdb.LabelService) *AuthedLabelService { +func NewAuthedLabelService(s influxdb.LabelService, orgSvc authorizer.OrganizationService) *AuthedLabelService { return &AuthedLabelService{ - s: s, + s: s, + orgSvc: orgSvc, } } func (s *AuthedLabelService) CreateLabel(ctx context.Context, l *influxdb.Label) error { @@ -57,19 +58,22 @@ func (s *AuthedLabelService) FindResourceLabels(ctx context.Context, filter infl if err := filter.ResourceType.Valid(); err != nil { return nil, err } - // first fetch all labels for this resource - ls, err := s.s.FindResourceLabels(ctx, filter) + + if s.orgSvc == nil { + return nil, errors.New("failed to find orgSvc") + } + orgID, err := s.orgSvc.FindResourceOrganizationID(ctx, filter.ResourceType, filter.ResourceID) if err != nil { return nil, err } - // check the permissions for the resource by the org on the context - orgID := kithttp.OrgIDFromContext(ctx) - if orgID == nil { - return nil, errors.New("failed to find orgID on context") + if _, _, err := authorizer.AuthorizeRead(ctx, filter.ResourceType, filter.ResourceID, orgID); err != nil { + return nil, err } - if _, _, err := authorizer.AuthorizeRead(ctx, filter.ResourceType, filter.ResourceID, *orgID); err != nil { + // first fetch all labels for this resource + ls, err := s.s.FindResourceLabels(ctx, filter) + if err != nil { return nil, err } @@ -108,6 +112,7 @@ func (s *AuthedLabelService) CreateLabelMapping(ctx context.Context, m *influxdb if err != nil { return err } + if _, _, err := authorizer.AuthorizeWrite(ctx, influxdb.LabelsResourceType, m.LabelID, l.OrgID); err != nil { return err } diff --git a/label/middleware_auth_test.go b/label/middleware_auth_test.go index b00c470b30..b9f382067b 100644 --- a/label/middleware_auth_test.go +++ b/label/middleware_auth_test.go @@ -9,7 +9,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/influxdata/influxdb/v2" influxdbcontext "github.com/influxdata/influxdb/v2/context" - kithttp "github.com/influxdata/influxdb/v2/kit/transport/http" "github.com/influxdata/influxdb/v2/mock" influxdbtesting "github.com/influxdata/influxdb/v2/testing" ) @@ -20,6 +19,11 @@ const ( var ( orgOneInfluxID = influxdbtesting.MustIDBase16(orgOneID) + orgSvc = &mock.OrganizationService{ + FindResourceOrganizationIDF: func(_ context.Context, _ influxdb.ResourceType, _ influxdb.ID) (influxdb.ID, error) { + return orgOneInfluxID, nil + }, + } ) var labelCmpOptions = cmp.Options{ @@ -112,7 +116,7 @@ func TestLabelService_FindLabelByID(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewAuthedLabelService(tt.fields.LabelService) + s := NewAuthedLabelService(tt.fields.LabelService, orgSvc) ctx := context.Background() ctx = influxdbcontext.SetAuthorizer(ctx, mock.NewMockAuthorizer(false, []influxdb.Permission{tt.args.permission})) @@ -267,9 +271,9 @@ func TestLabelService_FindLabels(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewAuthedLabelService(tt.fields.LabelService) + s := NewAuthedLabelService(tt.fields.LabelService, orgSvc) - ctx := context.WithValue(context.Background(), kithttp.CtxOrgKey, orgOneInfluxID) + ctx := context.Background() ctx = influxdbcontext.SetAuthorizer(ctx, mock.NewMockAuthorizer(false, []influxdb.Permission{tt.args.permission})) labels, err := s.FindLabels(ctx, influxdb.LabelFilter{}) @@ -375,7 +379,7 @@ func TestLabelService_UpdateLabel(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewAuthedLabelService(tt.fields.LabelService) + s := NewAuthedLabelService(tt.fields.LabelService, orgSvc) ctx := context.Background() ctx = influxdbcontext.SetAuthorizer(ctx, mock.NewMockAuthorizer(false, tt.args.permissions)) @@ -475,7 +479,7 @@ func TestLabelService_DeleteLabel(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewAuthedLabelService(tt.fields.LabelService) + s := NewAuthedLabelService(tt.fields.LabelService, orgSvc) ctx := context.Background() ctx = influxdbcontext.SetAuthorizer(ctx, mock.NewMockAuthorizer(false, tt.args.permissions)) @@ -579,7 +583,7 @@ func TestLabelService_CreateLabel(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewAuthedLabelService(tt.fields.LabelService) + s := NewAuthedLabelService(tt.fields.LabelService, orgSvc) ctx := context.Background() ctx = influxdbcontext.SetAuthorizer(ctx, mock.NewMockAuthorizer(false, []influxdb.Permission{tt.args.permission})) @@ -811,9 +815,9 @@ func TestLabelService_FindResourceLabels(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewAuthedLabelService(tt.fields.LabelService) + s := NewAuthedLabelService(tt.fields.LabelService, orgSvc) - ctx := context.WithValue(context.Background(), kithttp.CtxOrgKey, orgOneInfluxID) + ctx := context.Background() ctx = influxdbcontext.SetAuthorizer(ctx, mock.NewMockAuthorizer(false, tt.args.permissions)) labels, err := s.FindResourceLabels(ctx, tt.args.filter) @@ -963,7 +967,7 @@ func TestLabelService_CreateLabelMapping(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewAuthedLabelService(tt.fields.LabelService) + s := NewAuthedLabelService(tt.fields.LabelService, orgSvc) ctx := context.Background() ctx = influxdbcontext.SetAuthorizer(ctx, mock.NewMockAuthorizer(false, tt.args.permissions)) @@ -1111,7 +1115,7 @@ func TestLabelService_DeleteLabelMapping(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := NewAuthedLabelService(tt.fields.LabelService) + s := NewAuthedLabelService(tt.fields.LabelService, orgSvc) ctx := context.Background() ctx = influxdbcontext.SetAuthorizer(ctx, mock.NewMockAuthorizer(false, tt.args.permissions)) diff --git a/label/service.go b/label/service.go index 97dd326c0c..7785533950 100644 --- a/label/service.go +++ b/label/service.go @@ -2,6 +2,7 @@ package label import ( "context" + "fmt" "strings" "github.com/influxdata/influxdb/v2" @@ -144,13 +145,17 @@ func (s *Service) DeleteLabel(ctx context.Context, id influxdb.ID) error { // CreateLabelMapping creates a new mapping between a resource and a label. func (s *Service) CreateLabelMapping(ctx context.Context, m *influxdb.LabelMapping) error { + fmt.Println("creating label mapping") err := s.store.View(ctx, func(tx kv.Tx) error { if _, err := s.store.GetLabel(ctx, tx, m.LabelID); err != nil { + fmt.Println("could not get label") return err } + ls := []*influxdb.Label{} err := s.store.FindResourceLabels(ctx, tx, influxdb.LabelMappingFilter{ResourceID: m.ResourceID, ResourceType: m.ResourceType}, &ls) if err != nil { + fmt.Println("could not get resource labels") return err } for i := 0; i < len(ls); i++ { @@ -162,10 +167,12 @@ func (s *Service) CreateLabelMapping(ctx context.Context, m *influxdb.LabelMappi return nil }) if err != nil { + fmt.Println("could not .....?") return err } return s.store.Update(ctx, func(tx kv.Tx) error { + fmt.Println("calling to store ") return s.store.CreateLabelMapping(ctx, tx, m) }) }