From c15a1300da6cb8953dc7b9b6c594004c96e5a13a Mon Sep 17 00:00:00 2001 From: Troy Chiu Date: Fri, 17 Jul 2026 00:05:08 +0000 Subject: [PATCH 1/2] Move DebugClear RPC to a separate Debug gRPC service --- .../{controlapi => debugapi}/debug_clear.go | 2 +- cmd/ateapi/internal/debugapi/service.go | 35 +++++++++++++++++++ cmd/ateapi/main.go | 3 ++ internal/ateclient/builder.go | 5 ++- pkg/proto/ateapipb/ateapi.proto | 10 ++++-- 5 files changed, 50 insertions(+), 5 deletions(-) rename cmd/ateapi/internal/{controlapi => debugapi}/debug_clear.go (98%) create mode 100644 cmd/ateapi/internal/debugapi/service.go diff --git a/cmd/ateapi/internal/controlapi/debug_clear.go b/cmd/ateapi/internal/debugapi/debug_clear.go similarity index 98% rename from cmd/ateapi/internal/controlapi/debug_clear.go rename to cmd/ateapi/internal/debugapi/debug_clear.go index f333e7be6..923584505 100644 --- a/cmd/ateapi/internal/controlapi/debug_clear.go +++ b/cmd/ateapi/internal/debugapi/debug_clear.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package controlapi +package debugapi import ( "context" diff --git a/cmd/ateapi/internal/debugapi/service.go b/cmd/ateapi/internal/debugapi/service.go new file mode 100644 index 000000000..0f05fb3c2 --- /dev/null +++ b/cmd/ateapi/internal/debugapi/service.go @@ -0,0 +1,35 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package debugapi + +import ( + "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" + "github.com/agent-substrate/substrate/pkg/proto/ateapipb" +) + +// Service implements ateapipb.DebugServer. +type Service struct { + ateapipb.UnimplementedDebugServer + persistence store.Interface +} + +var _ ateapipb.DebugServer = (*Service)(nil) + +// NewService creates a new debug service. +func NewService(persistence store.Interface) *Service { + return &Service{ + persistence: persistence, + } +} diff --git a/cmd/ateapi/main.go b/cmd/ateapi/main.go index dd481bc50..c08feb51c 100644 --- a/cmd/ateapi/main.go +++ b/cmd/ateapi/main.go @@ -29,6 +29,7 @@ import ( "github.com/agent-substrate/substrate/cmd/ateapi/internal/controlapi" "github.com/agent-substrate/substrate/cmd/ateapi/internal/credbundle" + "github.com/agent-substrate/substrate/cmd/ateapi/internal/debugapi" "github.com/agent-substrate/substrate/cmd/ateapi/internal/k8sjwt" "github.com/agent-substrate/substrate/cmd/ateapi/internal/sessionidentity" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store/ateredis" @@ -159,6 +160,7 @@ func main() { } sessionIdentitySrv := sessionidentity.New(*clientJWTIssuer, *clientJWTAudience, *sessionIDJWTPoolFile, *sessionIDCAPoolFile, *workerpoolCACerts, jwtIssuerDiscoveryClient) + debugSrv := debugapi.NewService(redisPersistence) lisCfg := &net.ListenConfig{} lis, err := lisCfg.Listen(ctx, "tcp", *listenAddr) @@ -191,6 +193,7 @@ func main() { reflection.Register(mux) ateapipb.RegisterControlServer(mux, sm) ateapipb.RegisterSessionIdentityServer(mux, sessionIdentitySrv) + ateapipb.RegisterDebugServer(mux, debugSrv) go serverboot.StartMetricsServer(ctx, serverboot.MetricsServerOptions{ Addr: *metricsListenAddr, diff --git a/internal/ateclient/builder.go b/internal/ateclient/builder.go index 6e64eb56d..d8a8fc9bd 100644 --- a/internal/ateclient/builder.go +++ b/internal/ateclient/builder.go @@ -44,9 +44,10 @@ import ( "k8s.io/client-go/transport/spdy" ) -// Client wraps the gRPC ControlClient and ensures the port-forward connection is closed when done. +// Client wraps the gRPC ControlClient and DebugClient and ensures the port-forward connection is closed when done. type Client struct { ateapipb.ControlClient + ateapipb.DebugClient conn *grpc.ClientConn cancel func() tracerProvider *sdktrace.TracerProvider @@ -108,6 +109,7 @@ func dialDirect(kubeconfigPath, k8sContext, endpoint string, traceEnabled bool) } return &Client{ ControlClient: ateapipb.NewControlClient(conn), + DebugClient: ateapipb.NewDebugClient(conn), conn: conn, cancel: func() {}, }, nil @@ -230,6 +232,7 @@ func dialPortForward(ctx context.Context, kubeconfigPath, k8sContext string, tra return &Client{ ControlClient: ateapipb.NewControlClient(conn), + DebugClient: ateapipb.NewDebugClient(conn), conn: conn, cancel: func() { close(stopCh) diff --git a/pkg/proto/ateapipb/ateapi.proto b/pkg/proto/ateapipb/ateapi.proto index 4c1d0461f..8c8955749 100644 --- a/pkg/proto/ateapipb/ateapi.proto +++ b/pkg/proto/ateapipb/ateapi.proto @@ -62,9 +62,6 @@ service Control { // Delete an empty Atespace. Rejects (FailedPrecondition) if any actors remain. rpc DeleteAtespace(DeleteAtespaceRequest) returns (Atespace) {} - - // Debugging: drop all data from the ate database. - rpc DebugClear(DebugClearRequest) returns (DebugClearResponse) {} } message ExternalSnapshotInfo { @@ -306,6 +303,13 @@ message KubeNamespacedObjectRef { string name = 2; } +// Debug is the RPC interface for administrative and debugging operations +// (such as wiping state during development). +service Debug { + // Debugging: drop all data from the ate database. + rpc DebugClear(DebugClearRequest) returns (DebugClearResponse) {} +} + message DebugClearRequest {} message DebugClearResponse {} From d34400d661d9dca4aac280d5c158da6106121243 Mon Sep 17 00:00:00 2001 From: Troy Chiu Date: Fri, 17 Jul 2026 00:05:11 +0000 Subject: [PATCH 2/2] Regenerate protobuf code --- pkg/proto/ateapipb/ateapi.pb.go | 11 +- pkg/proto/ateapipb/ateapi_grpc.pb.go | 144 ++++++++++++++++++++------- 2 files changed, 113 insertions(+), 42 deletions(-) diff --git a/pkg/proto/ateapipb/ateapi.pb.go b/pkg/proto/ateapipb/ateapi.pb.go index 0350f9fd9..b9569467d 100644 --- a/pkg/proto/ateapipb/ateapi.pb.go +++ b/pkg/proto/ateapipb/ateapi.pb.go @@ -2279,7 +2279,7 @@ const file_ateapi_proto_rawDesc = "" + "session_id\x18\x03 \x01(\tR\tsessionId\x12>\n" + "\x1bcertificate_signing_request\x18\x04 \x01(\fR\x19certificateSigningRequest\"E\n" + "\x10MintCertResponse\x121\n" + - "\x14session_certificates\x18\x01 \x03(\fR\x13sessionCertificates2\xd0\a\n" + + "\x14session_certificates\x18\x01 \x03(\fR\x13sessionCertificates2\x89\a\n" + "\aControl\x124\n" + "\bGetActor\x12\x17.ateapi.GetActorRequest\x1a\r.ateapi.Actor\"\x00\x12:\n" + "\vCreateActor\x12\x1a.ateapi.CreateActorRequest\x1a\r.ateapi.Actor\"\x00\x12H\n" + @@ -2295,7 +2295,8 @@ const file_ateapi_proto_rawDesc = "" + "\x0eCreateAtespace\x12\x1d.ateapi.CreateAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12=\n" + "\vGetAtespace\x12\x1a.ateapi.GetAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12N\n" + "\rListAtespaces\x12\x1c.ateapi.ListAtespacesRequest\x1a\x1d.ateapi.ListAtespacesResponse\"\x00\x12C\n" + - "\x0eDeleteAtespace\x12\x1d.ateapi.DeleteAtespaceRequest\x1a\x10.ateapi.Atespace\"\x00\x12E\n" + + "\x0eDeleteAtespace\x12\x1d.ateapi.DeleteAtespaceRequest\x1a\x10.ateapi.Atespace\"\x002N\n" + + "\x05Debug\x12E\n" + "\n" + "DebugClear\x12\x19.ateapi.DebugClearRequest\x1a\x1a.ateapi.DebugClearResponse\"\x002\x8c\x01\n" + "\x0fSessionIdentity\x12:\n" + @@ -2405,7 +2406,7 @@ var file_ateapi_proto_depIdxs = []int32{ 10, // 42: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest 11, // 43: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest 13, // 44: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest - 32, // 45: ateapi.Control.DebugClear:input_type -> ateapi.DebugClearRequest + 32, // 45: ateapi.Debug.DebugClear:input_type -> ateapi.DebugClearRequest 34, // 46: ateapi.SessionIdentity.MintJWT:input_type -> ateapi.MintJWTRequest 36, // 47: ateapi.SessionIdentity.MintCert:input_type -> ateapi.MintCertRequest 6, // 48: ateapi.Control.GetActor:output_type -> ateapi.Actor @@ -2421,7 +2422,7 @@ var file_ateapi_proto_depIdxs = []int32{ 7, // 58: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace 12, // 59: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse 7, // 60: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace - 33, // 61: ateapi.Control.DebugClear:output_type -> ateapi.DebugClearResponse + 33, // 61: ateapi.Debug.DebugClear:output_type -> ateapi.DebugClearResponse 35, // 62: ateapi.SessionIdentity.MintJWT:output_type -> ateapi.MintJWTResponse 37, // 63: ateapi.SessionIdentity.MintCert:output_type -> ateapi.MintCertResponse 48, // [48:64] is the sub-list for method output_type @@ -2448,7 +2449,7 @@ func file_ateapi_proto_init() { NumEnums: 1, NumMessages: 39, NumExtensions: 0, - NumServices: 2, + NumServices: 3, }, GoTypes: file_ateapi_proto_goTypes, DependencyIndexes: file_ateapi_proto_depIdxs, diff --git a/pkg/proto/ateapipb/ateapi_grpc.pb.go b/pkg/proto/ateapipb/ateapi_grpc.pb.go index 59eaeffc2..793cc1b8e 100644 --- a/pkg/proto/ateapipb/ateapi_grpc.pb.go +++ b/pkg/proto/ateapipb/ateapi_grpc.pb.go @@ -48,7 +48,6 @@ const ( Control_GetAtespace_FullMethodName = "/ateapi.Control/GetAtespace" Control_ListAtespaces_FullMethodName = "/ateapi.Control/ListAtespaces" Control_DeleteAtespace_FullMethodName = "/ateapi.Control/DeleteAtespace" - Control_DebugClear_FullMethodName = "/ateapi.Control/DebugClear" ) // ControlClient is the client API for Control service. @@ -83,8 +82,6 @@ type ControlClient interface { ListAtespaces(ctx context.Context, in *ListAtespacesRequest, opts ...grpc.CallOption) (*ListAtespacesResponse, error) // Delete an empty Atespace. Rejects (FailedPrecondition) if any actors remain. DeleteAtespace(ctx context.Context, in *DeleteAtespaceRequest, opts ...grpc.CallOption) (*Atespace, error) - // Debugging: drop all data from the ate database. - DebugClear(ctx context.Context, in *DebugClearRequest, opts ...grpc.CallOption) (*DebugClearResponse, error) } type controlClient struct { @@ -225,16 +222,6 @@ func (c *controlClient) DeleteAtespace(ctx context.Context, in *DeleteAtespaceRe return out, nil } -func (c *controlClient) DebugClear(ctx context.Context, in *DebugClearRequest, opts ...grpc.CallOption) (*DebugClearResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DebugClearResponse) - err := c.cc.Invoke(ctx, Control_DebugClear_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - // ControlServer is the server API for Control service. // All implementations must embed UnimplementedControlServer // for forward compatibility. @@ -267,8 +254,6 @@ type ControlServer interface { ListAtespaces(context.Context, *ListAtespacesRequest) (*ListAtespacesResponse, error) // Delete an empty Atespace. Rejects (FailedPrecondition) if any actors remain. DeleteAtespace(context.Context, *DeleteAtespaceRequest) (*Atespace, error) - // Debugging: drop all data from the ate database. - DebugClear(context.Context, *DebugClearRequest) (*DebugClearResponse, error) mustEmbedUnimplementedControlServer() } @@ -318,9 +303,6 @@ func (UnimplementedControlServer) ListAtespaces(context.Context, *ListAtespacesR func (UnimplementedControlServer) DeleteAtespace(context.Context, *DeleteAtespaceRequest) (*Atespace, error) { return nil, status.Error(codes.Unimplemented, "method DeleteAtespace not implemented") } -func (UnimplementedControlServer) DebugClear(context.Context, *DebugClearRequest) (*DebugClearResponse, error) { - return nil, status.Error(codes.Unimplemented, "method DebugClear not implemented") -} func (UnimplementedControlServer) mustEmbedUnimplementedControlServer() {} func (UnimplementedControlServer) testEmbeddedByValue() {} @@ -576,24 +558,6 @@ func _Control_DeleteAtespace_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } -func _Control_DebugClear_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DebugClearRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).DebugClear(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: Control_DebugClear_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).DebugClear(ctx, req.(*DebugClearRequest)) - } - return interceptor(ctx, in, info, handler) -} - // Control_ServiceDesc is the grpc.ServiceDesc for Control service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -653,9 +617,115 @@ var Control_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteAtespace", Handler: _Control_DeleteAtespace_Handler, }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "ateapi.proto", +} + +const ( + Debug_DebugClear_FullMethodName = "/ateapi.Debug/DebugClear" +) + +// DebugClient is the client API for Debug service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// Debug is the RPC interface for administrative and debugging operations +// (such as wiping state during development). +type DebugClient interface { + // Debugging: drop all data from the ate database. + DebugClear(ctx context.Context, in *DebugClearRequest, opts ...grpc.CallOption) (*DebugClearResponse, error) +} + +type debugClient struct { + cc grpc.ClientConnInterface +} + +func NewDebugClient(cc grpc.ClientConnInterface) DebugClient { + return &debugClient{cc} +} + +func (c *debugClient) DebugClear(ctx context.Context, in *DebugClearRequest, opts ...grpc.CallOption) (*DebugClearResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DebugClearResponse) + err := c.cc.Invoke(ctx, Debug_DebugClear_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DebugServer is the server API for Debug service. +// All implementations must embed UnimplementedDebugServer +// for forward compatibility. +// +// Debug is the RPC interface for administrative and debugging operations +// (such as wiping state during development). +type DebugServer interface { + // Debugging: drop all data from the ate database. + DebugClear(context.Context, *DebugClearRequest) (*DebugClearResponse, error) + mustEmbedUnimplementedDebugServer() +} + +// UnimplementedDebugServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDebugServer struct{} + +func (UnimplementedDebugServer) DebugClear(context.Context, *DebugClearRequest) (*DebugClearResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DebugClear not implemented") +} +func (UnimplementedDebugServer) mustEmbedUnimplementedDebugServer() {} +func (UnimplementedDebugServer) testEmbeddedByValue() {} + +// UnsafeDebugServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DebugServer will +// result in compilation errors. +type UnsafeDebugServer interface { + mustEmbedUnimplementedDebugServer() +} + +func RegisterDebugServer(s grpc.ServiceRegistrar, srv DebugServer) { + // If the following call panics, it indicates UnimplementedDebugServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Debug_ServiceDesc, srv) +} + +func _Debug_DebugClear_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DebugClearRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DebugServer).DebugClear(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Debug_DebugClear_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DebugServer).DebugClear(ctx, req.(*DebugClearRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Debug_ServiceDesc is the grpc.ServiceDesc for Debug service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Debug_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "ateapi.Debug", + HandlerType: (*DebugServer)(nil), + Methods: []grpc.MethodDesc{ { MethodName: "DebugClear", - Handler: _Control_DebugClear_Handler, + Handler: _Debug_DebugClear_Handler, }, }, Streams: []grpc.StreamDesc{},