The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/featuremgmt/openfeature.go

99 lines
2.5 KiB

package featuremgmt
import (
"fmt"
"net/http"
"net/url"
"time"
clientauthmiddleware "github.com/grafana/grafana/pkg/clientauth/middleware"
"github.com/grafana/grafana/pkg/setting"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/open-feature/go-sdk/openfeature"
)
const (
featuresProviderAudience = "features.grafana.app"
)
func InitOpenFeatureWithCfg(cfg *setting.Cfg) error {
confFlags, err := setting.ReadFeatureTogglesFromInitFile(cfg.Raw.Section("feature_toggles"))
if err != nil {
return fmt.Errorf("failed to read feature flags from config: %w", err)
}
var httpcli *http.Client
if cfg.OpenFeature.ProviderType == setting.GOFFProviderType {
m, err := clientauthmiddleware.NewTokenExchangeMiddleware(cfg)
if err != nil {
return fmt.Errorf("failed to create token exchange middleware: %w", err)
}
httpcli, err = goffHTTPClient(m)
if err != nil {
return err
}
}
err = initOpenFeature(cfg.OpenFeature.ProviderType, cfg.OpenFeature.URL, confFlags, httpcli)
if err != nil {
return fmt.Errorf("failed to initialize OpenFeature: %w", err)
}
openfeature.SetEvaluationContext(openfeature.NewEvaluationContext(cfg.OpenFeature.TargetingKey, cfg.OpenFeature.ContextAttrs))
return nil
}
func initOpenFeature(
providerType string,
u *url.URL,
staticFlags map[string]bool,
httpClient *http.Client,
) error {
p, err := createProvider(providerType, u, staticFlags, httpClient)
if err != nil {
return err
}
if err := openfeature.SetProviderAndWait(p); err != nil {
return fmt.Errorf("failed to set global feature provider: %s, %w", providerType, err)
}
return nil
}
func createProvider(
providerType string,
u *url.URL,
staticFlags map[string]bool,
httpClient *http.Client,
) (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(), httpClient)
}
func goffHTTPClient(m *clientauthmiddleware.TokenExchangeMiddleware) (*http.Client, error) {
httpcli, err := sdkhttpclient.NewProvider().New(sdkhttpclient.Options{
TLS: &sdkhttpclient.TLSOptions{InsecureSkipVerify: true},
Timeouts: &sdkhttpclient.TimeoutOptions{
Timeout: 10 * time.Second,
},
Middlewares: []sdkhttpclient.Middleware{
m.New([]string{featuresProviderAudience}),
},
})
if err != nil {
return nil, fmt.Errorf("failed to create http client for openfeature: %w", err)
}
return httpcli, nil
}