Skip to content

Commit

Permalink
add JSON/HTTP API server via gRPC gateway
Browse files Browse the repository at this point in the history
Signed-off-by: Jimmy Zelinskie <jimmy@zelinskie.com>
  • Loading branch information
jzelinskie committed Oct 25, 2021
1 parent ec71855 commit bbc2c05
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 43 deletions.
84 changes: 69 additions & 15 deletions cmd/spicedb/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import (
"time"

"github.com/alecthomas/units"
v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
"github.com/authzed/grpcutil"
"github.com/fatih/color"
grpcauth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
grpczerolog "github.com/grpc-ecosystem/go-grpc-middleware/providers/zerolog/v2"
grpclog "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/jzelinskie/cobrautil"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -100,6 +103,9 @@ func registerServeCmd(rootCmd *cobra.Command) {
// Flags for internal dispatch API
serveCmd.Flags().String("internal-grpc-addr", ":50053", "address to listen for internal requests")

// Flags for HTTP gateway
serveCmd.Flags().String("http-addr", ":443", "address to listen for HTTP API requests")

// Flags for configuring dispatch behavior
serveCmd.Flags().Uint32("dispatch-max-depth", 50, "maximum recursion depth for nested calls")
serveCmd.Flags().String("dispatch-redispatch-dns-name", "", "dns SRV record name to resolve for remote redispatch, empty string disables redispatch")
Expand Down Expand Up @@ -321,6 +327,14 @@ func serveRun(cmd *cobra.Command, args []string) {
log.Fatal().Err(err).Msg("failed to initialize redispatcher cache")
}

internalDispatch := graph.NewDispatcher(cachingRedispatch, nsm, ds)
cachingInternalDispatch, err := caching.NewCachingDispatcher(internalDispatch, nil, "dispatch")
if err != nil {
log.Fatal().Err(err).Msg("failed to initialize internal dispatcher cache")
}

internaldispatch.RegisterGrpcServices(internalGrpcServer, cachingInternalDispatch)

prefixRequiredOption := v1alpha1svc.PrefixRequired
if !cobrautil.MustGetBool(cmd, "schema-prefixes-required") {
prefixRequiredOption = v1alpha1svc.PrefixNotRequired
Expand All @@ -331,16 +345,15 @@ func serveRun(cmd *cobra.Command, args []string) {
v1SchemaServiceOption = services.V1SchemaServiceDisabled
}

maxDepth := cobrautil.MustGetUint32(cmd, "dispatch-max-depth")
services.RegisterGrpcServices(grpcServer, ds, nsm, cachingRedispatch, maxDepth, prefixRequiredOption, v1SchemaServiceOption)

internalDispatch := graph.NewDispatcher(cachingRedispatch, nsm, ds)
cachingInternalDispatch, err := caching.NewCachingDispatcher(internalDispatch, nil, "dispatch")
if err != nil {
log.Fatal().Err(err).Msg("failed to initialize internal dispatcher cache")
}

internaldispatch.RegisterGrpcServices(internalGrpcServer, cachingInternalDispatch)
services.RegisterGrpcServices(
grpcServer,
ds,
nsm,
cachingRedispatch,
cobrautil.MustGetUint32(cmd, "dispatch-max-depth"),
prefixRequiredOption,
v1SchemaServiceOption,
)

go func() {
addr := cobrautil.MustGetString(cmd, "grpc-addr")
Expand Down Expand Up @@ -370,8 +383,15 @@ func serveRun(cmd *cobra.Command, args []string) {
}
}()

gatewaySrv, err := newRestGateway(context.TODO(), cmd)
if err != nil {
log.Fatal().Err(err).Msg("failed to listen on addr for gRPC REST gateway server")
}

metricsrv := cobrautil.MetricsServerFromFlags(cmd)
go func() {
addr := cobrautil.MustGetStringExpanded(cmd, "metrics-addr")
log.Info().Str("addr", addr).Msg("metrics server started listening")
if err := metricsrv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal().Err(err).Msg("failed while serving metrics")
}
Expand All @@ -385,13 +405,11 @@ func serveRun(cmd *cobra.Command, args []string) {
}, ds)
if dashboardAddr != "" {
go func() {
log.Info().Str("addr", dashboardAddr).Msg("dashboard server started listening")
if err := dashboard.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal().Err(err).Msg("failed while serving dashboard")
}
}()

url := fmt.Sprintf("http://localhost%s", dashboardAddr)
log.Info().Str("url", url).Msg("dashboard running")
}

signalctx, _ := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
Expand All @@ -418,8 +436,8 @@ func serveRun(cmd *cobra.Command, args []string) {
internalGrpcServer.GracefulStop()
redispatchClientCancel()

if err := metricsrv.Close(); err != nil {
log.Fatal().Err(err).Msg("failed while shutting down metrics server")
if err := gatewaySrv.Close(); err != nil {
log.Fatal().Err(err).Msg("failed while shutting down rest gateway")
}

if err := nsm.Close(); err != nil {
Expand All @@ -430,9 +448,45 @@ func serveRun(cmd *cobra.Command, args []string) {
log.Fatal().Err(err).Msg("failed while shutting down datastore")
}

if err := metricsrv.Close(); err != nil {
log.Fatal().Err(err).Msg("failed while shutting down metrics server")
}

if dashboardAddr != "" {
if err := dashboard.Close(); err != nil {
log.Fatal().Err(err).Msg("failed while shutting down dashboard")
}
}
}

func newRestGateway(ctx context.Context, cmd *cobra.Command) (*http.Server, error) {
opts := []grpc.DialOption{grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor())}
if cobrautil.MustGetBool(cmd, "grpc-no-tls") {
opts = append(opts, grpc.WithInsecure())
} else {
opts = append(opts, grpcutil.WithCustomCerts(
cobrautil.MustGetStringExpanded(cmd, "grpc-cert-path"),
grpcutil.SkipVerifyCA,
))
}

mux := runtime.NewServeMux(runtime.WithMetadata(auth.PresharedKeyAnnotator))
upstream := cobrautil.MustGetString(cmd, "grpc-addr")
v1.RegisterSchemaServiceHandlerFromEndpoint(ctx, mux, upstream, opts)
v1.RegisterPermissionsServiceHandlerFromEndpoint(ctx, mux, upstream, opts)

addr := cobrautil.MustGetStringExpanded(cmd, "http-addr")
gatewaySrv := &http.Server{
Addr: addr,
Handler: mux,
}

go func() {
log.Info().Str("addr", addr).Msg("http server started listening")
if err := gatewaySrv.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal().Err(err).Msg("failed while serving rest gateway")
}
}()

return gatewaySrv, nil
}
9 changes: 5 additions & 4 deletions e2e/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ go 1.17

require (
github.com/authzed/authzed-go v0.1.1-0.20210923172306-b4b512e4d359
github.com/authzed/grpcutil v0.0.0-20210914195113-c0d8369e7e1f
github.com/authzed/grpcutil v0.0.0-20211020204402-aba1876830e6
github.com/authzed/spicedb v0.0.0
github.com/jackc/pgx/v4 v4.13.0
github.com/stretchr/testify v1.7.0
google.golang.org/grpc v1.41.0
)

require (
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/envoyproxy/protoc-gen-validate v0.6.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
Expand All @@ -28,10 +29,10 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect
golang.org/x/sys v0.0.0-20210921065528-437939a70204 // indirect
golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect
golang.org/x/sys v0.0.0-20211020174200-9d6173849985 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af // indirect
google.golang.org/genproto v0.0.0-20211020151524-b7c3a969101a // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
Expand Down

0 comments on commit bbc2c05

Please sign in to comment.