Skip to content

Commit 131c2ea

Browse files
authored
Merge pull request #215 from glennsarti/gh-213-use-fact-sidecar
(GH-213) Use Facts from the Sidecar
2 parents 0215658 + a8c5e1f commit 131c2ea

File tree

11 files changed

+136
-93
lines changed

11 files changed

+136
-93
lines changed

lib/puppet-languageserver-sidecar/facter_helper.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@ def self.retrieve_facts(_cache, _options = {})
1818
require 'puppet/indirector/facts/facter'
1919

2020
PuppetLanguageServerSidecar.log_message(:debug, '[FacterHelper::retrieve_facts] Starting')
21-
facts = PuppetLanguageServer::Sidecar::Protocol::Facts.new
21+
facts = PuppetLanguageServer::Sidecar::Protocol::FactList.new
2222
begin
2323
req = Puppet::Indirector::Request.new(:facts, :find, 'language_server', nil, environment: current_environment)
2424
result = Puppet::Node::Facts::Facter.new.find(req)
25-
facts.from_h!(result.values)
25+
result.values.each do |key, value|
26+
# TODO: This isn't strictly correct e.g. fully qualified facts will look a bit odd.
27+
# Consider a fact called foo.bar.baz = 'Hello'. Even though the fact name is `foo.bar.baz`
28+
# it will appear in the facts object as `facts['foo'] = { 'bar' => { 'baz' => 'Hello' }}`
29+
facts << PuppetLanguageServer::Sidecar::Protocol::Fact.new.from_h!('key' => key, 'value' => value)
30+
end
2631
rescue StandardError => e
2732
PuppetLanguageServerSidecar.log_message(:error, "[FacterHelper::_load_facts] Error loading facts #{e.message} #{e.backtrace}")
2833
rescue LoadError => e
2934
PuppetLanguageServerSidecar.log_message(:error, "[FacterHelper::_load_facts] Error loading facts (LoadError) #{e.message} #{e.backtrace}")
3035
end
3136

32-
PuppetLanguageServerSidecar.log_message(:debug, "[FacterHelper::retrieve_facts] Finished loading #{facts.keys.count} facts")
37+
PuppetLanguageServerSidecar.log_message(:debug, "[FacterHelper::retrieve_facts] Finished loading #{facts.count} facts")
3338
facts
3439
end
3540
end

lib/puppet-languageserver/facter_helper.rb

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,49 @@
22

33
module PuppetLanguageServer
44
module FacterHelper
5-
@ops_lock = Mutex.new
65
@facts_loaded = nil
76

8-
def self.reset
9-
@ops_lock.synchronize do
10-
_reset
11-
end
7+
def self.cache
8+
PuppetLanguageServer::PuppetHelper.cache
129
end
1310

14-
def self.load_facts_async
15-
Thread.new do
16-
load_facts
17-
end
11+
def self.sidecar_queue
12+
PuppetLanguageServer::PuppetHelper.sidecar_queue
1813
end
1914

15+
# Facts
2016
def self.facts_loaded?
2117
@facts_loaded.nil? ? false : @facts_loaded
2218
end
2319

24-
def self.load_facts
25-
@ops_lock.synchronize do
26-
_load_facts
27-
end
28-
end
29-
30-
def self.facts
31-
return {} if @facts_loaded == false
32-
@ops_lock.synchronize do
33-
_load_facts if @fact_hash.nil?
34-
@fact_hash.clone
35-
end
36-
end
37-
38-
# DO NOT ops_lock on any of these methods
39-
# deadlocks will ensue!
40-
def self._reset
41-
@facts_loaded = nil
42-
Facter.reset
43-
@fact_hash = nil
44-
end
45-
private_class_method :_reset
46-
47-
def self._load_facts
48-
_reset
49-
@fact_hash = {}
50-
begin
51-
Facter.loadfacts
52-
@fact_hash = Facter.to_hash
53-
rescue StandardError => e
54-
PuppetLanguageServer.log_message(:error, "[FacterHelper::_load_facts] Error loading facts #{e.message} #{e.backtrace}")
55-
rescue LoadError => e
56-
PuppetLanguageServer.log_message(:error, "[FacterHelper::_load_facts] Error loading facts (LoadError) #{e.message} #{e.backtrace}")
57-
end
58-
PuppetLanguageServer.log_message(:debug, "[FacterHelper::_load_facts] Finished loading #{@fact_hash.keys.count} facts")
20+
def self.assert_facts_loaded
5921
@facts_loaded = true
6022
end
61-
private_class_method :_load_facts
23+
24+
def self.load_facts
25+
@facts_loaded = false
26+
sidecar_queue.execute_sync('facts', [])
27+
end
28+
29+
def self.load_facts_async
30+
@facts_loaded = false
31+
sidecar_queue.enqueue('facts', [])
32+
end
33+
34+
def self.fact(name)
35+
return nil if @facts_loaded == false
36+
cache.object_by_name(:fact, name)
37+
end
38+
39+
def self.fact_value(name)
40+
return nil if @facts_loaded == false
41+
object = cache.object_by_name(:fact, name)
42+
object.nil? ? nil : object.value
43+
end
44+
45+
def self.fact_names
46+
return [] if @facts_loaded == false
47+
cache.object_names_by_section(:fact).map(&:to_s)
48+
end
6249
end
6350
end

