diff --git a/README.md b/README.md
index 0bd6fcbc0..8a7cfdefa 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ This now provides an accessible http or [https](#https) endpoint for flag evalua
Command:
```sh
-curl -X POST "localhost:8013/flags/myBoolFlag/resolve/boolean"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"myBoolFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
@@ -52,7 +52,7 @@ Result:
Command:
```sh
-curl -X POST "localhost:8013/flags/myStringFlag/resolve/string"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveString" -d '{"flagKey":"myStringFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
@@ -68,7 +68,7 @@ Result:
Command:
```sh
-curl -X POST "localhost:8013/flags/myIntFlag/resolve/int"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveInt" -d '{"flagKey":"myIntFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
@@ -86,7 +86,7 @@ Result:
Command:
```sh
-curl -X POST "localhost:8013/flags/myFloatFlag/resolve/float"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveFloat" -d '{"flagKey":"myFloatFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
@@ -102,7 +102,7 @@ Result:
Command:
```sh
-curl -X POST "localhost:8013/flags/myObjectFlag/resolve/object"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveObject" -d '{"flagKey":"myObjectFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
@@ -118,7 +118,7 @@ Result:
Command:
```sh
-curl -X POST "localhost:8013/flags/isColorYellow/resolve/boolean" -d '{"color": "yellow"}'
+curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"isColorYellow","context":{"color":"yellow"}}' -H "Content-Type: application/json"
```
Result:
@@ -136,13 +136,13 @@ A type mismatch error is returned when the resolved value of a flag does not mat
Command:
```sh
-curl -X POST "localhost:8013/flags/myBoolFlag/resolve/string"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveString" -d '{"flagKey":"myBoolFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
```sh
-{"error_code":"TYPE_MISMATCH","reason":"ERROR"}
+{"code":"invalid_argument","message":"TYPE_MISMATCH"}
```
@@ -154,13 +154,13 @@ The flag not found error is returned when flag key in the request doesn't match
Command:
```sh
-curl -X POST "localhost:8013/flags/aMissingFlag/resolve/string"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"aMissingFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
```sh
-{"error_code":"FLAG_NOT_FOUND","reason":"ERROR"}
+{"code":"not_found","message":"FLAG_NOT_FOUND"}
```
### https
@@ -172,7 +172,7 @@ When it is desired to use TLS for increased security, flagD can be started with
This enables you to use an upgraded connection for the previous example requests, such as the following:
```
-curl -X POST "https://localhost:8013/flags/myBoolFlag/resolve/boolean"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"myBoolFlag","context":{}}' -H "Content-Type: application/json"
// {"value":true,"reason":"DEFAULT","variant":"on"}
```
@@ -231,7 +231,7 @@ A flag is defined as such:
The rule provided returns `"on"` if `var color == "yellow"` and `"off"` otherwise:
```shell
-curl -X POST "localhost:8013/flags/isColorYellow/resolve/boolean" -d '{"color": "yellow"}'
+curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"isColorYellow","context":{"color":"yellow"}}' -H "Content-Type: application/json"
```
returns
@@ -243,7 +243,7 @@ returns
whereas
```shell
-curl -X POST "localhost:8013/flags/isColorYellow/resolve/boolean" -d '{"color": "white"}'
+curl -X POST "localhost:8013/schema.v1.Service/ResolveBoolean" -d '{"flagKey":"isColorYellow","context":{"color":"white"}}' -H "Content-Type: application/json"
```
returns
diff --git a/cmd/start.go b/cmd/start.go
index f129d2975..e17d4a3d8 100644
--- a/cmd/start.go
+++ b/cmd/start.go
@@ -11,16 +11,16 @@ import (
)
const (
- portFlagName = "port"
- serviceProviderFlagName = "service-provider"
- socketPathFlagName = "socket-path"
- syncProviderFlagName = "sync-provider"
- providerArgsFlagName = "sync-provider-args"
- evaluatorFlagName = "evaluator"
- serverCertPathFlagName = "server-cert-path"
- serverKeyPathFlagName = "server-key-path"
- uriFlagName = "uri"
- bearerTokenFlagName = "bearer-token"
+ portFlagName = "port"
+ socketPathFlagName = "socket-path"
+ syncProviderFlagName = "sync-provider"
+ providerArgsFlagName = "sync-provider-args"
+ evaluatorFlagName = "evaluator"
+ serverCertPathFlagName = "server-cert-path"
+ serverKeyPathFlagName = "server-key-path"
+ uriFlagName = "uri"
+ bearerTokenFlagName = "bearer-token"
+ corsFlagName = "cors-origin"
)
func init() {
@@ -34,7 +34,6 @@ func init() {
flags.StringP(socketPathFlagName, "d", "", "Flagd socket path. "+
"With grpc the service will become available on this address. "+
"With http(s) the grpc-gateway proxy will use this address internally.")
- flags.StringP(serviceProviderFlagName, "s", "http", "Set a service provider e.g. http or grpc")
flags.StringP(
syncProviderFlagName, "y", "filepath", "Set a sync provider e.g. filepath or remote",
)
@@ -49,10 +48,10 @@ func init() {
"flags with the same key, the later will be used.")
flags.StringP(
bearerTokenFlagName, "b", "", "Set a bearer token to use for remote sync")
+ flags.StringSliceP(corsFlagName, "C", []string{}, "CORS allowed origins, * will allow all origins")
_ = viper.BindPFlag(portFlagName, flags.Lookup(portFlagName))
_ = viper.BindPFlag(socketPathFlagName, flags.Lookup(socketPathFlagName))
- _ = viper.BindPFlag(serviceProviderFlagName, flags.Lookup(serviceProviderFlagName))
_ = viper.BindPFlag(syncProviderFlagName, flags.Lookup(syncProviderFlagName))
_ = viper.BindPFlag(providerArgsFlagName, flags.Lookup(providerArgsFlagName))
_ = viper.BindPFlag(evaluatorFlagName, flags.Lookup(evaluatorFlagName))
@@ -60,6 +59,7 @@ func init() {
_ = viper.BindPFlag(serverKeyPathFlagName, flags.Lookup(serverKeyPathFlagName))
_ = viper.BindPFlag(uriFlagName, flags.Lookup(uriFlagName))
_ = viper.BindPFlag(bearerTokenFlagName, flags.Lookup(bearerTokenFlagName))
+ _ = viper.BindPFlag(corsFlagName, flags.Lookup(corsFlagName))
}
// startCmd represents the start command
@@ -78,7 +78,6 @@ var startCmd = &cobra.Command{
}
// Build Runtime -----------------------------------------------------------
rt, err := runtime.FromConfig(runtime.Config{
- ServiceProvider: viper.GetString(serviceProviderFlagName),
ServicePort: viper.GetInt32(portFlagName),
ServiceSocketPath: viper.GetString(socketPathFlagName),
ServiceCertPath: viper.GetString(serverCertPathFlagName),
@@ -90,6 +89,8 @@ var startCmd = &cobra.Command{
SyncBearerToken: viper.GetString(bearerTokenFlagName),
Evaluator: viper.GetString(evaluatorFlagName),
+
+ CORS: viper.GetStringSlice(corsFlagName),
})
if err != nil {
log.Error(err)
diff --git a/docs/configuration.md b/docs/configuration.md
index 0d6057224..365c0d78b 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -12,11 +12,11 @@ Supported flags are as follows (result of running `./flagd start --help`):
-p, --port int32 Port to listen on (default 8013)
-c, --server-cert-path string Server side tls certificate path
-k, --server-key-path string Server side tls key path
- -s, --service-provider string Set a service provider e.g. http or grpc (default "http")
-a, --sync-provider-args Sync provider arguments as key values separated by =
- -d, --socket-path string Set the flagd socket path. With grpc the service will become available on this address. With http(s) the grpc-gateway proxy will use this address internally
+ -d, --socket-path string Set the flagd socket path.
-y, --sync-provider string Set a sync provider e.g. filepath or remote (default "filepath")
-f, --uri strings Set a sync provider uri to read data from this can be a filepath or url. Using multiple providers is supported where collisions between flags with the same key, the later will be used.
+ -C, --cors-origin strings Set a CORS allow origin header, setting "*" will allow all origins (by default CORS headers are not set)
```
Environment variable keys are uppercased, prefixed with `FLAGD_` and all `-` are replaced with `_`. For example,
diff --git a/docs/fractional_evaluation.md b/docs/fractional_evaluation.md
index 58c11d0bb..75ec3d412 100644
--- a/docs/fractional_evaluation.md
+++ b/docs/fractional_evaluation.md
@@ -45,9 +45,9 @@ Flags defined as such:
will return variant `red` 50% of the time, `blue` 20% of the time & `green` 30% of the time.
```shell
-$ curl -X POST "localhost:8013/flags/headerColor/resolve/string" -d '{"email": "foo@bar.com"}'
+$ curl -X POST "localhost:8013/schema.v1.Service/ResolveString" -d ''{"flagKey":"headerColor","context":{"email": "foo@bar.com"}}'' -H "Content-Type: application/json"
{"value":"#0000FF","reason":"TARGETING_MATCH","variant":"blue"}%
-$ curl -X POST "localhost:8013/flags/headerColor/resolve/string" -d '{"email": "foo@test.com"}'
+$ curl -X POST "localhost:8013/schema.v1.Service/ResolveString" -d ''{"flagKey":"headerColor","context":{"email": "foo@test.com"}}'' -H "Content-Type: application/json"
{"value":"#00FF00","reason":"TARGETING_MATCH","variant":"green"}%
```
diff --git a/docs/http_int_response.md b/docs/http_int_response.md
index a77853879..8a0e7d53a 100644
--- a/docs/http_int_response.md
+++ b/docs/http_int_response.md
@@ -4,7 +4,7 @@
Why is my `int` response a `string`?
Command:
```sh
-curl -X POST "localhost:8013/flags/myIntFlag/resolve/int"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveInt" -d '{"flagKey":"myIntFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
```sh
@@ -14,7 +14,7 @@ When interacting directly with the flagD http(s) api and requesting an `int` the
Command:
```sh
-curl -X POST "localhost:8013/flags/myIntFlag/resolve/float"
+curl -X POST "localhost:8013/schema.v1.Service/ResolveFloat" -d '{"flagKey":"myIntFlag","context":{}}' -H "Content-Type: application/json"
```
Result:
```sh
diff --git a/go.mod b/go.mod
index ed8eeb59b..42e4e9c95 100644
--- a/go.mod
+++ b/go.mod
@@ -3,25 +3,26 @@ module github.com/open-feature/flagd
go 1.18
require (
+ github.com/bufbuild/connect-go v0.4.0
github.com/deepmap/oapi-codegen v1.11.0
github.com/diegoholiveira/jsonlogic/v3 v3.2.3
github.com/dimiro1/banner v1.1.0
github.com/fsnotify/fsnotify v1.5.4
github.com/go-chi/chi/v5 v5.0.7
github.com/golang/mock v1.6.0
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.1
github.com/mattn/go-colorable v0.1.12
github.com/open-feature/open-feature-operator v0.0.10-0.20220826061622-a6421d66936a
github.com/open-feature/schemas v0.0.0-20220809125333-185e3bd77775
github.com/robfig/cron v1.2.0
+ github.com/rs/cors v1.8.2
github.com/sirupsen/logrus v1.8.1
- github.com/soheilhy/cmux v0.1.5
github.com/spf13/cobra v1.4.0
github.com/spf13/viper v1.12.0
github.com/stretchr/testify v1.7.4
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zeebo/xxh3 v1.0.2
- go.buf.build/open-feature/flagd-server/open-feature/flagd v1.1.2
+ go.buf.build/open-feature/flagd-connect/open-feature/flagd v1.1.3
+ golang.org/x/net v0.0.0-20220909164309-bea034e7d591
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
google.golang.org/grpc v1.48.0
google.golang.org/protobuf v1.28.1
@@ -73,7 +74,6 @@ require (
github.com/subosito/gotenv v1.3.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
- golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
diff --git a/go.sum b/go.sum
index 5863b52e4..91aed90dc 100644
--- a/go.sum
+++ b/go.sum
@@ -17,34 +17,14 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
-cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
-cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
-cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
-cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
-cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
-cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
-cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
-cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
-cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
-cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
-cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
-cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
-cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
-cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -55,20 +35,18 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
-cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/bufbuild/connect-go v0.4.0 h1:fIMyUYG8mXSTH+nnlOx9KmRUf3mBF0R2uKK+BQBoOHE=
+github.com/bufbuild/connect-go v0.4.0/go.mod h1:ZEtBnQ7J/m7bvWOW+H8T/+hKQCzPVfhhhICuvtcnjlI=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -79,8 +57,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
@@ -109,9 +85,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
@@ -153,8 +126,6 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
-github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -165,7 +136,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -183,10 +153,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-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/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
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=
@@ -200,11 +168,9 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -213,7 +179,6 @@ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@@ -224,29 +189,15 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
-github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
-github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
-github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
-github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
-github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.1 h1:/sDbPb60SusIXjiJGYLUoS/rAQurQmvGWmwn2bBPM9c=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.1/go.mod h1:G+WkljZi4mflcqVxYSgvt8MNctRQHjEH8ubKtt1Ka3w=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -355,12 +306,11 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
+github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
-github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
@@ -409,15 +359,14 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
-go.buf.build/open-feature/flagd-server/open-feature/flagd v1.1.2 h1:GbrVEHuAp/3a6JpfoV8cusP4/CveInar997DCCbXSAg=
-go.buf.build/open-feature/flagd-server/open-feature/flagd v1.1.2/go.mod h1:1gOZbwcemK1/404A8u4kXmzIn4DIIT1xIe8JuowznT8=
+go.buf.build/open-feature/flagd-connect/open-feature/flagd v1.1.3 h1:WdB8KPnlU7S+ux7euBKg6k4ZSrEiEHBJBJLnD5otxQM=
+go.buf.build/open-feature/flagd-connect/open-feature/flagd v1.1.3/go.mod h1:ZaEOUjTu/oxTFHuwNjF2788REepgWYPsU4Ty6lPIMzc=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
@@ -457,7 +406,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -499,27 +447,13 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220513224357-95641704303c/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -531,17 +465,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c h1:q3gFqPqH7NVofKo3c3yETAP//pPI+G5mvB7qqj1Y5kY=
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -592,41 +515,20 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220513210249-45d2b4557a2a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -638,7 +540,6 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -698,18 +599,12 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -730,26 +625,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
-google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
-google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
-google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
-google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
-google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
-google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
-google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
-google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
-google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
-google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
-google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
-google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
-google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -795,50 +670,7 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
-google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/genproto v0.0.0-20220728213248-dd149ef739b9 h1:d3fKQZK+1rWQMg3xLKQbPMirUCo29I/NRdI2WarSzTg=
google.golang.org/genproto v0.0.0-20220728213248-dd149ef739b9/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -859,22 +691,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/pkg/eval/ievaluator.go b/pkg/eval/ievaluator.go
index a75118962..3be3cfb7b 100644
--- a/pkg/eval/ievaluator.go
+++ b/pkg/eval/ievaluator.go
@@ -8,7 +8,6 @@ import (
IEvaluator implementations store the state of the flags,
do parsing and validation of the flag state and evaluate flags in response to handlers.
*/
-
type IEvaluator interface {
GetState() (string, error)
SetState(state string) error
diff --git a/pkg/model/reason.go b/pkg/model/reason.go
index d6dbd899b..25382447c 100644
--- a/pkg/model/reason.go
+++ b/pkg/model/reason.go
@@ -1,5 +1,7 @@
package model
+type EvaluationReason string
+
const (
TargetingMatchReason = "TARGETING_MATCH"
SplitReason = "SPLIT"
diff --git a/pkg/runtime/from_config.go b/pkg/runtime/from_config.go
index ca97c67d0..af68258be 100644
--- a/pkg/runtime/from_config.go
+++ b/pkg/runtime/from_config.go
@@ -25,49 +25,23 @@ func FromConfig(config Config) (*Runtime, error) {
if err := rt.setEvaluatorFromConfig(); err != nil {
return nil, err
}
- if err := rt.setServiceFromConfig(); err != nil {
- return nil, err
- }
if err := rt.setSyncImplFromConfig(); err != nil {
return nil, err
}
+ rt.setService()
return &rt, nil
}
-func (r *Runtime) setServiceFromConfig() error {
- switch r.config.ServiceProvider {
- case "http":
- r.Service = &service.HTTPService{
- HTTPServiceConfiguration: &service.HTTPServiceConfiguration{
- Port: r.config.ServicePort,
- ServerKeyPath: r.config.ServiceKeyPath,
- ServerCertPath: r.config.ServiceCertPath,
- ServerSocketPath: r.config.ServiceSocketPath,
- },
- GRPCService: &service.GRPCService{},
- Logger: log.WithFields(log.Fields{
- "service": "http",
- "component": "service",
- }),
- }
- case "grpc":
- r.Service = &service.GRPCService{
- GRPCServiceConfiguration: &service.GRPCServiceConfiguration{
- Port: r.config.ServicePort,
- ServerKeyPath: r.config.ServiceKeyPath,
- ServerCertPath: r.config.ServiceCertPath,
- ServerSocketPath: r.config.ServiceSocketPath,
- },
- Logger: log.WithFields(log.Fields{
- "service": "grpc",
- "component": "service",
- }),
- }
- default:
- return errors.New("no service-provider set")
+func (r *Runtime) setService() {
+ r.Service = &service.ConnectService{
+ ConnectServiceConfiguration: &service.ConnectServiceConfiguration{
+ Port: r.config.ServicePort,
+ ServerKeyPath: r.config.ServiceKeyPath,
+ ServerCertPath: r.config.ServiceCertPath,
+ ServerSocketPath: r.config.ServiceSocketPath,
+ CORS: r.config.CORS,
+ },
}
- log.Debugf("Using %s service-provider\n", r.config.ServiceProvider)
- return nil
}
func (r *Runtime) setEvaluatorFromConfig() error {
diff --git a/pkg/runtime/runtime.go b/pkg/runtime/runtime.go
index e5346abb1..55ac5b607 100644
--- a/pkg/runtime/runtime.go
+++ b/pkg/runtime/runtime.go
@@ -22,7 +22,6 @@ type Runtime struct {
}
type Config struct {
- ServiceProvider string
ServicePort int32
ServiceSocketPath string
ServiceCertPath string
@@ -34,6 +33,7 @@ type Config struct {
SyncBearerToken string
Evaluator string
+ CORS []string
}
func (r *Runtime) startSyncer(ctx context.Context, syncr sync.ISync) error {
diff --git a/pkg/service/connect_service.go b/pkg/service/connect_service.go
new file mode 100644
index 000000000..5c7ee9f8e
--- /dev/null
+++ b/pkg/service/connect_service.go
@@ -0,0 +1,238 @@
+package service
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net"
+ "net/http"
+ "time"
+
+ "github.com/bufbuild/connect-go"
+ "github.com/open-feature/flagd/pkg/eval"
+ "github.com/open-feature/flagd/pkg/model"
+ "github.com/rs/cors"
+ log "github.com/sirupsen/logrus"
+ schemaV1 "go.buf.build/open-feature/flagd-connect/open-feature/flagd/schema/v1"
+ schemaConnectV1 "go.buf.build/open-feature/flagd-connect/open-feature/flagd/schema/v1/schemav1connect"
+ "golang.org/x/net/http2"
+ "golang.org/x/net/http2/h2c"
+ "google.golang.org/protobuf/types/known/structpb"
+)
+
+const ErrorPrefix = "FlagdError:"
+
+type ConnectService struct {
+ Eval eval.IEvaluator
+ ConnectServiceConfiguration *ConnectServiceConfiguration
+ tls bool
+ server http.Server
+}
+
+type ConnectServiceConfiguration struct {
+ Port int32
+ ServerCertPath string
+ ServerKeyPath string
+ ServerSocketPath string
+ CORS []string
+}
+
+func (s *ConnectService) Serve(ctx context.Context, eval eval.IEvaluator) error {
+ s.Eval = eval
+
+ lis, err := s.setupServer()
+ if err != nil {
+ return err
+ }
+
+ errChan := make(chan error, 1)
+ go func() {
+ if s.tls {
+ if err := s.server.ServeTLS(
+ lis,
+ s.ConnectServiceConfiguration.ServerCertPath,
+ s.ConnectServiceConfiguration.ServerKeyPath,
+ ); err != nil && !errors.Is(err, http.ErrServerClosed) {
+ errChan <- err
+ }
+ } else {
+ if err := s.server.Serve(
+ lis,
+ ); err != nil && !errors.Is(err, http.ErrServerClosed) {
+ errChan <- err
+ }
+ }
+ close(errChan)
+ }()
+ <-ctx.Done()
+ if err := s.server.Shutdown(ctx); err != nil {
+ return err
+ }
+ return <-errChan
+}
+
+func (s *ConnectService) setupServer() (net.Listener, error) {
+ var lis net.Listener
+ var err error
+ mux := http.NewServeMux()
+ if s.ConnectServiceConfiguration.ServerSocketPath != "" {
+ lis, err = net.Listen("unix", s.ConnectServiceConfiguration.ServerSocketPath)
+ } else {
+ address := net.JoinHostPort("localhost", fmt.Sprintf("%d", s.ConnectServiceConfiguration.Port))
+ lis, err = net.Listen("tcp", address)
+ }
+ if err != nil {
+ return nil, err
+ }
+ path, handler := schemaConnectV1.NewServiceHandler(s)
+ mux.Handle(path, handler)
+ if s.ConnectServiceConfiguration.ServerCertPath != "" && s.ConnectServiceConfiguration.ServerKeyPath != "" {
+ s.tls = true
+ handler = s.newCORS().Handler(mux)
+ } else {
+ handler = h2c.NewHandler(
+ s.newCORS().Handler(mux),
+ &http2.Server{},
+ )
+ }
+ s.server = http.Server{
+ ReadTimeout: 2 * time.Second,
+ WriteTimeout: 4 * time.Second,
+ ReadHeaderTimeout: time.Second,
+ Handler: handler,
+ }
+ return lis, nil
+}
+
+func (s *ConnectService) ResolveBoolean(
+ ctx context.Context,
+ req *connect.Request[schemaV1.ResolveBooleanRequest],
+) (*connect.Response[schemaV1.ResolveBooleanResponse], error) {
+ res := connect.NewResponse(&schemaV1.ResolveBooleanResponse{})
+ result, variant, reason, err := s.Eval.ResolveBooleanValue(req.Msg.GetFlagKey(), req.Msg.GetContext())
+ if err != nil {
+ log.Error(err)
+ res.Msg.Reason = model.ErrorReason
+ return res, errFormat(err)
+ }
+ res.Msg.Reason = reason
+ res.Msg.Value = result
+ res.Msg.Variant = variant
+ return res, nil
+}
+
+func (s *ConnectService) ResolveString(
+ ctx context.Context,
+ req *connect.Request[schemaV1.ResolveStringRequest],
+) (*connect.Response[schemaV1.ResolveStringResponse], error) {
+ res := connect.NewResponse(&schemaV1.ResolveStringResponse{})
+ result, variant, reason, err := s.Eval.ResolveStringValue(req.Msg.GetFlagKey(), req.Msg.GetContext())
+ if err != nil {
+ log.Error(err)
+ res.Msg.Reason = model.ErrorReason
+ return res, errFormat(err)
+ }
+ res.Msg.Reason = reason
+ res.Msg.Value = result
+ res.Msg.Variant = variant
+ return res, nil
+}
+
+func (s *ConnectService) ResolveInt(
+ ctx context.Context,
+ req *connect.Request[schemaV1.ResolveIntRequest],
+) (*connect.Response[schemaV1.ResolveIntResponse], error) {
+ res := connect.NewResponse(&schemaV1.ResolveIntResponse{})
+ result, variant, reason, err := s.Eval.ResolveIntValue(req.Msg.GetFlagKey(), req.Msg.GetContext())
+ if err != nil {
+ log.Error(err)
+ res.Msg.Reason = model.ErrorReason
+ return res, errFormat(err)
+ }
+ res.Msg.Reason = reason
+ res.Msg.Value = result
+ res.Msg.Variant = variant
+ return res, nil
+}
+
+func (s *ConnectService) ResolveFloat(
+ ctx context.Context,
+ req *connect.Request[schemaV1.ResolveFloatRequest],
+) (*connect.Response[schemaV1.ResolveFloatResponse], error) {
+ res := connect.NewResponse(&schemaV1.ResolveFloatResponse{})
+ result, variant, reason, err := s.Eval.ResolveFloatValue(req.Msg.GetFlagKey(), req.Msg.GetContext())
+ if err != nil {
+ log.Error(err)
+ res.Msg.Reason = model.ErrorReason
+ return res, errFormat(err)
+ }
+ res.Msg.Reason = reason
+ res.Msg.Value = result
+ res.Msg.Variant = variant
+ return res, nil
+}
+
+func (s *ConnectService) ResolveObject(
+ ctx context.Context,
+ req *connect.Request[schemaV1.ResolveObjectRequest],
+) (*connect.Response[schemaV1.ResolveObjectResponse], error) {
+ res := connect.NewResponse(&schemaV1.ResolveObjectResponse{})
+ result, variant, reason, err := s.Eval.ResolveObjectValue(req.Msg.GetFlagKey(), req.Msg.GetContext())
+ if err != nil {
+ log.Error(err)
+ res.Msg.Reason = model.ErrorReason
+ return res, errFormat(err)
+ }
+ val, err := structpb.NewStruct(result)
+ if err != nil {
+ return res, err
+ }
+ res.Msg.Reason = reason
+ res.Msg.Value = val
+ res.Msg.Variant = variant
+ return res, nil
+}
+
+func (s *ConnectService) newCORS() *cors.Cors {
+ return cors.New(cors.Options{
+ AllowedMethods: []string{
+ http.MethodHead,
+ http.MethodGet,
+ http.MethodPost,
+ http.MethodPut,
+ http.MethodPatch,
+ http.MethodDelete,
+ },
+ AllowedOrigins: s.ConnectServiceConfiguration.CORS,
+ AllowedHeaders: []string{"*"},
+ ExposedHeaders: []string{
+ // Content-Type is in the default safelist.
+ "Accept",
+ "Accept-Encoding",
+ "Accept-Post",
+ "Connect-Accept-Encoding",
+ "Connect-Content-Encoding",
+ "Content-Encoding",
+ "Grpc-Accept-Encoding",
+ "Grpc-Encoding",
+ "Grpc-Message",
+ "Grpc-Status",
+ "Grpc-Status-Details-Bin",
+ },
+ })
+}
+
+func errFormat(err error) error {
+ switch err.Error() {
+ case model.FlagNotFoundErrorCode:
+ 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:
+ 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()))
+ }
+
+ return err
+}
diff --git a/pkg/service/grpc_service_test.go b/pkg/service/connect_service_test.go
similarity index 63%
rename from pkg/service/grpc_service_test.go
rename to pkg/service/connect_service_test.go
index dd8436d4b..a4d3f500b 100644
--- a/pkg/service/grpc_service_test.go
+++ b/pkg/service/connect_service_test.go
@@ -8,11 +8,12 @@ import (
"testing"
"time"
+ "github.com/bufbuild/connect-go"
"github.com/golang/mock/gomock"
"github.com/open-feature/flagd/pkg/model"
service "github.com/open-feature/flagd/pkg/service"
log "github.com/sirupsen/logrus"
- gen "go.buf.build/open-feature/flagd-server/open-feature/flagd/schema/v1"
+ gen "go.buf.build/open-feature/flagd-connect/open-feature/flagd/schema/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/protobuf/types/known/structpb"
@@ -32,10 +33,9 @@ type resolveBooleanEvalFields struct {
result bool
variant string
reason string
- err error
}
-func TestGRPCService_UnixConnection(t *testing.T) {
+func TestConnectService_UnixConnection(t *testing.T) {
type evalFields struct {
result bool
variant string
@@ -61,7 +61,7 @@ func TestGRPCService_UnixConnection(t *testing.T) {
err: nil,
},
req: &gen.ResolveBooleanRequest{
- FlagKey: "bool",
+ FlagKey: "myBoolFlag",
Context: &structpb.Struct{},
},
want: &gen.ResolveBooleanResponse{
@@ -82,8 +82,8 @@ func TestGRPCService_UnixConnection(t *testing.T) {
tt.evalFields.reason,
tt.evalFields.err,
).AnyTimes()
- service := service.GRPCService{
- GRPCServiceConfiguration: &service.GRPCServiceConfiguration{
+ service := service.ConnectService{
+ ConnectServiceConfiguration: &service.ConnectServiceConfiguration{
ServerSocketPath: tt.socketPath,
},
}
@@ -92,11 +92,11 @@ func TestGRPCService_UnixConnection(t *testing.T) {
defer cancel()
go func() { _ = service.Serve(ctx, eval) }()
-
conn, err := grpc.Dial(
- fmt.Sprintf("passthrough:///unix://%s", tt.socketPath),
+ fmt.Sprintf("unix://%s", tt.socketPath),
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
+ grpc.WithTimeout(2*time.Second),
)
if err != nil {
log.Errorf("grpc - fail to dial: %v", err)
@@ -107,32 +107,30 @@ func TestGRPCService_UnixConnection(t *testing.T) {
)
res, err := client.ResolveBoolean(ctx, tt.req)
if (err != nil) && !errors.Is(err, tt.wantErr) {
- t.Errorf("GRPCService.ResolveBoolean() error = %v, wantErr %v", err, tt.wantErr)
+ t.Errorf("ConnectService.ResolveBoolean() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(res.Reason, tt.want.Reason) {
- t.Errorf("GRPCService.ResolveBoolean() = %v, want %v", res, tt.want)
+ t.Errorf("ConnectService.ResolveBoolean() = %v, want %v", res, tt.want)
}
if !reflect.DeepEqual(res.Value, tt.want.Value) {
- t.Errorf("GRPCService.ResolveBoolean() = %v, want %v", res, tt.want)
+ t.Errorf("ConnectService.ResolveBoolean() = %v, want %v", res, tt.want)
}
if !reflect.DeepEqual(res.Variant, tt.want.Variant) {
- t.Errorf("GRPCService.ResolveBoolean() = %v, want %v", res, tt.want)
+ t.Errorf("ConnectService.ResolveBoolean() = %v, want %v", res, tt.want)
}
})
}
}
-func TestGRPCService_ResolveBoolean(t *testing.T) {
+func TestConnectService_ResolveBoolean(t *testing.T) {
ctrl := gomock.NewController(t)
- grpcS := service.GRPCService{}
tests := map[string]resolveBooleanArgs{
"happy path": {
evalFields: resolveBooleanEvalFields{
result: true,
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveBooleanFunctionArgs{
context.Background(),
@@ -152,8 +150,7 @@ func TestGRPCService_ResolveBoolean(t *testing.T) {
evalFields: resolveBooleanEvalFields{
result: true,
variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
+ reason: model.ErrorReason,
},
functionArgs: resolveBooleanFunctionArgs{
context.Background(),
@@ -162,8 +159,10 @@ func TestGRPCService_ResolveBoolean(t *testing.T) {
Context: &structpb.Struct{},
},
},
- want: &gen.ResolveBooleanResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
+ want: &gen.ResolveBooleanResponse{
+ Reason: model.ErrorReason,
+ },
+ wantErr: errors.New("eval interface error"),
},
}
for name, tt := range tests {
@@ -173,33 +172,31 @@ func TestGRPCService_ResolveBoolean(t *testing.T) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
- got, err := s.ResolveBoolean(tt.functionArgs.ctx, tt.functionArgs.req)
- if (err != nil) && !errors.Is(err, tt.wantErr) {
- t.Errorf("GRPCService.ResolveBoolean() error = %v, wantErr %v", err, tt.wantErr)
+ got, err := s.ResolveBoolean(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
+ if err != nil && !errors.Is(err, tt.wantErr) {
+ t.Errorf("ConnectService.ResolveBoolean() error = %v, wantErr %v", err.Error(), tt.wantErr.Error())
return
}
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("GRPCService.ResolveBoolean() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg, tt.want) {
+ t.Errorf("ConnectService.ResolveBoolean() = %v, want %v", got, tt.want)
}
})
}
}
-func BenchmarkGRPCService_ResolveBoolean(b *testing.B) {
+func BenchmarkConnectService_ResolveBoolean(b *testing.B) {
ctrl := gomock.NewController(b)
- grpcS := service.GRPCService{}
tests := map[string]resolveBooleanArgs{
"happy path": {
evalFields: resolveBooleanEvalFields{
result: true,
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveBooleanFunctionArgs{
context.Background(),
@@ -215,23 +212,6 @@ func BenchmarkGRPCService_ResolveBoolean(b *testing.B) {
},
wantErr: nil,
},
- "eval returns error": {
- evalFields: resolveBooleanEvalFields{
- result: true,
- variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
- },
- functionArgs: resolveBooleanFunctionArgs{
- context.Background(),
- &gen.ResolveBooleanRequest{
- FlagKey: "bool",
- Context: &structpb.Struct{},
- },
- },
- want: &gen.ResolveBooleanResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
- },
}
for name, tt := range tests {
eval := NewMockIEvaluator(ctrl)
@@ -239,20 +219,20 @@ func BenchmarkGRPCService_ResolveBoolean(b *testing.B) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
- got, err := s.ResolveBoolean(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveBoolean(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- b.Errorf("GRPCService.ResolveBoolean() error = %v, wantErr %v", err, tt.wantErr)
+ b.Errorf("ConnectService.ResolveBoolean() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got, tt.want) {
- b.Errorf("GRPCService.ResolveBoolean() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg, tt.want) {
+ b.Errorf("ConnectService.ResolveBoolean() = %v, want %v", got, tt.want)
}
}
})
@@ -273,19 +253,16 @@ type resolveStringEvalFields struct {
result string
variant string
reason string
- err error
}
-func TestGRPCService_ResolveString(t *testing.T) {
+func TestConnectService_ResolveString(t *testing.T) {
ctrl := gomock.NewController(t)
- grpcS := service.GRPCService{}
tests := map[string]resolveStringArgs{
"happy path": {
evalFields: resolveStringEvalFields{
result: "true",
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveStringFunctionArgs{
context.Background(),
@@ -305,8 +282,7 @@ func TestGRPCService_ResolveString(t *testing.T) {
evalFields: resolveStringEvalFields{
result: "true",
variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
+ reason: model.ErrorReason,
},
functionArgs: resolveStringFunctionArgs{
context.Background(),
@@ -315,8 +291,10 @@ func TestGRPCService_ResolveString(t *testing.T) {
Context: &structpb.Struct{},
},
},
- want: &gen.ResolveStringResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
+ want: &gen.ResolveStringResponse{
+ Reason: model.ErrorReason,
+ },
+ wantErr: errors.New("eval interface error"),
},
}
for name, tt := range tests {
@@ -326,33 +304,31 @@ func TestGRPCService_ResolveString(t *testing.T) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
)
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
- got, err := s.ResolveString(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveString(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- t.Errorf("GRPCService.ResolveString() error = %v, wantErr %v", err, tt.wantErr)
+ t.Errorf("ConnectService.ResolveString() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("GRPCService.ResolveString() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg, tt.want) {
+ t.Errorf("ConnectService.ResolveString() = %v, want %v", got, tt.want)
}
})
}
}
-func BenchmarkGRPCService_ResolveString(b *testing.B) {
+func BenchmarkConnectService_ResolveString(b *testing.B) {
ctrl := gomock.NewController(b)
- grpcS := service.GRPCService{}
tests := map[string]resolveStringArgs{
"happy path": {
evalFields: resolveStringEvalFields{
result: "true",
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveStringFunctionArgs{
context.Background(),
@@ -368,23 +344,6 @@ func BenchmarkGRPCService_ResolveString(b *testing.B) {
},
wantErr: nil,
},
- "eval returns error": {
- evalFields: resolveStringEvalFields{
- result: "true",
- variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
- },
- functionArgs: resolveStringFunctionArgs{
- context.Background(),
- &gen.ResolveStringRequest{
- FlagKey: "string",
- Context: &structpb.Struct{},
- },
- },
- want: &gen.ResolveStringResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
- },
}
for name, tt := range tests {
eval := NewMockIEvaluator(ctrl)
@@ -392,20 +351,20 @@ func BenchmarkGRPCService_ResolveString(b *testing.B) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
- got, err := s.ResolveString(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveString(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- b.Errorf("GRPCService.ResolveString() error = %v, wantErr %v", err, tt.wantErr)
+ b.Errorf("ConnectService.ResolveString() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got, tt.want) {
- b.Errorf("GRPCService.ResolveString() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg, tt.want) {
+ b.Errorf("ConnectService.ResolveString() = %v, want %v", got, tt.want)
}
}
})
@@ -426,19 +385,16 @@ type resolveFloatEvalFields struct {
result float64
variant string
reason string
- err error
}
-func TestGRPCService_ResolveFloat(t *testing.T) {
+func TestConnectService_ResolveFloat(t *testing.T) {
ctrl := gomock.NewController(t)
- grpcS := service.GRPCService{}
tests := map[string]resolveFloatArgs{
"happy path": {
evalFields: resolveFloatEvalFields{
result: 12,
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveFloatFunctionArgs{
context.Background(),
@@ -458,8 +414,7 @@ func TestGRPCService_ResolveFloat(t *testing.T) {
evalFields: resolveFloatEvalFields{
result: 12,
variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
+ reason: model.ErrorReason,
},
functionArgs: resolveFloatFunctionArgs{
context.Background(),
@@ -468,8 +423,10 @@ func TestGRPCService_ResolveFloat(t *testing.T) {
Context: &structpb.Struct{},
},
},
- want: &gen.ResolveFloatResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
+ want: &gen.ResolveFloatResponse{
+ Reason: model.ErrorReason,
+ },
+ wantErr: errors.New("eval interface error"),
},
}
for name, tt := range tests {
@@ -479,33 +436,31 @@ func TestGRPCService_ResolveFloat(t *testing.T) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
- got, err := s.ResolveFloat(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveFloat(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- t.Errorf("GRPCService.ResolveNumber() error = %v, wantErr %v", err, tt.wantErr)
+ t.Errorf("ConnectService.ResolveNumber() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("GRPCService.ResolveNumber() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg, tt.want) {
+ t.Errorf("ConnectService.ResolveNumber() = %v, want %v", got, tt.want)
}
})
}
}
-func BenchmarkGRPCService_ResolveFloat(b *testing.B) {
+func BenchmarkConnectService_ResolveFloat(b *testing.B) {
ctrl := gomock.NewController(b)
- grpcS := service.GRPCService{}
tests := map[string]resolveFloatArgs{
"happy path": {
evalFields: resolveFloatEvalFields{
result: 12,
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveFloatFunctionArgs{
context.Background(),
@@ -521,23 +476,6 @@ func BenchmarkGRPCService_ResolveFloat(b *testing.B) {
},
wantErr: nil,
},
- "eval returns error": {
- evalFields: resolveFloatEvalFields{
- result: 12,
- variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
- },
- functionArgs: resolveFloatFunctionArgs{
- context.Background(),
- &gen.ResolveFloatRequest{
- FlagKey: "float",
- Context: &structpb.Struct{},
- },
- },
- want: &gen.ResolveFloatResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
- },
}
for name, tt := range tests {
eval := NewMockIEvaluator(ctrl)
@@ -545,20 +483,20 @@ func BenchmarkGRPCService_ResolveFloat(b *testing.B) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
- got, err := s.ResolveFloat(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveFloat(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- b.Errorf("GRPCService.ResolveNumber() error = %v, wantErr %v", err, tt.wantErr)
+ b.Errorf("ConnectService.ResolveNumber() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got, tt.want) {
- b.Errorf("GRPCService.ResolveNumber() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg, tt.want) {
+ b.Errorf("ConnectService.ResolveNumber() = %v, want %v", got, tt.want)
}
}
})
@@ -579,19 +517,16 @@ type resolveIntEvalFields struct {
result int64
variant string
reason string
- err error
}
-func TestGRPCService_ResolveInt(t *testing.T) {
+func TestConnectService_ResolveInt(t *testing.T) {
ctrl := gomock.NewController(t)
- grpcS := service.GRPCService{}
tests := map[string]resolveIntArgs{
"happy path": {
evalFields: resolveIntEvalFields{
result: 12,
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveIntFunctionArgs{
context.Background(),
@@ -611,8 +546,7 @@ func TestGRPCService_ResolveInt(t *testing.T) {
evalFields: resolveIntEvalFields{
result: 12,
variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
+ reason: model.ErrorReason,
},
functionArgs: resolveIntFunctionArgs{
context.Background(),
@@ -621,8 +555,10 @@ func TestGRPCService_ResolveInt(t *testing.T) {
Context: &structpb.Struct{},
},
},
- want: &gen.ResolveIntResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
+ want: &gen.ResolveIntResponse{
+ Reason: model.ErrorReason,
+ },
+ wantErr: errors.New("eval interface error"),
},
}
for name, tt := range tests {
@@ -632,33 +568,31 @@ func TestGRPCService_ResolveInt(t *testing.T) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
- got, err := s.ResolveInt(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveInt(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- t.Errorf("GRPCService.ResolveNumber() error = %v, wantErr %v", err, tt.wantErr)
+ t.Errorf("ConnectService.ResolveNumber() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("GRPCService.ResolveNumber() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg, tt.want) {
+ t.Errorf("ConnectService.ResolveNumber() = %v, want %v", got, tt.want)
}
})
}
}
-func BenchmarkGRPCService_ResolveInt(b *testing.B) {
+func BenchmarkConnectService_ResolveInt(b *testing.B) {
ctrl := gomock.NewController(b)
- grpcS := service.GRPCService{}
tests := map[string]resolveIntArgs{
"happy path": {
evalFields: resolveIntEvalFields{
result: 12,
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveIntFunctionArgs{
context.Background(),
@@ -674,23 +608,6 @@ func BenchmarkGRPCService_ResolveInt(b *testing.B) {
},
wantErr: nil,
},
- "eval returns error": {
- evalFields: resolveIntEvalFields{
- result: 12,
- variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
- },
- functionArgs: resolveIntFunctionArgs{
- context.Background(),
- &gen.ResolveIntRequest{
- FlagKey: "int",
- Context: &structpb.Struct{},
- },
- },
- want: &gen.ResolveIntResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
- },
}
for name, tt := range tests {
eval := NewMockIEvaluator(ctrl)
@@ -698,20 +615,20 @@ func BenchmarkGRPCService_ResolveInt(b *testing.B) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
- got, err := s.ResolveInt(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveInt(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- b.Errorf("GRPCService.ResolveNumber() error = %v, wantErr %v", err, tt.wantErr)
+ b.Errorf("ConnectService.ResolveNumber() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got, tt.want) {
- b.Errorf("GRPCService.ResolveNumber() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg, tt.want) {
+ b.Errorf("ConnectService.ResolveNumber() = %v, want %v", got, tt.want)
}
}
})
@@ -732,12 +649,10 @@ type resolveObjectEvalFields struct {
result map[string]interface{}
variant string
reason string
- err error
}
-func TestGRPCService_ResolveObject(t *testing.T) {
+func TestConnectService_ResolveObject(t *testing.T) {
ctrl := gomock.NewController(t)
- grpcS := service.GRPCService{}
tests := map[string]resolveObjectArgs{
"happy path": {
evalFields: resolveObjectEvalFields{
@@ -746,7 +661,6 @@ func TestGRPCService_ResolveObject(t *testing.T) {
},
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveObjectFunctionArgs{
context.Background(),
@@ -768,8 +682,7 @@ func TestGRPCService_ResolveObject(t *testing.T) {
"food": "bars",
},
variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
+ reason: model.ErrorReason,
},
functionArgs: resolveObjectFunctionArgs{
context.Background(),
@@ -778,8 +691,10 @@ func TestGRPCService_ResolveObject(t *testing.T) {
Context: &structpb.Struct{},
},
},
- want: &gen.ResolveObjectResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
+ want: &gen.ResolveObjectResponse{
+ Reason: model.ErrorReason,
+ },
+ wantErr: errors.New("eval interface error"),
},
}
for name, tt := range tests {
@@ -789,9 +704,9 @@ func TestGRPCService_ResolveObject(t *testing.T) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
@@ -802,21 +717,20 @@ func TestGRPCService_ResolveObject(t *testing.T) {
}
tt.want.Value = outParsed
}
- got, err := s.ResolveObject(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveObject(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- t.Errorf("GRPCService.ResolveObject() error = %v, wantErr %v", err, tt.wantErr)
+ t.Errorf("ConnectService.ResolveObject() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got.Value.AsMap(), tt.want.Value.AsMap()) {
- t.Errorf("GRPCService.ResolveObject() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg.Value.AsMap(), tt.want.Value.AsMap()) {
+ t.Errorf("ConnectService.ResolveObject() = %v, want %v", got, tt.want)
}
})
}
}
-func BenchmarkGRPCService_ResolveObject(b *testing.B) {
+func BenchmarkConnectService_ResolveObject(b *testing.B) {
ctrl := gomock.NewController(b)
- grpcS := service.GRPCService{}
tests := map[string]resolveObjectArgs{
"happy path": {
evalFields: resolveObjectEvalFields{
@@ -825,7 +739,6 @@ func BenchmarkGRPCService_ResolveObject(b *testing.B) {
},
variant: "on",
reason: model.DefaultReason,
- err: nil,
},
functionArgs: resolveObjectFunctionArgs{
context.Background(),
@@ -841,25 +754,6 @@ func BenchmarkGRPCService_ResolveObject(b *testing.B) {
},
wantErr: nil,
},
- "eval returns error": {
- evalFields: resolveObjectEvalFields{
- result: map[string]interface{}{
- "food": "bars",
- },
- variant: ":(",
- reason: "ERROR",
- err: errors.New("eval interface error"),
- },
- functionArgs: resolveObjectFunctionArgs{
- context.Background(),
- &gen.ResolveObjectRequest{
- FlagKey: "object",
- Context: &structpb.Struct{},
- },
- },
- want: &gen.ResolveObjectResponse{},
- wantErr: grpcS.HandleEvaluationError(errors.New("eval interface error"), "ERROR"),
- },
}
for name, tt := range tests {
eval := NewMockIEvaluator(ctrl)
@@ -867,9 +761,9 @@ func BenchmarkGRPCService_ResolveObject(b *testing.B) {
tt.evalFields.result,
tt.evalFields.variant,
tt.evalFields.reason,
- tt.evalFields.err,
+ tt.wantErr,
).AnyTimes()
- s := service.GRPCService{
+ s := service.ConnectService{
Eval: eval,
}
if name != "eval returns error" {
@@ -881,13 +775,13 @@ func BenchmarkGRPCService_ResolveObject(b *testing.B) {
}
b.Run(name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
- got, err := s.ResolveObject(tt.functionArgs.ctx, tt.functionArgs.req)
+ got, err := s.ResolveObject(tt.functionArgs.ctx, connect.NewRequest(tt.functionArgs.req))
if (err != nil) && !errors.Is(err, tt.wantErr) {
- b.Errorf("GRPCService.ResolveObject() error = %v, wantErr %v", err, tt.wantErr)
+ b.Errorf("ConnectService.ResolveObject() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(got.Value.AsMap(), tt.want.Value.AsMap()) {
- b.Errorf("GRPCService.ResolveObject() = %v, want %v", got, tt.want)
+ if !reflect.DeepEqual(got.Msg.Value.AsMap(), tt.want.Value.AsMap()) {
+ b.Errorf("ConnectService.ResolveObject() = %v, want %v", got, tt.want)
}
}
})
diff --git a/pkg/service/grpc_service.go b/pkg/service/grpc_service.go
deleted file mode 100644
index 3adaa3190..000000000
--- a/pkg/service/grpc_service.go
+++ /dev/null
@@ -1,171 +0,0 @@
-package service
-
-import (
- "context"
- "fmt"
- "net"
-
- "github.com/open-feature/flagd/pkg/eval"
- "github.com/open-feature/flagd/pkg/model"
- log "github.com/sirupsen/logrus"
- gen "go.buf.build/open-feature/flagd-server/open-feature/flagd/schema/v1"
- "golang.org/x/sync/errgroup"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/credentials"
- "google.golang.org/grpc/status"
- "google.golang.org/protobuf/types/known/structpb"
-)
-
-type GRPCServiceConfiguration struct {
- Port int32
- ServerKeyPath string
- ServerCertPath string
- ServerSocketPath string
-}
-
-type GRPCService struct {
- GRPCServiceConfiguration *GRPCServiceConfiguration
- Eval eval.IEvaluator
- gen.UnimplementedServiceServer
- Logger *log.Entry
-}
-
-// Serve allows for the use of GRPC only without HTTP, where as HTTP service enables both
-// GRPC and HTTP
-func (s *GRPCService) Serve(ctx context.Context, eval eval.IEvaluator) error {
- var lis net.Listener
- var err error
- g, gCtx := errgroup.WithContext(ctx)
- s.Eval = eval
-
- // TLS
- var serverOpts []grpc.ServerOption
- if s.GRPCServiceConfiguration.ServerCertPath != "" && s.GRPCServiceConfiguration.ServerKeyPath != "" {
- config, err := loadTLSConfig(s.GRPCServiceConfiguration.ServerCertPath, s.GRPCServiceConfiguration.ServerKeyPath)
- if err != nil {
- return err
- }
- serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(config)))
- }
-
- grpcServer := grpc.NewServer(serverOpts...)
- gen.RegisterServiceServer(grpcServer, s)
-
- if s.GRPCServiceConfiguration.ServerSocketPath != "" {
- lis, err = net.Listen("unix", s.GRPCServiceConfiguration.ServerSocketPath)
- } else {
- lis, err = net.Listen("tcp", fmt.Sprintf(":%d", s.GRPCServiceConfiguration.Port))
- }
- if err != nil {
- return err
- }
-
- g.Go(func() error {
- return grpcServer.Serve(lis)
- })
- <-gCtx.Done()
- grpcServer.GracefulStop()
- return nil
-}
-
-// TODO: might be able to simplify some of this with generics.
-func (s *GRPCService) ResolveBoolean(
- ctx context.Context,
- req *gen.ResolveBooleanRequest,
-) (*gen.ResolveBooleanResponse, error) {
- res := gen.ResolveBooleanResponse{}
- result, variant, reason, err := s.Eval.ResolveBooleanValue(req.GetFlagKey(), req.GetContext())
- if err != nil {
- return &res, s.HandleEvaluationError(err, reason)
- }
- res.Reason = reason
- res.Value = result
- res.Variant = variant
- return &res, nil
-}
-
-func (s *GRPCService) ResolveString(
- ctx context.Context,
- req *gen.ResolveStringRequest,
-) (*gen.ResolveStringResponse, error) {
- res := gen.ResolveStringResponse{}
- result, variant, reason, err := s.Eval.ResolveStringValue(req.GetFlagKey(), req.GetContext())
- if err != nil {
- return &res, s.HandleEvaluationError(err, reason)
- }
- res.Reason = reason
- res.Value = result
- res.Variant = variant
- return &res, nil
-}
-
-func (s *GRPCService) ResolveInt(
- ctx context.Context,
- req *gen.ResolveIntRequest,
-) (*gen.ResolveIntResponse, error) {
- res := gen.ResolveIntResponse{}
- result, variant, reason, err := s.Eval.ResolveIntValue(req.GetFlagKey(), req.GetContext())
- if err != nil {
- return &res, s.HandleEvaluationError(err, reason)
- }
- res.Reason = reason
- res.Value = result
- res.Variant = variant
- return &res, nil
-}
-
-func (s *GRPCService) ResolveFloat(
- ctx context.Context,
- req *gen.ResolveFloatRequest,
-) (*gen.ResolveFloatResponse, error) {
- res := gen.ResolveFloatResponse{}
- result, variant, reason, err := s.Eval.ResolveFloatValue(req.GetFlagKey(), req.GetContext())
- if err != nil {
- return &res, s.HandleEvaluationError(err, reason)
- }
- res.Reason = reason
- res.Value = result
- res.Variant = variant
- return &res, nil
-}
-
-func (s *GRPCService) ResolveObject(
- ctx context.Context,
- req *gen.ResolveObjectRequest,
-) (*gen.ResolveObjectResponse, error) {
- res := gen.ResolveObjectResponse{}
- result, variant, reason, err := s.Eval.ResolveObjectValue(req.GetFlagKey(), req.GetContext())
- if err != nil {
- return &res, s.HandleEvaluationError(err, reason)
- }
- val, err := structpb.NewStruct(result)
- if err != nil {
- return &res, s.HandleEvaluationError(err, reason)
- }
- res.Reason = reason
- res.Value = val
- res.Variant = variant
- return &res, nil
-}
-
-func (s *GRPCService) HandleEvaluationError(err error, reason string) error {
- statusCode := codes.Internal
- message := err.Error()
- switch message {
- case model.FlagNotFoundErrorCode:
- statusCode = codes.NotFound
- case model.TypeMismatchErrorCode:
- statusCode = codes.InvalidArgument
- }
- st := status.New(statusCode, message)
- stWD, err := st.WithDetails(&gen.ErrorResponse{
- ErrorCode: message,
- Reason: "ERROR",
- })
- if err != nil {
- s.Logger.Error(err)
- return st.Err()
- }
- return stWD.Err()
-}
diff --git a/pkg/service/http_service.go b/pkg/service/http_service.go
deleted file mode 100644
index c12d89ca5..000000000
--- a/pkg/service/http_service.go
+++ /dev/null
@@ -1,194 +0,0 @@
-package service
-
-import (
- "context"
- "crypto/tls"
- "encoding/json"
- "errors"
- "fmt"
- "net"
- "net/http"
- "time"
-
- "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
- "github.com/open-feature/flagd/pkg/eval"
- log "github.com/sirupsen/logrus"
- "github.com/soheilhy/cmux"
- gen "go.buf.build/open-feature/flagd-server/open-feature/flagd/schema/v1"
- "golang.org/x/sync/errgroup"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/credentials/insecure"
- "google.golang.org/grpc/status"
-)
-
-type HTTPServiceConfiguration struct {
- Port int32
- ServerCertPath string
- ServerKeyPath string
- ServerSocketPath string
-}
-
-type HTTPService struct {
- HTTPServiceConfiguration *HTTPServiceConfiguration
- GRPCService *GRPCService
- Logger *log.Entry
-}
-
-func (s *HTTPService) tlsListener(l net.Listener) net.Listener {
- // Load TLS config
- config, err := loadTLSConfig(s.HTTPServiceConfiguration.ServerCertPath,
- s.HTTPServiceConfiguration.ServerKeyPath)
- if err != nil {
- log.Fatal(err)
- }
-
- tlsl := tls.NewListener(l, config)
- return tlsl
-}
-
-func (s *HTTPService) ServerGRPC(ctx context.Context, mux *runtime.ServeMux) *grpc.Server {
- var address string
- var dialOpts []grpc.DialOption
- var err error
- // handle cert
- if s.HTTPServiceConfiguration.ServerCertPath != "" && s.HTTPServiceConfiguration.ServerKeyPath != "" {
- tlsCreds, err := loadTLSCredentials(s.HTTPServiceConfiguration.ServerCertPath,
- s.HTTPServiceConfiguration.ServerKeyPath)
- if err != nil {
- log.Fatal(err)
- }
- dialOpts = append(dialOpts, grpc.WithTransportCredentials(tlsCreds))
- } else {
- dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
- }
- // handle unix socket
- if s.HTTPServiceConfiguration.ServerSocketPath != "" {
- address = s.HTTPServiceConfiguration.ServerSocketPath
- dialOpts = append(dialOpts, grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
- return net.Dial("unix", addr)
- }))
- } else {
- address = net.JoinHostPort("localhost", fmt.Sprintf("%d", s.HTTPServiceConfiguration.Port))
- }
- grpcServer := grpc.NewServer()
- gen.RegisterServiceServer(grpcServer, s.GRPCService)
- err = gen.RegisterServiceHandlerFromEndpoint(
- ctx,
- mux,
- address,
- dialOpts,
- )
- if err != nil {
- log.Fatal(err)
- }
- return grpcServer
-}
-
-func (s *HTTPService) ServeHTTP(mux *runtime.ServeMux) *http.Server {
- server := &http.Server{
- Handler: mux,
- ReadHeaderTimeout: 60 * time.Second,
- }
-
- return server
-}
-
-func (s *HTTPService) Serve(ctx context.Context, eval eval.IEvaluator) error {
- s.GRPCService.Eval = eval
- g, gCtx := errgroup.WithContext(ctx)
- // Mux Setup
- mux := runtime.NewServeMux(
- runtime.WithErrorHandler(s.HTTPErrorHandler),
- )
- // GRPC Setup
- grpcServer := s.ServerGRPC(ctx, mux)
- // HTTP Setup
- httpServer := s.ServeHTTP(mux)
- // Net listener
- l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.HTTPServiceConfiguration.Port))
- if err != nil {
- return err
- }
- tcpm := cmux.New(l)
- // We first match on HTTP 1.1 methods.
- httpl := tcpm.Match(cmux.HTTP1Fast())
- // If not matched, we assume that its TLS.
- tlsl := tcpm.Match(cmux.Any())
- if s.HTTPServiceConfiguration.ServerCertPath != "" && s.HTTPServiceConfiguration.ServerKeyPath != "" {
- tlsl = s.tlsListener(tlsl)
- }
- // Now, we build another mux recursively to match HTTPS and GoRPC.
- tlsm := cmux.New(tlsl)
- httpsl := tlsm.Match(cmux.HTTP1Fast())
- var gorpcl net.Listener
- if s.HTTPServiceConfiguration.ServerSocketPath != "" {
- gorpcl, err = net.Listen("unix", s.HTTPServiceConfiguration.ServerSocketPath)
- if err != nil {
- return err
- }
- } else {
- gorpcl = tlsm.Match(cmux.Any())
- }
- g.Go(func() error {
- return httpServer.Serve(httpl) // HTTP
- })
- g.Go(func() error {
- return httpServer.Serve(httpsl) // HTTPS
- })
- g.Go(func() error {
- return grpcServer.Serve(gorpcl) // GRPC
- })
- g.Go(func() error {
- return tlsm.Serve()
- })
- g.Go(func() error {
- return tcpm.Serve()
- })
- <-gCtx.Done()
- grpcServer.GracefulStop()
- if err = httpServer.Shutdown(context.Background()); err != nil {
- return err
- }
- err = g.Wait()
- if err != nil && !errors.Is(err, grpc.ErrServerStopped) && !errors.Is(err, http.ErrServerClosed) {
- return err
- }
- return nil
-}
-
-func (s HTTPService) HTTPErrorHandler(
- ctx context.Context,
- m *runtime.ServeMux,
- ma runtime.Marshaler,
- w http.ResponseWriter,
- r *http.Request,
- err error,
-) {
- st := status.Convert(err)
- switch {
- case st.Code() == codes.Unknown:
- w.WriteHeader(http.StatusInternalServerError)
- case st.Code() == codes.InvalidArgument:
- w.WriteHeader(http.StatusBadRequest)
- case st.Code() == codes.NotFound:
- w.WriteHeader(http.StatusNotFound)
- default:
- w.WriteHeader(http.StatusInternalServerError)
- }
- details := st.Details()
- if len(details) != 1 {
- log.Error(err)
- log.Errorf("malformed error received by error handler, details received: %d - %v", len(details), details)
- return
- }
- var res []byte
- if res, err = json.Marshal(details[0]); err != nil {
- log.Error(err)
- return
- }
- if _, err = w.Write(res); err != nil {
- log.Error(err)
- return
- }
-}
diff --git a/pkg/service/utils.go b/pkg/service/utils.go
deleted file mode 100644
index 78b956ed5..000000000
--- a/pkg/service/utils.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package service
-
-import (
- "crypto/rand"
- "crypto/tls"
-
- "google.golang.org/grpc/credentials"
-)
-
-func loadTLSCredentials(serverCertPath string, serverKeyPath string) (credentials.TransportCredentials, error) {
- // Load server's certificate and private key
- creds, err := credentials.NewServerTLSFromFile(serverCertPath, serverKeyPath)
- if err != nil {
- return nil, err
- }
-
- return creds, nil
-}
-
-func loadTLSConfig(certPath, keyPath string) (*tls.Config, error) {
- certificate, err := tls.LoadX509KeyPair(certPath,
- keyPath)
- if err != nil {
- return nil, err
- }
-
- config := &tls.Config{
- Certificates: []tls.Certificate{certificate},
- Rand: rand.Reader,
- MinVersion: tls.VersionTLS12,
- }
-
- return config, nil
-}