Skip to content

Commit

Permalink
Merge branch 'main' into fix/eventing-configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
beeme1mr authored Apr 13, 2023
2 parents 843af97 + 77664cd commit e5071a0
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 58 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release-please.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
# Release-please creates a PR that tracks all changes
steps:
- uses: google-github-actions/release-please-action@ee9822ec2c397e8a364d634464339ac43a06e042 # v3
- uses: google-github-actions/release-please-action@c078ea33917ab8cfa5300e48f4b7e6b16606aede # v3
id: release
with:
release-type: "go"
Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:
COMMIT=${{ github.sha }}
DATE=${{ steps.date.outputs.date }}
- name: Install Cosign
uses: sigstore/cosign-installer@8348525c79cd67509c593a9352954618bdc3a862
uses: sigstore/cosign-installer@9e9de2292db7abb3f51b7f4808d98f0d347a8919

- name: Sign the image
run: |
Expand Down
12 changes: 6 additions & 6 deletions core/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/diegoholiveira/jsonlogic/v3 v3.2.7
github.com/fsnotify/fsnotify v1.6.0
github.com/golang/mock v1.6.0
github.com/open-feature/open-feature-operator v0.2.31
github.com/open-feature/open-feature-operator v0.2.32
github.com/open-feature/schemas v0.2.8
github.com/prometheus/client_golang v1.14.0
github.com/robfig/cron v1.2.0
Expand Down Expand Up @@ -47,14 +47,14 @@ require (
github.com/emicklei/go-restful/v3 v3.10.1 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic v0.6.9 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
Expand Down Expand Up @@ -87,9 +87,9 @@ require (
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/oauth2 v0.5.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/term v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.3.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
Expand Down
12 changes: 12 additions & 0 deletions core/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,8 @@ github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
Expand Down Expand Up @@ -515,6 +517,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
Expand Down Expand Up @@ -632,6 +636,8 @@ github.com/onsi/ginkgo/v2 v2.8.1 h1:xFTEVwOFa1D/Ty24Ws1npBWkDYEV9BqZrsDxVrVkrrU=
github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754=
github.com/open-feature/open-feature-operator v0.2.31 h1:8MCULZPHGJpGVHIOm/vx07tpjDD6fMdNvOCdSFFhIaY=
github.com/open-feature/open-feature-operator v0.2.31/go.mod h1:4kdmeO7v33TFg/IxEH8N7c3Jply571G2Db12kXo8StA=
github.com/open-feature/open-feature-operator v0.2.32 h1:LoklX3q6cZp0Fdn/elOGy2Qrzj/qrxH1TFAn2AczgzE=
github.com/open-feature/open-feature-operator v0.2.32/go.mod h1:AQpIztSzUj+stvUi2GRJNTaEZhk6GAFt5xtUAQrWqtE=
github.com/open-feature/schemas v0.2.8 h1:oA75hJXpOd9SFgmNI2IAxWZkwzQPUDm7Jyyh3q489wM=
github.com/open-feature/schemas v0.2.8/go.mod h1:vj+rfTsOLlh5PtGGkAbitnJmFPYuTHXTjOy13kzNgKQ=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -956,12 +962,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand All @@ -976,6 +986,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
4 changes: 3 additions & 1 deletion core/pkg/eval/ievaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ type AnyValue struct {
Variant string
Reason string
FlagKey string
Error error
}

func NewAnyValue(value interface{}, variant string, reason string, flagKey string) AnyValue {
func NewAnyValue(value interface{}, variant string, reason string, flagKey string, err error) AnyValue {
return AnyValue{
Value: value,
Variant: variant,
Reason: reason,
FlagKey: flagKey,
Error: err,
}
}

Expand Down
20 changes: 20 additions & 0 deletions core/pkg/eval/ievaluator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package eval

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
)

func TestAnyValue(t *testing.T) {
obj := AnyValue{
Value: "val",
Variant: "variant",
Reason: "reason",
FlagKey: "key",
Error: fmt.Errorf("err"),
}

require.Equal(t, obj, NewAnyValue("val", "variant", "reason", "key", fmt.Errorf("err")))
}
7 changes: 3 additions & 4 deletions core/pkg/eval/json_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,8 @@ func (je *JSONEvaluator) ResolveAllValues(reqID string, context *structpb.Struct
}
if err != nil {
je.Logger.ErrorWithID(reqID, fmt.Sprintf("bulk evaluation: key: %s returned error: %s", flagKey, err.Error()))
continue
}
values = append(values, NewAnyValue(value, variant, reason, flagKey))
values = append(values, NewAnyValue(value, variant, reason, flagKey, err))
}
return values
}
Expand Down Expand Up @@ -238,7 +237,7 @@ func (je *JSONEvaluator) evaluateVariant(
targetingBytes, err := targeting.MarshalJSON()
if err != nil {
je.Logger.ErrorWithID(reqID, fmt.Sprintf("Error parsing rules for flag: %s, %s", flagKey, err))
return "", model.ErrorReason, err
return "", model.ErrorReason, errors.New(model.ParseErrorCode)
}

b, err := json.Marshal(context)
Expand All @@ -252,7 +251,7 @@ func (je *JSONEvaluator) evaluateVariant(
err = jsonlogic.Apply(bytes.NewReader(targetingBytes), bytes.NewReader(b), &result)
if err != nil {
je.Logger.ErrorWithID(reqID, fmt.Sprintf("error applying rules: %s", err))
return "", model.ErrorReason, err
return "", model.ErrorReason, errors.New(model.ParseErrorCode)
}
// strip whitespace and quotes from the variant
variant = strings.ReplaceAll(strings.TrimSpace(result.String()), "\"", "")
Expand Down
17 changes: 10 additions & 7 deletions core/pkg/service/flag-evaluation/flag_evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ func (s *FlagEvaluationService) ResolveAll(
}
values := s.eval.ResolveAllValues(reqID, req.Msg.GetContext())
for _, value := range values {
// register the impression for each flag evaluated
s.metrics.Impressions(ctx, value.FlagKey, value.Variant)
// register the impression and reason for each flag evaluated
s.metrics.RecordEvaluation(ctx, value.Error, value.Reason, value.Variant, value.FlagKey)
switch v := value.Value.(type) {
case bool:
res.Flags[value.FlagKey] = &schemaV1.AnyFlag{
Expand Down Expand Up @@ -149,21 +149,22 @@ func resolve[T constraints](
zap.Strings("context-keys", formatContextKeys(ctx)),
)

var evalErrFormatted error
result, variant, reason, evalErr := resolver(reqID, flagKey, ctx)
if evalErr != nil {
logger.WarnWithID(reqID, fmt.Sprintf("returning error response, reason: %v", evalErr))
reason = model.ErrorReason
evalErr = errFormat(evalErr)
} else {
metrics.Impressions(goCtx, flagKey, variant)
evalErrFormatted = errFormat(evalErr)
}

metrics.RecordEvaluation(goCtx, evalErr, reason, variant, flagKey)

if err := resp.SetResult(result, variant, reason); err != nil && evalErr == nil {
logger.ErrorWithID(reqID, err.Error())
return err
}

return evalErr
return evalErrFormatted
}

func (s *FlagEvaluationService) ResolveBoolean(
Expand Down Expand Up @@ -270,10 +271,12 @@ func errFormat(err error) error {
return connect.NewError(connect.CodeNotFound, fmt.Errorf("%s, %s", ErrorPrefix, err.Error()))
case model.TypeMismatchErrorCode:
return connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("%s, %s", ErrorPrefix, err.Error()))
case model.DisabledReason:
case model.FlagDisabledErrorCode:
return connect.NewError(connect.CodeUnavailable, fmt.Errorf("%s, %s", ErrorPrefix, err.Error()))
case model.ParseErrorCode:
return connect.NewError(connect.CodeDataLoss, fmt.Errorf("%s, %s", ErrorPrefix, err.Error()))
case model.GeneralErrorCode:
return connect.NewError(connect.CodeUnknown, fmt.Errorf("%s, %s", ErrorPrefix, err.Error()))
}

return err
Expand Down
10 changes: 5 additions & 5 deletions core/pkg/service/flag-evaluation/flag_evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func TestFlag_Evaluation_ResolveBoolean(t *testing.T) {
wantErr: nil,
},
"eval returns error": {
mCount: 0,
mCount: 1,
evalFields: resolveBooleanEvalFields{
result: true,
variant: ":(",
Expand Down Expand Up @@ -317,7 +317,7 @@ func TestFlag_Evaluation_ResolveString(t *testing.T) {
wantErr: nil,
},
"eval returns error": {
mCount: 0,
mCount: 1,
evalFields: resolveStringEvalFields{
result: "true",
variant: ":(",
Expand Down Expand Up @@ -468,7 +468,7 @@ func TestFlag_Evaluation_ResolveFloat(t *testing.T) {
wantErr: nil,
},
"eval returns error": {
mCount: 0,
mCount: 1,
evalFields: resolveFloatEvalFields{
result: 12,
variant: ":(",
Expand Down Expand Up @@ -619,7 +619,7 @@ func TestFlag_Evaluation_ResolveInt(t *testing.T) {
wantErr: nil,
},
"eval returns error": {
mCount: 0,
mCount: 1,
evalFields: resolveIntEvalFields{
result: 12,
variant: ":(",
Expand Down Expand Up @@ -772,7 +772,7 @@ func TestFlag_Evaluation_ResolveObject(t *testing.T) {
wantErr: nil,
},
"eval returns error": {
mCount: 0,
mCount: 1,
evalFields: resolveObjectEvalFields{
result: map[string]interface{}{
"food": "bars",
Expand Down
48 changes: 43 additions & 5 deletions core/pkg/telemetry/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@ import (
)

const (
requestDurationName = "http_request_duration_seconds"
responseSizeName = "http_response_size_bytes"
requestDurationName = "http_request_duration_seconds"
responseSizeName = "http_response_size_bytes"
FlagdProviderName = "flagd"
FeatureFlagReasonKeyName = "feature_flag.reason"
ExceptionTypeKeyName = "exception.type"
FeatureFlagReasonKey = attribute.Key(FeatureFlagReasonKeyName)
ExceptionTypeKey = attribute.Key(ExceptionTypeKeyName)
)

type MetricsRecorder struct {
httpRequestDurHistogram instrument.Float64Histogram
httpResponseSizeHistogram instrument.Float64Histogram
httpRequestsInflight instrument.Int64UpDownCounter
impressions instrument.Int64Counter
reasons instrument.Int64Counter
}

func (r MetricsRecorder) HTTPAttributes(svcName, url, method, code string) []attribute.KeyValue {
Expand Down Expand Up @@ -53,14 +59,33 @@ func (r MetricsRecorder) InFlightRequestEnd(ctx context.Context, attrs []attribu
r.httpRequestsInflight.Add(ctx, -1, attrs...)
}

func (r MetricsRecorder) Impressions(ctx context.Context, key, variant string) {
func (r MetricsRecorder) Impressions(ctx context.Context, reason, variant, key string) {
r.impressions.Add(ctx, 1, []attribute.KeyValue{
semconv.FeatureFlagProviderName(FlagdProviderName),
semconv.FeatureFlagKey(key),
semconv.FeatureFlagVariant(variant),
semconv.FeatureFlagProviderName("flagd"),
FeatureFlagReason(reason),
}...)
}

func (r MetricsRecorder) Reasons(ctx context.Context, reason string, err error) {
attrs := []attribute.KeyValue{
semconv.FeatureFlagProviderName(FlagdProviderName),
FeatureFlagReason(reason),
}
if err != nil {
attrs = append(attrs, ExceptionType(err.Error()))
}
r.reasons.Add(ctx, 1, attrs...)
}

func (r MetricsRecorder) RecordEvaluation(ctx context.Context, err error, reason, variant, key string) {
if err == nil {
r.Impressions(ctx, reason, variant, key)
}
r.Reasons(ctx, reason, err)
}

func getDurationView(svcName, viewName string, bucket []float64) metric.View {
return metric.NewView(
metric.Instrument{
Expand All @@ -76,6 +101,14 @@ func getDurationView(svcName, viewName string, bucket []float64) metric.View {
)
}

func FeatureFlagReason(val string) attribute.KeyValue {
return FeatureFlagReasonKey.String(val)
}

func ExceptionType(val string) attribute.KeyValue {
return ExceptionTypeKey.String(val)
}

// NewOTelRecorder creates a MetricsRecorder based on the provided metric.Reader. Note that, metric.NewMeterProvider is
// created here but not registered globally as this is the only place we derive a metric.Meter. Consider global provider
// registration if we need more meters
Expand Down Expand Up @@ -109,12 +142,17 @@ func NewOTelRecorder(exporter metric.Reader, resource *resource.Resource, servic
)
impressions, _ := meter.Int64Counter(
"impressions",
instrument.WithDescription("The number of evaluation for a given flag"),
instrument.WithDescription("The number of evaluations for a given flag"),
)
reasons, _ := meter.Int64Counter(
"reasons",
instrument.WithDescription("The number of evaluations for a given reason"),
)
return &MetricsRecorder{
httpRequestDurHistogram: hduration,
httpResponseSizeHistogram: hsize,
httpRequestsInflight: reqCounter,
impressions: impressions,
reasons: reasons,
}
}
Loading

0 comments on commit e5071a0

Please sign in to comment.