lib/puppet-languageserver/manifest/completion_provider.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def self.keywords(keywords = [], &block)
144144
end
145145

146146
def self.all_facts(&block)
147-
PuppetLanguageServer::FacterHelper.facts.each_key do |name|
147+
PuppetLanguageServer::FacterHelper.fact_names.each do |name|
148148
item = LSP::CompletionItem.new(
149149
'label' => name.to_s,
150150
'insertText' => "'#{name}'",
@@ -206,7 +206,7 @@ def self.resolve(completion_item)
206206
data = result.data
207207
case data['type']
208208
when 'variable_expr_fact'
209-
value = PuppetLanguageServer::FacterHelper.facts[data['expr']]
209+
value = PuppetLanguageServer::FacterHelper.fact_value(data['expr'])
210210
# TODO: More things?
211211
result.documentation = value.to_s
212212

lib/puppet-languageserver/manifest/hover_provider.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,9 @@ def self.get_hover_content_for_access_expression(path, expr)
107107

108108
# Content generation functions
109109
def self.get_fact_content(factname)
110-
return nil unless PuppetLanguageServer::FacterHelper.facts.key?(factname)
111-
value = PuppetLanguageServer::FacterHelper.facts[factname]
110+
fact = PuppetLanguageServer::FacterHelper.fact(factname)
111+
return nil if fact.nil?
112+
value = fact.value
112113
content = "**#{factname}** Fact\n\n"
113114

114115
if value.is_a?(Hash)

lib/puppet-languageserver/puppet_helper.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,6 @@ def self.purge_workspace
305305
def self.sidecar_queue
306306
@sidecar_queue_obj ||= PuppetLanguageServer::SidecarQueue.new(@helper_options)
307307
end
308-
private_class_method :sidecar_queue
309308

310309
def self.with_temporary_file(content)
311310
tempfile = Tempfile.new('langserver-sidecar')

lib/puppet-languageserver/puppet_helper/cache.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module PuppetLanguageServer
44
module PuppetHelper
55
class Cache
6-
SECTIONS = %i[class type function datatype].freeze
6+
SECTIONS = %i[class type function datatype fact].freeze
77
ORIGINS = %i[default workspace bolt].freeze
88

99
def initialize(_options = {})

lib/puppet-languageserver/sidecar_protocol.rb

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -521,26 +521,28 @@ def list_for_object_class(klass)
521521
end
522522
end
523523

524-
class Facts < Hash
525-
include Base
524+
class Fact < BasePuppetObject
525+
attr_accessor :value
526526

527-
def from_h!(value)
528-
value.keys.each { |key| self[key] = value[key] }
529-
self
527+
def to_h
528+
super.to_h.merge(
529+
'value' => value
530+
)
530531
end
531532

532-
def to_json(*options)
533-
::JSON.generate(to_h, options)
534-
end
533+
def from_h!(value)
534+
super
535535

536-
def from_json!(json_string)
537-
obj = ::JSON.parse(json_string)
538-
obj.each do |key, value|
539-
self[key] = value
540-
end
536+
self.value = value['value']
541537
self
542538
end
543539
end
540+
541+
class FactList < BasePuppetObjectList
542+
def child_type
543+
Fact
544+
end
545+
end
544546
end
545547
end
546548
end

lib/puppet-languageserver/sidecar_queue.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ def execute_sync(action, additional_args, handle_errors = false)
111111

112112
PuppetLanguageServer::PuppetHelper.assert_default_types_loaded
113113

114+
when 'facts'
115+
list = PuppetLanguageServer::Sidecar::Protocol::FactList.new.from_json!(result)
116+
@cache.import_sidecar_list!(list, :fact, :default)
117+
PuppetLanguageServer.log_message(:debug, "SidecarQueue Thread: facts returned #{list.count} items")
118+
119+
PuppetLanguageServer::FacterHelper.assert_facts_loaded
120+
114121
when 'node_graph'
115122
return PuppetLanguageServer::Sidecar::Protocol::NodeGraph.new.from_json!(result)
116123

spec/languageserver-sidecar/integration/puppet-languageserver-sidecar/facter_helper_spec.rb

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,33 @@ def run_sidecar(cmd_options)
2020
return stdout.bytes.pack('U*')
2121
end
2222

23-
let(:default_fact_names) { ['hostname', 'fixture_agent_custom_fact'] }
24-
let(:module_fact_names) { ['fixture_module_custom_fact', 'fixture_module_external_fact'] }
25-
let(:environment_fact_names) { ['fixture_environment_custom_fact', 'fixture_environment_external_fact'] }
23+
RSpec::Matchers.define :contain_child_with_key do |key|
24+
match do |actual|
25+
!(actual.index { |item| item.key == key }).nil?
26+
end
27+
28+
failure_message do |actual|
29+
"expected that #{actual.class.to_s} would contain a child with key #{key}"
30+
end
31+
end
32+
33+
let(:default_fact_names) { %i[hostname fixture_agent_custom_fact] }
34+
let(:module_fact_names) { %i[fixture_module_custom_fact fixture_module_external_fact] }
35+
let(:environment_fact_names) { %i[fixture_environment_custom_fact fixture_environment_external_fact] }
2636

2737
describe 'when running facts action' do
2838
let (:cmd_options) { ['--action', 'facts'] }
2939

3040
it 'should return a deserializable facts object with all default facts' do
3141
result = run_sidecar(cmd_options)
32-
deserial = PuppetLanguageServer::Sidecar::Protocol::Facts.new
42+
deserial = PuppetLanguageServer::Sidecar::Protocol::FactList.new
3343
expect { deserial.from_json!(result) }.to_not raise_error
34-
3544
default_fact_names.each do |name|
36-
expect(deserial).to include(name)
45+
expect(deserial).to contain_child_with_key(name)
3746
end
3847

3948
module_fact_names.each do |name|
40-
expect(deserial).not_to include(name)
49+
expect(deserial).to_not contain_child_with_key(name)
4150
end
4251
end
4352
end
@@ -51,15 +60,15 @@ def run_sidecar(cmd_options)
5160

5261
it 'should return a deserializable facts object with default facts and workspace facts' do
5362
result = run_sidecar(cmd_options)
54-
deserial = PuppetLanguageServer::Sidecar::Protocol::Facts.new
63+
deserial = PuppetLanguageServer::Sidecar::Protocol::FactList.new
5564
expect { deserial.from_json!(result) }.to_not raise_error
5665

5766
default_fact_names.each do |name|
58-
expect(deserial).to include(name)
67+
expect(deserial).to contain_child_with_key(name)
5968
end
6069

6170
module_fact_names.each do |name|
62-
expect(deserial).to include(name)
71+
expect(deserial).to contain_child_with_key(name)
6372
end
6473
end
6574
end
@@ -74,15 +83,15 @@ def run_sidecar(cmd_options)
7483

7584
it 'should return a deserializable facts object with default facts and workspace facts' do
7685
result = run_sidecar(cmd_options)
77-
deserial = PuppetLanguageServer::Sidecar::Protocol::Facts.new
86+
deserial = PuppetLanguageServer::Sidecar::Protocol::FactList.new
7887
expect { deserial.from_json!(result) }.to_not raise_error
7988

8089
default_fact_names.each do |name|
81-
expect(deserial).to include(name)
90+
expect(deserial).to contain_child_with_key(name)
8291
end
8392

8493
environment_fact_names.each do |name|
85-
expect(deserial).to include(name)
94+
expect(deserial).to contain_child_with_key(name)
8695
end
8796
end
8897
end

spec/languageserver/spec_helper.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ def wait_for_puppet_loading
2424
break if PuppetLanguageServer::PuppetHelper.default_functions_loaded? &&
2525
PuppetLanguageServer::PuppetHelper.default_types_loaded? &&
2626
PuppetLanguageServer::PuppetHelper.default_classes_loaded? &&
27-
PuppetLanguageServer::PuppetHelper.default_datatypes_loaded?
27+
PuppetLanguageServer::PuppetHelper.default_datatypes_loaded? &&
28+
PuppetLanguageServer::FacterHelper.facts_loaded?
2829
sleep(1)
2930
interation += 1
3031
next if interation < 90
3132
raise <<-ERRORMSG
3233
Puppet has not be initialised in time:
34+
facts_loaded? = #{PuppetLanguageServer::FacterHelper.facts_loaded?}
3335
functions_loaded? = #{PuppetLanguageServer::PuppetHelper.default_functions_loaded?}
3436
types_loaded? = #{PuppetLanguageServer::PuppetHelper.default_types_loaded?}
3537
classes_loaded? = #{PuppetLanguageServer::PuppetHelper.default_classes_loaded?}
@@ -59,6 +61,13 @@ def add_random_basepuppetobject_values!(value)
5961
value
6062
end
6163

64+
def random_sidecar_fact(key = nil)
65+
result = add_random_basepuppetobject_values!(PuppetLanguageServer::Sidecar::Protocol::Fact.new())
66+
result.key = key unless key.nil?
67+
result.value = 'value' + rand(1000).to_s
68+
result
69+
end
70+
6271
def random_sidecar_puppet_class(key = nil)
6372
result = add_random_basepuppetobject_values!(PuppetLanguageServer::Sidecar::Protocol::PuppetClass.new())
6473
result.key = key unless key.nil?

0 commit comments

Comments
 (0)