Skip to content

Commit 8d450ee

Browse files
author
James Pogran
authored
Merge pull request #134 from glennsarti/gh-118-fail-if-no-puppet
(GH-118) Fail gracefully when critical gems cannot load
2 parents 59b8306 + c8a8639 commit 8d450ee

File tree

4 files changed

+108
-5
lines changed

4 files changed

+108
-5
lines changed

lib/puppet-languageserver/json_rpc_handler.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ def reply_result(result)
278278
end
279279

280280
def reply_internal_error(message = nil)
281-
return nil if @json_rpc_handler.error?
281+
return nil if @json_rpc_handler.connection_error?
282282
@json_rpc_handler.reply_error(@id, CODE_INTERNAL_ERROR, message || MSG_INTERNAL_ERROR)
283283
end
284284

lib/puppet-languageserver/message_router.rb

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,68 @@ def receive_notification(method, params)
247247
raise
248248
end
249249
end
250+
251+
class DisabledMessageRouter
252+
attr_accessor :json_rpc_handler
253+
254+
def initialize(_options)
255+
end
256+
257+
def receive_request(request)
258+
case request.rpc_method
259+
when 'initialize'
260+
PuppetLanguageServer.log_message(:debug, 'Received initialize method')
261+
# If the Language Server is not active then we can not respond to any capability. We also
262+
# send a warning to the user telling them this
263+
request.reply_result('capabilities' => PuppetLanguageServer::ServerCapabilites.no_capabilities)
264+
# Add a minor delay before sending the notification to give the client some processing time
265+
sleep(0.5)
266+
@json_rpc_handler.send_show_message_notification(
267+
LSP::MessageType::WARNING,
268+
'An error occured while the Language Server was starting. The server has been disabled.'
269+
)
270+
271+
when 'shutdown'
272+
PuppetLanguageServer.log_message(:debug, 'Received shutdown method')
273+
request.reply_result(nil)
274+
275+
when 'puppet/getVersion'
276+
# Clients may use the getVersion request to figure out when the server has "finished" loading. In this
277+
# case just fake the response that we are fully loaded with unknown gem versions
278+
request.reply_result(LSP::PuppetVersion.new(
279+
'puppetVersion' => 'Unknown',
280+
'facterVersion' => 'Unknown',
281+
'factsLoaded' => true,
282+
'functionsLoaded' => true,
283+
'typesLoaded' => true,
284+
'classesLoaded' => true
285+
))
286+
287+
else
288+
# For any request return an internal error.
289+
request.reply_internal_error('Puppet Language Server is not active')
290+
PuppetLanguageServer.log_message(:error, "Unknown RPC method #{request.rpc_method}")
291+
end
292+
rescue StandardError => e
293+
PuppetLanguageServer::CrashDump.write_crash_file(e, nil, 'request' => request.rpc_method, 'params' => request.params)
294+
raise
295+
end
296+
297+
def receive_notification(method, params)
298+
case method
299+
when 'initialized'
300+
PuppetLanguageServer.log_message(:info, 'Client has received initialization')
301+
302+
when 'exit'
303+
PuppetLanguageServer.log_message(:info, 'Received exit notification. Closing connection to client...')
304+
@json_rpc_handler.close_connection
305+
306+
else
307+
PuppetLanguageServer.log_message(:error, "Unknown RPC notification #{method}")
308+
end
309+
rescue StandardError => e
310+
PuppetLanguageServer::CrashDump.write_crash_file(e, nil, 'notification' => method, 'params' => params)
311+
raise
312+
end
313+
end
250314
end

lib/puppet-languageserver/server_capabilities.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,11 @@ def self.capabilities
1717
'workspaceSymbolProvider' => true
1818
}
1919
end
20+
21+
def self.no_capabilities
22+
# Any empty hash denotes no capabilities at all
23+
{
24+
}
25+
end
2026
end
2127
end

lib/puppet_languageserver.rb

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,18 @@ def self.version
1616
PuppetEditorServices.version
1717
end
1818

19+
# Whether the language server is actually in a state that can be used.
20+
# Typically this is false when a catastrophic error occurs during startup e.g. Puppet is missing.
21+
#
22+
# @return [Bool] Whether the language server is actually in a state that can be used
23+
def self.active?
24+
@server_is_active
25+
end
26+
1927
def self.require_gems(options)
2028
original_verbose = $VERBOSE
2129
$VERBOSE = nil
30+
@server_is_active = false
2231

2332
# Use specific Puppet Gem version if possible
2433
unless options[:puppet_version].nil?
@@ -32,16 +41,33 @@ def self.require_gems(options)
3241
end
3342
end
3443

35-
require 'lsp/lsp'
36-
require 'puppet'
37-
44+
# These libraries do not require the puppet gem and required for the
45+
# server to respond to clients.
3846
%w[
3947
json_rpc_handler
4048
document_store
4149
crash_dump
4250
message_router
43-
validation_queue
4451
server_capabilities
52+
].each do |lib|
53+
begin
54+
require "puppet-languageserver/#{lib}"
55+
rescue LoadError
56+
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver', lib))
57+
end
58+
end
59+
60+
begin
61+
require 'lsp/lsp'
62+
require 'puppet'
63+
rescue LoadError => e
64+
log_message(:error, "Error while loading a critical gem: #{e} #{e.backtrace}")
65+
return
66+
end
67+
68+
# These libraries require the puppet and LSP gems.
69+
%w[
70+
validation_queue
4571
sidecar_protocol
4672
sidecar_queue
4773
puppet_parser_helper
@@ -57,6 +83,7 @@ def self.require_gems(options)
5783
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver', lib))
5884
end
5985
end
86+
@server_is_active = true
6087
ensure
6188
$VERBOSE = original_verbose
6289
end
@@ -165,6 +192,7 @@ def self.init_puppet(options)
165192
log_message(:info, "Language Server is v#{PuppetEditorServices.version}")
166193
log_message(:debug, 'Loading gems...')
167194
require_gems(options)
195+
return unless active?
168196
log_message(:info, "Using Puppet v#{Puppet.version}")
169197

170198
log_message(:debug, "Detected additional puppet settings #{options[:puppet_settings]}")
@@ -220,6 +248,11 @@ def self.rpc_server(options)
220248
log_message(:info, 'Starting RPC Server...')
221249
options[:servicename] = 'LANGUAGE SERVER'
222250

251+
unless active?
252+
options[:message_router] = @message_router = PuppetLanguageServer::DisabledMessageRouter.new(options)
253+
log_message(:info, 'Configured the Language Server to use the Disabled Message Router')
254+
end
255+
223256
if options[:stdio]
224257
log_message(:debug, 'Using STDIO')
225258
server = PuppetEditorServices::SimpleSTDIOServer.new

0 commit comments

Comments
 (0)