Files
grafana/pkg/services/featuremgmt/openfeature.go
Tania 67a952c34e Implement OFREP compatible feature flag service (#105632)
* Add ofrep pkg

* api server: Use namespace from request in case user is not authenticated

* Add handlers to ofrep api builder

* Add NewOpenFeatureService to initialize mt apiserver

* allow specifying CA and insecure

* Compare namespace with eval ctx stackID

* Organize ofrep package

* Implement AllowedV0Alpha1Resources

* Revert folderimpl changes

* Handle default namespace

* Fix extracting stack id from eval ctx

* Add more logs

* Update pkg/registry/apis/ofrep/register.go

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

* Update pkg/registry/apis/ofrep/register.go

Co-authored-by: Dave Henderson <dave.henderson@grafana.com>

* Apply review feedback

* Replace contexthandler with types

* Fix identifying authed request

* Refactor checks in the handlers

* Remove anonymous from isAuthenticatedRequest check

---------

Co-authored-by: Todd Treece <360020+toddtreece@users.noreply.github.com>
Co-authored-by: Gabriel Mabille <gabriel.mabille@grafana.com>
Co-authored-by: Charandas Batra <charandas.batra@grafana.com>
Co-authored-by: Dave Henderson <dave.henderson@grafana.com>
2025-06-27 18:30:38 +02:00

69 lines
2.2 KiB
Go

package featuremgmt
import (
"fmt"
"net/url"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/open-feature/go-sdk/openfeature"
)
type OpenFeatureService struct {
log log.Logger
provider openfeature.FeatureProvider
Client openfeature.IClient
}
// ProvideOpenFeatureService is used for wiring dependencies in single tenant grafana
func ProvideOpenFeatureService(cfg *setting.Cfg) (*OpenFeatureService, error) {
confFlags, err := setting.ReadFeatureTogglesFromInitFile(cfg.Raw.Section("feature_toggles"))
if err != nil {
return nil, fmt.Errorf("failed to read feature toggles from config: %w", err)
}
openfeature.SetEvaluationContext(openfeature.NewEvaluationContext(cfg.OpenFeature.TargetingKey, cfg.OpenFeature.ContextAttrs))
return newOpenFeatureService(cfg.OpenFeature.ProviderType, cfg.OpenFeature.URL, confFlags)
}
// TODO: might need to be public, so other MT services could set up open feature client
func newOpenFeatureService(pType string, u *url.URL, staticFlags map[string]bool) (*OpenFeatureService, error) {
p, err := createProvider(pType, u, staticFlags)
if err != nil {
return nil, fmt.Errorf("failed to create feature provider: type %s, %w", pType, err)
}
if err := openfeature.SetProviderAndWait(p); err != nil {
return nil, fmt.Errorf("failed to set global feature provider: %s, %w", pType, err)
}
client := openfeature.NewClient("grafana-openfeature-client")
return &OpenFeatureService{
log: log.New("openfeatureservice"),
provider: p,
Client: client,
}, nil
}
func createProvider(providerType string, u *url.URL, staticFlags map[string]bool) (openfeature.FeatureProvider, error) {
if providerType != setting.GOFFProviderType {
return newStaticProvider(staticFlags)
}
if u.String() == "" {
return nil, fmt.Errorf("feature provider url is required for GOFFProviderType")
}
return newGOFFProvider(u.String())
}
func createClient(provider openfeature.FeatureProvider) (openfeature.IClient, error) {
if err := openfeature.SetProviderAndWait(provider); err != nil {
return nil, fmt.Errorf("failed to set global feature provider: %w", err)
}
client := openfeature.NewClient("grafana-openfeature-client")
return client, nil
}