From 9acc250dc9b6ce92f1cee13b404a408e17804564 Mon Sep 17 00:00:00 2001 From: Kim Tao Date: Mon, 28 Jan 2019 17:50:03 -0500 Subject: [PATCH] SERVER-39094,SERVER-38136: update Jasper process to reflect RPC changes; fix Windows failures due to sigterm. --- buildscripts/resmoke.py | 4 +- buildscripts/resmokelib/core/jasper.proto | 27 +++++++++--- .../resmokelib/core/jasper_process.py | 44 +++++++++++-------- 3 files changed, 51 insertions(+), 24 deletions(-) diff --git a/buildscripts/resmoke.py b/buildscripts/resmoke.py index 2094610d65d..9ad57d86ff1 100755 --- a/buildscripts/resmoke.py +++ b/buildscripts/resmoke.py @@ -318,7 +318,9 @@ class Resmoke(object): # pylint: disable=too-many-instance-attributes jasper_process.Process.jasper_pb2_grpc = jasper_pb2_grpc curator_path = "build/curator" - git_hash = "1b8c7344aa1daed0846e32204dffb21cfdda208c" + if sys.platform == "win32": + curator_path += ".exe" + git_hash = "d846f0c875716e9377044ab2a50542724369662a" curator_exists = os.path.isfile(curator_path) curator_same_version = False if curator_exists: diff --git a/buildscripts/resmokelib/core/jasper.proto b/buildscripts/resmokelib/core/jasper.proto index a85472a6632..3b626ee8e51 100644 --- a/buildscripts/resmokelib/core/jasper.proto +++ b/buildscripts/resmokelib/core/jasper.proto @@ -139,7 +139,6 @@ message ProcessTags { message JasperProcessID { string value = 1; - } message OperationOutcome { @@ -181,23 +180,39 @@ message ArchiveOptions { } message DownloadInfo { - string url = 1; - string path = 2; - ArchiveOptions archive_opts = 3; + string url = 1; + string path = 2; + ArchiveOptions archive_opts = 3; } message BuildloggerURLs { repeated string urls = 1; } +enum SignalTriggerID { + NONE = 0; + CLEANTERMINATION = 1; +} + +message SignalTriggerParams { + JasperProcessID processID = 1; + SignalTriggerID signalTriggerID = 2; +} + +message EventName { + string value = 1; +} + service JasperProcessManager { - rpc Status(google.protobuf.Empty) returns (StatusResponse); + rpc Status(google.protobuf.Empty) returns (StatusResponse); rpc Create(CreateOptions) returns (ProcessInfo); rpc List(Filter) returns (stream ProcessInfo); rpc Group(TagName) returns (stream ProcessInfo); rpc Get(JasperProcessID) returns (ProcessInfo); rpc Wait(JasperProcessID) returns (OperationOutcome); + rpc Respawn(JasperProcessID) returns (ProcessInfo); rpc Signal(SignalProcess) returns (OperationOutcome); + rpc Clear(google.protobuf.Empty) returns (OperationOutcome); rpc Close(google.protobuf.Empty) returns (OperationOutcome); rpc TagProcess(ProcessTags) returns (OperationOutcome); rpc ResetTags(JasperProcessID) returns (OperationOutcome); @@ -206,4 +221,6 @@ service JasperProcessManager { rpc DownloadMongoDB(MongoDBDownloadOptions) returns (OperationOutcome); rpc ConfigureCache(CacheOptions) returns (OperationOutcome); rpc GetBuildloggerURLs(JasperProcessID) returns (BuildloggerURLs); + rpc RegisterSignalTriggerID(SignalTriggerParams) returns (OperationOutcome); + rpc SignalEvent(EventName) returns (OperationOutcome); } diff --git a/buildscripts/resmokelib/core/jasper_process.py b/buildscripts/resmokelib/core/jasper_process.py index e00762c663b..60c349743ff 100644 --- a/buildscripts/resmokelib/core/jasper_process.py +++ b/buildscripts/resmokelib/core/jasper_process.py @@ -5,6 +5,8 @@ Serves as an alternative to process.py. from __future__ import absolute_import +import sys + try: import grpc except ImportError: @@ -52,19 +54,28 @@ class Process(_process.Process): def stop(self, kill=False): """Terminate the process.""" signal = self.jasper_pb2.Signals.Value("TERMINATE") - if kill: + if sys.platform == "win32": + if not kill: + event_name = self.jasper_pb2.EventName(value="Global\\Mongo_" + str(self.pid)) + signal_event = self._stub.SignalEvent(event_name) + if signal_event.success: + wait = self._stub.Wait(self._id, timeout=60) + if wait.success: + return + clean_termination_params = self.jasper_pb2.SignalTriggerParams( + processID=self._id, + signalTriggerID=self.jasper_pb2.SignalTriggerID.Value("CLEANTERMINATION")) + self._stub.RegisterSignalTriggerID(clean_termination_params) + elif kill: signal = self.jasper_pb2.Signals.Value("KILL") signal_process = self.jasper_pb2.SignalProcess(ProcessID=self._id, signal=signal) - try: - val = self._stub.Signal(signal_process) - if not val.success: - raise OSError("Unable to stop process %d." % self.pid) - except grpc.RpcError as err: - err.details = err.details() - if "cannot signal a process that has terminated" not in err.details \ - and "os: process already finished" not in err.details: - raise + val = self._stub.Signal(signal_process) + if not val.success \ + and "cannot signal a process that has terminated" not in val.text \ + and "os: process already finished" not in val.text: + raise OSError("Failed to signal Jasper process with pid {}: {}".format( + self.pid, val.text)) def poll(self): """Poll.""" @@ -77,12 +88,9 @@ class Process(_process.Process): def wait(self): """Wait until process has terminated and all output has been consumed by the logger pipes.""" if self._return_code is None: - try: - wait = self._stub.Wait(self._id) - self._return_code = wait.exit_code - except grpc.RpcError as err: - if "problem encountered while waiting: operation failed" not in err.details(): - raise - wait = self._stub.Get(self._id) - self._return_code = wait.exit_code + wait = self._stub.Wait(self._id) + if not wait.success: + raise OSError("Failed to wait on process with pid {}: {}.".format( + self.pid, wait.text)) + self._return_code = wait.exit_code return self._return_code