This document covers administrative services in gRPC-Go, including channelz and CSDS (Client Status Discovery Service).
The admin package provides a convenient method for registering administrative services:
import "google.golang.org/grpc/admin"import (
"google.golang.org/grpc"
"google.golang.org/grpc/admin"
)
server := grpc.NewServer()
// Register admin services
cleanup, err := admin.Register(server)
if err != nil {
log.Fatalf("failed to register admin services: %v", err)
}
// Register your services
pb.RegisterMyServiceServer(server, &myServiceImpl{})
// Serve
lis, _ := net.Listen("tcp", ":50051")
go server.Serve(lis)
// Cleanup when done
defer cleanup()
defer server.Stop()// Register registers the following services:
// - Channelz
// - CSDS (if server is *grpc.Server or *xds.GRPCServer)
func Register(s grpc.ServiceRegistrar) (cleanup func(), _ error)Channelz provides runtime debugging information about gRPC channels, subchannels, and sockets.
# Get top-level channels
grpcurl -plaintext localhost:50051 grpc.channelz.v1.Channelz.GetTopChannels
# Get servers
grpcurl -plaintext localhost:50051 grpc.channelz.v1.Channelz.GetServers
# Get specific channel
grpcurl -plaintext -d '{"channel_id": 1}' localhost:50051 grpc.channelz.v1.Channelz.GetChannel
# Get subchannel
grpcurl -plaintext -d '{"subchannel_id": 1}' localhost:50051 grpc.channelz.v1.Channelz.GetSubchannel
# Get socket
grpcurl -plaintext -d '{"socket_id": 1}' localhost:50051 grpc.channelz.v1.Channelz.GetSocketChannelz provides:
import (
"context"
"google.golang.org/grpc"
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
)
conn, err := grpc.NewClient("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := channelzpb.NewChannelzClient(conn)
// Get top channels
resp, err := client.GetTopChannels(context.Background(),
&channelzpb.GetTopChannelsRequest{
StartChannelId: 0,
})
if err != nil {
log.Fatal(err)
}
for _, ch := range resp.Channel {
fmt.Printf("Channel %d: %s\n", ch.Ref.ChannelId, ch.Ref.Name)
fmt.Printf(" State: %v\n", ch.Data.State.State)
fmt.Printf(" Target: %s\n", ch.Data.Target)
fmt.Printf(" Calls started: %d\n", ch.Data.CallsStarted)
fmt.Printf(" Calls succeeded: %d\n", ch.Data.CallsSucceeded)
fmt.Printf(" Calls failed: %d\n", ch.Data.CallsFailed)
}CSDS allows querying xDS configuration status from gRPC clients and servers.
# Get client config status
grpcurl -plaintext localhost:50051 envoy.service.status.v3.ClientStatusDiscoveryService.FetchClientStatusCSDS provides:
import (
"context"
"google.golang.org/grpc"
csdspb "github.com/envoyproxy/go-control-plane/envoy/service/status/v3"
)
conn, err := grpc.NewClient("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := csdspb.NewClientStatusDiscoveryServiceClient(conn)
// Fetch client status
resp, err := client.FetchClientStatus(context.Background(),
&csdspb.ClientStatusRequest{})
if err != nil {
log.Fatal(err)
}
for _, config := range resp.Config {
fmt.Printf("Node: %s\n", config.Node.Id)
for _, xdsConfig := range config.GenericXdsConfigs {
fmt.Printf(" Type: %s\n", xdsConfig.TypeUrl)
fmt.Printf(" Version: %s\n", xdsConfig.VersionInfo)
fmt.Printf(" Status: %v\n", xdsConfig.ClientStatus)
}
}import (
"google.golang.org/grpc"
"google.golang.org/grpc/admin"
)
// Main server
mainServer := grpc.NewServer()
pb.RegisterMyServiceServer(mainServer, &myServiceImpl{})
// Admin server on separate port
adminServer := grpc.NewServer()
cleanup, err := admin.Register(adminServer)
if err != nil {
log.Fatal(err)
}
defer cleanup()
// Start both servers
mainLis, _ := net.Listen("tcp", ":50051")
adminLis, _ := net.Listen("tcp", ":50052")
go mainServer.Serve(mainLis)
go adminServer.Serve(adminLis)
// Wait for shutdown
<-done
mainServer.GracefulStop()
adminServer.GracefulStop()import (
"context"
"strings"
"google.golang.org/grpc"
"google.golang.org/grpc/admin"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
func adminAuthInterceptor(
ctx context.Context,
req any,
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (any, error) {
// Check if this is an admin service call
if strings.HasPrefix(info.FullMethod, "/grpc.channelz.") ||
strings.HasPrefix(info.FullMethod, "/envoy.service.status.") {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, "missing metadata")
}
token := md.Get("admin-token")
if len(token) == 0 || token[0] != "secret" {
return nil, status.Error(codes.PermissionDenied, "invalid token")
}
}
return handler(ctx, req)
}
server := grpc.NewServer(
grpc.ChainUnaryInterceptor(adminAuthInterceptor))
admin.Register(server)import (
"testing"
"google.golang.org/grpc"
"google.golang.org/grpc/admin"
"google.golang.org/grpc/admin/test"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/test/bufconn"
)
func TestAdminServices(t *testing.T) {
lis := bufconn.Listen(1024 * 1024)
server := grpc.NewServer()
defer server.Stop()
cleanup, err := admin.Register(server)
if err != nil {
t.Fatal(err)
}
defer cleanup()
go server.Serve(lis)
conn, err := grpc.NewClient("bufnet",
grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
return lis.DialContext(ctx)
}),
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
t.Fatal(err)
}
defer conn.Close()
// Test admin services
test.RunRegisterTests(t, test.ExpectedStatusCodes{
ChannelzCode: codes.OK,
CSDSCode: codes.OK,
})
}