Comments (3)
Slight change of plan. Setting require_unimplemented_servers=false
and removing the foopb.UnimplementedFooServer
embed from all of our implementation structs works, but it also means that those structs don't have an obvious thing declaring up-front that they are attempting to implement a gRPC server.
So instead I'm going to leave the require_unimplemented_servers=true
(the default), but change all of our implementation structs to embed foopb.UnsafeFooServer
, which only provides the mustEmbedUnimplementedFooServer
method, without providing "unimplemented" versions of all the other API methods. This way our impl structs will all have an obvious declaration of intent upfront, but we'll still get the improvements to compile-time type checking discussed above.
from boulder.
Ah crudbuckets, and in the process of exploring this I have found a stumbling block: The compile-time type checking comes by virtue of calling the auto-generated RegisterFooServer function, which ensures that its argument fully implements the corresponding auto-generated FooServer interface. For example:
boulder/akamai/proto/akamai_grpc.pb.go
Lines 70 to 72 in 5be3650
boulder/akamai/proto/akamai_grpc.pb.go
Lines 49 to 52 in 5be3650
But we don't actually call those RegisterFooServer functions. We add our server implementations to a server builder, and that builder skips straight to the inner RegisterService call:
boulder/cmd/akamai-purger/main.go
Lines 426 to 427 in 5be3650
Lines 181 to 183 in 5be3650
I'm still looking into how best (how most elegantly) to get this type safety anyway.
from boulder.
Consolation prize: the integration tests will fail if any of our impl types don't actually implement the gRPC interface they want to, because the underlying server.RegisterService function uses reflect to check:
boulder/vendor/google.golang.org/grpc/server.go
Lines 723 to 726 in 89213f9
But I'd still prefer to do this at compile time, to get editor integration and to prevent mistakes for any gRPC services which (somehow) aren't started in our integration tests.
One option, of course, is to just do it like everyone else does it, and add a line like this below all of our gRPC impl struct type declarations:
var _ akamaipb.AkamaiPurgerServer = (*akamaiPurger)(nil)
Upside: it just works, it's idiomatic. Downside: it doesn't work automatically -- if someone forgets to add a line like this, no compile-time errors for them. Since they have to register their server at some point, it would be nice to do the check then.
So I tried a much more convoluted (but very pleasing to me) approach using generics and curried functions! Wheee I'm not going overboard at all!
// curriedReg is a curried (i.e. some of the arguments have been populated)
// version of a gRPC auto-generated registration function like
// foopb.RegisterFooServer. Its second argument, the impl to be registered,
// has already been provided via CurryReg.
type curriedReg func(grpc.ServiceRegistrar)
// CurryReg takes an auto-generated service registration func (such as
// foopb.RegisterFooServer) and a gRPC service implementation (such as would be
// passed as the second argument to such a function) and returns a function
// which, when passed a service registrar, will use the registration function
// to register the impl on that registrar.
func CurryReg[T any](reg func(grpc.ServiceRegistrar, T), impl T) curriedReg {
return func(s grpc.ServiceRegistrar) { reg(s, impl) }
}
start, err := bgrpc.NewServer(c.AkamaiPurger.GRPC, logger).Add(
&akamaipb.AkamaiPurger_ServiceDesc, grpc.CurryReg(akamaipb.RegisterAkamaiPurgerServer, ap), ap).Build(tlsConfig, scope, clk)
But it turns out this falls down in two ways:
- It becomes very verbose and redundant. It seems like we should be able to pass only the curried reg func into
bgrpc.Server{}.Add()
, but we actually need direct access to both the ServiceDesc and the impl itself in order to automatically instantiate health-check services. - For reasons I still don't quite understand, go doesn't actually like
grpc.CurryReg(akamaipb.RegisterAkamaiPurgerServer, ap)
. It throws a "CannotInferTypeArgs" error, meaning that it can't infer that this should be shorthand forgrpc.CurryReg[akamaipb.AkamaiPurgerServer](akamaipb.RegisterAkamaiPurgerServer, ap)
, even thoughakamaipb.AkamaiPurgerServer
is explicitly the type of the second argument to the registration function, and is an interface implemented by the impl. So it forces you to explicitly include the type parameter, and that makes the verbosity and redundancy problem in (1) even worse.
Still digging.
from boulder.
Related Issues (20)
- va: reduce duplication in "Validation result" log message HOT 1
- Resolve comments from PR #7500
- crl-updater: CRL number not strictly increasing sometimes in integration test HOT 3
- Investigate the unreasonably fast denial latency metrics
- WFE: Advertise available profiles in directory meta
- RA: Allow profile selection to be gated on account-based allowlists
- RA: Allow profile to control aspects of validation such as order/authz lifetime
- The RA can plumb a requested new-order profile to the SA
- CA: Allow profile to control CN, KUs, EKUs, and SKID
- Prepare for Ballot SC-067 v3 (MPIC) HOT 2
- In boulder-va, the remoteVAs configuration should be expanded to add a unique identifying name for each RVA cluster and Regional Internet Registry
- boulder-va should explicitly check that each validation has validations from at least 2 RIRs
- The validation audit log events should now include the RVA cluster name and RIR
- boulder-va should explicitly record the quorum met for the issuance (eg, 5/6 or 6/6)
- Collect a small set of (account, identifier) pairs to pause HOT 1
- Implement admin tooling for manually pausing and unpausing (account, identifier) pairs in batches.
- Comms review of the web UI for tone and branding
- Allow CSRs whose CN is longer than acceptable
- "error checking key quality" should be an internal error in event of DB unavailibility
- Remove RSA KEX usage tracking
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from boulder.