From 7927bb5c1123f24357fbcc287b44f9b215be7a6e Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Thu, 12 Dec 2024 11:52:31 +1100 Subject: [PATCH] fix: hot reload endpoint uses runner proxy (#3636) --- backend/controller/controller.go | 13 +- .../xyz/block/ftl/language/v1/language.pb.go | 282 ++++++++------- .../xyz/block/ftl/language/v1/language.proto | 3 + .../scaling/localscaling/local_scaling.go | 13 +- backend/runner/runner.go | 122 ++++--- .../echo/src/main/java/ftl/echo/Echo.java | 2 +- .../xyz/block/ftl/language/v1/language_pb.ts | 8 + internal/buildengine/build.go | 2 +- internal/buildengine/languageplugin/plugin.go | 16 +- internal/dev/devendpoint.go | 15 +- .../block/ftl/deployment/ModuleProcessor.java | 40 ++- .../block/ftl/runtime/DatasourceDetails.java | 60 ++++ .../ftl/runtime/DefaultRunnerDetails.java | 47 +++ .../ftl/runtime/DevModeRunnerDetails.java | 119 +++++++ .../xyz/block/ftl/runtime/FTLController.java | 336 +++--------------- .../ftl/runtime/FTLDatasourceCredentials.java | 2 +- .../xyz/block/ftl/runtime/FTLRecorder.java | 11 +- .../ftl/runtime/FTLRunnerConnection.java | 264 ++++++++++++++ .../xyz/block/ftl/runtime/HotReloadSetup.java | 4 +- .../xyz/block/ftl/runtime/RunnerDetails.java | 22 ++ .../ftl/runtime/config/FTLConfigSource.java | 13 +- jvm-runtime/plugin/common/jvmcommon.go | 5 + .../xyz/block/ftl/language/v1/language_pb2.py | 32 +- .../block/ftl/language/v1/language_pb2.pyi | 6 +- 24 files changed, 909 insertions(+), 528 deletions(-) create mode 100644 jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DatasourceDetails.java create mode 100644 jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DefaultRunnerDetails.java create mode 100644 jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java create mode 100644 jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRunnerConnection.java create mode 100644 jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/RunnerDetails.java diff --git a/backend/controller/controller.go b/backend/controller/controller.go index b86f2644a9..df079e5e94 100644 --- a/backend/controller/controller.go +++ b/backend/controller/controller.go @@ -663,18 +663,7 @@ func (s *Service) GetDeploymentContext(ctx context.Context, req *connect.Request logger := log.FromContext(ctx) updates := s.routeTable.Subscribe() defer s.routeTable.Unsubscribe(updates) - view := s.controllerState.View() depName := req.Msg.Deployment - if !strings.HasPrefix(depName, "dpl-") { - // For hot reload endponts we might not have a deployment key - deps := view.GetActiveDeployments() - for _, dep := range deps { - if dep.Module == depName { - depName = dep.Key.String() - break - } - } - } key, err := model.ParseDeploymentKey(depName) if err != nil { return connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("invalid deployment key: %w", err)) @@ -723,7 +712,7 @@ func (s *Service) GetDeploymentContext(ctx context.Context, req *connect.Request } } if deployment.Schema.Runtime != nil && deployment.Schema.Runtime.Deployment != nil { - routeTable[module] = deployment.Schema.Runtime.Deployment.Endpoint + routeTable[deployment.Key.String()] = deployment.Schema.Runtime.Deployment.Endpoint } if err != nil { diff --git a/backend/protos/xyz/block/ftl/language/v1/language.pb.go b/backend/protos/xyz/block/ftl/language/v1/language.pb.go index 5a39060771..3ec32bd275 100644 --- a/backend/protos/xyz/block/ftl/language/v1/language.pb.go +++ b/backend/protos/xyz/block/ftl/language/v1/language.pb.go @@ -1290,6 +1290,8 @@ type BuildSuccess struct { DevEndpoint *string `protobuf:"bytes,7,opt,name=dev_endpoint,json=devEndpoint,proto3,oneof" json:"dev_endpoint,omitempty"` // Dev mode debug port DebugPort *int32 `protobuf:"varint,8,opt,name=debug_port,json=debugPort,proto3,oneof" json:"debug_port,omitempty"` + // Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin + DevRunnerInfoFile *string `protobuf:"bytes,9,opt,name=dev_runner_info_file,json=devRunnerInfoFile,proto3,oneof" json:"dev_runner_info_file,omitempty"` } func (x *BuildSuccess) Reset() { @@ -1378,6 +1380,13 @@ func (x *BuildSuccess) GetDebugPort() int32 { return 0 } +func (x *BuildSuccess) GetDevRunnerInfoFile() string { + if x != nil && x.DevRunnerInfoFile != nil { + return *x.DevRunnerInfoFile + } + return "" +} + // BuildFailure should be sent when a build fails. // // FTL may ignore this event if it does not match FTL's current build context and state. @@ -2050,7 +2059,7 @@ var file_xyz_block_ftl_language_v1_language_proto_rawDesc = []byte{ 0x74, 0x65, 0x78, 0x74, 0x22, 0x33, 0x0a, 0x12, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x22, 0xfd, 0x02, 0x0a, 0x0c, 0x42, 0x75, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x22, 0xcc, 0x03, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, @@ -2072,146 +2081,151 @@ var file_xyz_block_ftl_language_v1_language_proto_rawDesc = []byte{ 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x09, 0x64, 0x65, 0x62, - 0x75, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x65, - 0x76, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, - 0x65, 0x62, 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x22, 0xd6, 0x01, 0x0a, 0x0c, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, - 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, - 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, - 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, - 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x37, 0x0a, 0x17, 0x69, 0x6e, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, - 0x65, 0x73, 0x22, 0x9b, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, - 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, - 0x64, 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, + 0x75, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x14, 0x64, 0x65, 0x76, + 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x66, 0x69, 0x6c, + 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x11, 0x64, 0x65, 0x76, 0x52, 0x75, + 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x46, 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x42, + 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x42, + 0x17, 0x0a, 0x15, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0xd6, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x61, + 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, + 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x37, 0x0a, 0x17, 0x69, 0x6e, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, + 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, + 0x73, 0x22, 0x9b, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, + 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, + 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, + 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, + 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, + 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x46, + 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, + 0xa8, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x5e, 0x0a, 0x14, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x12, 0x6e, 0x61, 0x74, 0x69, + 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, 0x01, + 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0xa2, 0x01, 0x0a, 0x19, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, + 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x75, 0x62, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, 0x62, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x79, 0x6e, 0x63, + 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb9, 0x08, 0x0a, 0x0f, 0x4c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, + 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, + 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x6f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, - 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x32, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x22, 0xa8, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, - 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x6d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, - 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x30, 0x01, 0x12, 0x84, 0x01, 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x5e, 0x0a, 0x14, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x12, 0x6e, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, - 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa2, 0x01, 0x0a, 0x19, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, - 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, - 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x75, 0x62, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, 0x62, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x79, 0x6e, - 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb9, 0x08, 0x0a, 0x0f, 0x4c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, - 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, - 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x6f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x0d, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x12, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, - 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, - 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x30, 0x01, 0x12, 0x84, 0x01, 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x35, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x0d, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x12, 0x2f, 0x2e, 0x78, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, - 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x81, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x34, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, - 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x52, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x54, 0x42, 0x44, 0x35, 0x34, 0x35, 0x36, 0x36, 0x39, 0x37, 0x35, 0x2f, - 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, - 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x81, + 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, + 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x34, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, + 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x52, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x54, 0x42, 0x44, 0x35, 0x34, 0x35, 0x36, 0x36, 0x39, 0x37, 0x35, 0x2f, 0x66, + 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x6c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/backend/protos/xyz/block/ftl/language/v1/language.proto b/backend/protos/xyz/block/ftl/language/v1/language.proto index 0ff5e620dc..4a31873768 100644 --- a/backend/protos/xyz/block/ftl/language/v1/language.proto +++ b/backend/protos/xyz/block/ftl/language/v1/language.proto @@ -235,6 +235,9 @@ message BuildSuccess { // Dev mode debug port optional int32 debug_port = 8; + + // Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin + optional string dev_runner_info_file = 9; } // BuildFailure should be sent when a build fails. diff --git a/backend/provisioner/scaling/localscaling/local_scaling.go b/backend/provisioner/scaling/localscaling/local_scaling.go index 4cf1ccf8d3..86bbda3428 100644 --- a/backend/provisioner/scaling/localscaling/local_scaling.go +++ b/backend/provisioner/scaling/localscaling/local_scaling.go @@ -103,8 +103,9 @@ func (l *localScaling) TerminatePreviousDeployments(ctx context.Context, module type devModeRunner struct { uri url.URL // The deployment key of the deployment that is currently running - deploymentKey optional.Option[model.DeploymentKey] - debugPort int + deploymentKey optional.Option[model.DeploymentKey] + debugPort int + runnerInfoFile optional.Option[string] } func (l *localScaling) Start(ctx context.Context) error { @@ -127,8 +128,9 @@ func (l *localScaling) Start(ctx context.Context) error { // Must be called under lock func (l *localScaling) updateDevModeEndpoint(ctx context.Context, devEndpoints dev.LocalEndpoint) { l.devModeEndpoints[devEndpoints.Module] = &devModeRunner{ - uri: devEndpoints.Endpoint, - debugPort: devEndpoints.DebugPort, + uri: devEndpoints.Endpoint, + debugPort: devEndpoints.DebugPort, + runnerInfoFile: devEndpoints.RunnerInfoFile, } if ide, ok := l.ideSupport.Get(); ok { if devEndpoints.DebugPort != 0 { @@ -262,9 +264,11 @@ func (l *localScaling) startRunner(ctx context.Context, deploymentKey model.Depl devEndpoint := l.devModeEndpoints[info.module] devURI := optional.None[url.URL]() + devRunnerInfoFile := optional.None[string]() debugPort := 0 if devEndpoint != nil { devURI = optional.Some(devEndpoint.uri) + devRunnerInfoFile = devEndpoint.runnerInfoFile if devKey, ok := devEndpoint.deploymentKey.Get(); ok && devKey.Equal(deploymentKey) { // Already running, don't start another return nil @@ -304,6 +308,7 @@ func (l *localScaling) startRunner(ctx context.Context, deploymentKey model.Depl Deployment: deploymentKey, DebugPort: debugPort, DevEndpoint: devURI, + DevRunnerInfoFile: devRunnerInfoFile, } simpleName := fmt.Sprintf("runner%d", keySuffix) diff --git a/backend/runner/runner.go b/backend/runner/runner.go index 8880eac611..3e8ddf02bb 100644 --- a/backend/runner/runner.go +++ b/backend/runner/runner.go @@ -25,6 +25,7 @@ import ( mysql "github.com/block/ftl-mysql-auth-proxy" "github.com/jpillora/backoff" "github.com/otiai10/copy" + "github.com/puzpuzpuz/xsync/v3" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" @@ -67,7 +68,8 @@ type Config struct { HeartbeatJitter time.Duration `help:"Jitter to add to heartbeat period." default:"2s"` Deployment model.DeploymentKey `help:"The deployment this runner is for." env:"FTL_DEPLOYMENT"` DebugPort int `help:"The port to use for debugging." env:"FTL_DEBUG_PORT"` - DevEndpoint optional.Option[url.URL] `help:"An existing endpoint to connect to in development mode" env:"FTL_DEV_ENDPOINT"` + DevEndpoint optional.Option[url.URL] `help:"An existing endpoint to connect to in development mode" hidden:""` + DevRunnerInfoFile optional.Option[string] `help:"The path to a file that we write dev endpoint information to." hidden:""` } func Start(ctx context.Context, config Config, storage *artefacts.OCIArtefactService) error { @@ -121,6 +123,7 @@ func Start(ctx context.Context, config Config, storage *artefacts.OCIArtefactSer deploymentLogQueue: make(chan log.Entry, 10000), cancelFunc: doneFunc, devEndpoint: config.DevEndpoint, + devRunnerInfoFile: config.DevRunnerInfoFile, } module, err := svc.getModule(ctx, config.Deployment) @@ -131,11 +134,12 @@ func Start(ctx context.Context, config Config, storage *artefacts.OCIArtefactSer startedLatch := &sync.WaitGroup{} startedLatch.Add(2) g, ctx := errgroup.WithContext(ctx) + dbAddresses := xsync.NewMapOf[string, string]() g.Go(func() error { - return svc.startPgProxy(ctx, module, startedLatch) + return svc.startPgProxy(ctx, module, startedLatch, dbAddresses) }) g.Go(func() error { - return svc.startMySQLProxy(ctx, module, startedLatch) + return svc.startMySQLProxy(ctx, module, startedLatch, dbAddresses) }) g.Go(func() error { startedLatch.Wait() @@ -143,15 +147,15 @@ func Start(ctx context.Context, config Config, storage *artefacts.OCIArtefactSer case <-ctx.Done(): return ctx.Err() default: - return svc.startDeployment(ctx, config.Deployment, module) + return svc.startDeployment(ctx, config.Deployment, module, dbAddresses) } }) return fmt.Errorf("failure in runner: %w", g.Wait()) } -func (s *Service) startDeployment(ctx context.Context, key model.DeploymentKey, module *schema.Module) error { - err := s.deploy(ctx, key, module) +func (s *Service) startDeployment(ctx context.Context, key model.DeploymentKey, module *schema.Module, dbAddresses *xsync.MapOf[string, string]) error { + err := s.deploy(ctx, key, module, dbAddresses) if err != nil { // If we fail to deploy we just exit // Kube or local scaling will start a new instance to continue @@ -252,6 +256,7 @@ type Service struct { deploymentLogQueue chan log.Entry cancelFunc func() devEndpoint optional.Option[url.URL] + devRunnerInfoFile optional.Option[string] proxy *proxy.Service pubSub *pubsub.Service proxyBindAddress *url.URL @@ -296,7 +301,7 @@ func (s *Service) getModule(ctx context.Context, key model.DeploymentKey) (*sche return module, nil } -func (s *Service) deploy(ctx context.Context, key model.DeploymentKey, module *schema.Module) error { +func (s *Service) deploy(ctx context.Context, key model.DeploymentKey, module *schema.Module, dbAddresses *xsync.MapOf[string, string]) error { logger := log.FromContext(ctx) if err, ok := s.registrationFailure.Load().Get(); ok { @@ -331,6 +336,45 @@ func (s *Service) deploy(ctx context.Context, key model.DeploymentKey, module *s return fmt.Errorf("failed to create deployment directory: %w", err) } } + + deploymentServiceClient := rpc.Dial(ftldeploymentconnect.NewDeploymentServiceClient, s.config.ControllerEndpoint.String(), log.Error) + ctx = rpc.ContextWithClient(ctx, deploymentServiceClient) + + leaseServiceClient := rpc.Dial(ftlleaseconnect.NewLeaseServiceClient, s.config.LeaseEndpoint.String(), log.Error) + + timelineClient := timeline.NewClient(ctx, s.config.TimelineEndpoint) + s.proxy = proxy.New(deploymentServiceClient, leaseServiceClient, timelineClient) + + pubSub, err := pubsub.New(module, key, s, timelineClient) + if err != nil { + observability.Deployment.Failure(ctx, optional.Some(key.String())) + return fmt.Errorf("failed to create pubsub service: %w", err) + } + s.pubSub = pubSub + + parse, err := url.Parse("http://127.0.0.1:0") + if err != nil { + return fmt.Errorf("failed to parse url: %w", err) + } + proxyServer, err := rpc.NewServer(ctx, parse, + rpc.GRPC(ftlv1connect.NewVerbServiceHandler, s.proxy), + rpc.GRPC(ftldeploymentconnect.NewDeploymentServiceHandler, s.proxy), + rpc.GRPC(ftlleaseconnect.NewLeaseServiceHandler, s.proxy), + rpc.GRPC(pubconnect.NewPublishServiceHandler, s.pubSub), + ) + if err != nil { + return fmt.Errorf("failed to create server: %w", err) + } + urls := proxyServer.Bind.Subscribe(nil) + go func() { + err := proxyServer.Serve(ctx) + if err != nil { + logger.Errorf(err, "failed to serve") + return + } + }() + s.proxyBindAddress = <-urls + var dep *deployment if ep, ok := s.devEndpoint.Get(); ok { client := rpc.Dial(ftlv1connect.NewVerbServiceClient, ep.String(), log.Error) @@ -341,6 +385,18 @@ func (s *Service) deploy(ctx context.Context, key model.DeploymentKey, module *s endpoint: &ep, client: client, } + if file, ok := s.devRunnerInfoFile.Get(); ok { + fileContents := "proxy.bind.address=" + s.proxyBindAddress.String() + fileContents += fmt.Sprintf("\ndeployment=%s", s.config.Deployment.String()) + dbAddresses.Range(func(key string, value string) bool { + fileContents += fmt.Sprintf("\ndatabase.%s.url=%s", key, value) + return true + }) + err = os.WriteFile(file, []byte(fileContents), 0660) // #nosec + if err != nil { + logger.Errorf(err, "could not create FTL dev Config") + } + } } else { err := download.ArtefactsFromOCI(ctx, s.controllerClient, key, deploymentDir, s.storage) if err != nil { @@ -348,44 +404,6 @@ func (s *Service) deploy(ctx context.Context, key model.DeploymentKey, module *s return fmt.Errorf("failed to download artefacts: %w", err) } - deploymentServiceClient := rpc.Dial(ftldeploymentconnect.NewDeploymentServiceClient, s.config.ControllerEndpoint.String(), log.Error) - ctx = rpc.ContextWithClient(ctx, deploymentServiceClient) - - leaseServiceClient := rpc.Dial(ftlleaseconnect.NewLeaseServiceClient, s.config.LeaseEndpoint.String(), log.Error) - - timelineClient := timeline.NewClient(ctx, s.config.TimelineEndpoint) - s.proxy = proxy.New(deploymentServiceClient, leaseServiceClient, timelineClient) - - pubSub, err := pubsub.New(module, key, s, timelineClient) - if err != nil { - observability.Deployment.Failure(ctx, optional.Some(key.String())) - return fmt.Errorf("failed to create pubsub service: %w", err) - } - s.pubSub = pubSub - - parse, err := url.Parse("http://127.0.0.1:0") - if err != nil { - return fmt.Errorf("failed to parse url: %w", err) - } - proxyServer, err := rpc.NewServer(ctx, parse, - rpc.GRPC(ftlv1connect.NewVerbServiceHandler, s.proxy), - rpc.GRPC(ftldeploymentconnect.NewDeploymentServiceHandler, s.proxy), - rpc.GRPC(ftlleaseconnect.NewLeaseServiceHandler, s.proxy), - rpc.GRPC(pubconnect.NewPublishServiceHandler, s.pubSub), - ) - if err != nil { - return fmt.Errorf("failed to create server: %w", err) - } - urls := proxyServer.Bind.Subscribe(nil) - go func() { - err := proxyServer.Serve(ctx) - if err != nil { - logger.Errorf(err, "failed to serve") - return - } - }() - s.proxyBindAddress = <-urls - logger.Debugf("Setting FTL_ENDPOINT to %s", s.proxyBindAddress.String()) envVars := []string{"FTL_ENDPOINT=" + s.proxyBindAddress.String(), "FTL_CONFIG=" + strings.Join(s.config.Config, ","), @@ -600,7 +618,7 @@ func (s *Service) healthCheck(writer http.ResponseWriter, request *http.Request) writer.WriteHeader(http.StatusServiceUnavailable) } -func (s *Service) startPgProxy(ctx context.Context, module *schema.Module, started *sync.WaitGroup) error { +func (s *Service) startPgProxy(ctx context.Context, module *schema.Module, started *sync.WaitGroup, addresses *xsync.MapOf[string, string]) error { logger := log.FromContext(ctx) databases := map[string]*schema.Database{} @@ -620,7 +638,11 @@ func (s *Service) startPgProxy(ctx context.Context, module *schema.Module, start go func() { select { case pgProxy := <-channel: - os.Setenv("FTL_PROXY_POSTGRES_ADDRESS", fmt.Sprintf("127.0.0.1:%d", pgProxy.Address.Port)) + address := fmt.Sprintf("127.0.0.1:%d", pgProxy.Address.Port) + for db := range databases { + addresses.Store(db, address) + } + os.Setenv("FTL_PROXY_POSTGRES_ADDRESS", address) started.Done() case <-ctx.Done(): started.Done() @@ -649,7 +671,7 @@ func (s *Service) startPgProxy(ctx context.Context, module *schema.Module, start return nil } -func (s *Service) startMySQLProxy(ctx context.Context, module *schema.Module, latch *sync.WaitGroup) error { +func (s *Service) startMySQLProxy(ctx context.Context, module *schema.Module, latch *sync.WaitGroup, addresses *xsync.MapOf[string, string]) error { defer latch.Done() logger := log.FromContext(ctx) @@ -689,7 +711,9 @@ func (s *Service) startMySQLProxy(ctx context.Context, module *schema.Module, la case port = <-portC: } - os.Setenv(strings.ToUpper("FTL_PROXY_MYSQL_ADDRESS_"+decl.Name), fmt.Sprintf("127.0.0.1:%d", port)) + address := fmt.Sprintf("127.0.0.1:%d", port) + addresses.Store(decl.Name, address) + os.Setenv(strings.ToUpper("FTL_PROXY_MYSQL_ADDRESS_"+decl.Name), address) } return nil } diff --git a/examples/java/echo/src/main/java/ftl/echo/Echo.java b/examples/java/echo/src/main/java/ftl/echo/Echo.java index a73c468d53..dca24169fd 100644 --- a/examples/java/echo/src/main/java/ftl/echo/Echo.java +++ b/examples/java/echo/src/main/java/ftl/echo/Echo.java @@ -10,6 +10,6 @@ public class Echo { @Verb public EchoResponse echo(EchoRequest req, TimeClient time) { var response = time.time(); - return new EchoResponse("Hello, " + req.name().orElse("anonymous") + "! The time is " + response.toString() + "."); + return new EchoResponse("Hello, " + req.name().orElse("anonymous") + "! The time is " + response.getTime() + "."); } } diff --git a/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts b/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts index c1a105f8a5..2c588100e7 100644 --- a/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts +++ b/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts @@ -1169,6 +1169,13 @@ export class BuildSuccess extends Message { */ debugPort?: number; + /** + * Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin + * + * @generated from field: optional string dev_runner_info_file = 9; + */ + devRunnerInfoFile?: string; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -1185,6 +1192,7 @@ export class BuildSuccess extends Message { { no: 6, name: "errors", kind: "message", T: ErrorList }, { no: 7, name: "dev_endpoint", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 8, name: "debug_port", kind: "scalar", T: 5 /* ScalarType.INT32 */, opt: true }, + { no: 9, name: "dev_runner_info_file", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): BuildSuccess { diff --git a/internal/buildengine/build.go b/internal/buildengine/build.go index f3b048c402..12bb003244 100644 --- a/internal/buildengine/build.go +++ b/internal/buildengine/build.go @@ -88,7 +88,7 @@ func handleBuildResult(ctx context.Context, projectConfig projectconfig.Config, if devModeEndpoints != nil { parsed, err := url.Parse(endpoint) if err == nil { - devModeEndpoints <- dev.LocalEndpoint{Module: config.Module, Endpoint: *parsed, DebugPort: result.DebugPort, Language: config.Language} + devModeEndpoints <- dev.LocalEndpoint{Module: config.Module, Endpoint: *parsed, DebugPort: result.DebugPort, Language: config.Language, RunnerInfoFile: result.DevRunnerInfoFile} } } } diff --git a/internal/buildengine/languageplugin/plugin.go b/internal/buildengine/languageplugin/plugin.go index 750026024e..cc3638f8b7 100644 --- a/internal/buildengine/languageplugin/plugin.go +++ b/internal/buildengine/languageplugin/plugin.go @@ -40,6 +40,9 @@ type BuildResult struct { // Endpoint of an instance started by the plugin to use in dev mode DevEndpoint optional.Option[string] + // File that the runner can use to pass info into the hot reload endpoint + DevRunnerInfoFile optional.Option[string] + DebugPort int } @@ -572,12 +575,13 @@ func buildResultFromProto(result either.Either[*langpb.BuildResponse_BuildSucces port = int(*buildSuccess.DebugPort) } return BuildResult{ - Errors: errs, - Schema: moduleSch, - Deploy: buildSuccess.Deploy, - StartTime: startTime, - DevEndpoint: optional.Ptr(buildSuccess.DevEndpoint), - DebugPort: port, + Errors: errs, + Schema: moduleSch, + Deploy: buildSuccess.Deploy, + StartTime: startTime, + DevEndpoint: optional.Ptr(buildSuccess.DevEndpoint), + DevRunnerInfoFile: optional.Ptr(buildSuccess.DevRunnerInfoFile), + DebugPort: port, }, nil case either.Right[*langpb.BuildResponse_BuildSuccess, *langpb.BuildResponse_BuildFailure]: buildFailure := result.Get().BuildFailure diff --git a/internal/dev/devendpoint.go b/internal/dev/devendpoint.go index 4ad8a5b652..ec7f7c2877 100644 --- a/internal/dev/devendpoint.go +++ b/internal/dev/devendpoint.go @@ -1,10 +1,15 @@ package dev -import "net/url" +import ( + "net/url" + + "github.com/alecthomas/types/optional" +) type LocalEndpoint struct { - Module string - Endpoint url.URL - DebugPort int - Language string + Module string + Endpoint url.URL + DebugPort int + Language string + RunnerInfoFile optional.Option[string] } diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index 85734dc8d6..74608f0d7c 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -1,5 +1,6 @@ package xyz.block.ftl.deployment; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -33,12 +34,15 @@ import io.quarkus.deployment.builditem.ApplicationInfoBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.LaunchModeBuildItem; import io.quarkus.deployment.builditem.RunTimeConfigBuilderBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.grpc.deployment.BindableServiceBuildItem; +import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.util.HashUtil; import io.quarkus.vertx.http.deployment.RequireSocketHttpBuildItem; import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem; import xyz.block.ftl.language.v1.Error; @@ -61,6 +65,12 @@ public class ModuleProcessor { private static final String SCHEMA_OUT = "schema.pb"; private static final String ERRORS_OUT = "errors.pb"; + public static final String DEV_MODE_RUNNER_INFO_FILE = "FTL_RUNNER_INFO"; + + /** + * Persistent schema hash, used to detect runner restarts in dev mode. + */ + static String schemaHash; @BuildStep BindableServiceBuildItem verbService() { @@ -191,6 +201,7 @@ public void generateSchema(CombinedIndexBuildItem index, VerbClientBuildItem verbClientBuildItem, DefaultOptionalBuildItem defaultOptionalBuildItem, List schemaContributorBuildItems, + LaunchModeBuildItem launchModeBuildItem, CommentsBuildItem comments) throws Exception { String moduleName = moduleNameBuildItem.getModuleName(); @@ -205,11 +216,34 @@ public void generateSchema(CombinedIndexBuildItem index, log.infof("Generating module '%s' schema from %d decls", moduleName, moduleBuilder.getDeclsCount()); Path output = outputTargetBuildItem.getOutputDirectory().resolve(SCHEMA_OUT); Path errorOutput = outputTargetBuildItem.getOutputDirectory().resolve(ERRORS_OUT); - try (var out = Files.newOutputStream(output)) { - try (var errorOut = Files.newOutputStream(errorOutput)) { - moduleBuilder.writeTo(out, errorOut); + ByteArrayOutputStream sch = new ByteArrayOutputStream(); + ByteArrayOutputStream err = new ByteArrayOutputStream(); + moduleBuilder.writeTo(sch, err); + + var schBytes = sch.toByteArray(); + var errBytes = err.toByteArray(); + + if (launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT) { + // Handle runner restarts in development mode. If this is the first launch, or the schema has changed, we need to + // get updated runner information, although we don't actually get this until the runner has started. + var hash = HashUtil.sha256(schBytes); + if (Objects.equals(hash, schemaHash)) { + return; + } + schemaHash = hash; + String runnerInfo = System.getenv(DEV_MODE_RUNNER_INFO_FILE); + if (runnerInfo != null) { + Path path = Path.of(runnerInfo); + // Delete the runner info file if it already exists + Files.deleteIfExists(path); + // This method tells the runtime not to actually start until we have updated runner details + recorder.handleDevModeRunnerStart(runnerInfo); } } + recorder.loadModuleContextOnStartup(); + + Files.write(output, schBytes); + Files.write(errorOutput, errBytes); output = outputTargetBuildItem.getOutputDirectory().resolve("launch"); try (var out = Files.newOutputStream(output)) { diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DatasourceDetails.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DatasourceDetails.java new file mode 100644 index 0000000000..7bc69c1db3 --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DatasourceDetails.java @@ -0,0 +1,60 @@ +package xyz.block.ftl.runtime; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.regex.Pattern; + +import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; + +public record DatasourceDetails(String connectionString, String username, String password) { + + public static DatasourceDetails fromDSN(String dsn, GetDeploymentContextResponse.DbType type) { + String prefix = type.equals(GetDeploymentContextResponse.DbType.DB_TYPE_MYSQL) ? "jdbc:mysql" : "jdbc:postgresql"; + try { + URI uri = new URI(dsn); + String username = ""; + String password = ""; + String userInfo = uri.getUserInfo(); + if (userInfo != null) { + var split = userInfo.split(":"); + username = split[0]; + password = split[1]; + return new DatasourceDetails( + new URI(prefix, null, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), null) + .toASCIIString(), + username, password); + } else { + //TODO: this is horrible, just quick hack for now + var matcher = Pattern.compile("[&?]user=([^?&]*)").matcher(dsn); + if (matcher.find()) { + username = matcher.group(1); + dsn = matcher.replaceAll(""); + } + matcher = Pattern.compile("[&?]password=([^?&]*)").matcher(dsn); + if (matcher.find()) { + password = matcher.group(1); + dsn = matcher.replaceAll(""); + } + matcher = Pattern.compile("^([^:]+):([^:]+)@").matcher(dsn); + if (matcher.find()) { + username = matcher.group(1); + password = matcher.group(2); + dsn = matcher.replaceAll(""); + } + matcher = Pattern.compile("tcp\\(([^:)]+):([^:)]+)\\)").matcher(dsn); + if (matcher.find()) { + // Mysql has a messed up syntax + dsn = matcher.replaceAll(matcher.group(1) + ":" + matcher.group(2)); + } + dsn = dsn.replaceAll("postgresql://", ""); + dsn = dsn.replaceAll("postgres://", ""); + dsn = dsn.replaceAll("mysql://", ""); + dsn = prefix + "://" + dsn; + return new DatasourceDetails(dsn, username, password); + } + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DefaultRunnerDetails.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DefaultRunnerDetails.java new file mode 100644 index 0000000000..aaa7acd8db --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DefaultRunnerDetails.java @@ -0,0 +1,47 @@ +package xyz.block.ftl.runtime; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; + +/** + * Default implementation of RunnerDetails, that uses the environment variables to get the proxy address and database + */ +public class DefaultRunnerDetails implements RunnerDetails { + + static final DefaultRunnerDetails INSTANCE = new DefaultRunnerDetails(); + + private final Map databases = new ConcurrentHashMap<>(); + + @Override + public String getProxyAddress() { + String endpoint = System.getenv("FTL_ENDPOINT"); + String testEndpoint = System.getProperty("ftl.test.endpoint"); //set by the test framework + if (testEndpoint != null) { + endpoint = testEndpoint; + } + if (endpoint == null) { + endpoint = "http://localhost:8892"; + } + return endpoint; + } + + @Override + public Optional getDatabase(String name, GetDeploymentContextResponse.DbType type) { + if (type == GetDeploymentContextResponse.DbType.DB_TYPE_POSTGRES) { + var proxyAddress = System.getenv("FTL_PROXY_POSTGRES_ADDRESS"); + return Optional.of(new DatasourceDetails("jdbc:postgresql://" + proxyAddress + "/" + name, "ftl", "ftl")); + } else if (type == GetDeploymentContextResponse.DbType.DB_TYPE_MYSQL) { + var proxyAddress = System.getenv("FTL_PROXY_MYSQL_ADDRESS_" + name.toUpperCase()); + return Optional.of(new DatasourceDetails("jdbc:mysql://" + proxyAddress + "/" + name, "ftl", "ftl")); + } + return Optional.empty(); + } + + @Override + public String getDeploymentKey() { + return System.getenv("FTL_DEPLOYMENT"); + } +} diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java new file mode 100644 index 0000000000..d1ccc30699 --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java @@ -0,0 +1,119 @@ +package xyz.block.ftl.runtime; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; + +public class DevModeRunnerDetails implements RunnerDetails { + + private final Path path; + private volatile Map databases; + private volatile String proxyAddress; + private volatile String deployment; + private volatile boolean closed; + private static final Pattern dbNames = Pattern.compile("database\\.([a-zA-Z0-9]+).url"); + private volatile boolean loaded = false; + + public DevModeRunnerDetails(Path path) { + this.path = path; + startWatchThread(); + } + + void startWatchThread() { + Thread watchThread = new Thread(() -> { + while (!closed) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + if (Files.exists(path)) { + Properties p = new Properties(); + try (InputStream stream = Files.newInputStream(path)) { + p.load(stream); + synchronized (this) { + proxyAddress = p.getProperty("proxy.bind.address"); + deployment = p.getProperty("deployment"); + var dbs = new HashMap(); + for (var addr : p.stringPropertyNames()) { + Matcher m = dbNames.matcher(addr); + if (m.matches()) { + dbs.put(m.group(1), p.getProperty(addr)); + } + } + databases = dbs; + loaded = true; + notifyAll(); + return; + } + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + }); + watchThread.setDaemon(true); + watchThread.start(); + } + + @Override + public String getProxyAddress() { + waitForLoad(); + if (closed) { + return null; + } + return proxyAddress; + } + + private void waitForLoad() { + while (proxyAddress == null && !closed) { + synchronized (this) { + if (proxyAddress == null && !closed) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(e); + } + } + } + } + } + + @Override + public Optional getDatabase(String database, GetDeploymentContextResponse.DbType type) { + waitForLoad(); + if (closed) { + return Optional.empty(); + } + String connectionString = databases.get(database); + if (connectionString == null) { + return Optional.empty(); + } + return Optional.of(new DatasourceDetails(connectionString, "ftl", "ftl")); + } + + @Override + public String getDeploymentKey() { + waitForLoad(); + if (closed) { + return null; + } + return deployment; + } + + @Override + public void close() { + closed = true; + } +} diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java index bb603edc74..62ecafb2ca 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java @@ -1,59 +1,32 @@ package xyz.block.ftl.runtime; -import java.net.URI; -import java.net.URISyntaxException; +import java.nio.file.Path; import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.Pattern; import org.jboss.logging.Logger; -import com.google.protobuf.ByteString; - -import io.grpc.ManagedChannelBuilder; -import io.grpc.stub.StreamObserver; import xyz.block.ftl.LeaseClient; import xyz.block.ftl.LeaseFailedException; import xyz.block.ftl.LeaseHandle; -import xyz.block.ftl.deployment.v1.*; -import xyz.block.ftl.lease.v1.AcquireLeaseRequest; -import xyz.block.ftl.lease.v1.AcquireLeaseResponse; -import xyz.block.ftl.lease.v1.LeaseServiceGrpc; -import xyz.block.ftl.publish.v1.*; -import xyz.block.ftl.publish.v1.PublishEventRequest; -import xyz.block.ftl.publish.v1.PublishEventResponse; -import xyz.block.ftl.schema.v1.Ref; -import xyz.block.ftl.v1.*; +import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; public class FTLController implements LeaseClient { private static final Logger log = Logger.getLogger(FTLController.class); final String moduleName; - final String deploymentName; - - private Throwable currentError; - private volatile GetDeploymentContextResponse moduleContextResponse; - private boolean waiters = false; - - final VerbServiceGrpc.VerbServiceStub verbService; - final DeploymentServiceGrpc.DeploymentServiceStub deploymentService; - final LeaseServiceGrpc.LeaseServiceStub leaseService; - final PublishServiceGrpc.PublishServiceStub publishService; - final StreamObserver moduleObserver = new ModuleObserver(); + private volatile FTLRunnerConnection runnerConnection; private static volatile FTLController controller; + /** + * The details of how to connect to the runners proxy. For dev mode this needs to be determined after startup, + * which is why this needs to be pluggable. + */ + private RunnerDetails runnerDetails = DefaultRunnerDetails.INSTANCE; private final Map databases = new ConcurrentHashMap<>(); - /** - * TODO: look at how init should work, this is terrible and will break dev mode - */ public static FTLController instance() { if (controller == null) { synchronized (FTLController.class) { @@ -66,29 +39,7 @@ public static FTLController instance() { } FTLController() { - String endpoint = System.getenv("FTL_ENDPOINT"); - String ftlDeployment = System.getenv("FTL_DEPLOYMENT"); - String testEndpoint = System.getProperty("ftl.test.endpoint"); //set by the test framework - if (testEndpoint != null) { - endpoint = testEndpoint; - } - if (endpoint == null) { - endpoint = "http://localhost:8892"; - } - var uri = URI.create(endpoint); this.moduleName = System.getProperty("ftl.module.name"); - deploymentName = ftlDeployment == null ? moduleName : ftlDeployment; // We use the module name as the default deployment name for running ftl-dev - var channelBuilder = ManagedChannelBuilder.forAddress(uri.getHost(), uri.getPort()); - if (uri.getScheme().equals("http")) { - channelBuilder.usePlaintext(); - } - var channel = channelBuilder.build(); - deploymentService = DeploymentServiceGrpc.newStub(channel); - deploymentService.getDeploymentContext(GetDeploymentContextRequest.newBuilder().setDeployment(deploymentName).build(), - moduleObserver); - verbService = VerbServiceGrpc.newStub(channel); - publishService = PublishServiceGrpc.newStub(channel); - leaseService = LeaseServiceGrpc.newStub(channel); } public void registerDatabase(String name, GetDeploymentContextResponse.DbType type) { @@ -96,256 +47,71 @@ public void registerDatabase(String name, GetDeploymentContextResponse.DbType ty } public byte[] getSecret(String secretName) { - var context = getDeploymentContext(); - if (context.containsSecrets(secretName)) { - return context.getSecretsMap().get(secretName).toByteArray(); - } - throw new RuntimeException("Secret not found: " + secretName); - } - - public byte[] getConfig(String secretName) { - var context = getDeploymentContext(); - if (context.containsConfigs(secretName)) { - return context.getConfigsMap().get(secretName).toByteArray(); - } - throw new RuntimeException("Config not found: " + secretName); - } - - public Datasource getDatasource(String name) { - if (databases.get(name) == GetDeploymentContextResponse.DbType.DB_TYPE_POSTGRES) { - var proxyAddress = System.getenv("FTL_PROXY_POSTGRES_ADDRESS"); - return new Datasource("jdbc:postgresql://" + proxyAddress + "/" + name, "ftl", "ftl"); - } else if (databases.get(name) == GetDeploymentContextResponse.DbType.DB_TYPE_MYSQL) { - var proxyAddress = System.getenv("FTL_PROXY_MYSQL_ADDRESS_" + name.toUpperCase()); - return new Datasource("jdbc:mysql://" + proxyAddress + "/" + name, "ftl", "ftl"); - } - List databasesList = getDeploymentContext().getDatabasesList(); - for (var i : databasesList) { - if (i.getName().equals(name)) { - return Datasource.fromDSN(i.getDsn(), i.getType()); - } - } - return null; - } - - public byte[] callVerb(String name, String module, byte[] payload) { - CompletableFuture cf = new CompletableFuture<>(); - - verbService.call(CallRequest.newBuilder().setVerb(Ref.newBuilder().setModule(module).setName(name)) - .setBody(ByteString.copyFrom(payload)).build(), new StreamObserver<>() { - - @Override - public void onNext(CallResponse callResponse) { - if (callResponse.hasError()) { - cf.completeExceptionally(new RuntimeException(callResponse.getError().getMessage())); - } else { - cf.complete(callResponse.getBody().toByteArray()); - } - } - - @Override - public void onError(Throwable throwable) { - cf.completeExceptionally(throwable); - } - - @Override - public void onCompleted() { - - } - }); - try { - return cf.get(); - } catch (Exception e) { - throw new RuntimeException(e); - } + return getRunnerConnection().getSecret(secretName); } - public void publishEvent(String topic, String callingVerbName, byte[] event, String key) { - CompletableFuture cf = new CompletableFuture<>(); - publishService.publishEvent(PublishEventRequest.newBuilder() - .setCaller(callingVerbName).setBody(ByteString.copyFrom(event)) - .setTopic(Ref.newBuilder().setModule(moduleName).setName(topic).build()) - .setKey(key).build(), - new StreamObserver() { - @Override - public void onNext(PublishEventResponse publishEventResponse) { - cf.complete(null); - } - - @Override - public void onError(Throwable throwable) { - cf.completeExceptionally(throwable); - } - - @Override - public void onCompleted() { - cf.complete(null); - } - }); - try { - cf.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } - - public LeaseHandle acquireLease(Duration duration, String... keys) throws LeaseFailedException { - CompletableFuture cf = new CompletableFuture<>(); - var client = leaseService.acquireLease(new StreamObserver() { - @Override - public void onNext(AcquireLeaseResponse value) { - cf.complete(null); - } - - @Override - public void onError(Throwable t) { - cf.completeExceptionally(t); - } - - @Override - public void onCompleted() { - if (!cf.isDone()) { - onError(new RuntimeException("stream closed")); + private FTLRunnerConnection getRunnerConnection() { + if (runnerConnection == null) { + synchronized (this) { + if (runnerConnection == null) { + runnerConnection = new FTLRunnerConnection(runnerDetails.getProxyAddress(), + runnerDetails.getDeploymentKey(), moduleName); } } - }); - List realKeys = new ArrayList<>(); - realKeys.add("module"); - realKeys.add(moduleName); - realKeys.addAll(Arrays.asList(keys)); - client.onNext(AcquireLeaseRequest.newBuilder() - .addAllKey(realKeys) - .setTtl(com.google.protobuf.Duration.newBuilder() - .setSeconds(duration.toSeconds())) - .build()); - try { - cf.get(); - } catch (Exception e) { - throw new LeaseFailedException("lease already held", e); } - return new LeaseHandle() { - @Override - public void close() { - client.onCompleted(); - } - }; + return runnerConnection; } - private GetDeploymentContextResponse getDeploymentContext() { - var moduleContext = moduleContextResponse; - if (moduleContext != null) { - return moduleContext; - } - synchronized (moduleObserver) { - for (;;) { - moduleContext = moduleContextResponse; - if (moduleContext != null) { - return moduleContext; - } - if (currentError != null) { - throw new RuntimeException(currentError); - } - waiters = true; + public void waitForDevModeStart(Path runnerInfo) { + synchronized (this) { + if (runnerConnection != null) { try { - moduleObserver.wait(); - } catch (InterruptedException e) { - throw new RuntimeException(e); + runnerConnection.close(); + } catch (Exception e) { + log.error("Failed to close runner connection", e); } + runnerConnection = null; } - + runnerDetails.close(); + runnerDetails = new DevModeRunnerDetails(runnerInfo); } - } - private class ModuleObserver implements StreamObserver { + } - final AtomicInteger failCount = new AtomicInteger(); + public byte[] getConfig(String config) { + return getRunnerConnection().getConfig(config); + } - @Override - public void onNext(GetDeploymentContextResponse moduleContextResponse) { - synchronized (this) { - currentError = null; - FTLController.this.moduleContextResponse = moduleContextResponse; - if (waiters) { - this.notifyAll(); - waiters = false; - } + public DatasourceDetails getDatasource(String name) { + GetDeploymentContextResponse.DbType type = databases.get(name); + if (type != null) { + var address = runnerDetails.getDatabase(name, type); + if (address.isPresent()) { + return address.get(); } - } - - @Override - public void onError(Throwable throwable) { - log.error("GRPC connection error", throwable); - synchronized (this) { - currentError = throwable; - if (waiters) { - this.notifyAll(); - waiters = false; - } - } - if (failCount.incrementAndGet() < 5) { - deploymentService.getDeploymentContext( - GetDeploymentContextRequest.newBuilder().setDeployment(deploymentName).build(), - moduleObserver); + List databasesList = getRunnerConnection().getDeploymentContext().getDatabasesList(); + for (var i : databasesList) { + if (i.getName().equals(name)) { + return DatasourceDetails.fromDSN(i.getDsn(), i.getType()); } } + return null; + } - @Override - public void onCompleted() { - onError(new RuntimeException("connection closed")); - } + public byte[] callVerb(String name, String module, byte[] payload) { + return getRunnerConnection().callVerb(name, module, payload); } - public record Datasource(String connectionString, String username, String password) { + public void publishEvent(String topic, String callingVerbName, byte[] event, String key) { + getRunnerConnection().publishEvent(topic, callingVerbName, event, key); + } - public static Datasource fromDSN(String dsn, GetDeploymentContextResponse.DbType type) { - String prefix = type.equals(GetDeploymentContextResponse.DbType.DB_TYPE_MYSQL) ? "jdbc:mysql" : "jdbc:postgresql"; - try { - URI uri = new URI(dsn); - String username = ""; - String password = ""; - String userInfo = uri.getUserInfo(); - if (userInfo != null) { - var split = userInfo.split(":"); - username = split[0]; - password = split[1]; - return new Datasource( - new URI(prefix, null, uri.getHost(), uri.getPort(), uri.getPath(), uri.getQuery(), null) - .toASCIIString(), - username, password); - } else { - //TODO: this is horrible, just quick hack for now - var matcher = Pattern.compile("[&?]user=([^?&]*)").matcher(dsn); - if (matcher.find()) { - username = matcher.group(1); - dsn = matcher.replaceAll(""); - } - matcher = Pattern.compile("[&?]password=([^?&]*)").matcher(dsn); - if (matcher.find()) { - password = matcher.group(1); - dsn = matcher.replaceAll(""); - } - matcher = Pattern.compile("^([^:]+):([^:]+)@").matcher(dsn); - if (matcher.find()) { - username = matcher.group(1); - password = matcher.group(2); - dsn = matcher.replaceAll(""); - } - matcher = Pattern.compile("tcp\\(([^:)]+):([^:)]+)\\)").matcher(dsn); - if (matcher.find()) { - // Mysql has a messed up syntax - dsn = matcher.replaceAll(matcher.group(1) + ":" + matcher.group(2)); - } - dsn = dsn.replaceAll("postgresql://", ""); - dsn = dsn.replaceAll("postgres://", ""); - dsn = dsn.replaceAll("mysql://", ""); - dsn = prefix + "://" + dsn; - return new Datasource(dsn, username, password); - } - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } + public LeaseHandle acquireLease(Duration duration, String... keys) throws LeaseFailedException { + return getRunnerConnection().acquireLease(duration, keys); + } + public void loadDeploymentContext() { + getRunnerConnection().getDeploymentContext(); } } diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLDatasourceCredentials.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLDatasourceCredentials.java index 255b79d3cd..f9cb6efcfc 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLDatasourceCredentials.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLDatasourceCredentials.java @@ -15,7 +15,7 @@ public class FTLDatasourceCredentials implements CredentialsProvider { @Override public Map getCredentials(String credentialsProviderName) { - FTLController.Datasource datasource = FTLController.instance().getDatasource(credentialsProviderName); + DatasourceDetails datasource = FTLController.instance().getDatasource(credentialsProviderName); if (datasource == null) { return null; } diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java index 7b1f19fdf0..3b67e8e65e 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java @@ -2,6 +2,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; import java.util.List; import java.util.Timer; import java.util.TimerTask; @@ -162,7 +163,7 @@ public void startReloadTimer(ShutdownContext shutdownContext) { t.schedule(new TimerTask() { @Override public void run() { - HotReloadSetup.doScan(); + HotReloadSetup.doScan(false); } }, 1000, 1000); shutdownContext.addShutdownTask(new Runnable() { @@ -176,4 +177,12 @@ public void run() { public void registerDatabase(String dbKind, GetDeploymentContextResponse.DbType name) { FTLController.instance().registerDatabase(dbKind, name); } + + public void handleDevModeRunnerStart(String runnerInfo) { + FTLController.instance().waitForDevModeStart(Path.of(runnerInfo)); + } + + public void loadModuleContextOnStartup() { + FTLController.instance().loadDeploymentContext(); + } } diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRunnerConnection.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRunnerConnection.java new file mode 100644 index 0000000000..93f72537f9 --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRunnerConnection.java @@ -0,0 +1,264 @@ +package xyz.block.ftl.runtime; + +import java.io.Closeable; +import java.net.URI; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.jboss.logging.Logger; + +import com.google.protobuf.ByteString; + +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.stub.StreamObserver; +import xyz.block.ftl.LeaseFailedException; +import xyz.block.ftl.LeaseHandle; +import xyz.block.ftl.deployment.v1.DeploymentServiceGrpc; +import xyz.block.ftl.deployment.v1.GetDeploymentContextRequest; +import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; +import xyz.block.ftl.lease.v1.AcquireLeaseRequest; +import xyz.block.ftl.lease.v1.AcquireLeaseResponse; +import xyz.block.ftl.lease.v1.LeaseServiceGrpc; +import xyz.block.ftl.publish.v1.PublishEventRequest; +import xyz.block.ftl.publish.v1.PublishEventResponse; +import xyz.block.ftl.publish.v1.PublishServiceGrpc; +import xyz.block.ftl.schema.v1.Ref; +import xyz.block.ftl.v1.CallRequest; +import xyz.block.ftl.v1.CallResponse; +import xyz.block.ftl.v1.VerbServiceGrpc; + +class FTLRunnerConnection implements Closeable { + private static final Logger log = Logger.getLogger(FTLRunnerConnection.class); + final String moduleName; + final String deploymentName; + private final ManagedChannel channel; + private final String endpoint; + + private Throwable currentError; + private volatile GetDeploymentContextResponse moduleContextResponse; + private boolean waiters = false; + + final VerbServiceGrpc.VerbServiceStub verbService; + final DeploymentServiceGrpc.DeploymentServiceStub deploymentService; + final LeaseServiceGrpc.LeaseServiceStub leaseService; + final PublishServiceGrpc.PublishServiceStub publishService; + final StreamObserver moduleObserver = new ModuleObserver(); + + FTLRunnerConnection(final String endpoint, final String deploymentName, final String moduleName) { + var uri = URI.create(endpoint); + this.moduleName = moduleName; + var channelBuilder = ManagedChannelBuilder.forAddress(uri.getHost(), uri.getPort()); + if (uri.getScheme().equals("http")) { + channelBuilder.usePlaintext(); + } + this.channel = channelBuilder.build(); + this.deploymentName = deploymentName; + deploymentService = DeploymentServiceGrpc.newStub(channel); + deploymentService.getDeploymentContext(GetDeploymentContextRequest.newBuilder().setDeployment(deploymentName).build(), + moduleObserver); + verbService = VerbServiceGrpc.newStub(channel); + publishService = PublishServiceGrpc.newStub(channel); + leaseService = LeaseServiceGrpc.newStub(channel); + this.endpoint = endpoint; + } + + public String getEndpoint() { + return endpoint; + } + + byte[] getSecret(String secretName) { + var context = getDeploymentContext(); + if (context.containsSecrets(secretName)) { + return context.getSecretsMap().get(secretName).toByteArray(); + } + throw new RuntimeException("Secret not found: " + secretName); + } + + byte[] getConfig(String secretName) { + var context = getDeploymentContext(); + if (context.containsConfigs(secretName)) { + return context.getConfigsMap().get(secretName).toByteArray(); + } + throw new RuntimeException("Config not found: " + secretName); + } + + byte[] callVerb(String name, String module, byte[] payload) { + CompletableFuture cf = new CompletableFuture<>(); + + verbService.call(CallRequest.newBuilder().setVerb(Ref.newBuilder().setModule(module).setName(name)) + .setBody(ByteString.copyFrom(payload)).build(), new StreamObserver<>() { + + @Override + public void onNext(CallResponse callResponse) { + if (callResponse.hasError()) { + cf.completeExceptionally(new RuntimeException(callResponse.getError().getMessage())); + } else { + cf.complete(callResponse.getBody().toByteArray()); + } + } + + @Override + public void onError(Throwable throwable) { + cf.completeExceptionally(throwable); + } + + @Override + public void onCompleted() { + + } + }); + try { + return cf.get(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + void publishEvent(String topic, String callingVerbName, byte[] event, String key) { + CompletableFuture cf = new CompletableFuture<>(); + publishService.publishEvent(PublishEventRequest.newBuilder() + .setCaller(callingVerbName).setBody(ByteString.copyFrom(event)) + .setTopic(Ref.newBuilder().setModule(moduleName).setName(topic).build()) + .setKey(key).build(), + new StreamObserver() { + @Override + public void onNext(PublishEventResponse publishEventResponse) { + cf.complete(null); + } + + @Override + public void onError(Throwable throwable) { + cf.completeExceptionally(throwable); + } + + @Override + public void onCompleted() { + cf.complete(null); + } + }); + try { + cf.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + public LeaseHandle acquireLease(Duration duration, String... keys) throws LeaseFailedException { + CompletableFuture cf = new CompletableFuture<>(); + var client = leaseService.acquireLease(new StreamObserver() { + @Override + public void onNext(AcquireLeaseResponse value) { + cf.complete(null); + } + + @Override + public void onError(Throwable t) { + cf.completeExceptionally(t); + } + + @Override + public void onCompleted() { + if (!cf.isDone()) { + onError(new RuntimeException("stream closed")); + } + } + }); + List realKeys = new ArrayList<>(); + realKeys.add("module"); + realKeys.add(moduleName); + realKeys.addAll(Arrays.asList(keys)); + client.onNext(AcquireLeaseRequest.newBuilder() + .addAllKey(realKeys) + .setTtl(com.google.protobuf.Duration.newBuilder() + .setSeconds(duration.toSeconds())) + .build()); + try { + cf.get(); + } catch (Exception e) { + throw new LeaseFailedException("lease already held", e); + } + return new LeaseHandle() { + @Override + public void close() { + client.onCompleted(); + } + }; + } + + GetDeploymentContextResponse getDeploymentContext() { + var moduleContext = moduleContextResponse; + if (moduleContext != null) { + return moduleContext; + } + synchronized (moduleObserver) { + for (;;) { + moduleContext = moduleContextResponse; + if (moduleContext != null) { + return moduleContext; + } + if (currentError != null) { + throw new RuntimeException(currentError); + } + waiters = true; + try { + moduleObserver.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + } + + @Override + public void close() { + channel.shutdown(); + } + + private class ModuleObserver implements StreamObserver { + + final AtomicInteger failCount = new AtomicInteger(); + + @Override + public void onNext(GetDeploymentContextResponse moduleContextResponse) { + synchronized (this) { + currentError = null; + FTLRunnerConnection.this.moduleContextResponse = moduleContextResponse; + if (waiters) { + this.notifyAll(); + waiters = false; + } + } + + } + + @Override + public void onError(Throwable throwable) { + log.error("GRPC connection error", throwable); + synchronized (this) { + currentError = throwable; + if (waiters) { + this.notifyAll(); + waiters = false; + } + } + if (failCount.incrementAndGet() < 5) { + deploymentService.getDeploymentContext( + GetDeploymentContextRequest.newBuilder().setDeployment(deploymentName).build(), + moduleObserver); + } + } + + @Override + public void onCompleted() { + onError(new RuntimeException("connection closed")); + } + } + +} diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java index e8769e693b..8fd712567a 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java @@ -16,10 +16,10 @@ public void setupHotDeployment(HotReplacementContext hrc) { context = hrc; } - static void doScan() { + static void doScan(boolean force) { if (context != null) { try { - context.doScan(false); + context.doScan(force); } catch (Exception e) { Logger.getLogger(HotReloadSetup.class).error("Failed to scan for changes", e); } diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/RunnerDetails.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/RunnerDetails.java new file mode 100644 index 0000000000..91864a0f7c --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/RunnerDetails.java @@ -0,0 +1,22 @@ +package xyz.block.ftl.runtime; + +import java.io.Closeable; +import java.util.Optional; + +import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; + +/** + * Details about the proxy endpoints provided by the runner + * + */ +public interface RunnerDetails extends Closeable { + + String getProxyAddress(); + + Optional getDatabase(String database, GetDeploymentContextResponse.DbType type); + + String getDeploymentKey(); + + default void close() { + } +} diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/config/FTLConfigSource.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/config/FTLConfigSource.java index f0359aaf9e..5c71782e49 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/config/FTLConfigSource.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/config/FTLConfigSource.java @@ -13,6 +13,7 @@ import org.eclipse.microprofile.config.spi.ConfigSource; +import xyz.block.ftl.runtime.DatasourceDetails; import xyz.block.ftl.runtime.FTLController; public class FTLConfigSource implements ConfigSource { @@ -107,33 +108,33 @@ public String getValue(String s) { if (s.startsWith("quarkus.datasource")) { switch (s) { case DEFAULT_USER -> { - return Optional.ofNullable(controller.getDatasource("default")).map(FTLController.Datasource::username) + return Optional.ofNullable(controller.getDatasource("default")).map(DatasourceDetails::username) .orElse(null); } case DEFAULT_PASSWORD -> { - return Optional.ofNullable(controller.getDatasource("default")).map(FTLController.Datasource::password) + return Optional.ofNullable(controller.getDatasource("default")).map(DatasourceDetails::password) .orElse(null); } case DEFAULT_URL -> { return Optional.ofNullable(controller.getDatasource("default")) - .map(FTLController.Datasource::connectionString) + .map(DatasourceDetails::connectionString) .orElse(null); } //TODO: just support the default datasource for now } Matcher m = USER_PATTERN.matcher(s); if (m.matches()) { - return Optional.ofNullable(controller.getDatasource(m.group(1))).map(FTLController.Datasource::username) + return Optional.ofNullable(controller.getDatasource(m.group(1))).map(DatasourceDetails::username) .orElse(null); } m = PASSWORD_PATTERN.matcher(s); if (m.matches()) { - return Optional.ofNullable(controller.getDatasource(m.group(1))).map(FTLController.Datasource::password) + return Optional.ofNullable(controller.getDatasource(m.group(1))).map(DatasourceDetails::password) .orElse(null); } m = URL_PATTERN.matcher(s); if (m.matches()) { - return Optional.ofNullable(controller.getDatasource(m.group(1))).map(FTLController.Datasource::connectionString) + return Optional.ofNullable(controller.getDatasource(m.group(1))).map(DatasourceDetails::connectionString) .orElse(null); } } diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index dac0b5e65c..0e0174bc9d 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -283,8 +283,10 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } errorFile := filepath.Join(buildCtx.Config.DeployDir, ErrorFile) schemaFile := filepath.Join(buildCtx.Config.DeployDir, SchemaFile) + runnerInfoFile := filepath.Join(buildCtx.Config.DeployDir, ".runner-info") os.Remove(errorFile) os.Remove(schemaFile) + os.Remove(runnerInfoFile) errorHash := sha256.SHA256{} schemaHash := sha256.SHA256{} @@ -293,6 +295,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb devModeBuild := buildCtx.Config.DevModeBuild debugPort, err := plugin.AllocatePort() debugPort32 := int32(debugPort.Port) + if err == nil { devModeBuild = fmt.Sprintf("%s -Ddebug=%d", devModeBuild, debugPort.Port) } @@ -300,6 +303,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb logger.Infof("Using dev mode build command '%s'", devModeBuild) command := exec.Command(ctx, log.Debug, buildCtx.Config.Dir, "bash", "-c", devModeBuild) command.Env = append(command.Env, fmt.Sprintf("FTL_BIND=%s", bind)) + command.Env = append(command.Env, fmt.Sprintf("FTL_RUNNER_INFO=%s", runnerInfoFile)) command.Stdout = os.Stdout command.Stderr = os.Stderr err = command.RunBuffered(ctx) @@ -388,6 +392,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb IsAutomaticRebuild: !firstAttempt, Module: moduleProto, DevEndpoint: ptr(fmt.Sprintf("http://localhost:%d", address.Port)), + DevRunnerInfoFile: &runnerInfoFile, DebugPort: &debugPort32, Deploy: []string{SchemaFile}, }, diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py index 9ca5f57944..044e4ebe8d 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py @@ -27,7 +27,7 @@ from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(xyz/block/ftl/language/v1/language.proto\x12\x19xyz.block.ftl.language.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"\xc5\x03\n\x0cModuleConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12\x1a\n\x08language\x18\x03 \x01(\tR\x08language\x12\x1d\n\ndeploy_dir\x18\x04 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x05 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x06 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\x1d\n\nbuild_lock\x18\x07 \x01(\tR\tbuildLock\x12\x35\n\x14generated_schema_dir\x18\x08 \x01(\tH\x02R\x12generatedSchemaDir\x88\x01\x01\x12\x14\n\x05watch\x18\t \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\n \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x0b \x01(\tR\x0fsqlMigrationDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_buildB\x17\n\x15_generated_schema_dir\"d\n\rProjectConfig\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x15\n\x06no_git\x18\x03 \x01(\x08R\x05noGit\x12\x16\n\x06hermit\x18\x04 \x01(\x08R\x06hermit\"9\n\x1bGetCreateModuleFlagsRequest\x12\x1a\n\x08language\x18\x01 \x01(\tR\x08language\"\xcf\x02\n\x1cGetCreateModuleFlagsResponse\x12R\n\x05\x66lags\x18\x01 \x03(\x0b\x32<.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse.FlagR\x05\x66lags\x1a\xda\x01\n\x04\x46lag\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04help\x18\x02 \x01(\tR\x04help\x12\x19\n\x05\x65nvar\x18\x03 \x01(\tH\x00R\x05\x65nvar\x88\x01\x01\x12\x19\n\x05short\x18\x04 \x01(\tH\x01R\x05short\x88\x01\x01\x12%\n\x0bplaceholder\x18\x05 \x01(\tH\x02R\x0bplaceholder\x88\x01\x01\x12\x1d\n\x07\x64\x65\x66\x61ult\x18\x06 \x01(\tH\x03R\x07\x64\x65\x66\x61ult\x88\x01\x01\x42\x08\n\x06_envarB\x08\n\x06_shortB\x0e\n\x0c_placeholderB\n\n\x08_default\"\xbb\x01\n\x13\x43reateModuleRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12O\n\x0eproject_config\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.language.v1.ProjectConfigR\rprojectConfig\x12-\n\x05\x66lags\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x05\x66lags\"\x16\n\x14\x43reateModuleResponse\"/\n\x1bModuleConfigDefaultsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\"\xa7\x03\n\x1cModuleConfigDefaultsResponse\x12\x1d\n\ndeploy_dir\x18\x01 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x02 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x03 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\"\n\nbuild_lock\x18\x04 \x01(\tH\x02R\tbuildLock\x88\x01\x01\x12\x35\n\x14generated_schema_dir\x18\x05 \x01(\tH\x03R\x12generatedSchemaDir\x88\x01\x01\x12\x14\n\x05watch\x18\x06 \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x08 \x01(\tR\x0fsqlMigrationDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_buildB\r\n\x0b_build_lockB\x17\n\x15_generated_schema_dir\"f\n\x16GetDependenciesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\"3\n\x17GetDependenciesResponse\x12\x18\n\x07modules\x18\x01 \x03(\tR\x07modules\"\xe6\x01\n\x0c\x42uildContext\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12L\n\rmodule_config\x18\x02 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x37\n\x06schema\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\x12\"\n\x0c\x64\x65pendencies\x18\x04 \x03(\tR\x0c\x64\x65pendencies\x12\x1b\n\tbuild_env\x18\x05 \x03(\tR\x08\x62uildEnv\"j\n\x1a\x42uildContextUpdatedRequest\x12L\n\rbuild_context\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"\x1d\n\x1b\x42uildContextUpdatedResponse\"\xa4\x03\n\x05\x45rror\x12\x10\n\x03msg\x18\x01 \x01(\tR\x03msg\x12\x41\n\x05level\x18\x04 \x01(\x0e\x32+.xyz.block.ftl.language.v1.Error.ErrorLevelR\x05level\x12:\n\x03pos\x18\x05 \x01(\x0b\x32#.xyz.block.ftl.language.v1.PositionH\x00R\x03pos\x88\x01\x01\x12>\n\x04type\x18\x06 \x01(\x0e\x32*.xyz.block.ftl.language.v1.Error.ErrorTypeR\x04type\"l\n\nErrorLevel\x12\x1b\n\x17\x45RROR_LEVEL_UNSPECIFIED\x10\x00\x12\x14\n\x10\x45RROR_LEVEL_INFO\x10\x01\x12\x14\n\x10\x45RROR_LEVEL_WARN\x10\x02\x12\x15\n\x11\x45RROR_LEVEL_ERROR\x10\x03\"T\n\tErrorType\x12\x1a\n\x16\x45RROR_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x45RROR_TYPE_FTL\x10\x01\x12\x17\n\x13\x45RROR_TYPE_COMPILER\x10\x02\x42\x06\n\x04_pos\"|\n\x08Position\x12\x1a\n\x08\x66ilename\x18\x01 \x01(\tR\x08\x66ilename\x12\x12\n\x04line\x18\x02 \x01(\x03R\x04line\x12!\n\x0cstart_column\x18\x03 \x01(\x03R\x0bstartColumn\x12\x1d\n\nend_column\x18\x04 \x01(\x03R\tendColumn\"E\n\tErrorList\x12\x38\n\x06\x65rrors\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.language.v1.ErrorR\x06\x65rrors\"\xd3\x01\n\x0c\x42uildRequest\x12!\n\x0cproject_root\x18\x01 \x01(\tR\x0bprojectRoot\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x33\n\x15rebuild_automatically\x18\x03 \x01(\x08R\x14rebuildAutomatically\x12L\n\rbuild_context\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"3\n\x12\x41utoRebuildStarted\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\"\xfd\x02\n\x0c\x42uildSuccess\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12\x37\n\x06module\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12\x16\n\x06\x64\x65ploy\x18\x04 \x03(\tR\x06\x64\x65ploy\x12!\n\x0c\x64ocker_image\x18\x05 \x01(\tR\x0b\x64ockerImage\x12<\n\x06\x65rrors\x18\x06 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12&\n\x0c\x64\x65v_endpoint\x18\x07 \x01(\tH\x00R\x0b\x64\x65vEndpoint\x88\x01\x01\x12\"\n\ndebug_port\x18\x08 \x01(\x05H\x01R\tdebugPort\x88\x01\x01\x42\x0f\n\r_dev_endpointB\r\n\x0b_debug_port\"\xd6\x01\n\x0c\x42uildFailure\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12<\n\x06\x65rrors\x18\x03 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12\x37\n\x17invalidate_dependencies\x18\x04 \x01(\x08R\x16invalidateDependencies\"\x9b\x02\n\rBuildResponse\x12\x61\n\x14\x61uto_rebuild_started\x18\x02 \x01(\x0b\x32-.xyz.block.ftl.language.v1.AutoRebuildStartedH\x00R\x12\x61utoRebuildStarted\x12N\n\rbuild_success\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildSuccessH\x00R\x0c\x62uildSuccess\x12N\n\rbuild_failure\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildFailureH\x00R\x0c\x62uildFailureB\x07\n\x05\x65vent\"\xa8\x02\n\x14GenerateStubsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x37\n\x06module\x18\x02 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12L\n\rmodule_config\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12^\n\x14native_module_config\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigH\x00R\x12nativeModuleConfig\x88\x01\x01\x42\x17\n\x15_native_module_config\"\x17\n\x15GenerateStubsResponse\"\xa2\x01\n\x19SyncStubReferencesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x18\n\x07modules\x18\x03 \x03(\tR\x07modules\"\x1c\n\x1aSyncStubReferencesResponse2\xb9\x08\n\x0fLanguageService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12\x87\x01\n\x14GetCreateModuleFlags\x12\x36.xyz.block.ftl.language.v1.GetCreateModuleFlagsRequest\x1a\x37.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse\x12o\n\x0c\x43reateModule\x12..xyz.block.ftl.language.v1.CreateModuleRequest\x1a/.xyz.block.ftl.language.v1.CreateModuleResponse\x12\x87\x01\n\x14ModuleConfigDefaults\x12\x36.xyz.block.ftl.language.v1.ModuleConfigDefaultsRequest\x1a\x37.xyz.block.ftl.language.v1.ModuleConfigDefaultsResponse\x12x\n\x0fGetDependencies\x12\x31.xyz.block.ftl.language.v1.GetDependenciesRequest\x1a\x32.xyz.block.ftl.language.v1.GetDependenciesResponse\x12\\\n\x05\x42uild\x12\'.xyz.block.ftl.language.v1.BuildRequest\x1a(.xyz.block.ftl.language.v1.BuildResponse0\x01\x12\x84\x01\n\x13\x42uildContextUpdated\x12\x35.xyz.block.ftl.language.v1.BuildContextUpdatedRequest\x1a\x36.xyz.block.ftl.language.v1.BuildContextUpdatedResponse\x12r\n\rGenerateStubs\x12/.xyz.block.ftl.language.v1.GenerateStubsRequest\x1a\x30.xyz.block.ftl.language.v1.GenerateStubsResponse\x12\x81\x01\n\x12SyncStubReferences\x12\x34.xyz.block.ftl.language.v1.SyncStubReferencesRequest\x1a\x35.xyz.block.ftl.language.v1.SyncStubReferencesResponseBRP\x01ZNgithub.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/language/v1;languagepbb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(xyz/block/ftl/language/v1/language.proto\x12\x19xyz.block.ftl.language.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"\xc5\x03\n\x0cModuleConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12\x1a\n\x08language\x18\x03 \x01(\tR\x08language\x12\x1d\n\ndeploy_dir\x18\x04 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x05 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x06 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\x1d\n\nbuild_lock\x18\x07 \x01(\tR\tbuildLock\x12\x35\n\x14generated_schema_dir\x18\x08 \x01(\tH\x02R\x12generatedSchemaDir\x88\x01\x01\x12\x14\n\x05watch\x18\t \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\n \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x0b \x01(\tR\x0fsqlMigrationDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_buildB\x17\n\x15_generated_schema_dir\"d\n\rProjectConfig\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x15\n\x06no_git\x18\x03 \x01(\x08R\x05noGit\x12\x16\n\x06hermit\x18\x04 \x01(\x08R\x06hermit\"9\n\x1bGetCreateModuleFlagsRequest\x12\x1a\n\x08language\x18\x01 \x01(\tR\x08language\"\xcf\x02\n\x1cGetCreateModuleFlagsResponse\x12R\n\x05\x66lags\x18\x01 \x03(\x0b\x32<.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse.FlagR\x05\x66lags\x1a\xda\x01\n\x04\x46lag\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04help\x18\x02 \x01(\tR\x04help\x12\x19\n\x05\x65nvar\x18\x03 \x01(\tH\x00R\x05\x65nvar\x88\x01\x01\x12\x19\n\x05short\x18\x04 \x01(\tH\x01R\x05short\x88\x01\x01\x12%\n\x0bplaceholder\x18\x05 \x01(\tH\x02R\x0bplaceholder\x88\x01\x01\x12\x1d\n\x07\x64\x65\x66\x61ult\x18\x06 \x01(\tH\x03R\x07\x64\x65\x66\x61ult\x88\x01\x01\x42\x08\n\x06_envarB\x08\n\x06_shortB\x0e\n\x0c_placeholderB\n\n\x08_default\"\xbb\x01\n\x13\x43reateModuleRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12O\n\x0eproject_config\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.language.v1.ProjectConfigR\rprojectConfig\x12-\n\x05\x66lags\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x05\x66lags\"\x16\n\x14\x43reateModuleResponse\"/\n\x1bModuleConfigDefaultsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\"\xa7\x03\n\x1cModuleConfigDefaultsResponse\x12\x1d\n\ndeploy_dir\x18\x01 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x02 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x03 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\"\n\nbuild_lock\x18\x04 \x01(\tH\x02R\tbuildLock\x88\x01\x01\x12\x35\n\x14generated_schema_dir\x18\x05 \x01(\tH\x03R\x12generatedSchemaDir\x88\x01\x01\x12\x14\n\x05watch\x18\x06 \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x08 \x01(\tR\x0fsqlMigrationDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_buildB\r\n\x0b_build_lockB\x17\n\x15_generated_schema_dir\"f\n\x16GetDependenciesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\"3\n\x17GetDependenciesResponse\x12\x18\n\x07modules\x18\x01 \x03(\tR\x07modules\"\xe6\x01\n\x0c\x42uildContext\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12L\n\rmodule_config\x18\x02 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x37\n\x06schema\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\x12\"\n\x0c\x64\x65pendencies\x18\x04 \x03(\tR\x0c\x64\x65pendencies\x12\x1b\n\tbuild_env\x18\x05 \x03(\tR\x08\x62uildEnv\"j\n\x1a\x42uildContextUpdatedRequest\x12L\n\rbuild_context\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"\x1d\n\x1b\x42uildContextUpdatedResponse\"\xa4\x03\n\x05\x45rror\x12\x10\n\x03msg\x18\x01 \x01(\tR\x03msg\x12\x41\n\x05level\x18\x04 \x01(\x0e\x32+.xyz.block.ftl.language.v1.Error.ErrorLevelR\x05level\x12:\n\x03pos\x18\x05 \x01(\x0b\x32#.xyz.block.ftl.language.v1.PositionH\x00R\x03pos\x88\x01\x01\x12>\n\x04type\x18\x06 \x01(\x0e\x32*.xyz.block.ftl.language.v1.Error.ErrorTypeR\x04type\"l\n\nErrorLevel\x12\x1b\n\x17\x45RROR_LEVEL_UNSPECIFIED\x10\x00\x12\x14\n\x10\x45RROR_LEVEL_INFO\x10\x01\x12\x14\n\x10\x45RROR_LEVEL_WARN\x10\x02\x12\x15\n\x11\x45RROR_LEVEL_ERROR\x10\x03\"T\n\tErrorType\x12\x1a\n\x16\x45RROR_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x45RROR_TYPE_FTL\x10\x01\x12\x17\n\x13\x45RROR_TYPE_COMPILER\x10\x02\x42\x06\n\x04_pos\"|\n\x08Position\x12\x1a\n\x08\x66ilename\x18\x01 \x01(\tR\x08\x66ilename\x12\x12\n\x04line\x18\x02 \x01(\x03R\x04line\x12!\n\x0cstart_column\x18\x03 \x01(\x03R\x0bstartColumn\x12\x1d\n\nend_column\x18\x04 \x01(\x03R\tendColumn\"E\n\tErrorList\x12\x38\n\x06\x65rrors\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.language.v1.ErrorR\x06\x65rrors\"\xd3\x01\n\x0c\x42uildRequest\x12!\n\x0cproject_root\x18\x01 \x01(\tR\x0bprojectRoot\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x33\n\x15rebuild_automatically\x18\x03 \x01(\x08R\x14rebuildAutomatically\x12L\n\rbuild_context\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"3\n\x12\x41utoRebuildStarted\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\"\xcc\x03\n\x0c\x42uildSuccess\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12\x37\n\x06module\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12\x16\n\x06\x64\x65ploy\x18\x04 \x03(\tR\x06\x64\x65ploy\x12!\n\x0c\x64ocker_image\x18\x05 \x01(\tR\x0b\x64ockerImage\x12<\n\x06\x65rrors\x18\x06 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12&\n\x0c\x64\x65v_endpoint\x18\x07 \x01(\tH\x00R\x0b\x64\x65vEndpoint\x88\x01\x01\x12\"\n\ndebug_port\x18\x08 \x01(\x05H\x01R\tdebugPort\x88\x01\x01\x12\x34\n\x14\x64\x65v_runner_info_file\x18\t \x01(\tH\x02R\x11\x64\x65vRunnerInfoFile\x88\x01\x01\x42\x0f\n\r_dev_endpointB\r\n\x0b_debug_portB\x17\n\x15_dev_runner_info_file\"\xd6\x01\n\x0c\x42uildFailure\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12<\n\x06\x65rrors\x18\x03 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12\x37\n\x17invalidate_dependencies\x18\x04 \x01(\x08R\x16invalidateDependencies\"\x9b\x02\n\rBuildResponse\x12\x61\n\x14\x61uto_rebuild_started\x18\x02 \x01(\x0b\x32-.xyz.block.ftl.language.v1.AutoRebuildStartedH\x00R\x12\x61utoRebuildStarted\x12N\n\rbuild_success\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildSuccessH\x00R\x0c\x62uildSuccess\x12N\n\rbuild_failure\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildFailureH\x00R\x0c\x62uildFailureB\x07\n\x05\x65vent\"\xa8\x02\n\x14GenerateStubsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x37\n\x06module\x18\x02 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12L\n\rmodule_config\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12^\n\x14native_module_config\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigH\x00R\x12nativeModuleConfig\x88\x01\x01\x42\x17\n\x15_native_module_config\"\x17\n\x15GenerateStubsResponse\"\xa2\x01\n\x19SyncStubReferencesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x18\n\x07modules\x18\x03 \x03(\tR\x07modules\"\x1c\n\x1aSyncStubReferencesResponse2\xb9\x08\n\x0fLanguageService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12\x87\x01\n\x14GetCreateModuleFlags\x12\x36.xyz.block.ftl.language.v1.GetCreateModuleFlagsRequest\x1a\x37.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse\x12o\n\x0c\x43reateModule\x12..xyz.block.ftl.language.v1.CreateModuleRequest\x1a/.xyz.block.ftl.language.v1.CreateModuleResponse\x12\x87\x01\n\x14ModuleConfigDefaults\x12\x36.xyz.block.ftl.language.v1.ModuleConfigDefaultsRequest\x1a\x37.xyz.block.ftl.language.v1.ModuleConfigDefaultsResponse\x12x\n\x0fGetDependencies\x12\x31.xyz.block.ftl.language.v1.GetDependenciesRequest\x1a\x32.xyz.block.ftl.language.v1.GetDependenciesResponse\x12\\\n\x05\x42uild\x12\'.xyz.block.ftl.language.v1.BuildRequest\x1a(.xyz.block.ftl.language.v1.BuildResponse0\x01\x12\x84\x01\n\x13\x42uildContextUpdated\x12\x35.xyz.block.ftl.language.v1.BuildContextUpdatedRequest\x1a\x36.xyz.block.ftl.language.v1.BuildContextUpdatedResponse\x12r\n\rGenerateStubs\x12/.xyz.block.ftl.language.v1.GenerateStubsRequest\x1a\x30.xyz.block.ftl.language.v1.GenerateStubsResponse\x12\x81\x01\n\x12SyncStubReferences\x12\x34.xyz.block.ftl.language.v1.SyncStubReferencesRequest\x1a\x35.xyz.block.ftl.language.v1.SyncStubReferencesResponseBRP\x01ZNgithub.com/TBD54566975/ftl/backend/protos/xyz/block/ftl/language/v1;languagepbb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -80,19 +80,19 @@ _globals['_AUTOREBUILDSTARTED']._serialized_start=3174 _globals['_AUTOREBUILDSTARTED']._serialized_end=3225 _globals['_BUILDSUCCESS']._serialized_start=3228 - _globals['_BUILDSUCCESS']._serialized_end=3609 - _globals['_BUILDFAILURE']._serialized_start=3612 - _globals['_BUILDFAILURE']._serialized_end=3826 - _globals['_BUILDRESPONSE']._serialized_start=3829 - _globals['_BUILDRESPONSE']._serialized_end=4112 - _globals['_GENERATESTUBSREQUEST']._serialized_start=4115 - _globals['_GENERATESTUBSREQUEST']._serialized_end=4411 - _globals['_GENERATESTUBSRESPONSE']._serialized_start=4413 - _globals['_GENERATESTUBSRESPONSE']._serialized_end=4436 - _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_start=4439 - _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_end=4601 - _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_start=4603 - _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_end=4631 - _globals['_LANGUAGESERVICE']._serialized_start=4634 - _globals['_LANGUAGESERVICE']._serialized_end=5715 + _globals['_BUILDSUCCESS']._serialized_end=3688 + _globals['_BUILDFAILURE']._serialized_start=3691 + _globals['_BUILDFAILURE']._serialized_end=3905 + _globals['_BUILDRESPONSE']._serialized_start=3908 + _globals['_BUILDRESPONSE']._serialized_end=4191 + _globals['_GENERATESTUBSREQUEST']._serialized_start=4194 + _globals['_GENERATESTUBSREQUEST']._serialized_end=4490 + _globals['_GENERATESTUBSRESPONSE']._serialized_start=4492 + _globals['_GENERATESTUBSRESPONSE']._serialized_end=4515 + _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_start=4518 + _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_end=4680 + _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_start=4682 + _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_end=4710 + _globals['_LANGUAGESERVICE']._serialized_start=4713 + _globals['_LANGUAGESERVICE']._serialized_end=5794 # @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi index b813c38ba9..aaeed085c5 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi @@ -219,7 +219,7 @@ class AutoRebuildStarted(_message.Message): def __init__(self, context_id: _Optional[str] = ...) -> None: ... class BuildSuccess(_message.Message): - __slots__ = ("context_id", "is_automatic_rebuild", "module", "deploy", "docker_image", "errors", "dev_endpoint", "debug_port") + __slots__ = ("context_id", "is_automatic_rebuild", "module", "deploy", "docker_image", "errors", "dev_endpoint", "debug_port", "dev_runner_info_file") CONTEXT_ID_FIELD_NUMBER: _ClassVar[int] IS_AUTOMATIC_REBUILD_FIELD_NUMBER: _ClassVar[int] MODULE_FIELD_NUMBER: _ClassVar[int] @@ -228,6 +228,7 @@ class BuildSuccess(_message.Message): ERRORS_FIELD_NUMBER: _ClassVar[int] DEV_ENDPOINT_FIELD_NUMBER: _ClassVar[int] DEBUG_PORT_FIELD_NUMBER: _ClassVar[int] + DEV_RUNNER_INFO_FILE_FIELD_NUMBER: _ClassVar[int] context_id: str is_automatic_rebuild: bool module: _schema_pb2.Module @@ -236,7 +237,8 @@ class BuildSuccess(_message.Message): errors: ErrorList dev_endpoint: str debug_port: int - def __init__(self, context_id: _Optional[str] = ..., is_automatic_rebuild: bool = ..., module: _Optional[_Union[_schema_pb2.Module, _Mapping]] = ..., deploy: _Optional[_Iterable[str]] = ..., docker_image: _Optional[str] = ..., errors: _Optional[_Union[ErrorList, _Mapping]] = ..., dev_endpoint: _Optional[str] = ..., debug_port: _Optional[int] = ...) -> None: ... + dev_runner_info_file: str + def __init__(self, context_id: _Optional[str] = ..., is_automatic_rebuild: bool = ..., module: _Optional[_Union[_schema_pb2.Module, _Mapping]] = ..., deploy: _Optional[_Iterable[str]] = ..., docker_image: _Optional[str] = ..., errors: _Optional[_Union[ErrorList, _Mapping]] = ..., dev_endpoint: _Optional[str] = ..., debug_port: _Optional[int] = ..., dev_runner_info_file: _Optional[str] = ...) -> None: ... class BuildFailure(_message.Message): __slots__ = ("context_id", "is_automatic_rebuild", "errors", "invalidate_dependencies")