-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added custom grpc resolver (#1424)
## This PR Added custom gRPC resolver to support envoy proxy - support gRPC custom resolver ### Related Issues Fixes open-feature/go-sdk-contrib#585 ### Notes - update `.github/workflow/build.yaml` to install `envoy` [binary](https://www.envoyproxy.io/docs/envoy/latest/start/install#install-binaries) part of e2e test. - this is a pre-requisite for `flagd` go [provider update](open-feature/go-sdk-contrib#585) ### How to test Unit test are already added for the custom resolver for integration test you need a working envoy proxy support or any of the supported core resolver mentioned [here](https://grpc.io/docs/guides/custom-name-resolution/#overview) ```shell bin % ./flagd start -x --uri envoy://localhost:9211/test.service ______ __ ________ _______ ______ /_____/\ /_/\ /_______/\ /______/\ /_____/\ \::::_\/_\:\ \ \::: _ \ \\::::__\/__\:::_ \ \ \:\/___/\\:\ \ \::(_) \ \\:\ /____/\\:\ \ \ \ \:::._\/ \:\ \____\:: __ \ \\:\\_ _\/ \:\ \ \ \ \:\ \ \:\/___/\\:.\ \ \ \\:\_\ \ \ \:\/.:| | \_\/ \_____\/ \__\/\__\/ \_____\/ \____/_/ 2024-10-14T20:19:51.411+0200 info cmd/start.go:120 flagd version: dev (f716423), built at: 2024-10-14T20:19:34Z {"component": "start"} 2024-10-14T20:19:51.412+0200 debug telemetry/builder.go:81 skipping trace provider setup as collector target is not set. Traces will use NoopTracerProvider provider and propagator will use no-Op TextMapPropagator 2024-10-14T20:19:51.412+0200 info flag-sync/sync_service.go:54 starting flag sync service on port 8015 {"component": "FlagSyncService"} 2024-10-14T20:19:51.412+0200 debug builder/syncbuilder.go:111 using grpc sync-provider for: envoy://localhost:9211/test.service {"component": "sync"} 2024-10-14T20:19:51.413+0200 info flag-evaluation/connect_service.go:247 metrics and probes listening at 8014 {"component": "service"} 2024-10-14T20:19:51.413+0200 info ofrep/ofrep_service.go:56 ofrep service listening at 8016 {"component": "OFREPService"} 2024-10-14T20:19:51.415+0200 info flag-evaluation/connect_service.go:227 Flag IResolver listening at [::]:8013 {"component": "service"} 2024-10-14T20:19:51.428+0200 debug grpc/grpc_sync.go:201 received full configuration payload {"component": "sync", "sync": "grpc"} 2024-10-14T20:19:55.057+0200 debug grpc/grpc_sync.go:201 received full configuration payload {"component": "sync", "sync": "grpc"} ``` --------- Signed-off-by: Pradeep <[email protected]> Signed-off-by: Matthew Wilson <[email protected]> Co-authored-by: Matthew Wilson <[email protected]>
- Loading branch information
1 parent
02d881e
commit e5007e2
Showing
8 changed files
with
272 additions
and
332 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package nameresolvers | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"google.golang.org/grpc/resolver" | ||
) | ||
|
||
const scheme = "envoy" | ||
|
||
type envoyBuilder struct{} | ||
|
||
// Build A custom NameResolver to resolve gRPC target uri for envoy in the | ||
// format of. | ||
// | ||
// Custom URI Scheme: | ||
// | ||
// envoy://[proxy-agent-host]:[proxy-agent-port]/[service-name] | ||
func (*envoyBuilder) Build(target resolver.Target, | ||
cc resolver.ClientConn, _ resolver.BuildOptions, | ||
) (resolver.Resolver, error) { | ||
_, err := isValidTarget(target) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
r := &envoyResolver{ | ||
target: target, | ||
cc: cc, | ||
} | ||
r.start() | ||
return r, nil | ||
} | ||
|
||
func (*envoyBuilder) Scheme() string { | ||
return scheme | ||
} | ||
|
||
type envoyResolver struct { | ||
target resolver.Target | ||
cc resolver.ClientConn | ||
} | ||
|
||
// Envoy NameResolver, will always override the authority with the specified authority i.e. URL.path and | ||
// use the socketAddress i.e. Host:Port to connect. | ||
func (r *envoyResolver) start() { | ||
addr := fmt.Sprintf("%s:%s", r.target.URL.Hostname(), r.target.URL.Port()) | ||
err := r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: addr}}}) | ||
if err != nil { | ||
return | ||
} | ||
} | ||
|
||
func (*envoyResolver) ResolveNow(resolver.ResolveNowOptions) {} | ||
|
||
func (*envoyResolver) Close() {} | ||
|
||
// Validate user specified target | ||
// | ||
// Sample target string: envoy://localhost:9211/test.service | ||
// | ||
// return `true` if the target string used match the scheme and format | ||
func isValidTarget(target resolver.Target) (bool, error) { | ||
// make sure and host and port not empty | ||
// used as resolver.Address | ||
if target.URL.Scheme != "envoy" || target.URL.Hostname() == "" || target.URL.Port() == "" { | ||
return false, fmt.Errorf("envoy-resolver: invalid scheme or missing host/port, target: %s", | ||
target) | ||
} | ||
|
||
// make sure the path is valid | ||
// used as :authority e.g. test.service | ||
path := target.Endpoint() | ||
if path == "" || strings.Contains(path, "/") { | ||
return false, fmt.Errorf("envoy-resolver: invalid path %s", path) | ||
} | ||
|
||
return true, nil | ||
} | ||
|
||
func init() { | ||
resolver.Register(&envoyBuilder{}) | ||
} |
103 changes: 103 additions & 0 deletions
103
core/pkg/sync/grpc/nameresolvers/envoy_resolver_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package nameresolvers | ||
|
||
import ( | ||
"net/url" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
"google.golang.org/grpc/resolver" | ||
) | ||
|
||
func Test_EnvoyTargetString(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
mockURL url.URL | ||
mockError string | ||
shouldError bool | ||
}{ | ||
{ | ||
name: "Should be valid string", | ||
mockURL: url.URL{ | ||
Scheme: "envoy", | ||
Host: "localhost:8080", | ||
Path: "/test.service", | ||
}, | ||
mockError: "", | ||
shouldError: false, | ||
}, | ||
{ | ||
name: "Should be valid scheme", | ||
mockURL: url.URL{ | ||
Scheme: "invalid", | ||
Host: "localhost:8080", | ||
Path: "/test.service", | ||
}, | ||
mockError: "envoy-resolver: invalid scheme or missing host/port, target: invalid://localhost:8080/test.service", | ||
shouldError: true, | ||
}, | ||
{ | ||
name: "Should be valid path", | ||
mockURL: url.URL{ | ||
Scheme: "envoy", | ||
Host: "localhost:8080", | ||
Path: "/test.service/test", | ||
}, | ||
mockError: "envoy-resolver: invalid path test.service/test", | ||
shouldError: true, | ||
}, | ||
{ | ||
name: "Should be valid path", | ||
mockURL: url.URL{ | ||
Scheme: "envoy", | ||
Host: "localhost:8080", | ||
Path: "/test.service/", | ||
}, | ||
mockError: "envoy-resolver: invalid path test.service/", | ||
shouldError: true, | ||
}, | ||
{ | ||
name: "Hostname should not be empty", | ||
mockURL: url.URL{ | ||
Scheme: "envoy", | ||
Host: ":8080", | ||
Path: "/test.service", | ||
}, | ||
mockError: "envoy-resolver: invalid scheme or missing host/port, target: envoy://:8080/test.service", | ||
shouldError: true, | ||
}, | ||
{ | ||
name: "Port should not be empty", | ||
mockURL: url.URL{ | ||
Scheme: "envoy", | ||
Host: "localhost", | ||
Path: "/test.service", | ||
}, | ||
mockError: "envoy-resolver: invalid scheme or missing host/port, target: envoy://localhost/test.service", | ||
shouldError: true, | ||
}, | ||
{ | ||
name: "Hostname and Port should not be empty", | ||
mockURL: url.URL{ | ||
Scheme: "envoy", | ||
Path: "/test.service", | ||
}, | ||
mockError: "envoy-resolver: invalid scheme or missing host/port, target: envoy:///test.service", | ||
shouldError: true, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
target := resolver.Target{URL: test.mockURL} | ||
|
||
isValid, err := isValidTarget(target) | ||
|
||
if test.shouldError { | ||
require.False(t, isValid, "Should not be valid") | ||
require.NotNilf(t, err, "Error should not be nil") | ||
require.Containsf(t, err.Error(), test.mockError, "Error should contains %s", test.mockError) | ||
} else { | ||
require.True(t, isValid, "Should be valid") | ||
require.NoErrorf(t, err, "Error should be nil") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.