mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-25 09:01:29 +02:00
## Describe your changes This adds an extension point to the management server for registering additional gRPC services. We already have a generic integrations system and dependency injection for server components. This closes the gap on being able to also extend the gRPC API cleanly. ## Issue ticket number and link N/A ## Stack <!-- branch-stack --> ### Checklist - [ ] Is it a bug fix - [ ] Is a typo/documentation fix - [x] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) - [ ] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) No docs needed. This is strictly a small internal plumbing enhancement / refactor. <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/netbirdio/codesmith/netbird/pr/6894"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img alt="View with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787582002&installation_model_id=427504&pr_number=6894&repository=netbirdio%2Fnetbird&return_to=https%3A%2F%2Fgithub.com%2Fnetbirdio%2Fnetbird%2Fpull%2F6894&signature=3288061677db243031830964fec8f0f34c82f7fc63a39298cd0b4e3490551060"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with [code]smith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>@codesmith-bot</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a gRPC extension mechanism to contribute additional services and automatically chain extra unary and stream interceptors. * Extension shutdown hooks now run as part of server stop. * Added exported proxy token generation via `GenerateProxyToken()` for external integrations. * **Tests** * Added coverage for extension interceptor/service wiring, extension shutdown execution, and proxy token generation validation (including hash consistency and prefix). <!-- end of auto-generated comment: release notes by coderabbit.ai -->
75 lines
3.0 KiB
Go
75 lines
3.0 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
// GRPCExtension bundles an external module's contribution to the management
|
|
// gRPC server: the registration of one or more services onto the shared
|
|
// grpc.Server, any server-wide interceptors those services require, and an
|
|
// optional shutdown hook. It is a generic extension point with no knowledge of
|
|
// any specific service.
|
|
type GRPCExtension struct {
|
|
// Register is invoked with the shared grpc.Server (as a ServiceRegistrar)
|
|
// after the built-in services are registered. It may register any number of
|
|
// services. May be nil.
|
|
Register func(grpc.ServiceRegistrar)
|
|
// UnaryInterceptors are appended to the server's unary interceptor chain,
|
|
// running after the built-in interceptors. May be empty.
|
|
UnaryInterceptors []grpc.UnaryServerInterceptor
|
|
// StreamInterceptors are appended to the server's stream interceptor chain,
|
|
// running after the built-in interceptors. May be empty.
|
|
StreamInterceptors []grpc.StreamServerInterceptor
|
|
// Shutdown, if non-nil, is called once during Stop() with the context
|
|
// governing server shutdown, which carries a deadline. The hook MUST
|
|
// return promptly and MUST abandon its work once that context is
|
|
// cancelled or expires: it runs before the rest of Stop()'s cleanup
|
|
// (store, event store, embedded IdP) and before Stop() itself checks the
|
|
// context's deadline, so a hook that ignores the context will delay all
|
|
// of that cleanup and prevent Stop() from returning on time. May be nil.
|
|
Shutdown func(ctx context.Context)
|
|
}
|
|
|
|
// RegisterGRPCExtension registers a gRPC extension. Call before the gRPC server
|
|
// is first built (i.e. before Start); registrations after that have no effect.
|
|
func (s *BaseServer) RegisterGRPCExtension(ext GRPCExtension) {
|
|
s.grpcExtensions = append(s.grpcExtensions, ext)
|
|
}
|
|
|
|
// appendExtensionInterceptors appends each extension's interceptors to the gRPC
|
|
// server options as additional chained interceptors. grpc.ChainUnaryInterceptor
|
|
// and grpc.ChainStreamInterceptor are additive, so the returned options run the
|
|
// extension interceptors after any interceptors already present in opts.
|
|
func appendExtensionInterceptors(opts []grpc.ServerOption, exts []GRPCExtension) []grpc.ServerOption {
|
|
for _, ext := range exts {
|
|
if len(ext.UnaryInterceptors) > 0 {
|
|
opts = append(opts, grpc.ChainUnaryInterceptor(ext.UnaryInterceptors...))
|
|
}
|
|
if len(ext.StreamInterceptors) > 0 {
|
|
opts = append(opts, grpc.ChainStreamInterceptor(ext.StreamInterceptors...))
|
|
}
|
|
}
|
|
return opts
|
|
}
|
|
|
|
// registerExtensions registers each extension's services onto reg.
|
|
func registerExtensions(reg grpc.ServiceRegistrar, exts []GRPCExtension) {
|
|
for _, ext := range exts {
|
|
if ext.Register != nil {
|
|
ext.Register(reg)
|
|
}
|
|
}
|
|
}
|
|
|
|
// runExtensionShutdownHooks calls each extension's shutdown hook, if set,
|
|
// passing ctx through so hooks can honor its deadline/cancellation.
|
|
func runExtensionShutdownHooks(ctx context.Context, exts []GRPCExtension) {
|
|
for _, ext := range exts {
|
|
if ext.Shutdown != nil {
|
|
ext.Shutdown(ctx)
|
|
}
|
|
}
|
|
}
|